diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bf0086c..ff46a03c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,35 @@ # Changelog -## 02/01/2022 - v3.10.0 +## 15/01/2023 - v3.11.0 + +- **UI:** + - (Improvement) Layer navigation load time by parallel `Mat` to `Bitmap` conversion + - (Improvement) Allow to show exceptions without the stack trace and detailed trigger action by using the `MessageExceiption` (#644) + - (Improvement) Allow progress to have and display a detailed log (#644) + - (Improvement) Convert format to another with multiple versions will now only show the possible versions for the extension +- **Suggestion - Wait time before cure:** + - (Improvement) Set the first wait time based on first valid layer mass rather than use the fixed limit + - (Improvement) Set zero time to empty and dummy layers + - (Improvement) When creating the dummy layer also increment the bottom layer count as the created layer count as one +- **PCB Exposure:** + - (Add) Excellon Drill Format (drl) to cut off holes (Implementation may lack some advanced features, please confirm the result) (#646) + - (Fix) Arc (G03) with negative offsets (I-/J-) was not drawing the shape correctly + - (Fix) Implement the rotation for the outline primitive (#645) +- **File formats:** + - (Improvement) Formats now sanitize the selected version before encode given the file extension, if version is out of range it will force the last known version + - (Fix) CBDDLP: Remove a table from the file that might cause layer corruption +- (Add) Operations - `AfterCompleteReport` property: Gets or sets an report to show to the user after complete the operation with success +- (Improvement) Suggestion - Wait time after cure: Set zero time to empty and dummy layers +- (Improvement) Slight improvement on the contour intersection check, yields better performance on resin and suction cup detection +- (Improvement) Allow to trigger message boxes from operations and scripts (#644) +- (Upgrade) .NET from 6.0.12 to 6.0.13 + +## 02/01/2023 - v3.10.0 - (Add) File format: Anet N7 (#635) - (Add) PrusaSlicer Printer: Anet N7 (#635) - (Improvement) Each layer can now be aware of it own resolution -- (Improvement) Better set of file resolution from layers when using a compression codec other than png +- (Improvement) Better set of file resolution from layers when using a compression codec other than PNG - (Fix) Anet N4 printer reset on latest firmware (#633) - (Fix) PrusaSlicer printers: Change thumbnails resolution to match file preview resolution (Fix stretch images) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 42ef146e..0e89cdbf 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,7 +1,22 @@ -- (Add) File format: Anet N7 (#635) -- (Add) PrusaSlicer Printer: Anet N7 (#635) -- (Improvement) Each layer can now be aware of it own resolution -- (Improvement) Better set of file resolution from layers when using a compression codec other than png -- (Fix) Anet N4 printer reset on latest firmware (#633) -- (Fix) PrusaSlicer printers: Change thumbnails resolution to match file preview resolution (Fix stretch images) +- **UI:** + - (Improvement) Layer navigation load time by parallel `Mat` to `Bitmap` conversion + - (Improvement) Allow to show exceptions without the stack trace and detailed trigger action by using the `MessageExceiption` (#644) + - (Improvement) Allow progress to have and display a detailed log (#644) + - (Improvement) Convert format to another with multiple versions will now only show the possible versions for the extension +- **Suggestion - Wait time before cure:** + - (Improvement) Set the first wait time based on first valid layer mass rather than use the fixed limit + - (Improvement) Set zero time to empty and dummy layers + - (Improvement) When creating the dummy layer also increment the bottom layer count as the created layer count as one +- **PCB Exposure:** + - (Add) Excellon Drill Format (drl) to cut off holes (Implementation may lack some advanced features, please confirm the result) (#646) + - (Fix) Arc (G03) with negative offsets (I-/J-) was not drawing the shape correctly + - (Fix) Implement the rotation for the outline primitive (#645) +- **File formats:** + - (Improvement) Formats now sanitize the selected version before encode given the file extension, if version is out of range it will force the last known version + - (Fix) CBDDLP: Remove a table from the file that might cause layer corruption +- (Add) Operations - `AfterCompleteReport` property: Gets or sets an report to show to the user after complete the operation with success +- (Improvement) Suggestion - Wait time after cure: Set zero time to empty and dummy layers +- (Improvement) Slight improvement on the contour intersection check, yields better performance on resin and suction cup detection +- (Improvement) Allow to trigger message boxes from operations and scripts (#644) +- (Upgrade) .NET from 6.0.12 to 6.0.13 diff --git a/Scripts/010 Editor/ctb.bt b/Scripts/010 Editor/ctb.bt index 5e8e98ab..a9d99a6b 100644 --- a/Scripts/010 Editor/ctb.bt +++ b/Scripts/010 Editor/ctb.bt @@ -101,11 +101,11 @@ struct SLICER_INFO { if(MachineNameAddress > 0 && MachineNameSize > 0) { + FSeek(MachineNameAddress); char MachineName[MachineNameSize] ; } }; - if(header.PreviewLargeOffsetAddress > 0) { FSeek(header.PreviewLargeOffsetAddress); diff --git a/UVtools.ScriptSample/README.md b/Scripts/UVtools.ScriptSample/README.md similarity index 100% rename from UVtools.ScriptSample/README.md rename to Scripts/UVtools.ScriptSample/README.md diff --git a/Scripts/UVtools.ScriptSample/ScriptAdvancedDialogSample.cs b/Scripts/UVtools.ScriptSample/ScriptAdvancedDialogSample.cs new file mode 100644 index 00000000..36e482af --- /dev/null +++ b/Scripts/UVtools.ScriptSample/ScriptAdvancedDialogSample.cs @@ -0,0 +1,91 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Threading; +using UVtools.Core.Dialogs; +using UVtools.Core.Exceptions; +using UVtools.Core.Managers; +using UVtools.Core.Scripting; + +namespace UVtools.ScriptSample; + +/// +/// Change layer properties to random values +/// +public class ScriptAdvancedDialogSample : ScriptGlobals +{ + readonly ScriptNumericalInput Iterations = new() + { + Label = "Number of iterations to run", + Unit = "iterations", + Minimum = 1, + Maximum = byte.MaxValue, + Increment = 1, + Value = 4, + }; + + /// + /// Set configurations here, this function trigger just after load a script + /// + public void ScriptInit() + { + Script.Name = "Tests advanced dialogs"; + Script.Description = "This script does nothing other than show advanced dialogs and progress"; + Script.Author = "Tiago Conceição"; + Script.Version = new Version(0, 1); + Script.MinimumVersionToRun = new Version(3, 11, 0); // Advanced dialogs started here + + Script.UserInputs.Add(Iterations); + } + + /// + /// Validate user inputs here, this function trigger when user click on execute + /// + /// A error message, empty or null if validation passes. + public string? ScriptValidate() + { + return null; + } + + /// + /// Execute the script, this function trigger when when user click on execute and validation passes + /// + /// True if executes successfully to the end, otherwise false. + public bool ScriptExecute() + { + Progress.Reset("Some work", Iterations.Value); // Sets the progress name and number of items to process + + // Trigger an message box to user, will also show in console runs but in text form + var result = MessageBoxManager.Standard.ShowDialog("This is my script", + "Script is about to start, are you sure you want to continue?\n" + + "This will destroy your file!", AbstractMessageBoxStandard.MessageButtons.YesNo).Result; + + // throw error without stack trace + if (result != AbstractMessageBoxStandard.MessageButtonResult.Yes) throw new MessageException("User wanted to abort the script :("); + + // Write some text to show after the operation has completed with success + Operation.AfterCompleteReport = "My operation has performed the following changes:\n"; + + + for (int i = 0; i < Iterations.Value; i++) + { + Progress.ThrowIfCancellationRequested(); + + Thread.Sleep(1000); + Progress.Log = $"Task {i}: Completed!\n{Progress.Log}"; + Operation.AfterCompleteReport += $"- Task {i}: Waited for 1s\n"; + Progress.LockAndIncrement(); + } + + Thread.Sleep(1000); + + // return true if not cancelled by user + return !Progress.Token.IsCancellationRequested; + } +} \ No newline at end of file diff --git a/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs b/Scripts/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs similarity index 99% rename from UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs rename to Scripts/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs index ad1b991a..5bfc8766 100644 --- a/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; -using Emgu.CV.CvEnum; using UVtools.Core.Operations; using UVtools.Core.Scripting; diff --git a/UVtools.ScriptSample/ScriptBuildGrid.cs b/Scripts/UVtools.ScriptSample/ScriptBuildGrid.cs similarity index 98% rename from UVtools.ScriptSample/ScriptBuildGrid.cs rename to Scripts/UVtools.ScriptSample/ScriptBuildGrid.cs index c6349037..8893085b 100644 --- a/UVtools.ScriptSample/ScriptBuildGrid.cs +++ b/Scripts/UVtools.ScriptSample/ScriptBuildGrid.cs @@ -1,9 +1,9 @@ -using System; +using Emgu.CV; +using Emgu.CV.CvEnum; +using System; using System.Drawing; using UVtools.Core.Extensions; using UVtools.Core.Scripting; -using Emgu.CV; -using Emgu.CV.CvEnum; namespace UVtools.ScriptSample; @@ -103,7 +103,7 @@ public bool ScriptExecute() Progress.Reset("Changing layers", 2); // Sets the progress name and number of items to process SlicerFile.Reallocate(2); - + using var pattern = GenerateGridPattern(); SlicerFile[0].LayerMat = pattern; Progress++; diff --git a/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs b/Scripts/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs similarity index 100% rename from UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs rename to Scripts/UVtools.ScriptSample/ScriptChangeLayerPropertiesSample.cs diff --git a/UVtools.ScriptSample/ScriptCloneSettings.cs b/Scripts/UVtools.ScriptSample/ScriptCloneSettings.cs similarity index 99% rename from UVtools.ScriptSample/ScriptCloneSettings.cs rename to Scripts/UVtools.ScriptSample/ScriptCloneSettings.cs index a9435223..8b6edd02 100644 --- a/UVtools.ScriptSample/ScriptCloneSettings.cs +++ b/Scripts/UVtools.ScriptSample/ScriptCloneSettings.cs @@ -7,12 +7,12 @@ */ using System; -using UVtools.Core.Scripting; -using System.IO; using System.Collections.Generic; -using UVtools.Core.FileFormats; using System.Diagnostics; +using System.IO; using System.Linq; +using UVtools.Core.FileFormats; +using UVtools.Core.Scripting; namespace UVtools.ScriptSample; @@ -199,7 +199,7 @@ public bool ScriptExecute() // Load the file file.Decode(filePath); - if (string.Compare(file.MachineName, SlicerFile.MachineName, true) != 0) + if (string.Compare(file.MachineName, SlicerFile.MachineName, StringComparison.OrdinalIgnoreCase) != 0) { var deets = new List { diff --git a/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs b/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs similarity index 99% rename from UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs rename to Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs index bab6cc44..a2178feb 100644 --- a/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs +++ b/Scripts/UVtools.ScriptSample/ScriptCompensateCrossBleeding.cs @@ -1,10 +1,10 @@ -using System; +using Emgu.CV; +using System; using System.Drawing; using System.Threading.Tasks; using UVtools.Core; using UVtools.Core.Extensions; using UVtools.Core.Scripting; -using Emgu.CV; namespace UVtools.ScriptSample; diff --git a/UVtools.ScriptSample/ScriptCustomGCode.cs b/Scripts/UVtools.ScriptSample/ScriptCustomGCode.cs similarity index 98% rename from UVtools.ScriptSample/ScriptCustomGCode.cs rename to Scripts/UVtools.ScriptSample/ScriptCustomGCode.cs index b243c0f4..665def0b 100644 --- a/UVtools.ScriptSample/ScriptCustomGCode.cs +++ b/Scripts/UVtools.ScriptSample/ScriptCustomGCode.cs @@ -8,10 +8,6 @@ using System; using UVtools.Core.Scripting; -using System.IO; -using UVtools.Core; -using UVtools.Core.Extensions; -using UVtools.Core.FileFormats; namespace UVtools.ScriptSample; diff --git a/UVtools.ScriptSample/ScriptDebandingZSample.cs b/Scripts/UVtools.ScriptSample/ScriptDebandingZSample.cs similarity index 98% rename from UVtools.ScriptSample/ScriptDebandingZSample.cs rename to Scripts/UVtools.ScriptSample/ScriptDebandingZSample.cs index 6b893496..03157ad1 100644 --- a/UVtools.ScriptSample/ScriptDebandingZSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptDebandingZSample.cs @@ -152,7 +152,7 @@ public bool ScriptExecute() var firstLayer = SlicerFile.FirstLayer; if (firstLayer is not null) { - if (firstLayer.NonZeroPixelCount > 1) // First layer is not blank as it seems, lets create one + if (!firstLayer.IsDummy) // First layer is not blank as it seems, lets create one { firstLayer = firstLayer.Clone(); using var mat = EmguExtensions.InitMat(SlicerFile.Resolution); diff --git a/UVtools.ScriptSample/ScriptInsetSample.cs b/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs similarity index 100% rename from UVtools.ScriptSample/ScriptInsetSample.cs rename to Scripts/UVtools.ScriptSample/ScriptInsetSample.cs index 2220d899..2a1cc577 100644 --- a/UVtools.ScriptSample/ScriptInsetSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptInsetSample.cs @@ -6,15 +6,15 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; using System; using System.Drawing; using System.Text; using System.Threading.Tasks; -using UVtools.Core.Scripting; -using Emgu.CV; -using Emgu.CV.CvEnum; using UVtools.Core; using UVtools.Core.Extensions; +using UVtools.Core.Scripting; namespace UVtools.ScriptSample; diff --git a/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs b/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs similarity index 100% rename from UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs rename to Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs index 508a0bcd..88ca4639 100644 --- a/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptLightBleedCompensationSample.cs @@ -6,15 +6,15 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.Structure; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using UVtools.Core.Scripting; -using Emgu.CV; -using Emgu.CV.Structure; using UVtools.Core; using UVtools.Core.Extensions; +using UVtools.Core.Scripting; namespace UVtools.ScriptSample; diff --git a/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs b/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs similarity index 99% rename from UVtools.ScriptSample/ScriptPreventResinShrinkage.cs rename to Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs index efda1e86..333d5f31 100644 --- a/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs +++ b/Scripts/UVtools.ScriptSample/ScriptPreventResinShrinkage.cs @@ -1,13 +1,13 @@ -using System; +using Emgu.CV; +using Emgu.CV.CvEnum; +using System; using System.Drawing; using System.Linq; using System.Threading.Tasks; using UVtools.Core; using UVtools.Core.Extensions; -using UVtools.Core.Scripting; using UVtools.Core.Layers; -using Emgu.CV; -using Emgu.CV.CvEnum; +using UVtools.Core.Scripting; namespace UVtools.ScriptSample; diff --git a/UVtools.ScriptSample/ScriptSetLiftHeightSample.cs b/Scripts/UVtools.ScriptSample/ScriptSetLiftHeightSample.cs similarity index 100% rename from UVtools.ScriptSample/ScriptSetLiftHeightSample.cs rename to Scripts/UVtools.ScriptSample/ScriptSetLiftHeightSample.cs diff --git a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs b/Scripts/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs similarity index 98% rename from UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs rename to Scripts/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs index e23b5180..7d52cfb4 100644 --- a/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs +++ b/Scripts/UVtools.ScriptSample/ScriptTestPerLayerSettingsSample.cs @@ -6,10 +6,10 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; +using System; +using System.Drawing; using UVtools.Core.Extensions; using UVtools.Core.Operations; using UVtools.Core.Scripting; @@ -85,13 +85,11 @@ public bool ScriptExecute() // Exercise for you: Do eyebrows var mats = EmguExtensions.InitMats(layerCount, SlicerFile.Resolution); // Allocate x images with file resolution - int x, y; int xCenter = (int) (SlicerFile.ResolutionX / 2); //int yCenter = (int) (SlicerFile.ResolutionY / 2); - // Do the left eye - x = xCenter - noseThickness/2 - faceSpacing - eyeDiameter/2; - y = faceSpacing; + var x = xCenter - noseThickness/2 - faceSpacing - eyeDiameter/2; + int y = faceSpacing; CvInvoke.Circle(mats[0], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteColor, -1, lineType); CvInvoke.Circle(mats[1], new Point(x, y), eyeDiameter/2, EmguExtensions.WhiteColor, -1, lineType); Progress++; diff --git a/UVtools.ScriptSample/ScriptTester.cs b/Scripts/UVtools.ScriptSample/ScriptTester.cs similarity index 99% rename from UVtools.ScriptSample/ScriptTester.cs rename to Scripts/UVtools.ScriptSample/ScriptTester.cs index cf28fe5c..0807cd6a 100644 --- a/UVtools.ScriptSample/ScriptTester.cs +++ b/Scripts/UVtools.ScriptSample/ScriptTester.cs @@ -6,18 +6,17 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Util; using System; using System.Collections.Generic; using System.Drawing; -using UVtools.Core.Scripting; -using System.IO; using System.Threading.Tasks; -using Emgu.CV; -using Emgu.CV.CvEnum; -using Emgu.CV.Util; using UVtools.Core; using UVtools.Core.EmguCV; using UVtools.Core.Extensions; +using UVtools.Core.Scripting; namespace UVtools.ScriptSample; diff --git a/UVtools.ScriptSample/ScriptTimelapseSample.cs b/Scripts/UVtools.ScriptSample/ScriptTimelapseSample.cs similarity index 100% rename from UVtools.ScriptSample/ScriptTimelapseSample.cs rename to Scripts/UVtools.ScriptSample/ScriptTimelapseSample.cs diff --git a/UVtools.ScriptSample/ScriptVATClean.cs b/Scripts/UVtools.ScriptSample/ScriptVATClean.cs similarity index 100% rename from UVtools.ScriptSample/ScriptVATClean.cs rename to Scripts/UVtools.ScriptSample/ScriptVATClean.cs index 065c86e2..02ca3224 100644 --- a/UVtools.ScriptSample/ScriptVATClean.cs +++ b/Scripts/UVtools.ScriptSample/ScriptVATClean.cs @@ -6,11 +6,11 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; +using System; +using System.Drawing; using UVtools.Core.Extensions; using UVtools.Core.Scripting; diff --git a/UVtools.ScriptSample/UVtools.ScriptSample.csproj b/Scripts/UVtools.ScriptSample/UVtools.ScriptSample.csproj similarity index 91% rename from UVtools.ScriptSample/UVtools.ScriptSample.csproj rename to Scripts/UVtools.ScriptSample/UVtools.ScriptSample.csproj index 51e1abdb..ece3ed95 100644 --- a/UVtools.ScriptSample/UVtools.ScriptSample.csproj +++ b/Scripts/UVtools.ScriptSample/UVtools.ScriptSample.csproj @@ -15,7 +15,7 @@ - + diff --git a/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs b/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs index e2133df5..f73f15bd 100644 --- a/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs +++ b/UVtools.AvaloniaControls/AdvancedImageBox.axaml.cs @@ -6,13 +6,6 @@ * of this license document, but changing it is not allowed. */ // Port from: https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox to AvaloniaUI -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Drawing; -using System.Runtime.CompilerServices; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Presenters; @@ -22,6 +15,12 @@ using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Platform; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Runtime.CompilerServices; using Bitmap = Avalonia.Media.Imaging.Bitmap; using Color = Avalonia.Media.Color; using Pen = Avalonia.Media.Pen; @@ -326,9 +325,7 @@ public void RemoveAt(int index) /// An array containing copies of the elements of the . public int[] ToArray() { - int[] results; - - results = new int[Count]; + var results = new int[Count]; CopyTo(results, 0); return results; @@ -1142,7 +1139,7 @@ public override void Render(DrawingContext context) var zoomFactor = ZoomFactor; - if (HaveTrackerImage && _pointerPosition.X >= 0 && _pointerPosition.Y >= 0) + if (HaveTrackerImage && _pointerPosition is {X: >= 0, Y: >= 0}) { var destSize = TrackerImageAutoZoom ? new Size(_trackerImage!.Size.Width * zoomFactor, _trackerImage.Size.Height * zoomFactor) @@ -2291,7 +2288,7 @@ public Rect GetSourceImageRegion() public Rect GetImageViewPort() { var viewPortSize = ViewPortSize; - if (!IsImageLoaded || (viewPortSize.Width == 0 && viewPortSize.Height == 0)) return Rect.Empty; + if (!IsImageLoaded || viewPortSize is {Width: 0, Height: 0}) return Rect.Empty; double xOffset = 0; double yOffset = 0; diff --git a/UVtools.Cmd/Program.cs b/UVtools.Cmd/Program.cs index 15b1c76b..ce9337aa 100644 --- a/UVtools.Cmd/Program.cs +++ b/UVtools.Cmd/Program.cs @@ -9,7 +9,6 @@ using System; using System.CommandLine; using System.Diagnostics; -using System.Globalization; using System.IO; using System.Linq; using System.Threading; diff --git a/UVtools.Cmd/Symbols/ConvertCommand.cs b/UVtools.Cmd/Symbols/ConvertCommand.cs index 07d7fb78..9555c27e 100644 --- a/UVtools.Cmd/Symbols/ConvertCommand.cs +++ b/UVtools.Cmd/Symbols/ConvertCommand.cs @@ -38,7 +38,7 @@ internal static Command CreateCommand() if (string.Equals(targetTypeExt, "auto", StringComparison.InvariantCultureIgnoreCase)) { - using var testFile = Program.OpenInputFile(inputFile!, FileFormat.FileDecodeType.Partial); + using var testFile = Program.OpenInputFile(inputFile, FileFormat.FileDecodeType.Partial); string? convertFileExtension; switch (testFile) { diff --git a/UVtools.Cmd/Symbols/RunCommand.cs b/UVtools.Cmd/Symbols/RunCommand.cs index bcaf156f..b8f6a21c 100644 --- a/UVtools.Cmd/Symbols/RunCommand.cs +++ b/UVtools.Cmd/Symbols/RunCommand.cs @@ -85,7 +85,12 @@ internal static Command CreateCommand() Program.ProgressBarWork($"Operation {++runs}: {operation.ProgressTitle}", () => { - if(operation.Execute(Program.Progress)) successfulRuns++; + if (!operation.Execute(Program.Progress)) return; + successfulRuns++; + if (!string.IsNullOrWhiteSpace(operation.AfterCompleteReport)) + { + Program.WriteLine(operation.AfterCompleteReport); + } }); } else diff --git a/UVtools.Cmd/Symbols/SetThumbnailCommand.cs b/UVtools.Cmd/Symbols/SetThumbnailCommand.cs index 718778d1..5e7365f5 100644 --- a/UVtools.Cmd/Symbols/SetThumbnailCommand.cs +++ b/UVtools.Cmd/Symbols/SetThumbnailCommand.cs @@ -7,10 +7,10 @@ */ using Emgu.CV; +using Emgu.CV.CvEnum; using System; using System.CommandLine; using System.IO; -using Emgu.CV.CvEnum; namespace UVtools.Cmd.Symbols; diff --git a/UVtools.Cmd/UVtools.Cmd.csproj b/UVtools.Cmd/UVtools.Cmd.csproj index d6585d35..e1c75e0f 100644 --- a/UVtools.Cmd/UVtools.Cmd.csproj +++ b/UVtools.Cmd/UVtools.Cmd.csproj @@ -5,7 +5,7 @@ net6.0 UVtoolsCmd UVtools.ico - 1.0.5 + 1.0.6 Tiago Conceição, sn4k3 PTRTECH LICENSE diff --git a/UVtools.Core/Dialogs/AbstractMessageBoxStandard.cs b/UVtools.Core/Dialogs/AbstractMessageBoxStandard.cs new file mode 100644 index 00000000..06cb7a0f --- /dev/null +++ b/UVtools.Core/Dialogs/AbstractMessageBoxStandard.cs @@ -0,0 +1,42 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Threading.Tasks; + +namespace UVtools.Core.Dialogs; + +public abstract class AbstractMessageBoxStandard +{ + public enum MessageButtons + { + Ok, + YesNo, + OkCancel, + OkAbort, + YesNoCancel, + YesNoAbort, + } + + [Flags] + public enum MessageButtonResult + { + Ok = 0, + Yes = 1, + No = 2, + Abort = No | Yes, // 0x00000003 + Cancel = 4, + None = Cancel | Yes, // 0x00000005 + } + + protected AbstractMessageBoxStandard() + { } + + public abstract Task ShowDialog(string? title, string message, MessageButtons buttons = MessageButtons.Ok); + public Task ShowDialog(string message, MessageButtons buttons = MessageButtons.Ok) => ShowDialog(null, message, buttons); +} \ No newline at end of file diff --git a/UVtools.Core/Dialogs/ConsoleMessageBoxStandard.cs b/UVtools.Core/Dialogs/ConsoleMessageBoxStandard.cs new file mode 100644 index 00000000..aedfc242 --- /dev/null +++ b/UVtools.Core/Dialogs/ConsoleMessageBoxStandard.cs @@ -0,0 +1,109 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Threading.Tasks; + +namespace UVtools.Core.Dialogs; + +public class ConsoleMessageBoxStandard : AbstractMessageBoxStandard +{ + #region Instance + private static readonly Lazy _instance = new(() => new ConsoleMessageBoxStandard()); + + public static ConsoleMessageBoxStandard Instance => _instance.Value; + #endregion + + #region Constructor + private ConsoleMessageBoxStandard() + { } + #endregion + + #region Methods + public override Task ShowDialog(string? title, string message, MessageButtons buttons = MessageButtons.Ok) + { + if(!string.IsNullOrWhiteSpace(title)) Console.WriteLine(title); + Console.WriteLine(message); + + while (true) + { + Console.Write("Response "); + + switch (buttons) + { + case MessageButtons.Ok: + Console.Write("[OK]"); + break; + case MessageButtons.OkCancel: + Console.Write("[OK/Cancel]"); + break; + case MessageButtons.OkAbort: + Console.Write("[OK/Abort]"); + break; + case MessageButtons.YesNo: + Console.Write("[Yes/No]"); + break; + case MessageButtons.YesNoCancel: + Console.Write("[Yes/No/Cancel]"); + break; + case MessageButtons.YesNoAbort: + Console.Write("[Yes/No/Abort]"); + break; + default: + throw new ArgumentOutOfRangeException(nameof(buttons)); + } + + Console.Write(": "); + + var line = Console.ReadLine(); + + line = line?.Trim().ToLower(); + + switch (buttons) + { + case MessageButtons.Ok: + /*if (line is "ok" or "y" or "yes") */return Task.FromResult(MessageButtonResult.Ok); + break; + case MessageButtons.OkCancel: + if (string.IsNullOrWhiteSpace(line)) continue; + if (line is "ok" or "y" or "yes") return Task.FromResult(MessageButtonResult.Ok); + if (line is "cancel" or "c") return Task.FromResult(MessageButtonResult.Cancel); + break; + case MessageButtons.OkAbort: + if (string.IsNullOrWhiteSpace(line)) continue; + if (line is "ok" or "y" or "yes") return Task.FromResult(MessageButtonResult.Ok); + if (line is "a" or "abort") return Task.FromResult(MessageButtonResult.Abort); + break; + case MessageButtons.YesNo: + if (string.IsNullOrWhiteSpace(line)) continue; + if (line is "y" or "yes") return Task.FromResult(MessageButtonResult.Yes); + if (line is "n" or "no") return Task.FromResult(MessageButtonResult.No); + break; + case MessageButtons.YesNoCancel: + if (string.IsNullOrWhiteSpace(line)) continue; + if (line is "y" or "yes") return Task.FromResult(MessageButtonResult.Yes); + if (line is "n" or "no") return Task.FromResult(MessageButtonResult.No); + if (line is "cancel" or "c") return Task.FromResult(MessageButtonResult.Cancel); + break; + case MessageButtons.YesNoAbort: + if (string.IsNullOrWhiteSpace(line)) continue; + if (line is "y" or "yes") return Task.FromResult(MessageButtonResult.Yes); + if (line is "n" or "no") return Task.FromResult(MessageButtonResult.No); + if (line is "a" or "abort") return Task.FromResult(MessageButtonResult.Abort); + break; + default: + throw new ArgumentOutOfRangeException(nameof(buttons)); + } + + //Console.Write("\b\b -- Invalid option!"); + } + + return Task.FromResult(MessageButtonResult.Cancel); + } + #endregion +} \ No newline at end of file diff --git a/UVtools.Core/EmguCV/EmguContours.cs b/UVtools.Core/EmguCV/EmguContours.cs index 09153513..555b7520 100644 --- a/UVtools.Core/EmguCV/EmguContours.cs +++ b/UVtools.Core/EmguCV/EmguContours.cs @@ -6,15 +6,15 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.Collections; using Emgu.CV; +using Emgu.CV.CvEnum; using Emgu.CV.Util; +using System; +using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Threading.Tasks; -using Emgu.CV.CvEnum; using UVtools.Core.Extensions; namespace UVtools.Core.EmguCV; diff --git a/UVtools.Core/EmguCV/MatRoi.cs b/UVtools.Core/EmguCV/MatRoi.cs index 6bd5b3f6..46755011 100644 --- a/UVtools.Core/EmguCV/MatRoi.cs +++ b/UVtools.Core/EmguCV/MatRoi.cs @@ -7,9 +7,9 @@ */ +using Emgu.CV; using System; using System.Drawing; -using Emgu.CV; using UVtools.Core.Extensions; namespace UVtools.Core.EmguCV; diff --git a/UVtools.Core/Enumerations.cs b/UVtools.Core/Enumerations.cs index 28de9924..afc21dc5 100644 --- a/UVtools.Core/Enumerations.cs +++ b/UVtools.Core/Enumerations.cs @@ -112,6 +112,24 @@ public enum TimeUnits : byte Seconds } +public enum MidpointRoundingType +{ + [Description("To even: The strategy of rounding to the nearest number, and when a number is halfway between two others, it's rounded toward the nearest even number.")] + ToEven = MidpointRounding.ToEven, + + [Description("Away from zero: The strategy of rounding to the nearest number, and when a number is halfway between two others, it's rounded toward the nearest number that's away from zero.")] + AwayFromZero = MidpointRounding.AwayFromZero, + + [Description("To zero: The strategy of directed rounding toward zero, with the result closest to and no greater in magnitude than the infinitely precise result.")] + ToZero = MidpointRounding.ToZero, + + [Description("To negative inifity: The strategy of downwards-directed rounding, with the result closest to and no greater than the infinitely precise result.")] + ToNegativeInfinity = MidpointRounding.ToNegativeInfinity, + + [Description("To positive inifity: The strategy of upwards-directed rounding, with the result closest to and no less than the infinitely precise result.")] + ToPositiveInfinity = MidpointRounding.ToPositiveInfinity +} + public enum RemoveSourceFileAction : byte { [Description("Keep the source file")] diff --git a/UVtools.Core/Excellon/ExcellonDrillFormat.cs b/UVtools.Core/Excellon/ExcellonDrillFormat.cs new file mode 100644 index 00000000..1c3dd006 --- /dev/null +++ b/UVtools.Core/Excellon/ExcellonDrillFormat.cs @@ -0,0 +1,411 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using Emgu.CV; +using Emgu.CV.CvEnum; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Text.RegularExpressions; +using UVtools.Core.Extensions; +using UVtools.Core.Operations; + +namespace UVtools.Core.Excellon; + +/// +/// The Excellon drill format is a subset of RS274D and is used by the drilling and routing machines made by the Excellon corporation. +/// Because of Excellon's long history and dominance of the PCB drilling business for many years their format is a defacto industry standard. +/// Almost every PCB layout software can produce this format.However we have noticed that many PCB layout tools do not take +/// full advantage of the header information which makes reading the drill file more difficult than it should be. +/// https://www.artwork.com/gerber/drl2laser/excellon/index.htm +/// https://gist.github.com/katyo/5692b935abc085b1037e +/// +public class ExcellonDrillFormat +{ + #region Sub classes + + /// + /// Defines tool as having a diameter. + /// For each tool used in the data the diameter should be defined here. + /// There are additional parameters but if you are a PCB designer it is not up to you to specify feed rates and such. + /// + public class Tool + { + public uint Index { get; init; } + + public float Diameter { get; init; } + + public Tool(uint index, float diameter) + { + Index = index; + Diameter = diameter; + } + + public override string ToString() + { + return $"T{Index}C{nameof(Diameter)}"; + } + } + + public class Drill + { + public Tool Tool { get; init; } + + public PointF Position { get; init; } + + public float Diameter => Tool.Diameter; + + public Drill(Tool tool, PointF position) + { + Tool = tool; + Position = position; + } + + public override string ToString() + { + return $"X{Position.X}Y{Position.Y}"; + } + } + + #endregion + + #region Enums + public enum ExcellonDrillUnitType : byte + { + Millimeter, + Inch + } + + public enum ExcellonDrillZerosIncludeType : byte + { + /// + /// Use float system + /// + None, + /// + /// Include left zeros + /// + Leading, + /// + /// Include right zeros + /// + Trail, + + } + + #endregion + + #region Constants + public const string Extension = "drl"; + + /// + /// Indicates the start of the header. should always be the first line in the header + /// + public const string CommandM48 = "M48"; + + /// + /// Number of padding zeros on coordinate system + /// + public const byte PaddingZeros = 6; + + public const int InchResolution = 10000; + public const int MillimeterResolution = 1000; + #endregion + + #region Properties + + /// + /// Use Format 2 commands; alternative would be FMAT,1 + /// + public uint FormatVersion { get; set; } = 2; + public ExcellonDrillUnitType UnitType { get; set; } = ExcellonDrillUnitType.Millimeter; + public ExcellonDrillZerosIncludeType ZerosIncludeType { get; set; } = ExcellonDrillZerosIncludeType.Leading; + + public Dictionary Tools { get; init; } = new(); + + public List Drills { get; init; } = new(); + + private SizeF XYppmm { get; set; } + + /// + /// Gets or sets the X offset for drawings in millimeters + /// + public float OffsetX { get; set; } + + /// + /// Gets or sets the Y offset for drawings in millimeters + /// + public float OffsetY { get; set; } + + /// + /// Gets or sets to inverse the polarity on drawing + /// + public bool InversePolarity { get; set; } + + /// + /// Gets or sets the scale to apply to each shape drawing size. + /// Positions and vectors aren't affected by this. + /// + public double SizeScale { get; set; } = 1; + + public MidpointRoundingType SizeMidpointRounding { get; set; } = MidpointRoundingType.AwayFromZero; + + #endregion + + #region Constructor + + public ExcellonDrillFormat() + { + } + + public ExcellonDrillFormat(string filePath) + { + Load(filePath); + } + #endregion + + #region Methods + private void Load(string filePath) + { + if (!File.Exists(filePath)) throw new FileNotFoundException("File not found.", filePath); + using var tr = new StreamReader(filePath); + var line = tr.ReadLine()?.Trim(); + + if(string.IsNullOrWhiteSpace(line) || line != CommandM48) throw new InvalidDataException("Invalid Excellon Drill file, should start with M48."); + + Tools.Clear(); + Drills.Clear(); + + bool endOfHeader = false; + bool drillMode = false; + uint selectedToolIndex = 0; + + float x = 0, y = 0; + + while ((line = tr.ReadLine()?.Trim()) is not null) + { + if (line.Length == 0) continue; + + if (line.StartsWith("FMAT,")) + { + var split = line.Split(',', StringSplitOptions.TrimEntries); + FormatVersion = uint.Parse(split[1]); + + continue; + } + + if (line.StartsWith("METRIC") || line.StartsWith("INCH")) + { + var split = line.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + UnitType = split[0] == "METRIC" ? ExcellonDrillUnitType.Millimeter : ExcellonDrillUnitType.Inch; + + if (split.Length >= 2) + { + ZerosIncludeType = split[1] switch + { + "LZ" => ExcellonDrillZerosIncludeType.Leading, + "TZ" => ExcellonDrillZerosIncludeType.Trail, + _ => ExcellonDrillZerosIncludeType.None + }; + } + else + { + ZerosIncludeType = ExcellonDrillZerosIncludeType.None; + } + + continue; + } + + if (line == "ICI") + { + throw new NotImplementedException("ICI (Incremental input of program coordinates) is not yet implemented, please use absolute coordinate system."); + } + + if (line is "%" or "M95") + { + endOfHeader = true; + continue; + } + + if (line == "G05") + { + drillMode = true; + continue; + } + + // Tool or select tool + if (line[0] == 'T') + { + if (!endOfHeader) + { + var match = Regex.Match(line, @"^T(\d+)C(([0-9]*[.])?[0-9]+)"); + if (match is + { + Success: true, + Groups.Count: >= 4 + }) + { + var index = uint.Parse(match.Groups[1].Value); + var diameter = float.Parse(match.Groups[2].Value); + var tool = new Tool(index, diameter); + Tools.Add(index, tool); + } + } + else + { + selectedToolIndex = uint.Parse(line[1..]); + } + + + continue; + } + + // Drill coordinate + if (line[0] == 'X' || line[0] == 'Y') + { + if(!drillMode) continue; + + var match = Regex.Match(line, @"^X-?(([0-9]*[.])?[0-9]+)"); + if (match is + { + Success: true, + Groups.Count: >= 2 + }) + { + if (match.Groups[1].Value.Contains('.') || ZerosIncludeType == ExcellonDrillZerosIncludeType.None) + { + x = float.Parse(match.Groups[1].Value); + } + else + { + switch (ZerosIncludeType) + { + case ExcellonDrillZerosIncludeType.Leading: + x = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadRight(PaddingZeros, '0'))); + break; + case ExcellonDrillZerosIncludeType.Trail: + x = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadLeft(PaddingZeros, '0'))); + break; + } + } + + } + + match = Regex.Match(line, @"Y-?(([0-9]*[.])?[0-9]+)"); + if (match is + { + Success: true, + Groups.Count: >= 2 + }) + { + if (match.Groups[1].Value.Contains('.') || ZerosIncludeType == ExcellonDrillZerosIncludeType.None) + { + y = float.Parse(match.Groups[1].Value); + } + else + { + switch (ZerosIncludeType) + { + case ExcellonDrillZerosIncludeType.Leading: + y = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadRight(PaddingZeros, '0'))); + break; + case ExcellonDrillZerosIncludeType.Trail: + y = ValueToCoordinate(float.Parse(match.Groups[1].Value.PadLeft(PaddingZeros, '0'))); + break; + } + } + + } + + var drill = new Drill(Tools[selectedToolIndex], new PointF(x, y)); + Drills.Add(drill); + + continue; + } + } + } + + public float ValueToCoordinate(float value) => + UnitType switch + { + ExcellonDrillUnitType.Millimeter => (float) Math.Round(value / MillimeterResolution, PaddingZeros), + ExcellonDrillUnitType.Inch => (float) Math.Round(value / InchResolution, PaddingZeros), + _ => throw new ArgumentOutOfRangeException() + }; + + public float ValueToCoordinate(int value) => + ValueToCoordinate((float)value); + + public float GetMillimeters(float size) + { + if (UnitType == ExcellonDrillUnitType.Millimeter) return size; + return size * (float)UnitExtensions.InchToMillimeter; + } + + public double GetMillimeters(double size) + { + if (UnitType == ExcellonDrillUnitType.Millimeter) return size; + return size * UnitExtensions.InchToMillimeter; + } + + + public SizeF GetMillimeters(SizeF size) + { + if (UnitType == ExcellonDrillUnitType.Millimeter) return size; + return new SizeF(size.Width * (float)UnitExtensions.InchToMillimeter, size.Height * (float)UnitExtensions.InchToMillimeter); + } + + public PointF GetMillimeters(PointF point) + { + if (UnitType == ExcellonDrillUnitType.Millimeter) return point; + return new PointF(point.X * (float)UnitExtensions.InchToMillimeter, point.Y * (float)UnitExtensions.InchToMillimeter); + } + + public Point PositionMmToPx(PointF atMm) + => new((int)Math.Round((atMm.X + OffsetX) * XYppmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round((atMm.Y + OffsetY) * XYppmm.Height, MidpointRounding.AwayFromZero)); + + public int SizeMmToPx(float sizeMm) + => (int)Math.Max(1, Math.Round(sizeMm * XYppmm.Max() * SizeScale, (MidpointRounding)SizeMidpointRounding)); + #endregion + + #region Static methods + public static void ParseAndDraw(ExcellonDrillFormat document, string filePath, Mat mat, bool enableAntiAliasing = false) + { + document.Load(filePath); + + foreach (var drill in document.Drills) + { + var position = document.PositionMmToPx(document.GetMillimeters(drill.Position)); + var radius = document.SizeMmToPx(document.GetMillimeters(drill.Diameter / 2)); + CvInvoke.Circle(mat, position, radius, + document.InversePolarity ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor, + -1, + enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected); + } + } + + public static ExcellonDrillFormat ParseAndDraw(OperationPCBExposure.PCBExposureFile file, Mat mat, SizeF xyPpmm, + MidpointRoundingType sizeMidpointRounding = MidpointRoundingType.AwayFromZero, SizeF offset = default, bool enableAntiAliasing = false) + { + var document = new ExcellonDrillFormat + { + SizeMidpointRounding = sizeMidpointRounding, + XYppmm = xyPpmm, + OffsetX = offset.Width, + OffsetY = offset.Height, + InversePolarity = file.InvertPolarity, + SizeScale = file.SizeScale + }; + + ParseAndDraw(document, file.FilePath, mat, enableAntiAliasing); + + return document; + } + #endregion +} \ No newline at end of file diff --git a/UVtools.Core/Exceptions/MessageException.cs b/UVtools.Core/Exceptions/MessageException.cs new file mode 100644 index 00000000..11b5bdb5 --- /dev/null +++ b/UVtools.Core/Exceptions/MessageException.cs @@ -0,0 +1,33 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Runtime.Serialization; + +namespace UVtools.Core.Exceptions; + +/// +/// Generic exception to show only the message instead of the full stack-trace +/// +public class MessageException : Exception +{ + public string? Title { get; } + + protected MessageException(SerializationInfo info, StreamingContext context) : base(info, context) + { } + + public MessageException(string? message, string? title = null) : base(message) + { + Title = title; + } + + public MessageException(string? message, Exception? innerException, string? title = null) : base(message, innerException) + { + Title = title; + } +} \ No newline at end of file diff --git a/UVtools.Core/Extensions/CompressionExtensions.cs b/UVtools.Core/Extensions/CompressionExtensions.cs index d21de95f..558848b9 100644 --- a/UVtools.Core/Extensions/CompressionExtensions.cs +++ b/UVtools.Core/Extensions/CompressionExtensions.cs @@ -6,11 +6,11 @@ * of this license document, but changing it is not allowed. */ +using CommunityToolkit.HighPerformance; using System; using System.IO; using System.IO.Compression; using System.Runtime.CompilerServices; -using CommunityToolkit.HighPerformance; namespace UVtools.Core.Extensions; diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index 20801340..f58d822a 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -6,6 +6,7 @@ * of this license document, but changing it is not allowed. */ +using CommunityToolkit.HighPerformance; using Emgu.CV; using Emgu.CV.Cuda; using Emgu.CV.CvEnum; @@ -17,7 +18,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Text; -using CommunityToolkit.HighPerformance; using UVtools.Core.EmguCV; using UVtools.Core.Objects; @@ -915,7 +915,7 @@ public static void Transform(this Mat src, double xScale, double yScale, double /// public static void Rotate(this Mat src, Mat dst, double angle, Size newSize = default, double scale = 1.0) { - if (angle % 360 == 0 && scale == 1.0) return; + if (angle % 360 == 0 && Math.Abs(scale - 1.0) < 0.001) return; if (newSize.IsEmpty) { newSize = src.Size; @@ -953,7 +953,7 @@ public static void Rotate(this Mat src, Mat dst, double angle, Size newSize = de /// public static void RotateAdjustBounds(this Mat src, Mat dst, double angle, double scale = 1.0) { - if (angle % 360 == 0 && scale == 1.0) return; + if (angle % 360 == 0 && Math.Abs(scale - 1.0) < 0.001) return; var halfWidth = src.Width / 2.0f; var halfHeight = src.Height / 2.0f; using var translateTransform = new Matrix(2, 3); @@ -999,7 +999,7 @@ public static void TransformFromCenter(this Mat src, double xScale, double yScal /// public static void Resize(this Mat src, double scale, Inter interpolation = Inter.Linear) { - if (scale == 1) return; + if (Math.Abs(scale - 1) < 0.001) return; CvInvoke.Resize(src, src, new Size((int) (src.Width * scale), (int) (src.Height * scale)), 0, 0, interpolation); } #endregion diff --git a/UVtools.Core/Extensions/MathExtensions.cs b/UVtools.Core/Extensions/MathExtensions.cs index 23ea24af..cd392939 100644 --- a/UVtools.Core/Extensions/MathExtensions.cs +++ b/UVtools.Core/Extensions/MathExtensions.cs @@ -13,7 +13,7 @@ namespace UVtools.Core.Extensions; public static class MathExtensions { - public static byte Clamp(this byte value, byte min, byte max) + /*public static byte Clamp(this byte value, byte min, byte max) { return value <= min ? min : value >= max ? max : value; } @@ -76,7 +76,7 @@ public static T Clamp(T value, T min, T max) where T : IComparable return max; return value; - } + }*/ public static uint DecimalDigits(this float val) { diff --git a/UVtools.Core/Extensions/PointExtensions.cs b/UVtools.Core/Extensions/PointExtensions.cs index 9d2dfb90..056b1f1a 100644 --- a/UVtools.Core/Extensions/PointExtensions.cs +++ b/UVtools.Core/Extensions/PointExtensions.cs @@ -15,8 +15,8 @@ public static class PointExtensions public static double FindLength(Point start, Point end) => Math.Sqrt(Math.Pow(end.Y - start.Y, 2) + Math.Pow(end.X - start.X, 2)); public static bool IsAnyNegative(this Point point) => point.X < 0 || point.Y < 0; - public static bool IsBothNegative(this Point point) => point.X < 0 && point.Y < 0; - public static bool IsBothZeroOrPositive(this Point point) => point.X >= 0 && point.Y >= 0; + public static bool IsBothNegative(this Point point) => point is {X: < 0, Y: < 0}; + public static bool IsBothZeroOrPositive(this Point point) => point is {X: >= 0, Y: >= 0}; public static Point Rotate(this Point point, double angleDegree, Point pivot = default) { diff --git a/UVtools.Core/Extensions/RectangleExtensions.cs b/UVtools.Core/Extensions/RectangleExtensions.cs index eb1174fc..513dc084 100644 --- a/UVtools.Core/Extensions/RectangleExtensions.cs +++ b/UVtools.Core/Extensions/RectangleExtensions.cs @@ -5,6 +5,7 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ +using System; using System.Drawing; using System.Linq; @@ -26,32 +27,39 @@ public static Rectangle OffsetBy(this Rectangle src, int x, int y) public static Rectangle OffsetBy(this Rectangle src, Point position) => src.OffsetBy(position.X, position.Y); + public static int Perimeter(this Rectangle src) => src.Width * 2 + src.Height * 2; + public static float Perimeter(this RectangleF src) => src.Width * 2 + src.Height * 2; + public static float Perimeter(this RectangleF src, int round) => (float)Math.Round(src.Perimeter(), round); + public static int Area(this Rectangle src) => src.Width * src.Height; + public static float Area(this RectangleF src) => src.Width * src.Height; + public static float Area(this RectangleF src, int round) => (float)Math.Round(src.Area(), round); + /// /// Gets the smallest rectangle among all rectangles /// /// /// The smallest rectangle - public static Rectangle SmallestRectangle(params Rectangle[] rectangles) => rectangles.MinBy(rectangle => rectangle.Area()); + public static Rectangle SmallestRectangle(params Rectangle[] rectangles) => rectangles.MinBy(rectangle => rectangle.Perimeter()); /// /// Gets the smallest rectangle among all rectangles /// /// /// The smallest rectangle - public static RectangleF SmallestRectangle(params RectangleF[] rectangles) => rectangles.MinBy(rectangle => rectangle.Area()); + public static RectangleF SmallestRectangle(params RectangleF[] rectangles) => rectangles.MinBy(rectangle => rectangle.Perimeter()); /// /// Gets the largest rectangle among all rectangles /// /// /// The largest rectangle - public static Rectangle LargestRectangle(params Rectangle[] rectangles) => rectangles.MaxBy(rectangle => rectangle.Area()); + public static Rectangle LargestRectangle(params Rectangle[] rectangles) => rectangles.MaxBy(rectangle => rectangle.Perimeter()); /// /// Gets the largest rectangle among all rectangles /// /// /// The largest rectangle - public static RectangleF LargestRectangle(params RectangleF[] rectangles) => rectangles.MaxBy(rectangle => rectangle.Area()); + public static RectangleF LargestRectangle(params RectangleF[] rectangles) => rectangles.MaxBy(rectangle => rectangle.Perimeter()); } \ No newline at end of file diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs index 5fda5145..73832d15 100644 --- a/UVtools.Core/Extensions/SizeExtensions.cs +++ b/UVtools.Core/Extensions/SizeExtensions.cs @@ -56,7 +56,7 @@ public static string SizeSuffix(long value, byte decimalPlaces = 2) public static Size Multiply(this Size size, double dxy) => new((int)(size.Width * dxy), (int)(size.Height * dxy)); public static Size Multiply(this Size size, double dx, double dy) => new((int)(size.Width * dx), (int)(size.Height * dy)); - public static Size Divide(this Size size, Size otherSize) => new((int)(otherSize.Width == 0 ? 0 : size.Width / otherSize.Width), (int)(otherSize.Height == 0 ? 0 : size.Height / otherSize.Height)); + public static Size Divide(this Size size, Size otherSize) => new(otherSize.Width == 0 ? 0 : size.Width / otherSize.Width, (int)(otherSize.Height == 0 ? 0 : size.Height / otherSize.Height)); public static Size Divide(this Size size, SizeF otherSize) => new((int)(otherSize.Width == 0 ? 0 : size.Width / otherSize.Width), (int)(otherSize.Height == 0 ? 0 : size.Height / otherSize.Height)); public static Size Divide(this Size size) => size.Divide(size); public static Size Divide(this Size size, double dxy) => dxy == 0 ? Size.Empty : new((int)(size.Width / dxy), (int)(size.Height / dxy)); @@ -82,15 +82,11 @@ public static string SizeSuffix(long value, byte decimalPlaces = 2) /// public static Size Exchange(this Size size) => new(size.Height, size.Width); - public static int Area(this Rectangle rect) => rect.Width * rect.Height; - public static int Area(this Size size) => size.Width * size.Height; public static int Max(this Size size) => Math.Max(size.Width, size.Height); - public static float Area(this RectangleF rect, int round = -1) => round >= 0 ? (float) Math.Round(rect.Width * rect.Height, round) : rect.Width * rect.Height; - /// /// Gets if this size have a zero value on width or height /// @@ -98,7 +94,8 @@ public static string SizeSuffix(long value, byte decimalPlaces = 2) /// public static bool HaveZero(this SizeF size) => size.Width <= 0 || size.Height <= 0; - public static float Area(this SizeF size, int round = -1) => round >= 0 ? (float)Math.Round(size.Width * size.Height, round) : size.Width * size.Height; + public static float Area(this SizeF size) => size.Width * size.Height; + public static float Area(this SizeF size, int round) => (float)Math.Round(size.Area(), round) ; public static float Max(this SizeF size) => Math.Max(size.Width, size.Height); diff --git a/UVtools.Core/Extensions/ZipArchiveExtensions.cs b/UVtools.Core/Extensions/ZipArchiveExtensions.cs index 050b3d68..100496de 100644 --- a/UVtools.Core/Extensions/ZipArchiveExtensions.cs +++ b/UVtools.Core/Extensions/ZipArchiveExtensions.cs @@ -201,7 +201,7 @@ public static void AddToArchive(string archiveFullName, //Throws an error if the file exists if (archiveExists) { - throw new IOException(string.Format("The zip file {0} already exists.", archiveFullName)); + throw new IOException($"The zip file {archiveFullName} already exists."); } break; case ArchiveAction.Ignore: diff --git a/UVtools.Core/FileFormats/CTBEncryptedFile.cs b/UVtools.Core/FileFormats/CTBEncryptedFile.cs index 1552f3ed..c70dc5d1 100644 --- a/UVtools.Core/FileFormats/CTBEncryptedFile.cs +++ b/UVtools.Core/FileFormats/CTBEncryptedFile.cs @@ -435,14 +435,9 @@ void AddRep() AddRep(); - if (Parent!.Settings.LayerXorKey > 0) - { - RLEData = ChituboxFile.LayerRleCrypt(Parent.Settings.LayerXorKey, layerIndex, rawData); - } - else - { - RLEData = rawData.ToArray(); - } + RLEData = Parent!.Settings.LayerXorKey > 0 + ? ChituboxFile.LayerRleCrypt(Parent.Settings.LayerXorKey, layerIndex, rawData) + : rawData.ToArray(); DataLength = (uint)RLEData.Length; @@ -604,13 +599,13 @@ void RleRGB15() new(200, 125) }; - public Preview[] Previews { get; protected internal set; } + public Preview[] Previews { get; private set; } - public FileHeader Header { get; protected internal set; } = new(); + public FileHeader Header { get; private set; } = new(); - public SlicerSettings Settings { get; protected internal set; } = new(); - public LayerPointer[]? LayersPointer { get; protected internal set; } - public LayerDef[]? LayersDefinition { get; protected internal set; } + public SlicerSettings Settings { get; private set; } = new(); + public LayerPointer[]? LayersPointer { get; private set; } + public LayerDef[]? LayersDefinition { get; private set; } public override PrintParameterModifier[]? PrintParameterModifiers { get; } = { PrintParameterModifier.BottomLayerCount, @@ -798,7 +793,7 @@ public override float LightOffDelay public override float BottomWaitTimeBeforeCure { - get => base.BottomWaitTimeBeforeCure > 0 ? base.BottomWaitTimeBeforeCure : this.FirstOrDefault(layer => layer is not null && layer.IsBottomLayer && layer.NonZeroPixelCount > 1)?.WaitTimeBeforeCure ?? 0; + get => base.BottomWaitTimeBeforeCure > 0 ? base.BottomWaitTimeBeforeCure : this.FirstOrDefault(layer => layer is { IsBottomLayer: true, IsDummy: false })?.WaitTimeBeforeCure ?? 0; set => base.BottomWaitTimeBeforeCure = value; } @@ -825,7 +820,7 @@ public override float BottomExposureTime public override float BottomWaitTimeAfterCure { - get => base.BottomWaitTimeAfterCure > 0 ? base.BottomWaitTimeAfterCure : this.FirstOrDefault(layer => layer is not null && layer.IsBottomLayer && layer.NonZeroPixelCount > 1)?.WaitTimeAfterCure ?? 0; + get => base.BottomWaitTimeAfterCure > 0 ? base.BottomWaitTimeAfterCure : this.FirstOrDefault(layer => layer is { IsBottomLayer: true, IsDummy: false })?.WaitTimeAfterCure ?? 0; set => base.BottomWaitTimeAfterCure = value; } @@ -921,7 +916,7 @@ public override float LiftSpeed2 public override float BottomWaitTimeAfterLift { - get => base.BottomWaitTimeAfterLift > 0 ? base.BottomWaitTimeAfterLift : this.FirstOrDefault(layer => layer is not null && layer.IsBottomLayer && layer.NonZeroPixelCount > 1)?.WaitTimeAfterLift ?? 0; + get => base.BottomWaitTimeAfterLift > 0 ? base.BottomWaitTimeAfterLift : this.FirstOrDefault(layer => layer is { IsBottomLayer: true, IsDummy: false })?.WaitTimeAfterLift ?? 0; set => base.BottomWaitTimeAfterLift = value; } diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index 4d55fa27..8ee130ab 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -421,7 +421,7 @@ public override FlipDirection DisplayMirror { get { - if (OutputSettings.FlipX && OutputSettings.FlipY) return FlipDirection.Both; + if (OutputSettings is {FlipX: true, FlipY: true}) return FlipDirection.Both; if (OutputSettings.FlipX) return FlipDirection.Horizontally; if (OutputSettings.FlipY) return FlipDirection.Vertically; return FlipDirection.None; @@ -439,7 +439,7 @@ public override byte AntiAliasing get => (byte) OutputSettings.AntiAliasingValue; set { - base.AntiAliasing = (byte)(OutputSettings.AntiAliasingValue = value.Clamp(1, 16)); + base.AntiAliasing = (byte)(OutputSettings.AntiAliasingValue = Math.Clamp(value, 1u, 16u)); OutputSettings.AntiAliasing = OutputSettings.AntiAliasingValue > 1; } } @@ -737,8 +737,7 @@ protected override void DecodeInternally(OperationProgress progress) } using TextReader tr = new StreamReader(entry.Open()); - string? line; - while ((line = tr.ReadLine()) != null) + while (tr.ReadLine() is { } line) { line = line.Replace("# ", string.Empty); if (string.IsNullOrEmpty(line)) continue; @@ -767,9 +766,8 @@ protected override void DecodeInternally(OperationProgress progress) using (TextReader tr = new StreamReader(entry.Open())) { - string? line; GCode!.Clear(); - while ((line = tr.ReadLine()) != null) + while (tr.ReadLine() is { } line) { GCode.AppendLine(line); if (string.IsNullOrEmpty(line)) continue; diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index 7bf46edf..5a14221e 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -8,6 +8,7 @@ using BinarySerialization; using Emgu.CV; +using Emgu.CV.CvEnum; using Emgu.CV.Structure; using System; using System.Collections.Generic; @@ -19,7 +20,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Emgu.CV.CvEnum; using UVtools.Core.Converters; using UVtools.Core.EmguCV; using UVtools.Core.Extensions; @@ -417,10 +417,10 @@ public override string ToString() #region Properties - public Header HeaderSettings { get; protected internal set; } = new(); - public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new(); - public SlicerInfoV3 SlicerInfoV3Settings { get; protected internal set; } = new(); - public Footer FooterSettings { get; protected internal set; } = new(); + public Header HeaderSettings { get; private set; } = new(); + public SlicerInfo SlicerInfoSettings { get; private set; } = new(); + public SlicerInfoV3 SlicerInfoV3Settings { get; private set; } = new(); + public Footer FooterSettings { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; @@ -612,7 +612,7 @@ public override string MachineName { // Parse from machine name, if coming from PrusaSlicer this will help var match = Regex.Match(value, @"(CL|CT)-\d+"); - if (match.Success && match.Groups.Count > 1) + if (match is {Success: true, Groups.Count: > 1}) { value = match.Value; } diff --git a/UVtools.Core/FileFormats/CXDLPv1File.cs b/UVtools.Core/FileFormats/CXDLPv1File.cs index 2dda82d3..e7c11e46 100644 --- a/UVtools.Core/FileFormats/CXDLPv1File.cs +++ b/UVtools.Core/FileFormats/CXDLPv1File.cs @@ -305,9 +305,9 @@ public void Validate() #region Properties - public Header HeaderSettings { get; protected internal set; } = new(); - public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new(); - public Footer FooterSettings { get; protected internal set; } = new(); + public Header HeaderSettings { get; private set; } = new(); + public SlicerInfo SlicerInfoSettings { get; private set; } = new(); + public Footer FooterSettings { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index 41c00b21..4b908d08 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -285,7 +285,6 @@ public override string ToString() public class SlicerInfo { - private string _machineName = DefaultMachineName; [FieldOrder(0)] public float BottomLiftHeight2 { get; set; } [FieldOrder(1)] public float BottomLiftSpeed2 { get; set; } [FieldOrder(2)] public float LiftHeight2 { get; set; } @@ -302,7 +301,7 @@ public class SlicerInfo /// /// Gets the machine size in bytes /// - [FieldOrder(8)] public uint MachineNameSize { get; set; } = (uint)(string.IsNullOrEmpty(DefaultMachineName) ? 0 : DefaultMachineName.Length); + [FieldOrder(8)] public uint MachineNameSize { get; set; } /// /// CBDDLP: 0 [No AA] / 8 [AA] for cbddlp files. CTB: 7(0x7) [No AA] / 15(0x0F) [AA] @@ -348,16 +347,7 @@ public class SlicerInfo /// [FieldOrder(21)] [FieldLength(nameof(MachineNameSize))] - public string MachineName - { - get => _machineName; - set - { - if (string.IsNullOrEmpty(value)) value = DefaultMachineName; - _machineName = value; - MachineNameSize = string.IsNullOrEmpty(_machineName) ? 0 : (uint)_machineName.Length; - } - } + public string MachineName { get; set; } = DefaultMachineName; public override string ToString() { @@ -947,14 +937,9 @@ void AddRep() AddRep(); - if (Parent!.HeaderSettings.EncryptionKey > 0) - { - EncodedRle = LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData); - } - else - { - EncodedRle = rawData.ToArray(); - } + EncodedRle = Parent!.HeaderSettings.EncryptionKey > 0 + ? LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData) + : rawData.ToArray(); DataSize = (uint)EncodedRle.Length; @@ -1062,13 +1047,13 @@ public override string ToString() #region Properties - public Header HeaderSettings { get; protected internal set; } = new(); - public PrintParameters PrintParametersSettings { get; protected internal set; } = new(); + public Header HeaderSettings { get; private set; } = new(); + public PrintParameters PrintParametersSettings { get; private set; } = new(); - public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new(); - public PrintParametersV4 PrintParametersV4Settings { get; protected internal set; } = new(); + public SlicerInfo SlicerInfoSettings { get; private set; } = new(); + public PrintParametersV4 PrintParametersV4Settings { get; private set; } = new(); - public Preview[] Previews { get; protected internal set; } + public Preview[] Previews { get; } public LayerDef[,]? LayerDefinitions { get; private set; } @@ -1241,6 +1226,21 @@ public override PrintParameterModifier[]? PrintParameterPerLayerModifiers { public override uint[] AvailableVersions { get; } = { 1, 2, 3, 4 }; + public override uint[] GetAvailableVersionsForExtension(string? extension) + { + if (string.IsNullOrWhiteSpace(extension)) return AvailableVersions; + if (extension[0] == '.') extension = extension.Remove(0, 1).ToLower(); + + switch (extension) + { + case "cbddlp": + case "photon": + return new uint[] {1, 2}; + default: + return AvailableVersions; + } + } + public override uint DefaultVersion => DEFAULT_VERSION; public override uint Version @@ -1302,11 +1302,11 @@ public override byte AntiAliasing { if (IsCtbFile) { - base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = value.Clamp(1, 16)); + base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = Math.Clamp(value, 1u, 16u)); } else if(IsCbddlpFile) { - base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = HeaderSettings.AntiAliasLevel = value.Clamp(1, 16)); + base.AntiAliasing = (byte)(SlicerInfoSettings.AntiAliasLevel = HeaderSettings.AntiAliasLevel = Math.Clamp(value, 1u, 16u)); ValidateAntiAliasingLevel(); } } @@ -1384,7 +1384,7 @@ public override float LightOffDelay public override float BottomWaitTimeBeforeCure { - get => base.BottomWaitTimeBeforeCure > 0 ? base.BottomWaitTimeBeforeCure : this.FirstOrDefault(layer => layer is not null && layer.IsBottomLayer && layer.NonZeroPixelCount > 1)?.WaitTimeBeforeCure ?? 0; + get => base.BottomWaitTimeBeforeCure > 0 ? base.BottomWaitTimeBeforeCure : this.FirstOrDefault(layer => layer is {IsBottomLayer: true, IsDummy: false})?.WaitTimeBeforeCure ?? 0; set { if (HeaderSettings.Version < 4) @@ -1432,7 +1432,7 @@ public override float BottomExposureTime public override float BottomWaitTimeAfterCure { - get => HeaderSettings.Version >= 4 ? (base.BottomWaitTimeAfterCure > 0 ? base.BottomWaitTimeAfterCure : this.FirstOrDefault(layer => layer is not null && layer.IsBottomLayer && layer.NonZeroPixelCount > 1)?.WaitTimeAfterCure ?? 0) : 0; + get => HeaderSettings.Version >= 4 ? (base.BottomWaitTimeAfterCure > 0 ? base.BottomWaitTimeAfterCure : this.FirstOrDefault(layer => layer is {IsBottomLayer: true, IsDummy: false})?.WaitTimeAfterCure ?? 0) : 0; set { if (HeaderSettings.Version < 4) return; @@ -1553,7 +1553,7 @@ public override float LiftSpeed2 public override float BottomWaitTimeAfterLift { - get => HeaderSettings.Version >= 4 ? (base.BottomWaitTimeAfterLift > 0 ? base.BottomWaitTimeAfterLift : this.FirstOrDefault(layer => layer is not null && layer.IsBottomLayer && layer.NonZeroPixelCount > 1)?.WaitTimeAfterLift ?? 0) : 0; + get => HeaderSettings.Version >= 4 ? (base.BottomWaitTimeAfterLift > 0 ? base.BottomWaitTimeAfterLift : this.FirstOrDefault(layer => layer is { IsBottomLayer: true, IsDummy: false })?.WaitTimeAfterLift ?? 0) : 0; set { if (HeaderSettings.Version < 4) return; @@ -1688,10 +1688,7 @@ public override object[] Configs { <= 1 => new object[] { HeaderSettings }, <= 3 => new object[] { HeaderSettings, PrintParametersSettings, SlicerInfoSettings }, - _ => new object[] // v4 - { - HeaderSettings, PrintParametersSettings, SlicerInfoSettings, PrintParametersV4Settings - } + /*v4*/ _ => new object[] { HeaderSettings, PrintParametersSettings, SlicerInfoSettings, PrintParametersV4Settings } }; } } @@ -1777,7 +1774,7 @@ private void SanitizeMagicVersion() else if (FileEndsWith(".cbddlp") || FileEndsWith(".photon")) { HeaderSettings.Magic = MAGIC_CBDDLP; - if (HeaderSettings.Version > 3) HeaderSettings.Version = 3; + if (HeaderSettings.Version > 2) HeaderSettings.Version = 2; } } @@ -1871,30 +1868,30 @@ protected override void EncodeInternally(OperationProgress progress) if (HeaderSettings.Version >= 2) { HeaderSettings.PrintParametersOffsetAddress = (uint)outputFile.Position; - outputFile.WriteSerialize(PrintParametersSettings); - HeaderSettings.SlicerOffset = (uint)outputFile.Position; - HeaderSettings.SlicerSize = (uint) Helpers.Serializer.SizeOf(SlicerInfoSettings) - SlicerInfoSettings.MachineNameSize; - - SlicerInfoSettings.MachineNameAddress = HeaderSettings.SlicerOffset + HeaderSettings.SlicerSize; - - if (HeaderSettings.Version >= 4) + if (IsCtbFile) { - SlicerInfoSettings.PrintParametersV4Address = (uint)(HeaderSettings.SlicerOffset + - Helpers.Serializer.SizeOf(SlicerInfoSettings) + - CTBv4_DISCLAIMER_SIZE); - } + HeaderSettings.SlicerOffset = (uint) outputFile.Position; + HeaderSettings.SlicerSize = (uint) Helpers.Serializer.SizeOf(SlicerInfoSettings) - + SlicerInfoSettings.MachineNameSize; + SlicerInfoSettings.MachineNameAddress = HeaderSettings.SlicerOffset + HeaderSettings.SlicerSize; - outputFile.WriteSerialize(SlicerInfoSettings); + if (HeaderSettings.Version >= 4) + { + SlicerInfoSettings.PrintParametersV4Address = (uint) (HeaderSettings.SlicerOffset + Helpers.Serializer.SizeOf(SlicerInfoSettings) + CTBv4_DISCLAIMER_SIZE); + } + + outputFile.WriteSerialize(SlicerInfoSettings); - if (HeaderSettings.Version >= 4) - { - PrintParametersV4Settings.DisclaimerAddress = (uint)outputFile.Position; - PrintParametersV4Settings.DisclaimerLength = (uint)CTBv4_DISCLAIMER.Length; - outputFile.WriteBytes(Encoding.UTF8.GetBytes(CTBv4_DISCLAIMER)); - outputFile.WriteSerialize(PrintParametersV4Settings); + if (HeaderSettings.Version >= 4) + { + PrintParametersV4Settings.DisclaimerAddress = (uint) outputFile.Position; + PrintParametersV4Settings.DisclaimerLength = (uint) CTBv4_DISCLAIMER.Length; + outputFile.WriteBytes(Encoding.UTF8.GetBytes(CTBv4_DISCLAIMER)); + outputFile.WriteSerialize(PrintParametersV4Settings); + } } } @@ -2038,8 +2035,6 @@ protected override void DecodeInternally(OperationProgress progress) PrintParametersSettings = Helpers.Deserialize(inputFile); Debug.Write("Print Parameters -> "); Debug.WriteLine(PrintParametersSettings); - - } if (HeaderSettings.SlicerOffset > 0) @@ -2061,7 +2056,7 @@ protected override void DecodeInternally(OperationProgress progress) if (SlicerInfoSettings.PrintParametersV4Address == 0) { throw new FileLoadException( - $"Malformed file, PrintParametersV4Address is missing", + "Malformed file, PrintParametersV4Address is missing", FileFullPath); } @@ -2167,11 +2162,15 @@ protected override void PartialSaveInternally(OperationProgress progress) outputFile.Seek(0, SeekOrigin.Begin); outputFile.WriteSerialize(HeaderSettings); - if (HeaderSettings.Version >= 2 && HeaderSettings.PrintParametersOffsetAddress > 0) + if (HeaderSettings is {Version: >= 2, PrintParametersOffsetAddress: > 0}) { outputFile.Seek(HeaderSettings.PrintParametersOffsetAddress, SeekOrigin.Begin); outputFile.WriteSerialize(PrintParametersSettings); - outputFile.WriteSerialize(SlicerInfoSettings); + + if (IsCtbFile && HeaderSettings.SlicerOffset > 0) + { + outputFile.WriteSerialize(SlicerInfoSettings); + } } outputFile.Seek(HeaderSettings.LayersDefinitionOffsetAddress, SeekOrigin.Begin); diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs index 2f279ddf..94ee5709 100644 --- a/UVtools.Core/FileFormats/ChituboxZipFile.cs +++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs @@ -187,7 +187,7 @@ public override FlipDirection DisplayMirror public override byte AntiAliasing { get => HeaderSettings.AntiAliasing; - set => base.AntiAliasing = HeaderSettings.AntiAliasing = value.Clamp(1, 16); + set => base.AntiAliasing = HeaderSettings.AntiAliasing = Math.Clamp(value, (byte)1, (byte)16); } public override float LayerHeight @@ -425,9 +425,8 @@ protected override void DecodeInternally(OperationProgress progress) //Clear(); //throw new FileLoadException("run.gcode not found", fileFullPath); using TextReader tr = new StreamReader(entry.Open()); - string? line; GCode!.Clear(); - while ((line = tr.ReadLine()) != null) + while (tr.ReadLine() is { } line) { GCode.AppendLine(line); if (string.IsNullOrEmpty(line)) continue; diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs index a9b1e42c..776d6f60 100644 --- a/UVtools.Core/FileFormats/FDGFile.cs +++ b/UVtools.Core/FileFormats/FDGFile.cs @@ -597,14 +597,9 @@ void AddRep() } - if (Parent!.HeaderSettings.EncryptionKey > 0) - { - EncodedRle = LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData); - } - else - { - EncodedRle = rawData.ToArray(); - } + EncodedRle = Parent!.HeaderSettings.EncryptionKey > 0 + ? LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData) + : rawData.ToArray(); DataSize = (uint) EncodedRle.Length; } @@ -620,9 +615,9 @@ public override string ToString() #region Properties - public Header HeaderSettings { get; protected internal set; } = new Header(); + public Header HeaderSettings { get; private set; } = new(); - public Preview[] Previews { get; protected internal set; } + public Preview[] Previews { get; private set; } public LayerDef[] LayersDefinitions { get; private set; } = null!; @@ -722,7 +717,7 @@ public override FlipDirection DisplayMirror public override byte AntiAliasing { get => (byte) HeaderSettings.AntiAliasLevelInfo; - set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16)); + set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = Math.Clamp(value, 1u, 16u)); } public override float LayerHeight @@ -1059,7 +1054,7 @@ protected override void DecodeInternally(OperationProgress progress) progress++; } - if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) + if (HeaderSettings is {MachineNameAddress: > 0, MachineNameSize: > 0}) { inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); var buffer = new byte[HeaderSettings.MachineNameSize]; diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 0e2006f2..a9d94d2d 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -422,9 +422,9 @@ public static List>> AllFileFiltersAvalonia new("All slicer files", new List()) }; - for (int i = 0; i < AvailableFormats.Length; i++) + foreach (var format in AvailableFormats) { - foreach (var fileExtension in AvailableFormats[i].FileExtensions) + foreach (var fileExtension in format.FileExtensions) { if(!fileExtension.IsVisibleOnFileFilters) continue; result[0].Value.Add(fileExtension.Extension); @@ -1184,12 +1184,26 @@ public string FileFilterExtensionsOnly /// /// Gets the available versions to set in this file format /// - public virtual uint[]? AvailableVersions => null; + public virtual uint[] AvailableVersions => Array.Empty(); /// /// Gets the amount of available versions in this file format /// - public virtual byte AvailableVersionsCount => (byte)(AvailableVersions?.Length ?? 0); + public virtual byte AvailableVersionsCount => (byte)AvailableVersions.Length; + + /// + /// Gets the available versions to set in this file format for the given extension + /// + /// Extension name, with or without dot (.) + /// + public virtual uint[] GetAvailableVersionsForExtension(string? extension) => AvailableVersions; + + /// + /// Gets the available versions to set in this file format for the given file name + /// + /// + /// + public uint[] GetAvailableVersionsForFileName(string? fileName) => GetAvailableVersionsForExtension(Path.GetExtension(fileName)); /// /// Gets the default version to use in this file when not setting the version @@ -1204,7 +1218,7 @@ public virtual uint Version get => _version; set { - if (AvailableVersions is not null && !AvailableVersions.Contains(value)) + if (AvailableVersions.Length > 0 && !AvailableVersions.Contains(value)) { throw new VersionNotFoundException($"Version {value} not known for this file format"); } @@ -1347,22 +1361,22 @@ public Layer[] Layers /// /// Gets the smallest bottom layer using the pixel count /// - public Layer? SmallestBottomLayer => this.Where(layer => layer.IsBottomLayer && !layer.IsEmpty).MinBy(layer => layer.NonZeroPixelCount); + public Layer? SmallestBottomLayer => this.Where(layer => layer is {IsBottomLayer: true, IsEmpty: false}).MinBy(layer => layer.NonZeroPixelCount); /// /// Gets the largest bottom layer using the pixel count /// - public Layer? LargestBottomLayer => this.Where(layer => layer.IsBottomLayer && !layer.IsEmpty).MaxBy(layer => layer.NonZeroPixelCount); + public Layer? LargestBottomLayer => this.Where(layer => layer is {IsBottomLayer: true, IsEmpty: false}).MaxBy(layer => layer.NonZeroPixelCount); /// /// Gets the smallest normal layer using the pixel count /// - public Layer? SmallestNormalLayer => this.Where(layer => layer.IsNormalLayer && !layer.IsEmpty).MinBy(layer => layer.NonZeroPixelCount); + public Layer? SmallestNormalLayer => this.Where(layer => layer is {IsNormalLayer: true, IsEmpty: false}).MinBy(layer => layer.NonZeroPixelCount); /// /// Gets the largest layer using the pixel count /// - public Layer? LargestNormalLayer => this.Where(layer => layer.IsNormalLayer && !layer.IsEmpty).MaxBy(layer => layer.NonZeroPixelCount); + public Layer? LargestNormalLayer => this.Where(layer => layer is {IsNormalLayer: true, IsEmpty: false}).MaxBy(layer => layer.NonZeroPixelCount); /// /// Gets the smallest normal layer using the pixel count @@ -3254,9 +3268,9 @@ public virtual void Clear() _layers = Array.Empty(); GCode?.Clear(); - for (int i = 0; i < Thumbnails.Length; i++) + foreach (var mat in Thumbnails) { - Thumbnails[i]?.Dispose(); + mat?.Dispose(); } } @@ -3487,13 +3501,32 @@ protected virtual void OnAfterEncode(bool isPartialEncode) { } public void Encode(string? fileFullPath, OperationProgress? progress = null) { fileFullPath ??= FileFullPath ?? throw new ArgumentNullException(nameof(fileFullPath)); - if (DecodeType == FileDecodeType.Partial) throw new InvalidOperationException("File was partial decoded, a full encode is not possible."); progress ??= new OperationProgress(); progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); Sanitize(); + + // Backup old file name and prepare the temporary file to be written next + var oldFilePath = FileFullPath; + FileFullPath = fileFullPath; + var tempFile = TemporaryOutputFileFullPath; + if (File.Exists(tempFile)) File.Delete(tempFile); + + // Sanitize Version + if (AvailableVersionsCount > 0) + { + var possibleVersions = GetAvailableVersionsForExtension(FileExtension); + if (possibleVersions.Length > 0) + { + if (!possibleVersions.Contains(Version)) // Version not found on possible versions, set to last + { + Version = possibleVersions[^1]; + } + } + } + OnBeforeEncode(false); for (var i = 0; i < Thumbnails.Length; i++) @@ -3503,12 +3536,6 @@ public void Encode(string? fileFullPath, OperationProgress? progress = null) CvInvoke.Resize(Thumbnails[i], Thumbnails[i], new Size(ThumbnailsOriginalSize[i].Width, ThumbnailsOriginalSize[i].Height)); } - // Backup old file name and prepare the temporary file to be written next - var oldFilePath = FileFullPath; - FileFullPath = fileFullPath; - var tempFile = TemporaryOutputFileFullPath; - if (File.Exists(tempFile)) File.Delete(tempFile); - try { EncodeInternally(progress); diff --git a/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs index a466df58..a1f5770f 100644 --- a/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs +++ b/UVtools.Core/FileFormats/FlashForgeSVGXFile.cs @@ -252,9 +252,9 @@ public override string ToString() #region Properties - public Header HeaderSettings { get; protected internal set; } = new(); + public Header HeaderSettings { get; private set; } = new(); - public FlashForgeSVGXSvg SVGDocument { get; protected internal set; } = new(); + public FlashForgeSVGXSvg SVGDocument { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; diff --git a/UVtools.Core/FileFormats/GR1File.cs b/UVtools.Core/FileFormats/GR1File.cs index 1c66ab1b..5cf49b49 100644 --- a/UVtools.Core/FileFormats/GR1File.cs +++ b/UVtools.Core/FileFormats/GR1File.cs @@ -144,8 +144,8 @@ public sealed class PageBreak #region Properties - public Header HeaderSettings { get; protected internal set; } = new(); - public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new(); + public Header HeaderSettings { get; private set; } = new(); + public SlicerInfo SlicerInfoSettings { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { diff --git a/UVtools.Core/FileFormats/JXSFile.cs b/UVtools.Core/FileFormats/JXSFile.cs index ed31bb08..df65bb5c 100644 --- a/UVtools.Core/FileFormats/JXSFile.cs +++ b/UVtools.Core/FileFormats/JXSFile.cs @@ -330,11 +330,9 @@ private void RebuildFileProperties() ControlFile.Actions.Clear(); using var tw = new StringReader(GCodeStr!); - string? line; var lastG0 = "G1 Z100 F150"; - while ((line = tw.ReadLine()) is not null) + while (tw.ReadLine()?.Trim() is { } line) { - line = line.Trim(); if (line == string.Empty) continue; if (line.StartsWith(GCode!.CommandClearImage.Command)) @@ -435,8 +433,7 @@ protected override void DecodeInternally(OperationProgress progress) { using var stream = entry.Open(); using TextReader reader = new StreamReader(stream); - string? line; - while((line = reader.ReadLine()) is not null) + while(reader.ReadLine() is { } line) { var keyValue = line.Split('=', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if(keyValue.Length < 2) continue; diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index 23e05e10..d97d6bc2 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -260,7 +260,7 @@ public Mat Decode(bool consumeRle = true) #region Properties - public Header HeaderSettings { get; protected internal set; } = new(); + public Header HeaderSettings { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; public override string ConvertMenuGroup => "Longer3D"; diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs index aed0d718..3dfbfb34 100644 --- a/UVtools.Core/FileFormats/MDLPFile.cs +++ b/UVtools.Core/FileFormats/MDLPFile.cs @@ -15,7 +15,6 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; using UVtools.Core.Extensions; using UVtools.Core.Layers; @@ -149,8 +148,8 @@ public sealed class PageBreak #region Properties - public Header HeaderSettings { get; protected internal set; } = new(); - public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new(); + public Header HeaderSettings { get; private set; } = new(); + public SlicerInfo SlicerInfoSettings { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { diff --git a/UVtools.Core/FileFormats/OSFFile.cs b/UVtools.Core/FileFormats/OSFFile.cs index 248415f6..cf42e397 100644 --- a/UVtools.Core/FileFormats/OSFFile.cs +++ b/UVtools.Core/FileFormats/OSFFile.cs @@ -7,13 +7,13 @@ */ using BinarySerialization; +using Emgu.CV; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Threading.Tasks; -using Emgu.CV; using UVtools.Core.Extensions; using UVtools.Core.Layers; using UVtools.Core.Objects; @@ -307,8 +307,8 @@ internal Mat DecodeImage(OSFFile parent) #region Properties - public OSFHeader Header { get; protected internal set; } = new(); - public OSFSettings Settings { get; protected internal set; } = new(); + public OSFHeader Header { get; private set; } = new(); + public OSFSettings Settings { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { diff --git a/UVtools.Core/FileFormats/OSLAFile.cs b/UVtools.Core/FileFormats/OSLAFile.cs index 3ba1a63e..17f3369f 100644 --- a/UVtools.Core/FileFormats/OSLAFile.cs +++ b/UVtools.Core/FileFormats/OSLAFile.cs @@ -67,7 +67,7 @@ public override string ToString() public void Update() { ModifiedDateTime= DateTime.UtcNow.ToString("u"); - ModifiedBy = About.SoftwareWithVersion;; + ModifiedBy = About.SoftwareWithVersion; } public void Validate() @@ -242,9 +242,9 @@ public class GCodeDef #region Properties - public FileDef FileSettings { get; protected internal set; } = new(); - public Header HeaderSettings { get; protected internal set; } = new(); - public CustomTable CustomTableSettings { get; protected internal set; } = new(); + public FileDef FileSettings { get; private set; } = new(); + public Header HeaderSettings { get; private set; } = new(); + public CustomTable CustomTableSettings { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index 3514083f..bfc1d7b7 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -611,14 +611,9 @@ void AddRep() } - if (Parent.HeaderSettings.EncryptionKey > 0) - { - EncodedRle = LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData); - } - else - { - EncodedRle = rawData.ToArray(); - } + EncodedRle = Parent.HeaderSettings.EncryptionKey > 0 + ? LayerRleCrypt(Parent.HeaderSettings.EncryptionKey, layerIndex, rawData) + : rawData.ToArray(); DataSize = (uint) EncodedRle.Length; } @@ -634,9 +629,9 @@ public override string ToString() #region Properties - public Header HeaderSettings { get; protected internal set; } = new(); + public Header HeaderSettings { get; private set; } = new(); - public Preview[] Previews { get; protected internal set; } + public Preview[] Previews { get; private set; } public LayerDef[] LayersDefinitions { get; private set; } = null!; @@ -738,7 +733,7 @@ public override FlipDirection DisplayMirror public override byte AntiAliasing { get => (byte) HeaderSettings.AntiAliasLevelInfo; - set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = value.Clamp(1, 16)); + set => base.AntiAliasing = (byte)(HeaderSettings.AntiAliasLevelInfo = Math.Clamp(value, 1u, 16u)); } public override float LayerHeight @@ -1081,7 +1076,7 @@ protected override void DecodeInternally(OperationProgress progress) progress++; } - if (HeaderSettings.MachineNameAddress > 0 && HeaderSettings.MachineNameSize > 0) + if (HeaderSettings is {MachineNameAddress: > 0, MachineNameSize: > 0}) { inputFile.Seek(HeaderSettings.MachineNameAddress, SeekOrigin.Begin); byte[] buffer = new byte[HeaderSettings.MachineNameSize]; diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 4147850a..5463b191 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -1007,20 +1007,20 @@ public void Validate() #region Properties - public FileMark FileMarkSettings { get; protected internal set; } = new(); + public FileMark FileMarkSettings { get; private set; } = new(); - public Header HeaderSettings { get; protected internal set; } = new(); - public HeaderV516 HeaderV516Settings { get; protected internal set; } = new(); - public HeaderV517 HeaderV517Settings { get; protected internal set; } = new(); + public Header HeaderSettings { get; private set; } = new(); + public HeaderV516 HeaderV516Settings { get; private set; } = new(); + public HeaderV517 HeaderV517Settings { get; private set; } = new(); - public Preview PreviewSettings { get; protected internal set; } = new(); - public LayerImageColorTable LayerImageColorSettings { get; protected internal set; } = new(); + public Preview PreviewSettings { get; private set; } = new(); + public LayerImageColorTable LayerImageColorSettings { get; private set; } = new(); - public LayerDefinition LayersDefinition { get; protected internal set; } = new(); - public Extra ExtraSettings { get; protected internal set; } = new(); - public Machine MachineSettings { get; protected internal set; } = new(true); - public Software SoftwareSettings { get; protected internal set; } = new(); - public Model ModelSettings { get; protected internal set; } = new(); + public LayerDefinition LayersDefinition { get; private set; } = new(); + public Extra ExtraSettings { get; private set; } = new(); + public Machine MachineSettings { get; private set; } = new(true); + public Software SoftwareSettings { get; private set; } = new(); + public Model ModelSettings { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; @@ -1114,6 +1114,37 @@ public override PrintParameterModifier[]? PrintParameterModifiers public override uint[] AvailableVersions { get; } = { VERSION_1, VERSION_515, VERSION_516, VERSION_517 }; + public override uint[] GetAvailableVersionsForExtension(string? extension) + { + if (string.IsNullOrWhiteSpace(extension)) return AvailableVersions; + if (extension[0] == '.') extension = extension.Remove(0, 1).ToLower(); + + switch (extension) + { + case "pws": + case "pw0": + case "pwx": + return new uint[] {VERSION_1}; + case "pwmo": + case "pwms": + case "pmsq": + case "dlp": + return new uint[] { VERSION_1, VERSION_515 }; + case "pwma": + case "pwmx": + case "pm3": + case "pm3m": + return new uint[] { VERSION_515, VERSION_516 }; + case "pwmb": + case "dl2p": + case "pmx2": + case "pm3r": + return new uint[] { VERSION_515, VERSION_516, VERSION_517 }; + default: + return AvailableVersions; + } + } + public override uint DefaultVersion => VERSION_1; public override uint Version @@ -1234,7 +1265,7 @@ public override byte AntiAliasing get => (byte) HeaderSettings.AntiAliasing; set { - base.AntiAliasing = (byte)(HeaderSettings.AntiAliasing = value.Clamp(1, 16)); + base.AntiAliasing = (byte)(HeaderSettings.AntiAliasing = Math.Clamp(value, (byte)1, (byte)16)); ValidateAntiAliasingLevel(); } } @@ -1835,7 +1866,7 @@ protected override void DecodeInternally(OperationProgress progress) PreviewSettings.Data = null!; } - if (FileMarkSettings.Version >= VERSION_515 && FileMarkSettings.LayerImageColorTableAddress > 0) + if (FileMarkSettings is {Version: >= VERSION_515, LayerImageColorTableAddress: > 0}) { inputFile.Seek(FileMarkSettings.LayerImageColorTableAddress, SeekOrigin.Begin); LayerImageColorSettings = Helpers.Deserialize(inputFile); @@ -1843,7 +1874,7 @@ protected override void DecodeInternally(OperationProgress progress) Debug.WriteLine(LayerImageColorSettings); } - if (FileMarkSettings.Version >= VERSION_516 && FileMarkSettings.ExtraAddress > 0) + if (FileMarkSettings is {Version: >= VERSION_516, ExtraAddress: > 0}) { inputFile.Seek(FileMarkSettings.ExtraAddress, SeekOrigin.Begin); ExtraSettings = Helpers.Deserialize(inputFile); @@ -1852,7 +1883,7 @@ protected override void DecodeInternally(OperationProgress progress) Debug.WriteLine(ExtraSettings); } - if (FileMarkSettings.Version >= VERSION_516 && FileMarkSettings.MachineAddress > 0) + if (FileMarkSettings is {Version: >= VERSION_516, MachineAddress: > 0}) { inputFile.Seek(FileMarkSettings.MachineAddress, SeekOrigin.Begin); MachineSettings = Helpers.Deserialize(inputFile); @@ -1861,7 +1892,7 @@ protected override void DecodeInternally(OperationProgress progress) Debug.WriteLine(MachineSettings); } - if (FileMarkSettings.Version >= VERSION_517 && FileMarkSettings.SoftwareAddress > 0) + if (FileMarkSettings is {Version: >= VERSION_517, SoftwareAddress: > 0}) { inputFile.Seek(FileMarkSettings.SoftwareAddress, SeekOrigin.Begin); SoftwareSettings = Helpers.Deserialize(inputFile); @@ -1869,7 +1900,7 @@ protected override void DecodeInternally(OperationProgress progress) Debug.WriteLine(SoftwareSettings); } - if (FileMarkSettings.Version >= VERSION_517 && FileMarkSettings.ModelAddress > 0) + if (FileMarkSettings is {Version: >= VERSION_517, ModelAddress: > 0}) { inputFile.Seek(FileMarkSettings.ModelAddress, SeekOrigin.Begin); ModelSettings = Helpers.Deserialize(inputFile); diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index 0f73d4cf..30f48922 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -392,7 +392,7 @@ public override FlipDirection DisplayMirror { get { - if (PrinterSettings.DisplayMirrorX > 0 && PrinterSettings.DisplayMirrorY > 0) return FlipDirection.Both; + if (PrinterSettings is {DisplayMirrorX: > 0, DisplayMirrorY: > 0}) return FlipDirection.Both; if (PrinterSettings.DisplayMirrorX > 0) return FlipDirection.Horizontally; if (PrinterSettings.DisplayMirrorY > 0) return FlipDirection.Vertically; return FlipDirection.None; @@ -620,8 +620,7 @@ protected override void DecodeInternally(OperationProgress progress) if (!entity.Name.EndsWith(".ini")) continue; iniFiles.Add(entity.Name); using StreamReader streamReader = new(entity.Open()); - string? line; - while ((line = streamReader.ReadLine()) != null) + while (streamReader.ReadLine() is { } line) { var keyValue = line.Split('=', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (keyValue.Length < 2) continue; diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs index 486e7ca8..7702a4b7 100644 --- a/UVtools.Core/FileFormats/UVJFile.cs +++ b/UVtools.Core/FileFormats/UVJFile.cs @@ -268,7 +268,7 @@ public override float DisplayHeight public override byte AntiAliasing { get => JsonSettings.Properties.AntiAliasLevel; - set => base.AntiAliasing = JsonSettings.Properties.AntiAliasLevel = value.Clamp(1, 16); + set => base.AntiAliasing = JsonSettings.Properties.AntiAliasLevel = Math.Clamp(value, (byte)1, (byte)16); } public override float LayerHeight diff --git a/UVtools.Core/FileFormats/VDAFile.cs b/UVtools.Core/FileFormats/VDAFile.cs index d7eea3b7..6aec4d69 100644 --- a/UVtools.Core/FileFormats/VDAFile.cs +++ b/UVtools.Core/FileFormats/VDAFile.cs @@ -249,7 +249,7 @@ public override float MachineZ public override byte AntiAliasing { get => ManifestFile.Machines.AntiAliasing; - set => base.AntiAliasing = ManifestFile.Machines.AntiAliasing = value.Clamp(1, 16); + set => base.AntiAliasing = ManifestFile.Machines.AntiAliasing = Math.Clamp(value, (byte)1, (byte)16); } public override float LayerHeight diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs index 7f45df95..7ea77251 100644 --- a/UVtools.Core/FileFormats/VDTFile.cs +++ b/UVtools.Core/FileFormats/VDTFile.cs @@ -324,7 +324,7 @@ public override FlipDirection DisplayMirror { get { - if (ManifestFile.Machine.XMirror && ManifestFile.Machine.YMirror) return FlipDirection.Both; + if (ManifestFile.Machine is {XMirror: true, YMirror: true}) return FlipDirection.Both; if (ManifestFile.Machine.XMirror) return FlipDirection.Horizontally; if (ManifestFile.Machine.YMirror) return FlipDirection.Vertically; return FlipDirection.None; @@ -340,7 +340,7 @@ public override FlipDirection DisplayMirror public override byte AntiAliasing { get => ManifestFile.AdvancedParameters.AntialasingLevel; - set => base.AntiAliasing = ManifestFile.AdvancedParameters.AntialasingLevel = value.Clamp(1, 16); + set => base.AntiAliasing = ManifestFile.AdvancedParameters.AntialasingLevel = Math.Clamp(value, (byte)1, (byte)16); } public override float LayerHeight diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs index 1105fba8..d11e9b17 100644 --- a/UVtools.Core/FileFormats/ZCodeFile.cs +++ b/UVtools.Core/FileFormats/ZCodeFile.cs @@ -276,7 +276,7 @@ public override float MachineZ public override byte AntiAliasing { get => ManifestFile.Profile.Slice.AntiAliasing; - set => base.AntiAliasing = ManifestFile.Profile.Slice.AntiAliasing = value.Clamp(1, 16); + set => base.AntiAliasing = ManifestFile.Profile.Slice.AntiAliasing = Math.Clamp(value, (byte)1, (byte)16); } public override float LayerHeight @@ -518,9 +518,8 @@ protected override void DecodeInternally(OperationProgress progress) using (TextReader tr = new StreamReader(entry.Open())) { - string? line; progress.Reset("Decrypting GCode", (uint) (entry.Length / 88)); - while ((line = tr.ReadLine()) != null) + while (tr.ReadLine() is { } line) { if (string.IsNullOrEmpty(line)) continue; if (!line.EndsWith("==")) continue; @@ -581,8 +580,7 @@ private string EncryptGCode(OperationProgress progress) encryptEngine.Init(true, keyParameter); using StringReader sr = new(GCodeStr!); - string? line; - while ((line = sr.ReadLine()) != null) + while (sr.ReadLine() is { } line) { line = line.Trim(); if (line == string.Empty || line[0] == ';') continue; // No empty lines nor comment start lines diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index 69a0aa96..24996bfd 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -198,7 +198,7 @@ public override FlipDirection DisplayMirror public override byte AntiAliasing { get => UserSettings.AntiAliasing; - set => base.AntiAliasing = UserSettings.AntiAliasing = value.Clamp(1, 16); + set => base.AntiAliasing = UserSettings.AntiAliasing = Math.Clamp(value, (byte)1, (byte)16); } public override float LayerHeight @@ -473,12 +473,11 @@ protected override void DecodeInternally(OperationProgress progress) GCode!.Clear(); using (TextReader tr = new StreamReader(entry.Open())) { - string? line; int layerIndex = 0; int layerFileIndex = 0; string layerimagePath = null!; float currentHeight = 0; - while ((line = tr.ReadLine()) is not null) + while (tr.ReadLine() is { } line) { GCode.AppendLine(line); if (line.StartsWith(GCodeKeywordSlice)) @@ -517,7 +516,7 @@ M106 S0 float liftHeight = 0; float liftSpeed = GetBottomOrNormalValue((uint)layerIndex, BottomLiftSpeed, LiftSpeed); float retractSpeed = RetractSpeed; - byte pwm = GetBottomOrNormalValue((uint)layerIndex, BottomLightPWM, LightPWM); ; + byte pwm = GetBottomOrNormalValue((uint)layerIndex, BottomLightPWM, LightPWM); //var currPos = Regex.Match(stripGcode, "G1 Z([+-]?([0-9]*[.])?[0-9]+)", RegexOptions.IgnoreCase); var moveG1Regex = Regex.Match(stripGcode, @"G1 Z([+-]?([0-9]*[.])?[0-9]+) F(\d+)", RegexOptions.IgnoreCase); diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index 68b087ef..340e021c 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -819,7 +819,7 @@ public void RebuildGCode(FileFormat slicerFile, string? header = null) } AppendWaitG4(waitBeforeCure, "Wait before cure"); // Safer to parse if present - if (_gCodeShowImagePosition == GCodeShowImagePositions.WhenRequired && layer.CanExpose) + if (_gCodeShowImagePosition == GCodeShowImagePositions.WhenRequired && layer.ShouldExpose) { AppendShowImageM6054(GetShowImageString(layerIndex)); } @@ -868,8 +868,7 @@ public void RebuildGCode(FileFormat slicerFile, object[]? configs, string separa public GCodePositioningTypes ParsePositioningTypeFromGCode(string gcode) { using var reader = new StringReader(gcode); - string? line; - while ((line = reader.ReadLine()) != null) + while (reader.ReadLine() is { } line) { if (line.StartsWith(CommandPositioningAbsoluteG90.Command)) return GCodePositioningTypes.Absolute; if (line.StartsWith(CommandPositioningPartialG91.Command)) return GCodePositioningTypes.Relative; @@ -878,8 +877,8 @@ public GCodePositioningTypes ParsePositioningTypeFromGCode(string gcode) return _gCodePositioningType; } - public void ParseLayersFromGCode(FileFormat slicerFile, bool rebuildGlobalTable = true) => - ParseLayersFromGCode(slicerFile, null, rebuildGlobalTable); + public void ParseLayersFromGCode(FileFormat slicerFile, bool rebuildGlobalTable = true) + => ParseLayersFromGCode(slicerFile, null, rebuildGlobalTable); public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebuildGlobalTable = true) { @@ -910,11 +909,9 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu //bool parseLayerIndexFromComments = false; using var reader = new StringReader(gcode); - string? line; - while ((line = reader.ReadLine()) != null) + while (reader.ReadLine()?.Trim() is { } line) { - line = line.Trim(); - if (string.IsNullOrWhiteSpace(line)) continue; + if (string.IsNullOrEmpty(line)) continue; // Search for and switch position type when needed if (line.StartsWith(CommandPositioningAbsoluteG90.Command)) @@ -935,7 +932,7 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu { // Can be dangerous! match = Regex.Match(line, @"^;\W*(layer\W*|LAYER_START:\s*)(\d+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - if (match.Success && match.Groups.Count > 2 && uint.TryParse(match.Groups[2].Value, out var layerIndex)) + if (match is {Success: true, Groups.Count: > 2} && uint.TryParse(match.Groups[2].Value, out var layerIndex)) { if (layerIndex > slicerFile.LayerCount) { @@ -954,7 +951,7 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu if (line.StartsWith(CommandShowImageM6054.Command)) { match = Regex.Match(line, CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"(\d+)")), RegexOptions.IgnoreCase); - if (match.Success && match.Groups.Count >= 2) // Begin new layer + if (match is {Success: true, Groups.Count: >= 2}) // Begin new layer { var layerIndex = uint.Parse(match.Groups[1].Value); if (_gCodeShowImageType is GCodeShowImageTypes.FilenamePng1Started or GCodeShowImageTypes.LayerIndex1Started) layerIndex--; @@ -984,7 +981,7 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu { match = Regex.Match(line, $"{CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)")}|{CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)")}", RegexOptions.IgnoreCase); - if (match.Success && match.Groups.Count >= 9 && !layerBlock.RetractSpeed2.HasValue && layerBlock.LightOffCount < 2) + if (match is {Success: true, Groups.Count: >= 9} && !layerBlock.RetractSpeed2.HasValue && layerBlock.LightOffCount < 2) { var startIndex = match.Groups[1].Value.Length > 0 ? 0 : 4; float pos = float.Parse(match.Groups[startIndex+1].Value, CultureInfo.InvariantCulture); @@ -1034,7 +1031,7 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu if (line.StartsWith(CommandWaitG4.Command)) { match = Regex.Match(line, CommandWaitG4.ToStringWithoutComments(@"(([0-9]*[.])?[0-9]+)"), RegexOptions.IgnoreCase); - if (match.Success && match.Groups.Count >= 2) + if (match is {Success: true, Groups.Count: >= 2}) { if (/*CommandWaitSyncDelay.Enabled && */match.Groups[1].Value.StartsWith('0')) { @@ -1090,7 +1087,7 @@ public void ParseLayersFromGCode(FileFormat slicerFile, string? gcode, bool rebu if (line.StartsWith(CommandTurnLEDM106.Command)) { match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"), RegexOptions.IgnoreCase); - if (match.Success && match.Groups.Count >= 2) + if (match is {Success: true, Groups.Count: >= 2}) { byte pwm; if (_maxLedPower == byte.MaxValue) diff --git a/UVtools.Core/Gerber/Apertures/Aperture.cs b/UVtools.Core/Gerber/Apertures/Aperture.cs index 09190a29..0ded93e5 100644 --- a/UVtools.Core/Gerber/Apertures/Aperture.cs +++ b/UVtools.Core/Gerber/Apertures/Aperture.cs @@ -6,14 +6,14 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; using System; using System.Drawing; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; -using Emgu.CV; -using Emgu.CV.CvEnum; -using Emgu.CV.Structure; using UVtools.Core.Extensions; namespace UVtools.Core.Gerber.Apertures; @@ -31,28 +31,28 @@ public abstract class Aperture /// public string Name { get; set; } = string.Empty; - public GerberDocument Document { get; set; } = null!; + public GerberFormat Document { get; set; } = null!; #endregion protected Aperture() { } - protected Aperture(GerberDocument document, int index) + protected Aperture(GerberFormat document, int index) { Document = document; Index = index; } - protected Aperture(GerberDocument document, string name) + protected Aperture(GerberFormat document, string name) { Document = document; Name = name; } - protected Aperture(GerberDocument document, int index, string name) : this(document, index) { Name = name; } + protected Aperture(GerberFormat document, int index, string name) : this(document, index) { Name = name; } public abstract void DrawFlashD3(Mat mat, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected); - public static Aperture? Parse(string line, GerberDocument document) + public static Aperture? Parse(string line, GerberFormat document) { var match = Regex.Match(line, @"\%ADD(\d+)(\w+),?(\S+)?\*\%"); if (!match.Success || match.Groups.Count < 3) return null; diff --git a/UVtools.Core/Gerber/Apertures/CircleAperture.cs b/UVtools.Core/Gerber/Apertures/CircleAperture.cs index 219def16..42a2a64a 100644 --- a/UVtools.Core/Gerber/Apertures/CircleAperture.cs +++ b/UVtools.Core/Gerber/Apertures/CircleAperture.cs @@ -6,10 +6,10 @@ * of this license document, but changing it is not allowed. */ -using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; +using System.Drawing; namespace UVtools.Core.Gerber.Apertures; @@ -20,9 +20,9 @@ public class CircleAperture : Aperture #endregion #region Constructor - public CircleAperture(GerberDocument document) : base(document, "Circle") { } + public CircleAperture(GerberFormat document) : base(document, "Circle") { } - public CircleAperture(GerberDocument document, int index, double diameter) : base(document, index, "Circle") + public CircleAperture(GerberFormat document, int index, double diameter) : base(document, index, "Circle") { Diameter = document.GetMillimeters(diameter); } diff --git a/UVtools.Core/Gerber/Apertures/EllipseAperture.cs b/UVtools.Core/Gerber/Apertures/EllipseAperture.cs index e65e43ce..13453989 100644 --- a/UVtools.Core/Gerber/Apertures/EllipseAperture.cs +++ b/UVtools.Core/Gerber/Apertures/EllipseAperture.cs @@ -6,10 +6,10 @@ * of this license document, but changing it is not allowed. */ -using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; +using System.Drawing; namespace UVtools.Core.Gerber.Apertures; @@ -20,14 +20,14 @@ public class EllipseAperture : Aperture #endregion #region Constructor - public EllipseAperture(GerberDocument document) : base(document, "Ellipse") { } + public EllipseAperture(GerberFormat document) : base(document, "Ellipse") { } - public EllipseAperture(GerberDocument document, int index, float width, float height) : this(document, index, new SizeF(width, height)) + public EllipseAperture(GerberFormat document, int index, float width, float height) : this(document, index, new SizeF(width, height)) { } - public EllipseAperture(GerberDocument document, int index, SizeF axes) : base(document, index, "Ellipse") + public EllipseAperture(GerberFormat document, int index, SizeF axes) : base(document, index, "Ellipse") { Axes = document.GetMillimeters(axes); } diff --git a/UVtools.Core/Gerber/Apertures/MacroAperture.cs b/UVtools.Core/Gerber/Apertures/MacroAperture.cs index c00174b2..737dda7f 100644 --- a/UVtools.Core/Gerber/Apertures/MacroAperture.cs +++ b/UVtools.Core/Gerber/Apertures/MacroAperture.cs @@ -6,10 +6,10 @@ * of this license document, but changing it is not allowed. */ -using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; +using System.Drawing; namespace UVtools.Core.Gerber.Apertures; @@ -21,9 +21,9 @@ public class MacroAperture : Aperture #endregion #region Constructor - public MacroAperture(GerberDocument document) : base(document, "Macro") { } + public MacroAperture(GerberFormat document) : base(document, "Macro") { } - public MacroAperture(GerberDocument document, int index, Macro macro) : base(document, index, "Macro") + public MacroAperture(GerberFormat document, int index, Macro macro) : base(document, index, "Macro") { Macro = macro; } diff --git a/UVtools.Core/Gerber/Apertures/PoygonAperture.cs b/UVtools.Core/Gerber/Apertures/PoygonAperture.cs index 19d92987..e06789b2 100644 --- a/UVtools.Core/Gerber/Apertures/PoygonAperture.cs +++ b/UVtools.Core/Gerber/Apertures/PoygonAperture.cs @@ -6,11 +6,10 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; +using System.Drawing; using UVtools.Core.Extensions; namespace UVtools.Core.Gerber.Apertures; @@ -23,9 +22,9 @@ public class PolygonAperture : Aperture #endregion #region Constructor - public PolygonAperture(GerberDocument document) : base(document, "Polygon") { } + public PolygonAperture(GerberFormat document) : base(document, "Polygon") { } - public PolygonAperture(GerberDocument document, int index, double diameter, ushort vertices) : base(document, index, "Polygon") + public PolygonAperture(GerberFormat document, int index, double diameter, ushort vertices) : base(document, index, "Polygon") { Diameter = document.GetMillimeters(diameter); Vertices = vertices; diff --git a/UVtools.Core/Gerber/Apertures/RectangleAperture.cs b/UVtools.Core/Gerber/Apertures/RectangleAperture.cs index d49dde57..fc531bc1 100644 --- a/UVtools.Core/Gerber/Apertures/RectangleAperture.cs +++ b/UVtools.Core/Gerber/Apertures/RectangleAperture.cs @@ -6,11 +6,11 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; +using System; +using System.Drawing; namespace UVtools.Core.Gerber.Apertures; @@ -21,12 +21,12 @@ public class RectangleAperture : Aperture #endregion #region Constructor - public RectangleAperture(GerberDocument document) : base(document, "Rectangle") { } + public RectangleAperture(GerberFormat document) : base(document, "Rectangle") { } - public RectangleAperture(GerberDocument document, int index, float width, float height) : this(document, index, new SizeF(width, height)) + public RectangleAperture(GerberFormat document, int index, float width, float height) : this(document, index, new SizeF(width, height)) { } - public RectangleAperture(GerberDocument document, int index, SizeF size) : base(document, index, "Rectangle") + public RectangleAperture(GerberFormat document, int index, SizeF size) : base(document, index, "Rectangle") { Size = document.GetMillimeters(size); } diff --git a/UVtools.Core/Gerber/Enumerations.cs b/UVtools.Core/Gerber/Enumerations.cs index 01ce4494..674b3e4a 100644 --- a/UVtools.Core/Gerber/Enumerations.cs +++ b/UVtools.Core/Gerber/Enumerations.cs @@ -6,9 +6,6 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.ComponentModel; - namespace UVtools.Core.Gerber; public enum GerberZerosSuppressionType : byte @@ -54,22 +51,4 @@ public enum GerberMoveType : byte Arc, // G03 ArcCounterClockwise -} - -public enum GerberMidpointRounding -{ - [Description("To even: The strategy of rounding to the nearest number, and when a number is halfway between two others, it's rounded toward the nearest even number.")] - ToEven = MidpointRounding.ToEven, - - [Description("Away from zero: The strategy of rounding to the nearest number, and when a number is halfway between two others, it's rounded toward the nearest number that's away from zero.")] - AwayFromZero = MidpointRounding.AwayFromZero, - - [Description("To zero: The strategy of directed rounding toward zero, with the result closest to and no greater in magnitude than the infinitely precise result.")] - ToZero = MidpointRounding.ToZero, - - [Description("To negative inifity: The strategy of downwards-directed rounding, with the result closest to and no greater than the infinitely precise result.")] - ToNegativeInfinity = MidpointRounding.ToNegativeInfinity, - - [Description("To positive inifity: The strategy of upwards-directed rounding, with the result closest to and no less than the infinitely precise result.")] - ToPositiveInfinity = MidpointRounding.ToPositiveInfinity } \ No newline at end of file diff --git a/UVtools.Core/Gerber/GerberDocument.cs b/UVtools.Core/Gerber/GerberFormat.cs similarity index 93% rename from UVtools.Core/Gerber/GerberDocument.cs rename to UVtools.Core/Gerber/GerberFormat.cs index 32f37022..c1e86a8c 100644 --- a/UVtools.Core/Gerber/GerberDocument.cs +++ b/UVtools.Core/Gerber/GerberFormat.cs @@ -6,16 +6,16 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; +using Emgu.CV.Util; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; using System.Text.RegularExpressions; -using Emgu.CV; -using Emgu.CV.CvEnum; -using Emgu.CV.Structure; -using Emgu.CV.Util; using UVtools.Core.Extensions; using UVtools.Core.Gerber.Apertures; using UVtools.Core.Operations; @@ -25,7 +25,7 @@ namespace UVtools.Core.Gerber; /// /// https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2022-02_en.pdf?ac97011bf6bce9aaf0b1aac43d84b05f /// -public class GerberDocument +public class GerberFormat { #region Properties @@ -34,7 +34,7 @@ public class GerberDocument public GerberUnitType UnitType { get; set; } = GerberUnitType.Millimeter; public GerberPolarityType Polarity { get; set; } = GerberPolarityType.Dark; public GerberMoveType MoveType { get; set; } = GerberMoveType.Linear; - public GerberMidpointRounding SizeMidpointRounding { get; set; } = GerberMidpointRounding.AwayFromZero; + public MidpointRoundingType SizeMidpointRounding { get; set; } = MidpointRoundingType.AwayFromZero; public byte CoordinateXIntegers { get; set; } = 3; public byte CoordinateXFractionalDigits { get; set; } = 6; @@ -80,15 +80,15 @@ public class GerberDocument #endregion - public GerberDocument() + public GerberFormat() { } - public GerberDocument(string filePath) + public GerberFormat(string filePath) { } - public static void ParseAndDraw(GerberDocument document, string filePath, Mat mat, bool enableAntiAliasing = false) + public static void ParseAndDraw(GerberFormat document, string filePath, Mat mat, bool enableAntiAliasing = false) { using var file = new StreamReader(filePath); @@ -101,11 +101,9 @@ public static void ParseAndDraw(GerberDocument document, string filePath, Mat ma Aperture? currentAperture = null; var regionPoints = new List(); bool insideRegion = false; - string? line; - while ((line = file.ReadLine()) is not null) + while (file.ReadLine()?.Trim() is { } line) { - line = line.Trim(); - if (line == string.Empty) continue; + if (line.Length == 0) continue; if (line.StartsWith("M02")) break; var accumulatedLine = line; @@ -362,30 +360,38 @@ public static void ParseAndDraw(GerberDocument document, string filePath, Mat ma var matchJ = Regex.Match(line, @"J(-?\d+)"); if (!matchI.Success || !matchJ.Success || matchI.Groups.Count < 2 || matchJ.Groups.Count < 2) continue; + // xOffset + var matchValue = matchI.Groups[1].Value[0] == '-' + ? matchI.Groups[1].Value.Remove(0, 1) + : matchI.Groups[1].Value; var valueStr = document.ZerosSuppressionType switch { - GerberZerosSuppressionType.Trail => matchI.Groups[1].Value.PadRight(document.CoordinateXLength, '0'), - _ => matchI.Groups[1].Value.PadLeft(document.CoordinateXLength, '0'), + GerberZerosSuppressionType.Trail => matchValue.PadRight(document.CoordinateXLength, '0'), + _ => matchValue.PadLeft(document.CoordinateXLength, '0'), }; var integers = valueStr[..document.CoordinateXIntegers]; var fraction = valueStr[document.CoordinateXIntegers..]; - double.TryParse($"{integers}.{fraction}", NumberStyles.Float, CultureInfo.InvariantCulture, out xOffset); + double.TryParse($"{(matchI.Groups[1].Value[0] == '-' ? "-" : string.Empty)}{integers}.{fraction}", NumberStyles.Float, CultureInfo.InvariantCulture, out xOffset); xOffset = document.GetMillimeters(xOffset); + // yOffset + matchValue = matchJ.Groups[1].Value[0] == '-' + ? matchJ.Groups[1].Value.Remove(0, 1) + : matchJ.Groups[1].Value; valueStr = document.ZerosSuppressionType switch { - GerberZerosSuppressionType.Trail => matchJ.Groups[1].Value.PadRight(document.CoordinateYLength, '0'), - _ => matchJ.Groups[1].Value.PadLeft(document.CoordinateYLength, '0'), + GerberZerosSuppressionType.Trail => matchValue.PadRight(document.CoordinateYLength, '0'), + _ => matchValue.PadLeft(document.CoordinateYLength, '0'), }; integers = valueStr[..document.CoordinateYIntegers]; fraction = valueStr[document.CoordinateYIntegers..]; - double.TryParse($"{integers}.{fraction}", NumberStyles.Float, CultureInfo.InvariantCulture, out yOffset); + double.TryParse($"{(matchJ.Groups[1].Value[0] == '-' ? "-" : string.Empty)}{integers}.{fraction}", NumberStyles.Float, CultureInfo.InvariantCulture, out yOffset); yOffset = document.GetMillimeters(yOffset); @@ -455,9 +461,9 @@ public static void ParseAndDraw(GerberDocument document, string filePath, Mat ma } } - public static GerberDocument ParseAndDraw(OperationPCBExposure.PCBExposureFile file, Mat mat, SizeF xyPpmm, GerberMidpointRounding sizeMidpointRounding = GerberMidpointRounding.AwayFromZero, SizeF offset = default, bool enableAntiAliasing = false) + public static GerberFormat ParseAndDraw(OperationPCBExposure.PCBExposureFile file, Mat mat, SizeF xyPpmm, MidpointRoundingType sizeMidpointRounding = MidpointRoundingType.AwayFromZero, SizeF offset = default, bool enableAntiAliasing = false) { - var document = new GerberDocument + var document = new GerberFormat { SizeMidpointRounding = sizeMidpointRounding, XYppmm = xyPpmm, @@ -472,9 +478,9 @@ public static GerberDocument ParseAndDraw(OperationPCBExposure.PCBExposureFile f return document; } - public static GerberDocument ParseAndDraw(string filePath, Mat mat, SizeF xyPpmm, GerberMidpointRounding sizeMidpointRounding = GerberMidpointRounding.AwayFromZero, SizeF offset = default, bool enableAntiAliasing = false) + public static GerberFormat ParseAndDraw(string filePath, Mat mat, SizeF xyPpmm, MidpointRoundingType sizeMidpointRounding = MidpointRoundingType.AwayFromZero, SizeF offset = default, bool enableAntiAliasing = false) { - var document = new GerberDocument + var document = new GerberFormat { SizeMidpointRounding = sizeMidpointRounding, XYppmm = xyPpmm, diff --git a/UVtools.Core/Gerber/Macro.cs b/UVtools.Core/Gerber/Macro.cs index 06589587..8b9f8783 100644 --- a/UVtools.Core/Gerber/Macro.cs +++ b/UVtools.Core/Gerber/Macro.cs @@ -18,7 +18,7 @@ public class Macro : IReadOnlyList { #region Properties - public GerberDocument Document { get; init; } + public GerberFormat Document { get; init; } /// /// Gets the macro name @@ -28,12 +28,12 @@ public class Macro : IReadOnlyList public List Primitives { get; } = new(); #endregion - public Macro(GerberDocument document) + public Macro(GerberFormat document) { Document = document; } - public Macro(GerberDocument document, string name) : this(document) + public Macro(GerberFormat document, string name) : this(document) { Name = name; } @@ -108,7 +108,7 @@ public Macro Clone() } - public static Macro? Parse(GerberDocument document, string line) + public static Macro? Parse(GerberFormat document, string line) { var match = Regex.Match(line, @"%?AM([a-zA-Z\d]+)\*?"); if (!match.Success || match.Groups.Count < 2) return null; diff --git a/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs b/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs index 2debcb98..868d8640 100644 --- a/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs @@ -6,13 +6,13 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; using System; using System.Data; using System.Drawing; using System.Globalization; using System.Text.RegularExpressions; -using Emgu.CV; -using Emgu.CV.CvEnum; using UVtools.Core.Extensions; namespace UVtools.Core.Gerber.Primitives; @@ -75,9 +75,9 @@ public class CenterLinePrimitive : Primitive public float Rotation { get; set; } = 0; #endregion - protected CenterLinePrimitive(GerberDocument document) : base(document) { } + protected CenterLinePrimitive(GerberFormat document) : base(document) { } - public CenterLinePrimitive(GerberDocument document, string exposureExpression, string widthExpression = "0", string heightExpression = "0", string centerXExpression = "0", string centerYExpression = "0", string rotationExpression = "0") : base(document) + public CenterLinePrimitive(GerberFormat document, string exposureExpression, string widthExpression = "0", string heightExpression = "0", string centerXExpression = "0", string centerYExpression = "0", string rotationExpression = "0") : base(document) { ExposureExpression = exposureExpression; WidthExpression = widthExpression; diff --git a/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs b/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs index aff6a509..8609c3c8 100644 --- a/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs @@ -6,13 +6,13 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; using System; using System.Data; using System.Drawing; using System.Globalization; using System.Text.RegularExpressions; -using Emgu.CV; -using Emgu.CV.CvEnum; namespace UVtools.Core.Gerber.Primitives; @@ -67,9 +67,9 @@ public class CirclePrimitive : Primitive public float Rotation { get; set; } = 0; #endregion - protected CirclePrimitive(GerberDocument document) : base(document) { } + protected CirclePrimitive(GerberFormat document) : base(document) { } - public CirclePrimitive(GerberDocument document, string exposureExpression = "1", string diameterExpression = "0", string centerXExpression = "0", string centerYExpression = "0", string rotationExpression = "0") : base(document) + public CirclePrimitive(GerberFormat document, string exposureExpression = "1", string diameterExpression = "0", string centerXExpression = "0", string centerYExpression = "0", string rotationExpression = "0") : base(document) { ExposureExpression = exposureExpression; DiameterExpression = diameterExpression; diff --git a/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs b/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs index 35cdc8a8..22e7818b 100644 --- a/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs @@ -6,9 +6,9 @@ * of this license document, but changing it is not allowed. */ -using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; +using System.Drawing; namespace UVtools.Core.Gerber.Primitives; @@ -33,12 +33,12 @@ public class CommentPrimitive : Primitive public string Comment { get; set; } = string.Empty; #endregion - public CommentPrimitive(GerberDocument document) : base(document) + public CommentPrimitive(GerberFormat document) : base(document) { IsParsed = true; } - public CommentPrimitive(GerberDocument document, string comment) : base(document) + public CommentPrimitive(GerberFormat document, string comment) : base(document) { Comment = comment; IsParsed = true; diff --git a/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs b/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs index 21af733a..f25ad25f 100644 --- a/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs @@ -6,6 +6,9 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Util; using System; using System.Collections.Generic; using System.Data; @@ -13,9 +16,7 @@ using System.Globalization; using System.Linq; using System.Text.RegularExpressions; -using Emgu.CV; -using Emgu.CV.CvEnum; -using Emgu.CV.Util; +using UVtools.Core.Extensions; namespace UVtools.Core.Gerber.Primitives; @@ -65,9 +66,9 @@ public class OutlinePrimitive : Primitive public float Rotation { get; set; } = 0; #endregion - protected OutlinePrimitive(GerberDocument document) : base(document) { } + protected OutlinePrimitive(GerberFormat document) : base(document) { } - public OutlinePrimitive(GerberDocument document, string exposureExpression, string[] coordinatesExpression, string rotationExpression) : base(document) + public OutlinePrimitive(GerberFormat document, string exposureExpression, string[] coordinatesExpression, string rotationExpression) : base(document) { ExposureExpression = exposureExpression; CoordinatesExpression = coordinatesExpression; @@ -81,13 +82,14 @@ public override void DrawFlashD3(Mat mat, PointF at, LineType lineType = LineTyp if (Rotation != 0) { - throw new NotImplementedException($"{Name} primitive with code {Code} have a rotation value of {Rotation} which is not implemented. Open a issue regarding this problem and provide a sample file to be able to implement rotation correctly on this primitive."); + //throw new NotImplementedException($"{Name} primitive with code {Code} have a rotation value of {Rotation} which is not implemented. Open a issue regarding this problem and provide a sample file to be able to implement rotation correctly on this primitive."); } var points = new List(); for (int i = 0; i < Coordinates.Length-1; i++) { - var pt = Document.PositionMmToPx(at.X + Coordinates[i].X, at.Y + Coordinates[i].Y); + var point = new PointF(at.X + Coordinates[i].X, at.Y + Coordinates[i].Y).Rotate(-Rotation, at); + var pt = Document.PositionMmToPx(point); if(points.Count > 0 && points[^1] == pt) continue; // Prevent series of duplicates points.Add(pt); } diff --git a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs index 27d9ac64..c7df0be0 100644 --- a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs @@ -6,13 +6,13 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; using System; using System.Data; using System.Drawing; using System.Globalization; using System.Text.RegularExpressions; -using Emgu.CV; -using Emgu.CV.CvEnum; using UVtools.Core.Extensions; namespace UVtools.Core.Gerber.Primitives; @@ -73,9 +73,9 @@ public class PolygonPrimitive : Primitive public float Rotation { get; set; } = 0; #endregion - protected PolygonPrimitive(GerberDocument document) : base(document) { } + protected PolygonPrimitive(GerberFormat document) : base(document) { } - public PolygonPrimitive(GerberDocument document, string exposureExpression, string verticesCountExpression, string centerXExpression = "0", string centerYExpression = "0", string diameterExpression = "0", string rotationExpression = "0") : base(document) + public PolygonPrimitive(GerberFormat document, string exposureExpression, string verticesCountExpression, string centerXExpression = "0", string centerYExpression = "0", string diameterExpression = "0", string rotationExpression = "0") : base(document) { ExposureExpression = exposureExpression; VerticesCountExpression = verticesCountExpression; diff --git a/UVtools.Core/Gerber/Primitives/Primitive.cs b/UVtools.Core/Gerber/Primitives/Primitive.cs index bedb066b..56355a6a 100644 --- a/UVtools.Core/Gerber/Primitives/Primitive.cs +++ b/UVtools.Core/Gerber/Primitives/Primitive.cs @@ -6,9 +6,9 @@ * of this license document, but changing it is not allowed. */ -using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; +using System.Drawing; namespace UVtools.Core.Gerber.Primitives; @@ -19,13 +19,13 @@ public abstract class Primitive public bool IsParsed { get; protected set; } = false; - public GerberDocument Document { get; init; } + public GerberFormat Document { get; init; } #endregion //protected Primitive() { } - protected Primitive(GerberDocument document) + protected Primitive(GerberFormat document) { Document = document; } diff --git a/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs b/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs index 7b608a5d..12f33a09 100644 --- a/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs @@ -6,13 +6,13 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; using System; using System.Data; using System.Drawing; using System.Globalization; using System.Text.RegularExpressions; -using Emgu.CV; -using Emgu.CV.CvEnum; using UVtools.Core.Extensions; namespace UVtools.Core.Gerber.Primitives; @@ -84,9 +84,9 @@ public class VectorLinePrimitive : Primitive public float Rotation { get; set; } = 0; #endregion - protected VectorLinePrimitive(GerberDocument document) : base(document) { } + protected VectorLinePrimitive(GerberFormat document) : base(document) { } - public VectorLinePrimitive(GerberDocument document, string exposureExpression, string lineWidthExpression, string startXExpression, string startYExpression, string endXExpression, string endYExpression, string rotationExpression = "0") : base(document) + public VectorLinePrimitive(GerberFormat document, string exposureExpression, string lineWidthExpression, string startXExpression, string startYExpression, string endXExpression, string endYExpression, string rotationExpression = "0") : base(document) { ExposureExpression = exposureExpression; LineWidthExpression = lineWidthExpression; diff --git a/UVtools.Core/Helpers.cs b/UVtools.Core/Helpers.cs index fb6e8ac2..50facf7a 100644 --- a/UVtools.Core/Helpers.cs +++ b/UVtools.Core/Helpers.cs @@ -8,10 +8,7 @@ using BinarySerialization; using System; -using System.Globalization; using System.IO; -using System.Reflection; -using System.Text.Json; using UVtools.Core.Extensions; namespace UVtools.Core; diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs index 7bbd958f..3c8cd1b9 100644 --- a/UVtools.Core/Layers/Layer.cs +++ b/UVtools.Core/Layers/Layer.cs @@ -8,6 +8,7 @@ using Emgu.CV; using Emgu.CV.CvEnum; +using K4os.Compression.LZ4; using System; using System.Collections.Generic; using System.ComponentModel; @@ -17,7 +18,6 @@ using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; -using K4os.Compression.LZ4; using UVtools.Core.EmguCV; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; @@ -174,6 +174,11 @@ public double NonZeroPixelPercentage /// public bool IsEmpty => _nonZeroPixelCount == 0; + /// + /// Gets if this layer is a dummy layer to bypass a firmware constrain, that is contain at most one pixel and exposure time no more than 0.01s + /// + public bool IsDummy => _nonZeroPixelCount <= 1 || _exposureTime <= 0.01; + /// /// Gets the layer area (XY) in mm^2 /// Pixel size * number of pixels @@ -710,6 +715,11 @@ public float MaximumSpeed /// public bool CanExpose => _exposureTime > 0 && _lightPWM > 0; + /// + /// Gets if this layer should be exposed to UV light, ie: if layer is empty or no exposure time then it useless to expose it + /// + public bool ShouldExpose => !IsEmpty && CanExpose; + /// /// Gets the layer height in millimeters of this layer /// @@ -1032,28 +1042,33 @@ public bool IsUsingGlobalParameters public bool IsUsingTSMC => LiftHeight2 > 0 || RetractHeight2 > 0; /// - /// Gets tree contours cache for this layer, however should call instead with instance + /// Gets tree contours cache for this layer, however should call instead with instance. + /// If not set it will calculate contours first /// public EmguContours Contours { get { - if (_contours is null) - { - using var mat = LayerMat; - _contours = new EmguContours(mat, RetrType.Tree); - } - - return _contours; + if (_contours is not null) return _contours; + using var matRoi = LayerMatBoundingRectangle; + return GetContours(matRoi); } } /// - /// Gets tree contours cache for this layer + /// Gets tree contours cache for this layer. If not set it will calculate contours first + /// + public EmguContours GetContours(IInputOutputArray mat, Point offset = default) + { + return _contours ??= new EmguContours(mat, RetrType.Tree, ChainApproxMethod.ChainApproxSimple, offset); + } + + /// + /// Gets tree contours cache for this layer. If not set it will calculate contours first /// - public EmguContours GetContours(IInputOutputArray mat) + public EmguContours GetContours(MatRoi matRoi) { - return _contours ??= new EmguContours(mat, RetrType.Tree); + return _contours ??= new EmguContours(matRoi.RoiMat, RetrType.Tree, ChainApproxMethod.ChainApproxSimple, matRoi.Roi.Location); } #endregion diff --git a/UVtools.Core/Managers/IssueManager.cs b/UVtools.Core/Managers/IssueManager.cs index 68467a2d..6b8e7f7a 100644 --- a/UVtools.Core/Managers/IssueManager.cs +++ b/UVtools.Core/Managers/IssueManager.cs @@ -1,4 +1,5 @@ -using Emgu.CV; +using CommunityToolkit.HighPerformance; +using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Util; using System; @@ -8,7 +9,6 @@ using System.Drawing; using System.Linq; using System.Threading.Tasks; -using CommunityToolkit.HighPerformance; using UVtools.Core.EmguCV; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; @@ -181,8 +181,7 @@ void GenerateAirMap(IInputArray input, IInputOutputArray output, VectorOfVectorO if (!layer.IsEmpty) continue; if (!emptyLayerConfig.IgnoreStartingEmptyLayers - && !emptyLayerConfig.IgnoreLooseEmptyLayers - && !emptyLayerConfig.IgnoreEndingEmptyLayers) + && emptyLayerConfig is {IgnoreLooseEmptyLayers: false, IgnoreEndingEmptyLayers: false}) { AddIssue(new MainIssue(MainIssue.IssueType.EmptyLayer, new Issue(layer))); continue; diff --git a/UVtools.Core/Managers/KernelCacheManager.cs b/UVtools.Core/Managers/KernelCacheManager.cs index 340c45c4..385eed82 100644 --- a/UVtools.Core/Managers/KernelCacheManager.cs +++ b/UVtools.Core/Managers/KernelCacheManager.cs @@ -7,9 +7,9 @@ */ using Emgu.CV; +using Emgu.CV.CvEnum; using System; using System.Collections.Concurrent; -using Emgu.CV.CvEnum; using UVtools.Core.Extensions; namespace UVtools.Core.Managers; diff --git a/UVtools.Core/Managers/MessageBoxManager.cs b/UVtools.Core/Managers/MessageBoxManager.cs new file mode 100644 index 00000000..bb88d535 --- /dev/null +++ b/UVtools.Core/Managers/MessageBoxManager.cs @@ -0,0 +1,20 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using UVtools.Core.Dialogs; + +namespace UVtools.Core.Managers; + +public static class MessageBoxManager +{ + /// + /// Gets the standard message box for this session which will trigger a message with pre-defined buttons. + /// If using console it will prompt there as text, otherwise if in UI will use dialogs. + /// + public static AbstractMessageBoxStandard Standard { get; set; } = ConsoleMessageBoxStandard.Instance; +} \ No newline at end of file diff --git a/UVtools.Core/Objects/GenericFileRepresentation.cs b/UVtools.Core/Objects/GenericFileRepresentation.cs index 2d3825ef..74c0c77b 100644 --- a/UVtools.Core/Objects/GenericFileRepresentation.cs +++ b/UVtools.Core/Objects/GenericFileRepresentation.cs @@ -108,7 +108,7 @@ public int CompareTo(GenericFileRepresentation? other) public int CompareTo(string? other) { - return FileName.CompareTo(other); + return string.Compare(FileName, other, StringComparison.Ordinal); } public bool Equals(GenericFileRepresentation? other) diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs index 7daab2c1..06230f61 100644 --- a/UVtools.Core/Operations/Operation.cs +++ b/UVtools.Core/Operations/Operation.cs @@ -331,6 +331,12 @@ public Point[][]? MaskPoints /// [XmlIgnore] public bool IsValidated { get; private set; } + + /// + /// Gets or sets an report to show to the user after complete the operation with success + /// + [XmlIgnore] + public string? AfterCompleteReport { get; set; } #endregion #region Constructor @@ -720,6 +726,9 @@ public bool Execute(OperationProgress? progress = null) { if (_slicerFile is null) throw new InvalidOperationException($"{Title} can't execute due the lacking of a file parent."); if (_slicerFile.DecodeType == FileFormat.FileDecodeType.Partial && !CanRunInPartialMode) throw new InvalidOperationException($"The file was open in partial mode and the tool \"{Title}\" is unable to run in this mode.\nPlease reload the file in full mode in order to use this tool."); + + AfterCompleteReport = null; + if (!IsValidated) { var msg = Validate(); diff --git a/UVtools.Core/Operations/OperationCalibrateBloomingEffect.cs b/UVtools.Core/Operations/OperationCalibrateBloomingEffect.cs index a4394808..d6c0dbf0 100644 --- a/UVtools.Core/Operations/OperationCalibrateBloomingEffect.cs +++ b/UVtools.Core/Operations/OperationCalibrateBloomingEffect.cs @@ -68,7 +68,7 @@ public sealed class OperationCalibrateBloomingEffect : Operation return $"{NotSupportedMessage}\nReason: Can't use more than one layer per same position."; } - if (!SlicerFile.CanUseLayerWaitTimeBeforeCure && !SlicerFile.CanUseLayerLightOffDelay) + if (SlicerFile is {CanUseLayerWaitTimeBeforeCure: false, CanUseLayerLightOffDelay: false}) { return $"{NotSupportedMessage}\nReason: No per layer wait time before cure capabilities."; } diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs index ee0cddb8..929842ae 100644 --- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs +++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs @@ -90,7 +90,7 @@ public sealed class OperationCalibrateElephantFoot : Operation sb.AppendLine("Wall dimming start brightness can't be higher than end brightness."); } - if (SlicerFile.IsAntiAliasingEmulated && SlicerFile.AntiAliasing < 2) + if (SlicerFile is {IsAntiAliasingEmulated: true, AntiAliasing: < 2}) { sb.AppendLine($"With a emulated anti-aliasing of {SlicerFile.AntiAliasing}x, is not possible to run the dimming method, use the erode instead."); sb.AppendLine("As alternative, re-slice the file with a AntiAliasing level greater than 1 and run this tool again."); diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs index 073a2b40..c5d4f946 100644 --- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -2046,7 +2046,6 @@ void AddLayer(decimal currentHeight, decimal layerHeight, decimal bottomExposure bool isBottomLayer = layerCountOnHeight <= _bottomLayers; bool isBaseLayer = currentHeight <= _baseHeight; ushort microns = (ushort)(layerHeight * 1000); - Point position; bool addSomething = false; bool reUseLastLayer = @@ -2069,6 +2068,7 @@ lastExposureItem is not null && ExposureItem key = new(layerHeight, bottomExposure, normalExposure, brightness); lastExposureItem = key; + Point position; if (table.TryGetValue(key, out var pos)) { position = pos; diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs index e65d1b5a..c6f17c67 100644 --- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs +++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs @@ -354,7 +354,7 @@ public Mat[] GetLayers() { spiralAngle = -spiralAngle; } - Point location = new((int) (center.X - baseRadius + spiralRadius), center.Y); + Point location = center with {X = (int) (center.X - baseRadius + spiralRadius)}; var locationCW = location.Rotate((double) spiralAngle, center); var locationCCW = location.Rotate((double) -spiralAngle, center); diff --git a/UVtools.Core/Operations/OperationDoubleExposure.cs b/UVtools.Core/Operations/OperationDoubleExposure.cs index ac75e16c..957e4520 100644 --- a/UVtools.Core/Operations/OperationDoubleExposure.cs +++ b/UVtools.Core/Operations/OperationDoubleExposure.cs @@ -9,7 +9,6 @@ using Emgu.CV; using Emgu.CV.CvEnum; using System; -using System.Drawing; using System.Text; using System.Threading.Tasks; using UVtools.Core.Extensions; diff --git a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs index cee2d0fe..1f60dc5e 100644 --- a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs +++ b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs @@ -29,50 +29,19 @@ public sealed class OperationDynamicLayerHeight : Operation #region Sub Classes public sealed class Report { - private uint _oldLayerCount; - private uint _newLayerCount; - private uint _stackedLayers; - private float _maximumLayerHeight1; - private float _oldPrintTime; - private float _newPrintTime; + public uint OldLayerCount { get; set; } - public uint OldLayerCount - { - get => _oldLayerCount; - set => _oldLayerCount = value; - } + public uint NewLayerCount { get; set; } - public uint NewLayerCount - { - get => _newLayerCount; - set => _newLayerCount = value; - } - - public uint StackedLayers - { - get => _stackedLayers; - set => _stackedLayers = value; - } + public uint StackedLayers { get; set; } public uint ReusedLayers => OldLayerCount - StackedLayers; - public float MaximumLayerHeight - { - get => _maximumLayerHeight1; - set => _maximumLayerHeight1 = value; - } + public float MaximumLayerHeight { get; set; } - public float OldPrintTime - { - get => _oldPrintTime; - set => _oldPrintTime = value; - } + public float OldPrintTime { get; set; } - public float NewPrintTime - { - get => _newPrintTime; - set => _newPrintTime = value; - } + public float NewPrintTime { get; set; } public double SparedPrintTime => Math.Round(OldPrintTime - NewPrintTime, 2); @@ -827,7 +796,7 @@ void ReUseLayer(uint layerIndex) report.NewLayerCount = SlicerFile.LayerCount; report.NewPrintTime = SlicerFile.PrintTime; - Tag = report; + AfterCompleteReport = report.ToString(); return true; } diff --git a/UVtools.Core/Operations/OperationDynamicLifts.cs b/UVtools.Core/Operations/OperationDynamicLifts.cs index dcc815ba..c444a735 100644 --- a/UVtools.Core/Operations/OperationDynamicLifts.cs +++ b/UVtools.Core/Operations/OperationDynamicLifts.cs @@ -306,13 +306,13 @@ protected override bool ExecuteInternally(OperationProgress progress) switch (_setMethod) { case DynamicLiftsSetMethod.Traditional: - liftHeight = (_largestBottomLiftHeight * calculateLayer.NonZeroPixelCount / maxBottomPixels).Clamp(_smallestBottomLiftHeight, _largestBottomLiftHeight); - liftSpeed = (_fastestBottomLiftSpeed - (_fastestBottomLiftSpeed * calculateLayer.NonZeroPixelCount / maxBottomPixels)).Clamp(_slowestBottomLiftSpeed, _fastestBottomLiftSpeed); + liftHeight = Math.Clamp(_largestBottomLiftHeight * calculateLayer.NonZeroPixelCount / maxBottomPixels, _smallestBottomLiftHeight, _largestBottomLiftHeight); + liftSpeed = Math.Clamp(_fastestBottomLiftSpeed - (_fastestBottomLiftSpeed * calculateLayer.NonZeroPixelCount / maxBottomPixels), _slowestBottomLiftSpeed, _fastestBottomLiftSpeed); break; case DynamicLiftsSetMethod.FullRange: var pixelRatio = (calculateLayer.NonZeroPixelCount - minBottomPixels) / (float)(maxBottomPixels - minBottomPixels); // pixel_ratio is between 0 and 1 - liftHeight = (_smallestBottomLiftHeight + ((_largestBottomLiftHeight - _smallestBottomLiftHeight) * pixelRatio)).Clamp(_smallestBottomLiftHeight, _largestBottomLiftHeight); - liftSpeed = (_fastestBottomLiftSpeed - ((_fastestBottomLiftSpeed - _slowestBottomLiftSpeed) * pixelRatio)).Clamp(_slowestBottomLiftSpeed, _fastestBottomLiftSpeed); + liftHeight = Math.Clamp(_smallestBottomLiftHeight + (_largestBottomLiftHeight - _smallestBottomLiftHeight) * pixelRatio, _smallestBottomLiftHeight, _largestBottomLiftHeight); + liftSpeed = Math.Clamp(_fastestBottomLiftSpeed - (_fastestBottomLiftSpeed - _slowestBottomLiftSpeed) * pixelRatio, _slowestBottomLiftSpeed, _fastestBottomLiftSpeed); break; default: throw new NotImplementedException(nameof(SetMethod)); @@ -324,13 +324,13 @@ protected override bool ExecuteInternally(OperationProgress progress) switch (_setMethod) { case DynamicLiftsSetMethod.Traditional: - liftHeight = (_largestLiftHeight * calculateLayer.NonZeroPixelCount / maxNormalPixels).Clamp(_smallestLiftHeight, _largestLiftHeight); - liftSpeed = (_fastestLiftSpeed - (_fastestLiftSpeed * calculateLayer.NonZeroPixelCount / maxNormalPixels)).Clamp(_slowestLiftSpeed, _fastestLiftSpeed); + liftHeight = Math.Clamp(_largestLiftHeight * calculateLayer.NonZeroPixelCount / maxNormalPixels, _smallestLiftHeight, _largestLiftHeight); + liftSpeed = Math.Clamp(_fastestLiftSpeed - _fastestLiftSpeed * calculateLayer.NonZeroPixelCount / maxNormalPixels, _slowestLiftSpeed, _fastestLiftSpeed); break; case DynamicLiftsSetMethod.FullRange: var pixelRatio = (calculateLayer.NonZeroPixelCount - minNormalPixels) / (float)(maxNormalPixels - minNormalPixels); // pixel_ratio is between 0 and 1 - liftHeight = (_smallestLiftHeight + ((_largestLiftHeight - _smallestLiftHeight) * pixelRatio)).Clamp(_smallestLiftHeight, _largestLiftHeight); - liftSpeed = (_fastestLiftSpeed - ((_fastestLiftSpeed - _slowestLiftSpeed) * pixelRatio)).Clamp(_slowestLiftSpeed, _fastestLiftSpeed); + liftHeight = Math.Clamp(_smallestLiftHeight + (_largestLiftHeight - _smallestLiftHeight) * pixelRatio, _smallestLiftHeight, _largestLiftHeight); + liftSpeed = Math.Clamp(_fastestLiftSpeed - (_fastestLiftSpeed - _slowestLiftSpeed) * pixelRatio, _slowestLiftSpeed, _fastestLiftSpeed); break; default: throw new NotImplementedException(nameof(SetMethod)); diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs index d54b279d..d4a8a069 100644 --- a/UVtools.Core/Operations/OperationFlip.cs +++ b/UVtools.Core/Operations/OperationFlip.cs @@ -8,7 +8,6 @@ using Emgu.CV; using Emgu.CV.CvEnum; -using System; using System.Threading.Tasks; using UVtools.Core.FileFormats; diff --git a/UVtools.Core/Operations/OperationLayerArithmetic.cs b/UVtools.Core/Operations/OperationLayerArithmetic.cs index 925d784c..ffdbfe99 100644 --- a/UVtools.Core/Operations/OperationLayerArithmetic.cs +++ b/UVtools.Core/Operations/OperationLayerArithmetic.cs @@ -123,7 +123,7 @@ public string Sentence set => RaiseAndSetIfChanged(ref _sentence, value); } [XmlIgnore] - public List Operations { get; private set; } = new(); + public List Operations { get; } = new(); public bool IsValid => Operations.Count > 0; #endregion diff --git a/UVtools.Core/Operations/OperationLayerClone.cs b/UVtools.Core/Operations/OperationLayerClone.cs index 24806567..1386d38c 100644 --- a/UVtools.Core/Operations/OperationLayerClone.cs +++ b/UVtools.Core/Operations/OperationLayerClone.cs @@ -10,7 +10,6 @@ using System.Linq; using System.Text; using UVtools.Core.FileFormats; -using UVtools.Core.Layers; namespace UVtools.Core.Operations; diff --git a/UVtools.Core/Operations/OperationLayerExportHtml.cs b/UVtools.Core/Operations/OperationLayerExportHtml.cs index 17d4f737..9686ece7 100644 --- a/UVtools.Core/Operations/OperationLayerExportHtml.cs +++ b/UVtools.Core/Operations/OperationLayerExportHtml.cs @@ -6,14 +6,14 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; using System; using System.Collections; using System.IO; using System.Reflection; using System.Text; using System.Threading.Tasks; -using Emgu.CV; -using Emgu.CV.CvEnum; using UVtools.Core.EmguCV; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; @@ -260,7 +260,7 @@ protected override bool ExecuteInternally(OperationProgress progress) html.WriteLine($" {SlicerFile.Resolution} px"); html.WriteLine(" "); - if (SlicerFile.DisplayWidth > 0 && SlicerFile.DisplayHeight > 0) + if (SlicerFile is {DisplayWidth: > 0, DisplayHeight: > 0}) { html.WriteLine(" "); html.WriteLine($" Display size"); diff --git a/UVtools.Core/Operations/OperationLayerExportImage.cs b/UVtools.Core/Operations/OperationLayerExportImage.cs index 18d805ee..3685873c 100644 --- a/UVtools.Core/Operations/OperationLayerExportImage.cs +++ b/UVtools.Core/Operations/OperationLayerExportImage.cs @@ -12,7 +12,6 @@ using System.ComponentModel; using System.IO; using System.Threading.Tasks; -using UVtools.Core.EmguCV; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs index 439f34d5..dc5a65cf 100644 --- a/UVtools.Core/Operations/OperationLayerImport.cs +++ b/UVtools.Core/Operations/OperationLayerImport.cs @@ -348,8 +348,7 @@ protected override bool ExecuteInternally(OperationProgress progress) if (y + fileFormatBoundingRectangle.Height >= SlicerFile.ResolutionY) continue; - roiRectangle = new Rectangle(x, y, fileFormatBoundingRectangle.Width, - fileFormatBoundingRectangle.Height); + roiRectangle = fileFormatBoundingRectangle with {X = x, Y = y}; } if (_extendBeyondLayerCount) diff --git a/UVtools.Core/Operations/OperationLayerRemove.cs b/UVtools.Core/Operations/OperationLayerRemove.cs index 1fa4145b..cf4aba9a 100644 --- a/UVtools.Core/Operations/OperationLayerRemove.cs +++ b/UVtools.Core/Operations/OperationLayerRemove.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Text; using UVtools.Core.FileFormats; -using UVtools.Core.Layers; namespace UVtools.Core.Operations; diff --git a/UVtools.Core/Operations/OperationLithophane.cs b/UVtools.Core/Operations/OperationLithophane.cs index f282ce1b..dd11dda4 100644 --- a/UVtools.Core/Operations/OperationLithophane.cs +++ b/UVtools.Core/Operations/OperationLithophane.cs @@ -6,6 +6,9 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; using System; using System.Collections.Concurrent; using System.Drawing; @@ -13,9 +16,6 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Emgu.CV; -using Emgu.CV.CvEnum; -using Emgu.CV.Structure; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Layers; diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs index 29a0d82c..6991af5d 100644 --- a/UVtools.Core/Operations/OperationMask.cs +++ b/UVtools.Core/Operations/OperationMask.cs @@ -7,13 +7,11 @@ */ using Emgu.CV; -using System; +using Emgu.CV.CvEnum; using System.Drawing; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; -using Emgu.CV.CvEnum; -using Emgu.CV.DepthAI; using UVtools.Core.FileFormats; namespace UVtools.Core.Operations; @@ -78,7 +76,7 @@ public OperationMask(FileFormat slicerFile) : base(slicerFile) { } public void LoadFromFile(string filePath, bool invertMask = false, Size maskSize = default) { Mask = CvInvoke.Imread(filePath, ImreadModes.Grayscale); - if (maskSize.Width > 0 && maskSize.Height > 0 && Mask.Size != maskSize) + if (maskSize is {Width: > 0, Height: > 0} && Mask.Size != maskSize) { CvInvoke.Resize(Mask, Mask, maskSize); } diff --git a/UVtools.Core/Operations/OperationPCBExposure.cs b/UVtools.Core/Operations/OperationPCBExposure.cs index 25fcdb62..7a3236e5 100644 --- a/UVtools.Core/Operations/OperationPCBExposure.cs +++ b/UVtools.Core/Operations/OperationPCBExposure.cs @@ -6,6 +6,8 @@ * of this license document, but changing it is not allowed. */ +using Emgu.CV; +using Emgu.CV.CvEnum; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -14,8 +16,7 @@ using System.IO.Compression; using System.Linq; using System.Text; -using Emgu.CV; -using Emgu.CV.CvEnum; +using UVtools.Core.Excellon; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Gerber; @@ -75,6 +76,7 @@ public PCBExposureFile(string filePath, bool invertPolarity = false) : base(file "gbo", // Bottom silkscreen layer "gbs", // Bottom solder mask layer "gml", // Mechanical layer + "drl" // Drill holes }; #endregion @@ -84,7 +86,7 @@ public PCBExposureFile(string filePath, bool invertPolarity = false) : base(file private bool _mergeFiles; private decimal _layerHeight; private decimal _exposureTime; - private GerberMidpointRounding _sizeMidpointRounding = GerberMidpointRounding.AwayFromZero; + private MidpointRoundingType _sizeMidpointRounding = MidpointRoundingType.AwayFromZero; private decimal _offsetX; private decimal _offsetY; private bool _mirror; @@ -187,7 +189,7 @@ public decimal ExposureTime set => RaiseAndSetIfChanged(ref _exposureTime, Math.Round(Math.Max(0, value), 2)); } - public GerberMidpointRounding SizeMidpointRounding + public MidpointRoundingType SizeMidpointRounding { get => _sizeMidpointRounding; set => RaiseAndSetIfChanged(ref _sizeMidpointRounding, value); @@ -291,6 +293,14 @@ public void AddFile(string filePath, bool handleZipFiles = true) var file = new PCBExposureFile(filePath); if (_files.Contains(file)) return; _files.Add(file); + + /*var drlFiles = _files.Where(exposureFile => exposureFile.FileExtension == ".drl"); + var drlArray = drlFiles.ToArray(); + if (drlArray.Length > 0) + { + _files.RemoveRange(drlArray); + _files.AddRange(drlArray); + }*/ } public void AddFiles(string[] files, bool handleZipFiles = true) @@ -314,8 +324,15 @@ public void DrawMat(PCBExposureFile file, Mat mat) { if (!file.Exists) return; - GerberDocument.ParseAndDraw(file, mat, SlicerFile.Ppmm, _sizeMidpointRounding, new SizeF((float)OffsetX, (float)OffsetY), _enableAntiAliasing); - + if (file.IsExtension("drl")) + { + ExcellonDrillFormat.ParseAndDraw(file, mat, SlicerFile.Ppmm, _sizeMidpointRounding, new SizeF((float)OffsetX, (float)OffsetY), _enableAntiAliasing); + } + else + { + GerberFormat.ParseAndDraw(file, mat, SlicerFile.Ppmm, _sizeMidpointRounding, new SizeF((float)OffsetX, (float)OffsetY), _enableAntiAliasing); + } + //var boundingRectangle = CvInvoke.BoundingRectangle(mat); //var cropped = mat.Roi(new Size(boundingRectangle.Right, boundingRectangle.Bottom)); var cropped = mat.CropByBounds(); @@ -337,7 +354,10 @@ protected override bool ExecuteInternally(OperationProgress progress) var layers = new List(); using var mergeMat = SlicerFile.CreateMat(); progress.ItemCount = FileCount; - for (var i = 0; i < _files.Count; i++) + + var orderFiles = _files.OrderBy(file => file.IsExtension(".drl")).ToArray(); + + for (var i = 0; i < orderFiles.Length; i++) { /*using var mat = GetMat(file); @@ -350,7 +370,7 @@ protected override bool ExecuteInternally(OperationProgress progress) CvInvoke.Max(mergeMat, mat, mergeMat); }*/ - DrawMat(_files[i], mergeMat); + DrawMat(orderFiles[i], mergeMat); if (!_mergeFiles) { @@ -364,7 +384,7 @@ protected override bool ExecuteInternally(OperationProgress progress) else { using var mat = SlicerFile.CreateMat(); - DrawMat(_files[i], mat); + DrawMat(orderFiles[i], mat); if (CvInvoke.CountNonZero(mat) > 0) { layers.Add(new Layer(mat, SlicerFile)); @@ -400,7 +420,7 @@ protected override bool ExecuteInternally(OperationProgress progress) SlicerFile.Layers = layers.ToArray(); }, true); - + if (_mirror) // Reposition layers { using var op = new OperationMove(SlicerFile, Anchor.TopLeft) diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs index 8b980018..028aa5cd 100644 --- a/UVtools.Core/Operations/OperationPattern.cs +++ b/UVtools.Core/Operations/OperationPattern.cs @@ -7,7 +7,6 @@ */ using Emgu.CV; -using System; using System.Drawing; using System.Text; using System.Threading.Tasks; diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs index 6679bb70..f5605609 100644 --- a/UVtools.Core/Operations/OperationPixelArithmetic.cs +++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs @@ -1258,7 +1258,7 @@ public void GenerateInfill(string pattern) PatternText = p1.Trim('\n', '\r'); - PatternTextAlternate = p2.Trim('\n', '\r'); ; + PatternTextAlternate = p2.Trim('\n', '\r'); return; } } diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs index b0346fb9..3728b30a 100644 --- a/UVtools.Core/Operations/OperationPixelDimming.cs +++ b/UVtools.Core/Operations/OperationPixelDimming.cs @@ -603,7 +603,7 @@ public void GenerateInfill(string pattern) PatternText = p1.Trim('\n', '\r'); - AlternatePatternText = p2.Trim('\n', '\r'); ; + AlternatePatternText = p2.Trim('\n', '\r'); return; } } diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs index 4958f4a4..94493ff3 100644 --- a/UVtools.Core/Operations/OperationProgress.cs +++ b/UVtools.Core/Operations/OperationProgress.cs @@ -8,7 +8,6 @@ using System; using System.Diagnostics; using System.Threading; -using UVtools.Core.Extensions; using UVtools.Core.Objects; namespace UVtools.Core.Operations; @@ -45,7 +44,8 @@ public sealed class OperationProgress : BindableBase private string _itemName = "Initializing"; private uint _processedItems; private uint _itemCount; - + private string _log = string.Empty; + public OperationProgress() { @@ -72,7 +72,7 @@ public bool CanCancel get { if (!_canCancel) return _canCancel; - return !Token.IsCancellationRequested && Token.CanBeCanceled && _canCancel; + return Token is {IsCancellationRequested: false, CanBeCanceled: true} && _canCancel; } set => RaiseAndSetIfChanged(ref _canCancel, value); } @@ -132,6 +132,15 @@ public uint ItemCount } } + /// + /// Detailed log to show below the progress bar + /// + public string Log + { + get => _log; + set => RaiseAndSetIfChanged(ref _log, value); + } + /// /// Gets or sets an tag /// @@ -151,7 +160,7 @@ public uint ItemCount /// /// Gets the progress from 0 to 100% /// - public double ProgressPercent => _itemCount == 0 ? 0 : Math.Round(_processedItems * 100.0 / _itemCount, 2).Clamp(0, 100); + public double ProgressPercent => _itemCount == 0 ? 0 : Math.Clamp(Math.Round(_processedItems * 100.0 / _itemCount, 2), 0, 100); public static OperationProgress operator +(OperationProgress progress, uint value) { @@ -178,8 +187,9 @@ public void Init(bool canCancel = true) ItemName = "Initializing"; ItemCount = 0; ProcessedItems = 0; + Log = string.Empty; - TokenSource = new(); + TokenSource = new CancellationTokenSource(); RaisePropertyChanged(nameof(CanCancel)); } diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs index 9503a261..443dae4a 100644 --- a/UVtools.Core/Operations/OperationRaftRelief.cs +++ b/UVtools.Core/Operations/OperationRaftRelief.cs @@ -9,12 +9,12 @@ using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; +using Emgu.CV.Util; using System; using System.ComponentModel; using System.Drawing; using System.Text; using System.Threading.Tasks; -using Emgu.CV.Util; using UVtools.Core.EmguCV; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; diff --git a/UVtools.Core/Operations/OperationScripting.cs b/UVtools.Core/Operations/OperationScripting.cs index 9a13b8da..240e002c 100644 --- a/UVtools.Core/Operations/OperationScripting.cs +++ b/UVtools.Core/Operations/OperationScripting.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; using System; -using System.Diagnostics; using System.IO; using System.Xml.Serialization; using UVtools.Core.FileFormats; @@ -56,9 +55,8 @@ public sealed class OperationScripting : Operation { if (ScriptGlobals is not null && About.Version.CompareTo(ScriptGlobals.Script.MinimumVersionToRun) >= 0) { - return - $"Unable to run due {About.Software} version {About.VersionStr} is lower than required {ScriptGlobals.Script.MinimumVersionToRun}\n" + - $"Please update {About.Software} in order to run this script."; + return $"Unable to run due {About.Software} version {About.VersionStr} is lower than required {ScriptGlobals.Script.MinimumVersionToRun}\n" + + $"Please update {About.Software} in order to run this script."; } return "Script is not loaded."; @@ -170,24 +168,21 @@ public void ReloadScriptFromText(string? text = null) if (!string.IsNullOrWhiteSpace(text)) ScriptText = text; if (string.IsNullOrWhiteSpace(_scriptText)) return; - ScriptText = ScriptParser.ParseScriptFromText(_scriptText); ScriptGlobals = new ScriptGlobals { SlicerFile = SlicerFile, Operation = this }; - _scriptState = CSharpScript.RunAsync(_scriptText, + + _scriptState = CSharpScript.RunAsync(_scriptText, ScriptOptions.Default .AddReferences(typeof(About).Assembly) .WithAllowUnsafe(true), ScriptGlobals).Result; - var result = _scriptState.ContinueWithAsync("ScriptInit();").Result; - + RaisePropertyChanged(nameof(CanExecute)); OnScriptReload?.Invoke(this, EventArgs.Empty); } - - protected override bool ExecuteInternally(OperationProgress progress) { if (ScriptGlobals is null || _scriptState is null) return false; diff --git a/UVtools.Core/Operations/OperationTimelapse.cs b/UVtools.Core/Operations/OperationTimelapse.cs index bf08db5f..eea24d12 100644 --- a/UVtools.Core/Operations/OperationTimelapse.cs +++ b/UVtools.Core/Operations/OperationTimelapse.cs @@ -67,7 +67,7 @@ public enum TimelapseRaiseMode public override string? ValidateSpawn() { - if(!SlicerFile.CanUseLayerPositionZ && !SlicerFile.CanUseLayerLiftHeight) + if(SlicerFile is {CanUseLayerPositionZ: false, CanUseLayerLiftHeight: false}) { return NotSupportedMessage; } diff --git a/UVtools.Core/PixelEditor/PixelHistory.cs b/UVtools.Core/PixelEditor/PixelHistory.cs index a8c16638..9a6ec326 100644 --- a/UVtools.Core/PixelEditor/PixelHistory.cs +++ b/UVtools.Core/PixelEditor/PixelHistory.cs @@ -13,7 +13,7 @@ namespace UVtools.Core.PixelEditor; public class PixelHistory : IEnumerable { - public List Items { get; } = new List(); + public List Items { get; } = new(); public int Count => Items.Count; diff --git a/UVtools.Core/Printer/Machine.cs b/UVtools.Core/Printer/Machine.cs index af778aaf..fc6bed89 100644 --- a/UVtools.Core/Printer/Machine.cs +++ b/UVtools.Core/Printer/Machine.cs @@ -365,7 +365,6 @@ public static IEnumerable GetMachinesFromPrusaSlicer() } using var reader = new StreamReader(file); - string? line; var machine = new Machine { @@ -373,7 +372,7 @@ public static IEnumerable GetMachinesFromPrusaSlicer() Model = split[1] }; - while ((line = reader.ReadLine()) is not null) + while (reader.ReadLine() is { } line) { var keyValue = line.Split('=', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if(keyValue.Length < 2) continue; diff --git a/UVtools.Core/Scripting/ScriptConfiguration.cs b/UVtools.Core/Scripting/ScriptConfiguration.cs index 437e3a31..5f0ac70b 100644 --- a/UVtools.Core/Scripting/ScriptConfiguration.cs +++ b/UVtools.Core/Scripting/ScriptConfiguration.cs @@ -6,27 +6,27 @@ namespace UVtools.Core.Scripting; public sealed class ScriptConfiguration { /// - /// Gets the script name + /// Gets or sets the script name /// public string Name { get; set; } = "Unnamed script"; /// - /// Gets the script description of what it does + /// Gets or sets the script description of what it does /// public string Description { get; set; } = "I don't know my purpose, do not run me!"; /// - /// Gets the script author name + /// Gets or sets the script author name /// public string Author { get; set; } = "Undefined"; /// - /// Gets the script version + /// Gets or sets the script version /// public Version Version { get; set; } = new(0, 1); /// - /// Gets the minimum version able to run this script + /// Gets or sets the minimum version able to run this script /// Scripts were introduced on v2.8 /// public Version MinimumVersionToRun { get; set; } = new(2, 8, 0); diff --git a/UVtools.Core/Statistics.cs b/UVtools.Core/Statistics.cs index ce0ab1c5..d5bde1eb 100644 --- a/UVtools.Core/Statistics.cs +++ b/UVtools.Core/Statistics.cs @@ -16,11 +16,11 @@ public class Statistics { #region Properties - public List ImplementedKeys { get; } = new List(); - public List MissingKeys { get; } = new List(); + public List ImplementedKeys { get; } = new(); + public List MissingKeys { get; } = new(); public ushort TotalKeys => (ushort)(ImplementedKeys.Count + MissingKeys.Count); - public Stopwatch ExecutionTime { get; } = new Stopwatch(); + public Stopwatch ExecutionTime { get; } = new(); #endregion #region Overrides diff --git a/UVtools.Core/Suggestions/SuggestionLayerHeight.cs b/UVtools.Core/Suggestions/SuggestionLayerHeight.cs index f5fda810..63cda97b 100644 --- a/UVtools.Core/Suggestions/SuggestionLayerHeight.cs +++ b/UVtools.Core/Suggestions/SuggestionLayerHeight.cs @@ -6,6 +6,7 @@ * of this license document, but changing it is not allowed. */ +using System; using System.Text; using UVtools.Core.Extensions; using Layer = UVtools.Core.Layers.Layer; @@ -64,19 +65,19 @@ public override bool IsApplied public decimal MinimumLayerHeight { get => _minimumLayerHeight; - set => RaiseAndSetIfChanged(ref _minimumLayerHeight, Layer.RoundHeight(value.Clamp(Layer.MinimumHeight, Layer.MaximumHeight))); + set => RaiseAndSetIfChanged(ref _minimumLayerHeight, Layer.RoundHeight(Math.Clamp(value, Layer.MinimumHeight, Layer.MaximumHeight))); } public decimal MaximumLayerHeight { get => _maximumLayerHeight; - set => RaiseAndSetIfChanged(ref _maximumLayerHeight, Layer.RoundHeight(value.Clamp(Layer.MinimumHeight, Layer.MaximumHeight))); + set => RaiseAndSetIfChanged(ref _maximumLayerHeight, Layer.RoundHeight(Math.Clamp(value, Layer.MinimumHeight, Layer.MaximumHeight))); } public byte MaximumLayerHeightDecimalPlates { get => _maximumLayerHeightDecimalPlates; - set => RaiseAndSetIfChanged(ref _maximumLayerHeightDecimalPlates, value.Clamp(2, 4)); + set => RaiseAndSetIfChanged(ref _maximumLayerHeightDecimalPlates, Math.Clamp(value, (byte)2, (byte)4)); } #endregion diff --git a/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs b/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs index 3ba2815f..2470deea 100644 --- a/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs +++ b/UVtools.Core/Suggestions/SuggestionWaitTimeAfterCure.cs @@ -9,7 +9,6 @@ using System; using System.ComponentModel; using System.Text; -using UVtools.Core.Extensions; using UVtools.Core.Operations; namespace UVtools.Core.Suggestions; @@ -66,9 +65,15 @@ public override bool IsApplied { foreach (var layer in SlicerFile) { - if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers - if ((decimal)layer.WaitTimeAfterCure < _minimumWaitTimeAfterCure || - (decimal)layer.WaitTimeAfterCure > _maximumWaitTimeAfterCure) return false; + if (layer.IsDummy) + { + if (layer.WaitTimeAfterCure > 0.1) return false; + } + else + { + if ((decimal) layer.WaitTimeAfterCure < _minimumWaitTimeAfterCure || + (decimal) layer.WaitTimeAfterCure > _maximumWaitTimeAfterCure) return false; + } } } @@ -87,8 +92,14 @@ public override bool IsApplied { foreach (var layer in SlicerFile) { - if(layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers - if (Math.Abs(layer.WaitTimeAfterCure - CalculateWaitTime(layer.IsBottomLayer, (decimal)layer.ExposureTime)) > 0.1) return false; + if (layer.IsDummy) + { + if (layer.WaitTimeAfterCure > 0.1) return false; + } + else + { + if (Math.Abs(layer.WaitTimeAfterCure - CalculateWaitTime(layer.IsBottomLayer, (decimal)layer.ExposureTime)) > 0.1) return false; + } } } break; @@ -101,7 +112,7 @@ public override bool IsApplied } public override string Title => "Wait time after cure"; - public override string Description => "Rest some time after cure the layer and before the lift sequence can be important to allow the layer to cooldown a bit and detach better from the FEP."; + public override string Description => "Rest some time after curing the layer and before the lift sequence may allow the resin reaction to settle, harden and cool down a bit to detach more easily from the FEP, then yields less tension on the model, better print quality and easier lift's."; public override string Message => IsApplied ? $"{GlobalAppliedMessage}: {SlicerFile.BottomWaitTimeAfterCure}/{SlicerFile.WaitTimeAfterCure}s" @@ -235,13 +246,12 @@ protected override bool ExecuteInternally(OperationProgress progress) { SlicerFile.WaitTimeAfterCure = CalculateWaitTime(false, (decimal)SlicerFile.ExposureTime); } - + if (SlicerFile.CanUseLayerWaitTimeAfterCure) { foreach (var layer in SlicerFile) { - if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers - layer.WaitTimeAfterCure = CalculateWaitTime(layer.IsBottomLayer, (decimal) layer.ExposureTime); + layer.WaitTimeAfterCure = layer.IsDummy ? 0 : CalculateWaitTime(layer.IsBottomLayer, (decimal) layer.ExposureTime); } } @@ -256,10 +266,12 @@ public float CalculateWaitTime(bool isBottomLayer, decimal exposureTime) SuggestionWaitTimeAfterCureSetType.Fixed => (float) (isBottomLayer ? _fixedBottomWaitTimeAfterCure : _fixedWaitTimeAfterCure), - SuggestionWaitTimeAfterCureSetType.ProportionalExposure => (float) Math.Round( - (exposureTime * _proportionalWaitTimeAfterCure / _proportionalExposureTime).Clamp( + SuggestionWaitTimeAfterCureSetType.ProportionalExposure => + (float)Math.Clamp( + Math.Round(exposureTime * _proportionalWaitTimeAfterCure / _proportionalExposureTime, 2), isBottomLayer ? _minimumBottomWaitTimeAfterCure : _minimumWaitTimeAfterCure, - isBottomLayer ? _maximumBottomWaitTimeAfterCure : _maximumWaitTimeAfterCure), 2), + isBottomLayer ? _maximumBottomWaitTimeAfterCure : _maximumWaitTimeAfterCure + ), _ => throw new ArgumentOutOfRangeException() }; } diff --git a/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs b/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs index b086481e..b9fe3e6f 100644 --- a/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs +++ b/UVtools.Core/Suggestions/SuggestionWaitTimeBeforeCure.cs @@ -10,7 +10,6 @@ using System.ComponentModel; using System.Linq; using System.Text; -using UVtools.Core.Extensions; using UVtools.Core.Layers; using UVtools.Core.Operations; @@ -41,26 +40,26 @@ public enum SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom : byte #endregion #region Members - private SuggestionWaitTimeBeforeCureSetType _setType = SuggestionWaitTimeBeforeCureSetType.Fixed; + private SuggestionWaitTimeBeforeCureSetType _setType = SuggestionWaitTimeBeforeCureSetType.ProportionalLayerArea; private decimal _bottomHeight = 1; - private decimal _fixedBottomWaitTimeBeforeCure = 20; + private decimal _fixedBottomWaitTimeBeforeCure = 15; private decimal _fixedWaitTimeBeforeCure = 2; private byte _waitTimeBeforeCureTransitionLayerCount = 8; - private decimal _proportionalBottomWaitTimeBeforeCure = 20; - private decimal _proportionalWaitTimeBeforeCure = 2; - private uint _proportionalBottomLayerPixels = 1000000; - private uint _proportionalLayerPixels = 1000000; - private uint _proportionalBottomLayerArea = 1000; - private uint _proportionalLayerArea = 1000; + private decimal _proportionalBottomWaitTimeBeforeCure = 30; + private decimal _proportionalWaitTimeBeforeCure = 10; + private uint _proportionalBottomLayerPixels = 5000000; + private uint _proportionalLayerPixels = 5000000; + private uint _proportionalBottomLayerArea = 10000; + private uint _proportionalLayerArea = 10000; private decimal _proportionalBottomWaitTimeBeforeCureMaximumDifference = 1; private decimal _proportionalWaitTimeBeforeCureMaximumDifference = 1; private SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom _proportionalCalculateMassFrom = SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom.Previous; private decimal _proportionalMassRelativeHeight = 0.2m; - private decimal _minimumBottomWaitTimeBeforeCure = 5; + private decimal _minimumBottomWaitTimeBeforeCure = 10; private decimal _minimumWaitTimeBeforeCure = 1; - private decimal _maximumBottomWaitTimeBeforeCure = 120; - private decimal _maximumWaitTimeBeforeCure = 12; + private decimal _maximumBottomWaitTimeBeforeCure = 30; + private decimal _maximumWaitTimeBeforeCure = 4; private bool _createEmptyFirstLayer = true; #endregion @@ -102,9 +101,15 @@ public override bool IsApplied { foreach (var layer in SlicerFile) { - if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers - var waitTime = (decimal)layer.GetWaitTimeBeforeCure(); - if (waitTime < _minimumWaitTimeBeforeCure || waitTime > _maximumWaitTimeBeforeCure) return false; + if (layer.IsDummy) + { + if (layer.GetWaitTimeBeforeCure() > 0.1) return false; + } + else + { + var waitTime = (decimal)layer.GetWaitTimeBeforeCure(); + if (waitTime < _minimumWaitTimeBeforeCure || waitTime > _maximumWaitTimeBeforeCure) return false; + } } } @@ -123,8 +128,14 @@ public override bool IsApplied { foreach (var layer in SlicerFile) { - if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers - if (Math.Abs(layer.GetWaitTimeBeforeCure() - CalculateWaitTime(layer)) > 0.1) return false; + if (layer.IsDummy) + { + if (layer.GetWaitTimeBeforeCure() > 0.1) return false; + } + else + { + if (Math.Abs(layer.GetWaitTimeBeforeCure() - CalculateWaitTime(layer)) > 0.1) return false; + } } } break; @@ -383,28 +394,30 @@ public SuggestionWaitTimeBeforeCure() protected override bool ExecuteInternally(OperationProgress progress) { - if (SlicerFile.CanUseBottomWaitTimeAfterCure || SlicerFile.CanUseBottomLightOffDelay) + SlicerFile.SuppressRebuildPropertiesWork(() => { - SlicerFile.SetBottomWaitTimeBeforeCureOrLightOffDelay(CalculateWaitTime(true)); - } - if (SlicerFile.CanUseWaitTimeAfterCure || SlicerFile.CanUseLightOffDelay) - { - SlicerFile.SetNormalWaitTimeBeforeCureOrLightOffDelay(CalculateWaitTime(false)); - } - + if (SlicerFile.CanUseBottomWaitTimeAfterCure || SlicerFile.CanUseBottomLightOffDelay) + { + SlicerFile.SetBottomWaitTimeBeforeCureOrLightOffDelay(CalculateWaitTime(true)); + } + if (SlicerFile.CanUseWaitTimeAfterCure || SlicerFile.CanUseLightOffDelay) + { + SlicerFile.SetNormalWaitTimeBeforeCureOrLightOffDelay(CalculateWaitTime(false)); + } + }); + if (SlicerFile.CanUseLayerWaitTimeAfterCure || SlicerFile.CanUseLayerLightOffDelay) { foreach (var layer in SlicerFile) { - if (layer.NonZeroPixelCount <= 1) continue; // Ignore empty layers layer.SetWaitTimeBeforeCureOrLightOffDelay(CalculateWaitTime(layer)); } } - if (_createEmptyFirstLayer && SlicerFile.CanUseLayerPositionZ && !SlicerFile.SupportsGCode) + if (_createEmptyFirstLayer && SlicerFile is {CanUseLayerPositionZ: true, SupportsGCode: false}) { var firstLayer = SlicerFile.FirstLayer!; - if (firstLayer.NonZeroPixelCount > 1) // First layer is not blank as it seems, lets create one + if (!firstLayer.IsDummy) // First layer is not blank as it seems, lets create one { firstLayer = firstLayer.Clone(); using var mat = SlicerFile.CreateMatWithDummyPixel(); @@ -413,9 +426,16 @@ protected override bool ExecuteInternally(OperationProgress progress) //firstLayer.LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f; SlicerFile.FirstLayer!.LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f; // Already on position, try to not lift firstLayer.SetNoDelays(); - SlicerFile.SuppressRebuildPropertiesWork(() => { SlicerFile.Prepend(firstLayer); }); + SlicerFile.SuppressRebuildPropertiesWork(() => + { + if (SlicerFile.BottomLayerCount is > 0 and <= 7) + { + // Increase bottom layer count as dummy layer will count as one but constrain to a maximum + SlicerFile.BottomLayerCount++; + } + SlicerFile.Prepend(firstLayer); + }); } - } return true; @@ -455,7 +475,7 @@ public float CalculateWaitTime(bool isBottomLayer, Layer? layer = null) return (float)(isBottomLayer ? _fixedBottomWaitTimeBeforeCure : _fixedWaitTimeBeforeCure); } - if (layer.NonZeroPixelCount <= 1) return 0; // Empty layer, don't need wait time + if (layer.IsDummy) return 0; // Empty layer or not exposed, don't need wait time float mass = 0; if (layer.Index > 0 && _proportionalCalculateMassFrom != SuggestionWaitTimeBeforeCureProportionalCalculateMassFrom.Previous && _proportionalMassRelativeHeight > 0) @@ -464,11 +484,11 @@ public float CalculateWaitTime(bool isBottomLayer, Layer? layer = null) //if (previousLayer is not null) layer = previousLayer; uint count = 0; - Layer? previousLayer = layer; + var previousLayer = layer; while ((previousLayer = previousLayer!.PreviousLayer) is not null && (layer.PositionZ - previousLayer.PositionZ) <= (float)_proportionalMassRelativeHeight) { - if(previousLayer.NonZeroPixelCount < 2) continue; // Skip empty layers + if(previousLayer.IsDummy) continue; // Skip empty or not exposed layers count++; switch (_proportionalCalculateMassFrom) @@ -496,8 +516,8 @@ public float CalculateWaitTime(bool isBottomLayer, Layer? layer = null) if (mass <= 0) { - var previousLayer = layer.GetPreviousLayerWithAtLeastPixelCountOf(2); // Skip all previous empty layer - if (previousLayer is null) + var previousLayer = layer.GetPreviousLayerWithAtLeastPixelCountOf(2) ?? layer.GetNextLayerWithAtLeastPixelCountOf(2); // Skip all empty layers + if (previousLayer is null) // No parent layer to calculate from, set to fixed values { return isBottomLayer ? (float)_fixedBottomWaitTimeBeforeCure : (float)_fixedWaitTimeBeforeCure; } @@ -524,7 +544,7 @@ public float CalculateWaitTime(bool isBottomLayer, Layer? layer = null) var previousLayer = layer.GetPreviousLayerWithAtLeastPixelCountOf(2); if(previousLayer is not null) { - value = value.Clamp( + value = Math.Clamp(value, Math.Max(0, previousLayer.WaitTimeBeforeCure - (float)_proportionalBottomWaitTimeBeforeCureMaximumDifference), previousLayer.WaitTimeBeforeCure + (float)_proportionalBottomWaitTimeBeforeCureMaximumDifference); } @@ -537,19 +557,19 @@ public float CalculateWaitTime(bool isBottomLayer, Layer? layer = null) var previousLayer = layer.GetPreviousLayerWithAtLeastPixelCountOf(2); if (previousLayer is not null) { - value = value.Clamp( + value = Math.Clamp(value, Math.Max(0, previousLayer.WaitTimeBeforeCure - (float)_proportionalWaitTimeBeforeCureMaximumDifference), previousLayer.WaitTimeBeforeCure + (float)_proportionalWaitTimeBeforeCureMaximumDifference); } } } - return (float)Math.Round((decimal)value, 2).Clamp( + return (float)Math.Clamp(Math.Round((decimal)value, 2), isBottomLayer ? _minimumBottomWaitTimeBeforeCure : _minimumWaitTimeBeforeCure, isBottomLayer ? _maximumBottomWaitTimeBeforeCure : _maximumWaitTimeBeforeCure); } - public float CalculateWaitTime(Layer layer) => CalculateWaitTime(false, layer); + public float CalculateWaitTime(Layer layer) => layer.IsDummy ? 0 : CalculateWaitTime(false, layer); #endregion } \ No newline at end of file diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index cedcbf9d..c2053bdb 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 3.10.0 + 3.11.0 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 @@ -78,11 +78,12 @@ - + + diff --git a/UVtools.Core/Voxel/MarchingCubes.cs b/UVtools.Core/Voxel/MarchingCubes.cs index 4e99b531..0ff7d457 100644 --- a/UVtools.Core/Voxel/MarchingCubes.cs +++ b/UVtools.Core/Voxel/MarchingCubes.cs @@ -1,10 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Numerics; -using System.Runtime.Intrinsics; -using System.Text; -using System.Threading.Tasks; namespace UVtools.Core.Voxel; diff --git a/UVtools.Core/Voxel/Voxelizer.cs b/UVtools.Core/Voxel/Voxelizer.cs index 40bbbf40..689a5067 100644 --- a/UVtools.Core/Voxel/Voxelizer.cs +++ b/UVtools.Core/Voxel/Voxelizer.cs @@ -182,8 +182,8 @@ public static Mat BuildVoxelLayerImage(Mat curLayer, Mat? layerAbove = null, Mat { var lowerLeft = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ); var lowerRight = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize, positionZ); - var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height); - var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height); + var upperLeft = lowerLeft with {Z = lowerLeft.Z + height}; + var upperRight = lowerRight with {Z = lowerRight.Z + height}; yield return (lowerLeft, lowerRight, upperRight, FrontNormal); yield return (upperRight, upperLeft, lowerLeft, FrontNormal); } @@ -191,8 +191,8 @@ public static Mat BuildVoxelLayerImage(Mat curLayer, Mat? layerAbove = null, Mat { var lowerRight = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize + ySize, positionZ); var lowerLeft = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize + ySize, positionZ); - var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height); - var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height); + var upperLeft = lowerLeft with {Z = lowerLeft.Z + height}; + var upperRight = lowerRight with {Z = lowerRight.Z + height}; yield return (lowerLeft, lowerRight, upperRight, BackNormal); yield return (upperRight, upperLeft, lowerLeft, BackNormal); } @@ -200,8 +200,8 @@ public static Mat BuildVoxelLayerImage(Mat curLayer, Mat? layerAbove = null, Mat { var lowerLeft = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, positionZ); var lowerRight = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y) * ySize, positionZ); - var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height); - var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height); + var upperLeft = lowerLeft with {Z = lowerLeft.Z + height}; + var upperRight = lowerRight with {Z = lowerRight.Z + height}; yield return (lowerLeft, lowerRight, upperRight, LeftNormal); yield return (upperRight, upperLeft, lowerLeft, LeftNormal); } @@ -209,8 +209,8 @@ public static Mat BuildVoxelLayerImage(Mat curLayer, Mat? layerAbove = null, Mat { var lowerRight = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, positionZ); var lowerLeft = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y) * ySize, positionZ); - var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height); - var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height); + var upperLeft = lowerLeft with {Z = lowerLeft.Z + height}; + var upperRight = lowerRight with {Z = lowerRight.Z + height}; yield return (lowerLeft, lowerRight, upperRight, RightNormal); yield return (upperRight, upperLeft, lowerLeft, RightNormal); } diff --git a/UVtools.Installer/Code/HeatGeneratedFileList.wxs b/UVtools.Installer/Code/HeatGeneratedFileList.wxs index 008cb57a..cdda20b9 100644 --- a/UVtools.Installer/Code/HeatGeneratedFileList.wxs +++ b/UVtools.Installer/Code/HeatGeneratedFileList.wxs @@ -320,8 +320,8 @@ - - + + @@ -1693,7 +1693,7 @@ - + diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs index 12926b32..e4f88ced 100644 --- a/UVtools.WPF/App.axaml.cs +++ b/UVtools.WPF/App.axaml.cs @@ -6,13 +6,6 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Web; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; @@ -22,6 +15,13 @@ using Avalonia.Styling; using Avalonia.Themes.Fluent; using Emgu.CV; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Web; using UVtools.Core; using UVtools.Core.FileFormats; using UVtools.Core.Managers; @@ -289,6 +289,7 @@ public override void OnFrameworkInitializationCompleted() MainWindow = new MainWindow(); desktop.MainWindow = MainWindow; + MessageBoxManager.Standard = UiMessageBoxStandard.Instance; } } diff --git a/UVtools.WPF/Assets/Styles/Styles.xaml b/UVtools.WPF/Assets/Styles/Styles.xaml index d74492ad..e679be22 100644 --- a/UVtools.WPF/Assets/Styles/Styles.xaml +++ b/UVtools.WPF/Assets/Styles/Styles.xaml @@ -41,11 +41,31 @@ + + + + + + diff --git a/UVtools.WPF/Controls/ButtonWithIcon.cs b/UVtools.WPF/Controls/ButtonWithIcon.cs index 576df3ae..c720fe1c 100644 --- a/UVtools.WPF/Controls/ButtonWithIcon.cs +++ b/UVtools.WPF/Controls/ButtonWithIcon.cs @@ -5,13 +5,13 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ -using System; using Avalonia; using Avalonia.Controls; using Avalonia.Layout; using Avalonia.Styling; using Avalonia.Threading; using JetBrains.Annotations; +using System; namespace UVtools.WPF.Controls; diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateBloomingEffectControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateBloomingEffectControl.axaml.cs index 76a94f31..7754b113 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateBloomingEffectControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateBloomingEffectControl.axaml.cs @@ -1,7 +1,7 @@ -using System.Timers; using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Threading; +using System.Timers; using UVtools.Core.Operations; using UVtools.WPF.Controls.Tools; using UVtools.WPF.Extensions; @@ -64,7 +64,7 @@ public void UpdatePreview() { using var mat = Operation.GetLayerPreview(); _previewImage?.Dispose(); - PreviewImage = mat.ToBitmap(); + PreviewImage = mat.ToBitmapParallel(); } } } diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs index 93ee1454..f2b296c1 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateElephantFootControl.axaml.cs @@ -1,7 +1,7 @@ -using System.Timers; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Threading; +using System.Timers; using UVtools.Core.Operations; using UVtools.WPF.Controls.Tools; using UVtools.WPF.Extensions; @@ -70,7 +70,7 @@ public void UpdatePreview() { var layers = Operation.GetLayers(); _previewImage?.Dispose(); - PreviewImage = layers[0].ToBitmap(); + PreviewImage = layers[0].ToBitmapParallel(); foreach (var layer in layers) { layer.Dispose(); diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs index 91685871..0ea85e24 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateExposureFinderControl.axaml.cs @@ -1,12 +1,11 @@ -using System; -using System.Linq; -using System.Timers; using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Threading; using MessageBox.Avalonia.Enums; -using UVtools.Core.Extensions; +using System; +using System.Linq; +using System.Timers; using UVtools.Core.FileFormats; using UVtools.Core.Objects; using UVtools.Core.Operations; @@ -20,10 +19,10 @@ public class CalibrateExposureFinderControl : ToolControl { public OperationCalibrateExposureFinder Operation => BaseOperation as OperationCalibrateExposureFinder; - private Timer _timer; + private readonly Timer _timer; private Bitmap _previewImage; - private DataGrid _exposureTable; + private readonly DataGrid _exposureTable; public Bitmap PreviewImage { @@ -78,7 +77,7 @@ public void UpdatePreview() _previewImage?.Dispose(); if (layers is not null) { - PreviewImage = layers[^1].ToBitmap(); + PreviewImage = layers[^1].ToBitmapParallel(); foreach (var layer in layers) { layer.Dispose(); @@ -91,7 +90,7 @@ public void BrightnessExposureGenAdd() var values = Operation.MultipleBrightnessValuesArray.ToList(); // normal exposure - 255 // wanted - x - byte brightness = (byte)Math.Round(Operation.MultipleBrightnessGenExposureTime * byte.MaxValue / Operation.NormalExposure).Clamp(1, byte.MaxValue); + byte brightness = (byte) Math.Clamp(Math.Round(Operation.MultipleBrightnessGenExposureTime * byte.MaxValue / Operation.NormalExposure), 1, byte.MaxValue); if (values.Contains(brightness)) return; values.Add(brightness); values.Sort((b, b1) => b1.CompareTo(b)); diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs index 998013e2..6007c071 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateGrayscaleControl.axaml.cs @@ -1,7 +1,7 @@ -using System.Timers; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Threading; +using System.Timers; using UVtools.Core.Operations; using UVtools.WPF.Controls.Tools; using UVtools.WPF.Extensions; @@ -69,7 +69,7 @@ public void UpdatePreview() { var layers = Operation.GetLayers(); _previewImage?.Dispose(); - PreviewImage = layers[2].ToBitmap(); + PreviewImage = layers[2].ToBitmapParallel(); foreach (var layer in layers) { layer.Dispose(); diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs index bb7a2d36..f81b15c4 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateLiftHeightControl.axaml.cs @@ -1,7 +1,7 @@ -using System.Timers; using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Threading; +using System.Timers; using UVtools.Core.Operations; using UVtools.WPF.Controls.Tools; using UVtools.WPF.Extensions; @@ -64,7 +64,7 @@ public void UpdatePreview() { var layers = Operation.GetLayers(); _previewImage?.Dispose(); - PreviewImage = layers[0].ToBitmap(); + PreviewImage = layers[0].ToBitmapParallel(); foreach (var layer in layers) { layer.Dispose(); diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs index 96025182..37f4fabc 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateToleranceControl.axaml.cs @@ -1,7 +1,7 @@ -using System.Timers; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Threading; +using System.Timers; using UVtools.Core.Operations; using UVtools.WPF.Controls.Tools; using UVtools.WPF.Extensions; @@ -13,7 +13,7 @@ public class CalibrateToleranceControl : ToolControl { public OperationCalibrateTolerance Operation => BaseOperation as OperationCalibrateTolerance; - private Timer _timer; + private readonly Timer _timer; /*public string ProfileName { @@ -81,7 +81,7 @@ public void UpdatePreview() { var layers = Operation.GetLayers(); _previewImage?.Dispose(); - PreviewImage = layers[^1].ToBitmap(); + PreviewImage = layers[^1].ToBitmapParallel(); foreach (var layer in layers) { layer.Dispose(); diff --git a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs index fa31de91..726560d1 100644 --- a/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs +++ b/UVtools.WPF/Controls/Calibrators/CalibrateXYZAccuracyControl.axaml.cs @@ -1,8 +1,8 @@ -using System.Timers; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Threading; using MessageBox.Avalonia.Enums; +using System.Timers; using UVtools.Core.Operations; using UVtools.WPF.Controls.Tools; using UVtools.WPF.Extensions; @@ -80,7 +80,7 @@ public void UpdatePreview() { var layers = Operation.GetLayers(); _previewImage?.Dispose(); - PreviewImage = layers[1].ToBitmap(); + PreviewImage = layers[1].ToBitmapParallel(); foreach (var layer in layers) { layer.Dispose(); diff --git a/UVtools.WPF/Controls/DummyControl.axaml.cs b/UVtools.WPF/Controls/DummyControl.axaml.cs index 16c8e230..bbc83e22 100644 --- a/UVtools.WPF/Controls/DummyControl.axaml.cs +++ b/UVtools.WPF/Controls/DummyControl.axaml.cs @@ -1,5 +1,4 @@ -using Avalonia; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace UVtools.WPF.Controls; diff --git a/UVtools.WPF/Controls/Helpers.cs b/UVtools.WPF/Controls/Helpers.cs index b1b880b6..988271a1 100644 --- a/UVtools.WPF/Controls/Helpers.cs +++ b/UVtools.WPF/Controls/Helpers.cs @@ -5,9 +5,9 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ +using Avalonia.Controls; using System.Collections.Generic; using System.Linq; -using Avalonia.Controls; namespace UVtools.WPF.Controls; diff --git a/UVtools.WPF/Controls/ToggleButtonWithIcon.cs b/UVtools.WPF/Controls/ToggleButtonWithIcon.cs index f5e146ae..03ef5f6e 100644 --- a/UVtools.WPF/Controls/ToggleButtonWithIcon.cs +++ b/UVtools.WPF/Controls/ToggleButtonWithIcon.cs @@ -1,10 +1,10 @@ -using System; -using Avalonia; +using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Layout; using Avalonia.Styling; using Avalonia.Threading; +using System; namespace UVtools.WPF.Controls; diff --git a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs index a1fea36c..456ac37b 100644 --- a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs @@ -1,5 +1,4 @@ -using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using UVtools.Core.Operations; namespace UVtools.WPF.Controls.Tools; diff --git a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs index cf08d995..741ea8bd 100644 --- a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml.cs @@ -1,5 +1,5 @@ -using System; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; +using System; using UVtools.Core.FileFormats; using UVtools.Core.Operations; using UVtools.WPF.Windows; diff --git a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs index 92a7814b..3c045efb 100644 --- a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using Avalonia.Threading; +using System.Collections.Generic; using UVtools.Core.Operations; using UVtools.Core.Printer; diff --git a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs index 833fc8ac..a225499e 100644 --- a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System.Threading.Tasks; using UVtools.Core.Objects; using UVtools.Core.Operations; using UVtools.WPF.Extensions; diff --git a/UVtools.WPF/Controls/Tools/ToolDoubleExposureControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolDoubleExposureControl.axaml.cs index fedc8423..2efb5ce0 100644 --- a/UVtools.WPF/Controls/Tools/ToolDoubleExposureControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolDoubleExposureControl.axaml.cs @@ -1,4 +1,3 @@ -using Avalonia.Controls; using Avalonia.Markup.Xaml; using UVtools.Core.Operations; diff --git a/UVtools.WPF/Controls/Tools/ToolDynamicLiftsControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolDynamicLiftsControl.axaml.cs index 2f1561b3..fca86904 100644 --- a/UVtools.WPF/Controls/Tools/ToolDynamicLiftsControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolDynamicLiftsControl.axaml.cs @@ -1,6 +1,5 @@ using Avalonia.Markup.Xaml; using UVtools.Core.Operations; -using UVtools.WPF.Extensions; namespace UVtools.WPF.Controls.Tools; diff --git a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs index b75e2199..bf9d92e4 100644 --- a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs @@ -1,11 +1,11 @@ -using System.ComponentModel; -using System.Globalization; -using Avalonia; +using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Layout; using Avalonia.Markup.Xaml; -using UVtools.Core.Extensions; +using System; +using System.ComponentModel; +using System.Globalization; using UVtools.Core.FileFormats; using UVtools.Core.Operations; using UVtools.WPF.Windows; @@ -33,7 +33,7 @@ public RowControl(FileFormat.PrintParameterModifier modifier) { Modifier = modifier; - modifier.NewValue = modifier.OldValue.Clamp(modifier.Minimum, modifier.Maximum); + modifier.NewValue = Math.Clamp(modifier.OldValue, modifier.Minimum, modifier.Maximum); Name = new TextBlock { diff --git a/UVtools.WPF/Controls/Tools/ToolFadeExposureTimeControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolFadeExposureTimeControl.axaml.cs index 998689e0..9efe8b9e 100644 --- a/UVtools.WPF/Controls/Tools/ToolFadeExposureTimeControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolFadeExposureTimeControl.axaml.cs @@ -1,4 +1,3 @@ -using System; using Avalonia.Markup.Xaml; using UVtools.Core.Operations; using UVtools.WPF.Windows; diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml.cs index 047966e9..29a479d0 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerExportGifControl.axaml.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; -using System.IO; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System.Collections.Generic; +using System.IO; using UVtools.Core.Operations; namespace UVtools.WPF.Controls.Tools; diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml.cs index 549cfece..e8c30ee8 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerExportHeatMapControl.axaml.cs @@ -1,8 +1,6 @@ -using System.Collections.Generic; -using System.IO; -using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System.IO; using UVtools.Core.Operations; namespace UVtools.WPF.Controls.Tools; diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportHtmlControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportHtmlControl.axaml.cs index ba835fbb..79b5e73c 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerExportHtmlControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerExportHtmlControl.axaml.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; -using System.IO; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System.Collections.Generic; +using System.IO; using UVtools.Core.Operations; namespace UVtools.WPF.Controls.Tools diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs index 4f421e88..bfead49c 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerExportImageControl.axaml.cs @@ -1,6 +1,6 @@ -using System.IO; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System.IO; using UVtools.Core.Operations; namespace UVtools.WPF.Controls.Tools; diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml.cs index 15dbaa53..853bb75f 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerExportMeshControl.axaml.cs @@ -1,8 +1,7 @@ -using System.Collections.Generic; -using System.IO; -using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System.Collections.Generic; +using System.IO; using UVtools.Core.MeshFormats; using UVtools.Core.Operations; diff --git a/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs index 4931f97d..bf9e79b9 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerExportSkeletonControl.axaml.cs @@ -1,6 +1,6 @@ -using System.IO; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System.IO; using UVtools.Core.Operations; namespace UVtools.WPF.Controls.Tools; diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs index 6a7e135d..8e923106 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; +using System.Collections.Generic; +using System.Linq; using UVtools.Core.FileFormats; using UVtools.Core.Objects; using UVtools.Core.Operations; diff --git a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs index 95e8afa8..ff0b2176 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs @@ -1,5 +1,4 @@ -using System; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using UVtools.Core.Operations; using UVtools.WPF.Windows; diff --git a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs index 38a79418..7e13e7be 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerRemoveControl.axaml.cs @@ -1,6 +1,5 @@ -using System; -using Avalonia.Markup.Xaml; -using UVtools.Core; +using Avalonia.Markup.Xaml; +using System; using UVtools.Core.Layers; using UVtools.Core.Operations; using UVtools.WPF.Windows; diff --git a/UVtools.WPF/Controls/Tools/ToolLithophaneControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLithophaneControl.axaml.cs index f75f7e7d..77830e53 100644 --- a/UVtools.WPF/Controls/Tools/ToolLithophaneControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLithophaneControl.axaml.cs @@ -1,8 +1,8 @@ -using System.Timers; using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Media.Imaging; using Avalonia.Threading; +using System.Timers; using UVtools.Core.Operations; using UVtools.WPF.Extensions; using UVtools.WPF.Windows; @@ -62,7 +62,7 @@ public void UpdatePreview() { using var mat = Operation.GetTargetMat(); _previewImage?.Dispose(); - PreviewImage = mat?.ToBitmap(); + PreviewImage = mat?.ToBitmapParallel(); } public async void SelectFile() diff --git a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs index 40b79237..ed5468c3 100644 --- a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs @@ -1,10 +1,10 @@ -using System; -using System.Drawing; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Markup.Xaml; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; +using System; +using System.Drawing; using UVtools.Core.Extensions; using UVtools.Core.Operations; using UVtools.WPF.Extensions; @@ -30,7 +30,7 @@ public bool IsMaskInverted if(!RaiseAndSetIfChanged(ref _isMaskInverted, value)) return; if (!Operation.HaveInputMask) return; Operation.InvertMask(); - MaskImage = Operation.Mask.ToBitmap(); + MaskImage = Operation.Mask.ToBitmapParallel(); } } @@ -107,7 +107,7 @@ public async void ImportImageMask() try { Operation.LoadFromFile(result[0], _isMaskInverted, App.MainWindow.ROI.Size.IsEmpty ? SlicerFile.Resolution : App.MainWindow.ROI.Size); - MaskImage = Operation.Mask.ToBitmap(); + MaskImage = Operation.Mask.ToBitmapParallel(); } catch (Exception e) { @@ -127,7 +127,7 @@ public void GenerateMask() } else { - radius = radius.Clamp(2, Math.Min(Operation.Mask.Width, Operation.Mask.Height)) / 2; + radius = Math.Clamp(radius, 2, Math.Min(Operation.Mask.Width, Operation.Mask.Height)) / 2; } var maxScalar = new MCvScalar(_genMaximumBrightness); @@ -144,6 +144,6 @@ public void GenerateMask() } if (_isMaskInverted) Operation.InvertMask(); - MaskImage = Operation.Mask.ToBitmap(); + MaskImage = Operation.Mask.ToBitmapParallel(); } } \ No newline at end of file diff --git a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs index 1feb7c7a..15208dd7 100644 --- a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs @@ -1,5 +1,4 @@ -using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using UVtools.Core.Operations; namespace UVtools.WPF.Controls.Tools; diff --git a/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml.cs index d6bf2870..ff2452d8 100644 --- a/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml.cs @@ -1,12 +1,12 @@ +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Markup.Xaml; +using Avalonia.Threading; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Timers; -using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Markup.Xaml; -using Avalonia.Threading; using UVtools.Core.Extensions; using UVtools.Core.Operations; using UVtools.WPF.Extensions; @@ -125,18 +125,18 @@ public void UpdatePreview() if (!OperationPCBExposure.ValidExtensions.Any(extension => _selectedFile.IsExtension(extension)) || !_selectedFile.Exists) return; var file = (OperationPCBExposure.PCBExposureFile)_selectedFile.Clone(); - file.InvertPolarity = false; + file.InvertPolarity = file.IsExtension(".drl"); _previewImage?.Dispose(); using var mat = Operation.GetMat(file); if (_cropPreview) { using var matCropped = mat.CropByBounds(20); - PreviewImage = matCropped.ToBitmap(); + PreviewImage = matCropped.ToBitmapParallel(); } else { - PreviewImage = mat.ToBitmap(); + PreviewImage = mat.ToBitmapParallel(); } } catch (Exception e) diff --git a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs index 59e6875b..095fb2e7 100644 --- a/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolPatternControl.axaml.cs @@ -1,7 +1,5 @@ -using System; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using UVtools.Core.Operations; -using UVtools.WPF.Extensions; using UVtools.WPF.Windows; namespace UVtools.WPF.Controls.Tools; diff --git a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs index f8458848..392422d9 100644 --- a/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolRedrawModelControl.axaml.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Markup.Xaml; +using System.Collections.Generic; using UVtools.Core.FileFormats; using UVtools.Core.Operations; using UVtools.WPF.Windows; diff --git a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs index 583e1a06..c7e6e487 100644 --- a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs @@ -1,9 +1,10 @@ -using System; using Avalonia; using Avalonia.Controls; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Threading; +using System; +using System.Threading.Tasks; using UVtools.Core; using UVtools.Core.Operations; using UVtools.Core.Scripting; @@ -51,7 +52,7 @@ public override void Callback(ToolWindow.Callbacks callback) ParentWindow.ButtonOkEnabled = Operation.CanExecute; } }; - Operation.OnScriptReload += (sender, e) => ReloadGUI(); + Operation.OnScriptReload += (sender, e) => Dispatcher.UIThread.InvokeAsync(ReloadGUI); break; } } @@ -76,17 +77,25 @@ public async void ReloadScript() { try { - Operation.ReloadScriptFromFile(); + ParentWindow.IsEnabled = false; + + await Task.Run(() => Operation.ReloadScriptFromFile()); + if (Operation.ScriptGlobals is not null && About.Version.CompareTo(Operation.ScriptGlobals.Script.MinimumVersionToRun) < 0) { - await ParentWindow.MessageBoxError($"Unable to run due {About.Software} version {About.VersionStr} is lower than required {Operation.ScriptGlobals.Script.MinimumVersionToRun}\n" + - $"Please update {About.Software} in order to run this script."); + await ParentWindow.MessageBoxError( + $"Unable to run due {About.Software} version {About.VersionStr} is lower than required {Operation.ScriptGlobals.Script.MinimumVersionToRun}\n" + + $"Please update {About.Software} in order to run this script."); } } catch (Exception e) { await ParentWindow.MessageBoxError(e.Message); } + finally + { + ParentWindow.IsEnabled = true; + } } diff --git a/UVtools.WPF/Controls/UserControlEx.cs b/UVtools.WPF/Controls/UserControlEx.cs index 123591ce..28547fe0 100644 --- a/UVtools.WPF/Controls/UserControlEx.cs +++ b/UVtools.WPF/Controls/UserControlEx.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; +using Avalonia.Controls; +using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; -using Avalonia.Controls; using UVtools.Core.FileFormats; namespace UVtools.WPF.Controls; diff --git a/UVtools.WPF/Controls/WindowEx.cs b/UVtools.WPF/Controls/WindowEx.cs index adc67a74..a50d8bf1 100644 --- a/UVtools.WPF/Controls/WindowEx.cs +++ b/UVtools.WPF/Controls/WindowEx.cs @@ -6,14 +6,14 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Runtime.CompilerServices; using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Styling; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; using UVtools.Core.FileFormats; using UVtools.Core.SystemOS; using UVtools.WPF.Extensions; diff --git a/UVtools.WPF/Converters/EnumToCollectionConverter.cs b/UVtools.WPF/Converters/EnumToCollectionConverter.cs index a49f87d0..8baf655a 100644 --- a/UVtools.WPF/Converters/EnumToCollectionConverter.cs +++ b/UVtools.WPF/Converters/EnumToCollectionConverter.cs @@ -6,8 +6,8 @@ * of this license document, but changing it is not allowed. */ -using System; using Avalonia.Data.Converters; +using System; using UVtools.Core.Extensions; namespace UVtools.WPF.Converters; diff --git a/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs b/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs index 70bf5e62..9a3f08d8 100644 --- a/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs +++ b/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs @@ -6,9 +6,9 @@ * of this license document, but changing it is not allowed. */ +using Avalonia.Data.Converters; using System; using System.Linq; -using Avalonia.Data.Converters; using UVtools.Core.Extensions; namespace UVtools.WPF.Converters; diff --git a/UVtools.WPF/Extensions/BitmapExtension.cs b/UVtools.WPF/Extensions/BitmapExtension.cs index 0bf30f35..ed63ebe8 100644 --- a/UVtools.WPF/Extensions/BitmapExtension.cs +++ b/UVtools.WPF/Extensions/BitmapExtension.cs @@ -5,13 +5,15 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ -using System; using Avalonia; using Avalonia.Media.Imaging; using Avalonia.Platform; using Emgu.CV; using Emgu.CV.CvEnum; using SkiaSharp; +using System; +using System.Threading.Tasks; +using UVtools.Core.Extensions; namespace UVtools.WPF.Extensions; @@ -20,11 +22,23 @@ namespace UVtools.WPF.Extensions; /// public static class BitmapExtension { - public static int GetStep(this WriteableBitmap bitmap) - => (int)bitmap.Size.Width; + public static int GetByteStep(this ILockedFramebuffer buffer) + => buffer.RowBytes; + + public static int GetLength(this ILockedFramebuffer buffer) + => buffer.Size.Width * buffer.Size.Height; + + public static int GetPixelPos(this ILockedFramebuffer buffer, int x, int y) + => buffer.RowBytes * y + x; + + public static int GetPixelPos(this ILockedFramebuffer buffer, System.Drawing.Point location) + => buffer.GetPixelPos(location.X, location.Y); + + public static int GetByteStep(this WriteableBitmap bitmap) + => (int)bitmap.Size.Width * 4; /// - /// Gets the total length of this + /// Gets the total length of this as uint /// /// /// The total length of this @@ -32,50 +46,46 @@ public static int GetLength(this WriteableBitmap bitmap) => (int) (bitmap.Size.Width * bitmap.Size.Height); public static int GetPixelPos(this WriteableBitmap bitmap, int x, int y) - => (int)(bitmap.Size.Width * y + x); + => bitmap.GetByteStep() * y + x; + + public static int GetPixelPos(this WriteableBitmap bitmap, System.Drawing.Point location) + => bitmap.GetPixelPos(location.X, location.Y); + + public static unsafe Span GetPixelSpan(this ILockedFramebuffer buffer, int length = 0, int offset = 0) + => new(IntPtr.Add(buffer.Address, offset).ToPointer(), length > 0 ? length : buffer.GetLength()); - public static int GetPixelPos(this WriteableBitmap bitmap, System.Drawing.Point location) => - bitmap.GetPixelPos(location.X, location.Y); + public static Span GetSinglePixelSpan(this ILockedFramebuffer buffer, int x, int y, int length = 1) + => buffer.GetPixelSpan(length, buffer.GetPixelPos(x, y)); + + public static Span GetSinglePixelPosSpan(this ILockedFramebuffer buffer, int pos, int length = 1) + => buffer.GetPixelSpan(length, pos); + + public static Span GetPixelRowSpan(this ILockedFramebuffer buffer, int y, int length = 0, int offset = 0) + => buffer.GetPixelSpan(length == 0 ? buffer.Size.Width : length, buffer.GetPixelPos(offset, y)); /// /// Gets a single pixel span to manipulate or read pixels /// - /// Pixel type - /// Input /// A containing all pixels in data memory - public static unsafe Span GetPixelSpan(this WriteableBitmap bitmap) + public static Span GetPixelSpan(this WriteableBitmap bitmap, int length = 0, int offset = 0) { using var l = bitmap.Lock(); - return new Span(l.Address.ToPointer(), bitmap.GetLength()); + return l.GetPixelSpan(length, offset); } - - public static unsafe Span GetPixelSpan(this WriteableBitmap bitmap, int length, int offset = 0) - { - using var l = bitmap.Lock(); - return new Span(IntPtr.Add(l.Address, offset).ToPointer(), length); - } + public static Span GetSinglePixelSpan(this WriteableBitmap bitmap, int x, int y, int length = 1) + => bitmap.GetPixelSpan(length, bitmap.GetPixelPos(x, y)); - public static Span GetSinglePixelSpan(this WriteableBitmap bitmap, int x, int y, int length = 1) - { - using var l = bitmap.Lock(); - return bitmap.GetPixelSpan(length, bitmap.GetPixelPos(x, y)); - } - - public static Span GetSinglePixelPosSpan(this WriteableBitmap bitmap, int pos, int length = 3) + public static Span GetSinglePixelPosSpan(this WriteableBitmap bitmap, int pos, int length = 1) => bitmap.GetPixelSpan(length, pos); - public static unsafe Span GetPixelRowSpan(this WriteableBitmap bitmap, int y, int length = 0, int offset = 0) - { - using var l = bitmap.Lock(); - return new Span(IntPtr.Add(l.Address, (int) (bitmap.Size.Width * y + offset)).ToPointer(), (int) (length == 0 ? bitmap.Size.Width : length)); - } + public static Span GetPixelRowSpan(this WriteableBitmap bitmap, int y, int length = 0, int offset = 0) + => bitmap.GetPixelSpan(length == 0 ? (int)bitmap.Size.Width : length, bitmap.GetPixelPos(offset, y)); public static SKBitmap ToSkBitmap(this Mat mat) { - SKBitmap bitmap; - Mat target = mat; + var target = mat; SKColorType colorType; switch (mat.NumberOfChannels) { @@ -96,7 +106,7 @@ public static SKBitmap ToSkBitmap(this Mat mat) throw new Exception("Unknown color type"); } - bitmap = new SKBitmap(new SKImageInfo(target.Width, target.Height, colorType)); + var bitmap = new SKBitmap(new SKImageInfo(target.Width, target.Height, colorType)); bitmap.SetPixels(target.DataPointer); return bitmap; } @@ -115,13 +125,10 @@ public static WriteableBitmap ToBitmap(this Mat mat) var writableBitmap = new WriteableBitmap(new PixelSize(mat.Width, mat.Height), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Unpremul); - - using var lockBuffer = writableBitmap.Lock(); - + unsafe { - var targetPixels = (uint*) (void*) lockBuffer.Address; switch (mat.NumberOfChannels) { @@ -197,4 +204,71 @@ public static WriteableBitmap ToBitmap(this Mat mat) Marshal.Copy(buffer, y * lockBuffer.RowBytes, new IntPtr(lockBuffer.Address.ToInt64() + y * lockBuffer.RowBytes), lockBuffer.RowBytes); }*/ } + + public static WriteableBitmap ToBitmapParallel(this Mat mat) + { + var width = mat.Width; + var height = mat.Height; + var dataCount = width * height; + + var writableBitmap = new WriteableBitmap(new PixelSize(mat.Width, mat.Height), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Unpremul); + using var lockBuffer = writableBitmap.Lock(); + + unsafe + { + switch (mat.NumberOfChannels) + { + // Method 1 (Span copy) + case 1: + Parallel.For(0, height, y => + { + var spanBitmap = lockBuffer.GetPixelRowSpan(y); + var spanMat = mat.GetRowSpan(y); + for (var x = 0; x < width; x++) + { + var color = spanMat[x]; + spanBitmap[x] = (uint)(color | color << 8 | color << 16 | 0xff << 24); + } + }); + + + break; + case 3: + Parallel.For(0, height, y => + { + var spanBitmap = lockBuffer.GetPixelRowSpan(y); + var spanMat = mat.GetRowSpan(y); + int pixel = 0; + for (var x = 0; x < width; x++) + { + spanBitmap[x] = (uint)(spanMat[pixel++] | spanMat[pixel++] << 8 | spanMat[pixel++] << 16 | 0xff << 24); + } + }); + + break; + case 4: + if (mat.Depth == DepthType.Cv8U) + { + Parallel.For(0, height, y => + { + var spanBitmap = lockBuffer.GetPixelRowSpan(y); + var spanMat = mat.GetRowSpan(y); + int pixel = 0; + for (var x = 0; x < width; x++) + { + spanBitmap[x] = (uint)(spanMat[pixel++] | spanMat[pixel++] << 8 | spanMat[pixel++] << 16 | spanMat[pixel++] << 24); + } + }); + } + else if (mat.Depth == DepthType.Cv32S) + { + mat.GetDataSpan().CopyTo(new Span(lockBuffer.Address.ToPointer(), dataCount)); + } + + break; + } + } + + return writableBitmap; + } } \ No newline at end of file diff --git a/UVtools.WPF/Extensions/DrawingExtensions.cs b/UVtools.WPF/Extensions/DrawingExtensions.cs index bc21f5fb..32a4a302 100644 --- a/UVtools.WPF/Extensions/DrawingExtensions.cs +++ b/UVtools.WPF/Extensions/DrawingExtensions.cs @@ -6,8 +6,8 @@ * of this license document, but changing it is not allowed. */ -using System.Drawing; using Avalonia; +using System.Drawing; namespace UVtools.WPF.Extensions; diff --git a/UVtools.WPF/Extensions/WindowExtensions.cs b/UVtools.WPF/Extensions/WindowExtensions.cs index 8a672205..782c7c4b 100644 --- a/UVtools.WPF/Extensions/WindowExtensions.cs +++ b/UVtools.WPF/Extensions/WindowExtensions.cs @@ -6,13 +6,13 @@ * of this license document, but changing it is not allowed. */ -using System.Threading; -using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Platform; using Avalonia.Threading; using MessageBox.Avalonia.DTO; using MessageBox.Avalonia.Enums; +using System.Threading; +using System.Threading.Tasks; namespace UVtools.WPF.Extensions; diff --git a/UVtools.WPF/MainWindow.Clipboard.cs b/UVtools.WPF/MainWindow.Clipboard.cs index 97e43a11..47f2b461 100644 --- a/UVtools.WPF/MainWindow.Clipboard.cs +++ b/UVtools.WPF/MainWindow.Clipboard.cs @@ -7,12 +7,12 @@ */ -using System; -using System.ComponentModel; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Threading; using MessageBox.Avalonia.Enums; +using System; +using System.ComponentModel; using UVtools.WPF.Extensions; namespace UVtools.WPF; diff --git a/UVtools.WPF/MainWindow.GCode.cs b/UVtools.WPF/MainWindow.GCode.cs index 2b1e003b..985af66f 100644 --- a/UVtools.WPF/MainWindow.GCode.cs +++ b/UVtools.WPF/MainWindow.GCode.cs @@ -5,11 +5,11 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ -using System; -using System.IO; using Avalonia; using Avalonia.Controls; using MessageBox.Avalonia.Enums; +using System; +using System.IO; using UVtools.Core.SystemOS; using UVtools.WPF.Extensions; using Helpers = UVtools.WPF.Controls.Helpers; diff --git a/UVtools.WPF/MainWindow.Information.cs b/UVtools.WPF/MainWindow.Information.cs index 050196ee..5e95d0a6 100644 --- a/UVtools.WPF/MainWindow.Information.cs +++ b/UVtools.WPF/MainWindow.Information.cs @@ -5,6 +5,12 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Emgu.CV; +using Emgu.CV.CvEnum; +using MessageBox.Avalonia.Enums; using System; using System.Collections; using System.Collections.ObjectModel; @@ -12,20 +18,12 @@ using System.IO; using System.Reflection; using System.Text; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Input; -using Avalonia.Threading; -using Emgu.CV; -using Emgu.CV.CvEnum; -using MessageBox.Avalonia.Enums; using UVtools.Core.FileFormats; using UVtools.Core.Layers; using UVtools.Core.Objects; using UVtools.Core.SystemOS; using UVtools.WPF.Extensions; using UVtools.WPF.Structures; -using static Org.BouncyCastle.Bcpg.Attr.ImageAttrib; using Bitmap = Avalonia.Media.Imaging.Bitmap; using Helpers = UVtools.WPF.Controls.Helpers; @@ -133,7 +131,7 @@ public uint VisibleThumbnailIndex if (SlicerFile.Thumbnails[index] is null || SlicerFile.Thumbnails[index].IsEmpty) return; if (!RaiseAndSetIfChanged(ref _visibleThumbnailIndex, value)) return; - VisibleThumbnailImage = SlicerFile.Thumbnails[index].ToBitmap(); + VisibleThumbnailImage = SlicerFile.Thumbnails[index].ToBitmapParallel(); RaisePropertyChanged(nameof(ThumbnailCanGoPrevious)); RaisePropertyChanged(nameof(ThumbnailCanGoNext)); } @@ -348,7 +346,7 @@ public async void OnClickThumbnailImportHeatmap(bool replaceAll = false) result = SlicerFile.SetThumbnail((int) i, mat); } - mat?.Dispose();; + mat?.Dispose(); if(result) CanSave = true; } @@ -361,7 +359,7 @@ public void RefreshThumbnail() { return; } - VisibleThumbnailImage = SlicerFile.Thumbnails[i].ToBitmap(); + VisibleThumbnailImage = SlicerFile.Thumbnails[i].ToBitmapParallel(); } #endregion diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs index c1823f72..b3cb0d4f 100644 --- a/UVtools.WPF/MainWindow.Issues.cs +++ b/UVtools.WPF/MainWindow.Issues.cs @@ -5,13 +5,6 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Shapes; @@ -22,7 +15,14 @@ using Emgu.CV; using Emgu.CV.Util; using MessageBox.Avalonia.Enums; -using UVtools.Core; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using UVtools.Core; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Layers; diff --git a/UVtools.WPF/MainWindow.LayerPreview.cs b/UVtools.WPF/MainWindow.LayerPreview.cs index 33a3fc93..2016c721 100644 --- a/UVtools.WPF/MainWindow.LayerPreview.cs +++ b/UVtools.WPF/MainWindow.LayerPreview.cs @@ -6,14 +6,6 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using System.Timers; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; @@ -25,6 +17,14 @@ using Emgu.CV.Structure; using Emgu.CV.Util; using MessageBox.Avalonia.Enums; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Timers; using UVtools.AvaloniaControls; using UVtools.Core; using UVtools.Core.EmguCV; @@ -62,8 +62,8 @@ public enum ZoomToFitType : byte private Canvas _issuesSliderCanvas; - private Timer _layerNavigationTooltipTimer = new(0.1) { AutoReset = false }; - private Timer _layerNavigationSliderDebounceTimer = new(25) { AutoReset = false }; + private readonly Timer _layerNavigationTooltipTimer = new(0.1) { AutoReset = false }; + private readonly Timer _layerNavigationSliderDebounceTimer = new(25) { AutoReset = false }; private uint _actualLayerSlider; private uint _actualLayer; @@ -651,7 +651,7 @@ public Thickness LayerNavigationTooltipMargin { double trackerPos = LayerSlicerTrack.Thumb.Bounds.Height / 2 + LayerSlicerTrack.Thumb.Bounds.Top; double halfTooltipHeight = LayerNavigationTooltipBorder.Bounds.Height / 2; - top = (trackerPos - halfTooltipHeight).Clamp(0, + top = Math.Clamp(trackerPos - halfTooltipHeight, 0, LayerSlider.Bounds.Height - LayerNavigationTooltipBorder.Bounds.Height); } @@ -839,7 +839,7 @@ public void GoMassLayer(string which) public void RefreshLayerImage() { - LayerImageBox.Image = LayerCache.ImageBgr.ToBitmap(); + LayerImageBox.Image = LayerCache.ImageBgr.ToBitmapParallel(); } /// @@ -1474,7 +1474,7 @@ and not MainIssue.IssueType.PrintHeight CvInvoke.Rotate(LayerCache.ImageBgr, LayerCache.ImageBgr, _showLayerImageRotateCcwDirection ? RotateFlags.Rotate90CounterClockwise : RotateFlags.Rotate90Clockwise); } - LayerImageBox.Image = LayerCache.Bitmap = LayerCache.ImageBgr.ToBitmap(); + LayerImageBox.Image = LayerCache.Bitmap = LayerCache.ImageBgr.ToBitmapParallel(); RefreshCurrentLayerData(); @@ -1506,11 +1506,9 @@ public void DrawCrosshair(Rectangle rect) var startPoint = new Point(Math.Max(0, rect.X - Settings.LayerPreview.CrosshairMargin - 1), rect.Y + rect.Height / 2); var endPoint = - new Point( - Settings.LayerPreview.CrosshairLength == 0 - ? 0 - : (int)Math.Max(0, startPoint.X - Settings.LayerPreview.CrosshairLength + 1), - startPoint.Y); + startPoint with {X = Settings.LayerPreview.CrosshairLength == 0 + ? 0 + : (int)Math.Max(0, startPoint.X - Settings.LayerPreview.CrosshairLength + 1)}; CvInvoke.Line(LayerCache.ImageBgr, startPoint, @@ -1535,10 +1533,9 @@ public void DrawCrosshair(Rectangle rect) // TOP startPoint = new Point(rect.X + rect.Width / 2, Math.Max(0, rect.Y - Settings.LayerPreview.CrosshairMargin - 1)); - endPoint = new Point(startPoint.X, - (int)(Settings.LayerPreview.CrosshairLength == 0 - ? 0 - : Math.Max(0, startPoint.Y - Settings.LayerPreview.CrosshairLength + 1))); + endPoint = startPoint with {Y = (int)(Settings.LayerPreview.CrosshairLength == 0 + ? 0 + : Math.Max(0, startPoint.Y - Settings.LayerPreview.CrosshairLength + 1))}; CvInvoke.Line(LayerCache.ImageBgr, @@ -1569,12 +1566,12 @@ void Flip() if (!_showLayerImageFlipped) return; if (_showLayerImageFlippedHorizontally) { - point = new Point(LayerCache.Image.Width - 1 - point.X, point.Y); + point = point with {X = LayerCache.Image.Width - 1 - point.X}; } if (_showLayerImageFlippedVertically) { - point = new Point(point.X, LayerCache.Image.Height - 1 - point.Y); + point = point with {Y = LayerCache.Image.Height - 1 - point.Y}; } } @@ -2446,7 +2443,7 @@ public void UpdatePixelEditorCursor() if (diameter >= _pixelEditorCursorMinDiamater) { - cursor = EmguExtensions.InitMat(new System.Drawing.Size(diameter, diameter), 4); + cursor = EmguExtensions.InitMat(new Size(diameter, diameter), 4); var center = new Point(diameter / 2, diameter / 2); CvInvoke.Circle(cursor, center, @@ -2467,7 +2464,8 @@ public void UpdatePixelEditorCursor() if (cursor is not null) { - LayerImageBox.TrackerImage = cursor.ToBitmap(); + LayerImageBox.TrackerImage = cursor.ToBitmapParallel(); + //LayerImageBox.Cursor = new Cursor(cursor.ToBitmap(), new PixelPoint(cursor.Width / 2, cursor.Height / 2)); //cursor.Save("D:\\Cursor.png"); //LayerImageBox.TrackerImage.Save("D:\\CursorAVA.png"); } diff --git a/UVtools.WPF/MainWindow.PixelEditor.cs b/UVtools.WPF/MainWindow.PixelEditor.cs index 2c1a3246..9163f2d2 100644 --- a/UVtools.WPF/MainWindow.PixelEditor.cs +++ b/UVtools.WPF/MainWindow.PixelEditor.cs @@ -6,12 +6,6 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Drawing; -using System.Linq; -using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Media.Imaging; @@ -21,6 +15,12 @@ using Emgu.CV.Structure; using MessageBox.Avalonia.Enums; using SkiaSharp; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Drawing; +using System.Linq; +using System.Threading.Tasks; using UVtools.Core.Extensions; using UVtools.Core.Layers; using UVtools.Core.PixelEditor; @@ -220,8 +220,8 @@ void DrawPixel(bool isAdd, Point location, KeyModifiers keyModifiers) switch (operationDrawing.BrushShape) { case PixelDrawing.BrushShapeType.Line: - Point point1 = new(location.X - halfBrush, location.Y); - Point point2 = new(location.X + halfBrush, location.Y); + Point point1 = location with {X = location.X - halfBrush}; + Point point2 = location with {X = location.X + halfBrush}; if (_showLayerImageRotated) { diff --git a/UVtools.WPF/MainWindow.Progress.cs b/UVtools.WPF/MainWindow.Progress.cs index 3cceffc4..2d81e31e 100644 --- a/UVtools.WPF/MainWindow.Progress.cs +++ b/UVtools.WPF/MainWindow.Progress.cs @@ -6,9 +6,9 @@ * of this license document, but changing it is not allowed. */ +using Avalonia.Threading; using System; using System.Timers; -using Avalonia.Threading; using UVtools.Core.Operations; using UVtools.WPF.Structures; diff --git a/UVtools.WPF/MainWindow.Suggestions.cs b/UVtools.WPF/MainWindow.Suggestions.cs index 84b0605e..997035ab 100644 --- a/UVtools.WPF/MainWindow.Suggestions.cs +++ b/UVtools.WPF/MainWindow.Suggestions.cs @@ -6,15 +6,15 @@ * of this license document, but changing it is not allowed. */ +using Avalonia.Controls; +using Avalonia.Threading; +using MessageBox.Avalonia.Enums; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; -using Avalonia.Controls; -using Avalonia.Threading; -using MessageBox.Avalonia.Enums; using UVtools.Core.Suggestions; using UVtools.WPF.Extensions; using UVtools.WPF.Structures; diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index bdbaba8f..aec02024 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -2235,26 +2235,28 @@ RowDefinitions="*,Auto,*" ColumnDefinitions="*,Auto,*" IsEnabled="{Binding IsProgressVisible}" IsVisible="{Binding IsProgressVisible}"> - - - - - - - + + + + + + + + + diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 4e68de58..f221fa8e 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -20,10 +20,12 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Text; using System.Threading.Tasks; using System.Web; using UVtools.AvaloniaControls; using UVtools.Core; +using UVtools.Core.Exceptions; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Managers; @@ -798,6 +800,7 @@ protected override void OnKeyDown(KeyEventArgs e) || !IsFileLoaded || LayerImageBox.IsPanning || LayerImageBox.TrackerImage is not null + //|| LayerImageBox.Cursor != Cursor.Default || LayerImageBox.Cursor == StaticControls.CrossCursor || LayerImageBox.Cursor == StaticControls.HandCursor || LayerImageBox.SelectionMode == AdvancedImageBox.SelectionModes.Rectangle @@ -805,7 +808,7 @@ protected override void OnKeyDown(KeyEventArgs e) var imageBoxMousePosition = _globalPointerEventArgs?.GetPosition(LayerImageBox) ?? new Point(-1, -1); if (imageBoxMousePosition.X < 0 || imageBoxMousePosition.Y < 0) return; - + // Pixel Edit is active, Shift is down, and the cursor is over the image region. if (e.KeyModifiers == KeyModifiers.Shift) { @@ -1277,7 +1280,9 @@ public async void ProcessFiles(string[] files, bool openNewWindow = false, FileF try { var operation = new OperationScripting(SlicerFile); - operation.ReloadScriptFromFile(files[i]); + //operation.FilePath = files[i]; + await Task.Run(() => operation.ReloadScriptFromFile(files[i])); + if (operation.CanExecute) { if ((_globalModifiers & KeyModifiers.Shift) != 0) await RunOperation(operation); @@ -1687,7 +1692,7 @@ await this.MessageBoxWaring( RefreshProperties(); ResetDataContext(); - ForceUpdateActualLayer(actualLayer.Clamp(actualLayer, SliderMaximumValue)); + ForceUpdateActualLayer(SlicerFile.SanitizeLayerIndex(actualLayer)); if (Settings.LayerPreview.ZoomToFitPrintVolumeBounds) { @@ -1796,14 +1801,15 @@ await Dispatcher.UIThread.InvokeAsync(() => private async void ConvertToOnTapped(object? sender, RoutedEventArgs e) { - if (sender is not MenuItem item) return; - if (item.Tag is not FileExtension fileExtension) return; + if (sender is not MenuItem {Tag: FileExtension fileExtension}) return; var fileFormat = fileExtension.GetFileFormat(); uint version = fileFormat.DefaultVersion; - if (fileFormat.AvailableVersionsCount > 1) + var availableVersions = fileFormat.GetAvailableVersionsForExtension(fileExtension.Extension); + + if (availableVersions.Length > 1) { - var versionSelectorWindow = new VersionSelectorWindow(fileFormat, fileExtension); + var versionSelectorWindow = new VersionSelectorWindow(fileFormat, fileExtension, availableVersions); await versionSelectorWindow.ShowDialog(this); switch (versionSelectorWindow.DialogResult) { @@ -2040,7 +2046,7 @@ public async Task ShowOperation(Type type, Operation loadOperation = var calibrateTypeBase = typeof(CalibrateElephantFootControl); var classname = type.Name.StartsWith("OperationCalibrate") ? $"{calibrateTypeBase.Namespace}.{type.Name.Remove(0, Operation.ClassNameLength)}Control" : - $"{toolTypeBase.Namespace}.Tool{type.Name.Remove(0, Operation.ClassNameLength)}Control"; ; + $"{toolTypeBase.Namespace}.Tool{type.Name.Remove(0, Operation.ClassNameLength)}Control"; var controlType = Type.GetType(classname); ToolControl control; @@ -2138,10 +2144,52 @@ public async Task RunOperation(Operation baseOperation) catch (OperationCanceledException) { } + catch (MessageException ex) + { + Dispatcher.UIThread.InvokeAsync(async () + => await this.MessageBoxError(ex.Message, $"{baseOperation.Title} Error")); + } + catch (AggregateException ex) + { + var sb = new StringBuilder(); + + var title = $"{baseOperation.Title} Error"; + + if (ex.InnerExceptions.Count > 0) + { + if (ex.InnerExceptions.Count == 1) + { + if (ex.InnerExceptions[0] is MessageException messageException) + { + if (messageException.Title is not null) title = messageException.Title; + sb.AppendLine(ex.InnerExceptions[0].Message); + } + else + { + sb.AppendLine(ex.InnerExceptions[0].ToString()); + } + } + else + { + for (var i = 0; i < ex.InnerExceptions.Count; i++) + { + if (ex.InnerExceptions[i] is MessageException {Title: { }} messageException) + { + title = messageException.Title; + } + + if (i > 0) sb.AppendLine("---------------"); + sb.AppendLine($"({i + 1}) {(ex.InnerExceptions[i] is MessageException ? ex.InnerExceptions[i].Message : ex.InnerExceptions[i].ToString())}"); + } + } + } + + Dispatcher.UIThread.InvokeAsync(async () => await this.MessageBoxError(sb.ToString(), title)); + } catch (Exception ex) { - Dispatcher.UIThread.InvokeAsync(async () => - await this.MessageBoxError(ex.ToString(), $"{baseOperation.Title} Error")); + Dispatcher.UIThread.InvokeAsync(async () + => await this.MessageBoxError(ex.ToString(), $"{baseOperation.Title} Error")); } return false; @@ -2173,22 +2221,18 @@ public async Task RunOperation(Operation baseOperation) await OnClickDetectIssues(); break; } + + if (!string.IsNullOrWhiteSpace(baseOperation.AfterCompleteReport)) + { + await this.MessageBoxInfo(baseOperation.AfterCompleteReport, $"{baseOperation.Title} report ({LastStopWatch.Elapsed.Hours}h{LastStopWatch.Elapsed.Minutes}m{LastStopWatch.Elapsed.Seconds}s)"); + } } else { Clipboard.RestoreSnapshot(); } - if (baseOperation.Tag is not null) - { - var message = baseOperation.Tag.ToString(); - if (!string.IsNullOrWhiteSpace(message)) - { - //message += $"\nExecution time: "; - - await this.MessageBoxInfo(message, $"{baseOperation.Title} report ({LastStopWatch.Elapsed.Hours}h{LastStopWatch.Elapsed.Minutes}m{LastStopWatch.Elapsed.Seconds}s)"); - } - } + return result; } diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs index 608c5749..47dd8119 100644 --- a/UVtools.WPF/Program.cs +++ b/UVtools.WPF/Program.cs @@ -1,11 +1,11 @@ -using System; -using System.Diagnostics; -using System.Globalization; -using System.Threading.Tasks; -using Avalonia; +using Avalonia; using Projektanker.Icons.Avalonia; using Projektanker.Icons.Avalonia.FontAwesome; using Projektanker.Icons.Avalonia.MaterialDesign; +using System; +using System.Diagnostics; +using System.Globalization; +using System.Threading.Tasks; using UVtools.Core; using UVtools.Core.SystemOS; diff --git a/UVtools.WPF/Structures/Color.cs b/UVtools.WPF/Structures/Color.cs index 43565a6c..456b8739 100644 --- a/UVtools.WPF/Structures/Color.cs +++ b/UVtools.WPF/Structures/Color.cs @@ -5,8 +5,8 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ -using System; using Avalonia.Media; +using System; namespace UVtools.WPF.Structures; diff --git a/UVtools.WPF/Structures/PSProfileFolder.cs b/UVtools.WPF/Structures/PSProfileFolder.cs index a8aaa2b1..a459224f 100644 --- a/UVtools.WPF/Structures/PSProfileFolder.cs +++ b/UVtools.WPF/Structures/PSProfileFolder.cs @@ -1,10 +1,10 @@ -using System; +using Avalonia.Controls; +using Avalonia.Media; +using System; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; -using Avalonia.Controls; -using Avalonia.Media; using UVtools.Core.Objects; namespace UVtools.WPF.Structures; diff --git a/UVtools.WPF/Structures/RecentFiles.cs b/UVtools.WPF/Structures/RecentFiles.cs index eb80e6c4..c719e00a 100644 --- a/UVtools.WPF/Structures/RecentFiles.cs +++ b/UVtools.WPF/Structures/RecentFiles.cs @@ -29,7 +29,7 @@ public class RecentFiles : IList #region Singleton - private static Lazy _instanceHolder = + private static readonly Lazy _instanceHolder = new(() => new RecentFiles()); /// @@ -74,8 +74,7 @@ public static void Load() { using var tr = new StreamReader(FilePath); - string path; - while ((path = tr.ReadLine()) is not null) + while (tr.ReadLine() is { } path) { if(string.IsNullOrWhiteSpace(path)) continue; diff --git a/UVtools.WPF/Structures/UiMessageBoxStandard.cs b/UVtools.WPF/Structures/UiMessageBoxStandard.cs new file mode 100644 index 00000000..78e409dd --- /dev/null +++ b/UVtools.WPF/Structures/UiMessageBoxStandard.cs @@ -0,0 +1,41 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using Avalonia.Threading; +using MessageBox.Avalonia.Enums; +using System; +using System.Threading.Tasks; +using UVtools.Core.Dialogs; +using UVtools.WPF.Extensions; + +namespace UVtools.WPF.Structures; + +public class UiMessageBoxStandard : AbstractMessageBoxStandard +{ + #region Instance + private static readonly Lazy _instance = new(() => new UiMessageBoxStandard()); + + public static UiMessageBoxStandard Instance => _instance.Value; + #endregion + + #region Constructor + private UiMessageBoxStandard() + { } + #endregion + + #region Methods + public override Task ShowDialog(string? title, string message, MessageButtons buttons = MessageButtons.Ok) + { + return Dispatcher.UIThread.InvokeAsync(async () => + { + var result = await App.MainWindow.MessageBoxQuestion(message, string.IsNullOrWhiteSpace(title) ? "Question" : title, (ButtonEnum) buttons, false, true); + return (MessageButtonResult) result; + }); + } + #endregion +} \ No newline at end of file diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 60e1fc2f..787e8f7c 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ LICENSE https://github.com/sn4k3/UVtools Git - 3.10.0 + 3.11.0 AnyCPU;x64 UVtools.png README.md diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs index 00e638ed..a2692e50 100644 --- a/UVtools.WPF/UserSettings.cs +++ b/UVtools.WPF/UserSettings.cs @@ -6,6 +6,8 @@ * of this license document, but changing it is not allowed. */ +using Avalonia.Media; +using JetBrains.Annotations; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -13,15 +15,13 @@ using System.Drawing; using System.IO; using System.Xml.Serialization; -using Avalonia.Media; -using JetBrains.Annotations; using UVtools.Core; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Layers; using UVtools.Core.Network; using UVtools.Core.Objects; -using Color=UVtools.WPF.Structures.Color; +using Color = UVtools.WPF.Structures.Color; namespace UVtools.WPF; diff --git a/UVtools.WPF/Windows/AboutWindow.axaml.cs b/UVtools.WPF/Windows/AboutWindow.axaml.cs index 283ac6c5..987278ed 100644 --- a/UVtools.WPF/Windows/AboutWindow.axaml.cs +++ b/UVtools.WPF/Windows/AboutWindow.axaml.cs @@ -1,9 +1,9 @@ -using System; -using System.Runtime.InteropServices; -using System.Text; -using Avalonia; +using Avalonia; using Avalonia.Markup.Xaml; using Emgu.CV; +using System; +using System.Runtime.InteropServices; +using System.Text; using UVtools.Core; using UVtools.Core.Extensions; using UVtools.Core.SystemOS; diff --git a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs index b7686e6f..fc8ac7ab 100644 --- a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs +++ b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs @@ -1,13 +1,13 @@ -using System; +using Avalonia.Markup.Xaml; +using Avalonia.Threading; +using Emgu.CV; +using Emgu.CV.CvEnum; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Threading; using System.Threading.Tasks; -using Avalonia.Markup.Xaml; -using Avalonia.Threading; -using Emgu.CV; -using Emgu.CV.CvEnum; using UVtools.Core.Extensions; using UVtools.Core.Layers; using UVtools.WPF.Controls; diff --git a/UVtools.WPF/Windows/MaterialManagerWindow.axaml.cs b/UVtools.WPF/Windows/MaterialManagerWindow.axaml.cs index d2210596..6a8e51d7 100644 --- a/UVtools.WPF/Windows/MaterialManagerWindow.axaml.cs +++ b/UVtools.WPF/Windows/MaterialManagerWindow.axaml.cs @@ -1,9 +1,9 @@ -using System; -using System.Linq; using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; using MessageBox.Avalonia.Enums; +using System; +using System.Linq; using UVtools.Core.Managers; using UVtools.Core.Objects; using UVtools.WPF.Controls; diff --git a/UVtools.WPF/Windows/MessageWindow.axaml.cs b/UVtools.WPF/Windows/MessageWindow.axaml.cs index 98caefdf..3a9fc50e 100644 --- a/UVtools.WPF/Windows/MessageWindow.axaml.cs +++ b/UVtools.WPF/Windows/MessageWindow.axaml.cs @@ -1,8 +1,8 @@ -using System; using Avalonia; using Avalonia.Controls; using Avalonia.Layout; using Avalonia.Markup.Xaml; +using System; using UVtools.Core.SystemOS; using UVtools.WPF.Controls; diff --git a/UVtools.WPF/Windows/ProgressWindow.axaml.cs b/UVtools.WPF/Windows/ProgressWindow.axaml.cs index 1525697a..68b8ac26 100644 --- a/UVtools.WPF/Windows/ProgressWindow.axaml.cs +++ b/UVtools.WPF/Windows/ProgressWindow.axaml.cs @@ -6,12 +6,12 @@ * of this license document, but changing it is not allowed. */ -using System; -using System.Diagnostics; -using System.Timers; using Avalonia.Input; using Avalonia.Markup.Xaml; using Avalonia.Threading; +using System; +using System.Diagnostics; +using System.Timers; using UVtools.Core.Operations; using UVtools.WPF.Controls; using UVtools.WPF.Structures; @@ -23,9 +23,9 @@ public class ProgressWindow : WindowEx, IDisposable public Stopwatch StopWatch => Progress.StopWatch; public OperationProgress Progress { get; } = new (); - private LogItem _logItem = new (); + private readonly LogItem _logItem = new (); - private Timer _timer = new (200) { AutoReset = true }; + private readonly Timer _timer = new (200) { AutoReset = true }; private long _lastTotalSeconds = 0; diff --git a/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml.cs b/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml.cs index 28ea8d75..744754c0 100644 --- a/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml.cs +++ b/UVtools.WPF/Windows/PrusaSlicerManagerWindow.axaml.cs @@ -1,8 +1,7 @@ -using System; -using System.IO; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; +using Avalonia.Markup.Xaml; using MessageBox.Avalonia.Enums; +using System; +using System.IO; using UVtools.WPF.Controls; using UVtools.WPF.Extensions; using UVtools.WPF.Structures; diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml.cs b/UVtools.WPF/Windows/SettingsWindow.axaml.cs index 77669678..b627d85e 100644 --- a/UVtools.WPF/Windows/SettingsWindow.axaml.cs +++ b/UVtools.WPF/Windows/SettingsWindow.axaml.cs @@ -1,11 +1,11 @@ -using System; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using MessageBox.Avalonia.Enums; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using MessageBox.Avalonia.Enums; using UVtools.Core; using UVtools.Core.FileFormats; using UVtools.Core.Network; @@ -20,9 +20,9 @@ public class SettingsWindow : WindowEx { private double _scrollViewerMaxHeight; private int _selectedTabIndex; - private DataGrid _sendToCustomLocationsGrid; - private DataGrid _sendToProcessGrid; - private ComboBox _networkRemotePrinterComboBox; + private readonly DataGrid _sendToCustomLocationsGrid; + private readonly DataGrid _sendToProcessGrid; + private readonly ComboBox _networkRemotePrinterComboBox; public int MaxProcessorCount => Environment.ProcessorCount; diff --git a/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml.cs b/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml.cs index bd43fa8d..eb2c7d44 100644 --- a/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml.cs +++ b/UVtools.WPF/Windows/SuggestionSettingsWindow.axaml.cs @@ -1,11 +1,11 @@ -using System; -using System.Linq; -using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Threading; using MessageBox.Avalonia.Enums; +using System; +using System.Linq; +using System.Threading.Tasks; using UVtools.Core.Extensions; using UVtools.Core.Suggestions; using UVtools.WPF.Controls; @@ -17,7 +17,7 @@ namespace UVtools.WPF.Windows; public partial class SuggestionSettingsWindow : WindowEx { - private ContentControl _activeSuggestionContentPanel; + private readonly ContentControl _activeSuggestionContentPanel; private Suggestion _activeSuggestion; private Suggestion _selectedSuggestion; @@ -37,10 +37,10 @@ public Suggestion SelectedSuggestion { switch (await this.MessageBoxQuestion( $"You have pending changes on '{_activeSuggestion.Title}' and you are about to discard them.\n" + - "Do you want to continue?\n\n" + - "Yes: Discard all changes and continue\n" + - "No: Save changes and continue\n" + - "Cancel: Do not continue", + "Do you want to discard the changes?\n\n" + + "Yes: Discard changes\n" + + "No: Save changes\n" + + "Cancel: Continue editing", "Pending changes", ButtonEnum.YesNoCancel)) { case ButtonResult.Yes: @@ -86,7 +86,7 @@ public Suggestion ActiveSuggestion _activeSuggestion.PropertyChanged += (sender, e) => PendingChanges = true; var type = typeof(SuggestionControl); - var classname = $"{type.Namespace}.{_activeSuggestion.GetType().Name}Control"; ; + var classname = $"{type.Namespace}.{_activeSuggestion.GetType().Name}Control"; var controlType = Type.GetType(classname); SuggestionControl control; diff --git a/UVtools.WPF/Windows/TerminalWindow.axaml.cs b/UVtools.WPF/Windows/TerminalWindow.axaml.cs index 46500d35..f9b32696 100644 --- a/UVtools.WPF/Windows/TerminalWindow.axaml.cs +++ b/UVtools.WPF/Windows/TerminalWindow.axaml.cs @@ -5,15 +5,15 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ -using System; -using System.IO; -using System.Text; using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; +using System; +using System.IO; +using System.Text; using UVtools.Core; using UVtools.WPF.Controls; diff --git a/UVtools.WPF/Windows/ToolWindow.axaml.cs b/UVtools.WPF/Windows/ToolWindow.axaml.cs index ce6cfbc2..ed8f2899 100644 --- a/UVtools.WPF/Windows/ToolWindow.axaml.cs +++ b/UVtools.WPF/Windows/ToolWindow.axaml.cs @@ -5,14 +5,14 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ -using System; -using System.Collections.ObjectModel; -using System.Drawing; using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; using MessageBox.Avalonia.Enums; +using System; +using System.Collections.ObjectModel; +using System.Drawing; using UVtools.Core; using UVtools.Core.Extensions; using UVtools.Core.Layers; @@ -57,7 +57,6 @@ public enum Callbacks : byte private string _profileText; private IControl _contentControl; - private readonly ScrollViewer _contentScrollViewer; private bool _isButton1Visible; private string _button1Text = "Reset to defaults"; @@ -507,7 +506,7 @@ public IControl ContentControl set => RaiseAndSetIfChanged(ref _contentControl, value); } - public ScrollViewer ContentScrollViewer => _contentScrollViewer; + public ScrollViewer ContentScrollViewer { get; } #endregion @@ -579,7 +578,7 @@ public ToolWindow() InitializeComponent(); - _contentScrollViewer = this.FindControl("ContentScrollViewer"); + ContentScrollViewer = this.FindControl("ContentScrollViewer"); SelectAllLayers(); if (ROI != Rectangle.Empty) diff --git a/UVtools.WPF/Windows/VersionSelectorWindow.axaml b/UVtools.WPF/Windows/VersionSelectorWindow.axaml index e182eb99..82ffdc30 100644 --- a/UVtools.WPF/Windows/VersionSelectorWindow.axaml +++ b/UVtools.WPF/Windows/VersionSelectorWindow.axaml @@ -20,7 +20,7 @@ Text="Version:"/> diff --git a/UVtools.WPF/Windows/VersionSelectorWindow.axaml.cs b/UVtools.WPF/Windows/VersionSelectorWindow.axaml.cs index d9bf6ce7..37acb620 100644 --- a/UVtools.WPF/Windows/VersionSelectorWindow.axaml.cs +++ b/UVtools.WPF/Windows/VersionSelectorWindow.axaml.cs @@ -1,4 +1,5 @@ using Avalonia.Markup.Xaml; +using System.Linq; using UVtools.Core.FileFormats; using UVtools.WPF.Controls; @@ -15,7 +16,9 @@ public partial class VersionSelectorWindow : WindowEx public sealed override FileFormat SlicerFile { get; set; } - public FileExtension FileExtension { get; set; } + public FileExtension FileExtension { get; init; } + + public uint[] AvailableVersions { get; init; } public uint Version { @@ -29,11 +32,12 @@ public VersionSelectorWindow() DialogResult = DialogResults.Cancel; } - public VersionSelectorWindow(FileFormat slicerFile, FileExtension fileExtension) : this() + public VersionSelectorWindow(FileFormat slicerFile, FileExtension fileExtension, uint[] availableVersions) : this() { SlicerFile = slicerFile; FileExtension = fileExtension; - Version = slicerFile.DefaultVersion; + Version = availableVersions.Contains(slicerFile.DefaultVersion) ? SlicerFile.DefaultVersion : availableVersions[^1]; + AvailableVersions = availableVersions; Title += $" - {FileExtension.Description}"; DataContext = this; } @@ -45,13 +49,14 @@ private void InitializeComponent() public void SelectVersion() { - DialogResult = Version == SlicerFile.DefaultVersion ? DialogResults.Unknown : DialogResults.OK; + DialogResult = DialogResults.OK; Close(); } public void SelectDefault() { - DialogResult = DialogResults.Unknown; + Version = AvailableVersions.Contains(SlicerFile.DefaultVersion) ? SlicerFile.DefaultVersion : AvailableVersions[^1]; + DialogResult = DialogResults.OK; Close(); } } \ No newline at end of file diff --git a/UVtools.sln b/UVtools.sln index 2353766d..f2faaf95 100644 --- a/UVtools.sln +++ b/UVtools.sln @@ -13,7 +13,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UVtools.WPF", "UVtools.WPF\ {7C9927F8-132E-4A37-B894-440E0FD5AA3D} = {7C9927F8-132E-4A37-B894-440E0FD5AA3D} EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UVtools.ScriptSample", "UVtools.ScriptSample\UVtools.ScriptSample.csproj", "{6CCAA18A-8810-42DF-B75B-4160B40B9DD5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UVtools.ScriptSample", "Scripts\UVtools.ScriptSample\UVtools.ScriptSample.csproj", "{6CCAA18A-8810-42DF-B75B-4160B40B9DD5}" ProjectSection(ProjectDependencies) = postProject {7C9927F8-132E-4A37-B894-440E0FD5AA3D} = {7C9927F8-132E-4A37-B894-440E0FD5AA3D} EndProjectSection diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml index cb9550d9..1770e966 100644 --- a/documentation/UVtools.Core.xml +++ b/documentation/UVtools.Core.xml @@ -336,6 +336,79 @@ Default order of issues to show on the UI list + + + The Excellon drill format is a subset of RS274D and is used by the drilling and routing machines made by the Excellon corporation. + Because of Excellon's long history and dominance of the PCB drilling business for many years their format is a defacto industry standard. + Almost every PCB layout software can produce this format.However we have noticed that many PCB layout tools do not take + full advantage of the header information which makes reading the drill file more difficult than it should be. + https://www.artwork.com/gerber/drl2laser/excellon/index.htm + https://gist.github.com/katyo/5692b935abc085b1037e + + + + + Defines tool as having a diameter. + For each tool used in the data the diameter should be defined here. + There are additional parameters but if you are a PCB designer it is not up to you to specify feed rates and such. + + + + + Use float system + + + + + Include left zeros + + + + + Include right zeros + + + + + Indicates the start of the header. should always be the first line in the header + + + + + Number of padding zeros on coordinate system + + + + + Use Format 2 commands; alternative would be FMAT,1 + + + + + Gets or sets the X offset for drawings in millimeters + + + + + Gets or sets the Y offset for drawings in millimeters + + + + + Gets or sets to inverse the polarity on drawing + + + + + Gets or sets the scale to apply to each shape drawing size. + Positions and vectors aren't affected by this. + + + + + Generic exception to show only the message instead of the full stack-trace + + Gets the Unix timestamp since Jan 1, 1970 UTC @@ -2439,6 +2512,20 @@ Gets the amount of available versions in this file format + + + Gets the available versions to set in this file format for the given extension + + Extension name, with or without dot (.) + + + + + Gets the available versions to set in this file format for the given file name + + + + Gets the default version to use in this file when not setting the version @@ -4426,32 +4513,32 @@ Omit right zeros - + https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2022-02_en.pdf?ac97011bf6bce9aaf0b1aac43d84b05f - + Gets or sets the X offset for drawings in millimeters - + Gets or sets the Y offset for drawings in millimeters - + - Gets the current polarity as . will affect the return value + Gets the current polarity as . will affect the return value - + Gets or sets to inverse the polarity on drawing - + Gets or sets the scale to apply to each shape drawing size. Positions and vectors aren't affected by this. @@ -4771,6 +4858,11 @@ Gets if this layer is empty/all black pixels + + + Gets if this layer is a dummy layer to bypass a firmware constrain, that is contain at most one pixel and exposure time no more than 0.01s + + Gets the layer area (XY) in mm^2 @@ -4999,6 +5091,11 @@ Gets if this layer can be exposed to UV light + + + Gets if this layer should be exposed to UV light, ie: if layer is empty or no exposure time then it useless to expose it + + Gets the layer height in millimeters of this layer @@ -5100,12 +5197,18 @@ - Gets tree contours cache for this layer, however should call instead with instance + Gets tree contours cache for this layer, however should call instead with instance. + If not set it will calculate contours first - + - Gets tree contours cache for this layer + Gets tree contours cache for this layer. If not set it will calculate contours first + + + + + Gets tree contours cache for this layer. If not set it will calculate contours first @@ -5668,6 +5771,12 @@ Save settings to file + + + Gets the standard message box for this session which will trigger a message with pre-defined buttons. + If using console it will prompt there as text, otherwise if in UI will use dialogs. + + Gets the file format for this mesh @@ -6189,6 +6298,11 @@ Gets if this operation have validated at least once + + + Gets or sets an report to show to the user after complete the operation with success + + Gets if the operation can spawn @@ -6608,6 +6722,11 @@ Gets or sets the total of item count on this operation + + + Detailed log to show below the progress bar + + Gets or sets an tag @@ -6851,27 +6970,27 @@ - Gets the script name + Gets or sets the script name - Gets the script description of what it does + Gets or sets the script description of what it does - Gets the script author name + Gets or sets the script author name - Gets the script version + Gets or sets the script version - Gets the minimum version able to run this script + Gets or sets the minimum version able to run this script Scripts were introduced on v2.8