diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-08-13 01:19:47 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-08-13 01:19:47 +0300 |
commit | 80a9adbcd0d80a62eec3f2714ee902af6fb562f1 (patch) | |
tree | 54e5d6b0eb55a963a24ee743fd1c5406349d3c62 | |
parent | b8e05db1133b98ae3c023318b963962834c842eb (diff) |
v2.18.0v2.18.0
- **Command line arguments:**
- (Add) Convert files: UVtools.exe -c \<inputfile\> \<outputfile1/ext1\> [outputfile2/ext2]
- (Add) Extract files: UVtools.exe -e \<inputfile\> [output_folder]
- https://github.com/sn4k3/UVtools#command-line-arguments
- **File formats:**
- (Add) Implement TSMC (Two Stage Motor Control) for the supported formats
- (Add) Implement 'Bottom retract speed' for the supported formats
- (Add) LGS: Support for lgs120 and lg4k (#218)
- (Add) CTB: Special/virtual file extensions .v2.ctb, .v3.ctb, .v4.ctb to force a convertion to the set version (2 to 4). The .ctb is Version 3 by default when creating/converting files
- (Improvement) Better performance for file formats that decode images in sequential pixels groups
- **GCode:**
- (Improvement) Better parsing of the movements / lifts
- (Improvement) Better handling of lifts performed after cure the layer
- (Improvement) More fail-safe checks and sanitize of gcode while parsing
- (Improvement) CTBv3: Enable per layer settings if disabled when fast save without reencode
- (Upgrade) .NET from 5.0.8 to 5.0.9
- (Fix) PrusaSlicer printer: Longer Orange 4k with correct resolution and display size
- (Fix) Odd error when changing properties too fast in multi-thread
39 files changed, 1958 insertions, 369 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 16e1e67..316c0ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 12/08/2021 - v2.18.0 + +- **Command line arguments:** + - (Add) Convert files: UVtools.exe -c \<inputfile\> \<outputfile1/ext1\> [outputfile2/ext2] + - (Add) Extract files: UVtools.exe -e \<inputfile\> [output_folder] + - https://github.com/sn4k3/UVtools#command-line-arguments +- **File formats:** + - (Add) Implement TSMC (Two Stage Motor Control) for the supported formats + - (Add) Implement 'Bottom retract speed' for the supported formats + - (Add) LGS: Support for lgs120 and lg4k (#218) + - (Add) CTB: Special/virtual file extensions .v2.ctb, .v3.ctb, .v4.ctb to force a convertion to the set version (2 to 4). The .ctb is Version 3 by default when creating/converting files + - (Improvement) Better performance for file formats that decode images in sequential pixels groups +- **GCode:** + - (Improvement) Better parsing of the movements / lifts + - (Improvement) Better handling of lifts performed after cure the layer + - (Improvement) More fail-safe checks and sanitize of gcode while parsing +- (Improvement) CTBv3: Enable per layer settings if disabled when fast save without reencode +- (Upgrade) .NET from 5.0.8 to 5.0.9 +- (Fix) PrusaSlicer printer: Longer Orange 4k with correct resolution and display size +- (Fix) Odd error when changing properties too fast in multi-thread + ## 08/08/2021 - v2.17.0 - **Windows MSI:** diff --git a/Documentation/osla.md b/Documentation/osla.md index be765c8..2a5c432 100644 --- a/Documentation/osla.md +++ b/Documentation/osla.md @@ -198,9 +198,9 @@ LiftSpeed=100 (float) Speed in mm/min LiftHeight2=0 (float) Extra distance in mm to raise from current position LiftSpeed2=50 (float) Speed in mm/min WaitTimeAfterLift=0 (float) Time to wait in seconds after lift / before retract +RetractSpeed=100 (float) Speed in mm/min RetractHeight2=0 (float) Offset retract distance in mm to perform RetractSpeed2=150 (float) Speed in mm/min -RetractSpeed=100 (float) Speed in mm/min WaitTimeBeforeCure=2.5 (float) Time to wait in seconds before cure the layer / turn on LED ExposureTime=2.8 (float) Time in seconds while curing the layer WaitTimeAfterCure=1 (float) Time to wait in seconds after cure the layer / turn off LED diff --git a/PrusaSlicer/printer/Longer Orange 4K.ini b/PrusaSlicer/printer/Longer Orange 4K.ini index b41bded..9de296c 100644 --- a/PrusaSlicer/printer/Longer Orange 4K.ini +++ b/PrusaSlicer/printer/Longer Orange 4K.ini @@ -1,18 +1,18 @@ -# generated by PrusaSlicer 2.3.1+win64 on 2021-06-28 at 08:59:40 UTC +# generated by PrusaSlicer 2.3.1+win64 on 2021-08-12 at 03:59:08 UTC absolute_correction = 0 area_fill = 50 bed_custom_model = bed_custom_texture = -bed_shape = 0x0,68.04x0,68.04x120.96,0x120.96 +bed_shape = 0x0,120.96x0,120.96x68.04,0x68.04 default_sla_material_profile = Prusa Orange Tough 0.05 default_sla_print_profile = 0.05 Normal -display_height = 120.96 +display_height = 68.04 display_mirror_x = 1 display_mirror_y = 0 display_orientation = landscape -display_pixels_x = 6480 -display_pixels_y = 3840 -display_width = 68.04 +display_pixels_x = 3840 +display_pixels_y = 6480 +display_width = 120.96 elefant_foot_compensation = 0.2 elefant_foot_min_width = 0.2 fast_tilt_time = 5 diff --git a/Scripts/010 Editor/lgs.bt b/Scripts/010 Editor/lgs.bt index 1f9e630..7b917b8 100644 --- a/Scripts/010 Editor/lgs.bt +++ b/Scripts/010 Editor/lgs.bt @@ -12,9 +12,6 @@ typedef struct() { BYTE LayerRLE[DataSize] <fgcolor=cBlack, bgcolor=cRed>; } layerData; -typedef struct(int size) { - BYTE layerDataBlock[size] <fgcolor=cBlack, bgcolor=0x00FF00>; -} rgbPreviewImageRawData; struct HEADER { char Name[8] <fgcolor=cBlack, bgcolor=cWhite>; @@ -62,15 +59,20 @@ struct HEADER { uint32 Uint_a8 <fgcolor=cBlack, bgcolor=cRed>; uint32 PreviewSizeX <fgcolor=cBlack, bgcolor=cRed>; uint32 PreviewSizeY <fgcolor=cBlack, bgcolor=cRed>; +} header; + - rgbPreviewImageRawData preview(PreviewSizeX*PreviewSizeY*2); +byte preview_data[header.PreviewSizeX*header.PreviewSizeY*2]; - if(PrinterModel == 120) // .lgs120 - { +if(header.PrinterModel == 120) // .lgs120 +{ + struct PNG_PREVIEW { uint32 pngLength <fgcolor=cBlack, bgcolor=cRed>; byte png[pngLength] <fgcolor=cBlack, bgcolor=cYellow>; - } -} header; + ushort padding <fgcolor=cBlack, bgcolor=cRed>; + } png_preview; +} + struct LAYERS { local int i; diff --git a/Scripts/010 Editor/osla.bt b/Scripts/010 Editor/osla.bt index c56b8e3..355ac8a 100644 --- a/Scripts/010 Editor/osla.bt +++ b/Scripts/010 Editor/osla.bt @@ -25,7 +25,7 @@ struct HEADER { float MachineZ <fgcolor=cBlack, bgcolor=cRed>; float DisplayWidth <fgcolor=cBlack, bgcolor=cRed>; float DisplayHeight <fgcolor=cBlack, bgcolor=cRed>; - byte DisplayMirror <fgcolor=cBlack, bgcolor=cRed>; + ubyte DisplayMirror <fgcolor=cBlack, bgcolor=cRed>; char PreviewDataType[16] <fgcolor=cBlack, bgcolor=cRed>; char LayerDataType[16] <fgcolor=cBlack, bgcolor=cRed>; @@ -66,16 +66,33 @@ struct PREVIEW { ushort ResolutionX <fgcolor=cBlack, bgcolor=cPurple>; ushort ResolutionY <fgcolor=cBlack, bgcolor=cPurple>; uint ImageLength <fgcolor=cBlack, bgcolor=cPurple>; - byte ImageData[ImageLength] <fgcolor=cWhite, bgcolor=cBlack>; + ubyte ImageData[ImageLength] <fgcolor=cWhite, bgcolor=cBlack>; }; struct LAYER_DEF { uint DataAddress <fgcolor=cBlack, bgcolor=cPurple>; + float PositionZ <fgcolor=cBlack, bgcolor=cPurple>; + float LiftHeight <fgcolor=cBlack, bgcolor=cPurple>; + float LiftSpeed <fgcolor=cBlack, bgcolor=cPurple>; + float LiftHeight2 <fgcolor=cBlack, bgcolor=cPurple>; + float LiftSpeed2 <fgcolor=cBlack, bgcolor=cPurple>; + float WaitTimeAfterLift <fgcolor=cBlack, bgcolor=cPurple>; + float RetractSpeed <fgcolor=cBlack, bgcolor=cPurple>; + float RetractHeight2 <fgcolor=cBlack, bgcolor=cPurple>; + float RetractSpeed2 <fgcolor=cBlack, bgcolor=cPurple>; + float WaitTimeBeforeCure <fgcolor=cBlack, bgcolor=cPurple>; + float ExposureTime <fgcolor=cBlack, bgcolor=cPurple>; + float WaitTimeAfterCure <fgcolor=cBlack, bgcolor=cPurple>; + ubyte LightPWM <fgcolor=cBlack, bgcolor=cPurple>; + uint BoundingRectangleX <fgcolor=cBlack, bgcolor=cPurple>; + uint BoundingRectangleY <fgcolor=cBlack, bgcolor=cPurple>; + uint BoundingRectangleWidth <fgcolor=cBlack, bgcolor=cPurple>; + uint BoundingRectangleHeight <fgcolor=cBlack, bgcolor=cPurple>; }; struct LAYER_DATA{ uint ImageLength <fgcolor=cWhite, bgcolor=cBlack>; - byte ImageData[ImageLength] <fgcolor=cWhite, bgcolor=cBlack>; + ubyte ImageData[ImageLength] <fgcolor=cWhite, bgcolor=cBlack>; }; struct GCODE{ diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index d53b283..5968786 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -149,6 +149,11 @@ namespace UVtools.Core.Extensions return mat.GetDataSpan<byte>(); } + public static unsafe Span<byte> GetDataByteSpan(this Mat mat, int length, int offset = 0) + { + return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.GetLength() : length); + } + /// <summary> /// Gets the data span to manipulate or read pixels given a length and offset /// </summary> @@ -217,6 +222,56 @@ namespace UVtools.Core.Extensions } #endregion + #region Memory Fill + + /// <summary> + /// Fill a mat span with a color + /// </summary> + /// <param name="mat">Mat to fill</param> + /// <param name="startPosition">Start position, this reference will increment by the <see cref="length"/></param> + /// <param name="length">Length to fill</param> + /// <param name="color">Color to fill with</param> + /// <param name="colorFillMinThreshold">Ignore and sum <see cref="startPosition"/> to <see cref="length"/> if <see cref="color"/> is less than the threshold value</param> + public static void FillSpan(this Mat mat, ref int startPosition, int length, byte color, byte colorFillMinThreshold = 1) + { + if (length <= 0) return; + if (color < colorFillMinThreshold) // Ignore threshold (mostly if blacks), spare cycles + { + startPosition += length; + return; + } + + mat.GetDataByteSpan(length, startPosition).Fill(color); + startPosition += length; + } + + /// <summary> + /// Fill a mat span with a color + /// </summary> + /// <param name="mat">Mat to fill</param> + /// <param name="x"></param> + /// <param name="y"></param> + /// <param name="length">Length to fill</param> + /// <param name="color">Color to fill with</param> + /// <param name="colorFillMinThreshold">Ignore and sum <see cref="startPosition"/> to <see cref="length"/> if <see cref="color"/> is less than the threshold value</param> + public static void FillSpan(this Mat mat, int x, int y, int length, byte color, byte colorFillMinThreshold = 1) + { + if (length <= 0 || color < colorFillMinThreshold) return; // Ignore threshold (mostly if blacks), spare cycles + mat.GetDataByteSpan(length, mat.GetPixelPos(x, y)).Fill(color); + } + + /// <summary> + /// Fill a mat span with a color + /// </summary> + /// <param name="mat">Mat to fill</param> + /// <param name="position"></param> + /// <param name="length">Length to fill</param> + /// <param name="color">Color to fill with</param> + /// <param name="colorFillMinThreshold">Ignore and sum <see cref="startPosition"/> to <see cref="length"/> if <see cref="color"/> is less than the threshold value</param> + public static void FillSpan(this Mat mat, Point position, int length, byte color, byte colorFillMinThreshold = 1) + => mat.FillSpan(position.X, position.Y, length, color, colorFillMinThreshold); + #endregion + #region Get/Set methods /// <summary> /// Gets the total length of this <see cref="Mat"/></param> diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs index 8bbc547..6be074e 100644 --- a/UVtools.Core/Extensions/SizeExtensions.cs +++ b/UVtools.Core/Extensions/SizeExtensions.cs @@ -60,7 +60,7 @@ namespace UVtools.Core.Extensions /// </summary> /// <param name="size"></param> /// <returns></returns> - public static Size Invert(this Size size) => new(size.Height, size.Width); + public static Size Exchange(this Size size) => new(size.Height, size.Width); public static int Area(this Rectangle rect) => rect.Width * rect.Height; diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 40f4ca1..0145ba4 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -309,9 +309,6 @@ namespace UVtools.Core.FileFormats public override PrintParameterModifier[] PrintParameterModifiers { get; } = { PrintParameterModifier.BottomLayerCount, - //PrintParameterModifier.BottomLightOffDelay, - //PrintParameterModifier.LightOffDelay, - PrintParameterModifier.BottomWaitTimeBeforeCure, PrintParameterModifier.WaitTimeBeforeCure, @@ -326,11 +323,22 @@ namespace UVtools.Core.FileFormats 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, }; @@ -341,8 +349,12 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.WaitTimeAfterCure, PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, + PrintParameterModifier.LiftHeight2, + PrintParameterModifier.LiftSpeed2, PrintParameterModifier.WaitTimeAfterLift, PrintParameterModifier.RetractSpeed, + PrintParameterModifier.RetractHeight2, + PrintParameterModifier.RetractSpeed2, PrintParameterModifier.LightPWM, }; diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index f6f9d6d..0d8b5f0 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -548,6 +548,8 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value; } + public override float BottomRetractSpeed => RetractSpeed; + public override float RetractSpeed { get => SlicerInfoSettings.RetractSpeed; diff --git a/UVtools.Core/FileFormats/CXDLPv1File.cs b/UVtools.Core/FileFormats/CXDLPv1File.cs index 752cebc..81a845f 100644 --- a/UVtools.Core/FileFormats/CXDLPv1File.cs +++ b/UVtools.Core/FileFormats/CXDLPv1File.cs @@ -497,6 +497,8 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value; } + public override float BottomRetractSpeed => RetractSpeed; + public override float RetractSpeed { get => SlicerInfoSettings.RetractSpeed; diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index d2fdc78..9c4d346 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -27,6 +27,8 @@ namespace UVtools.Core.FileFormats { #region Constants + public const byte USED_VERSION = 3; // 318570521 + public const uint MAGIC_CBDDLP = 0x12FD0019; // 318570521 public const uint MAGIC_CBT = 0x12FD0086; // 318570630 public const uint MAGIC_CBTv4 = 0x12FD0106; // 318570758 @@ -69,7 +71,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets the software version /// </summary> - [FieldOrder(1)] public uint Version { get; set; } = 3; + [FieldOrder(1)] public uint Version { get; set; } = USED_VERSION; /// <summary> /// Gets dimensions of the printer’s X output volume, in millimeters. @@ -285,7 +287,7 @@ namespace UVtools.Core.FileFormats public class SlicerInfo { private string _machineName; - [FieldOrder(0)] public float BottomLiftDistance2 { get; set; } + [FieldOrder(0)] public float BottomLiftHeight2 { get; set; } [FieldOrder(1)] public float BottomLiftSpeed2 { get; set; } [FieldOrder(2)] public float LiftHeight2 { get; set; } [FieldOrder(3)] public float LiftSpeed2 { get; set; } @@ -352,7 +354,7 @@ namespace UVtools.Core.FileFormats public override string ToString() { - return $"{nameof(BottomLiftDistance2)}: {BottomLiftDistance2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(MysteriousId)}: {MysteriousId}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift2)}: {RestTimeAfterLift2}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(MachineName)}: {MachineName}"; + return $"{nameof(BottomLiftHeight2)}: {BottomLiftHeight2}, {nameof(BottomLiftSpeed2)}: {BottomLiftSpeed2}, {nameof(LiftHeight2)}: {LiftHeight2}, {nameof(LiftSpeed2)}: {LiftSpeed2}, {nameof(RetractHeight2)}: {RetractHeight2}, {nameof(RetractSpeed2)}: {RetractSpeed2}, {nameof(RestTimeAfterLift)}: {RestTimeAfterLift}, {nameof(MachineNameAddress)}: {MachineNameAddress}, {nameof(MachineNameSize)}: {MachineNameSize}, {nameof(EncryptionMode)}: {EncryptionMode}, {nameof(MysteriousId)}: {MysteriousId}, {nameof(AntiAliasLevel)}: {AntiAliasLevel}, {nameof(SoftwareVersion)}: {SoftwareVersion}, {nameof(RestTimeAfterRetract)}: {RestTimeAfterRetract}, {nameof(RestTimeAfterLift2)}: {RestTimeAfterLift2}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(Padding1)}: {Padding1}, {nameof(Padding2)}: {Padding2}, {nameof(Padding3)}: {Padding3}, {nameof(MachineName)}: {MachineName}"; } } @@ -706,10 +708,10 @@ namespace UVtools.Core.FileFormats return image; } - private unsafe Mat DecodeCbtImage(uint layerIndex) + private Mat DecodeCbtImage(uint layerIndex) { - var image = EmguExtensions.InitMat(Parent.Resolution); - var span = image.GetBytePointer(); + var mat = EmguExtensions.InitMat(Parent.Resolution); + //var span = mat.GetBytePointer(); if (Parent.HeaderSettings.EncryptionKey > 0) { @@ -751,7 +753,7 @@ namespace UVtools.Core.FileFormats } else { - image.Dispose(); + mat.Dispose(); throw new FileLoadException("Corrupted RLE data"); } } @@ -762,22 +764,24 @@ namespace UVtools.Core.FileFormats code = (byte)((code << 1) | 1); } - if (stride == 0) continue; // Nothing to do + mat.FillSpan(ref pixel, stride, code); - if (code == 0) // Ignore blacks, spare cycles + //if (stride <= 0) continue; // Nothing to do + + /*if (code == 0) // Ignore blacks, spare cycles { pixel += stride; continue; - } + }*/ - for (; stride > 0; stride--) + /*for (; stride > 0; stride--) { span[pixel] = code; pixel++; - } + }*/ } - return image; + return mat; } public byte[] Encode(Mat image, byte aaIndex, uint layerIndex) @@ -950,7 +954,7 @@ namespace UVtools.Core.FileFormats public class LayerDataEx { /// <summary> - /// Gets a copy of layer data defenition + /// Gets a copy of layer data definition /// </summary> [FieldOrder(0)] public LayerData LayerData { get; set; } = new(); @@ -986,6 +990,12 @@ namespace UVtools.Core.FileFormats if (layerData.Parent.HeaderSettings.Version >= 4) { + LiftHeight2 = layerData.Parent[layerIndex].LiftHeight2; + LiftSpeed2 = layerData.Parent[layerIndex].LiftSpeed2; + + RetractHeight2 = layerData.Parent[layerIndex].RetractHeight2; + RetractSpeed2 = layerData.Parent[layerIndex].RetractSpeed2; + RestTimeAfterRetract = layerData.Parent[layerIndex].WaitTimeBeforeCure; RestTimeBeforeLift = layerData.Parent[layerIndex].WaitTimeAfterCure; RestTimeAfterLift = layerData.Parent[layerIndex].WaitTimeAfterLift; @@ -1075,7 +1085,11 @@ namespace UVtools.Core.FileFormats public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { - new("ctb", "Chitubox CTB"), + new("ctb", $"Chitubox CTBv{USED_VERSION}"), + //new("v2.ctb", "Chitubox CTBv2"), + //new("v3.ctb", "Chitubox CTBv3"), + new("v4.ctb", "Chitubox CTBv4"), + //new("encrypted.ctb", "Chitubox encrypted CTB"), new("cbddlp", "Chitubox CBDDLP"), new("photon", "Chitubox Photon"), }; @@ -1107,11 +1121,20 @@ namespace UVtools.Core.FileFormats 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, @@ -1169,8 +1192,12 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.WaitTimeAfterCure, PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, + PrintParameterModifier.LiftHeight2, + PrintParameterModifier.LiftSpeed2, PrintParameterModifier.WaitTimeAfterLift, PrintParameterModifier.RetractSpeed, + PrintParameterModifier.RetractHeight2, + PrintParameterModifier.RetractSpeed2, PrintParameterModifier.LightPWM, }; } @@ -1421,6 +1448,46 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = PrintParametersSettings.LiftSpeed = (float)Math.Round(value, 2); } + public override float BottomLiftHeight2 + { + get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.BottomLiftHeight2 : 0; + set + { + if (HeaderSettings.Version < 4) return; + base.BottomLiftHeight2 = SlicerInfoSettings.BottomLiftHeight2 = (float)Math.Round(value, 2); + } + } + + public override float LiftHeight2 + { + get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.LiftHeight2 : 0; + set + { + if (HeaderSettings.Version < 4) return; + base.LiftHeight2 = SlicerInfoSettings.LiftHeight2 = (float)Math.Round(value, 2); + } + } + + public override float BottomLiftSpeed2 + { + get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.BottomLiftSpeed2 : 0; + set + { + if (HeaderSettings.Version < 4) return; + base.BottomLiftSpeed2 = SlicerInfoSettings.BottomLiftSpeed2 = (float)Math.Round(value, 2); + } + } + + public override float LiftSpeed2 + { + get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.LiftSpeed2 : 0; + set + { + if (HeaderSettings.Version < 4) return; + base.LiftSpeed2 = SlicerInfoSettings.LiftSpeed2 = (float)Math.Round(value, 2); + } + } + public override float BottomWaitTimeAfterLift => WaitTimeAfterLift; public override float WaitTimeAfterLift { @@ -1437,10 +1504,52 @@ namespace UVtools.Core.FileFormats } } + public override float BottomRetractSpeed + { + get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.BottomRetractSpeed : RetractSpeed; + set + { + if (HeaderSettings.Version < 4) return; + base.BottomRetractSpeed = PrintParametersV4Settings.BottomRetractSpeed = (float)Math.Round(value, 2); + } + } + public override float RetractSpeed { get => PrintParametersSettings.RetractSpeed; - set => base.RetractSpeed = PrintParametersV4Settings.BottomRetractSpeed = PrintParametersSettings.RetractSpeed = (float)Math.Round(value, 2); + set => base.RetractSpeed = PrintParametersSettings.RetractSpeed = (float)Math.Round(value, 2); + } + + public override float BottomRetractHeight2 + { + get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.BottomRetractHeight2 : 0; + set + { + if (HeaderSettings.Version < 4) return; + base.BottomRetractSpeed2 = PrintParametersV4Settings.BottomRetractHeight2 = (float)Math.Round(value, 2); + } + } + + public override float RetractHeight2 + { + get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.RetractHeight2 : 0; + set => base.RetractSpeed2 = SlicerInfoSettings.RetractHeight2 = (float)Math.Round(value, 2); + } + + public override float BottomRetractSpeed2 + { + get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.BottomRetractSpeed2 : 0; + set + { + if (HeaderSettings.Version < 4) return; + base.BottomRetractSpeed2 = PrintParametersV4Settings.BottomRetractSpeed2 = (float)Math.Round(value, 2); + } + } + + public override float RetractSpeed2 + { + get => HeaderSettings.Version >= 4 ? SlicerInfoSettings.RetractSpeed2 : 0; + set => base.RetractSpeed2 = SlicerInfoSettings.RetractSpeed2 = (float)Math.Round(value, 2); } public override byte BottomLightPWM @@ -1537,13 +1646,8 @@ namespace UVtools.Core.FileFormats LayerDefinitions = null; } - protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + public void SanitizeProperties() { - LayersHash.Clear(); - - HeaderSettings.Magic = FileEndsWith(".ctb") ? MAGIC_CBT : MAGIC_CBDDLP; - HeaderSettings.PrintParametersSize = (uint)Helpers.Serializer.SizeOf(PrintParametersSettings); - if (IsCbtFile) { if (SlicerInfoSettings.AntiAliasLevel <= 1) @@ -1555,30 +1659,52 @@ namespace UVtools.Core.FileFormats if (HeaderSettings.Version <= 2) { - //if (SlicerInfoSettings.RestTimeAfterRetract == 0) - //SlicerInfoSettings.RestTimeAfterRetract = 0x200; // 512 for v2 | 0 for v3 SlicerInfoSettings.EncryptionMode = ENCRYPTYION_MODE_CTBv2; PrintParametersSettings.Padding4 = 0x1234; // 4660 if (SlicerInfoSettings.MysteriousId == 0) SlicerInfoSettings.MysteriousId = 305419896; } - else if(HeaderSettings.Version == 3) + else if (HeaderSettings.Version == 3) { SlicerInfoSettings.EncryptionMode = ENCRYPTYION_MODE_CTBv3; if (SlicerInfoSettings.MysteriousId == 0) SlicerInfoSettings.MysteriousId = 305419896; } - else + else if (HeaderSettings.Version >= 4) { SlicerInfoSettings.EncryptionMode = ENCRYPTYION_MODE_CTBv4; if (SlicerInfoSettings.MysteriousId == 0) SlicerInfoSettings.MysteriousId = 27087820; } + } + } + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) + { + LayersHash.Clear(); + HeaderSettings.Magic = FileEndsWith(".ctb") ? MAGIC_CBT : MAGIC_CBDDLP; + HeaderSettings.PrintParametersSize = (uint)Helpers.Serializer.SizeOf(PrintParametersSettings); + + if (FileEndsWith(".v2.ctb")) + { + HeaderSettings.Version = 2; + } + else if (FileEndsWith(".v3.ctb")) + { + HeaderSettings.Version = 3; + } + else if (FileEndsWith(".v4.ctb")) + { + HeaderSettings.Version = 4; + } + + SanitizeProperties(); + if (IsCbtFile) + { if (HeaderSettings.EncryptionKey == 0) { var rnd = new Random(); @@ -1883,6 +2009,10 @@ namespace UVtools.Core.FileFormats if (HeaderSettings.Version >= 4) { + layer.LiftHeight2 = LayerDefinitionsEx[layerIndex].LiftHeight2; + layer.LiftSpeed2 = LayerDefinitionsEx[layerIndex].LiftSpeed2; + layer.RetractHeight2 = LayerDefinitionsEx[layerIndex].RetractHeight2; + layer.RetractSpeed2 = LayerDefinitionsEx[layerIndex].RetractSpeed2; layer.WaitTimeBeforeCure = LayerDefinitionsEx[layerIndex].RestTimeAfterRetract; layer.WaitTimeAfterCure = LayerDefinitionsEx[layerIndex].RestTimeBeforeLift; layer.WaitTimeAfterLift = LayerDefinitionsEx[layerIndex].RestTimeAfterLift; @@ -1915,6 +2045,7 @@ namespace UVtools.Core.FileFormats FileFullPath = filePath; } + SanitizeProperties(); using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); outputFile.Seek(0, SeekOrigin.Begin); Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs index 7d663e8..1039758 100644 --- a/UVtools.Core/FileFormats/ChituboxZipFile.cs +++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs @@ -96,11 +96,22 @@ namespace UVtools.Core.FileFormats 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, }; @@ -111,8 +122,12 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.WaitTimeAfterCure, PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, + PrintParameterModifier.LiftHeight2, + PrintParameterModifier.LiftSpeed2, PrintParameterModifier.WaitTimeAfterLift, PrintParameterModifier.RetractSpeed, + PrintParameterModifier.RetractHeight2, + PrintParameterModifier.RetractSpeed2, PrintParameterModifier.LightPWM, }; diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs index a80e0c1..2d601d2 100644 --- a/UVtools.Core/FileFormats/FDGFile.cs +++ b/UVtools.Core/FileFormats/FDGFile.cs @@ -867,6 +867,8 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = HeaderSettings.LiftSpeed = (float)Math.Round(value, 2); } + public override float BottomRetractSpeed => RetractSpeed; + public override float RetractSpeed { get => HeaderSettings.RetractSpeed; diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 465e68a..4b49fb0 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -44,15 +44,26 @@ namespace UVtools.Core.FileFormats public const ushort DefaultBottomLayerCount = 4; public const float DefaultBottomExposureTime = 30; + public const float DefaultExposureTime = 3; + public const float DefaultBottomLiftHeight = 5; public const float DefaultLiftHeight = 5; public const float DefaultBottomLiftSpeed = 100; - - public const float DefaultExposureTime = 3; public const float DefaultLiftSpeed = 100; + + public const float DefaultBottomLiftHeight2 = 0; + public const float DefaultLiftHeight2 = 0; + public const float DefaultBottomLiftSpeed2 = 300; + public const float DefaultLiftSpeed2 = 300; + + + public const float DefaultBottomRetractSpeed = 100; public const float DefaultRetractSpeed = 100; - public const float DefaultBottomLightOffDelay = 0; - public const float DefaultLightOffDelay = 0; + public const float DefaultBottomRetractHeight2 = 0; + public const float DefaultRetractHeight2 = 0; + public const float DefaultBottomRetractSpeed2 = 300; + public const float DefaultRetractSpeed2 = 300; + public const byte DefaultBottomLightPWM = 255; public const byte DefaultLightPWM = 255; @@ -61,7 +72,7 @@ namespace UVtools.Core.FileFormats public const float MinimumLayerHeight = 0.01f; public const float MaximumLayerHeight = 0.20f; - private const ushort QueueTimerPrintTime = 250; + private const ushort QueueTimerPrintTime = 250; // ms #endregion #region Enums @@ -108,16 +119,28 @@ namespace UVtools.Core.FileFormats public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s", 0, 1000, 2); public static PrintParameterModifier BottomLiftHeight { get; } = new ("Bottom lift height", "Bottom lift/peel height between layers", "mm", 1); - public static PrintParameterModifier LiftHeight { get; } = new ("Lift height", @"Lift/peel height between layers", "mm"); + public static PrintParameterModifier LiftHeight { get; } = new ("Lift height", @"Lift/peel height between layers", "mm", 1); public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", null, "mm/min", 10, 5000, 2); public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", null, "mm/min", 10, 5000, 2); - + + public static PrintParameterModifier BottomLiftHeight2 { get; } = new("2) Bottom lift height", "Bottom second lift/peel height between layers", "mm"); + public static PrintParameterModifier LiftHeight2 { get; } = new("2) Lift height", @"Second lift/peel height between layers", "mm"); + + public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", null, "mm/min", 10, 5000, 2); + public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", null, "mm/min", 10, 5000, 2); + public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift", "Time to wait/rest after a lift/peel sequence at bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s", 0, 1000, 2); public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift", "Time to wait/rest after a lift/peel sequence at layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s", 0, 1000, 2); + public static PrintParameterModifier BottomRetractSpeed { get; } = new ("Bottom retract speed", "Bottom down speed from lift height to next layer cure position", "mm/min", 10, 5000, 2); public static PrintParameterModifier RetractSpeed { get; } = new ("Retract speed", "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 2); + public static PrintParameterModifier BottomRetractHeight2 { get; } = new("2) Bottom retract height", "Second extra bottom retract height ", "mm"); + public static PrintParameterModifier RetractHeight2 { get; } = new("2) Retract height", @"Second extra retract height", "mm"); + public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", "Bottom second down speed from lift height to next layer cure position", "mm/min", 10, 5000, 2); + public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", "Second down speed from lift height to next layer cure position", "mm/min", 10, 5000, 2); + public static PrintParameterModifier BottomLightPWM { get; } = new ("Bottom light PWM", "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 0); public static PrintParameterModifier LightPWM { get; } = new ("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 0); @@ -360,21 +383,48 @@ namespace UVtools.Core.FileFormats #region Members private bool _haveModifiedLayers; private LayerManager _layerManager; - private float _printTime; - private float _materialMilliliters; - private float _machineZ; + private ushort _bottomLayerCount = DefaultBottomLayerCount; + + private float _bottomLightOffDelay; + private float _lightOffDelay; + + private float _bottomWaitTimeBeforeCure; + private float _waitTimeBeforeCure; + private float _bottomExposureTime = DefaultBottomExposureTime; private float _exposureTime = DefaultExposureTime; + + private float _bottomWaitTimeAfterCure; + private float _waitTimeAfterCure; + private float _bottomLiftHeight = DefaultBottomLiftHeight; private float _liftHeight = DefaultLiftHeight; private float _bottomLiftSpeed = DefaultBottomLiftSpeed; private float _liftSpeed = DefaultLiftSpeed; + + private float _bottomLiftHeight2 = DefaultBottomLiftHeight2; + private float _liftHeight2 = DefaultLiftHeight2; + private float _bottomLiftSpeed2 = DefaultBottomLiftSpeed2; + private float _liftSpeed2 = DefaultLiftSpeed2; + + private float _bottomWaitTimeAfterLift; + private float _waitTimeAfterLift; + + private float _bottomRetractHeight2 = DefaultBottomRetractHeight2; + private float _retractHeight2 = DefaultRetractHeight2; + private float _bottomRetractSpeed2 = DefaultBottomRetractSpeed2; + private float _retractSpeed2 = DefaultRetractSpeed2; + private float _bottomRetractSpeed = DefaultBottomRetractSpeed; private float _retractSpeed = DefaultRetractSpeed; - private float _bottomLightOffDelay = DefaultBottomLightOffDelay; - private float _lightOffDelay = DefaultLightOffDelay; + + private byte _bottomLightPwm = DefaultBottomLightPWM; private byte _lightPwm = DefaultLightPWM; + + private float _printTime; + private float _materialMilliliters; + private float _machineZ; private string _machineName = "Unknown"; private string _materialName; private float _materialGrams; @@ -382,17 +432,12 @@ namespace UVtools.Core.FileFormats private bool _suppressRebuildGCode; private readonly Timer _queueTimerPrintTime = new(QueueTimerPrintTime){AutoReset = false}; - private float _bottomWaitTimeBeforeCure; - private float _waitTimeBeforeCure; - private float _bottomWaitTimeAfterCure; - private float _waitTimeAfterCure; - private float _bottomWaitTimeAfterLift; - private float _waitTimeAfterLift; - #endregion #region Properties + public object Mutex = new(); + /// <summary> /// Gets the file format type /// </summary> @@ -857,6 +902,9 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets or sets the bottom time in seconds to wait before cure the layer + /// </summary> public virtual float BottomWaitTimeBeforeCure { get => _bottomWaitTimeBeforeCure; @@ -868,6 +916,9 @@ namespace UVtools.Core.FileFormats } + /// <summary> + /// Gets or sets the time in seconds to wait after cure the layer + /// </summary> public virtual float WaitTimeBeforeCure { get => _waitTimeBeforeCure; @@ -904,6 +955,9 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets or sets the bottom time in seconds to wait after cure the layer + /// </summary> public virtual float BottomWaitTimeAfterCure { get => _bottomWaitTimeAfterCure; @@ -914,6 +968,9 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets or sets the time in seconds to wait after cure the layer + /// </summary> public virtual float WaitTimeAfterCure { get => _waitTimeAfterCure; @@ -925,6 +982,34 @@ namespace UVtools.Core.FileFormats } /// <summary> + /// Gets: Total bottom lift height (lift1 + lift2) + /// Sets: Bottom lift1 with value and lift2 with 0 + /// </summary> + public float BottomLiftHeightTotal + { + get => (float)Math.Round(_bottomLiftHeight + _bottomLiftHeight2); + set + { + BottomLiftHeight = value; + BottomLiftHeight2 = 0; + } + } + + /// <summary> + /// Gets: Total lift height (lift1 + lift2) + /// Sets: Lift1 with value and lift2 with 0 + /// </summary> + public float LiftHeightTotal + { + get => (float)Math.Round(_liftHeight + _liftHeight2); + set + { + LiftHeight = value; + LiftHeight2 = 0; + } + } + + /// <summary> /// Gets or sets the bottom lift height in mm /// </summary> public virtual float BottomLiftHeight @@ -933,36 +1018,42 @@ namespace UVtools.Core.FileFormats set { RaiseAndSet(ref _bottomLiftHeight, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(BottomLiftHeightTotal)); RaisePropertyChanged(nameof(LiftRepresentation)); + BottomRetractHeight2 = _bottomRetractHeight2; // Sanitize } } /// <summary> - /// Gets or sets the lift height in mm + /// Gets or sets the bottom lift speed in mm/min /// </summary> - public virtual float LiftHeight + public virtual float BottomLiftSpeed { - get => _liftHeight; + get => _bottomLiftSpeed; set { - RaiseAndSet(ref _liftHeight, (float)Math.Round(value, 2)); + RaiseAndSet(ref _bottomLiftSpeed, (float)Math.Round(value, 2)); RaisePropertyChanged(nameof(LiftRepresentation)); } } /// <summary> - /// Gets or sets the bottom lift speed in mm/min + /// Gets or sets the lift height in mm /// </summary> - public virtual float BottomLiftSpeed + public virtual float LiftHeight { - get => _bottomLiftSpeed; + get => _liftHeight; set { - RaiseAndSet(ref _bottomLiftSpeed, (float)Math.Round(value, 2)); + RaiseAndSet(ref _liftHeight, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(LiftHeightTotal)); RaisePropertyChanged(nameof(LiftRepresentation)); + RetractHeight2 = _retractHeight2; // Sanitize } } + + /// <summary> /// Gets or sets the speed in mm/min /// </summary> @@ -976,6 +1067,66 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets or sets the second bottom lift height in mm + /// </summary> + public virtual float BottomLiftHeight2 + { + get => _bottomLiftHeight2; + set + { + RaiseAndSet(ref _bottomLiftHeight2, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(BottomLiftHeightTotal)); + RaisePropertyChanged(nameof(LiftRepresentation)); + BottomRetractHeight2 = _bottomRetractHeight2; // Sanitize + } + } + + /// <summary> + /// Gets or sets the second bottom lift speed in mm/min + /// </summary> + public virtual float BottomLiftSpeed2 + { + get => _bottomLiftSpeed2; + set + { + RaiseAndSet(ref _bottomLiftSpeed2, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(LiftRepresentation)); + } + } + + /// <summary> + /// Gets or sets the second lift height in mm + /// </summary> + public virtual float LiftHeight2 + { + get => _liftHeight2; + set + { + RaiseAndSet(ref _liftHeight2, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(LiftHeightTotal)); + RaisePropertyChanged(nameof(LiftRepresentation)); + RetractHeight2 = _retractHeight2; // Sanitize + } + } + + + /// <summary> + /// Gets or sets the second speed in mm/min + /// </summary> + public virtual float LiftSpeed2 + { + get => _liftSpeed2; + set + { + RaiseAndSet(ref _liftSpeed2, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(LiftRepresentation)); + } + } + + /// <summary> + /// Gets or sets the bottom time in seconds to wait after lift / before retract + /// </summary> public virtual float BottomWaitTimeAfterLift { get => _bottomWaitTimeAfterLift; @@ -986,6 +1137,9 @@ namespace UVtools.Core.FileFormats } } + /// <summary> + /// Gets or sets the time in seconds to wait after lift / before retract + /// </summary> public virtual float WaitTimeAfterLift { get => _waitTimeAfterLift; @@ -997,6 +1151,39 @@ namespace UVtools.Core.FileFormats } /// <summary> + /// Gets: Total bottom retract height (retract1 + retract2) alias of <see cref="BottomLiftHeightTotal"/> + /// </summary> + public float BottomRetractHeightTotal => BottomLiftHeightTotal; + + /// <summary> + /// Gets: Total retract height (retract1 + retract2) alias of <see cref="LiftHeightTotal"/> + /// </summary> + public float RetractHeightTotal => LiftHeightTotal; + + /// <summary> + /// Gets the bottom retract height in mm + /// </summary> + public float BottomRetractHeight => (float)Math.Round(BottomLiftHeightTotal - _bottomRetractHeight2); + + /// <summary> + /// Gets the speed in mm/min for the bottom retracts + /// </summary> + public virtual float BottomRetractSpeed + { + get => _bottomRetractSpeed; + set + { + RaiseAndSet(ref _bottomRetractSpeed, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(RetractRepresentation)); + } + } + + /// <summary> + /// Gets the retract height in mm + /// </summary> + public float RetractHeight => (float)Math.Round(LiftHeightTotal - _retractHeight2); + + /// <summary> /// Gets the speed in mm/min for the retracts /// </summary> public virtual float RetractSpeed @@ -1010,6 +1197,64 @@ namespace UVtools.Core.FileFormats } /// <summary> + /// Gets or sets the second bottom retract height in mm + /// </summary> + public virtual float BottomRetractHeight2 + { + get => _bottomRetractHeight2; + set + { + value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal); + RaiseAndSet(ref _bottomRetractHeight2, value); + RaisePropertyChanged(nameof(BottomRetractHeight)); + RaisePropertyChanged(nameof(BottomRetractHeightTotal)); + RaisePropertyChanged(nameof(RetractRepresentation)); + } + } + + /// <summary> + /// Gets the speed in mm/min for the retracts + /// </summary> + public virtual float BottomRetractSpeed2 + { + get => _bottomRetractSpeed2; + set + { + RaiseAndSet(ref _bottomRetractSpeed2, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(RetractRepresentation)); + } + } + + /// <summary> + /// Gets or sets the second retract height in mm + /// </summary> + public virtual float RetractHeight2 + { + get => _retractHeight2; + set + { + value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal); + RaiseAndSet(ref _retractHeight2, value); + RaisePropertyChanged(nameof(RetractHeight)); + RaisePropertyChanged(nameof(RetractHeightTotal)); + RaisePropertyChanged(nameof(RetractRepresentation)); + } + } + + /// <summary> + /// Gets the speed in mm/min for the retracts + /// </summary> + public virtual float RetractSpeed2 + { + get => _retractSpeed2; + set + { + RaiseAndSet(ref _retractSpeed2, (float)Math.Round(value, 2)); + RaisePropertyChanged(nameof(RetractRepresentation)); + } + } + + /// <summary> /// Gets or sets the bottom pwm value from 0 to 255 /// </summary> public virtual byte BottomLightPWM @@ -1053,11 +1298,28 @@ namespace UVtools.Core.FileFormats public bool CanUseLiftSpeed => HavePrintParameterModifier(PrintParameterModifier.LiftSpeed); public bool CanUseAnyLiftSpeed => CanUseBottomLiftSpeed || CanUseLiftSpeed; + public bool CanUseBottomLiftHeight2 => HavePrintParameterModifier(PrintParameterModifier.BottomLiftHeight2); + public bool CanUseLiftHeight2 => HavePrintParameterModifier(PrintParameterModifier.LiftHeight2); + public bool CanUseAnyLiftHeight2 => CanUseBottomLiftHeight2 || CanUseLiftHeight2; + + public bool CanUseBottomLiftSpeed2 => HavePrintParameterModifier(PrintParameterModifier.BottomLiftSpeed2); + public bool CanUseLiftSpeed2 => HavePrintParameterModifier(PrintParameterModifier.LiftSpeed2); + public bool CanUseAnyLiftSpeed2 => CanUseBottomLiftSpeed2 || CanUseLiftSpeed2; + public bool CanUseBottomWaitTimeAfterLift => HavePrintParameterModifier(PrintParameterModifier.BottomWaitTimeAfterLift); public bool CanUseWaitTimeAfterLift => HavePrintParameterModifier(PrintParameterModifier.WaitTimeAfterLift); public bool CanUseAnyWaitTimeAfterLift => CanUseBottomWaitTimeAfterLift || CanUseWaitTimeAfterLift; + public bool CanUseBottomRetractSpeed => HavePrintParameterModifier(PrintParameterModifier.BottomRetractSpeed); public bool CanUseRetractSpeed => HavePrintParameterModifier(PrintParameterModifier.RetractSpeed); + public bool CanUseAnyRetractSpeed => CanUseBottomRetractSpeed || CanUseRetractSpeed; + + public bool CanUseBottomRetractHeight2 => HavePrintParameterModifier(PrintParameterModifier.BottomRetractHeight2); + public bool CanUseRetractHeight2 => HavePrintParameterModifier(PrintParameterModifier.RetractHeight2); + public bool CanUseAnyRetractHeight2 => CanUseBottomRetractHeight2 || CanUseRetractHeight2; + public bool CanUseBottomRetractSpeed2 => HavePrintParameterModifier(PrintParameterModifier.BottomRetractSpeed2); + public bool CanUseRetractSpeed2 => HavePrintParameterModifier(PrintParameterModifier.RetractSpeed2); + public bool CanUseAnyRetractSpeed2 => CanUseBottomRetractSpeed2 || CanUseRetractSpeed2; public bool CanUseAnyWaitTime => CanUseBottomWaitTimeBeforeCure || CanUseBottomWaitTimeAfterCure || CanUseBottomWaitTimeAfterLift || CanUseWaitTimeBeforeCure || CanUseWaitTimeAfterCure || CanUseWaitTimeAfterLift; @@ -1071,8 +1333,12 @@ namespace UVtools.Core.FileFormats public bool CanUseLayerWaitTimeAfterCure => HaveLayerParameterModifier(PrintParameterModifier.WaitTimeAfterCure); public bool CanUseLayerLiftHeight => HaveLayerParameterModifier(PrintParameterModifier.LiftHeight); public bool CanUseLayerLiftSpeed => HaveLayerParameterModifier(PrintParameterModifier.LiftSpeed); + public bool CanUseLayerLiftHeight2 => HaveLayerParameterModifier(PrintParameterModifier.LiftHeight2); + public bool CanUseLayerLiftSpeed2 => HaveLayerParameterModifier(PrintParameterModifier.LiftSpeed2); public bool CanUseLayerWaitTimeAfterLift => HaveLayerParameterModifier(PrintParameterModifier.WaitTimeAfterLift); public bool CanUseLayerRetractSpeed => HaveLayerParameterModifier(PrintParameterModifier.RetractSpeed); + public bool CanUseLayerRetractHeight2 => HaveLayerParameterModifier(PrintParameterModifier.RetractHeight2); + public bool CanUseLayerRetractSpeed2 => HaveLayerParameterModifier(PrintParameterModifier.RetractSpeed2); public bool CanUseLayerLightOffDelay => HaveLayerParameterModifier(PrintParameterModifier.LightOffDelay); public bool CanUseLayerLightPWM => HaveLayerParameterModifier(PrintParameterModifier.LightPWM); @@ -1106,9 +1372,12 @@ namespace UVtools.Core.FileFormats var haveBottomLiftHeight = CanUseBottomLiftHeight; var haveLiftHeight = CanUseLiftHeight; + var haveBottomLiftHeight2 = CanUseBottomLiftHeight2; + var haveLiftHeight2 = CanUseLiftHeight2; - if (!haveBottomLiftHeight && !haveLiftHeight) return str; + if (!haveBottomLiftHeight && !haveLiftHeight && !haveBottomLiftHeight2 && !haveLiftHeight2) return str; + // Sequence 1 if (haveBottomLiftHeight) { str += BottomLiftHeight.ToString(CultureInfo.InvariantCulture); @@ -1137,6 +1406,35 @@ namespace UVtools.Core.FileFormats str += "mm/min"; + // Sequence 2 + if (haveBottomLiftHeight2) + { + str += $"\n2th: {BottomLiftHeight2.ToString(CultureInfo.InvariantCulture)}"; + } + if (haveLiftHeight2) + { + str += str.EndsWith("mm/min") ? "\n2th: " : '/'; + str += LiftHeight2.ToString(CultureInfo.InvariantCulture); + } + + if (str.EndsWith("mm/min")) return str; + + str += "mm @ "; + + var haveBottomLiftSpeed2 = CanUseBottomLiftSpeed2; + var haveLiftSpeed2 = CanUseLiftSpeed2; + if (haveBottomLiftSpeed2) + { + str += BottomLiftSpeed2.ToString(CultureInfo.InvariantCulture); + } + if (haveLiftSpeed2) + { + if (haveBottomLiftSpeed2) str += '/'; + str += LiftSpeed2.ToString(CultureInfo.InvariantCulture); + } + + str += "mm/min"; + return str; } } @@ -1147,12 +1445,71 @@ namespace UVtools.Core.FileFormats { var str = string.Empty; - if (CanUseRetractSpeed) + var haveBottomRetractHeight = CanUseLiftHeight; + var haveRetractHeight = CanUseBottomLiftHeight; + var haveBottomRetractSpeed = CanUseBottomRetractSpeed; + var haveRetractSpeed = CanUseRetractSpeed; + var haveBottomRetractHeight2 = CanUseBottomRetractHeight2; + var haveRetractHeight2 = CanUseRetractHeight2; + var haveBottomRetractSpeed2 = CanUseBottomRetractSpeed2; + var haveRetractSpeed2 = CanUseRetractSpeed2; + + if (!haveBottomRetractSpeed && !haveRetractSpeed && !haveBottomRetractHeight2 && !haveRetractHeight2) return str; + + // Sequence 1 + if (haveBottomRetractHeight) { + str += BottomRetractHeight.ToString(CultureInfo.InvariantCulture); + } + if (haveRetractHeight) + { + if (!string.IsNullOrEmpty(str)) str += '/'; + str += RetractHeight.ToString(CultureInfo.InvariantCulture); + } + + if (string.IsNullOrEmpty(str)) return str; + + str += "mm @ "; + + + if (haveBottomRetractSpeed) + { + str += BottomRetractSpeed.ToString(CultureInfo.InvariantCulture); + } + if (haveRetractSpeed) + { + if (haveBottomRetractSpeed) str += '/'; str += RetractSpeed.ToString(CultureInfo.InvariantCulture); } - if (!string.IsNullOrEmpty(str)) str += "mm/min"; + str += "mm/min"; + + // Sequence 2 + if (haveBottomRetractHeight2) + { + str += $"\n2th: {BottomRetractHeight2.ToString(CultureInfo.InvariantCulture)}"; + } + if (haveRetractHeight2) + { + str += str.EndsWith("mm/min") ? "\n2th: " : '/'; + str += RetractHeight2.ToString(CultureInfo.InvariantCulture); + } + + if (str.EndsWith("mm/min")) return str; + + str += "mm @ "; + + if (haveBottomRetractSpeed2) + { + str += BottomRetractSpeed2.ToString(CultureInfo.InvariantCulture); + } + if (haveRetractSpeed2) + { + if (haveBottomRetractSpeed2) str += '/'; + str += RetractSpeed2.ToString(CultureInfo.InvariantCulture); + } + + str += "mm/min"; return str; } @@ -1461,12 +1818,21 @@ namespace UVtools.Core.FileFormats or nameof(BottomWaitTimeAfterCure) or nameof(WaitTimeAfterCure) or nameof(BottomLiftHeight) + or nameof(BottomLiftSpeed) or nameof(LiftHeight) - or nameof(BottomLiftSpeed) or nameof(LiftSpeed) + or nameof(BottomLiftHeight2) + or nameof(BottomLiftSpeed2) + or nameof(LiftHeight2) + or nameof(LiftSpeed2) or nameof(BottomWaitTimeAfterLift) or nameof(WaitTimeAfterLift) - or nameof(RetractSpeed) + or nameof(BottomRetractSpeed) + or nameof(RetractSpeed) + or nameof(BottomRetractHeight2) + or nameof(BottomRetractSpeed2) + or nameof(RetractHeight2) + or nameof(RetractSpeed2) or nameof(BottomLightPWM) or nameof(LightPWM) ) @@ -1579,7 +1945,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// Checks if a extension is valid under the <see cref="FileFormat"/> /// </summary> - /// <param name="extension">Extension to check</param> + /// <param name="extension">Extension to check without the dot (.)</param> /// <param name="isFilePath">True if <see cref="extension"/> is a full file path, otherwise false for extension only</param> /// <returns>True if valid, otherwise false</returns> public bool IsExtensionValid(string extension, bool isFilePath = false) @@ -1865,24 +2231,36 @@ namespace UVtools.Core.FileFormats tw.WriteLine($"{nameof(layer.LayerHeight)}: {layer.LayerHeight}"); tw.WriteLine($"{nameof(layer.PositionZ)}: {layer.PositionZ}"); - if (HaveLayerParameterModifier(PrintParameterModifier.LightOffDelay)) + if (CanUseLayerLightOffDelay) tw.WriteLine($"{nameof(layer.LightOffDelay)}: {layer.LightOffDelay}"); - if (HaveLayerParameterModifier(PrintParameterModifier.WaitTimeBeforeCure)) + if (CanUseLayerWaitTimeBeforeCure) tw.WriteLine($"{nameof(layer.WaitTimeBeforeCure)}: {layer.WaitTimeBeforeCure}"); tw.WriteLine($"{nameof(layer.ExposureTime)}: {layer.ExposureTime}"); - if (HaveLayerParameterModifier(PrintParameterModifier.WaitTimeAfterCure)) + if (CanUseLayerWaitTimeAfterCure) tw.WriteLine($"{nameof(layer.WaitTimeAfterCure)}: {layer.WaitTimeAfterCure}"); - if (HaveLayerParameterModifier(PrintParameterModifier.LiftHeight)) + if (CanUseLayerLiftHeight) tw.WriteLine($"{nameof(layer.LiftHeight)}: {layer.LiftHeight}"); - if (HaveLayerParameterModifier(PrintParameterModifier.LiftSpeed)) + if (CanUseLayerLiftSpeed) tw.WriteLine($"{nameof(layer.LiftSpeed)}: {layer.LiftSpeed}"); - if (HaveLayerParameterModifier(PrintParameterModifier.WaitTimeAfterLift)) + if (CanUseLayerLiftHeight2) + tw.WriteLine($"{nameof(layer.LiftHeight2)}: {layer.LiftHeight2}"); + if (CanUseLayerLiftSpeed2) + tw.WriteLine($"{nameof(layer.LiftSpeed2)}: {layer.LiftSpeed2}"); + if (CanUseLayerWaitTimeAfterLift) tw.WriteLine($"{nameof(layer.WaitTimeAfterLift)}: {layer.WaitTimeAfterLift}"); - if (HaveLayerParameterModifier(PrintParameterModifier.RetractSpeed)) + if (CanUseLayerRetractSpeed) + { + tw.WriteLine($"{nameof(layer.RetractHeight)}: {layer.RetractHeight}"); tw.WriteLine($"{nameof(layer.RetractSpeed)}: {layer.RetractSpeed}"); - if (HaveLayerParameterModifier(PrintParameterModifier.LightPWM)) + } + if (CanUseLayerRetractHeight2) + tw.WriteLine($"{nameof(layer.RetractHeight2)}: {layer.RetractHeight2}"); + if (CanUseLayerRetractSpeed2) + tw.WriteLine($"{nameof(layer.RetractSpeed2)}: {layer.RetractSpeed2}"); + + if (CanUseLayerLightPWM) tw.WriteLine($"{nameof(layer.LightPWM)}: {layer.LightPWM}"); var materialMillilitersPercent = layer.MaterialMillilitersPercent; @@ -2025,14 +2403,14 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.BottomLiftHeight.Value = (decimal)BottomLiftHeight; } - if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftHeight)) + if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftSpeed)) { - PrintParameterModifier.LiftHeight.Value = (decimal)LiftHeight; + PrintParameterModifier.BottomLiftSpeed.Value = (decimal)BottomLiftSpeed; } - if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftSpeed)) + if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftHeight)) { - PrintParameterModifier.BottomLiftSpeed.Value = (decimal)BottomLiftSpeed; + PrintParameterModifier.LiftHeight.Value = (decimal)LiftHeight; } if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftSpeed)) @@ -2040,6 +2418,26 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LiftSpeed.Value = (decimal)LiftSpeed; } + if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftHeight2)) + { + PrintParameterModifier.BottomLiftHeight2.Value = (decimal)BottomLiftHeight2; + } + + if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLiftSpeed2)) + { + PrintParameterModifier.BottomLiftSpeed2.Value = (decimal)BottomLiftSpeed2; + } + + if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftHeight2)) + { + PrintParameterModifier.LiftHeight2.Value = (decimal)LiftHeight2; + } + + if (PrintParameterModifiers.Contains(PrintParameterModifier.LiftSpeed2)) + { + PrintParameterModifier.LiftSpeed2.Value = (decimal)LiftSpeed2; + } + if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomWaitTimeAfterLift)) { PrintParameterModifier.BottomWaitTimeAfterLift.Value = (decimal)BottomWaitTimeAfterLift; @@ -2050,11 +2448,36 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.WaitTimeAfterLift.Value = (decimal)WaitTimeAfterLift; } + if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomRetractSpeed)) + { + PrintParameterModifier.BottomRetractSpeed.Value = (decimal)BottomRetractSpeed; + } + if (PrintParameterModifiers.Contains(PrintParameterModifier.RetractSpeed)) { PrintParameterModifier.RetractSpeed.Value = (decimal)RetractSpeed; } + if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomRetractHeight2)) + { + PrintParameterModifier.BottomRetractHeight2.Value = (decimal)BottomRetractHeight2; + } + + if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomRetractSpeed2)) + { + PrintParameterModifier.BottomRetractSpeed2.Value = (decimal)BottomRetractSpeed2; + } + + if (PrintParameterModifiers.Contains(PrintParameterModifier.RetractHeight2)) + { + PrintParameterModifier.RetractHeight2.Value = (decimal)RetractHeight2; + } + + if (PrintParameterModifiers.Contains(PrintParameterModifier.RetractSpeed2)) + { + PrintParameterModifier.RetractSpeed2.Value = (decimal)RetractSpeed2; + } + if (PrintParameterModifiers.Contains(PrintParameterModifier.BottomLightPWM)) { PrintParameterModifier.BottomLightPWM.Value = BottomLightPWM; @@ -2104,6 +2527,16 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LiftSpeed.Value = (decimal)layer.LiftSpeed; } + if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftHeight2)) + { + PrintParameterModifier.LiftHeight2.Value = (decimal)layer.LiftHeight2; + } + + if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LiftSpeed2)) + { + PrintParameterModifier.LiftSpeed2.Value = (decimal)layer.LiftSpeed2; + } + if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.WaitTimeAfterLift)) { PrintParameterModifier.WaitTimeAfterLift.Value = (decimal)layer.WaitTimeAfterLift; @@ -2114,6 +2547,16 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.RetractSpeed.Value = (decimal)layer.RetractSpeed; } + if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.RetractHeight2)) + { + PrintParameterModifier.RetractHeight2.Value = (decimal)layer.RetractHeight2; + } + + if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.RetractSpeed2)) + { + PrintParameterModifier.RetractSpeed2.Value = (decimal)layer.RetractSpeed2; + } + if (PrintParameterPerLayerModifiers.Contains(PrintParameterModifier.LightPWM)) { PrintParameterModifier.LightPWM.Value = layer.LightPWM; @@ -2159,14 +2602,36 @@ namespace UVtools.Core.FileFormats if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed)) return LiftSpeed; + if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight2)) + return BottomLiftHeight2; + if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight2)) + return LiftHeight2; + if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed2)) + return BottomLiftSpeed2; + if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed2)) + return LiftSpeed2; + if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterLift)) return BottomWaitTimeAfterLift; if (ReferenceEquals(modifier, PrintParameterModifier.WaitTimeAfterLift)) return WaitTimeAfterLift; + if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed)) + return BottomRetractSpeed; if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) return RetractSpeed; + if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractHeight2)) + return BottomRetractHeight2; + if (ReferenceEquals(modifier, PrintParameterModifier.RetractHeight2)) + return RetractHeight2; + if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed2)) + return BottomRetractSpeed2; + if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed2)) + return RetractSpeed2; + + + if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) return BottomLightPWM; if (ReferenceEquals(modifier, PrintParameterModifier.LightPWM)) @@ -2254,6 +2719,27 @@ namespace UVtools.Core.FileFormats return true; } + if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftHeight2)) + { + BottomLiftHeight2 = (float)value; + return true; + } + if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight2)) + { + LiftHeight2 = (float)value; + return true; + } + if (ReferenceEquals(modifier, PrintParameterModifier.BottomLiftSpeed2)) + { + BottomLiftSpeed2 = (float)value; + return true; + } + if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed2)) + { + LiftSpeed2 = (float)value; + return true; + } + if (ReferenceEquals(modifier, PrintParameterModifier.BottomWaitTimeAfterLift)) { BottomWaitTimeAfterLift = (float)value; @@ -2265,12 +2751,41 @@ namespace UVtools.Core.FileFormats return true; } + if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed)) + { + BottomRetractSpeed = (float)value; + return true; + } + if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed)) { RetractSpeed = (float) value; return true; } + if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractHeight2)) + { + BottomRetractHeight2 = (float)value; + return true; + } + + if (ReferenceEquals(modifier, PrintParameterModifier.RetractHeight2)) + { + RetractHeight2 = (float)value; + return true; + } + if (ReferenceEquals(modifier, PrintParameterModifier.BottomRetractSpeed2)) + { + BottomRetractSpeed2 = (float)value; + return true; + } + + if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed2)) + { + RetractSpeed2 = (float)value; + return true; + } + if (ReferenceEquals(modifier, PrintParameterModifier.BottomLightPWM)) { BottomLightPWM = (byte)value; @@ -2315,17 +2830,15 @@ namespace UVtools.Core.FileFormats public float CalculateMotorMovementTime(bool isBottomLayer, float extraTime = 0) { return isBottomLayer - ? OperationCalculator.LightOffDelayC.CalculateSeconds(BottomLiftHeight, BottomLiftSpeed, RetractSpeed, extraTime) - : OperationCalculator.LightOffDelayC.CalculateSeconds(LiftHeight, LiftSpeed, RetractSpeed, extraTime); + ? OperationCalculator.LightOffDelayC.CalculateSeconds(BottomLiftHeight, BottomLiftSpeed, BottomRetractSpeed, extraTime, BottomLiftHeight2, BottomLiftSpeed2, BottomRetractHeight2, BottomRetractSpeed2) + : OperationCalculator.LightOffDelayC.CalculateSeconds(LiftHeight, LiftSpeed, RetractSpeed, extraTime, LiftHeight2, LiftSpeed2, RetractHeight2, RetractSpeed2); } public float CalculateLightOffDelay(bool isBottomLayer, float extraTime = 0) { extraTime = (float)Math.Round(extraTime, 2); if (SupportsGCode) return extraTime; - return isBottomLayer - ? OperationCalculator.LightOffDelayC.CalculateSeconds(BottomLiftHeight, BottomLiftSpeed, RetractSpeed, extraTime) - : OperationCalculator.LightOffDelayC.CalculateSeconds(LiftHeight, LiftSpeed, RetractSpeed, extraTime); + return CalculateMotorMovementTime(isBottomLayer, extraTime); } public bool SetLightOffDelay(bool isBottomLayer, float extraTime = 0) @@ -2415,13 +2928,23 @@ namespace UVtools.Core.FileFormats // Lift slicerFile.BottomLiftHeight = BottomLiftHeight; - slicerFile.LiftHeight = LiftHeight; - slicerFile.BottomLiftSpeed = BottomLiftSpeed; + slicerFile.LiftHeight = LiftHeight; slicerFile.LiftSpeed = LiftSpeed; - + + slicerFile.BottomLiftHeight2 = BottomLiftHeight2; + slicerFile.BottomLiftSpeed2 = BottomLiftSpeed2; + slicerFile.LiftHeight2 = LiftHeight2; + slicerFile.LiftSpeed2 = LiftSpeed2; + + slicerFile.BottomRetractSpeed = BottomRetractSpeed; slicerFile.RetractSpeed = RetractSpeed; + slicerFile.BottomRetractHeight2 = BottomRetractHeight2; + slicerFile.BottomRetractSpeed2 = BottomRetractSpeed2; + slicerFile.RetractHeight2 = RetractHeight2; + slicerFile.RetractSpeed2 = RetractSpeed2; + // Wait times slicerFile.BottomLightOffDelay = BottomLightOffDelay; slicerFile.LightOffDelay = LightOffDelay; @@ -2526,6 +3049,50 @@ namespace UVtools.Core.FileFormats return result; } + public void UpdateGlobalPropertiesFromLayers() + { + if (LayerCount == 0) return; + + SuppressRebuildPropertiesWork(() => + { + var bottomLayer = FirstLayer; + if (bottomLayer is not null) + { + if (bottomLayer.LightOffDelay > 0) BottomLightOffDelay = bottomLayer.LightOffDelay; + if (bottomLayer.WaitTimeBeforeCure > 0) BottomWaitTimeBeforeCure = bottomLayer.WaitTimeBeforeCure; + if (bottomLayer.ExposureTime > 0) BottomExposureTime = bottomLayer.ExposureTime; + if (bottomLayer.WaitTimeAfterCure > 0) BottomWaitTimeAfterCure = bottomLayer.WaitTimeAfterCure; + if (bottomLayer.LiftHeight > 0) BottomLiftHeight = bottomLayer.LiftHeight; + if (bottomLayer.LiftSpeed > 0) BottomLiftSpeed = bottomLayer.LiftSpeed; + if (bottomLayer.LiftHeight2 > 0) BottomLiftHeight2 = bottomLayer.LiftHeight2; + if (bottomLayer.LiftSpeed2 > 0) BottomLiftSpeed2 = bottomLayer.LiftSpeed2; + if (bottomLayer.WaitTimeAfterLift > 0) BottomWaitTimeAfterLift = bottomLayer.WaitTimeAfterLift; + if (bottomLayer.RetractSpeed > 0) BottomRetractSpeed = bottomLayer.RetractSpeed; + if (bottomLayer.RetractHeight2 > 0) BottomRetractHeight2 = bottomLayer.RetractHeight2; + if (bottomLayer.RetractSpeed2 > 0) BottomRetractSpeed2 = bottomLayer.RetractSpeed2; + if (bottomLayer.LightPWM > 0) BottomLightPWM = bottomLayer.LightPWM; + } + + var normalLayer = LastLayer; + if (normalLayer is not null) + { + if (normalLayer.LightOffDelay > 0) LightOffDelay = normalLayer.LightOffDelay; + if (normalLayer.WaitTimeBeforeCure > 0) WaitTimeBeforeCure = normalLayer.WaitTimeBeforeCure; + if (normalLayer.ExposureTime > 0) ExposureTime = normalLayer.ExposureTime; + if (normalLayer.WaitTimeAfterCure > 0) WaitTimeAfterCure = normalLayer.WaitTimeAfterCure; + if (normalLayer.LiftHeight > 0) LiftHeight = normalLayer.LiftHeight; + if (normalLayer.LiftSpeed > 0) LiftSpeed = normalLayer.LiftSpeed; + if (normalLayer.LiftHeight2 > 0) LiftHeight2 = normalLayer.LiftHeight2; + if (normalLayer.LiftSpeed2 > 0) LiftSpeed2 = normalLayer.LiftSpeed2; + if (normalLayer.WaitTimeAfterLift > 0) WaitTimeAfterLift = normalLayer.WaitTimeAfterLift; + if (normalLayer.RetractSpeed > 0) RetractSpeed = normalLayer.RetractSpeed; + if (normalLayer.RetractHeight2 > 0) RetractHeight2 = normalLayer.RetractHeight2; + if (normalLayer.RetractSpeed2 > 0) RetractSpeed2 = normalLayer.RetractSpeed2; + if (normalLayer.LightPWM > 0) LightPWM = normalLayer.LightPWM; + } + }); + } + public void UpdatePrintTime() { PrintTime = PrintTimeComputed; @@ -2534,8 +3101,11 @@ namespace UVtools.Core.FileFormats public void UpdatePrintTimeQueued() { - _queueTimerPrintTime.Stop(); - _queueTimerPrintTime.Start(); + lock (Mutex) + { + _queueTimerPrintTime.Stop(); + _queueTimerPrintTime.Start(); + } } #endregion diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs index fdf8ef2..4e977eb 100644 --- a/UVtools.Core/FileFormats/GR1File.cs +++ b/UVtools.Core/FileFormats/GR1File.cs @@ -332,6 +332,8 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = SlicerInfoSettings.LiftSpeed = (ushort)value; } + public override float BottomRetractSpeed => RetractSpeed; + public override float RetractSpeed { get => SlicerInfoSettings.RetractSpeed; diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index 5ecbe22..78e09b1 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -92,28 +92,32 @@ namespace UVtools.Core.FileFormats [FieldOrder(1)] [FieldLength(nameof(DataSize))] - public byte[] EncodedRle { get; set; } + public byte[] PngBytes { get; set; } + + [FieldOrder(2)] public ushort Padding { get; set; } public void Encode(Mat mat) { + mat ??= EmguExtensions.InitMat(new Size(ResolutionX, ResolutionY), 3); + if (mat.Width != ResolutionX || mat.Height != ResolutionY) { using var resizeMat = new Mat(); CvInvoke.Resize(mat, resizeMat, new Size(ResolutionX, ResolutionY)); - EncodedRle = resizeMat.GetPngByes(); + PngBytes = resizeMat.GetPngByes(); } else { - EncodedRle = mat.GetPngByes(); + PngBytes = mat.GetPngByes(); } } public Mat Decode(bool consumeRle = true) { var mat = new Mat(); - CvInvoke.Imdecode(EncodedRle, ImreadModes.AnyColor, mat); + CvInvoke.Imdecode(PngBytes, ImreadModes.AnyColor, mat); if (consumeRle) - EncodedRle = null; + PngBytes = null; return mat; } } @@ -133,10 +137,23 @@ namespace UVtools.Core.FileFormats [FieldLength(nameof(DataSize))] public byte[] EncodedRle { get; set; } + public LayerData() { } + + public LayerData(LGSFile parent) + { + Parent = parent; + } + public unsafe byte[] Encode(Mat mat) { List<byte> rawData = new(); List<byte> chunk = new(); + + if (Parent.HeaderSettings.PrinterModel is 4000 or 4500) + { + CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90Clockwise); + } + var spanMat = mat.GetBytePointer(); var imageLength = mat.GetLength(); @@ -171,65 +188,66 @@ namespace UVtools.Core.FileFormats addSpan(); EncodedRle = rawData.ToArray(); DataSize = (uint) EncodedRle.Length; + + if (Parent.HeaderSettings.PrinterModel is 4000 or 4500) + { + CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise); + } + return EncodedRle; } - public unsafe Mat Decode(bool consumeRle = true) + public Mat Decode(bool consumeRle = true) { - var mat = EmguExtensions.InitMat(Parent.Resolution); - var matSpan = mat.GetBytePointer(); + // lgs10/30 --------> + // lgs120/4k From Y bottom to top Y + var mat = EmguExtensions.InitMat(Parent.HeaderSettings.PrinterModel is 4000 or 4500 ? Parent.Resolution.Exchange() : Parent.Resolution); + //var matSpan = mat.GetBytePointer(); var imageLength = mat.GetLength(); + + int pixelPos = 0; - byte last = 0; - int span = 0; - int index = 0; - - foreach (var b in EncodedRle) + for (var i = 0; i < EncodedRle.Length; i++) { - byte color = (byte) ((b & 0xf0) | (b >> 4)); + var b = EncodedRle[i]; + byte colorNibble = (byte)(b >> 4); + byte color = (byte)(colorNibble << 0x4 | colorNibble); + int repeat = b & 0xf; - if (color == last) + while (i + 1 < EncodedRle.Length && (EncodedRle[i + 1] >> 4) == colorNibble) { - span = (span << 4) | (b & 0xf); + i++; + repeat = (repeat << 4) | (EncodedRle[i] & 0xf); } - else - { - for(; span > 0; span--) - { - if (index >= imageLength) - { - throw new FileLoadException($"'{span}' bytes to many"); - } - - matSpan[index++] = last; - } - - span = b & 0xf; + if (pixelPos >= imageLength) + { + throw new FileLoadException($"Too much buffer, expected: {imageLength}, got: {pixelPos}"); } - last = color; - } + mat.FillSpan(ref pixelPos, repeat, color); - for (; span > 0; span--) - { - if (index >= imageLength) + //if (repeat <= 0) continue; + /*while (repeat-- > 0) { - throw new FileLoadException($"'{span}' bytes to many"); - } + matSpan[pixel++] = color; + }*/ - matSpan[index++] = last; } - if (index != imageLength) + if (pixelPos != imageLength) { - throw new FileLoadException($"Incomplete buffer, expected: {imageLength}, got: {index}"); + throw new FileLoadException($"Incomplete buffer, expected: {imageLength}, got: {pixelPos}"); } - if (consumeRle) EncodedRle = null; + if (Parent.HeaderSettings.PrinterModel is 4000 or 4500) + { + CvInvoke.Rotate(mat, mat, RotateFlags.Rotate90CounterClockwise); + } + return mat; } } @@ -245,8 +263,8 @@ namespace UVtools.Core.FileFormats public override FileExtension[] FileExtensions { get; } = { new ("lgs", "Longer Orange 10"), new ("lgs30", "Longer Orange 30"), - //new ("lgs120", "Longer Orange 120"), - //new ("lgs4k", "Longer Orange 4k"), + new ("lgs120", "Longer Orange 120"), + new ("lgs4k", "Longer Orange 4k"), }; public override PrintParameterModifier[] PrintParameterModifiers { get; } = @@ -448,6 +466,8 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = HeaderSettings.LiftSpeed = HeaderSettings.LiftSpeed_ = value; } + public override float BottomRetractSpeed => RetractSpeed; + public override float RetractSpeed => LiftSpeed; /*public override float PrintTime => 0; @@ -548,6 +568,15 @@ namespace UVtools.Core.FileFormats outputFile.WriteSerialize(HeaderSettings); outputFile.WriteBytes(PreviewEncode(Thumbnails[0])); + if (HeaderSettings.PrinterModel == 120) + { + // Insert PNG here + var mat = GetThumbnail(true); + var pngPreview = new LGS120PngPreview(); + pngPreview.Encode(mat); + outputFile.WriteSerialize(pngPreview); + } + var layerData = new LayerData[LayerCount]; Parallel.For(0, LayerCount, layerIndex => @@ -555,7 +584,7 @@ namespace UVtools.Core.FileFormats if (progress.Token.IsCancellationRequested) return; using (var mat = this[layerIndex].LayerMat) { - layerData[layerIndex] = new LayerData(); + layerData[layerIndex] = new LayerData(this); layerData[layerIndex].Encode(mat); } @@ -587,13 +616,14 @@ namespace UVtools.Core.FileFormats throw new FileLoadException("Not a valid LGS file!", fileFullPath); } - // Fix inconsistencies found of different version of plugin and slicers - if (ResolutionX > ResolutionY) - { - var oldX = ResolutionX; - ResolutionX = ResolutionY; - ResolutionY = oldX; - } + //if (HeaderSettings.PrinterModel is 10 or 30 or 120) + //{ + // Fix inconsistencies found of different version of plugin and slicers + if (ResolutionX > ResolutionY) + { + (ResolutionX, ResolutionY) = (ResolutionY, ResolutionX); + } + //} int previewSize = (int) (HeaderSettings.PreviewSizeX * HeaderSettings.PreviewSizeY * 2); byte[] previewData = new byte[previewSize]; @@ -603,13 +633,13 @@ namespace UVtools.Core.FileFormats currentOffset += inputFile.ReadBytes(previewData); Thumbnails[0] = PreviewDecode(previewData); - if (FileEndsWith(".lgs120")) + if (HeaderSettings.PrinterModel == 120) { var pngPreview = Helpers.Deserialize<LGS120PngPreview>(inputFile); } - LayerData[] layerData = new LayerData[HeaderSettings.LayerCount]; + var layerData = new LayerData[HeaderSettings.LayerCount]; progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.LayerCount); for (int layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) @@ -656,12 +686,9 @@ namespace UVtools.Core.FileFormats FileFullPath = filePath; } - using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) - { - - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - } + using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(0, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); } #endregion diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs index 94d956d..d513036 100644 --- a/UVtools.Core/FileFormats/OSLAFile.cs +++ b/UVtools.Core/FileFormats/OSLAFile.cs @@ -98,7 +98,7 @@ namespace UVtools.Core.FileFormats [FieldOrder(11)] public float LayerHeight { get; set; } = 0.05f; [FieldOrder(12)] public ushort BottomLayersCount { get; set; } = 4; [FieldOrder(13)] public uint LayerCount { get; set; } - [FieldOrder(14)] public uint LayerTableSize { get; set; } = 4; + [FieldOrder(14)] public uint LayerTableSize { get; set; } = 69; [FieldOrder(15)] public uint LayerDefinitionsAddress { get; set; } [FieldOrder(16)] public uint GCodeAddress { get; set; } [FieldOrder(17)] public uint PrintTime { get; set; } @@ -107,6 +107,7 @@ namespace UVtools.Core.FileFormats [FieldOrder(20)] [FieldLength(50)] [SerializeAs(SerializedType.TerminatedString)] public string MaterialName { get; set; } [FieldOrder(21)] [FieldLength(50)] [SerializeAs(SerializedType.TerminatedString)] public string MachineName { get; set; } = "Unknown"; + public override string ToString() { return $"{nameof(TableSize)}: {TableSize}, {nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(MachineZ)}: {MachineZ}, {nameof(DisplayWidth)}: {DisplayWidth}, {nameof(DisplayHeight)}: {DisplayHeight}, {nameof(DisplayMirror)}: {DisplayMirror}, {nameof(PreviewDataType)}: {PreviewDataType}, {nameof(LayerDataType)}: {LayerDataType}, {nameof(PreviewTableSize)}: {PreviewTableSize}, {nameof(PreviewCount)}: {PreviewCount}, {nameof(LayerTableSize)}: {LayerTableSize}, {nameof(BottomLayersCount)}: {BottomLayersCount}, {nameof(LayerCount)}: {LayerCount}, {nameof(LayerDefinitionsAddress)}: {LayerDefinitionsAddress}, {nameof(GCodeAddress)}: {GCodeAddress}, {nameof(PrintTime)}: {PrintTime}, {nameof(MaterialMilliliters)}: {MaterialMilliliters}, {nameof(MaterialCost)}: {MaterialCost}, {nameof(MaterialName)}: {MaterialName}, {nameof(MachineName)}: {MachineName}"; @@ -161,17 +162,68 @@ namespace UVtools.Core.FileFormats #region Layer public class LayerDef { - [FieldOrder(0)] public uint DataAddress { get; set; } - - [Ignore] public byte[] ImageData { get; set; } + //[FieldOrder(0)] public uint DataAddress { get; set; } + + [FieldOrder(1)] public float PositionZ { get; set; } + [FieldOrder(2)] public float LiftHeight { get; set; } + [FieldOrder(3)] public float LiftSpeed { get; set; } + [FieldOrder(4)] public float LiftHeight2 { get; set; } + [FieldOrder(5)] public float LiftSpeed2 { get; set; } + [FieldOrder(6)] public float WaitTimeAfterLift { get; set; } + [FieldOrder(7)] public float RetractSpeed { get; set; } + [FieldOrder(8)] public float RetractHeight2 { get; set; } + [FieldOrder(9)] public float RetractSpeed2 { get; set; } + [FieldOrder(10)] public float WaitTimeBeforeCure { get; set; } + [FieldOrder(11)] public float ExposureTime { get; set; } + [FieldOrder(12)] public float WaitTimeAfterCure { get; set; } + [FieldOrder(13)] public byte LightPWM { get; set; } + [FieldOrder(14)] public uint BoundingRectangleX { get; set; } + [FieldOrder(15)] public uint BoundingRectangleY { get; set; } + [FieldOrder(16)] public uint BoundingRectangleWidth { get; set; } + [FieldOrder(17)] public uint BoundingRectangleHeight { get; set; } + + //[Ignore] public byte[] ImageData { get; set; } public LayerDef() { } - public override string ToString() + public LayerDef(Layer layer) { - return $"{nameof(DataAddress)}: {DataAddress}, {nameof(ImageData)}: {ImageData.Length}"; + PositionZ = layer.PositionZ; + LiftHeight = layer.LiftHeight; + LiftSpeed = layer.LiftSpeed; + LiftHeight2 = layer.LiftHeight2; + LiftSpeed2 = layer.LiftSpeed2; + WaitTimeAfterLift = layer.WaitTimeAfterLift; + RetractSpeed = layer.RetractSpeed; + RetractHeight2 = layer.RetractHeight2; + RetractSpeed2 = layer.RetractSpeed2; + WaitTimeBeforeCure = layer.WaitTimeBeforeCure; + ExposureTime = layer.ExposureTime; + WaitTimeAfterCure = layer.WaitTimeAfterCure; + LightPWM = layer.LightPWM; + BoundingRectangleX = (uint)layer.BoundingRectangle.X; + BoundingRectangleY = (uint)layer.BoundingRectangle.Y; + BoundingRectangleWidth = (uint)layer.BoundingRectangle.Width; + BoundingRectangleHeight = (uint)layer.BoundingRectangle.Height; + } + + public void SetTo(Layer layer) + { + layer.PositionZ = PositionZ; + layer.LiftHeight = LiftHeight; + layer.LiftSpeed = LiftSpeed; + layer.LiftHeight2 = LiftHeight2; + layer.LiftSpeed2 = LiftSpeed2; + layer.WaitTimeAfterLift = WaitTimeAfterLift; + layer.RetractSpeed = RetractSpeed; + layer.RetractHeight2 = RetractHeight2; + layer.RetractSpeed2 = RetractSpeed2; + layer.WaitTimeBeforeCure = WaitTimeBeforeCure; + layer.ExposureTime = ExposureTime; + layer.WaitTimeAfterCure = WaitTimeAfterCure; + layer.LightPWM = LightPWM; } } #endregion @@ -205,8 +257,7 @@ namespace UVtools.Core.FileFormats //new ("odlp", "Open DLP universal binary file"), }; - public override PrintParameterModifier[] PrintParameterModifiers { get; } = - { + public override PrintParameterModifier[] PrintParameterModifiers { get; } = { PrintParameterModifier.BottomLayerCount, PrintParameterModifier.BottomWaitTimeBeforeCure, @@ -223,11 +274,21 @@ namespace UVtools.Core.FileFormats 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, @@ -239,8 +300,12 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.WaitTimeAfterCure, PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, + PrintParameterModifier.LiftHeight2, + PrintParameterModifier.LiftSpeed2, PrintParameterModifier.WaitTimeAfterLift, PrintParameterModifier.RetractSpeed, + PrintParameterModifier.RetractHeight2, + PrintParameterModifier.RetractSpeed2, PrintParameterModifier.LightPWM, }; @@ -464,7 +529,7 @@ namespace UVtools.Core.FileFormats outputFile.Seek(HeaderSettings.LayerTableSize * LayerCount, SeekOrigin.Current); // Start of layer data var layerHash = new Dictionary<string, uint>(); - + var range = Enumerable.Range(0, (int)LayerCount); foreach (var batch in range.Batch(Environment.ProcessorCount * 10)) { @@ -496,23 +561,30 @@ namespace UVtools.Core.FileFormats layerHash.Add(hash, layerDataAddresses[layerIndex]); } - - layerBytes[layerIndex] = null; // Clean } } HeaderSettings.GCodeAddress = (uint)outputFile.Position; + uint layerTableSize = 0; outputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin); - for (int i = 0; i < layerDataAddresses.Length; i++) + for (int layerIndex = 0; layerIndex < layerDataAddresses.Length; layerIndex++) { progress.Token.ThrowIfCancellationRequested(); - outputFile.WriteUIntLittleEndian(layerDataAddresses[i]); - // Need to fill what we don't know - if (HeaderSettings.LayerTableSize > 4) + + var layer = this[layerIndex]; + var layerdef = new LayerDef(layer); + + outputFile.WriteUIntLittleEndian(layerDataAddresses[layerIndex]); + Helpers.SerializeWriteFileStream(outputFile, layerdef); + if (layerTableSize == 0) { - outputFile.Seek(HeaderSettings.LayerTableSize - 4, SeekOrigin.Current); + layerTableSize = 4 + (uint)Helpers.Serializer.SizeOf(layerdef); + } + if (HeaderSettings.LayerTableSize > layerTableSize) + { + outputFile.Seek(HeaderSettings.LayerTableSize - layerTableSize, SeekOrigin.Current); } } @@ -581,15 +653,25 @@ namespace UVtools.Core.FileFormats inputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin); LayerManager.Init(HeaderSettings.LayerCount); + var layerDef = new LayerDef[LayerCount]; + + progress.Reset(OperationProgress.StatusGatherLayers, HeaderSettings.LayerCount); uint[] layerDataAddresses = new uint[LayerCount]; + uint layerTableSize = 0; for (uint layerIndex = 0; layerIndex < HeaderSettings.LayerCount; layerIndex++) { progress.Token.ThrowIfCancellationRequested(); layerDataAddresses[layerIndex] = inputFile.ReadUIntLittleEndian(); - if (HeaderSettings.LayerTableSize > 4) + + layerDef[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile); + if (layerTableSize == 0) { - inputFile.Seek(HeaderSettings.LayerTableSize - 4, SeekOrigin.Current); + layerTableSize = 4 + (uint)Helpers.Serializer.SizeOf(layerDef[layerIndex]); + } + if (HeaderSettings.LayerTableSize > layerTableSize) + { + inputFile.Seek(HeaderSettings.LayerTableSize - layerTableSize, SeekOrigin.Current); } progress++; @@ -615,7 +697,9 @@ namespace UVtools.Core.FileFormats { if (progress.Token.IsCancellationRequested) return; using var mat = DecodeImage(layerBytes[layerIndex], HeaderSettings.LayerDataType, Resolution); - this[layerIndex] = new Layer((uint)layerIndex, mat, this); + var layer = new Layer((uint)layerIndex, mat, this); + layerDef[layerIndex].SetTo(layer); + this[layerIndex] = layer; layerBytes[layerIndex] = null; // Clean progress.LockAndIncrement(); @@ -626,7 +710,9 @@ namespace UVtools.Core.FileFormats inputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin); var gcodeDef = Helpers.Deserialize<GCodeDef>(inputFile); GCodeStr = gcodeDef.GCodeText; - GCode.ParseLayersFromGCode(this); + //GCode.ParseLayersFromGCode(this); + + UpdateGlobalPropertiesFromLayers(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) @@ -653,13 +739,23 @@ namespace UVtools.Core.FileFormats Helpers.SerializeWriteFileStream(outputFile, FileSettings); Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - outputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin); - outputFile.SetLength(HeaderSettings.GCodeAddress); + outputFile.Seek(HeaderSettings.LayerDefinitionsAddress, SeekOrigin.Begin); + foreach (var layer in this) + { + outputFile.Seek(4, SeekOrigin.Current); // skip address + Helpers.SerializeWriteFileStream(outputFile, new LayerDef(layer)); // Update layer values + } - RebuildGCode(); - var gcodeSettings = new GCodeDef {GCodeText = GCodeStr}; - gcodeSettings.GCodeSize = (uint) gcodeSettings.GCodeText.Length; - Helpers.SerializeWriteFileStream(outputFile, gcodeSettings); + if (HeaderSettings.GCodeAddress > 0) + { + outputFile.Seek(HeaderSettings.GCodeAddress, SeekOrigin.Begin); + outputFile.SetLength(HeaderSettings.GCodeAddress); + + RebuildGCode(); + var gcodeSettings = new GCodeDef { GCodeText = GCodeStr }; + gcodeSettings.GCodeSize = (uint)gcodeSettings.GCodeText.Length; + Helpers.SerializeWriteFileStream(outputFile, gcodeSettings); + } } #endregion diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index 65e7819..9f7f063 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -891,6 +891,8 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = HeaderSettings.LiftSpeed = (float)Math.Round(value, 2); } + public override float BottomRetractSpeed => RetractSpeed; + public override float RetractSpeed { get => HeaderSettings.RetractSpeed; diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index 21436be..429cdf2 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -165,44 +165,52 @@ namespace UVtools.Core.FileFormats public unsafe Mat Decode(bool consumeRle = true) { var mat = EmguExtensions.InitMat(new Size((int) ResolutionX, (int) ResolutionY)); - var matSpan = mat.GetBytePointer(); + //var matSpan = mat.GetBytePointer(); var imageLength = mat.GetLength(); - uint pixel = 0; + int pixelPos = 0; foreach (var run in EncodedRle) { + if (pixelPos > imageLength) + { + mat.Dispose(); + throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels."); + } + byte brightness = (byte) ((run & 0x01) * 255); - uint numPixelsInRun = - (uint) ((((run & 128) > 0 ? 1 : 0) | - ((run & 64) > 0 ? 2 : 0) | - ((run & 32) > 0 ? 4 : 0) | - ((run & 16) > 0 ? 8 : 0) | - ((run & 8) > 0 ? 16 : 0) | - ((run & 4) > 0 ? 32 : 0) | - ((run & 2) > 0 ? 64 : 0)) + 1); + int numPixelsInRun = + (((run & 128) > 0 ? 1 : 0) | + ((run & 64) > 0 ? 2 : 0) | + ((run & 32) > 0 ? 4 : 0) | + ((run & 16) > 0 ? 8 : 0) | + ((run & 8) > 0 ? 16 : 0) | + ((run & 4) > 0 ? 32 : 0) | + ((run & 2) > 0 ? 64 : 0)) + 1; - if (brightness == 0) // Don't fill black pixels + mat.FillSpan(ref pixelPos, numPixelsInRun, brightness); + + /*if (brightness == 0) // Don't fill black pixels { - pixel += numPixelsInRun; + pixelPos += numPixelsInRun; continue; } for (; numPixelsInRun > 0; numPixelsInRun--) { - if (pixel > imageLength) + if (pixelPos > imageLength) { mat.Dispose(); throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels."); } - matSpan[pixel++] = brightness; - } + matSpan[pixelPos++] = brightness; + }*/ } - if (pixel != imageLength && pixel-1 != imageLength) + if (pixelPos != imageLength && pixelPos-1 != imageLength) { mat.Dispose(); - throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {pixel} pixels."); + throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {pixelPos} pixels."); } // Not required as mat is all black by default @@ -369,6 +377,8 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = (float) (HeaderSettings.LiftSpeed = Math.Round(value / 60.0, 2)); } + public override float BottomRetractSpeed => RetractSpeed; + public override float RetractSpeed { get => (float)Math.Round(HeaderSettings.RetractSpeed * 60.0, 2); diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index b442a57..5fcf0bb 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -1142,6 +1142,8 @@ namespace UVtools.Core.FileFormats } } + public override float BottomRetractSpeed => RetractSpeed; + public override float RetractSpeed { get => (float)Math.Round(HeaderSettings.RetractSpeed * 60, 2); diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index 763d021..4481200 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -637,8 +637,8 @@ namespace UVtools.Core.FileFormats SuppressRebuildPropertiesWork(() => { - BottomLightOffDelay = LookupCustomValue(Keyword_BottomLightOffDelay, DefaultBottomLightOffDelay); - LightOffDelay = LookupCustomValue(Keyword_LightOffDelay, DefaultLightOffDelay); + BottomLightOffDelay = LookupCustomValue(Keyword_BottomLightOffDelay, 0f); + LightOffDelay = LookupCustomValue(Keyword_LightOffDelay, 0f); BottomWaitTimeBeforeCure = LookupCustomValue(Keyword_BottomWaitTimeBeforeCure, 0f); WaitTimeBeforeCure = LookupCustomValue(Keyword_WaitTimeBeforeCure, 0f); diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs index 24e160f..b7cefd5 100644 --- a/UVtools.Core/FileFormats/UVJFile.cs +++ b/UVtools.Core/FileFormats/UVJFile.cs @@ -146,6 +146,7 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.BottomLiftSpeed, PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, + PrintParameterModifier.BottomRetractSpeed, PrintParameterModifier.RetractSpeed, PrintParameterModifier.BottomLightPWM, @@ -313,6 +314,12 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = JsonSettings.Properties.Exposure.LiftSpeed = (float)Math.Round(value, 2); } + public override float BottomRetractSpeed + { + get => JsonSettings.Properties.Bottom.RetractSpeed; + set => base.BottomRetractSpeed = JsonSettings.Properties.Bottom.RetractSpeed = (float)Math.Round(value, 2); + } + public override float RetractSpeed { get => JsonSettings.Properties.Exposure.RetractSpeed; diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs index 9dfc917..ae4c366 100644 --- a/UVtools.Core/FileFormats/VDTFile.cs +++ b/UVtools.Core/FileFormats/VDTFile.cs @@ -97,22 +97,30 @@ namespace UVtools.Core.FileFormats { [JsonProperty("layer_thickness")] public float LayerHeight { get; set; } [JsonProperty("bottom_layers")] public ushort BottomLayers { get; set; } = DefaultBottomLayerCount; - [JsonProperty("bottom_light_off_delay")] public float BottomLightOffDelay { get; set; } = DefaultBottomLightOffDelay; - [JsonProperty("light_off_delay")] public float LightOffDelay { get; set; } = DefaultLightOffDelay; - [JsonProperty("bottom_wait_time_before_cure")] public float BottomWaitTimeBeforeCure { get; set; } = DefaultBottomLightOffDelay; - [JsonProperty("wait_time_before_cure")] public float WaitTimeBeforeCure { get; set; } = DefaultLightOffDelay; + [JsonProperty("bottom_light_off_delay")] public float BottomLightOffDelay { get; set; } + [JsonProperty("light_off_delay")] public float LightOffDelay { get; set; } + [JsonProperty("bottom_wait_time_before_cure")] public float BottomWaitTimeBeforeCure { get; set; } + [JsonProperty("wait_time_before_cure")] public float WaitTimeBeforeCure { get; set; } [JsonProperty("bottom_exposure_time")] public float BottomExposureTime { get; set; } = DefaultBottomExposureTime; [JsonProperty("exposure_time")] public float ExposureTime { get; set; } = DefaultExposureTime; - [JsonProperty("bottom_wait_time_after_cure")] public float BottomWaitTimeAfterCure { get; set; } = DefaultBottomLightOffDelay; - [JsonProperty("wait_time_after_cure")] public float WaitTimeAfterCure { get; set; } = DefaultLightOffDelay; + [JsonProperty("bottom_wait_time_after_cure")] public float BottomWaitTimeAfterCure { get; set; } + [JsonProperty("wait_time_after_cure")] public float WaitTimeAfterCure { get; set; } [JsonProperty("bottom_lift_distance")] public float BottomLiftHeight { get; set; } = DefaultBottomLiftHeight; - [JsonProperty("lift_distance")] public float LiftHeight { get; set; } = DefaultLiftHeight; [JsonProperty("bottom_lift_speed")] public float BottomLiftSpeed { get; set; } = DefaultBottomLiftSpeed; + [JsonProperty("lift_distance")] public float LiftHeight { get; set; } = DefaultLiftHeight; [JsonProperty("lift_speed")] public float LiftSpeed { get; set; } = DefaultLiftSpeed; - [JsonProperty("bottom_retract_speed")] public float BottomRetractSpeed { get; set; } = DefaultRetractSpeed; - [JsonProperty("bottom_wait_time_after_lift")] public float BottomWaitTimeAfterLift { get; set; } = DefaultBottomLightOffDelay; - [JsonProperty("wait_time_after_lift")] public float WaitTimeAfterLift { get; set; } = DefaultLightOffDelay; + [JsonProperty("bottom_lift_distance2")] public float BottomLiftHeight2 { get; set; } = DefaultBottomLiftHeight2; + [JsonProperty("bottom_lift_speed2")] public float BottomLiftSpeed2 { get; set; } = DefaultBottomLiftSpeed2; + [JsonProperty("lift_distance2")] public float LiftHeight2 { get; set; } = DefaultLiftHeight2; + [JsonProperty("lift_speed2")] public float LiftSpeed2 { get; set; } = DefaultLiftSpeed2; + [JsonProperty("bottom_wait_time_after_lift")] public float BottomWaitTimeAfterLift { get; set; } + [JsonProperty("wait_time_after_lift")] public float WaitTimeAfterLift { get; set; } + [JsonProperty("bottom_retract_speed")] public float BottomRetractSpeed { get; set; } = DefaultBottomRetractSpeed; [JsonProperty("retract_speed")] public float RetractSpeed { get; set; } = DefaultRetractSpeed; + [JsonProperty("bottom_retract_height2")] public float BottomRetractHeight2 { get; set; } = DefaultBottomRetractHeight2; + [JsonProperty("bottom_retract_speed2")] public float BottomRetractSpeed2 { get; set; } = DefaultBottomRetractSpeed2; + [JsonProperty("retract_height2")] public float RetractHeight2 { get; set; } = DefaultRetractHeight2; + [JsonProperty("retract_speed2")] public float RetractSpeed2 { get; set; } = DefaultRetractSpeed2; } public sealed class VDTPrintStatistics @@ -127,14 +135,18 @@ namespace UVtools.Core.FileFormats public sealed class VDTLayer { [JsonProperty("height")] public float PositionZ { get; set; } - [JsonProperty("light_off_delay")] public float LightOffDelay { get; set; } = DefaultLightOffDelay; - [JsonProperty("wait_time_before_cure")] public float WaitTimeBeforeCure { get; set; } = DefaultLightOffDelay; + [JsonProperty("light_off_delay")] public float LightOffDelay { get; set; } + [JsonProperty("wait_time_before_cure")] public float WaitTimeBeforeCure { get; set; } [JsonProperty("exposure_time")] public float ExposureTime { get; set; } = DefaultExposureTime; - [JsonProperty("wait_time_after_cure")] public float WaitTimeAfterCure { get; set; } = DefaultLightOffDelay; + [JsonProperty("wait_time_after_cure")] public float WaitTimeAfterCure { get; set; } [JsonProperty("lift_distance")] public float LiftHeight { get; set; } = DefaultLiftHeight; [JsonProperty("lift_speed")] public float LiftSpeed { get; set; } = DefaultLiftSpeed; - [JsonProperty("wait_time_after_lift")] public float WaitTimeAfterLift { get; set; } = DefaultLightOffDelay; + [JsonProperty("lift_distance2")] public float LiftHeight2 { get; set; } = DefaultLiftHeight2; + [JsonProperty("lift_speed2")] public float LiftSpeed2 { get; set; } = DefaultLiftSpeed2; + [JsonProperty("wait_time_after_lift")] public float WaitTimeAfterLift { get; set; } [JsonProperty("retract_speed")] public float RetractSpeed { get; set; } = DefaultRetractSpeed; + [JsonProperty("retract_distance2")] public float RetractHeight2 { get; set; } = DefaultRetractHeight2; + [JsonProperty("retract_speed2")] public float RetractSpeed2 { get; set; } = DefaultRetractSpeed2; [JsonProperty("light_pwm")] public byte LightPWM { get; set; } = DefaultLightPWM; } @@ -169,10 +181,20 @@ namespace UVtools.Core.FileFormats 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, @@ -186,8 +208,12 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.BottomWaitTimeAfterCure, PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, + PrintParameterModifier.LiftHeight2, + PrintParameterModifier.LiftSpeed2, PrintParameterModifier.WaitTimeAfterLift, PrintParameterModifier.RetractSpeed, + PrintParameterModifier.RetractHeight2, + PrintParameterModifier.RetractSpeed2, PrintParameterModifier.BottomLightPWM, PrintParameterModifier.LightPWM, @@ -362,6 +388,30 @@ namespace UVtools.Core.FileFormats set => base.LiftSpeed = ManifestFile.Print.LiftSpeed = (float)Math.Round(value, 2); } + public override float BottomLiftHeight2 + { + get => ManifestFile.Print.BottomLiftHeight2; + set => base.BottomLiftHeight2 = ManifestFile.Print.BottomLiftHeight2 = (float)Math.Round(value, 2); + } + + public override float LiftHeight2 + { + get => ManifestFile.Print.LiftHeight2; + set => base.LiftHeight2 = ManifestFile.Print.LiftHeight2 = (float)Math.Round(value, 2); + } + + public override float BottomLiftSpeed2 + { + get => ManifestFile.Print.BottomLiftSpeed2; + set => base.BottomLiftSpeed2 = ManifestFile.Print.BottomLiftSpeed2 = (float)Math.Round(value, 2); + } + + public override float LiftSpeed2 + { + get => ManifestFile.Print.LiftSpeed2; + set => base.LiftSpeed2 = ManifestFile.Print.LiftSpeed2 = (float)Math.Round(value, 2); + } + public override float BottomWaitTimeAfterLift { get => ManifestFile.Print.BottomWaitTimeAfterLift; @@ -373,10 +423,40 @@ namespace UVtools.Core.FileFormats set => base.WaitTimeAfterLift = ManifestFile.Print.WaitTimeAfterLift = (float)Math.Round(value, 2); } + public override float BottomRetractSpeed + { + get => ManifestFile.Print.BottomRetractSpeed; + set => base.BottomRetractSpeed = ManifestFile.Print.BottomRetractSpeed = (float)Math.Round(value, 2); + } + public override float RetractSpeed { get => ManifestFile.Print.RetractSpeed; - set => base.RetractSpeed = ManifestFile.Print.RetractSpeed = ManifestFile.Print.BottomRetractSpeed = (float)Math.Round(value, 2); + set => base.RetractSpeed = ManifestFile.Print.RetractSpeed = (float)Math.Round(value, 2); + } + + public override float BottomRetractHeight2 + { + get => ManifestFile.Print.BottomRetractHeight2; + set => base.BottomRetractHeight2 = ManifestFile.Print.BottomRetractHeight2 = (float)Math.Round(value, 2); + } + + public override float RetractHeight2 + { + get => ManifestFile.Print.RetractHeight2; + set => base.RetractHeight2 = ManifestFile.Print.RetractHeight2 = (float)Math.Round(value, 2); + } + + public override float BottomRetractSpeed2 + { + get => ManifestFile.Print.BottomRetractSpeed2; + set => base.BottomRetractSpeed2 = ManifestFile.Print.BottomRetractSpeed2 = (float)Math.Round(value, 2); + } + + public override float RetractSpeed2 + { + get => ManifestFile.Print.RetractSpeed2; + set => base.RetractSpeed2 = ManifestFile.Print.RetractSpeed2 = (float)Math.Round(value, 2); } public override byte BottomLightPWM @@ -456,8 +536,12 @@ namespace UVtools.Core.FileFormats WaitTimeAfterCure = layer.WaitTimeAfterCure, LiftHeight = layer.LiftHeight, LiftSpeed = layer.LiftSpeed, + LiftHeight2 = layer.LiftHeight2, + LiftSpeed2 = layer.LiftSpeed2, WaitTimeAfterLift = layer.WaitTimeAfterLift, RetractSpeed = layer.RetractSpeed, + RetractHeight2 = layer.RetractHeight2, + RetractSpeed2 = layer.RetractSpeed2, LightPWM = layer.LightPWM }; } @@ -540,8 +624,12 @@ namespace UVtools.Core.FileFormats WaitTimeAfterCure = manifestLayer.WaitTimeAfterCure, LiftHeight = manifestLayer.LiftHeight, LiftSpeed = manifestLayer.LiftSpeed, + LiftHeight2 = manifestLayer.LiftHeight2, + LiftSpeed2 = manifestLayer.LiftSpeed2, WaitTimeAfterLift = manifestLayer.WaitTimeAfterLift, RetractSpeed = manifestLayer.RetractSpeed, + RetractHeight2 = manifestLayer.RetractHeight2, + RetractSpeed2 = manifestLayer.RetractSpeed2, LightPWM = manifestLayer.LightPWM, }; } diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs index 643f14d..f0cb407 100644 --- a/UVtools.Core/FileFormats/ZCodeFile.cs +++ b/UVtools.Core/FileFormats/ZCodeFile.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.IO; using System.IO.Compression; @@ -84,10 +83,10 @@ namespace UVtools.Core.FileFormats public float LiftSpeed { get; set; } = FileFormat.DefaultLiftSpeed; [XmlAttribute("cooldown_bottom")] - public uint BottomLightOffDelay { get; set; } = (uint) (FileFormat.DefaultBottomLightOffDelay * 1000); + public uint BottomLightOffDelay { get; set; } [XmlAttribute("cooldown")] - public uint LightOffDelay { get; set; } = (uint) (FileFormat.DefaultLightOffDelay * 1000); + public uint LightOffDelay { get; set; } [XmlAttribute("thickness")] public float LayerHeight { get; set; } = FileFormat.DefaultLayerHeight; @@ -209,11 +208,22 @@ namespace UVtools.Core.FileFormats 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, }; @@ -224,8 +234,12 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.WaitTimeAfterCure, PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, + PrintParameterModifier.LiftHeight2, + PrintParameterModifier.LiftSpeed2, PrintParameterModifier.WaitTimeAfterLift, PrintParameterModifier.RetractSpeed, + PrintParameterModifier.RetractHeight2, + PrintParameterModifier.RetractSpeed2, PrintParameterModifier.LightPWM, }; diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index a268d32..56ec300 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -11,12 +11,9 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.IO.Compression; -using System.Text; using System.Text.RegularExpressions; using Emgu.CV; using Emgu.CV.CvEnum; -using Emgu.CV.Stitching; -using Emgu.CV.Util; using Newtonsoft.Json; using UVtools.Core.Extensions; using UVtools.Core.GCode; @@ -174,6 +171,7 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, + PrintParameterModifier.BottomRetractSpeed, PrintParameterModifier.RetractSpeed, }; @@ -609,6 +607,7 @@ M106 S0 } } + BottomRetractSpeed = RetractSpeed; // Compability LayerManager.GetBoundingRectangle(progress); } diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index e53faa2..9d5d156 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -7,6 +7,7 @@ */ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Globalization; @@ -390,12 +391,12 @@ namespace UVtools.Core.GCode AppendMoveG1(z, feedRate); } - public void AppendLiftMoveGx(float upZ, float upFeedRate, float downZ, float downFeedRate, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null) + public void AppendLiftMoveGx(float upZ, float upFeedRate, float upZ2, float upFeedRate2, float downZ, float downFeedRate, float downZ2, float downFeedRate2, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null) { if (_layerMoveCommand == GCodeMoveCommands.G0) - AppendLiftMoveG0(upZ, upFeedRate, downZ, downFeedRate, waitAfterLift, waitAfterRetract, layer); + AppendLiftMoveG0(upZ, upFeedRate, upZ2, upFeedRate2, downZ, downFeedRate, downZ2, downFeedRate2, waitAfterLift, waitAfterRetract, layer); else - AppendLiftMoveG1(upZ, upFeedRate, downZ, downFeedRate, waitAfterLift, waitAfterRetract, layer); + AppendLiftMoveG1(upZ, upFeedRate, upZ2, upFeedRate2, downZ, downFeedRate, downZ2, downFeedRate2, waitAfterLift, waitAfterRetract, layer); } @@ -405,14 +406,15 @@ namespace UVtools.Core.GCode AppendLine(CommandMoveG0, z, feedRate); } - public void AppendLiftMoveG0(float upZ, float upFeedRate, float downZ, float downFeedRate, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null) + public void AppendLiftMoveG0(float upZ, float upFeedRate, float upZ2 = 0, float upFeedRate2 = 0, float downZ = 0, float downFeedRate = 0, float downZ2 = 0, float downFeedRate2 = 0, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null) { - if (upZ == 0 || upFeedRate <= 0) return; - AppendLineOverrideComment(CommandMoveG0, "Z Lift", upZ, upFeedRate); // Z Lift + if ((upZ == 0 || upFeedRate <= 0) && (upZ2 == 0 || upFeedRate2 <= 0)) return; + + if(upZ > 0 && upFeedRate > 0) AppendLineOverrideComment(CommandMoveG0, "Z Lift", upZ, upFeedRate); // Z Lift + if(upZ2 > 0 && upFeedRate2 > 0) AppendLineOverrideComment(CommandMoveG0, "Z Lift (2)", upZ2, upFeedRate2); // Z Lift2 if (_syncMovementsWithDelay && layer is not null) { - // Finish this - var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, 0.75f); + var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer, 0.75f); var time = ConvertFromSeconds(seconds); AppendWaitG4($"0{time}", "Sync movement"); } @@ -422,13 +424,17 @@ namespace UVtools.Core.GCode AppendWaitG4(waitAfterLift, "Wait after lift"); } - if (downZ != 0 && downFeedRate > 0) + if ((downZ != 0 && downFeedRate > 0) || (downZ2 != 0 && downFeedRate2 > 0)) { - AppendLineOverrideComment(CommandMoveG0, "Retract to layer height", downZ, downFeedRate); + if(downZ2 != 0 && downFeedRate2 > 0) + AppendLineOverrideComment(CommandMoveG0, "Retract (2)", downZ2, downFeedRate2); + if (downZ != 0 && downFeedRate > 0) + AppendLineOverrideComment(CommandMoveG0, "Retract to layer height", downZ, downFeedRate); + if (_syncMovementsWithDelay && layer is not null) { // Finish this - var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.LiftHeight, layer.RetractSpeed, 0.75f); + var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.RetractHeight, layer.RetractSpeed, layer.RetractHeight2, layer.RetractSpeed2, 0.75f); var time = ConvertFromSeconds(seconds); AppendWaitG4($"0{time}", "Sync movement"); } @@ -446,19 +452,17 @@ namespace UVtools.Core.GCode AppendLine(CommandMoveG1, z, feedRate); } - public void AppendLiftMoveG1(float upZ, float upFeedRate, float downZ, float downFeedRate, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null) + public void AppendLiftMoveG1(float upZ, float upFeedRate, float upZ2 = 0, float upFeedRate2 = 0, float downZ = 0, float downFeedRate = 0, float downZ2 = 0, float downFeedRate2 = 0, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null) { - if (upZ == 0 || upFeedRate <= 0) return; - AppendLineOverrideComment(CommandMoveG1, "Lift Z", upZ, upFeedRate); // Z Lift + if ((upZ == 0 || upFeedRate <= 0) && (upZ2 == 0 || upFeedRate2 <= 0)) return; + + if (upZ > 0 && upFeedRate > 0) AppendLineOverrideComment(CommandMoveG1, "Z Lift", upZ, upFeedRate); // Z Lift + if (upZ2 > 0 && upFeedRate2 > 0) AppendLineOverrideComment(CommandMoveG1, "Z Lift (2)", upZ2, upFeedRate2); // Z Lift2 if (_syncMovementsWithDelay && layer is not null) { - // Finish this - var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, 0.75f); - if (seconds > layer.WaitTimeAfterLift) // Fix if wait time already include this - { - var time = ConvertFromSeconds(seconds); - AppendWaitG4($"0{time}", "Sync movement"); - } + var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer, 0.75f); + var time = ConvertFromSeconds(seconds); + AppendWaitG4($"0{time}", "Sync movement"); } if (waitAfterLift > 0) @@ -466,18 +470,19 @@ namespace UVtools.Core.GCode AppendWaitG4(waitAfterLift, "Wait after lift"); } - if (downZ != 0 && downFeedRate > 0) + if ((downZ != 0 && downFeedRate > 0) || (downZ2 != 0 && downFeedRate2 > 0)) { - AppendLineOverrideComment(CommandMoveG1, "Retract to layer height", downZ, downFeedRate); + if (downZ2 != 0 && downFeedRate2 > 0) + AppendLineOverrideComment(CommandMoveG1, "Retract (2)", downZ2, downFeedRate2); + if (downZ != 0 && downFeedRate > 0) + AppendLineOverrideComment(CommandMoveG1, "Retract to layer height", downZ, downFeedRate); + if (_syncMovementsWithDelay && layer is not null) { // Finish this - var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.LiftHeight, layer.RetractSpeed, 0.75f); - if (seconds > layer.WaitTimeBeforeCure) // Fix if wait time already include this - { - var time = ConvertFromSeconds(seconds); - AppendWaitG4($"0{time}", "Sync movement"); - } + var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.RetractHeight, layer.RetractSpeed, layer.RetractHeight2, layer.RetractSpeed2, 0.75f); + var time = ConvertFromSeconds(seconds); + AppendWaitG4($"0{time}", "Sync movement"); } } @@ -569,7 +574,7 @@ namespace UVtools.Core.GCode float lastZPosition = 0; - // Defaults for: Absolute, mm/m and s + // Defaults for: Absolute, mm/min and s for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++) { var layer = slicerFile[layerIndex]; @@ -578,23 +583,64 @@ namespace UVtools.Core.GCode float exposureTime = ConvertFromSeconds(layer.ExposureTime); float waitAfterCure = ConvertFromSeconds(layer.WaitTimeAfterCure); float liftHeight = layer.LiftHeight; - float liftZPos = Layer.RoundHeight(liftHeight + layer.PositionZ); - float liftZPosAbs = liftZPos; + //float liftZPos = Layer.RoundHeight(liftHeight + layer.PositionZ); + //float liftZPosAbs = liftZPos; float liftSpeed = ConvertFromMillimetersPerMinute(layer.LiftSpeed); + float liftHeight2 = layer.LiftHeight2; + float liftSpeed2 = ConvertFromMillimetersPerMinute(layer.LiftSpeed2); float waitAfterLift = ConvertFromSeconds(layer.WaitTimeAfterLift); - float retractPos = layer.PositionZ; + float retractHeight = layer.RetractHeight; float retractSpeed = ConvertFromMillimetersPerMinute(layer.RetractSpeed); + float retractHeight2 = layer.RetractHeight2; + float retractSpeed2 = ConvertFromMillimetersPerMinute(layer.RetractSpeed2); + float liftHeightTotal = layer.LiftHeightTotal; ushort pwmValue = layer.LightPWM; if (_maxLedPower != byte.MaxValue) { pwmValue = (ushort)(_maxLedPower * pwmValue / byte.MaxValue); } + float liftPos = 0; + float liftPos2 = 0; + float retractPos = 0; + float retractPos2 = 0; + switch (GCodePositioningType) { + case GCodePositioningTypes.Absolute: + var absLiftPos = Layer.RoundHeight(liftHeight + layer.PositionZ); + var absLiftPos2 = Layer.RoundHeight(absLiftPos + liftHeight2); + var absRetractPos2 = Layer.RoundHeight(absLiftPos2 - retractHeight2); + if (liftHeight > 0) liftPos = absLiftPos; + if (liftHeight2 > 0) liftPos2 = absLiftPos2; + if (retractHeight2 > 0) retractPos2 = absRetractPos2; + if (retractHeight > 0) retractPos = layer.PositionZ; + if (retractPos > 0 && retractPos > retractPos2) retractPos2 = 0; // Fail-safe + break; case GCodePositioningTypes.Partial: - liftZPos = liftHeight; - retractPos = Layer.RoundHeight(layer.PositionZ - lastZPosition - liftHeight); + var partialLiftPos = Layer.RoundHeight(layer.PositionZ - lastZPosition + liftHeight); + var partialLiftPosTotal = Layer.RoundHeight(partialLiftPos + liftHeight2); + var partialRetractPosTotal = Layer.RoundHeight(partialLiftPos + liftHeight2); + if (liftHeight > 0) + { + liftPos = partialLiftPos; + if (liftHeight2 > 0) liftPos2 = liftHeight2; + } + else + { + if (liftHeight2 > 0) liftPos2 = Layer.RoundHeight(partialLiftPos + liftHeight2); + } + + if (retractHeight2 > 0) retractPos2 = -retractHeight2; + if (retractHeight > 0) retractPos = -retractHeight; + + // Assert + if (Layer.RoundHeight(Math.Abs(retractPos + retractPos2)) != liftHeightTotal) // Fail-safe + { + retractPos2 = 0; + retractPos = -liftHeightTotal; + } + break; } @@ -605,9 +651,9 @@ namespace UVtools.Core.GCode AppendShowImageM6054(GetShowImageString(layerIndex)); //} - if (liftHeight > 0 && liftZPosAbs > layer.PositionZ) + if (liftHeightTotal > 0 && Layer.RoundHeight(liftHeightTotal + layer.PositionZ) > layer.PositionZ) { - AppendLiftMoveGx(liftZPos, liftSpeed, retractPos, retractSpeed, waitAfterLift, 0, layer); + AppendLiftMoveGx(liftPos, liftSpeed, liftPos2, liftSpeed2, retractPos, retractSpeed, retractPos2, retractSpeed2, waitAfterLift, 0, layer); } else if (lastZPosition < layer.PositionZ) // Ensure Z is on correct position { @@ -759,10 +805,7 @@ namespace UVtools.Core.GCode } // Propagate values before switch to the new layer - layerBlock.PositionZ ??= positionZ; - layerBlock.SetLayer(); - - layerBlock.Init(); + layerBlock.SetLayer(true); layerBlock.LayerIndex = layerIndex; continue; @@ -782,12 +825,16 @@ namespace UVtools.Core.GCode RegexOptions.IgnoreCase); match = moveG0Regex.Success && moveG0Regex.Groups.Count >= 2 ? moveG0Regex : moveG1Regex; - if (match.Success && match.Groups.Count >= 4 && !layerBlock.RetractSpeed.HasValue) + if (match.Success && match.Groups.Count >= 4 && !layerBlock.RetractSpeed2.HasValue && layerBlock.LightOffCount < 2) { float pos = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture); float speed = ConvertToMillimetersPerMinute(float.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture)); - if (!layerBlock.PositionZ.HasValue) // Lift or pos, set here for now + + layerBlock.Movements.Add(new(pos, speed)); + layerBlock.AssignMovements(positionType); + + /*if (!layerBlock.PositionZ.HasValue) // Lift or pos, set here for now { switch (positionType) { @@ -817,7 +864,7 @@ namespace UVtools.Core.GCode layerBlock.LiftHeight = layerBlock.PositionZ - positionZ; layerBlock.PositionZ = positionZ = Layer.RoundHeight(layerBlock.PositionZ.Value + pos); break; - } + }*/ continue; } @@ -897,7 +944,7 @@ namespace UVtools.Core.GCode if (pwm == 0 && layerBlock.LightPWM.HasValue) { - layerBlock.IsAfterLightOff = true; + layerBlock.LightOffCount++; } else if (!layerBlock.IsAfterLightOff) { @@ -910,7 +957,6 @@ namespace UVtools.Core.GCode } // Propagate values of left over layer - layerBlock.PositionZ ??= positionZ; layerBlock.SetLayer(); @@ -1016,36 +1062,7 @@ namespace UVtools.Core.GCode if (rebuildGlobalTable) { - slicerFile.SuppressRebuildPropertiesWork(() => - { - var bottomLayer = slicerFile.FirstLayer; - if (bottomLayer is not null) - { - if (bottomLayer.LightOffDelay > 0) slicerFile.BottomLightOffDelay = bottomLayer.LightOffDelay; - if (bottomLayer.WaitTimeBeforeCure > 0) slicerFile.BottomWaitTimeBeforeCure = bottomLayer.WaitTimeBeforeCure; - if (bottomLayer.ExposureTime > 0) slicerFile.BottomExposureTime = bottomLayer.ExposureTime; - if (bottomLayer.WaitTimeAfterCure > 0) slicerFile.BottomWaitTimeAfterCure = bottomLayer.WaitTimeAfterCure; - if (bottomLayer.LiftHeight > 0) slicerFile.BottomLiftHeight = bottomLayer.LiftHeight; - if (bottomLayer.LiftSpeed > 0) slicerFile.BottomLiftSpeed = bottomLayer.LiftSpeed; - if (bottomLayer.WaitTimeAfterLift > 0) slicerFile.BottomWaitTimeAfterLift = bottomLayer.WaitTimeAfterLift; - if (bottomLayer.RetractSpeed > 0) slicerFile.RetractSpeed = bottomLayer.RetractSpeed; - if (bottomLayer.LightPWM > 0) slicerFile.BottomLightPWM = bottomLayer.LightPWM; - } - - var normalLayer = slicerFile.LastLayer; - if (normalLayer is not null) - { - if (normalLayer.LightOffDelay > 0) slicerFile.LightOffDelay = normalLayer.LightOffDelay; - if (normalLayer.WaitTimeBeforeCure > 0) slicerFile.WaitTimeBeforeCure = normalLayer.WaitTimeBeforeCure; - if (normalLayer.ExposureTime > 0) slicerFile.ExposureTime = normalLayer.ExposureTime; - if (normalLayer.WaitTimeAfterCure > 0) slicerFile.WaitTimeAfterCure = normalLayer.WaitTimeAfterCure; - if (normalLayer.LiftHeight > 0) slicerFile.LiftHeight = normalLayer.LiftHeight; - if (normalLayer.LiftSpeed > 0) slicerFile.LiftSpeed = normalLayer.LiftSpeed; - if (normalLayer.WaitTimeAfterLift > 0) slicerFile.WaitTimeAfterLift = normalLayer.WaitTimeAfterLift; - if (normalLayer.RetractSpeed > 0) slicerFile.RetractSpeed = normalLayer.RetractSpeed; - if (normalLayer.LightPWM > 0) slicerFile.LightPWM = normalLayer.LightPWM; - } - }); + slicerFile.UpdateGlobalPropertiesFromLayers(); } } diff --git a/UVtools.Core/GCode/GCodeLayer.cs b/UVtools.Core/GCode/GCodeLayer.cs index 9f3c36b..cbd0fab 100644 --- a/UVtools.Core/GCode/GCodeLayer.cs +++ b/UVtools.Core/GCode/GCodeLayer.cs @@ -7,6 +7,7 @@ */ using System; +using System.Collections.Generic; using UVtools.Core.FileFormats; using UVtools.Core.Operations; @@ -14,13 +15,18 @@ namespace UVtools.Core.GCode { public class GCodeLayer { + private float? _positionZ; private float? _waitTimeBeforeCure; private float? _exposureTime; private float? _waitTimeAfterCure; private float? _liftHeight; private float? _liftSpeed; + private float? _liftHeight2; + private float? _liftSpeed2; private float? _waitTimeAfterLift; private float? _retractSpeed; + private float? _retractHeight2; + private float? _retractSpeed2; public enum GCodeLastParsedLine : byte { @@ -30,8 +36,16 @@ namespace UVtools.Core.GCode public bool IsValid => LayerIndex.HasValue; public FileFormat SlicerFile { get; } + public List<(float Pos, float Speed)> Movements = new(); public uint? LayerIndex { get; set; } - public float? PositionZ { get; set; } + + public float? PositionZ + { + get => _positionZ; + set => _positionZ = value; + } + + public float PreviousPositionZ { get; set; } public float? WaitTimeBeforeCure { @@ -54,7 +68,7 @@ namespace UVtools.Core.GCode public float? LiftHeight { get => _liftHeight; - set => _liftHeight = value is null ? null : (float)Math.Round(value.Value, 2); + set => _liftHeight = value is null ? null : Layer.RoundHeight(value.Value); } public float? LiftSpeed @@ -63,6 +77,20 @@ namespace UVtools.Core.GCode set => _liftSpeed = value is null ? null : (float)Math.Round(value.Value, 2); } + public float LiftHeightTotal => Layer.RoundHeight((LiftHeight ?? 0) + (LiftHeight2 ?? 0)); + + public float? LiftHeight2 + { + get => _liftHeight2; + set => _liftHeight2 = value is null ? null : Layer.RoundHeight(value.Value); + } + + public float? LiftSpeed2 + { + get => _liftSpeed2; + set => _liftSpeed2 = value is null ? null : (float)Math.Round(value.Value, 2); + } + public float? WaitTimeAfterLift { get => _waitTimeAfterLift; @@ -75,11 +103,25 @@ namespace UVtools.Core.GCode set => _retractSpeed = value is null ? null : (float)Math.Round(value.Value, 2); } + public float? RetractHeight2 + { + get => _retractHeight2; + set => _retractHeight2 = value is null ? null : Layer.RoundHeight(value.Value); + } + + public float? RetractSpeed2 + { + get => _retractSpeed2; + set => _retractSpeed2 = value is null ? null : (float)Math.Round(value.Value, 2); + } + public byte? LightPWM { get; set; } public bool IsExposing => LightPWM.HasValue && !IsAfterLightOff; + public bool IsExposed => LightPWM.HasValue && IsAfterLightOff; - public bool IsAfterLightOff { get; set; } + public byte LightOffCount { get; set; } + public bool IsAfterLightOff => LightOffCount > 0; public GCodeLayer(FileFormat slicerFile) { @@ -88,6 +130,9 @@ namespace UVtools.Core.GCode public void Init() { + PreviousPositionZ = PositionZ ?? 0; + + Movements.Clear(); LayerIndex = null; PositionZ = null; WaitTimeBeforeCure = null; @@ -95,39 +140,142 @@ namespace UVtools.Core.GCode WaitTimeAfterCure = null; LiftHeight = null; LiftSpeed = null; + LiftHeight2 = null; + LiftSpeed2 = null; WaitTimeAfterLift = null; RetractSpeed = null; + RetractHeight2 = null; + RetractSpeed2 = null; LightPWM = null; - IsAfterLightOff = false; + LightOffCount = 0; + } + + public void AssignMovements(GCodeBuilder.GCodePositioningTypes positionType) + { + if (Movements.Count == 0) return; + var currentZ = PreviousPositionZ; + + PositionZ = null; + LiftHeight = null; + LiftSpeed = null; + LiftHeight2 = null; + LiftSpeed2 = null; + RetractSpeed = null; + RetractHeight2 = null; + RetractSpeed2 = null; + + for (int i = 0; i < Movements.Count; i++) + { + var (pos, speed) = Movements[i]; + float heightRaw; + switch (positionType) + { + case GCodeBuilder.GCodePositioningTypes.Absolute: + heightRaw = Layer.RoundHeight(pos - currentZ); + currentZ = pos; + break; + case GCodeBuilder.GCodePositioningTypes.Partial: + heightRaw = pos; + currentZ = Layer.RoundHeight(currentZ + pos); + break; + default: + throw new ArgumentOutOfRangeException(nameof(positionType)); + } + + // Fail-safe check + if (currentZ < PreviousPositionZ) + throw new NotSupportedException("GCode parsing error: Attempting to crash the print on the LCD with negative position.\n" + + "Do not attempt to print this file!"); + + // Is position Z + if (i == Movements.Count - 1) + { + PositionZ = currentZ; + if (LiftHeight.HasValue) RetractSpeed = speed; // A lift exists, set to retract speed of this move + continue; + } + + if (heightRaw == 0) continue; + var height = Math.Abs(heightRaw); + + if (heightRaw > 0) // Is a lift + { + if (!LiftHeight.HasValue) + { + LiftHeight = height; + LiftSpeed = speed; + continue; + } + + LiftHeight2 ??= 0; + LiftHeight2 += height; + LiftSpeed2 = speed; + + continue; + } + + if(!LiftHeight.HasValue) continue; // Fail-safe: Retract without a lift? Skip + + // Is a extra retract (2) + RetractHeight2 ??= 0; + RetractHeight2 += height; + RetractSpeed2 = speed; + } + + if (Movements.Count == 1) // Only 1 move, this is the PositionZ only + { + LiftSpeed = Movements[0].Speed; + return; + } + + // Sanitize + if (PositionZ.HasValue && LiftHeight.HasValue && !IsExposed) // Lift before exposure order, need to remove layer height as offset + { + var liftHeight = Layer.RoundHeight(LiftHeight.Value - (PositionZ.Value - PreviousPositionZ)); + if(liftHeight <= 0) return; // Something not right or not the correct moment, skip + LiftHeight = liftHeight; + } + + if (LiftHeight.HasValue && RetractHeight2.HasValue) // Sanitize RetractHeight2 value + { + RetractHeight2 = Math.Clamp(RetractHeight2.Value, 0, LiftHeightTotal); + } } /// <summary> /// Set gathered data to the layer /// </summary> - public void SetLayer() + public void SetLayer(bool reinit = false) { if (!IsValid) return; uint layerIndex = LayerIndex.Value; var layer = SlicerFile[layerIndex]; - if(PositionZ.HasValue) layer.PositionZ = PositionZ.Value; + if (!PositionZ.HasValue) PositionZ = PreviousPositionZ; + layer.PositionZ = PositionZ.Value; layer.WaitTimeBeforeCure = WaitTimeBeforeCure ?? 0; layer.ExposureTime = ExposureTime ?? SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime); layer.WaitTimeAfterCure = WaitTimeAfterCure ?? 0; layer.LiftHeight = LiftHeight ?? 0; layer.LiftSpeed = LiftSpeed ?? SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed); + layer.LiftHeight2 = LiftHeight2 ?? 0; + layer.LiftSpeed2 = LiftSpeed2 ?? SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2); layer.WaitTimeAfterLift = WaitTimeAfterLift ?? 0; - layer.RetractSpeed = RetractSpeed ?? SlicerFile.RetractSpeed; + layer.RetractSpeed = RetractSpeed ?? SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed); + layer.RetractHeight2 = RetractHeight2 ?? 0; + layer.RetractSpeed2 = RetractSpeed2 ?? SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2); layer.LightPWM = LightPWM ?? 0;//SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); if (SlicerFile.GCode.SyncMovementsWithDelay) // Dirty fix of the value { - var syncTime = OperationCalculator.LightOffDelayC.CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed, 1.5f); + var syncTime = OperationCalculator.LightOffDelayC.CalculateSeconds(layer, 1.5f); if (syncTime < layer.WaitTimeBeforeCure) { layer.WaitTimeBeforeCure = (float) Math.Round(layer.WaitTimeBeforeCure - syncTime, 2); } } + + if(reinit) Init(); } } } diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs index 7ae01d0..456cec5 100644 --- a/UVtools.Core/Layer/Layer.cs +++ b/UVtools.Core/Layer/Layer.cs @@ -7,6 +7,7 @@ */ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Drawing; using System.Linq; using Emgu.CV; @@ -40,14 +41,18 @@ namespace UVtools.Core private bool _isModified; private uint _index; private float _positionZ; + private float _lightOffDelay; private float _waitTimeBeforeCure; private float _exposureTime; private float _waitTimeAfterCure; - private float _lightOffDelay = FileFormat.DefaultLightOffDelay; private float _liftHeight = FileFormat.DefaultLiftHeight; private float _liftSpeed = FileFormat.DefaultLiftSpeed; + private float _liftHeight2 = FileFormat.DefaultLiftHeight2; + private float _liftSpeed2 = FileFormat.DefaultLiftSpeed2; private float _waitTimeAfterLift; private float _retractSpeed = FileFormat.DefaultRetractSpeed; + private float _retractHeight2 = FileFormat.DefaultRetractHeight2; + private float _retractSpeed2 = FileFormat.DefaultRetractSpeed2; private byte _lightPwm = FileFormat.DefaultLightPWM; private float _materialMilliliters; #endregion @@ -132,6 +137,19 @@ namespace UVtools.Core } /// <summary> + /// Gets or sets the layer position on Z in mm + /// </summary> + public float PositionZ + { + get => _positionZ; + set + { + if (!RaiseAndSetIfChanged(ref _positionZ, value)) return; + RaisePropertyChanged(nameof(LayerHeight)); + } + } + + /// <summary> /// Gets or sets the wait time in seconds before cure the layer /// AKA: Light-off delay /// Chitubox: Rest time after retract @@ -191,6 +209,20 @@ namespace UVtools.Core } /// <summary> + /// Gets: Total lift height (lift1 + lift2) + /// Sets: Lift1 with value and lift2 with 0 + /// </summary> + public float LiftHeightTotal + { + get => (float)Math.Round(_liftHeight + _liftHeight2); + set + { + LiftHeight = value; + LiftHeight2 = 0; + } + } + + /// <summary> /// Gets or sets the lift height in mm /// </summary> public float LiftHeight @@ -200,6 +232,8 @@ namespace UVtools.Core { if (value < 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight); if(!RaiseAndSetIfChanged(ref _liftHeight, value)) return; + RaisePropertyChanged(nameof(LiftHeightTotal)); + RetractHeight2 = _retractHeight2; // Sanitize SlicerFile?.UpdatePrintTimeQueued(); } } @@ -218,6 +252,36 @@ namespace UVtools.Core } } + /// <summary> + /// Gets or sets the lift height in mm + /// </summary> + public float LiftHeight2 + { + get => _liftHeight2; + set + { + if (value < 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLiftHeight2, SlicerFile.LiftHeight2); + if (!RaiseAndSetIfChanged(ref _liftHeight2, value)) return; + RaisePropertyChanged(nameof(LiftHeightTotal)); + RetractHeight2 = _retractHeight2; // Sanitize + SlicerFile?.UpdatePrintTimeQueued(); + } + } + + /// <summary> + /// Gets or sets the speed in mm/min + /// </summary> + public float LiftSpeed2 + { + get => _liftSpeed2; + set + { + if (value <= 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2); + if (!RaiseAndSetIfChanged(ref _liftSpeed2, value)) return; + SlicerFile?.UpdatePrintTimeQueued(); + } + } + public float WaitTimeAfterLift { get => _waitTimeAfterLift; @@ -229,6 +293,16 @@ namespace UVtools.Core } /// <summary> + /// Gets: Total retract height (retract1 + retract2) alias of <see cref="LiftHeightTotal"/> + /// </summary> + public float RetractHeightTotal => LiftHeightTotal; + + /// <summary> + /// Gets the retract height in mm + /// </summary> + public float RetractHeight => (float)Math.Round(LiftHeightTotal - _retractHeight2); + + /// <summary> /// Gets the speed in mm/min for the retracts /// </summary> public float RetractSpeed @@ -243,38 +317,49 @@ namespace UVtools.Core } /// <summary> - /// Gets or sets the pwm value from 0 to 255 + /// Gets or sets the second retract height in mm /// </summary> - public byte LightPWM + public virtual float RetractHeight2 { - get => _lightPwm; + get => _retractHeight2; set { - //if (value == 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); - //if (value == 0) value = FileFormat.DefaultLightPWM; - RaiseAndSetIfChanged(ref _lightPwm, value); + value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal); + RaiseAndSetIfChanged(ref _retractHeight2, value); + RaisePropertyChanged(nameof(RetractHeight)); + RaisePropertyChanged(nameof(RetractHeightTotal)); } } /// <summary> - /// Gets if this layer can be exposed to UV light + /// Gets the speed in mm/min for the retracts /// </summary> - public bool CanExpose => _exposureTime > 0 && _lightPwm > 0; + public virtual float RetractSpeed2 + { + get => _retractSpeed2; + set => RaiseAndSetIfChanged(ref _retractSpeed2, (float)Math.Round(value, 2)); + } /// <summary> - /// Gets or sets the layer position on Z in mm + /// Gets or sets the pwm value from 0 to 255 /// </summary> - public float PositionZ + public byte LightPWM { - get => _positionZ; + get => _lightPwm; set { - if(!RaiseAndSetIfChanged(ref _positionZ, value)) return; - RaisePropertyChanged(nameof(LayerHeight)); + //if (value == 0) value = SlicerFile.GetInitialLayerValueOrNormal(Index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); + //if (value == 0) value = FileFormat.DefaultLightPWM; + RaiseAndSetIfChanged(ref _lightPwm, value); } } /// <summary> + /// Gets if this layer can be exposed to UV light + /// </summary> + public bool CanExpose => _exposureTime > 0 && _lightPwm > 0; + + /// <summary> /// Gets the layer height in millimeters of this layer /// </summary> public float LayerHeight @@ -406,7 +491,12 @@ namespace UVtools.Core WaitTimeBeforeCure != SlicerFile.BottomWaitTimeBeforeCure || LiftHeight != SlicerFile.BottomLiftHeight || LiftSpeed != SlicerFile.BottomLiftSpeed || + LiftHeight2 != SlicerFile.BottomLiftHeight2 || + LiftSpeed2 != SlicerFile.BottomLiftSpeed2 || WaitTimeAfterLift != SlicerFile.BottomWaitTimeAfterLift || + RetractSpeed != SlicerFile.BottomRetractSpeed || + RetractHeight2 != SlicerFile.BottomRetractHeight2 || + RetractSpeed2 != SlicerFile.BottomRetractSpeed2 || LightPWM != SlicerFile.BottomLightPWM ) return false; } @@ -419,13 +509,16 @@ namespace UVtools.Core WaitTimeAfterCure != SlicerFile.WaitTimeAfterCure || LiftHeight != SlicerFile.LiftHeight || LiftSpeed != SlicerFile.LiftSpeed || + LiftHeight2 != SlicerFile.LiftHeight2 || + LiftSpeed2 != SlicerFile.LiftSpeed2 || WaitTimeAfterLift != SlicerFile.WaitTimeAfterLift || + RetractSpeed != SlicerFile.RetractSpeed || + RetractHeight2 != SlicerFile.RetractHeight2 || + RetractSpeed2 != SlicerFile.RetractSpeed2 || LightPWM != SlicerFile.LightPWM ) return false; } - if (RetractSpeed != SlicerFile.RetractSpeed) return false; - return true; } } @@ -447,8 +540,12 @@ namespace UVtools.Core _waitTimeAfterCure = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomWaitTimeAfterCure, SlicerFile.WaitTimeAfterCure); _liftHeight = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight); _liftSpeed = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed); + _liftHeight2 = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLiftHeight2, SlicerFile.LiftHeight2); + _liftSpeed2 = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2); _waitTimeAfterLift = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomWaitTimeAfterLift, SlicerFile.WaitTimeAfterLift); - _retractSpeed = SlicerFile.RetractSpeed; + _retractSpeed = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed); + _retractHeight2 = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomRetractHeight2, SlicerFile.RetractHeight2); + _retractSpeed2 = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2); _lightPwm = SlicerFile.GetInitialLayerValueOrNormal(index, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); } @@ -569,8 +666,13 @@ namespace UVtools.Core $"{nameof(WaitTimeAfterCure)}: {WaitTimeAfterCure}s, " + $"{nameof(LiftHeight)}: {LiftHeight}mm, " + $"{nameof(LiftSpeed)}: {LiftSpeed}mm/mim, " + + $"{nameof(LiftHeight2)}: {LiftHeight2}mm, " + + $"{nameof(LiftSpeed2)}: {LiftSpeed2}mm/mim, " + $"{nameof(WaitTimeAfterLift)}: {WaitTimeAfterLift}s, " + + $"{nameof(RetractHeight)}: {RetractHeight}mm, " + $"{nameof(RetractSpeed)}: {RetractSpeed}mm/mim, " + + $"{nameof(RetractHeight2)}: {RetractHeight2}mm, " + + $"{nameof(RetractSpeed2)}: {RetractSpeed2}mm/mim, " + $"{nameof(LightPWM)}: {LightPWM}, " + $"{nameof(IsModified)}: {IsModified}, " + $"{nameof(HaveGlobalParameters)}: {HaveGlobalParameters}"; @@ -581,13 +683,13 @@ namespace UVtools.Core public float CalculateMotorMovementTime(float extraTime = 0) { - return OperationCalculator.LightOffDelayC.CalculateSeconds(_liftHeight, _liftSpeed, _retractSpeed, extraTime); + return OperationCalculator.LightOffDelayC.CalculateSeconds(this, extraTime); } public float CalculateLightOffDelay(float extraTime = 0) { - if (SlicerFile is null) return OperationCalculator.LightOffDelayC.CalculateSeconds(_liftHeight, _liftSpeed, _retractSpeed, extraTime); - return SlicerFile.SupportsGCode ? extraTime : OperationCalculator.LightOffDelayC.CalculateSeconds(_liftHeight, _liftSpeed, _retractSpeed, extraTime); + if (SlicerFile is null) return OperationCalculator.LightOffDelayC.CalculateSeconds(this, extraTime); + return SlicerFile.SupportsGCode ? extraTime : OperationCalculator.LightOffDelayC.CalculateSeconds(this, extraTime); } public void SetLightOffDelay(float extraTime = 0) @@ -702,6 +804,18 @@ namespace UVtools.Core return true; } + if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftHeight2)) + { + LiftHeight2 = (float)value; + return true; + } + + if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LiftSpeed2)) + { + LiftSpeed2 = (float)value; + return true; + } + if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.WaitTimeAfterLift)) { WaitTimeAfterLift = (float)value; @@ -714,6 +828,18 @@ namespace UVtools.Core return true; } + if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.RetractHeight2)) + { + RetractHeight2 = (float)value; + return true; + } + + if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.RetractSpeed2)) + { + RetractSpeed2 = (float)value; + return true; + } + if (ReferenceEquals(modifier, FileFormat.PrintParameterModifier.LightPWM)) { LightPWM = (byte)value; @@ -898,7 +1024,11 @@ namespace UVtools.Core public Layer Clone() { - return new(_index, CompressedBytes.ToArray(), ParentLayerManager) + //var layer = (Layer)MemberwiseClone(); + //layer.CompressedBytes = _compressedBytes.ToArray(); + //Debug.WriteLine(ReferenceEquals(_compressedBytes, layer.CompressedBytes)); + //return layer; + return new (_index, CompressedBytes.ToArray(), ParentLayerManager) { PositionZ = _positionZ, LightOffDelay = _lightOffDelay, @@ -907,8 +1037,12 @@ namespace UVtools.Core WaitTimeAfterCure = _waitTimeAfterCure, LiftHeight = _liftHeight, LiftSpeed = _liftSpeed, + LiftHeight2 = _liftHeight2, + LiftSpeed2 = _liftSpeed2, WaitTimeAfterLift = _waitTimeAfterLift, RetractSpeed = _retractSpeed, + RetractHeight2 = _retractHeight2, + RetractSpeed2 = _retractSpeed2, LightPWM = _lightPwm, BoundingRectangle = _boundingRectangle, NonZeroPixelCount = _nonZeroPixelCount, diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index 4d4c2a4..d17c7f8 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -505,8 +505,12 @@ namespace UVtools.Core layer.WaitTimeAfterCure = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomWaitTimeAfterCure, SlicerFile.WaitTimeAfterCure); layer.LiftHeight = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight); layer.LiftSpeed = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed); + layer.LiftHeight2 = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftHeight2, SlicerFile.LiftHeight2); + layer.LiftSpeed2 = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftSpeed2, SlicerFile.LiftSpeed2); layer.WaitTimeAfterLift = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomWaitTimeAfterLift, SlicerFile.WaitTimeAfterLift); - layer.RetractSpeed = SlicerFile.RetractSpeed; + layer.RetractSpeed = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomRetractSpeed, SlicerFile.RetractSpeed); + layer.RetractHeight2 = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomRetractHeight2, SlicerFile.RetractHeight2); + layer.RetractSpeed2 = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomRetractSpeed2, SlicerFile.RetractSpeed2); layer.LightPWM = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); } else @@ -533,13 +537,25 @@ namespace UVtools.Core case nameof(SlicerFile.BottomLiftSpeed): layer.LiftSpeed = SlicerFile.BottomLiftSpeed; break; + case nameof(SlicerFile.BottomLiftHeight2): + layer.LiftHeight2 = SlicerFile.BottomLiftHeight2; + break; + case nameof(SlicerFile.BottomLiftSpeed2): + layer.LiftSpeed2 = SlicerFile.BottomLiftSpeed2; + break; case nameof(SlicerFile.BottomWaitTimeAfterLift): layer.WaitTimeAfterLift = SlicerFile.BottomWaitTimeAfterLift; break; - case nameof(SlicerFile.RetractSpeed): - layer.RetractSpeed = SlicerFile.RetractSpeed; + case nameof(SlicerFile.BottomRetractSpeed): + layer.RetractSpeed = SlicerFile.BottomRetractSpeed; + break; + case nameof(SlicerFile.BottomRetractHeight2): + layer.RetractHeight2 = SlicerFile.BottomRetractHeight2; + break; + case nameof(SlicerFile.BottomRetractSpeed2): + layer.RetractSpeed2 = SlicerFile.BottomRetractSpeed2; break; - + case nameof(SlicerFile.BottomLightPWM): layer.LightPWM = SlicerFile.BottomLightPWM; break; @@ -567,12 +583,24 @@ namespace UVtools.Core case nameof(SlicerFile.LiftSpeed): layer.LiftSpeed = SlicerFile.LiftSpeed; break; + case nameof(SlicerFile.LiftHeight2): + layer.LiftHeight2 = SlicerFile.LiftHeight2; + break; + case nameof(SlicerFile.LiftSpeed2): + layer.LiftSpeed2 = SlicerFile.LiftSpeed2; + break; case nameof(SlicerFile.WaitTimeAfterLift): layer.WaitTimeAfterLift = SlicerFile.WaitTimeAfterLift; break; case nameof(SlicerFile.RetractSpeed): layer.RetractSpeed = SlicerFile.RetractSpeed; break; + case nameof(SlicerFile.RetractHeight2): + layer.RetractHeight2 = SlicerFile.RetractHeight2; + break; + case nameof(SlicerFile.RetractSpeed2): + layer.RetractSpeed2 = SlicerFile.RetractSpeed2; + break; case nameof(SlicerFile.LightPWM): layer.LightPWM = SlicerFile.LightPWM; break; @@ -603,7 +631,7 @@ namespace UVtools.Core { var layer = this[layerIndex]; if (this[layerIndex - 1].PositionZ != layer.PositionZ) continue; - layer.LiftHeight = liftHeight; + layer.LiftHeightTotal = liftHeight; layer.WaitTimeAfterLift = 0; if (zeroLightOffDelay) { diff --git a/UVtools.Core/Operations/OperationCalculator.cs b/UVtools.Core/Operations/OperationCalculator.cs index 6cf526b..a5e9216 100644 --- a/UVtools.Core/Operations/OperationCalculator.cs +++ b/UVtools.Core/Operations/OperationCalculator.cs @@ -286,35 +286,58 @@ namespace UVtools.Core.Operations return Math.Round(time, 2); } - public static float CalculateSeconds(float liftHeight, float liftSpeed, float retractSpeed, float extraWaitTime = 0) + public static float CalculateSeconds(float liftHeight, float liftSpeed, float retractSpeed, float extraWaitTime = 0, + float liftHeight2 = 0, float liftSpeed2 = 0, float retractHeight2 = 0, float retractSpeed2 = 0) { var time = extraWaitTime; - if (liftSpeed > 0) - { + if (liftHeight > 0 && liftSpeed > 0) time += liftHeight / (liftSpeed / 60f); - } - if (retractSpeed > 0) + + if (liftHeight2 > 0 && liftSpeed2 > 0) + time += liftHeight2 / (liftSpeed2 / 60f); + + if (retractHeight2 > 0 && retractSpeed2 > 0) + time += retractHeight2 / (retractSpeed2 / 60f); + + var remainingRetractHeight = liftHeight + liftHeight2 - retractHeight2; + + if (remainingRetractHeight > 0 && retractSpeed > 0) { - time += liftHeight / (retractSpeed / 60f); + time += remainingRetractHeight / (retractSpeed / 60f); } return (float)Math.Round(time, 2); } + public static float CalculateSeconds(Layer layer, float extraTime) + => CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed, extraTime, + layer.LiftHeight2, layer.LiftSpeed2, layer.RetractHeight2, layer.RetractSpeed2); + + public static uint CalculateMilliseconds(Layer layer, float extraTime) + => (uint)(CalculateSeconds(layer.LiftHeight, layer.LiftSpeed, layer.RetractSpeed, extraTime, + layer.LiftHeight2, layer.LiftSpeed2, layer.RetractHeight2, layer.RetractSpeed2) * 1000); + public static uint CalculateMilliseconds(float liftHeight, float liftSpeed, float retract, float extraWaitTime = 0) => (uint)(CalculateSeconds(liftHeight, liftSpeed, retract, extraWaitTime) * 1000); - public static float CalculateSecondsLiftOnly(float liftHeight, float liftSpeed, float extraWaitTime = 0) + public static float CalculateSecondsLiftOnly(float liftHeight, float liftSpeed, float liftHeight2 = 0, float liftSpeed2 = 0, float extraWaitTime = 0) { - if (liftSpeed <= 0) return extraWaitTime; - return (float)Math.Round(liftHeight / (liftSpeed / 60f) + extraWaitTime, 2); + var time = extraWaitTime; + if (liftHeight > 0 && liftSpeed > 0) time += (float)Math.Round(liftHeight / (liftSpeed / 60f) + extraWaitTime, 2); + if (liftHeight2 > 0 && liftSpeed2 > 0) time += (float)Math.Round(liftHeight2 / (liftSpeed2 / 60f) + extraWaitTime, 2); + return time; } - public static uint CalculateMillisecondsLiftOnly(float liftHeight, float liftSpeed, float extraWaitTime = 0) => - (uint)(CalculateSecondsLiftOnly(liftHeight, liftSpeed, extraWaitTime) * 1000); + public static uint CalculateMillisecondsLiftOnly(float liftHeight, float liftSpeed, float liftHeight2 = 0, float liftSpeed2 = 0, float extraWaitTime = 0) => + (uint)(CalculateSecondsLiftOnly(liftHeight, liftSpeed, liftHeight2, liftSpeed2, extraWaitTime) * 1000); + + public static float CalculateSecondsLiftOnly(Layer layer, float extraWaitTime = 0) => + CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, layer.LiftHeight2, layer.LiftSpeed2, extraWaitTime); + public static uint CalculateMillisecondsLiftOnly(Layer layer, float extraWaitTime = 0) => + (uint)(CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, layer.LiftHeight2, layer.LiftSpeed2, extraWaitTime) * 1000); } diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs index 608b8ec..ccd1305 100644 --- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -2170,15 +2170,10 @@ namespace UVtools.Core.Operations } else { - Layer layer = new(layerIndex++, mat, SlicerFile) + var layer = new Layer(layerIndex++, mat, SlicerFile) { PositionZ = (float)currentHeight, ExposureTime = isBottomLayer ? (float)bottomExposure : (float)normalExposure, - LiftHeight = isBottomLayer ? SlicerFile.BottomLiftHeight : SlicerFile.LiftHeight, - LiftSpeed = isBottomLayer ? SlicerFile.BottomLiftSpeed : SlicerFile.LiftSpeed, - RetractSpeed = SlicerFile.RetractSpeed, - LightOffDelay = isBottomLayer ? SlicerFile.BottomLightOffDelay : SlicerFile.LightOffDelay, - LightPWM = isBottomLayer ? SlicerFile.BottomLightPWM : SlicerFile.LightPWM, IsModified = true }; newLayers.Add(layer); @@ -2249,8 +2244,8 @@ namespace UVtools.Core.Operations var layers = SlicerFile.LayerManager.GetSamePositionedLayers(); foreach (var layer in layers) { - if(_samePositionedLayersLiftHeightEnabled) layer.LiftHeight = (float) _samePositionedLayersLiftHeight; - if(_samePositionedLayersLightOffDelayEnabled) layer.LightOffDelay = (float) _samePositionedLayersLightOffDelay; + if(_samePositionedLayersLiftHeightEnabled) layer.LiftHeightTotal = (float) _samePositionedLayersLiftHeight; + if(_samePositionedLayersLightOffDelayEnabled) layer.LightOffDelay = (float) _samePositionedLayersLightOffDelay; } } diff --git a/UVtools.Core/Operations/OperationDynamicLifts.cs b/UVtools.Core/Operations/OperationDynamicLifts.cs index 739c485..ea82e3d 100644 --- a/UVtools.Core/Operations/OperationDynamicLifts.cs +++ b/UVtools.Core/Operations/OperationDynamicLifts.cs @@ -252,10 +252,10 @@ namespace UVtools.Core.Operations { base.InitWithSlicerFile(); - if(_minBottomLiftHeight <= 0) _minBottomLiftHeight = SlicerFile.BottomLiftHeight; + if(_minBottomLiftHeight <= 0) _minBottomLiftHeight = SlicerFile.BottomLiftHeightTotal; if (_maxBottomLiftHeight <= 0 || _maxBottomLiftHeight < _minBottomLiftHeight) _maxBottomLiftHeight = _minBottomLiftHeight; - if (_minLiftHeight <= 0) _minLiftHeight = SlicerFile.LiftHeight; + if (_minLiftHeight <= 0) _minLiftHeight = SlicerFile.LiftHeightTotal; if (_maxLiftHeight <= 0 || _maxLiftHeight < _minLiftHeight) _maxLiftHeight = _minLiftHeight; if (_minBottomLiftSpeed <= 0) _minBottomLiftSpeed = SlicerFile.BottomLiftSpeed; @@ -340,7 +340,8 @@ namespace UVtools.Core.Operations liftSpeed = (_maxLiftSpeed - (_maxLiftSpeed * layer.NonZeroPixelCount / maxNormalPixels)).Clamp(_minLiftSpeed, _maxLiftSpeed); } - layer.LiftHeight = (float) Math.Round(liftHeight, 1); + layer.RetractHeight2 = 0; + layer.LiftHeightTotal = (float) Math.Round(liftHeight, 1); layer.LiftSpeed = (float) Math.Round(liftSpeed, 1); switch (_lightOffDelaySetMode) diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index b2d41a2..42df498 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description> - <Version>2.17.0</Version> + <Version>2.18.0</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs index e762ac3..543738a 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs @@ -311,8 +311,8 @@ <Component Id="owc6F31DEF09608AA645959666A4CB7FBBC" Guid="c94570a5-5bb4-a53f-e12f-9581bddf7d08" Win64="yes"> <File Id="owf6F31DEF09608AA645959666A4CB7FBBC" Source="$(var.SourceDir)\mscordaccore.dll" KeyPath="yes" /> </Component> - <Component Id="owc0365879AFD7A138F231419745CD7CD56" Guid="419f9e5f-a09c-aa5d-379f-f6b5994c509d" Win64="yes"> - <File Id="owf0365879AFD7A138F231419745CD7CD56" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_5.0.821.31504.dll" KeyPath="yes" /> + <Component Id="owc428BBD3686267EBE46F26528F46752D2" Guid="51ee2d87-3a8b-16e2-c5e8-4523e0869c04" Win64="yes"> + <File Id="owf428BBD3686267EBE46F26528F46752D2" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_5.0.921.35908.dll" KeyPath="yes" /> </Component> <Component Id="owc5FC34571A1AE47A011FC6C2A95B00DA6" Guid="2591451e-0fe5-ccad-abf6-1d1097251253" Win64="yes"> <File Id="owf5FC34571A1AE47A011FC6C2A95B00DA6" Source="$(var.SourceDir)\mscordbi.dll" KeyPath="yes" /> diff --git a/UVtools.WPF/ConsoleArguments.cs b/UVtools.WPF/ConsoleArguments.cs new file mode 100644 index 0000000..7839392 --- /dev/null +++ b/UVtools.WPF/ConsoleArguments.cs @@ -0,0 +1,152 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ +using System; +using System.Collections.Generic; +using System.IO; +using MoreLinq; +using UVtools.Core.FileFormats; + +namespace UVtools.WPF +{ + public static class ConsoleArguments + { + /// <summary> + /// Parse arguments from command line + /// </summary> + /// <param name="args"></param> + /// <returns>True if is a valid argument, otherwise false</returns> + public static bool ParseArgs(string[] args) + { + if(args is null || args.Length == 0) return false; + + // Convert to other file + if (args[0] is "-c" or "--convert") + { + if (args.Length < 3) + { + Console.WriteLine("Invalid syntax: <input_file> <output_file1/ext1> [output_file2/ext2]"); + return true; + } + if (!File.Exists(args[1])) + { + Console.WriteLine($"Input file does not exists: {args[1]}"); + return true; + } + + var slicerFile = FileFormat.FindByExtension(args[1], true, true); + if (slicerFile is null) + { + Console.WriteLine($"Invalid input file: {args[1]}"); + return true; + } + + + var filenameNoExt = FileFormat.GetFileNameStripExtensions(args[1], out _); + + var filesToConvert = new List<KeyValuePair<FileFormat, string>>(); + + for (int i = 2; i < args.Length; i++) + { + var outputFile = args[i]; + var targetFormat = FileFormat.FindByExtension(args[i]); + if (targetFormat is not null) + { + outputFile = $"{filenameNoExt}.{args[i]}"; + } + else + { + targetFormat = FileFormat.FindByExtension(args[i], true); + } + + if (targetFormat is null) + { + Console.WriteLine($"Invalid output file/extension: {args[i]}"); + continue; + } + + filesToConvert.Add(new KeyValuePair<FileFormat, string>(targetFormat, outputFile)); + } + + if (filesToConvert.Count == 0) + { + return true; + } + + //var workingDir = Path.GetDirectoryName(args[1]); + //if(!string.IsNullOrWhiteSpace(workingDir)) Directory.SetCurrentDirectory(workingDir); + + Console.WriteLine($"Loading file: {args[1]}"); + slicerFile.Decode(args[1]); + + foreach (var (outputSlicerFile, outputFile) in filesToConvert.DistinctBy(pair => pair.Value)) + { + Console.WriteLine($"Converting to: {outputFile}"); + slicerFile.Convert(outputSlicerFile, outputFile); + Console.WriteLine("Converted"); + + } + + Console.WriteLine("OK"); + + return true; + } + + // Extract the file + if (args[0] is "-e" or "--extract") + { + if (args.Length < 2) + { + Console.WriteLine("Invalid syntax: <input_file> [output_folder]"); + return true; + } + if (!File.Exists(args[1])) + { + Console.WriteLine($"Input file does not exists: {args[1]}"); + return true; + } + + var slicerFile = FileFormat.FindByExtension(args[1], true, true); + if (slicerFile is null) + { + Console.WriteLine($"Invalid input file: {args[1]}"); + return true; + } + + var outputFolder = FileFormat.GetFileNameStripExtensions(args[1], out _); + + if (args.Length >= 3 && !string.IsNullOrWhiteSpace(args[2])) + { + try + { + Path.GetFullPath(outputFolder); + outputFolder = args[2]; + } + catch (Exception) + { + Console.WriteLine($"Invalid output directory: {args[2]}"); + return true; + } + } + + //var workingDir = Path.GetDirectoryName(args[1]); + //if(!string.IsNullOrWhiteSpace(workingDir)) Directory.SetCurrentDirectory(workingDir); + + Console.WriteLine($"Loading file: {args[1]}"); + slicerFile.Decode(args[1]); + Console.WriteLine($"Extracting to: {outputFolder}"); + slicerFile.Extract(outputFolder); + Console.WriteLine("Extracted"); + Console.WriteLine("OK"); + return true; + } + + + return false; + } + } +}
\ No newline at end of file diff --git a/UVtools.WPF/MainWindow.Information.cs b/UVtools.WPF/MainWindow.Information.cs index 3efc701..60563ce 100644 --- a/UVtools.WPF/MainWindow.Information.cs +++ b/UVtools.WPF/MainWindow.Information.cs @@ -348,8 +348,13 @@ namespace UVtools.WPF if (SlicerFile.CanUseLayerLiftHeight) CurrentLayerProperties.Add(new ValueDescription($"{layer.LiftHeight.ToString(CultureInfo.InvariantCulture)}mm @ {layer.LiftSpeed.ToString(CultureInfo.InvariantCulture)}mm/min", nameof(layer.LiftHeight))); + if (SlicerFile.CanUseLayerLiftHeight2) + CurrentLayerProperties.Add(new ValueDescription($"{layer.LiftHeight2.ToString(CultureInfo.InvariantCulture)}mm @ {layer.LiftSpeed2.ToString(CultureInfo.InvariantCulture)}mm/min", nameof(layer.LiftHeight2))); + if (SlicerFile.CanUseLayerRetractSpeed) - CurrentLayerProperties.Add(new ValueDescription($"{layer.RetractSpeed}mm/min", nameof(layer.RetractSpeed))); + CurrentLayerProperties.Add(new ValueDescription($"{layer.RetractHeight.ToString(CultureInfo.InvariantCulture)}mm @ {layer.RetractSpeed}mm/min", nameof(layer.RetractHeight))); + if (SlicerFile.CanUseLayerRetractHeight2) + CurrentLayerProperties.Add(new ValueDescription($"{layer.RetractHeight2.ToString(CultureInfo.InvariantCulture)}mm @ {layer.RetractSpeed2}mm/min", nameof(layer.RetractHeight2))); if (SlicerFile.CanUseLayerLightOffDelay) CurrentLayerProperties.Add(new ValueDescription($"{layer.LightOffDelay}s", nameof(layer.LightOffDelay))); diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs index 48a070b..b3ee62d 100644 --- a/UVtools.WPF/Program.cs +++ b/UVtools.WPF/Program.cs @@ -20,6 +20,16 @@ namespace UVtools.WPF ProgramStartupTime = Stopwatch.StartNew(); Args = args; + try + { + if (ConsoleArguments.ParseArgs(args)) return; + } + catch (Exception e) + { + Console.WriteLine(e); + return; + } + /*Slicer slicer = new(Size.Empty, SizeF.Empty, "D:\\Cube100x100x100.stl"); var slices = slicer.SliceModel(0.05f); diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 35bb601..42cae7b 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ <PackageLicenseFile>LICENSE</PackageLicenseFile> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryType>Git</RepositoryType> - <Version>2.17.0</Version> + <Version>2.18.0</Version> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> diff --git a/build/CreateRelease.WPF.ps1 b/build/CreateRelease.WPF.ps1 index 4985e72..218e9b5 100644 --- a/build/CreateRelease.WPF.ps1 +++ b/build/CreateRelease.WPF.ps1 @@ -32,10 +32,10 @@ Set-Location $PSScriptRoot\.. #################################### ### Configuration ### #################################### -$enableMSI = $true -#$buildOnly = 'win-x64' +#$enableMSI = $true +$buildOnly = 'win-x64' #$buildOnly = 'linux-x64' -$enableNugetPublish = $true +#$enableNugetPublish = $true # Profilling $stopWatch = New-Object -TypeName System.Diagnostics.Stopwatch $deployStopWatch = New-Object -TypeName System.Diagnostics.Stopwatch |