diff --git a/CHANGELOG.md b/CHANGELOG.md index b334b34..339a7f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 24/07/2021 - v2.15.1 + +- **(Improvement) CWS:** + - Remove light-off delay from the format + - Sync movements with a delay time + - Auto convert the light-off delay time to wait before cure time when required +- **(Improvement) CTB:** + - When positively set the 'Wait time before cure' property on a CTBv3 or lower, it will compute the right light-off delay with that extra time into consideration + - When positively set any of the light-off delays on a CTBv4 it will auto zero the 'Wait times' properties and vice-versa + - Automation to set light-off delay on file load, will no longer do when any of 'Wait times' are defined for a CTBv4 +- (Improvement) PrusaSlicer printers that use .cws format, implement the wait times on printer notes +- (Fix) GCode parser: Commented commands were being parsed +- (Fix) Exposure time information on bottom status bar was inverted, showing normal/bottom time instead of bottom/normal +- (Fix) macOS: Installing libusb is no longer a requirement + ## 16/07/2021 - v2.15.0 - **File formats:** diff --git a/Documentation/rgb.cws/Photonsters.url b/Documentation/rgb.cws/Photonsters.url new file mode 100644 index 0000000..e6b9d1a --- /dev/null +++ b/Documentation/rgb.cws/Photonsters.url @@ -0,0 +1,5 @@ +[{000214A0-0000-0000-C000-000000000046}] +Prop3=19,11 +[InternetShortcut] +IDList= +URL=https://www.facebook.com/Photonsters/posts/1198980267222618 diff --git a/Documentation/rgb.cws/decode.jpg b/Documentation/rgb.cws/decode.jpg new file mode 100644 index 0000000..4e41c2e Binary files /dev/null and b/Documentation/rgb.cws/decode.jpg differ diff --git a/Documentation/rgb.cws/encode.jpg b/Documentation/rgb.cws/encode.jpg new file mode 100644 index 0000000..28d3aa3 Binary files /dev/null and b/Documentation/rgb.cws/encode.jpg differ diff --git a/PrusaSlicer/printer/Nova3D Bene4 Mono.ini b/PrusaSlicer/printer/Nova3D Bene4 Mono.ini index 9c10514..3899d99 100644 --- a/PrusaSlicer/printer/Nova3D Bene4 Mono.ini +++ b/PrusaSlicer/printer/Nova3D Bene4 Mono.ini @@ -1,4 +1,4 @@ -# generated by PrusaSlicer 2.3.1+win64 on 2021-04-29 at 17:56:19 UTC +# generated by PrusaSlicer 2.3.1+win64 on 2021-07-24 at 01:01:47 UTC absolute_correction = 0 area_fill = 50 bed_custom_model = @@ -26,7 +26,7 @@ min_exposure_time = 1 min_initial_exposure_time = 1 print_host = printer_model = SL1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_NOVA3D\nPRINTER_MODEL_BENE4_MONO\nFILEFORMAT_RGB.CWS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_120\nLiftSpeed_120\nRetractSpeed_120\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_NOVA3D\nPRINTER_MODEL_BENE4_MONO\nFILEFORMAT_RGB.CWS\n\nSTART_CUSTOM_VALUES\nBottomWaitBeforeCure_3\nWaitBeforeCure_2.5\nBottomWaitAfterCure_3\nWaitAfterCure_1\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_120\nLiftSpeed_120\nBottomWaitAfterLift_0\nWaitAfterLift_0\nRetractSpeed_120\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES printer_settings_id = printer_technology = SLA printer_variant = default diff --git a/PrusaSlicer/printer/Nova3D Elfin.ini b/PrusaSlicer/printer/Nova3D Elfin.ini index 336c23b..bbf3d07 100644 --- a/PrusaSlicer/printer/Nova3D Elfin.ini +++ b/PrusaSlicer/printer/Nova3D Elfin.ini @@ -1,4 +1,4 @@ -# generated by PrusaSlicer 2.3.0+win64 on 2021-01-13 at 02:35:32 UTC +# generated by PrusaSlicer 2.3.1+win64 on 2021-07-24 at 01:02:10 UTC absolute_correction = 0 area_fill = 50 bed_custom_model = @@ -26,7 +26,7 @@ min_exposure_time = 1 min_initial_exposure_time = 1 print_host = printer_model = SL1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_NOVA3D\nPRINTER_MODEL_ELFIN\nFILEFORMAT_CWS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_120\nLiftSpeed_120\nRetractSpeed_120\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_NOVA3D\nPRINTER_MODEL_ELFIN\nFILEFORMAT_CWS\n\nSTART_CUSTOM_VALUES\nBottomWaitBeforeCure_3\nWaitBeforeCure_2.5\nBottomWaitAfterCure_3\nWaitAfterCure_1\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_120\nLiftSpeed_120\nBottomWaitAfterLift_0\nWaitAfterLift_0\nRetractSpeed_120\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES printer_settings_id = printer_technology = SLA printer_variant = default diff --git a/PrusaSlicer/printer/Phrozen Sonic Mini.ini b/PrusaSlicer/printer/Phrozen Sonic Mini.ini index bb8a159..39c3a6f 100644 --- a/PrusaSlicer/printer/Phrozen Sonic Mini.ini +++ b/PrusaSlicer/printer/Phrozen Sonic Mini.ini @@ -1,4 +1,4 @@ -# generated by PrusaSlicer 2.3.0+win64 on 2021-03-21 at 18:54:48 UTC +# generated by PrusaSlicer 2.3.1+win64 on 2021-07-21 at 02:49:31 UTC absolute_correction = 0 area_fill = 50 bed_custom_model = @@ -26,7 +26,7 @@ min_exposure_time = 1 min_initial_exposure_time = 1 print_host = printer_model = SL1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_PHROZEN\nPRINTER_MODEL_SONIC_MINI\nFILEFORMAT_PHZ\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_8\nLiftHeight_6\nBottomLiftSpeed_100\nLiftSpeed_100\nRetractSpeed_200\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_PHROZEN\nPRINTER_MODEL_SONIC_MINI\nFILEFORMAT_PHZ\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_100\nLiftSpeed_150\nRetractSpeed_200\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES printer_settings_id = printer_technology = SLA printer_variant = default diff --git a/PrusaSlicer/printer/Wanhao D7.ini b/PrusaSlicer/printer/Wanhao D7.ini index e8b4733..f05b95a 100644 --- a/PrusaSlicer/printer/Wanhao D7.ini +++ b/PrusaSlicer/printer/Wanhao D7.ini @@ -1,4 +1,4 @@ -# generated by PrusaSlicer 2.3.0+win64 on 2021-04-09 at 19:00:09 UTC +# generated by PrusaSlicer 2.3.1+win64 on 2021-07-24 at 01:02:43 UTC absolute_correction = 0 area_fill = 50 bed_custom_model = @@ -26,7 +26,7 @@ min_exposure_time = 1 min_initial_exposure_time = 1 print_host = printer_model = SL1 -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_WANHAO\nPRINTER_MODEL_D7\nFILEFORMAT_XML.CWS\n\nSTART_CUSTOM_VALUES\nLightOffDelay_0\nBottomLightOffDelay_0\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_60\nLiftSpeed_60\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\nPRINTER_VENDOR_WANHAO\nPRINTER_MODEL_D7\nFILEFORMAT_XML.CWS\n\nSTART_CUSTOM_VALUES\nBottomWaitBeforeCure_3\nWaitBeforeCure_2.5\nBottomWaitAfterCure_3\nWaitAfterCure_1\nBottomLiftHeight_5\nLiftHeight_5\nBottomLiftSpeed_60\nLiftSpeed_60\nBottomWaitAfterLift_0\nWaitAfterLift_0\nRetractSpeed_150\nBottomLightPWM_255\nLightPWM_255\nEND_CUSTOM_VALUES printer_settings_id = printer_technology = SLA printer_variant = default diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 6a22b78..137f7f5 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -18,7 +18,6 @@ using System.Threading.Tasks; using System.Xml.Serialization; using Emgu.CV; using Emgu.CV.CvEnum; -using Emgu.CV.Util; using UVtools.Core.Extensions; using UVtools.Core.GCode; using UVtools.Core.Operations; @@ -310,8 +309,8 @@ namespace UVtools.Core.FileFormats public override PrintParameterModifier[] PrintParameterModifiers { get; } = { PrintParameterModifier.BottomLayerCount, - PrintParameterModifier.BottomLightOffDelay, - PrintParameterModifier.LightOffDelay, + //PrintParameterModifier.BottomLightOffDelay, + //PrintParameterModifier.LightOffDelay, PrintParameterModifier.BottomWaitTimeBeforeCure, PrintParameterModifier.WaitTimeBeforeCure, @@ -347,7 +346,7 @@ namespace UVtools.Core.FileFormats PrintParameterModifier.LightPWM, }; - public override System.Drawing.Size[] ThumbnailsOriginalSize { get; } = null; + public override System.Drawing.Size[] ThumbnailsOriginalSize => null; public override uint ResolutionX { @@ -542,6 +541,7 @@ namespace UVtools.Core.FileFormats { GCode = new GCodeBuilder { + SyncMovementsWithDelay = true, UseComments = true, GCodePositioningType = GCodeBuilder.GCodePositioningTypes.Partial, GCodeSpeedUnit = GCodeBuilder.GCodeSpeedUnits.MillimetersPerMinute, diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index db930c6..512ae46 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -1294,13 +1294,31 @@ namespace UVtools.Core.FileFormats public override float BottomLightOffDelay { get => PrintParametersSettings.BottomLightOffDelay; - set => base.BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay = (float)Math.Round(value, 2); + set + { + base.BottomLightOffDelay = PrintParametersSettings.BottomLightOffDelay = (float) Math.Round(value, 2); + if (HeaderSettings.Version >= 4 && value > 0) + { + WaitTimeBeforeCure = 0; + WaitTimeAfterCure = 0; + WaitTimeAfterLift = 0; + } + } } public override float LightOffDelay { get => PrintParametersSettings.LightOffDelay; - set => base.LightOffDelay = HeaderSettings.LightOffDelay = PrintParametersSettings.LightOffDelay = (float)Math.Round(value, 2); + set + { + base.LightOffDelay = HeaderSettings.LightOffDelay = PrintParametersSettings.LightOffDelay = (float) Math.Round(value, 2); + if (HeaderSettings.Version >= 4 && value > 0) + { + WaitTimeBeforeCure = 0; + WaitTimeAfterCure = 0; + WaitTimeAfterLift = 0; + } + } } public override float BottomWaitTimeBeforeCure => WaitTimeBeforeCure; @@ -1309,8 +1327,22 @@ namespace UVtools.Core.FileFormats get => HeaderSettings.Version >= 4 ? PrintParametersV4Settings.RestTimeAfterRetract : 0; set { - if (HeaderSettings.Version < 4) return; + if (HeaderSettings.Version < 4) + { + if (value > 0) + { + SetBottomLightOffDelay(value); + SetNormalLightOffDelay(value); + } + + return; + } base.WaitTimeBeforeCure = SlicerInfoSettings.RestTimeAfterRetract = PrintParametersV4Settings.RestTimeAfterRetract = (float)Math.Round(value, 2); + if (value > 0) + { + BottomLightOffDelay = 0; + LightOffDelay = 0; + } } } @@ -1327,7 +1359,12 @@ namespace UVtools.Core.FileFormats set { if (HeaderSettings.Version < 4) return; - base.WaitTimeAfterCure = PrintParametersV4Settings.RestTimeBeforeLift = (float)Math.Round(value, 2); + base.WaitTimeAfterCure = PrintParametersV4Settings.RestTimeBeforeLift = (float) Math.Round(value, 2); + if (value > 0) + { + BottomLightOffDelay = 0; + LightOffDelay = 0; + } } } @@ -1369,6 +1406,11 @@ namespace UVtools.Core.FileFormats { if (HeaderSettings.Version < 4) return; base.WaitTimeAfterLift = SlicerInfoSettings.RestTimeAfterLift = SlicerInfoSettings.RestTimeAfterLift2 = PrintParametersV4Settings.RestTimeAfterLift = (float)Math.Round(value, 2); + if (value > 0) + { + BottomLightOffDelay = 0; + LightOffDelay = 0; + } } } diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index ae96149..5f627c4 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -1079,12 +1079,12 @@ namespace UVtools.Core.FileFormats if (CanUseBottomExposureTime) { - str += ExposureTime.ToString(CultureInfo.InvariantCulture); + str += BottomExposureTime.ToString(CultureInfo.InvariantCulture); } if (CanUseExposureTime) { if (!string.IsNullOrEmpty(str)) str += '/'; - str += BottomExposureTime.ToString(CultureInfo.InvariantCulture); + str += ExposureTime.ToString(CultureInfo.InvariantCulture); } if (!string.IsNullOrEmpty(str)) str += 's'; diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index 8bdbfff..282c624 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -19,6 +19,7 @@ using Org.BouncyCastle.Asn1.Cms; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Objects; +using UVtools.Core.Operations; namespace UVtools.Core.GCode { @@ -98,12 +99,14 @@ namespace UVtools.Core.GCode #region Members private readonly StringBuilder _gcode = new(); - private bool _useTailComma = true; - private bool _useComments = true; + private GCodePositioningTypes _gCodePositioningType = GCodePositioningTypes.Absolute; private GCodeTimeUnits _gCodeTimeUnit = GCodeTimeUnits.Milliseconds; private GCodeSpeedUnits _gCodeSpeedUnit = GCodeSpeedUnits.MillimetersPerMinute; private GCodeShowImageTypes _gCodeShowImageType = GCodeShowImageTypes.FilenameNonZeroPNG; + private bool _syncMovementsWithDelay; + private bool _useTailComma = true; + private bool _useComments = true; private ushort _maxLedPower = byte.MaxValue; private uint _lineCount; private GCodeMoveCommands _layerMoveCommand; @@ -149,6 +152,12 @@ namespace UVtools.Core.GCode set => RaiseAndSetIfChanged(ref _endGCodeMoveCommand, value); } + public bool SyncMovementsWithDelay + { + get => _syncMovementsWithDelay; + set => RaiseAndSetIfChanged(ref _syncMovementsWithDelay, value); + } + public bool UseTailComma { get => _useTailComma; @@ -384,12 +393,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) + public void AppendLiftMoveGx(float upZ, float upFeedRate, float downZ, float downFeedRate, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null) { if (_layerMoveCommand == GCodeMoveCommands.G0) - AppendLiftMoveG0(upZ, upFeedRate, downZ, downFeedRate, waitAfterLift, waitAfterRetract); + AppendLiftMoveG0(upZ, upFeedRate, downZ, downFeedRate, waitAfterLift, waitAfterRetract, layer); else - AppendLiftMoveG1(upZ, upFeedRate, downZ, downFeedRate, waitAfterLift, waitAfterRetract); + AppendLiftMoveG1(upZ, upFeedRate, downZ, downFeedRate, waitAfterLift, waitAfterRetract, layer); } @@ -399,10 +408,17 @@ 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) + public void AppendLiftMoveG0(float upZ, float upFeedRate, float downZ, float downFeedRate, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null) { if (upZ == 0 || upFeedRate <= 0) return; AppendLineOverrideComment(CommandMoveG0, "Z Lift", upZ, upFeedRate); // Z Lift + if (_syncMovementsWithDelay && layer is not null) + { + // Finish this + var seconds = OperationCalculator.LightOffDelayC.CalculateSecondsLiftOnly(layer.LiftHeight, layer.LiftSpeed, 0.75f); + var time = ConvertFromSeconds(seconds); + AppendWaitG4($"0{time}", "Sync movement"); + } if (waitAfterLift > 0) { @@ -412,6 +428,13 @@ namespace UVtools.Core.GCode 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 time = ConvertFromSeconds(seconds); + AppendWaitG4($"0{time}", "Sync movement"); + } } if (waitAfterRetract > 0) @@ -426,10 +449,20 @@ 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) + public void AppendLiftMoveG1(float upZ, float upFeedRate, float downZ, float downFeedRate, float waitAfterLift = 0, float waitAfterRetract = 0, Layer layer = null) { if (upZ == 0 || upFeedRate <= 0) return; AppendLineOverrideComment(CommandMoveG1, "Lift Z", upZ, upFeedRate); // Z Lift + 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"); + } + } if (waitAfterLift > 0) { @@ -439,6 +472,16 @@ namespace UVtools.Core.GCode 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"); + } + } } if (waitAfterRetract > 0) @@ -453,6 +496,13 @@ namespace UVtools.Core.GCode AppendLineOverrideComment(CommandWaitG4, comment, time); } + public void AppendWaitG4(string timeStr, string comment = null) + { + if (!float.TryParse(timeStr, out var time)) return; + if (time < 0) return; + AppendLineOverrideComment(CommandWaitG4, comment, timeStr); + } + public void AppendTurnLightM106(ushort value) { AppendLineOverrideComment(CommandTurnLEDM106, "Turn LED " + (value == 0 ? "OFF" : "ON"), value); @@ -560,7 +610,7 @@ namespace UVtools.Core.GCode if (liftHeight > 0 && liftZPosAbs > layer.PositionZ) { - AppendLiftMoveGx(liftZPos, liftSpeed, retractPos, retractSpeed, waitAfterLift); + AppendLiftMoveGx(liftZPos, liftSpeed, retractPos, retractSpeed, waitAfterLift, 0, layer); } else if (lastZPosition < layer.PositionZ) // Ensure Z is on correct position { @@ -675,7 +725,8 @@ namespace UVtools.Core.GCode string line; while ((line = reader.ReadLine()) != null) { - if(string.IsNullOrWhiteSpace(line)) continue; + line = line.Trim(); + if (string.IsNullOrWhiteSpace(line)) continue; // Search for and switch position type when needed if (line.StartsWith(CommandPositioningAbsoluteG90.Command)) @@ -690,145 +741,174 @@ namespace UVtools.Core.GCode continue; } - var match = Regex.Match(line, CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"(\d+)")), RegexOptions.IgnoreCase); - if (match.Success && match.Groups.Count >= 2) // Begin new layer + Match match = null; + + // Display image + if (line.StartsWith(CommandShowImageM6054.Command)) { - var layerIndex = uint.Parse(match.Groups[1].Value); - if (_gCodeShowImageType is GCodeShowImageTypes.FilenameNonZeroPNG or GCodeShowImageTypes.LayerIndexNonZero) layerIndex--; - if (layerIndex > slicerFile.LayerCount) + match = Regex.Match(line, + CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"(\d+)")), + RegexOptions.IgnoreCase); + if (match.Success && match.Groups.Count >= 2) // Begin new layer { - throw new FileLoadException($"GCode parser detected the layer {layerIndex}, but the file was sliced to {slicerFile.LayerCount} layers.", slicerFile.FileFullPath); + var layerIndex = uint.Parse(match.Groups[1].Value); + if (_gCodeShowImageType is GCodeShowImageTypes.FilenameNonZeroPNG or GCodeShowImageTypes + .LayerIndexNonZero) layerIndex--; + if (layerIndex > slicerFile.LayerCount) + { + throw new FileLoadException( + $"GCode parser detected the layer {layerIndex}, but the file was sliced to {slicerFile.LayerCount} layers.", + slicerFile.FileFullPath); + } + + // Propagate values before switch to the new layer + layerBlock.PositionZ ??= positionZ; + layerBlock.SetLayer(); + + layerBlock.Init(); + layerBlock.LayerIndex = layerIndex; + + continue; } - - // Propagate values before switch to the new layer - layerBlock.PositionZ ??= positionZ; - layerBlock.SetLayer(); - - layerBlock.Init(); - layerBlock.LayerIndex = layerIndex; - - continue; } if (!layerBlock.IsValid) continue; // No layer yet found to work on, skip - // Check moves - var moveG0Regex = Regex.Match(line, CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"), RegexOptions.IgnoreCase); - var moveG1Regex = Regex.Match(line, CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"), RegexOptions.IgnoreCase); - match = moveG0Regex.Success && moveG0Regex.Groups.Count >= 2 ? moveG0Regex : moveG1Regex; - - if (match.Success && match.Groups.Count >= 4 && !layerBlock.RetractSpeed.HasValue) + if (line.StartsWith(CommandMoveG0.Command) || line.StartsWith(CommandMoveG1.Command)) { - float pos = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture); - float speed = ConvertToMillimetersPerMinute(float.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture)); + var moveG0Regex = Regex.Match(line, + CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"), + RegexOptions.IgnoreCase); + var moveG1Regex = Regex.Match(line, + CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"), + RegexOptions.IgnoreCase); + match = moveG0Regex.Success && moveG0Regex.Groups.Count >= 2 ? moveG0Regex : moveG1Regex; - if (!layerBlock.PositionZ.HasValue) // Lift or pos, set here for now + if (match.Success && match.Groups.Count >= 4 && !layerBlock.RetractSpeed.HasValue) { + 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 + { + switch (positionType) + { + case GCodePositioningTypes.Absolute: + layerBlock.PositionZ = pos; + break; + case GCodePositioningTypes.Partial: + layerBlock.PositionZ = Layer.RoundHeight(positionZ + pos); + break; + } + + layerBlock.LiftSpeed = speed; + continue; + } + + + // ~90% sure its retract here, still is possible to bug this with 2 lifts + layerBlock.RetractSpeed = speed; + switch (positionType) { case GCodePositioningTypes.Absolute: - layerBlock.PositionZ = pos; + layerBlock.LiftHeight = Layer.RoundHeight(layerBlock.PositionZ.Value - pos); + layerBlock.PositionZ = positionZ = pos; break; case GCodePositioningTypes.Partial: - layerBlock.PositionZ = Layer.RoundHeight(positionZ + pos); + layerBlock.LiftHeight = layerBlock.PositionZ - positionZ; + layerBlock.PositionZ = positionZ = Layer.RoundHeight(layerBlock.PositionZ.Value + pos); break; } - layerBlock.LiftSpeed = speed; continue; } - - - // ~90% sure its retract here, still is possible to bug this with 2 lifts - layerBlock.RetractSpeed = speed; - - switch (positionType) - { - case GCodePositioningTypes.Absolute: - layerBlock.LiftHeight = Layer.RoundHeight(layerBlock.PositionZ.Value - pos); - layerBlock.PositionZ = positionZ = pos; - break; - case GCodePositioningTypes.Partial: - layerBlock.LiftHeight = layerBlock.PositionZ - positionZ; - layerBlock.PositionZ = positionZ = Layer.RoundHeight(layerBlock.PositionZ.Value + pos); - break; - } - - continue; } // Check for waits - match = Regex.Match(line, CommandWaitG4.ToStringWithoutComments(@"(([0-9]*[.])?[0-9]+)"), RegexOptions.IgnoreCase); - if (match.Success && match.Groups.Count >= 2) + if (line.StartsWith(CommandWaitG4.Command)) { - var waitTime = float.Parse(match.Groups[1].Value); - - if (layerBlock.PositionZ.HasValue && !layerBlock.RetractSpeed.HasValue) // Must be wait time after lift, if not, don't blame me! + match = Regex.Match(line, CommandWaitG4.ToStringWithoutComments(@"(([0-9]*[.])?[0-9]+)"), + RegexOptions.IgnoreCase); + if (match.Success && match.Groups.Count >= 2) { - layerBlock.WaitTimeAfterLift ??= 0; - layerBlock.WaitTimeAfterLift += ConvertToSeconds(waitTime); - continue; - } + if (_syncMovementsWithDelay && match.Groups[1].Value.StartsWith('0')) continue; // Sync movement delay, skip - if (!layerBlock.LightPWM.HasValue) // Before cure - { - layerBlock.WaitTimeBeforeCure ??= 0; - layerBlock.WaitTimeBeforeCure += ConvertToSeconds(waitTime); - continue; - } + var waitTime = float.Parse(match.Groups[1].Value); - if (layerBlock.IsExposing) // Must be exposure time, if not, don't blame me! - { - layerBlock.ExposureTime ??= 0; - layerBlock.ExposureTime += ConvertToSeconds(waitTime); - continue; - } + if (layerBlock.PositionZ.HasValue && + !layerBlock.RetractSpeed.HasValue) // Must be wait time after lift, if not, don't blame me! + { + layerBlock.WaitTimeAfterLift ??= 0; + layerBlock.WaitTimeAfterLift += ConvertToSeconds(waitTime); + continue; + } - if (layerBlock.IsAfterLightOff) - { - if (!layerBlock.WaitTimeBeforeCure.HasValue) // Novamaker fix, delay on last line, broke logic but safer + if (!layerBlock.LightPWM.HasValue) // Before cure { layerBlock.WaitTimeBeforeCure ??= 0; layerBlock.WaitTimeBeforeCure += ConvertToSeconds(waitTime); + continue; } - else + + if (layerBlock.IsExposing) // Must be exposure time, if not, don't blame me! { - layerBlock.WaitTimeAfterCure ??= 0; - layerBlock.WaitTimeAfterCure += ConvertToSeconds(waitTime); + layerBlock.ExposureTime ??= 0; + layerBlock.ExposureTime += ConvertToSeconds(waitTime); + continue; } - + + if (layerBlock.IsAfterLightOff) + { + if (!layerBlock.WaitTimeBeforeCure + .HasValue) // Novamaker fix, delay on last line, broke logic but safer + { + layerBlock.WaitTimeBeforeCure ??= 0; + layerBlock.WaitTimeBeforeCure += ConvertToSeconds(waitTime); + } + else + { + layerBlock.WaitTimeAfterCure ??= 0; + layerBlock.WaitTimeAfterCure += ConvertToSeconds(waitTime); + } + + continue; + } + continue; } - - continue; } // Check LightPWM - match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"), RegexOptions.IgnoreCase); - if (match.Success && match.Groups.Count >= 2) + if (line.StartsWith(CommandTurnLEDM106.Command)) { - byte pwm; - if (_maxLedPower == byte.MaxValue) + match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"), + RegexOptions.IgnoreCase); + if (match.Success && match.Groups.Count >= 2) { - pwm = byte.Parse(match.Groups[1].Value); - } - else - { - ushort pwmValue = ushort.Parse(match.Groups[1].Value); - pwm = (byte)(pwmValue * byte.MaxValue / _maxLedPower); - } + byte pwm; + if (_maxLedPower == byte.MaxValue) + { + pwm = byte.Parse(match.Groups[1].Value); + } + else + { + ushort pwmValue = ushort.Parse(match.Groups[1].Value); + pwm = (byte) (pwmValue * byte.MaxValue / _maxLedPower); + } - if (pwm == 0 && layerBlock.LightPWM.HasValue) - { - layerBlock.IsAfterLightOff = true; - } - else if(!layerBlock.IsAfterLightOff) - { - layerBlock.LightPWM = pwm; - } + if (pwm == 0 && layerBlock.LightPWM.HasValue) + { + layerBlock.IsAfterLightOff = true; + } + else if (!layerBlock.IsAfterLightOff) + { + layerBlock.LightPWM = pwm; + } - continue; + continue; + } } } diff --git a/UVtools.Core/GCode/GCodeLayer.cs b/UVtools.Core/GCode/GCodeLayer.cs index a2bdfe4..bf60c02 100644 --- a/UVtools.Core/GCode/GCodeLayer.cs +++ b/UVtools.Core/GCode/GCodeLayer.cs @@ -8,6 +8,7 @@ using System; using UVtools.Core.FileFormats; +using UVtools.Core.Operations; namespace UVtools.Core.GCode { @@ -118,6 +119,15 @@ namespace UVtools.Core.GCode layer.WaitTimeAfterLift = WaitTimeAfterLift ?? 0; layer.RetractSpeed = RetractSpeed ?? SlicerFile.RetractSpeed; layer.LightPWM = LightPWM ?? 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); + if (syncTime < layer.WaitTimeBeforeCure) + { + layer.WaitTimeBeforeCure = (float) Math.Round(layer.WaitTimeBeforeCure - syncTime, 2); + } + } } } } diff --git a/UVtools.Core/Operations/OperationCalculator.cs b/UVtools.Core/Operations/OperationCalculator.cs index 3ea2fe4..6cf526b 100644 --- a/UVtools.Core/Operations/OperationCalculator.cs +++ b/UVtools.Core/Operations/OperationCalculator.cs @@ -271,23 +271,35 @@ namespace UVtools.Core.Operations _bottomWaitTime = bottomWaitTime; } - public static decimal CalculateSeconds(decimal liftHeight, decimal liftSpeed, decimal retract, decimal waitTime = 0) + public static decimal CalculateSeconds(decimal liftHeight, decimal liftSpeed, decimal retractSpeed, decimal extraWaitTime = 0) { - try + var time = extraWaitTime; + if (liftSpeed > 0) { - return Math.Round(liftHeight / (liftSpeed / 60m) + liftHeight / (retract / 60m) + waitTime, 2); + time += liftHeight / (liftSpeed / 60m); } - catch (Exception) + if (retractSpeed > 0) { - return 0; + time += liftHeight / (retractSpeed / 60m); } + + return Math.Round(time, 2); } - public static float CalculateSeconds(float liftHeight, float liftSpeed, float retract, float extraWaitTime = 0) + public static float CalculateSeconds(float liftHeight, float liftSpeed, float retractSpeed, float extraWaitTime = 0) { - if (liftSpeed == 0 || retract == 0) return extraWaitTime; - return (float) Math.Round(liftHeight / (liftSpeed / 60f) + liftHeight / (retract / 60f) + extraWaitTime, 2); - + var time = extraWaitTime; + if (liftSpeed > 0) + { + time += liftHeight / (liftSpeed / 60f); + } + if (retractSpeed > 0) + { + time += liftHeight / (retractSpeed / 60f); + } + + return (float)Math.Round(time, 2); + } public static uint CalculateMilliseconds(float liftHeight, float liftSpeed, float retract, float extraWaitTime = 0) => @@ -296,15 +308,8 @@ namespace UVtools.Core.Operations public static float CalculateSecondsLiftOnly(float liftHeight, float liftSpeed, float extraWaitTime = 0) { - try - { - return (float)Math.Round(liftHeight / (liftSpeed / 60f) + extraWaitTime, 2); - } - catch (Exception) - { - return 0; - } - + if (liftSpeed <= 0) return extraWaitTime; + return (float)Math.Round(liftHeight / (liftSpeed / 60f) + extraWaitTime, 2); } public static uint CalculateMillisecondsLiftOnly(float liftHeight, float liftSpeed, float extraWaitTime = 0) => diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index d1da60f..654fa5e 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 2.15.0 + 2.15.1 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 3b8bb8a..925e554 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -1125,33 +1125,52 @@ namespace UVtools.WPF SlicerFile.CanUseBottomLightOffDelay && (Settings.Automations.ChangeOnlyLightOffDelayIfZero && SlicerFile.BottomLightOffDelay == 0 || !Settings.Automations.ChangeOnlyLightOffDelayIfZero)) { - float lightOff = Settings.Automations.LightOffDelaySetMode switch + if (SlicerFile is ChituboxFile chituboxFile && + chituboxFile.HeaderSettings.Version >= 4 && + (chituboxFile.WaitTimeBeforeCure > 0 || chituboxFile.WaitTimeAfterCure > 0 || chituboxFile.WaitTimeAfterLift > 0)) { - Enumerations.LightOffDelaySetMode.UpdateWithExtraDelay => SlicerFile.CalculateBottomLightOffDelay((float) Settings.Automations.BottomLightOffDelay), - Enumerations.LightOffDelaySetMode.UpdateWithoutExtraDelay => SlicerFile.CalculateBottomLightOffDelay(), - _ => 0 - }; - if (lightOff != SlicerFile.BottomLightOffDelay) - { - modified = true; - SlicerFile.BottomLightOffDelay = lightOff; + // Ignore this automation } + else + { + float lightOff = Settings.Automations.LightOffDelaySetMode switch + { + Enumerations.LightOffDelaySetMode.UpdateWithExtraDelay => SlicerFile.CalculateBottomLightOffDelay((float)Settings.Automations.BottomLightOffDelay), + Enumerations.LightOffDelaySetMode.UpdateWithoutExtraDelay => SlicerFile.CalculateBottomLightOffDelay(), + _ => 0 + }; + if (lightOff != SlicerFile.BottomLightOffDelay) + { + modified = true; + SlicerFile.BottomLightOffDelay = lightOff; + } + } + } if (Settings.Automations.LightOffDelaySetMode != Enumerations.LightOffDelaySetMode.NoAction && SlicerFile.CanUseLightOffDelay && (Settings.Automations.ChangeOnlyLightOffDelayIfZero && SlicerFile.LightOffDelay == 0 || !Settings.Automations.ChangeOnlyLightOffDelayIfZero)) { - float lightOff = Settings.Automations.LightOffDelaySetMode switch + if (SlicerFile is ChituboxFile chituboxFile && + chituboxFile.HeaderSettings.Version >= 4 && + (chituboxFile.WaitTimeBeforeCure > 0 || chituboxFile.WaitTimeAfterCure > 0 || chituboxFile.WaitTimeAfterLift > 0)) { - Enumerations.LightOffDelaySetMode.UpdateWithExtraDelay => SlicerFile.CalculateNormalLightOffDelay((float)Settings.Automations.LightOffDelay), - Enumerations.LightOffDelaySetMode.UpdateWithoutExtraDelay => SlicerFile.CalculateNormalLightOffDelay(), - _ => 0 - }; - if (lightOff != SlicerFile.LightOffDelay) + // Ignore this automation + } + else { - modified = true; - SlicerFile.LightOffDelay = lightOff; + float lightOff = Settings.Automations.LightOffDelaySetMode switch + { + Enumerations.LightOffDelaySetMode.UpdateWithExtraDelay => SlicerFile.CalculateNormalLightOffDelay((float)Settings.Automations.LightOffDelay), + Enumerations.LightOffDelaySetMode.UpdateWithoutExtraDelay => SlicerFile.CalculateNormalLightOffDelay(), + _ => 0 + }; + if (lightOff != SlicerFile.LightOffDelay) + { + modified = true; + SlicerFile.LightOffDelay = lightOff; + } } } diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index e2a8f43..23296a9 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ LICENSE https://github.com/sn4k3/UVtools Git - 2.15.0 + 2.15.1