diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-03-28 06:26:01 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-03-28 06:26:01 +0300 |
commit | 1e9bbf9562357f1c53db91f0148cfc01e9db2d62 (patch) | |
tree | ca763c375807ae8243c23438aac301d90f8046d5 | |
parent | 2231fdaca309cec03bb02e59a52ed6685a906cf4 (diff) |
v2.7.2v2.7.2
* **Core:**
* Fix some improper locks for progress counter and change to Interlocked instead
* Fix a bug when chaging layer count by remove or add layers it will malform the file after save and crash the program with some tools and/or clipboard
* Fix when a operation fails by other reason different from cancelation it was not restoring the backup
* When manually delete/fix issues it will also backup the layers
* **LayerManager:**
* LayerManager is now readonly and no longer used to transpose layers, each FileFormat have now a unique `LayerManager` instance which is set on the generic constructor
* Implemented `List<Layer>` methods to easy modify the layers array
* Changing the `Layers` instance will now recompute some properties, call the properties rebuild and forced sanitize of the structure
* Better reallocation methods
* **Clipboard Manager:**
* Add the hability to do full backups, they will be marked with an asterisk (*) at clipboard items list
* When a partial backup is made and it backups all the layers it will be converted to full backup
* Clipboard can now restore a snapshot directly with `RestoreSnapshot`
* Prevent restore the initial backup upon file load and when clearing the clipboard
* Clip's that change the layer count will perform a full backup and also do a fail-safe backup behind if previous clip is not a full backup
* **Pixel dimming:**
* Allow to load an image file as a pattern (Do not use very large files or it will take much time to dump the data into the textbox)
* Empty lines on patterns will be discarded and not trigger validation error
51 files changed, 753 insertions, 661 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index bf60600..68b0ffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 28/03/2021 - v2.7.2 + +* **Core:** + * Fix some improper locks for progress counter and change to Interlocked instead + * Fix a bug when chaging layer count by remove or add layers it will malform the file after save and crash the program with some tools and/or clipboard + * Fix when a operation fails by other reason different from cancelation it was not restoring the backup + * When manually delete/fix issues it will also backup the layers +* **LayerManager:** + * LayerManager is now readonly and no longer used to transpose layers, each FileFormat have now a unique `LayerManager` instance which is set on the generic constructor + * Implemented `List<Layer>` methods to easy modify the layers array + * Changing the `Layers` instance will now recompute some properties, call the properties rebuild and forced sanitize of the structure + * Better reallocation methods +* **Clipboard Manager:** + * Add the hability to do full backups, they will be marked with an asterisk (*) at clipboard items list + * When a partial backup is made and it backups all the layers it will be converted to full backup + * Clipboard can now restore a snapshot directly with `RestoreSnapshot` + * Prevent restore the initial backup upon file load and when clearing the clipboard + * Clip's that change the layer count will perform a full backup and also do a fail-safe backup behind if previous clip is not a full backup +* **Pixel dimming:** + * Allow to load an image file as a pattern (Do not use very large files or it will take much time to dump the data into the textbox) + * Empty lines on patterns will be discarded and not trigger validation error + ## 24/03/2021 - v2.7.1 * **File formats:** @@ -49,4 +49,6 @@ * Nicholas Taylor * Nadine Maillard * Sidney Cheng -* Ben Ford
\ No newline at end of file +* Ben Ford +* Mario Molero +* Dennis Hansen
\ No newline at end of file diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index 2eaa12a..a6aa974 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -252,7 +252,8 @@ namespace UVtools.Core.Extensions public static Mat InitMat(Size size, int channels = 1, DepthType depthType = DepthType.Cv8U) { - var mat = new Mat(size, depthType, channels); + return Mat.Zeros(size.Height, size.Width, depthType, channels); + /*var mat = new Mat(size, depthType, channels); switch (channels) { case 1: @@ -266,7 +267,7 @@ namespace UVtools.Core.Extensions break; } - return mat; + return mat;*/ } public static Mat InitMat(Size size, MCvScalar scalar, int channels = 1, DepthType depthType = DepthType.Cv8U) diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs index d620337..f8863bb 100644 --- a/UVtools.Core/FileFormats/CWSFile.cs +++ b/UVtools.Core/FileFormats/CWSFile.cs @@ -770,7 +770,7 @@ namespace UVtools.Core.FileFormats tr.Close(); } - LayerManager = new LayerManager(OutputSettings.LayersNum, this); + LayerManager.Init(OutputSettings.LayersNum); progress.ItemCount = OutputSettings.LayersNum; @@ -832,159 +832,6 @@ namespace UVtools.Core.FileFormats } } - /*inputFile.Entries.AsParallel().ForAllInApproximateOrder(zipArchiveEntry => - //foreach (var zipArchiveEntry in inputFile.Entries) - { - if (!zipArchiveEntry.Name.EndsWith(".png") || progress.Token.IsCancellationRequested) return; - - var layerIndexStr = string.Empty; - var layerStr = zipArchiveEntry.Name.Substring(0, zipArchiveEntry.Name.Length - 4); - for (int i = layerStr.Length-1; i >= 0; i--) - { - if(layerStr[i] < '0' || layerStr[i] > '9') break; - layerIndexStr = $"{layerStr[i]}{layerIndexStr}"; - } - - if (string.IsNullOrEmpty(layerIndexStr)) return; - - // - .png - 4 numbers - // string layerStr = zipArchiveEntry.Name.Substring(zipArchiveEntry.Name.Length - 4 - layerSize, layerSize); - uint layerIndex = uint.Parse(layerIndexStr); - - var startStr = $"{GCodeKeywordSlice} {layerIndex}"; - var stripGcode = - gcode.Substring(gcode.IndexOf(startStr, StringComparison.InvariantCultureIgnoreCase) + - startStr.Length); - - var endStr = $"{GCodeKeywordSlice} {layerIndex + 1}"; - var endIndex = stripGcode.IndexOf(endStr, StringComparison.InvariantCulture); - if (endIndex < 0) endIndex = stripGcode.Length; - stripGcode = stripGcode - .Substring(0, endIndex) - .Trim(' ', '\n', '\r', '\t'); - //var startCurrPos = stripGcode.Remove(0, ";currPos:".Length); - - float liftHeight = 0; - float liftSpeed = GetInitialLayerValueOrNormal(layerIndex, BottomLiftSpeed, LiftSpeed); - float retractSpeed = RetractSpeed; - float lightOffDelay = GetInitialLayerValueOrNormal(layerIndex, BottomLightOffDelay, LightOffDelay); - byte pwm = GetInitialLayerValueOrNormal(layerIndex, BottomLightPWM, LightPWM); ; - float exposureTime = GetInitialLayerValueOrNormal(layerIndex, BottomExposureTime, ExposureTime); - - //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); - var pwmM106Regex = Regex.Match(stripGcode, @"M106 S(\d+)", RegexOptions.IgnoreCase); - var waitRegex = Regex.Match(stripGcode, ";<Delay> (\\d+)", RegexOptions.IgnoreCase); - - if (moveG1Regex.Success) - { - var liftHeightTemp = float.Parse(moveG1Regex.Groups[1].Value, CultureInfo.InvariantCulture); - var liftSpeedTemp = float.Parse(moveG1Regex.Groups[3].Value, CultureInfo.InvariantCulture); - moveG1Regex = moveG1Regex.NextMatch(); - if (moveG1Regex.Success) - { - liftHeight = liftHeightTemp; - liftSpeed = liftSpeedTemp; - retractSpeed = float.Parse(moveG1Regex.Groups[3].Value, CultureInfo.InvariantCulture); - } - } - - if (pwmM106Regex.Success) - { - pwm = byte.Parse(pwmM106Regex.Groups[1].Value); - } - if (layerIndex == 0) - { - OutputSettings.BottomLightPWM = pwm; - } - - if (waitRegex.Success) - { - exposureTime = (float)Math.Round(float.Parse(waitRegex.Groups[1].Value, CultureInfo.InvariantCulture) / 1000f, 2); - waitRegex = waitRegex.NextMatch(); - if (waitRegex.Success) - { - lightOffDelay = (float)Math.Round(float.Parse(waitRegex.Groups[1].Value, CultureInfo.InvariantCulture) / 1000f, 2); - } - else // Only one match, meaning light off delay is not present - { - lightOffDelay = GetInitialLayerValueOrNormal(layerIndex, BottomLightOffDelay, LightOffDelay); - } - } - - byte[] buffer; - - lock (progress.Mutex) - { - using (var stream = zipArchiveEntry.Open()) - { - buffer = stream.ToArray(); - if (Printer == PrinterType.Unknown) - { - using (Mat mat = new Mat()) - { - CvInvoke.Imdecode(buffer, ImreadModes.AnyColor, mat); - Printer = mat.NumberOfChannels == 1 ? PrinterType.Elfin : PrinterType.BeneMono; - } - } - - stream.Close(); - } - } - - - if (Printer == PrinterType.BeneMono) - { - using (Mat mat = new Mat()) - { - CvInvoke.Imdecode(buffer, ImreadModes.Color, mat); - using (Mat matDecode = new Mat(mat.Height, mat.Step, DepthType.Cv8U, 1)) - { - var span = mat.GetPixelSpan<byte>(); - var spanDecode = matDecode.GetPixelSpan<byte>(); - for (int i = 0; i < span.Length; i++) - { - spanDecode[i] = span[i]; - } - - this[layerIndex] = - new Layer(layerIndex, matDecode, LayerManager) - { - ExposureTime = exposureTime, - LiftHeight = liftHeight, - LiftSpeed = liftSpeed, - RetractSpeed = retractSpeed, - LightOffDelay = lightOffDelay, - LightPWM = pwm, - }; - } - } - } - else - { - this[layerIndex] = - new Layer(layerIndex, buffer, LayerManager) - { - ExposureTime = exposureTime, - LiftHeight = liftHeight, - LiftSpeed = liftSpeed, - RetractSpeed = retractSpeed, - LightOffDelay = lightOffDelay, - LightPWM = pwm, - }; - } - - progress++; - }); - - // Fix missing values from configuration - if (LayerCount > 0 && this[0] is not null) - { - SuppressRebuildProperties = true; - BottomLiftHeight = this[0].LiftHeight; - BottomLightOffDelay = this[0].LightOffDelay; - SuppressRebuildProperties = false; - }*/ LayerManager.GetBoundingRectangle(progress); } diff --git a/UVtools.Core/FileFormats/ChituboxFile.cs b/UVtools.Core/FileFormats/ChituboxFile.cs index 070d08f..2e4949a 100644 --- a/UVtools.Core/FileFormats/ChituboxFile.cs +++ b/UVtools.Core/FileFormats/ChituboxFile.cs @@ -1384,10 +1384,7 @@ namespace UVtools.Core.FileFormats LayerDefinitions[aaIndex, layerIndex] = layerData; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) @@ -1559,7 +1556,7 @@ namespace UVtools.Core.FileFormats } } - LayerManager = new LayerManager(HeaderSettings.LayerCount, this); + LayerManager.Init(HeaderSettings.LayerCount); progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); @@ -1589,11 +1586,8 @@ namespace UVtools.Core.FileFormats this[layerIndex] = layer; - - lock (progress.Mutex) - { - progress++; - } + + progress.LockAndIncrement(); }); } diff --git a/UVtools.Core/FileFormats/ChituboxZipFile.cs b/UVtools.Core/FileFormats/ChituboxZipFile.cs index 7e90511..ed8a707 100644 --- a/UVtools.Core/FileFormats/ChituboxZipFile.cs +++ b/UVtools.Core/FileFormats/ChituboxZipFile.cs @@ -438,7 +438,7 @@ namespace UVtools.Core.FileFormats } } - LayerManager = new LayerManager(HeaderSettings.LayerCount, this); + LayerManager.Init(HeaderSettings.LayerCount); progress.ItemCount = LayerCount; @@ -487,7 +487,7 @@ namespace UVtools.Core.FileFormats } entry = inputFile.GetEntry("preview_cropping.png"); - if (!ReferenceEquals(entry, null)) + if (entry is not null) { var count = CreatedThumbnailsCount; Thumbnails[count] = new Mat(); diff --git a/UVtools.Core/FileFormats/FDGFile.cs b/UVtools.Core/FileFormats/FDGFile.cs index 2febaf6..9aa0eda 100644 --- a/UVtools.Core/FileFormats/FDGFile.cs +++ b/UVtools.Core/FileFormats/FDGFile.cs @@ -995,10 +995,7 @@ namespace UVtools.Core.FileFormats LayersDefinitions[layerIndex] = layer; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); progress.Reset(OperationProgress.StatusWritingFile, LayerCount); @@ -1129,7 +1126,7 @@ namespace UVtools.Core.FileFormats progress.Token.ThrowIfCancellationRequested(); } - LayerManager = new LayerManager(HeaderSettings.LayerCount, this); + LayerManager.Init(HeaderSettings.LayerCount); progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount); @@ -1150,10 +1147,7 @@ namespace UVtools.Core.FileFormats }; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); } } diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index a99f567..86690fe 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -482,7 +482,8 @@ namespace UVtools.Core.FileFormats public LayerManager LayerManager { get => _layerManager; - set + private init => _layerManager = value; + /*set { var oldLayerManager = _layerManager; if (!RaiseAndSetIfChanged(ref _layerManager, value) || value is null) return; @@ -499,14 +500,14 @@ namespace UVtools.Core.FileFormats if (oldLayerManager is null) return; // Init - if (oldLayerManager.Count != LayerCount) + if (oldLayerManager.LayerCount != LayerCount) { - LayerCount = _layerManager.Count; + LayerCount = _layerManager.LayerCount; if (SuppressRebuildProperties) return; - if (LayerCount == 0 || this[LayerCount - 1] is null) return; // Not initialized + if (LayerCount == 0 || this[LastLayerIndex] is null) return; // Not initialized LayerManager.RebuildLayersProperties(); } - } + }*/ } /// <summary> @@ -722,7 +723,7 @@ namespace UVtools.Core.FileFormats /// <summary> /// Gets the last layer index /// </summary> - public uint LastLayerIndex => LayerCount - 1; + public uint LastLayerIndex => LayerManager?.LastLayerIndex ?? 0; /// <summary> /// Checks if this file format supports per layer settings @@ -734,7 +735,7 @@ namespace UVtools.Core.FileFormats /// </summary> public virtual uint LayerCount { - get => LayerManager?.Count ?? 0; + get => LayerManager?.LayerCount ?? 0; set { RaisePropertyChanged(); RaisePropertyChanged(nameof(NormalLayerCount)); @@ -1027,6 +1028,7 @@ namespace UVtools.Core.FileFormats #region Constructor protected FileFormat() { + LayerManager = new(this); Thumbnails = new Mat[ThumbnailsCount]; PropertyChanged += OnPropertyChanged; } @@ -1082,7 +1084,7 @@ namespace UVtools.Core.FileFormats #region Numerators public IEnumerator<Layer> GetEnumerator() { - return ((IEnumerable<Layer>)LayerManager.Layers).GetEnumerator(); + return LayerManager.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() @@ -1123,7 +1125,7 @@ namespace UVtools.Core.FileFormats public virtual void Clear() { FileFullPath = null; - LayerManager = null; + LayerManager.Clear(); GCode.Clear(); if (Thumbnails is not null) @@ -1491,16 +1493,11 @@ namespace UVtools.Core.FileFormats { if (progress.Token.IsCancellationRequested) return; var byteArr = layer.CompressedBytes; - using (FileStream stream = File.Create(Path.Combine(path, layer.Filename), - byteArr.Length)) - { - stream.Write(byteArr, 0, byteArr.Length); - stream.Close(); - lock (progress.Mutex) - { - progress++; - } - } + using var stream = File.Create(Path.Combine(path, layer.Filename), + byteArr.Length); + stream.Write(byteArr, 0, byteArr.Length); + stream.Close(); + progress.LockAndIncrement(); }); } } @@ -1813,9 +1810,9 @@ namespace UVtools.Core.FileFormats slicerFile.SuppressRebuildPropertiesWork(() => { - slicerFile.LayerManager = _layerManager.Clone(); + slicerFile.LayerManager.Init(_layerManager.CloneLayers()); slicerFile.AntiAliasing = ValidateAntiAliasingLevel(); - slicerFile.LayerCount = _layerManager.Count; + slicerFile.LayerCount = _layerManager.LayerCount; slicerFile.BottomLayerCount = BottomLayerCount; slicerFile.LayerHeight = LayerHeight; slicerFile.ResolutionX = ResolutionX; diff --git a/UVtools.Core/FileFormats/ImageFile.cs b/UVtools.Core/FileFormats/ImageFile.cs index da2cec9..668e2a8 100644 --- a/UVtools.Core/FileFormats/ImageFile.cs +++ b/UVtools.Core/FileFormats/ImageFile.cs @@ -97,8 +97,7 @@ namespace UVtools.Core.FileFormats { CvInvoke.CvtColor(ImageMat, ImageMat, ColorConversion.Bgr2Gray); }*/ - - LayerManager = new LayerManager(1, this); + LayerManager.Init(1); this[0] = new Layer(0, ImageMat, LayerManager); } diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index da5a3e4..03143e3 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -457,10 +457,7 @@ namespace UVtools.Core.FileFormats layerData[layerIndex].Encode(mat); } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); progress.ItemName = "Saving layers"; @@ -536,7 +533,7 @@ namespace UVtools.Core.FileFormats layerData[layerIndex].Parent = this; } - LayerManager = new LayerManager(HeaderSettings.LayerCount, this); + LayerManager.Init(HeaderSettings.LayerCount); progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); Parallel.For(0, LayerCount, @@ -548,10 +545,7 @@ namespace UVtools.Core.FileFormats using var image = layerData[layerIndex].Decode(); this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager); - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); LayerManager.RebuildLayersProperties(); diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs index 861d163..2a6491f 100644 --- a/UVtools.Core/FileFormats/PHZFile.cs +++ b/UVtools.Core/FileFormats/PHZFile.cs @@ -1020,10 +1020,7 @@ namespace UVtools.Core.FileFormats LayersDefinitions[layerIndex] = layer; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); progress.Reset(OperationProgress.StatusWritingFile, LayerCount); @@ -1151,7 +1148,7 @@ namespace UVtools.Core.FileFormats progress.Token.ThrowIfCancellationRequested(); } - LayerManager = new LayerManager(HeaderSettings.LayerCount, this); + LayerManager.Init(HeaderSettings.LayerCount); progress.Reset(OperationProgress.StatusDecodeLayers, HeaderSettings.LayerCount); @@ -1172,10 +1169,7 @@ namespace UVtools.Core.FileFormats }; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); } } diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index 50e99c6..d881a4f 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -406,10 +406,7 @@ namespace UVtools.Core.FileFormats layerData[layerIndex].Encode(mat); } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); progress.ItemName = "Saving layers"; @@ -491,7 +488,7 @@ namespace UVtools.Core.FileFormats Debug.WriteLine($"Layer {layerIndex} -> {layerData[layerIndex]}"); } - LayerManager = new LayerManager(LayerSettings.LayerCount, this); + LayerManager.Init(LayerSettings.LayerCount); progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); Parallel.For(0, LayerCount, @@ -502,10 +499,7 @@ namespace UVtools.Core.FileFormats using var image = layerData[layerIndex].Decode(); this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager); - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); LayerManager.RebuildLayersProperties(); diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 7ef1934..00e9670 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -1301,10 +1301,7 @@ namespace UVtools.Core.FileFormats layer.Encode(image); LayersDefinition.Layers[layerIndex] = layer; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); uint offsetLayerRle = FileMarkSettings.LayerImageAddress = (uint) (currentOffset + Helpers.Serializer.SizeOf(LayersDefinition.Section) + LayersDefinition.Section.Length); @@ -1396,7 +1393,7 @@ namespace UVtools.Core.FileFormats Debug.Write("LayersDefinition -> "); Debug.WriteLine(LayersDefinition); - LayerManager = new LayerManager(LayersDefinition.LayerCount, this); + LayerManager.Init(LayersDefinition.LayerCount); LayersDefinition.Layers = new LayerData[LayerCount]; @@ -1443,10 +1440,7 @@ namespace UVtools.Core.FileFormats }; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); // Fix position z height values diff --git a/UVtools.Core/FileFormats/SL1File.cs b/UVtools.Core/FileFormats/SL1File.cs index 7f854f1..05e32ae 100644 --- a/UVtools.Core/FileFormats/SL1File.cs +++ b/UVtools.Core/FileFormats/SL1File.cs @@ -641,7 +641,7 @@ namespace UVtools.Core.FileFormats LightPWM = LookupCustomValue(Keyword_LightPWM, DefaultBottomLightPWM); }); - LayerManager = new LayerManager((uint) (OutputConfigSettings.NumSlow + OutputConfigSettings.NumFast), this); + LayerManager.Init(OutputConfigSettings.NumSlow + OutputConfigSettings.NumFast); progress.ItemCount = LayerCount; diff --git a/UVtools.Core/FileFormats/UVJFile.cs b/UVtools.Core/FileFormats/UVJFile.cs index e30f5da..222ded1 100644 --- a/UVtools.Core/FileFormats/UVJFile.cs +++ b/UVtools.Core/FileFormats/UVJFile.cs @@ -397,7 +397,7 @@ namespace UVtools.Core.FileFormats JsonSettings = Helpers.JsonDeserializeObject<Settings>(entry.Open()); - LayerManager = new LayerManager(JsonSettings.Properties.Size.Layers, this); + LayerManager.Init(JsonSettings.Properties.Size.Layers); entry = inputFile.GetEntry(FilePreviewTinyName); if (!ReferenceEquals(entry, null)) diff --git a/UVtools.Core/FileFormats/ZCodeFile.cs b/UVtools.Core/FileFormats/ZCodeFile.cs index bb113a2..86e08e3 100644 --- a/UVtools.Core/FileFormats/ZCodeFile.cs +++ b/UVtools.Core/FileFormats/ZCodeFile.cs @@ -528,7 +528,7 @@ namespace UVtools.Core.FileFormats tr.Close(); } - LayerManager = new LayerManager(ManifestFile.Job.LayerCount, this); + LayerManager.Init(ManifestFile.Job.LayerCount); progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); //var gcode = GCode.ToString(); diff --git a/UVtools.Core/FileFormats/ZCodexFile.cs b/UVtools.Core/FileFormats/ZCodexFile.cs index 16cdcd7..e0137a4 100644 --- a/UVtools.Core/FileFormats/ZCodexFile.cs +++ b/UVtools.Core/FileFormats/ZCodexFile.cs @@ -485,7 +485,7 @@ namespace UVtools.Core.FileFormats throw new FileLoadException("ResinGCodeData not found", fileFullPath); } - LayerManager = new LayerManager(ResinMetadataSettings.TotalLayersCount, this); + LayerManager.Init(ResinMetadataSettings.TotalLayersCount); GCode.Clear(); using (TextReader tr = new StreamReader(entry.Open())) { diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index 33b5cc0..211832d 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -450,11 +450,11 @@ namespace UVtools.Core.GCode public void RebuildGCode(FileFormat slicerFile, StringBuilder header) => RebuildGCode(slicerFile, header?.ToString()); public void RebuildGCode(FileFormat slicerFile, string header = null) { - if (slicerFile.LayerCount == 0) return; Clear(); - AppendUVtools(); + if (slicerFile.LayerCount == 0) return; + if (!string.IsNullOrWhiteSpace(header)) { Append(header); diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs index a202ccf..b01fc52 100644 --- a/UVtools.Core/Layer/Layer.cs +++ b/UVtools.Core/Layer/Layer.cs @@ -537,7 +537,7 @@ namespace UVtools.Core public Layer NextLayer() { - if (ParentLayerManager is null || _index >= ParentLayerManager.Count - 1) + if (ParentLayerManager is null || _index >= ParentLayerManager.LayerCount - 1) return null; return ParentLayerManager[_index + 1]; @@ -802,6 +802,16 @@ namespace UVtools.Core public static string ShowHeight(double height) => string.Format($"{{0:F{HeightPrecision}}}", height); public static string ShowHeight(decimal height) => string.Format($"{{0:F{HeightPrecision}}}", height); + public static Layer[] CloneLayers(Layer[] layers) + { + var clonedLayers = new Layer[layers.Length]; + for (uint layerIndex = 0; layerIndex < layers.Length; layerIndex++) + { + clonedLayers[layerIndex] = layers[layerIndex]?.Clone(); + } + return clonedLayers; + } + #endregion } } diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs index 9a60479..49a6730 100644 --- a/UVtools.Core/Layer/LayerManager.cs +++ b/UVtools.Core/Layer/LayerManager.cs @@ -26,7 +26,7 @@ using UVtools.Core.PixelEditor; namespace UVtools.Core { - public class LayerManager : BindableBase, IEnumerable<Layer>, IDisposable + public class LayerManager : BindableBase, IList<Layer>, IDisposable { #region Properties public FileFormat SlicerFile { get; set; } @@ -39,21 +39,53 @@ namespace UVtools.Core public Layer[] Layers { get => _layers; - protected internal set + set { + //if (ReferenceEquals(_layers, value)) return; + + var rebuildProperties = false; + var oldLayerCount = LayerCount; + var oldLayers = _layers; _layers = value; BoundingRectangle = Rectangle.Empty; - if (SlicerFile.LayerCount != Count) + if (LayerCount != oldLayerCount) { - SlicerFile.LayerCount = Count; + SlicerFile.LayerCount = LayerCount; } SlicerFile.RequireFullEncode = true; - if (value is null) return; - SetAllIsModified(true); + SlicerFile.PrintHeight = SlicerFile.PrintHeight; SlicerFile.PrintTime = SlicerFile.PrintTimeComputed; - SlicerFile.RebuildGCode(); + + if (value is not null && LayerCount > 0) + { + SlicerFile.MaterialMilliliters = 0; + //SetAllIsModified(true); + + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) // Forced sanitize + { + if (_layers[layerIndex] is null) continue; + _layers[layerIndex].Index = layerIndex; + _layers[layerIndex].ParentLayerManager = this; + + if (layerIndex >= oldLayerCount || layerIndex < oldLayerCount && !_layers[layerIndex].Equals(oldLayers[layerIndex])) + { + // Marks as modified only if layer image changed on this index + _layers[layerIndex].IsModified = true; + } + } + + if (LayerCount != oldLayerCount && !SlicerFile.SuppressRebuildProperties && LastLayer is not null) + { + RebuildLayersProperties(); + rebuildProperties = true; + } + } + + if(!rebuildProperties) SlicerFile.RebuildGCode(); + + RaisePropertyChanged(); } } @@ -68,6 +100,11 @@ namespace UVtools.Core public Layer LastLayer => _layers?[^1]; /// <summary> + /// Gets the last layer index + /// </summary> + public uint LastLayerIndex => LayerCount - 1; + + /// <summary> /// Gets the bounding rectangle of the object /// </summary> private Rectangle _boundingRectangle = Rectangle.Empty; @@ -101,14 +138,135 @@ namespace UVtools.Core (float)Math.Round(_boundingRectangle.Height * pixelSize.Height, 2)); } } + + public void Init(uint layerCount) + { + _layers = new Layer[layerCount]; + } + + public void Init(Layer[] layers) + { + _layers = layers; + } + + public void Add(Layer layer) + { + Layers = _layers.Append(layer).ToArray(); + } + + public void Add(IEnumerable<Layer> layers) + { + var list = _layers.ToList(); + list.AddRange(layers); + Layers = list.ToArray(); + } + + public void Clear() + { + //Layers = Array.Empty<Layer>(); + Layers = null; + } + + public bool Contains(Layer layer) + { + return _layers.Contains(layer); + } + + public void CopyTo(Layer[] array, int arrayIndex) + { + _layers.CopyTo(array, arrayIndex); + } + + public bool Remove(Layer layer) + { + var list = _layers.ToList(); + var result = list.Remove(layer); + if (result) + { + Layers = list.ToArray(); + } + + return result; + } + + public int IndexOf(Layer layer) + { + for (int layerIndex = 0; layerIndex < Count; layerIndex++) + { + if (_layers[layerIndex].Equals(layer)) return layerIndex; + } + + return -1; + } + + public void Prepend(Layer layer) => Insert(0, layer); + public void Prepend(IEnumerable<Layer> layers) => InsertRange(0, layers); + public void Append(Layer layer) => Add(layer); + public void AppendRange(IEnumerable<Layer> layers) => Add(layers); + + public void Insert(int index, Layer layer) + { + if (index < 0) return; + if (index > Count) + { + Add(layer); // Append + return; + } + + var list = _layers.ToList(); + list.Insert(index, layer); + Layers = list.ToArray(); + } + + public void InsertRange(int index, IEnumerable<Layer> layers) + { + if (index < 0) return; + if (index > Count) + { + Add(layers); + return; + } + + var list = _layers.ToList(); + list.InsertRange(index, layers); + Layers = list.ToArray(); + } + + public void RemoveAt(int index) + { + if (index >= LastLayerIndex) return; + var list = _layers.ToList(); + list.RemoveAt(index); + Layers = list.ToArray(); + } + + public void RemoveRange(int index, int count) + { + if (count <= 0 || index >= LastLayerIndex) return; + var list = _layers.ToList(); + list.RemoveRange(index, count); + Layers = list.ToArray(); + } + + /// <summary> + /// Removes all null layers in the collection + /// </summary> + public void RemoveNulls() + { + Layers = _layers.Where(layer => layer is not null).ToArray(); + } + + public int Count => _layers?.Length ?? 0; + + public bool IsReadOnly => false; /// <summary> /// Gets the layers count /// </summary> - public uint Count => (uint) Layers.Length; + public uint LayerCount => (uint)(_layers?.Length ?? 0); - public byte LayerDigits => (byte)Count.ToString().Length; + public byte LayerDigits => (byte)LayerCount.ToString().Length; /// <summary> /// Gets if any layer got modified, otherwise false @@ -117,9 +275,9 @@ namespace UVtools.Core { get { - for (uint i = 0; i < Count; i++) + for (uint i = 0; i < LayerCount; i++) { - if (Layers[i].IsModified) return true; + if (_layers[i].IsModified) return true; } return false; } @@ -128,14 +286,19 @@ namespace UVtools.Core /// <summary> /// Gets if all layers have same value parameters as global settings /// </summary> - public bool AllLayersHaveGlobalParameters => Layers.Where(layer => layer is not null).All(layer => layer.HaveGlobalParameters); + public bool AllLayersHaveGlobalParameters => _layers.Where(layer => layer is not null).All(layer => layer.HaveGlobalParameters); //public float LayerHeight => Layers[0].PositionZ; #endregion #region Constructors - public LayerManager(uint layerCount, FileFormat slicerFile) + public LayerManager(FileFormat slicerFile) + { + SlicerFile = slicerFile; + } + + public LayerManager(uint layerCount, FileFormat slicerFile) : this(slicerFile) { SlicerFile = slicerFile; _layers = new Layer[layerCount]; @@ -145,20 +308,20 @@ namespace UVtools.Core #region Indexers public Layer this[uint index] { - get => Layers[index]; - set => AddLayer(index, value); + get => _layers[index]; + set => SetLayer(index, value); } public Layer this[int index] { - get => Layers[index]; - set => AddLayer((uint) index, value); + get => _layers[index]; + set => SetLayer((uint) index, value); } public Layer this[long index] { - get => Layers[index]; - set => AddLayer((uint) index, value); + get => _layers[index]; + set => SetLayer((uint) index, value); } #endregion @@ -234,65 +397,69 @@ namespace UVtools.Core public void RebuildLayersProperties(bool recalculateZPos = true, string property = null) { //var layerHeight = SlicerFile.LayerHeight; - for (uint layerIndex = 0; layerIndex < Count; layerIndex++) + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { var layer = this[layerIndex]; layer.Index = layerIndex; + layer.ParentLayerManager = this; - if (property is null || property == nameof(SlicerFile.BottomLayerCount)) - { - layer.ExposureTime = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime); - layer.LiftHeight = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight); - layer.LiftSpeed = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed); - layer.RetractSpeed = SlicerFile.RetractSpeed; - layer.LightPWM = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); - layer.LightOffDelay = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightOffDelay, SlicerFile.LightOffDelay); - } - else + if (property != string.Empty) { - if (layer.IsNormalLayer) + if (property is null || property == nameof(SlicerFile.BottomLayerCount)) { - switch (property) - { - case nameof(SlicerFile.ExposureTime): - layer.ExposureTime = SlicerFile.ExposureTime; - break; - case nameof(SlicerFile.LiftHeight): - layer.LiftHeight = SlicerFile.LiftHeight; - break; - case nameof(SlicerFile.LiftSpeed): - layer.LiftSpeed = SlicerFile.LiftSpeed; - break; - case nameof(SlicerFile.LightOffDelay): - layer.LightOffDelay = SlicerFile.LightOffDelay; - break; - case nameof(SlicerFile.LightPWM): - layer.LightPWM = SlicerFile.LightPWM; - break; - } + layer.ExposureTime = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomExposureTime, SlicerFile.ExposureTime); + layer.LiftHeight = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftHeight, SlicerFile.LiftHeight); + layer.LiftSpeed = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLiftSpeed, SlicerFile.LiftSpeed); + layer.RetractSpeed = SlicerFile.RetractSpeed; + layer.LightPWM = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightPWM, SlicerFile.LightPWM); + layer.LightOffDelay = SlicerFile.GetInitialLayerValueOrNormal(layerIndex, SlicerFile.BottomLightOffDelay, SlicerFile.LightOffDelay); } - else // Bottom layers + else { - switch (property) + if (layer.IsNormalLayer) { - case nameof(SlicerFile.BottomExposureTime): - layer.ExposureTime = SlicerFile.BottomExposureTime; - break; - case nameof(SlicerFile.BottomLiftHeight): - layer.LiftHeight = SlicerFile.BottomLiftHeight; - break; - case nameof(SlicerFile.BottomLiftSpeed): - layer.LiftSpeed = SlicerFile.BottomLiftSpeed; - break; - case nameof(SlicerFile.RetractSpeed): - layer.RetractSpeed = SlicerFile.RetractSpeed; - break; - case nameof(SlicerFile.BottomLightOffDelay): - layer.LightOffDelay = SlicerFile.BottomLightOffDelay; - break; - case nameof(SlicerFile.BottomLightPWM): - layer.LightPWM = SlicerFile.BottomLightPWM; - break; + switch (property) + { + case nameof(SlicerFile.ExposureTime): + layer.ExposureTime = SlicerFile.ExposureTime; + break; + case nameof(SlicerFile.LiftHeight): + layer.LiftHeight = SlicerFile.LiftHeight; + break; + case nameof(SlicerFile.LiftSpeed): + layer.LiftSpeed = SlicerFile.LiftSpeed; + break; + case nameof(SlicerFile.LightOffDelay): + layer.LightOffDelay = SlicerFile.LightOffDelay; + break; + case nameof(SlicerFile.LightPWM): + layer.LightPWM = SlicerFile.LightPWM; + break; + } + } + else // Bottom layers + { + switch (property) + { + case nameof(SlicerFile.BottomExposureTime): + layer.ExposureTime = SlicerFile.BottomExposureTime; + break; + case nameof(SlicerFile.BottomLiftHeight): + layer.LiftHeight = SlicerFile.BottomLiftHeight; + break; + case nameof(SlicerFile.BottomLiftSpeed): + layer.LiftSpeed = SlicerFile.BottomLiftSpeed; + break; + case nameof(SlicerFile.RetractSpeed): + layer.RetractSpeed = SlicerFile.RetractSpeed; + break; + case nameof(SlicerFile.BottomLightOffDelay): + layer.LightOffDelay = SlicerFile.BottomLightOffDelay; + break; + case nameof(SlicerFile.BottomLightPWM): + layer.LightPWM = SlicerFile.BottomLightPWM; + break; + } } } } @@ -311,7 +478,7 @@ namespace UVtools.Core /// </summary> public void SetNoLiftForSamePositionedLayers() { - for (int layerIndex = 1; layerIndex < Count; layerIndex++) + for (int layerIndex = 1; layerIndex < LayerCount; layerIndex++) { if (this[layerIndex - 1].PositionZ != this[layerIndex].PositionZ) continue; this[layerIndex].LiftHeight = 0; @@ -321,23 +488,20 @@ namespace UVtools.Core public Rectangle GetBoundingRectangle(OperationProgress progress = null) { - if (!_boundingRectangle.IsEmpty || Count == 0 || this[0] is null) return _boundingRectangle; - progress ??= new OperationProgress(OperationProgress.StatusOptimizingBounds, Count - 1); + if (!_boundingRectangle.IsEmpty || LayerCount == 0 || this[0] is null) return _boundingRectangle; + progress ??= new OperationProgress(OperationProgress.StatusOptimizingBounds, LayerCount - 1); _boundingRectangle = this[0].BoundingRectangle; if (_boundingRectangle.IsEmpty) // Safe checking { - progress.Reset(OperationProgress.StatusOptimizingBounds, Count-1); - Parallel.For(0, Count, layerIndex => + progress.Reset(OperationProgress.StatusOptimizingBounds, LayerCount-1); + Parallel.For(0, LayerCount, layerIndex => { if (progress.Token.IsCancellationRequested) return; this[layerIndex].GetBoundingRectangle(); if (progress is null) return; - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); _boundingRectangle = this[0].BoundingRectangle; @@ -348,10 +512,10 @@ namespace UVtools.Core } } - progress.Reset(OperationProgress.StatusCalculatingBounds, Count-1); - for (int i = 1; i < Count; i++) + progress.Reset(OperationProgress.StatusCalculatingBounds, LayerCount-1); + for (int i = 1; i < LayerCount; i++) { - if(this[i].BoundingRectangle.IsEmpty) continue; + if(this[i] is null || this[i].BoundingRectangle.IsEmpty) continue; _boundingRectangle = Rectangle.Union(_boundingRectangle, this[i].BoundingRectangle); progress++; } @@ -360,19 +524,18 @@ namespace UVtools.Core } /// <summary> - /// Add a layer + /// Sets a layer /// </summary> /// <param name="index">Layer index</param> /// <param name="layer">Layer to add</param> /// <param name="makeClone">True to add a clone of the layer</param> - public void AddLayer(uint index, Layer layer, bool makeClone = false) + public void SetLayer(uint index, Layer layer, bool makeClone = false) { - if (Layers[index] is not null && layer is not null) layer.IsModified = true; - Layers[index] = makeClone && layer is not null ? layer.Clone() : layer; - if (layer is not null) - { - layer.ParentLayerManager = this; - } + if (_layers[index] is not null && layer is not null) layer.IsModified = true; + _layers[index] = makeClone && layer is not null ? layer.Clone() : layer; + if (layer is null) return; + layer.Index = index; + layer.ParentLayerManager = this; } /// <summary> @@ -380,13 +543,11 @@ namespace UVtools.Core /// </summary> /// <param name="layers">Layers to add</param> /// <param name="makeClone">True to add a clone of layers</param> - public void AddLayers(IEnumerable<Layer> layers, bool makeClone = false) + public void SetLayers(IEnumerable<Layer> layers, bool makeClone = false) { - //layer.Index = index; foreach (var layer in layers) { - layer.ParentLayerManager = this; - Layers[layer.Index] = makeClone ? layer.Clone() : layer; + SetLayer(layer.Index, layer, makeClone); } } @@ -397,7 +558,7 @@ namespace UVtools.Core /// <returns></returns> public Layer GetLayer(uint index) { - return Layers[index]; + return _layers[index]; } public static void MutateGetVarsIterationChamfer(uint startLayerIndex, uint endLayerIndex, int iterationsStart, int iterationsEnd, ref bool isFade, out float iterationSteps, out int maxIteration) @@ -452,7 +613,7 @@ namespace UVtools.Core List<uint> actionLayers = new(); bool checkedEmptyLayers = false; Mat emptyMat = new (); - Mat[] cachedLayers = new Mat[Count]; + Mat[] cachedLayers = new Mat[LayerCount]; const uint cacheCount = 300; bool IsIgnored(LayerIssue issue) => !(ignoredIssues is null) && ignoredIssues.Count > 0 && ignoredIssues.Contains(issue); @@ -466,7 +627,7 @@ namespace UVtools.Core Mat GetCachedMat(uint layerIndex) { if (cachedLayers[layerIndex] is not null) return cachedLayers[layerIndex]; - Parallel.For(layerIndex, Math.Min(layerIndex + cacheCount, Count), i => + Parallel.For(layerIndex, Math.Min(layerIndex + cacheCount, LayerCount), i => { if (this[i].IsEmpty) return; // empty layers cachedLayers[i] = this[i].LayerMat; @@ -481,7 +642,7 @@ namespace UVtools.Core ) { checkedEmptyLayers = true; - for (uint layerIndex = 0; layerIndex < Count; layerIndex++) + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { var layer = this[layerIndex]; if (layer.IsEmpty) @@ -500,7 +661,7 @@ namespace UVtools.Core if (!checkedEmptyLayers && emptyLayersConfig) { checkedEmptyLayers = true; - for (uint layerIndex = 0; layerIndex < Count; layerIndex++) + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { var layer = this[layerIndex]; if (layer.IsEmpty) @@ -522,11 +683,8 @@ namespace UVtools.Core var layer = this[layerIndex]; if (layer.IsEmpty) { - lock (progress.Mutex) - { - progress++; - } - + progress.LockAndIncrement(); + return; // Empty layer } @@ -614,10 +772,7 @@ namespace UVtools.Core } } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); for (; i < layerAdvance - 1; i++) @@ -673,7 +828,7 @@ namespace UVtools.Core if (islandConfig.Enabled || overhangConfig.Enabled || resinTrapConfig.Enabled || touchBoundConfig.Enabled || emptyLayersConfig) { - progress.Reset(OperationProgress.StatusIslands, Count); + progress.Reset(OperationProgress.StatusIslands, LayerCount); // Detect contours Parallel.ForEach(this, @@ -1071,7 +1226,7 @@ namespace UVtools.Core listHollowArea.Add(new LayerHollowArea(contours[i].ToArray(), rect, layer.Index == 0 || - layer.Index == Count - 1 // First and Last layers, always drains + layer.Index == LayerCount - 1 // First and Last layers, always drains ? LayerHollowArea.AreaType.Drain : LayerHollowArea.AreaType.Unknown)); @@ -1087,11 +1242,11 @@ namespace UVtools.Core if (resinTrapConfig.Enabled) { - progress.Reset(OperationProgress.StatusResinTraps, Count); + progress.Reset(OperationProgress.StatusResinTraps, LayerCount); - for (uint layerIndex = 1; layerIndex < Count - 1; layerIndex++) // First and Last layers, always drains + for (uint layerIndex = 1; layerIndex < LayerCount - 1; layerIndex++) // First and Last layers, always drains { if (progress.Token.IsCancellationRequested) break; if (!layerHollowAreas.TryGetValue(layerIndex, out var areas)) @@ -1130,13 +1285,13 @@ namespace UVtools.Core checkArea.Processed = true; nextLayerIndex += dir; - if (nextLayerIndex < 0 || nextLayerIndex >= Count) + if (nextLayerIndex < 0 || nextLayerIndex >= LayerCount) break; // Exhausted layers bool haveNextAreas = layerHollowAreas.TryGetValue((uint) nextLayerIndex, out var nextAreas); Dictionary<int, LayerHollowArea> intersectingAreas = new(); - progress.Reset(OperationProgress.StatusResinTraps, Count, (uint) nextLayerIndex); + progress.Reset(OperationProgress.StatusResinTraps, LayerCount, (uint) nextLayerIndex); using (var image = this[nextLayerIndex].LayerMat) { @@ -1289,7 +1444,7 @@ namespace UVtools.Core });*/ if (progress.Token.IsCancellationRequested) return result.OrderBy(issue => issue.Type).ThenBy(issue => issue.LayerIndex).ThenBy(issue => issue.PixelsCount).ToList(); - for (uint layerIndex = 0; layerIndex < Count; layerIndex++) + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { if (!layerHollowAreas.TryGetValue(layerIndex, out var list)) continue; if (list.Count == 0) continue; @@ -1454,34 +1609,22 @@ namespace UVtools.Core } progress.Reset("Saving", (uint) modifiedLayers.Count); - Parallel.ForEach(modifiedLayers, (modfiedLayer, state) => + Parallel.ForEach(modifiedLayers, (modifiedLayer, state) => { - this[modfiedLayer.Key].LayerMat = modfiedLayer.Value; - modfiedLayer.Value.Dispose(); + this[modifiedLayer.Key].LayerMat = modifiedLayer.Value; + modifiedLayer.Value.Dispose(); - lock (progress) - { - progress++; - } + progress.LockAndIncrement(); }); - /*foreach (var modfiedLayer in modfiedLayers) - { - this[modfiedLayer.Key].LayerMat = modfiedLayer.Value; - modfiedLayer.Value.Dispose(); - progress++; - }*/ - //pixelHistory.Clear(); } - - /// <summary> /// Set the IsModified property for all layers /// </summary> public void SetAllIsModified(bool isModified) { - for (uint i = 0; i < Count; i++) + for (uint i = 0; i < LayerCount; i++) { if(Layers[i] is null) continue; Layers[i].IsModified = isModified; @@ -1492,27 +1635,46 @@ namespace UVtools.Core /// Reallocate with new size /// </summary> /// <returns></returns> - public LayerManager ReallocateNew(uint newLayerCount, bool makeClone = false) + public Layer[] ReallocateNew(uint newLayerCount, bool makeClone = false) { - LayerManager layerManager = new LayerManager(newLayerCount, SlicerFile); - foreach (var layer in this) + var layers = new Layer[newLayerCount]; + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) { - if (layer.Index >= newLayerCount) break; - layerManager[layer.Index] = makeClone ? layer.Clone() : layer; + if (layerIndex >= newLayerCount) break; + var layer = this[layerIndex]; + layers[layerIndex] = makeClone && layer is not null ? layer.Clone() : layer; } - layerManager.BoundingRectangle = Rectangle.Empty; + return layers; + } - return layerManager; + /// <summary> + /// Reallocate layer count with a new size + /// </summary> + /// <param name="newLayerCount">New layer count</param> + /// <param name="initBlack"></param> + public void Reallocate(uint newLayerCount, bool initBlack = false) + { + var oldLayerCount = LayerCount; + int differenceLayerCount = (int)newLayerCount - Count; + if (differenceLayerCount == 0) return; + Array.Resize(ref _layers, (int) newLayerCount); + if (differenceLayerCount > 0 && initBlack) + { + Parallel.For(oldLayerCount, newLayerCount, layerIndex => + { + this[layerIndex] = new Layer((uint)layerIndex, EmguExtensions.InitMat(SlicerFile.Resolution), this); + }); + } } /// <summary> - /// Reallocate with add size + /// Reallocate at given index /// </summary> /// <returns></returns> - public void Reallocate(uint insertAtLayerIndex, uint layerCount, bool initBlack = false) + public void ReallocateInsert(uint insertAtLayerIndex, uint layerCount, bool initBlack = false) { - var newLayers = new Layer[Count + layerCount]; + var newLayers = new Layer[LayerCount + layerCount]; // Rearrange for (uint layerIndex = 0; layerIndex < insertAtLayerIndex; layerIndex++) @@ -1539,9 +1701,14 @@ namespace UVtools.Core { Layers[layerIndex] = initBlack ? new Layer(layerIndex, EmguExtensions.InitMat(SlicerFile.Resolution), this) : null; }*/ - Layers = newLayers; + _layers = newLayers; } + /// <summary> + /// Reallocate at a kept range + /// </summary> + /// <param name="startLayerIndex"></param> + /// <param name="endLayerIndex"></param> public void ReallocateRange(uint startLayerIndex, uint endLayerIndex) { if ((int)(endLayerIndex - startLayerIndex) < 0) return; @@ -1553,20 +1720,20 @@ namespace UVtools.Core newLayers[currentLayerIndex++] = _layers[layerIndex]; } - Layers = newLayers; + _layers = newLayers; } /// <summary> /// Reallocate at start /// </summary> /// <returns></returns> - public void ReallocateStart(uint layerCount, bool initBlack = false) => Reallocate(0, layerCount, initBlack); + public void ReallocateStart(uint layerCount, bool initBlack = false) => ReallocateInsert(0, layerCount, initBlack); /// <summary> /// Reallocate at end /// </summary> /// <returns></returns> - public void ReallocateEnd(uint layerCount, bool initBlack = false) => Reallocate(Count, layerCount, initBlack); + public void ReallocateEnd(uint layerCount, bool initBlack = false) => ReallocateInsert(LayerCount, layerCount, initBlack); /// <summary> /// Clone this object @@ -1574,16 +1741,26 @@ namespace UVtools.Core /// <returns></returns> public LayerManager Clone() { - LayerManager layerManager = new LayerManager(Count, SlicerFile); - foreach (var layer in this) + LayerManager layerManager = new(SlicerFile); + layerManager.Init(CloneLayers()); + /*foreach (var layer in this) { layerManager[layer.Index] = layer.Clone(); - } + }*/ layerManager.BoundingRectangle = BoundingRectangle; return layerManager; } + /// <summary> + /// Clone layers + /// </summary> + /// <returns></returns> + public Layer[] CloneLayers() + { + return Layer.CloneLayers(_layers); + } + public void Dispose() { } #endregion @@ -1592,11 +1769,9 @@ namespace UVtools.Core public override string ToString() { - return $"{nameof(BoundingRectangle)}: {BoundingRectangle}, {nameof(Count)}: {Count}, {nameof(IsModified)}: {IsModified}"; + return $"{nameof(BoundingRectangle)}: {BoundingRectangle}, {nameof(LayerCount)}: {LayerCount}, {nameof(IsModified)}: {IsModified}"; } #endregion - - } } diff --git a/UVtools.Core/Managers/ClipboardManager.cs b/UVtools.Core/Managers/ClipboardManager.cs index cb851fa..a436625 100644 --- a/UVtools.Core/Managers/ClipboardManager.cs +++ b/UVtools.Core/Managers/ClipboardManager.cs @@ -9,17 +9,17 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Drawing; +using System.Diagnostics; +using System.Linq; using UVtools.Core.FileFormats; using UVtools.Core.Objects; namespace UVtools.Core.Managers { - public sealed class ClipboardItem : IList<Layer> + public sealed class ClipboardItem : List<Layer> { #region Properties - private readonly List<Layer> _layers = new(); - + /// <summary> /// Gets the LayerCount for this clip /// </summary> @@ -32,62 +32,36 @@ namespace UVtools.Core.Managers /// </summary> public string Description { get; set; } + public bool IsFullBackup { get; set; } + public Operations.Operation Operation { get; set; } #endregion #region Constructor - public ClipboardItem(FileFormat slicerFile, Operations.Operation operation) : this(slicerFile) + public ClipboardItem(FileFormat slicerFile, Operations.Operation operation, bool isFullBackup = false) : this(slicerFile) { Operation = operation; string description = operation.ToString(); if (!description.StartsWith(operation.Title)) description = $"{operation.Title}: {description}"; Description = description; + IsFullBackup = isFullBackup; } - public ClipboardItem(FileFormat slicerFile, string description = null) + public ClipboardItem(FileFormat slicerFile, string description = null, bool isFullBackup = false) { LayerCount = slicerFile.LayerCount; LayerHeight = slicerFile.LayerHeight; Description = description; - } - #endregion - - #region List Implementation - public IEnumerator<Layer> GetEnumerator() => _layers.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - public void Add(Layer item) => _layers.Add(item); - - public void Clear() => _layers.Clear(); - - public bool Contains(Layer item) => _layers.Contains(item); - - public void CopyTo(Layer[] array, int arrayIndex) => _layers.CopyTo(array, arrayIndex); - - public bool Remove(Layer item) => _layers.Remove(item); - - public int Count => _layers.Count; - public bool IsReadOnly => false; - public int IndexOf(Layer item) => _layers.IndexOf(item); - - public void Insert(int index, Layer item) => _layers.Insert(index, item); - - public void RemoveAt(int index) => _layers.RemoveAt(index); - - public Layer this[int index] - { - get => _layers[index]; - set => _layers[index] = value; + IsFullBackup = isFullBackup; } #endregion #region Methods public override string ToString() { - return $"{Description} ({Count})"; + return $"{(IsFullBackup ? "* " : "")}{Description} ({Count})"; } #endregion } @@ -96,12 +70,12 @@ namespace UVtools.Core.Managers { #region Properties - public ObservableCollection<ClipboardItem> Items { get; } = new ObservableCollection<ClipboardItem>(); + public ObservableCollection<ClipboardItem> Items { get; } = new(); public FileFormat SlicerFile { get; set; } private int _currentIndex = -1; - private LayerManager _snapshotLayerManager; + private Layer[] _snapshotLayers; private bool _reallocatedLayerCount; private bool _suppressRestore; @@ -121,27 +95,42 @@ namespace UVtools.Core.Managers if (value >= 0 && !SuppressRestore) { + ReallocatedLayerCount = false; int dir = oldIndex < _currentIndex ? 1 : -1; for (int i = oldIndex + dir; i >= 0 && i < Count; i += dir) { - var layerManager = SlicerFile.LayerManager; var clip = this[i]; - if (layerManager.Count != clip.LayerCount) // Need resize layer manager + + Layer[] layers; + if (clip.IsFullBackup) { - layerManager = layerManager.ReallocateNew(clip.LayerCount); - ReallocatedLayerCount = true; + if(!_reallocatedLayerCount && SlicerFile.LayerCount != clip.Count) ReallocatedLayerCount = true; + layers = clip.ToArray(); + } + else + { + layers = SlicerFile.LayerManager.Layers.ToArray(); + + if (SlicerFile.LayerCount != clip.LayerCount) // Need resize layer manager + { + //layers = SlicerFile.LayerManager.ReallocateNew(clip.LayerCount); + layers = SlicerFile.LayerManager.ReallocateNew(clip.LayerCount); + ReallocatedLayerCount = true; + } + + foreach (var layer in clip) + { + layers[layer.Index] = layer; + } } - layerManager.AddLayers(clip); - - layerManager.BoundingRectangle = Rectangle.Empty; if (SlicerFile.LayerHeight != clip.LayerHeight) { SlicerFile.LayerHeight = clip.LayerHeight; } - SlicerFile.LayerManager = layerManager.Clone(); + SlicerFile.LayerManager.Layers = Layer.CloneLayers(layers); if (i == _currentIndex) break; } } @@ -168,10 +157,10 @@ namespace UVtools.Core.Managers set => RaiseAndSetIfChanged(ref _reallocatedLayerCount, value); } - public LayerManager SnapshotLayerManager + public Layer[] SnapshotLayers { - get => _snapshotLayerManager; - private set => RaiseAndSetIfChanged(ref _snapshotLayerManager, value); + get => _snapshotLayers; + private set => RaiseAndSetIfChanged(ref _snapshotLayers, value); } public ClipboardItem CurrentClip => _currentIndex < 0 || _currentIndex >= Count ? null : this[_currentIndex]; @@ -183,7 +172,7 @@ namespace UVtools.Core.Managers #region Singleton private static readonly Lazy<ClipboardManager> InstanceHolder = - new Lazy<ClipboardManager>(() => new ClipboardManager()); + new(() => new ClipboardManager()); public static ClipboardManager Instance => InstanceHolder.Value; #endregion @@ -241,6 +230,24 @@ namespace UVtools.Core.Managers #region Methods + public void SuppressRestoreWork(Action action) + { + try + { + SuppressRestore = true; + action.Invoke(); + } + catch (Exception e) + { + Debug.WriteLine(e); + throw; + } + finally + { + SuppressRestore = false; + } + } + /// <summary> /// Clears the manager /// </summary> @@ -252,57 +259,91 @@ namespace UVtools.Core.Managers /// <param name="slicerFile"></param> public void Init(FileFormat slicerFile) { - Clear(); - SlicerFile = slicerFile; - if(slicerFile is null) return; - var clip = new ClipboardItem(SlicerFile, "Original layers"); - for (int layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) + SuppressRestoreWork(() => { - clip.Add(SlicerFile[layerIndex].Clone()); - } - - Add(clip); - slicerFile.SuppressRebuildPropertiesWork(() => CurrentIndex = 0); + Clear(); + SlicerFile = slicerFile; + if (slicerFile is null) return; + var clip = new ClipboardItem(SlicerFile, "Original layers", true); + clip.AddRange(SlicerFile.LayerManager.CloneLayers()); + Add(clip); + slicerFile.SuppressRebuildPropertiesWork(() => CurrentIndex = 0); + }); } /// <summary> /// Snapshot layers and prepare manager to collect modified layers with <see cref="Clip"/> /// </summary> - public void Snapshot(LayerManager layerManager = null) + public void Snapshot(Layer[] layers = null) + { + SnapshotLayers = layers ?? SlicerFile.LayerManager.CloneLayers(); + } + + public void RestoreSnapshot() { - SnapshotLayerManager = layerManager ?? SlicerFile.LayerManager.Clone(); + if (_snapshotLayers is null) return; + SlicerFile.LayerManager.Layers = _snapshotLayers; + SnapshotLayers = null; } /// <summary> /// Collect differences and create a clip /// </summary> - public ClipboardItem Clip(string description = null, LayerManager layerManagerSnapshot = null) + public ClipboardItem Clip(string description = null, Layer[] layers = null, bool doFullBackup = false) { - if(!(layerManagerSnapshot is null)) Snapshot(layerManagerSnapshot); - if (SnapshotLayerManager is null) throw new InvalidOperationException("A snapshot is required before perform a clip"); - var clip = new ClipboardItem(SlicerFile, description); - - int layerIndex = 0; - for (; - layerIndex < SlicerFile.LayerCount - && layerIndex < SnapshotLayerManager.Count - ; layerIndex++) + ClipboardItem safeClip = null; + if (!doFullBackup) { - //if(SnapshotLayerManager.Count - 1 < layerIndex) break; - if(SnapshotLayerManager[layerIndex].Equals(SlicerFile[layerIndex])) continue; - - clip.Add(SlicerFile[layerIndex].Clone()); + if (layers is not null) Snapshot(layers); + if (_snapshotLayers is null) throw new InvalidOperationException("A snapshot is required before perform a clip"); + + if (_snapshotLayers.Length != SlicerFile.LayerCount) + { + doFullBackup = true; // Force full backup when layer count changes + if (Count > 0 && !this[0].IsFullBackup) + { + safeClip = new ClipboardItem(SlicerFile, "Fail-safe full backup", true); + safeClip.AddRange(_snapshotLayers); + } + + //Insert(0, safeClip); + } } - // Collect leftovers from snapshot - // This happens when current layer manager has less layers then the snapshot/previous - // So we need to preserve them - for (; layerIndex < SlicerFile.LayerCount; layerIndex++) + var clip = new ClipboardItem(SlicerFile, description, doFullBackup); + + if (doFullBackup) { - clip.Add(SlicerFile[layerIndex].Clone()); + clip.AddRange(SlicerFile.LayerManager.CloneLayers()); } + else + { + int layerIndex = 0; + for (; + layerIndex < SlicerFile.LayerCount + && layerIndex < _snapshotLayers.Length; + layerIndex++) + { + //if(SnapshotLayers.Count - 1 < layerIndex) break; + if (_snapshotLayers[layerIndex].Equals(SlicerFile[layerIndex])) continue; + clip.Add(SlicerFile[layerIndex].Clone()); + } - SnapshotLayerManager = null; + // Collect leftovers from snapshot + // This happens when current state has less layers then the snapshot/previous + // So we need to preserve them + for (; layerIndex < SlicerFile.LayerCount; layerIndex++) + { + clip.Add(SlicerFile[layerIndex].Clone()); + } + + if (clip.Count == SlicerFile.LayerCount) + { + clip.IsFullBackup = true; + } + } + + SnapshotLayers = null; if (clip.Count == 0) { @@ -310,33 +351,38 @@ namespace UVtools.Core.Managers return null; } - SuppressRestore = true; - var oldCurrentIndex = _currentIndex; - CurrentIndex = -1; - - // Remove all redo's for integrity - for (int i = oldCurrentIndex - 1; i >= 0; i--) + SuppressRestoreWork(() => { - RemoveAt(i); - } + var oldCurrentIndex = _currentIndex; + CurrentIndex = -1; - - Insert(0, clip); - - CurrentIndex = 0; - SuppressRestore = false; + // Remove all redo's for integrity + for (int i = oldCurrentIndex - 1; i >= 0; i--) + { + //if(this[i].IsFullBackup) continue; // Full backups can survive + RemoveAt(i); + } + + if (safeClip is not null) + { + Insert(0, safeClip); + } + Insert(0, clip); + CurrentIndex = 0; + }); + return clip; } /// <summary> /// Collect differences and create a clip /// </summary> - public ClipboardItem Clip(Operations.Operation operation, LayerManager layerManagerSnapshot = null) + public ClipboardItem Clip(Operations.Operation operation, Layer[] layersSnapshot = null, bool doFullBackup = false) { string description = operation.ToString(); if (!description.StartsWith(operation.Title)) description = $"{operation.Title}: {description}"; - var clip = Clip(description, layerManagerSnapshot); + var clip = Clip(description, layersSnapshot, doFullBackup); if (clip is null) return null; clip.Operation = operation; return clip; @@ -357,38 +403,6 @@ namespace UVtools.Core.Managers public void SetToOldest() => CurrentIndex = Count-1; public void SetToNewest() => CurrentIndex = 0; - /*public bool SetTo(int index) - { - if (index == _currentIndex || index < 0 || index >= Count) return false; - bool changed = false; - int dir = _currentIndex < index ? 1 : -1; - - for (int i = _currentIndex+dir; i >= 0 && i < Count; i+=dir) - { - var layerManager = SlicerFile.LayerManager; - var clip = this[i]; - if (layerManager.Count != clip.LayerCount) // Need resize layer manager - { - layerManager = layerManager.Reallocate(clip.LayerCount); - ReallocatedLayerCount = true; - } - - layerManager.AddLayers(clip); - - layerManager.BoundingRectangle = Rectangle.Empty; - SlicerFile.LayerManager = layerManager.Clone(); - changed = true; - if (i == index) break; - } - - if (changed) - { - CurrentIndex = index; - } - - return changed; - }*/ - #endregion } } diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs index ff0acaf..b68f447 100644 --- a/UVtools.Core/Operations/OperationBlur.cs +++ b/UVtools.Core/Operations/OperationBlur.cs @@ -144,10 +144,7 @@ namespace UVtools.Core.Operations Execute(mat); SlicerFile[layerIndex].LayerMat = mat; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs index e588b19..db0490e 100644 --- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs +++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs @@ -420,10 +420,7 @@ namespace UVtools.Core.Operations { newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile.LayerManager) {IsModified = true}; layers[layerIndex].Dispose(); - lock (progress) - { - progress++; - } + progress.LockAndIncrement(); }); diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs index 13ad30d..e73314a 100644 --- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs +++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs @@ -732,10 +732,7 @@ namespace UVtools.Core.Operations { newLayers[layerIndex] = new Layer((uint)layerIndex, layers[layerIndex], SlicerFile.LayerManager) {IsModified = true}; layers[layerIndex].Dispose(); - lock (progress) - { - progress++; - } + progress.LockAndIncrement(); }); if (SlicerFile.ThumbnailsCount > 0) diff --git a/UVtools.Core/Operations/OperationChangeResolution.cs b/UVtools.Core/Operations/OperationChangeResolution.cs index 31a1883..c021753 100644 --- a/UVtools.Core/Operations/OperationChangeResolution.cs +++ b/UVtools.Core/Operations/OperationChangeResolution.cs @@ -173,10 +173,7 @@ namespace UVtools.Core.Operations //Execute(mat); SlicerFile[layerIndex].LayerMat = matDst; - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); progress.Token.ThrowIfCancellationRequested(); diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs index e2c55bb..2badeca 100644 --- a/UVtools.Core/Operations/OperationFlip.cs +++ b/UVtools.Core/Operations/OperationFlip.cs @@ -130,10 +130,7 @@ namespace UVtools.Core.Operations Execute(mat); SlicerFile[layerIndex].LayerMat = mat; - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs index 745750e..b07ba06 100644 --- a/UVtools.Core/Operations/OperationInfill.cs +++ b/UVtools.Core/Operations/OperationInfill.cs @@ -135,11 +135,8 @@ namespace UVtools.Core.Operations using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat, layerIndex); SlicerFile[layerIndex].LayerMat = mat; - - lock (progress.Mutex) - { - progress++; - } + + progress.LockAndIncrement(); }); return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationLayerClone.cs b/UVtools.Core/Operations/OperationLayerClone.cs index 9eca95c..5e82593 100644 --- a/UVtools.Core/Operations/OperationLayerClone.cs +++ b/UVtools.Core/Operations/OperationLayerClone.cs @@ -7,7 +7,9 @@ */ using System; +using System.Collections.Generic; using System.Drawing; +using System.Linq; using System.Text; using UVtools.Core.FileFormats; using UVtools.Core.Objects; @@ -88,13 +90,30 @@ namespace UVtools.Core.Operations protected override bool ExecuteInternally(OperationProgress progress) { - var oldLayers = SlicerFile.LayerManager.Layers; + uint totalClones = (LayerIndexEnd - LayerIndexStart + 1) * Clones; + progress.Reset(ProgressAction, totalClones); + + var newLayers = new Layer[SlicerFile.LayerCount + totalClones]; + uint newLayerIndex = 0; + for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) + { + newLayers[newLayerIndex++] = SlicerFile[layerIndex]; + if (layerIndex < LayerIndexStart || layerIndex > LayerIndexEnd) continue; + for (uint i = 0; i < Clones; i++) + { + newLayers[newLayerIndex++] = SlicerFile[layerIndex].Clone(); + progress++; + } + } + + SlicerFile.LayerManager.Layers = newLayers; + + /*var oldLayers = SlicerFile.LayerManager.Layers; uint totalClones = (LayerIndexEnd - LayerIndexStart + 1) * Clones; uint newLayerCount = (uint) (oldLayers.Length + totalClones); var layers = new Layer[newLayerCount]; - progress.Reset(ProgressAction, totalClones); uint newLayerIndex = 0; for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++) @@ -115,7 +134,7 @@ namespace UVtools.Core.Operations newLayerIndex++; } - SlicerFile.LayerManager.Layers = layers; + SlicerFile.LayerManager.Layers = layers;*/ return !progress.Token.IsCancellationRequested; } diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs index c3be204..e77fc49 100644 --- a/UVtools.Core/Operations/OperationLayerImport.cs +++ b/UVtools.Core/Operations/OperationLayerImport.cs @@ -245,7 +245,7 @@ namespace UVtools.Core.Operations { progress.Reset("Packing images", (uint)keyImage.Count); SL1File format = new(); - format.LayerManager = new LayerManager((uint)keyImage.Count, format); + format.LayerManager.Init((uint)keyImage.Count); Parallel.ForEach(keyImage, pair => { @@ -254,10 +254,7 @@ namespace UVtools.Core.Operations if (pair.Key == 0) format.Resolution = mat.Size; format[pair.Key] = new Layer(pair.Key, mat, format); - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); progress.Token.ThrowIfCancellationRequested(); @@ -306,7 +303,7 @@ namespace UVtools.Core.Operations if (SlicerFile.Resolution != fileFormat.Resolution && (SlicerFile.Resolution.Width < fileFormatBoundingRectangle.Width || SlicerFile.Resolution.Height < fileFormatBoundingRectangle.Height)) continue; - SlicerFile.LayerManager.Reallocate(_startLayerIndex, fileFormat.LayerCount); + SlicerFile.LayerManager.ReallocateInsert(_startLayerIndex, fileFormat.LayerCount); break; case ImportTypes.Replace: case ImportTypes.Stack: @@ -472,11 +469,7 @@ namespace UVtools.Core.Operations } - lock (progress.Mutex) - { - lastProcessedLayerIndex = Math.Max(lastProcessedLayerIndex, (int)layerIndex); - progress++; - } + progress.LockAndIncrement(); }); fileFormat.Dispose(); diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs index fd45ac4..17b70db 100644 --- a/UVtools.Core/Operations/OperationLayerReHeight.cs +++ b/UVtools.Core/Operations/OperationLayerReHeight.cs @@ -136,23 +136,21 @@ namespace UVtools.Core.Operations { progress.ItemCount = Item.LayerCount; - var oldLayers = SlicerFile.LayerManager.Layers; - var layers = new Layer[Item.LayerCount]; uint newLayerIndex = 0; - for (uint layerIndex = 0; layerIndex < oldLayers.Length; layerIndex++) + for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) { progress.Token.ThrowIfCancellationRequested(); - var oldLayer = oldLayers[layerIndex]; + var oldLayer = SlicerFile[layerIndex]; if (Item.IsDivision) { for (byte i = 0; i < Item.Modifier; i++) { var newLayer = oldLayer.Clone(); - newLayer.Index = newLayerIndex; - newLayer.PositionZ = (float)(Item.LayerHeight * (newLayerIndex + 1)); + //newLayer.Index = newLayerIndex; + //newLayer.PositionZ = (float)(Item.LayerHeight * (newLayerIndex + 1)); layers[newLayerIndex] = newLayer; newLayerIndex++; progress++; @@ -160,16 +158,16 @@ namespace UVtools.Core.Operations } else { - using var mat = oldLayers[layerIndex++].LayerMat; + using var mat = SlicerFile[layerIndex++].LayerMat; for (byte i = 1; i < Item.Modifier; i++) { - using var nextMat = oldLayers[layerIndex++].LayerMat; + using var nextMat = SlicerFile[layerIndex++].LayerMat; CvInvoke.Add(mat, nextMat, mat); } var newLayer = oldLayer.Clone(); - newLayer.Index = newLayerIndex; - newLayer.PositionZ = (float)(Item.LayerHeight * (newLayerIndex + 1)); + //newLayer.Index = newLayerIndex; + //newLayer.PositionZ = (float)(Item.LayerHeight * (newLayerIndex + 1)); newLayer.LayerMat = mat; layers[newLayerIndex] = newLayer; newLayerIndex++; @@ -178,8 +176,8 @@ namespace UVtools.Core.Operations } } - SlicerFile.LayerManager.Layers = layers; SlicerFile.LayerHeight = (float)Item.LayerHeight; + SlicerFile.LayerManager.Layers = layers; return !progress.Token.IsCancellationRequested; } diff --git a/UVtools.Core/Operations/OperationLayerRemove.cs b/UVtools.Core/Operations/OperationLayerRemove.cs index 406a24f..b815c7f 100644 --- a/UVtools.Core/Operations/OperationLayerRemove.cs +++ b/UVtools.Core/Operations/OperationLayerRemove.cs @@ -73,9 +73,17 @@ namespace UVtools.Core.Operations progress ??= new OperationProgress(false); - progress.Reset("removed layers", (uint)layersRemove.Count); + progress.Reset("Removed layers", (uint)layersRemove.Count); - var oldLayers = slicerFile.LayerManager.Layers; + foreach (var layerIndex in layersRemove) + { + slicerFile[layerIndex] = null; + progress++; + } + + slicerFile.LayerManager.RemoveNulls(); + + /*var oldLayers = slicerFile.LayerManager.Layers; var layerHeight = slicerFile.LayerHeight; var layers = new Layer[oldLayers.Length - layersRemove.Count]; @@ -109,7 +117,9 @@ namespace UVtools.Core.Operations progress++; } - slicerFile.LayerManager.Layers = layers; + slicerFile.LayerManager.Layers = layers;*/ + + return true; } diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs index 809fdbb..4b43b5a 100644 --- a/UVtools.Core/Operations/OperationMask.cs +++ b/UVtools.Core/Operations/OperationMask.cs @@ -81,10 +81,7 @@ namespace UVtools.Core.Operations using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); SlicerFile[layerIndex].LayerMat = mat; - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs index bc5845b..c3120cf 100644 --- a/UVtools.Core/Operations/OperationMorph.cs +++ b/UVtools.Core/Operations/OperationMorph.cs @@ -168,11 +168,8 @@ namespace UVtools.Core.Operations using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat, iterations); SlicerFile[layerIndex].LayerMat = mat; - - lock (progress.Mutex) - { - progress++; - } + + progress.LockAndIncrement(); }); return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs index 27ed2c2..b2cbacf 100644 --- a/UVtools.Core/Operations/OperationMove.cs +++ b/UVtools.Core/Operations/OperationMove.cs @@ -287,10 +287,7 @@ namespace UVtools.Core.Operations SlicerFile[layerIndex].LayerMat = mat; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); SlicerFile.LayerManager.BoundingRectangle = Rectangle.Empty; diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs index 432aa95..22398b3 100644 --- a/UVtools.Core/Operations/OperationPattern.cs +++ b/UVtools.Core/Operations/OperationPattern.cs @@ -293,10 +293,7 @@ namespace UVtools.Core.Operations //Execute(mat); SlicerFile[layerIndex].LayerMat = dstLayer; - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); SlicerFile.LayerManager.BoundingRectangle = Rectangle.Empty; diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs index 95341a3..6f92bf0 100644 --- a/UVtools.Core/Operations/OperationPixelDimming.cs +++ b/UVtools.Core/Operations/OperationPixelDimming.cs @@ -7,6 +7,7 @@ */ using System; +using System.Diagnostics; using System.Drawing; using System.Text; using System.Threading.Tasks; @@ -58,7 +59,8 @@ namespace UVtools.Core.Operations "1) Reduced layer expansion for large layer objects\n" + "2) Reduced cross layer exposure\n" + "3) Extended pixel life of the LCD\n\n" + - "NOTE: Run this tool only after repairs and all other transformations."; + "NOTE: Run this tool only after repairs and all other transformations.\n" + + "To create your own patterns: www.piskelapp.com"; public override string ConfirmationText => $"dim pixels from layers {LayerIndexStart} through {LayerIndexEnd}?"; @@ -85,11 +87,10 @@ namespace UVtools.Core.Operations foreach (var item in stringMatrix) { if (string.IsNullOrWhiteSpace(item.Text)) continue; - var lines = item.Text.Split('\n'); + var lines = item.Text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); for (var row = 0; row < lines.Length; row++) { - - var bytes = lines[row].Trim().Split(' '); + var bytes = lines[row].Split(' '); if (row == 0) { item.Pattern = new Matrix<byte>(lines.Length, bytes.Length); @@ -272,7 +273,52 @@ namespace UVtools.Core.Operations public bool IsAlternatePattern(uint layerIndex) => !IsNormalPattern(layerIndex); - + public unsafe void LoadPatternFromImage(Mat mat, bool isAlternatePattern = false) + { + var result = new string[mat.Height]; + var span = mat.GetBytePointer(); + Parallel.For(0, mat.Height, y => + { + result[y] = string.Empty; + for (int x = 0; x < mat.Width; x++) + { + result[y] += $"{span[mat.GetPixelPos(x, y)]} "; + } + + result[y] = result[y].Trim(); + }); + + StringBuilder sb = new(); + foreach (var s in result) + { + sb.AppendLine(s); + } + + if (isAlternatePattern) + { + AlternatePatternText = sb.ToString(); + } + else + { + PatternText = sb.ToString(); + } + } + + public void LoadPatternFromImage(string filepath, bool isAlternatePattern = false) + { + try + { + using var mat = CvInvoke.Imread(filepath, ImreadModes.Grayscale); + LoadPatternFromImage(mat, isAlternatePattern); + } + catch (Exception e) + { + Debug.WriteLine(e); + } + + } + + public void GeneratePixelDimming(string pattern) { if (pattern == "Chessboard") diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs index 027243e..0da2586 100644 --- a/UVtools.Core/Operations/OperationProgress.cs +++ b/UVtools.Core/Operations/OperationProgress.cs @@ -32,7 +32,7 @@ namespace UVtools.Core.Operations public const string StatusResinTraps = "Layers processed (Resin traps)"; public const string StatusRepairLayers = "Repaired Layers"; - public object Mutex = new(); + public readonly object Mutex = new(); public CancellationTokenSource TokenSource { get; } = new(); public CancellationToken Token => TokenSource.Token; @@ -104,7 +104,7 @@ namespace UVtools.Core.Operations set { //_processedItems = value; - RaiseAndSetIfChanged(ref _processedItems, value); + if(!RaiseAndSetIfChanged(ref _processedItems, value)) return; RaisePropertyChanged(nameof(ProgressPercent)); RaisePropertyChanged(nameof(Description)); } @@ -189,10 +189,14 @@ $"{_processedItems.ToString().PadLeft(_itemCount.ToString().Length, '0')}/{_item public void LockAndIncrement() { - lock (Mutex) + /*lock (Mutex) { ProcessedItems++; - } + }*/ + Interlocked.Increment(ref _processedItems); + RaisePropertyChanged(nameof(ProcessedItems)); + RaisePropertyChanged(nameof(ProgressPercent)); + RaisePropertyChanged(nameof(Description)); } } } diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs index 8a7c3f1..ffbfe6e 100644 --- a/UVtools.Core/Operations/OperationRaftRelief.cs +++ b/UVtools.Core/Operations/OperationRaftRelief.cs @@ -271,10 +271,7 @@ namespace UVtools.Core.Operations ApplyMask(original, result); SlicerFile[layerIndex].LayerMat = result; - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs index 06ae5ff..26e6630 100644 --- a/UVtools.Core/Operations/OperationRedrawModel.cs +++ b/UVtools.Core/Operations/OperationRedrawModel.cs @@ -244,10 +244,7 @@ namespace UVtools.Core.Operations SlicerFile[fullMatLayerIndex].LayerMat = fullMat; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs index 68c84b0..f1824b6 100644 --- a/UVtools.Core/Operations/OperationRepairLayers.cs +++ b/UVtools.Core/Operations/OperationRepairLayers.cs @@ -182,10 +182,7 @@ namespace UVtools.Core.Operations bytes[image.GetPixelPos(issuePixel)] = 0; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); } var nextLayerIndex = group.Key + 1; @@ -287,10 +284,7 @@ namespace UVtools.Core.Operations image.Dispose(); } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); } diff --git a/UVtools.Core/Operations/OperationResize.cs b/UVtools.Core/Operations/OperationResize.cs index 1cc1105..ec81b4d 100644 --- a/UVtools.Core/Operations/OperationResize.cs +++ b/UVtools.Core/Operations/OperationResize.cs @@ -190,10 +190,7 @@ namespace UVtools.Core.Operations } } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); if (newX == 1.0m && newY == 1.0m) return; diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs index e8641c9..9498246 100644 --- a/UVtools.Core/Operations/OperationRotate.cs +++ b/UVtools.Core/Operations/OperationRotate.cs @@ -90,10 +90,7 @@ namespace UVtools.Core.Operations using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); SlicerFile[layerIndex].LayerMat = mat; - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs index 566cc5b..9a97e1b 100644 --- a/UVtools.Core/Operations/OperationSolidify.cs +++ b/UVtools.Core/Operations/OperationSolidify.cs @@ -68,7 +68,7 @@ namespace UVtools.Core.Operations public override string ToString() { - var result = $"[Area: ={_areaCheckType} than {_minimumArea}px²]" + LayerRangeString; + var result = $"[Area: {_areaCheckType} than {_minimumArea}px²]" + LayerRangeString; if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; return result; } @@ -93,10 +93,7 @@ namespace UVtools.Core.Operations using var mat = SlicerFile[layerIndex].LayerMat; Execute(mat); SlicerFile[layerIndex].LayerMat = mat; - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs index 9768b23..d516b8e 100644 --- a/UVtools.Core/Operations/OperationThreshold.cs +++ b/UVtools.Core/Operations/OperationThreshold.cs @@ -90,10 +90,7 @@ namespace UVtools.Core.Operations SlicerFile[layerIndex].LayerMat = mat; } - lock (progress.Mutex) - { - progress++; - } + progress.LockAndIncrement(); }); return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 67e3169..5f4d166 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description> - <Version>2.7.1</Version> + <Version>2.7.2</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> @@ -46,7 +46,7 @@ <ItemGroup> <PackageReference Include="BinarySerializer" Version="8.6.0" /> <PackageReference Include="Emgu.CV" Version="4.5.1.4349" /> - <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> + <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.10" /> <PackageReference Include="System.Memory" Version="4.5.4" /> <PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" /> diff --git a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml index 8ec27a5..6128e4f 100644 --- a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml @@ -68,28 +68,43 @@ </StackPanel> <Grid - RowDefinitions="200,10,Auto" - ColumnDefinitions="450,10,450" - > + RowDefinitions="Auto,200,10,Auto" + ColumnDefinitions="450,10,450"> + + <Button Grid.Row="0" Grid.Column="0" + Content="Load pattern from image" + HorizontalContentAlignment="Center" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" + Command="{Binding LoadPatternFromImage}" + CommandParameter="False"/> + <TextBox + Grid.Row="1" Grid.Column="0" AcceptsReturn="True" Watermark="Pattern" UseFloatingWatermark="True" TextWrapping="NoWrap" - Text="{Binding Operation.PatternText}" - /> + Text="{Binding Operation.PatternText}"/> + + <Button Grid.Row="0" Grid.Column="2" + Content="Load alternate pattern from image" + HorizontalContentAlignment="Center" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" + Command="{Binding LoadPatternFromImage}" + CommandParameter="True"/> <TextBox - Grid.Column="2" + Grid.Row="1" Grid.Column="2" AcceptsReturn="True" Watermark="Alternate pattern (Optional)" UseFloatingWatermark="True" TextWrapping="NoWrap" - Text="{Binding Operation.AlternatePatternText}" - /> + Text="{Binding Operation.AlternatePatternText}"/> <Border - Grid.Row="3" - BorderBrush="LightGray" + Grid.Row="3" Grid.Column="0" + BorderBrush="LightGray" BorderThickness="1" Padding="5" > @@ -195,7 +210,7 @@ </Border> <Border - Grid.Row="2" + Grid.Row="3" Grid.Column="2" BorderBrush="LightGray" BorderThickness="1" diff --git a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs index 4d09bb6..2d32651 100644 --- a/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolPixelDimmingControl.axaml.cs @@ -1,4 +1,5 @@ using System; +using Avalonia.Controls; using Avalonia.Markup.Xaml; using Emgu.CV; using UVtools.Core.Extensions; @@ -23,7 +24,17 @@ namespace UVtools.WPF.Controls.Tools { AvaloniaXamlLoader.Load(this); } - + public async void LoadPatternFromImage(bool isAlternatePattern = false) + { + var dialog = new OpenFileDialog + { + AllowMultiple = false, + Filters = Helpers.ImagesFileFilter, + }; + var files = await dialog.ShowAsync(ParentWindow); + if (files is null || files.Length == 0) return; + Operation.LoadPatternFromImage(files[0], isAlternatePattern); + } } } diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs index 2971ea6..5e9c513 100644 --- a/UVtools.WPF/MainWindow.Issues.cs +++ b/UVtools.WPF/MainWindow.Issues.cs @@ -111,7 +111,9 @@ namespace UVtools.WPF IsGUIEnabled = false; - + + Clipboard.Snapshot(); + var task = await Task.Factory.StartNew(() => { ShowProgressWindow("Removing selected issues"); @@ -160,7 +162,7 @@ namespace UVtools.WPF } } - progress++; + progress.LockAndIncrement(); }); if (layersRemove.Count > 0) @@ -181,7 +183,11 @@ namespace UVtools.WPF IsGUIEnabled = true; - if (!task) return; + if (!task) + { + Clipboard.RestoreSnapshot(); + return; + } var whiteListLayers = new List<uint>(); @@ -207,6 +213,8 @@ namespace UVtools.WPF } + Clipboard.Clip($"Manually removed {issueRemoveList.Count} issues"); + Issues.RemoveMany(issueRemoveList); if (layersRemove.Count > 0) diff --git a/UVtools.WPF/MainWindow.PixelEditor.cs b/UVtools.WPF/MainWindow.PixelEditor.cs index ea23b9a..e7984cd 100644 --- a/UVtools.WPF/MainWindow.PixelEditor.cs +++ b/UVtools.WPF/MainWindow.PixelEditor.cs @@ -387,6 +387,7 @@ namespace UVtools.WPF try { SlicerFile.LayerManager.DrawModifications(Drawings, ProgressWindow.RestartProgress()); + return true; } catch (OperationCanceledException) { @@ -405,11 +406,18 @@ namespace UVtools.WPF IsGUIEnabled = true; + if (!task.Result) + { + Clipboard.RestoreSnapshot(); + ShowLayer(); + return; + } + Clipboard.Clip($"Draw {Drawings.Count} modifications"); if (Settings.PixelEditor.PartialUpdateIslandsOnEditing) { - List<uint> whiteListLayers = new List<uint>(); + List<uint> whiteListLayers = new(); foreach (var item in Drawings) { /*if (item.OperationType != PixelOperation.PixelOperationType.Drawing && diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index bc680e6..6512bea 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -1060,7 +1060,7 @@ namespace UVtools.WPF } } - ClipboardManager.Instance.Init(SlicerFile); + Clipboard.Init(SlicerFile); if (SlicerFile is not ImageFile) { @@ -1466,11 +1466,11 @@ namespace UVtools.WPF IsGUIEnabled = false; - LayerManager backup = null; + Clipboard.Snapshot(); + var result = await Task.Factory.StartNew(() => { ShowProgressWindow(baseOperation.ProgressTitle); - backup = SlicerFile.LayerManager.Clone(); try { @@ -1478,7 +1478,6 @@ namespace UVtools.WPF } catch (OperationCanceledException) { - SlicerFile.LayerManager = backup; } catch (Exception ex) { @@ -1494,7 +1493,7 @@ namespace UVtools.WPF if (result) { - ClipboardManager.Instance.Clip(baseOperation, backup); + Clipboard.Clip(baseOperation); ShowLayer(); RefreshProperties(); @@ -1510,6 +1509,10 @@ namespace UVtools.WPF break; } } + else + { + Clipboard.RestoreSnapshot(); + } if (baseOperation.Tag is not null) { diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index f6ff058..bd98a45 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ <PackageLicenseFile>LICENSE</PackageLicenseFile> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryType>Git</RepositoryType> - <Version>2.7.1</Version> + <Version>2.7.2</Version> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> |