diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-04-18 02:18:42 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-04-18 02:18:42 +0300 |
commit | 6cfedea4cc36200caab87301e0dd121e97ddc8f2 (patch) | |
tree | 3325980560fc5bb3fa8befea1b827a2ca945e9ea | |
parent | 4dae750e83987fde8b34087d26fc75407e88f55a (diff) |
v2.9.1v2.9.1
* **File formats:**
* PhotonS: Implement the write/encode method to allow to use this format and fix the thumbnail
* VDT: Allow to auto convert the .vdt to the target printer format using the Machine - Notes, using a flag: FILEFORMAT_YourPrinterExtension, for example: FILEFORMAT_CTB
* (Fix) Unable to convert files with no thumbnails to other file format that requires thumbnails
* **Tools:**
* (Add) Re-height: Option to Anti-Aliasing layers
* (Fix) Morph and Blur: The combobox was not setting to the selected item when preform a redo operation (Ctrl+Shift+Z)
* **GUI:**
* (Change) Progress window to be a grid element inside MainWindow, this allow to reuse the graphics and its elements without the need of spawning a Window instance everytime a progress is shown, resulting in better performance and more fluid transaction
* (Improvement) Clear issues when generating calibration tests
42 files changed, 917 insertions, 601 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index ef2019c..063050d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 18/04/2021 - v2.9.1 + +* **File formats:** + * PhotonS: Implement the write/encode method to allow to use this format and fix the thumbnail + * VDT: Allow to auto convert the .vdt to the target printer format using the Machine - Notes, using a flag: FILEFORMAT_YourPrinterExtension, for example: FILEFORMAT_CTB + * (Fix) Unable to convert files with no thumbnails to other file format that requires thumbnails +* **Tools:** + * (Add) Re-height: Option to Anti-Aliasing layers + * (Fix) Morph and Blur: The combobox was not setting to the selected item when preform a redo operation (Ctrl+Shift+Z) +* **GUI:** + * (Change) Progress window to be a grid element inside MainWindow, this allow to reuse the graphics and its elements without the need of spawning a Window instance everytime a progress is shown, resulting in better performance and more fluid transaction + * (Improvement) Clear issues when generating calibration tests + ## 14/04/2021 - v2.9.0 * **File formats:** @@ -55,7 +55,7 @@ But also, i need victims for test subject. Proceed at your own risk! * SL1 (PrusaSlicer) * Zip (Chitubox) * Photon (Chitubox) -* Photons (Chitubox) [Read-only] +* Photons (Chitubox) * CBDDLP (Chitubox) * CBT (Chitubox) * PHZ (Chitubox) diff --git a/UVtools.Core/Extensions/BitExtensions.cs b/UVtools.Core/Extensions/BitExtensions.cs new file mode 100644 index 0000000..7df1876 --- /dev/null +++ b/UVtools.Core/Extensions/BitExtensions.cs @@ -0,0 +1,15 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ +namespace UVtools.Core.Extensions +{ + public static class BitExtensions + { + public static ushort ToUShortLittleEndian(byte byte1, byte byte2) => (ushort)(byte1 + (byte2 << 8)); + public static ushort ToUShortBigEndian(byte byte1, byte byte2) => (ushort)((byte1 << 8) + byte2); + } +} diff --git a/UVtools.Core/Extensions/EnumExtensions.cs b/UVtools.Core/Extensions/EnumExtensions.cs new file mode 100644 index 0000000..493418c --- /dev/null +++ b/UVtools.Core/Extensions/EnumExtensions.cs @@ -0,0 +1,39 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using UVtools.Core.Objects; + +namespace UVtools.Core.Extensions +{ + public static class EnumExtensions + { + public static string GetDescription(this Enum value) + { + var attributes = value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false); + if (attributes.Any()) + return (attributes.First() as DescriptionAttribute)?.Description; + + // If no description is found, the least we can do is replace underscores with spaces + // You can add your own custom default formatting logic here + var ti = CultureInfo.CurrentCulture.TextInfo; + return ti.ToTitleCase(ti.ToLower(value.ToString().Replace("_", " "))); + } + + public static IEnumerable<ValueDescription> GetAllValuesAndDescriptions(Type t) + { + if (!t.IsEnum) + throw new ArgumentException($"{nameof(t)} must be an enum type"); + + return Enum.GetValues(t).Cast<Enum>().Select(e => new ValueDescription(e, e.GetDescription())).ToList(); + } + } +} diff --git a/UVtools.Core/FileFormats/CXDLPFile.cs b/UVtools.Core/FileFormats/CXDLPFile.cs index 2e680a3..bd44c26 100644 --- a/UVtools.Core/FileFormats/CXDLPFile.cs +++ b/UVtools.Core/FileFormats/CXDLPFile.cs @@ -255,6 +255,7 @@ namespace UVtools.Core.FileFormats public string FooterValue { get; set; } = HEADER_VALUE; [FieldOrder(2)] + [FieldEndianness(Endianness.Big)] public uint Unknown { get; set; } = 7; public void Validate() diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 2fe8a04..9b05d2e 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -1241,6 +1241,7 @@ namespace UVtools.Core.FileFormats /// <param name="images"></param> public void SetThumbnails(Mat[] images) { + if (images is null || images.Length == 0) return; byte imageIndex = 0; for (int i = 0; i < ThumbnailsCount; i++) { diff --git a/UVtools.Core/FileFormats/LGSFile.cs b/UVtools.Core/FileFormats/LGSFile.cs index cb3d38d..9a3a0a6 100644 --- a/UVtools.Core/FileFormats/LGSFile.cs +++ b/UVtools.Core/FileFormats/LGSFile.cs @@ -431,6 +431,26 @@ namespace UVtools.Core.FileFormats return bytes; } + public unsafe Mat PreviewDecode(byte[] data) + { + Mat mat = new((int)HeaderSettings.PreviewSizeY, (int)HeaderSettings.PreviewSizeX, DepthType.Cv8U, 3); + var span = mat.GetBytePointer(); + int spanIndex = 0; + for (int i = 0; i < data.Length; i += 2) + { + ushort rgb15 = BitExtensions.ToUShortBigEndian(data[i], data[i + 1]); + byte r = (byte)((((rgb15 >> 11) & 0x1f) << 3) | 0x7); + byte g = (byte)((((rgb15 >> 6) & 0x1f) << 3) | 0x7); + byte b = (byte)((((rgb15 >> 0) & 0x1f) << 3) | 0x7); + + span[spanIndex++] = b; + span[spanIndex++] = g; + span[spanIndex++] = r; + } + + return mat; + } + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { if (ResolutionY >= 2560) // Longer Orange 30 @@ -474,26 +494,6 @@ namespace UVtools.Core.FileFormats Debug.WriteLine("-End-"); } - public unsafe Mat PreviewDecode(byte []data) - { - Mat mat = new Mat((int) HeaderSettings.PreviewSizeY, (int)HeaderSettings.PreviewSizeX, DepthType.Cv8U, 3); - var span = mat.GetBytePointer(); - int spanIndex = 0; - for (int i = 0; i < data.Length; i+=2) - { - ushort rgb15 = (ushort) ((ushort)(data[i + 0] << 8) | data[i + 1]); - byte r = (byte) ((((rgb15 >> 11) & 0x1f) << 3) | 0x7); - byte g = (byte) ((((rgb15 >> 6) & 0x1f) << 3) | 0x7); - byte b = (byte) ((((rgb15 >> 0) & 0x1f) << 3) | 0x7); - - span[spanIndex++] = b; - span[spanIndex++] = g; - span[spanIndex++] = r; - } - - return mat; - } - protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs index 784ad28..475150f 100644 --- a/UVtools.Core/FileFormats/MDLPFile.cs +++ b/UVtools.Core/FileFormats/MDLPFile.cs @@ -420,7 +420,7 @@ namespace UVtools.Core.FileFormats int spanIndex = 0; for (int i = 0; i < previews[previewIndex].Length; i += 2) { - ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]); + ushort rgb15 = BitExtensions.ToUShortBigEndian(previews[previewIndex][i], previews[previewIndex][i + 1]); byte r = (byte)((rgb15 >> 11) << 3); byte g = (byte)((rgb15 >> 5) << 2); byte b = (byte)((rgb15 >> 0) << 3); diff --git a/UVtools.Core/FileFormats/PhotonSFile.cs b/UVtools.Core/FileFormats/PhotonSFile.cs index e2391a7..6e6fe47 100644 --- a/UVtools.Core/FileFormats/PhotonSFile.cs +++ b/UVtools.Core/FileFormats/PhotonSFile.cs @@ -24,7 +24,7 @@ namespace UVtools.Core.FileFormats { public class PhotonSFile : FileFormat { - public const byte RLEEncodingLimit = 0x7f - 2; // 128; + public const byte RLEEncodingLimit = 128; #region Sub Classes @@ -37,7 +37,7 @@ namespace UVtools.Core.FileFormats public const float DisplayWidth = 68.04f; public const float DisplayHeight = 120.96f; - public const float BuildZ = 150f; + public const float BuildZ = 165f; public const uint TAG1 = 2; public const ushort TAG2 = 49; @@ -55,7 +55,7 @@ namespace UVtools.Core.FileFormats [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public double LiftSpeed { get; set; } // mm/s [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public double RetractSpeed { get; set; } // mm/s [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public double VolumeMl { get; set; } // ml - [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionX { get; set; } = 225; + [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionX { get; set; } = 224; [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public uint Unknown2 { get; set; } = 42; [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public uint PreviewResolutionY { get; set; } = 168; [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public uint Unknown4 { get; set; } = 10; @@ -88,7 +88,14 @@ namespace UVtools.Core.FileFormats [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint ResolutionX { get; set; } = 1440; [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public uint ResolutionY { get; set; } = 2560; [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DataSize { get; set; } - [Ignore] public uint RleDataSize => (DataSize >> 3) - 4; + [Ignore] public uint RleDataSize + { + get => (DataSize >> 3) - 4; + set => DataSize = (value + 4) << 3; + //get => DataSize / 8 - 4; + //set => DataSize = (value + 4) * 8; + } + [FieldOrder(6)] [FieldEndianness(Endianness.Big)] public uint Unknown5 { get; set; } = 2684702720; [Ignore] public byte[] EncodedRle { get; set; } @@ -100,33 +107,34 @@ namespace UVtools.Core.FileFormats public unsafe byte[] Encode(Mat mat) { - List<byte> rawData = new List<byte>(); + List<byte> rawData = new(); var spanMat = mat.GetBytePointer(); var imageLength = mat.GetLength(); int rep = 0; byte color = 0; + int totalPixels = 0; void AddRep() { if (rep <= 0) return; + + totalPixels += rep; rep--; - byte rle = - (byte) (((rep & 1) > 0 ? 128 : 0) | - ((rep & 2) > 0 ? 64 : 0) | - ((rep & 4) > 0 ? 32 : 0) | - ((rep & 8) > 0 ? 16 : 0) | - ((rep & 16) > 0 ? 8 : 0) | - ((rep & 32) > 0 ? 4 : 0) | - ((rep & 64) > 0 ? 2 : 0) | color); + byte rle = (byte) (((rep & 1) > 0 ? 128 : 0) | + ((rep & 2) > 0 ? 64 : 0) | + ((rep & 4) > 0 ? 32 : 0) | + ((rep & 8) > 0 ? 16 : 0) | + ((rep & 16) > 0 ? 8 : 0) | + ((rep & 32) > 0 ? 4 : 0) | + ((rep & 64) > 0 ? 2 : 0) | color); rawData.Add(rle); } for (int i = 0; i < imageLength; i++) { - //color = color <= 127 ? 0 : 255; // Sanitize no AA - byte thisColor = spanMat[i] <= 127 ? byte.MinValue : byte.MaxValue; // Sanitize no AA + byte thisColor = spanMat[i] <= 127 ? (byte)0 : (byte)1; // Sanitize no AA if (thisColor != color) { AddRep(); @@ -146,8 +154,13 @@ namespace UVtools.Core.FileFormats AddRep(); + if (totalPixels != imageLength) + { + throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {totalPixels} pixels."); + } + EncodedRle = rawData.ToArray(); - DataSize = (uint)(EncodedRle.Length * 8 + 4); + RleDataSize = (uint) EncodedRle.Length; return EncodedRle; } @@ -157,31 +170,43 @@ namespace UVtools.Core.FileFormats var matSpan = mat.GetBytePointer(); var imageLength = mat.GetLength(); - int pixel = 0; + uint pixel = 0; foreach (var run in EncodedRle) { - byte col = (byte) ((run & 0x01) * 255); - - var numPixelsInRun = - (((run & 128) > 0 ? 1 : 0) | - ((run & 64) > 0 ? 2 : 0) | - ((run & 32) > 0 ? 4 : 0) | - ((run & 16) > 0 ? 8 : 0) | - ((run & 8) > 0 ? 16 : 0) | - ((run & 4) > 0 ? 32 : 0) | - ((run & 2) > 0 ? 64 : 0)) + 1; - + byte brightness = (byte) ((run & 0x01) * 255); + + uint numPixelsInRun = + (uint) ((((run & 128) > 0 ? 1 : 0) | + ((run & 64) > 0 ? 2 : 0) | + ((run & 32) > 0 ? 4 : 0) | + ((run & 16) > 0 ? 8 : 0) | + ((run & 8) > 0 ? 16 : 0) | + ((run & 4) > 0 ? 32 : 0) | + ((run & 2) > 0 ? 64 : 0)) + 1); + + if (brightness == 0) // Don't fill black pixels + { + pixel += numPixelsInRun; + continue; + } + for (; numPixelsInRun > 0; numPixelsInRun--) { if (pixel > imageLength) { mat.Dispose(); - throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels"); + throw new FileLoadException($"Error image ran off the end, expecting {imageLength} pixels."); } - matSpan[pixel++] = col; + matSpan[pixel++] = brightness; } } + if (pixel != imageLength && pixel-1 != imageLength) + { + mat.Dispose(); + throw new FileLoadException($"Error image ran shortly or off the end, expecting {imageLength} pixels, got {pixel} pixels."); + } + // Not required as mat is all black by default //for (;pixel < imageLength; pixel++) matSpan[pixel] = 0; @@ -197,8 +222,8 @@ namespace UVtools.Core.FileFormats #region Properties - public Header HeaderSettings { get; protected internal set; } = new Header(); - public LayerHeader LayerSettings { get; protected internal set; } = new LayerHeader(); + public Header HeaderSettings { get; protected internal set; } = new(); + public LayerHeader LayerSettings { get; protected internal set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { @@ -221,7 +246,7 @@ namespace UVtools.Core.FileFormats }; - public override Size[] ThumbnailsOriginalSize { get; } = {new(225, 168) }; + public override Size[] ThumbnailsOriginalSize { get; } = {new(224, 168) }; public override uint ResolutionX { @@ -261,7 +286,7 @@ namespace UVtools.Core.FileFormats public override float LayerHeight { - get => (float) Math.Round(HeaderSettings.LayerHeight); + get => (float) Layer.RoundHeight(HeaderSettings.LayerHeight); set { HeaderSettings.LayerHeight = Layer.RoundHeight(value); @@ -269,6 +294,12 @@ namespace UVtools.Core.FileFormats } } + public override float MachineZ + { + get => Header.BuildZ; + set { } + } + public override uint LayerCount { get => base.LayerCount; @@ -293,7 +324,7 @@ namespace UVtools.Core.FileFormats set => base.ExposureTime = (float) (HeaderSettings.ExposureSeconds = Math.Round(value, 2)); } - public override float BottomLiftHeight => LightOffDelay; + public override float BottomLiftHeight => LiftHeight; public override float LiftHeight { @@ -306,21 +337,13 @@ namespace UVtools.Core.FileFormats public override float LiftSpeed { get => (float) Math.Round(HeaderSettings.LiftSpeed * 60.0, 2); - set - { - HeaderSettings.LiftSpeed = Math.Round(value / 60.0, 2); - base.LiftSpeed = value; - } + set => base.LiftSpeed = (float) (HeaderSettings.LiftSpeed = Math.Round(value / 60.0, 2)); } public override float RetractSpeed { get => (float)Math.Round(HeaderSettings.RetractSpeed * 60.0, 2); - set - { - HeaderSettings.RetractSpeed = (float) Math.Round(value / 60.0, 2); - base.RetractSpeed = value; - } + set => base.RetractSpeed = (float) (HeaderSettings.RetractSpeed = (float) Math.Round(value / 60.0, 2)); } public override float BottomLightOffDelay => LightOffDelay; @@ -365,14 +388,14 @@ namespace UVtools.Core.FileFormats int index = 0; for (int i = 0; i < imageLength; i+=3) { + byte r = span[i + 2]; // 60 + byte g = span[i + 1]; byte b = span[i]; - byte g = span[i+1]; - byte r = span[i+2]; - ushort rgb15 = (ushort) (((r >> 3) << 11) | ((g >> 3) << 6) | ((b >> 3) << 0)); + ushort color = (ushort)(((b & 0xF8) << 8) | ((g & 0xFC) << 3) | (r >> 3)); - bytes[index++] = (byte) (rgb15 >> 8); - bytes[index++] = (byte) (rgb15 & 0xff); + bytes[index++] = (byte)color; + bytes[index++] = (byte)(color >> 8); } if (index != bytes.Length) @@ -383,41 +406,62 @@ namespace UVtools.Core.FileFormats return bytes; } + public unsafe Mat PreviewDecode(byte[] data) + { + Mat mat = new((int)HeaderSettings.PreviewResolutionY, (int)HeaderSettings.PreviewResolutionX, DepthType.Cv8U, 3); + var span = mat.GetBytePointer(); + int spanIndex = 0; + for (int i = 0; i < data.Length; i += 2) + { + ushort color16 = BitExtensions.ToUShortLittleEndian(data[i], data[i + 1]); + + //var r = (byte)((color16 & 0x1F) << 3); + //var g = (byte)(((color16 >> 5) & 0x3F) << 2); + //var b = (byte)(((color16 >> 11) & 0x1f) << 3); + var r = (byte)((color16 << 3) & 0xF8); // Mask: 11111000 + var g = (byte)((color16 >> 3) & 0xFC); // Mask: 11111100 + var b = (byte)((color16 >> 8) & 0xF8); // Mask: 11111000 + + span[spanIndex++] = b; + span[spanIndex++] = g; + span[spanIndex++] = r; + } + + return mat; + } + protected override void EncodeInternally(string fileFullPath, OperationProgress progress) { - throw new NotSupportedException("PhotonS is read-only format, please use pws instead!"); + //throw new NotSupportedException("PhotonS is read-only format, please use pws instead!"); //uint currentOffset = (uint)Helpers.Serializer.SizeOf(HeaderSettings); - using (var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write)) - { - outputFile.WriteSerialize(HeaderSettings); - outputFile.WriteBytes(PreviewEncode(Thumbnails[0])); - LayerSettings.LayerCount = LayerCount; - outputFile.WriteSerialize(LayerSettings); + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + outputFile.WriteSerialize(HeaderSettings); + outputFile.WriteBytes(PreviewEncode(Thumbnails[0])); + outputFile.WriteSerialize(LayerSettings); - LayerData[] layerData = new LayerData[LayerCount]; + var layerData = new LayerData[LayerCount]; - Parallel.For(0, LayerCount, layerIndex => + Parallel.For(0, LayerCount, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using (var mat = this[layerIndex].LayerMat) { - if (progress.Token.IsCancellationRequested) return; - using (var mat = this[layerIndex].LayerMat) - { - layerData[layerIndex] = new LayerData(); - layerData[layerIndex].Encode(mat); - } + layerData[layerIndex] = new LayerData(); + layerData[layerIndex].Encode(mat); + } - progress.LockAndIncrement(); - }); + progress.LockAndIncrement(); + }); - progress.ItemName = "Saving layers"; - progress.ProcessedItems = 0; + progress.ItemName = "Saving layers"; + progress.ProcessedItems = 0; - for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) - { - progress.Token.ThrowIfCancellationRequested(); - outputFile.WriteSerialize(layerData[layerIndex]); - outputFile.WriteBytes(layerData[layerIndex].EncodedRle); - progress++; - } + for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + outputFile.WriteSerialize(layerData[layerIndex]); + outputFile.WriteBytes(layerData[layerIndex].EncodedRle); + progress++; } Debug.WriteLine("Encode Results:"); @@ -425,84 +469,57 @@ namespace UVtools.Core.FileFormats Debug.WriteLine("-End-"); } - public unsafe Mat PreviewDecode(byte []data) - { - Mat mat = new Mat((int) HeaderSettings.PreviewResolutionX, (int)HeaderSettings.PreviewResolutionY, DepthType.Cv8U, 3); - var span = mat.GetBytePointer(); - int spanIndex = 0; - for (int i = 0; i < data.Length; i+=2) - { - ushort color16 = (ushort)(data[i] + (data[i + 1] << 8)); - - var r = (color16 >> 11) & 0x1F; - var g = (color16 >> 5) & 0x3F; - var b = (color16 >> 0) & 0x1F; - - /*span[spanIndex++] = (byte)(b << 3); - span[spanIndex++] = (byte)(g << 2); - span[spanIndex++] = (byte)(r << 3);*/ - - span[spanIndex++] = (byte)((b << 3) | (b & 0x7)); - span[spanIndex++] = (byte)((g << 2) | (g & 0x3)); - span[spanIndex++] = (byte)((r << 3) | (r & 0x7)); - } - - return mat; - } - protected override void DecodeInternally(string fileFullPath, OperationProgress progress) { - using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read)) + using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); + HeaderSettings = Helpers.Deserialize<Header>(inputFile); + if (HeaderSettings.Tag1 != Header.TAG1 || HeaderSettings.Tag2 != Header.TAG2) { - HeaderSettings = Helpers.Deserialize<Header>(inputFile); - if (HeaderSettings.Tag1 != Header.TAG1 || HeaderSettings.Tag2 != Header.TAG2) - { - throw new FileLoadException("Not a valid PHOTONS file! TAGs doesn't match", fileFullPath); - } + throw new FileLoadException("Not a valid PHOTONS file! TAGs doesn't match", fileFullPath); + } - int previewSize = (int) (HeaderSettings.PreviewResolutionX * HeaderSettings.PreviewResolutionY * 2); - byte[] previewData = new byte[previewSize]; + int previewSize = (int) (HeaderSettings.PreviewResolutionX * HeaderSettings.PreviewResolutionY * 2); + byte[] previewData = new byte[previewSize]; - uint currentOffset = (uint) Helpers.Serializer.SizeOf(HeaderSettings); - currentOffset += inputFile.ReadBytes(previewData); - Thumbnails[0] = PreviewDecode(previewData); + uint currentOffset = (uint) Helpers.Serializer.SizeOf(HeaderSettings); + currentOffset += inputFile.ReadBytes(previewData); + Thumbnails[0] = PreviewDecode(previewData); - LayerSettings = Helpers.Deserialize<LayerHeader>(inputFile); - currentOffset += (uint)Helpers.Serializer.SizeOf(LayerSettings); + LayerSettings = Helpers.Deserialize<LayerHeader>(inputFile); + currentOffset += (uint)Helpers.Serializer.SizeOf(LayerSettings); - Debug.WriteLine(HeaderSettings); - Debug.WriteLine(LayerSettings); + Debug.WriteLine(HeaderSettings); + Debug.WriteLine(LayerSettings); - LayerData[] layerData = new LayerData[LayerSettings.LayerCount]; - progress.Reset(OperationProgress.StatusGatherLayers, LayerSettings.LayerCount); + var layerData = new LayerData[LayerSettings.LayerCount]; + progress.Reset(OperationProgress.StatusGatherLayers, LayerSettings.LayerCount); - for (int layerIndex = 0; layerIndex < LayerSettings.LayerCount; layerIndex++) - { - progress.Token.ThrowIfCancellationRequested(); - layerData[layerIndex] = Helpers.Deserialize<LayerData>(inputFile); - layerData[layerIndex].EncodedRle = new byte[layerData[layerIndex].RleDataSize]; - currentOffset += inputFile.ReadBytes(layerData[layerIndex].EncodedRle); - Debug.WriteLine($"Layer {layerIndex} -> {layerData[layerIndex]}"); - } + for (int layerIndex = 0; layerIndex < LayerSettings.LayerCount; layerIndex++) + { + progress.Token.ThrowIfCancellationRequested(); + layerData[layerIndex] = Helpers.Deserialize<LayerData>(inputFile); + layerData[layerIndex].EncodedRle = new byte[layerData[layerIndex].RleDataSize]; + currentOffset += inputFile.ReadBytes(layerData[layerIndex].EncodedRle); + Debug.WriteLine($"Layer {layerIndex} -> {layerData[layerIndex]}"); + } - LayerManager.Init(LayerSettings.LayerCount); - progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); + LayerManager.Init(LayerSettings.LayerCount); + progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); - Parallel.For(0, LayerCount, - //new ParallelOptions{MaxDegreeOfParallelism = 1}, - layerIndex => + Parallel.For(0, LayerCount, + //new ParallelOptions{MaxDegreeOfParallelism = 1}, + layerIndex => { if (progress.Token.IsCancellationRequested) return; using var image = layerData[layerIndex].Decode(); - this[layerIndex] = new Layer((uint) layerIndex, image, LayerManager); + this[layerIndex] = new Layer((uint) layerIndex, image, this); progress.LockAndIncrement(); }); - LayerManager.RebuildLayersProperties(); - } + LayerManager.RebuildLayersProperties(); } public override void SaveAs(string filePath = null, OperationProgress progress = null) @@ -524,12 +541,9 @@ namespace UVtools.Core.FileFormats FileFullPath = filePath; } - using (var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write)) - { - - outputFile.Seek(0, SeekOrigin.Begin); - Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); - } + using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write); + outputFile.Seek(0, SeekOrigin.Begin); + Helpers.SerializeWriteFileStream(outputFile, HeaderSettings); } #endregion diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 4fbe61f..abff762 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -423,16 +423,16 @@ namespace UVtools.Core.FileFormats public unsafe Mat Decode(bool consumeData = true) { - Mat image = new Mat(new Size((int) Width, (int) Height), DepthType.Cv8U, 3); + Mat image = new(new Size((int) Width, (int) Height), DepthType.Cv8U, 3); var span = image.GetBytePointer(); int pixel = 0; for (uint i = 0; i < Data.Length; i += 2) { - ushort color16 = (ushort)(Data[i] + (Data[i+1] << 8)); - var r =(color16 >> 11) & 0x1f; - var g = (color16 >> 5) & 0x3f; - var b = (color16 >> 0) & 0x1f; + ushort color16 = BitExtensions.ToUShortLittleEndian(Data[i], Data[i+1]); + byte r = (byte)((color16 >> 11) & 0x1f); + byte g = (byte)((color16 >> 5) & 0x3f); + byte b = (byte)((color16 >> 0) & 0x1f); span[pixel++] = (byte) ((b << 3) | (b & 0x7)); span[pixel++] = (byte) ((g << 2) | (g & 0x3)); @@ -456,9 +456,9 @@ namespace UVtools.Core.FileFormats for (int pixel = 0; pixel < imageLength; pixel += image.NumberOfChannels) { // BGR - int b = span[pixel] >> 3; - int g = span[pixel+1] >> 2; - int r = span[pixel+2] >> 3; + byte b = (byte)(span[pixel] >> 3); + byte g = (byte)(span[pixel+1] >> 2); + byte r = (byte)(span[pixel+2] >> 3); ushort color = (ushort) ((r << 11) | (g << 5) | (b << 0)); @@ -699,19 +699,21 @@ namespace UVtools.Core.FileFormats case 0x0: color = 0x00; index++; - reps = reps * 256 + EncodedRle[index]; + //reps = reps * 256 + EncodedRle[index]; + reps = (reps << 8) + EncodedRle[index]; break; case 0xf: color = 0xff; index++; - reps = reps * 256 + EncodedRle[index]; + //reps = reps * 256 + EncodedRle[index]; + reps = (reps << 8) + EncodedRle[index]; break; default: color = (byte) ((code << 4) | code); break; } - color &= 0xff; + //color &= 0xff; // We only need to set the non-zero pixels if (color != 0) diff --git a/UVtools.Core/FileFormats/VDTFile.cs b/UVtools.Core/FileFormats/VDTFile.cs index 5dfa02c..5dc0e6b 100644 --- a/UVtools.Core/FileFormats/VDTFile.cs +++ b/UVtools.Core/FileFormats/VDTFile.cs @@ -82,6 +82,7 @@ namespace UVtools.Core.FileFormats [JsonProperty("resolution_y")] public ushort ResolutionY { get; set; } [JsonProperty("x_mirror")] public bool XMirror { get; set; } [JsonProperty("y_mirror")] public bool YMirror { get; set; } + [JsonProperty("notes")] public string Notes { get; set; } } public sealed class VDTResin @@ -89,6 +90,7 @@ namespace UVtools.Core.FileFormats [JsonProperty("name")] public string Name { get; set; } [JsonProperty("cost")] public float Cost { get; set; } [JsonProperty("density")] public float Density { get; set; } = 1; + [JsonProperty("notes")] public string Notes { get; set; } } public sealed class VDTPrint @@ -513,6 +515,48 @@ namespace UVtools.Core.FileFormats //Decode(FileFullPath, progress); } + + public T LookupCustomValue<T>(string name, T defaultValue, bool existsOnly = false) + { + //if (string.IsNullOrEmpty(PrinterSettings.PrinterNotes)) return defaultValue; + var result = string.Empty; + if (!existsOnly) name += '_'; + + foreach (var notes in new[] { ManifestFile.Machine.Notes }) + { + if (string.IsNullOrWhiteSpace(notes)) continue; + + var lines = notes.Split(new[] { "\\r\\n", "\\r", "\\n" }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + foreach (var line in lines) + { + if (!line.StartsWith(name)) continue; + if (existsOnly || line == name) return "true".Convert<T>(); + var value = line.Remove(0, name.Length); + foreach (var c in value) + { + if (typeof(T) == typeof(string)) + { + if (char.IsWhiteSpace(c)) break; + } + else + { + if (!char.IsLetterOrDigit(c) && c != '.') + { + break; + } + } + + + result += c; + } + } + + if (!string.IsNullOrEmpty(result)) break; // Found a candidate + } + + return string.IsNullOrWhiteSpace(result) ? defaultValue : result.Convert<T>(); + } #endregion } } diff --git a/UVtools.Core/Objects/StringTag.cs b/UVtools.Core/Objects/StringTag.cs deleted file mode 100644 index edaea6f..0000000 --- a/UVtools.Core/Objects/StringTag.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * GNU AFFERO GENERAL PUBLIC LICENSE - * Version 3, 19 November 2007 - * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> - * Everyone is permitted to copy and distribute verbatim copies - * of this license document, but changing it is not allowed. - */ - -using System; -using System.Collections.Generic; - -namespace UVtools.Core.Objects -{ - public class StringTag : BindableBase, IEquatable<StringTag>, IComparable<StringTag> - { - private string _content; - private object _tag; - - public string Content - { - get => _content; - set => RaiseAndSetIfChanged(ref _content, value); - } - - public object Tag - { - get => _tag; - set => RaiseAndSetIfChanged(ref _tag, value); - } - - public string TagString - { - get => Tag?.ToString(); - set => Tag = value; - } - - public StringTag(object content, object tag = null) - { - Content = content.ToString(); - Tag = tag; - } - public StringTag(string content, object tag = null) - { - Content = content; - Tag = tag; - } - - public bool Equals(StringTag other) - { - return _content == other._content && Equals(_tag, other._tag); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((StringTag) obj); - } - - public override int GetHashCode() - { - unchecked - { - return ((_content != null ? _content.GetHashCode() : 0) * 397) ^ (_tag != null ? _tag.GetHashCode() : 0); - } - } - - private sealed class ContentEqualityComparer : IEqualityComparer<StringTag> - { - public bool Equals(StringTag x, StringTag y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return x.Content == y.Content; - } - - public int GetHashCode(StringTag obj) - { - return (obj.Content != null ? obj.Content.GetHashCode() : 0); - } - } - - public static IEqualityComparer<StringTag> ContentComparer { get; } = new ContentEqualityComparer(); - - public int CompareTo(StringTag other) - { - if (ReferenceEquals(this, other)) return 0; - if (ReferenceEquals(null, other)) return 1; - return string.Compare(Content, other.Content, StringComparison.Ordinal); - } - - public override string ToString() - { - return Content; - } - } -} diff --git a/UVtools.Core/Objects/ValueDescription.cs b/UVtools.Core/Objects/ValueDescription.cs new file mode 100644 index 0000000..3f7372c --- /dev/null +++ b/UVtools.Core/Objects/ValueDescription.cs @@ -0,0 +1,73 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Collections.Generic; + +namespace UVtools.Core.Objects +{ + public class ValueDescription : BindableBase, IEquatable<ValueDescription> + { + private object _value; + private string _description; + + public object Value + { + get => _value; + set => RaiseAndSetIfChanged(ref _value, value); + } + + public string Description + { + get => _description; + set => RaiseAndSetIfChanged(ref _description, value); + } + + public string ValueAsString + { + get => Value?.ToString(); + set => Value = value; + } + + public ValueDescription(object value, string description = null) + { + Value = value; + Description = description; + } + public ValueDescription(object value, object description = null) + { + Value = value; + Description = description?.ToString(); + } + + public bool Equals(ValueDescription other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(_value, other._value) && _description == other._description; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((ValueDescription) obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(_value, _description); + } + + public override string ToString() + { + return Description; + } + } +} diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs index ed65ac8..c803177 100644 --- a/UVtools.Core/Operations/OperationBlur.cs +++ b/UVtools.Core/Operations/OperationBlur.cs @@ -7,6 +7,7 @@ */ using System; +using System.ComponentModel; using System.Drawing; using System.Text; using System.Threading.Tasks; @@ -68,42 +69,30 @@ namespace UVtools.Core.Operations #region Enums public enum BlurAlgorithm { + [Description("Blur: Normalized box filter")] Blur, + [Description("Pyramid: Down/up-sampling step of Gaussian pyramid decomposition")] Pyramid, + [Description("Median Blur: Each pixel becomes the median of its surrounding pixels")] MedianBlur, + [Description("Gaussian Blur: Each pixel is a sum of fractions of each pixel in its neighborhood")] GaussianBlur, + [Description("Filter 2D: Applies an arbitrary linear filter to an image")] Filter2D } #endregion #region Properties - public static StringTag[] BlurTypes => new[] - { - new StringTag("Blur: Normalized box filter", BlurAlgorithm.Blur), - new StringTag("Pyramid: Down/up-sampling step of Gaussian pyramid decomposition", BlurAlgorithm.Pyramid), - new StringTag("Median Blur: Each pixel becomes the median of its surrounding pixels", BlurAlgorithm.MedianBlur), - new StringTag("Gaussian Blur: Each pixel is a sum of fractions of each pixel in its neighborhood", BlurAlgorithm.GaussianBlur), - new StringTag("Filter 2D: Applies an arbitrary linear filter to an image", BlurAlgorithm.Filter2D), - }; - - public byte BlurTypeIndex - { - get - { - for (byte i = 0; i < BlurTypes.Length; i++) - { - if ((BlurAlgorithm)BlurTypes[i].Tag == BlurOperation) return i; - } - - return 0; - } - } - public BlurAlgorithm BlurOperation { get => _blurOperation; - set => RaiseAndSetIfChanged(ref _blurOperation, value); + set + { + if(!RaiseAndSetIfChanged(ref _blurOperation, value)) return; + RaisePropertyChanged(nameof(IsSizeEnabled)); + RaisePropertyChanged(nameof(IsKernelVisible)); + } } public uint Size @@ -112,8 +101,13 @@ namespace UVtools.Core.Operations set => RaiseAndSetIfChanged(ref _size, value); } + public bool IsSizeEnabled => BlurOperation != BlurAlgorithm.Pyramid && + BlurOperation != BlurAlgorithm.Filter2D; + + public bool IsKernelVisible => BlurOperation == BlurAlgorithm.Filter2D; + [XmlIgnore] - public Kernel Kernel { get; set; } = new Kernel(); + public Kernel Kernel { get; set; } = new (); public override string ToString() { diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs index fb69546..367e725 100644 --- a/UVtools.Core/Operations/OperationLayerImport.cs +++ b/UVtools.Core/Operations/OperationLayerImport.cs @@ -46,7 +46,7 @@ namespace UVtools.Core.Operations private bool _extendBeyondLayerCount = true; private bool _discardUnmodifiedLayers; private ushort _stackMargin = 50; - private ObservableCollection<StringTag> _files = new(); + private ObservableCollection<ValueDescription> _files = new(); #endregion #region Overrides @@ -161,7 +161,7 @@ namespace UVtools.Core.Operations } [XmlIgnore] - public ObservableCollection<StringTag> Files + public ObservableCollection<ValueDescription> Files { get => _files; set => RaiseAndSetIfChanged(ref _files, value); @@ -183,13 +183,13 @@ namespace UVtools.Core.Operations public void AddFile(string file) { - Files.Add(new StringTag(Path.GetFileNameWithoutExtension(file), file)); + Files.Add(new ValueDescription(file, Path.GetFileNameWithoutExtension(file))); } public void Sort() { var sortedFiles = Files.ToList(); - sortedFiles.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1.TagString), Path.GetFileNameWithoutExtension(file2.TagString), StringComparison.Ordinal)); + sortedFiles.Sort((file1, file2) => string.Compare(Path.GetFileNameWithoutExtension(file1.ValueAsString), Path.GetFileNameWithoutExtension(file2.ValueAsString), StringComparison.Ordinal)); Files.Clear(); foreach (var file in sortedFiles) { @@ -232,12 +232,12 @@ namespace UVtools.Core.Operations // Order raw images for (int i = 0; i < Count; i++) { - if (!Files[i].TagString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && - !Files[i].TagString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) && - !Files[i].TagString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && - !Files[i].TagString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && - !Files[i].TagString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; - keyImage.Add(new KeyValuePair<uint, string>((uint)keyImage.Count, Files[i].TagString)); + if (!Files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && + !Files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) && + !Files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && + !Files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && + !Files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; + keyImage.Add(new KeyValuePair<uint, string>((uint)keyImage.Count, Files[i].ValueAsString)); } // Create virtual file format with images @@ -264,15 +264,15 @@ namespace UVtools.Core.Operations // Order remaining possible file formats for (int i = 0; i < Count; i++) { - if (Files[i].TagString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || - Files[i].TagString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) || - Files[i].TagString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) || - Files[i].TagString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || - Files[i].TagString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; + if (Files[i].ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) || + Files[i].ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) || + Files[i].ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) || + Files[i].ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || + Files[i].ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) continue; - var fileFormat = FileFormat.FindByExtension(Files[i].TagString, true, true); + var fileFormat = FileFormat.FindByExtension(Files[i].ValueAsString, true, true); if (fileFormat is null) continue; - fileFormat.FileFullPath = Files[i].TagString; + fileFormat.FileFullPath = Files[i].ValueAsString; fileFormats.Add(fileFormat); } diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs index 6b45374..8a9d338 100644 --- a/UVtools.Core/Operations/OperationLayerReHeight.cs +++ b/UVtools.Core/Operations/OperationLayerReHeight.cs @@ -8,22 +8,35 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.ComponentModel; 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.Objects; namespace UVtools.Core.Operations { [Serializable] public sealed class OperationLayerReHeight : Operation { + #region Enums + + public enum OperationLayerReHeightAntiAliasingType : byte + { + //[Description("Hello")] + None, + [Description("Difference - Compute anti-aliasing by the layers difference and perform a down/up sample over pixels")] + Difference, + [Description("Average - Compute anti-aliasing by averaging the layers pixels")] + Average + } + #endregion #region Members - private OperationLayerReHeightItem _item; - private bool _antiAliasing = true; + private OperationLayerReHeightItem _selectedItem; + private OperationLayerReHeightAntiAliasingType _antiAliasingType; #endregion @@ -38,10 +51,10 @@ namespace UVtools.Core.Operations " than current height will reduce model detail.\n\n" + "Note: Using dedicated slicer software to re-slice will usually yeild better results."; public override string ConfirmationText => - $"adjust layer height to {Item.LayerHeight}mm?"; + $"adjust layer height to {SelectedItem.LayerHeight}mm?"; public override string ProgressTitle => - $"Adjusting layer height to {Item.LayerHeight}mm"; + $"Adjusting layer height to {SelectedItem.LayerHeight}mm"; public override string ProgressAction => "Height adjusted layers"; @@ -51,7 +64,7 @@ namespace UVtools.Core.Operations { var sb = new StringBuilder(); - if (Item is null) + if (SelectedItem is null) { sb.AppendLine("No valid configurations, unable to proceed."); } @@ -62,7 +75,7 @@ namespace UVtools.Core.Operations public override string ToString() { - var result = $"[Layer Count: {Item.LayerCount}] [Layer Height: {Item.LayerHeight}]" + LayerRangeString; + var result = $"[Layer Count: {SelectedItem.LayerCount}] [Layer Height: {SelectedItem.LayerHeight}]" + LayerRangeString; if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; return result; } @@ -70,22 +83,24 @@ namespace UVtools.Core.Operations #region Properties - public OperationLayerReHeightItem Item + public OperationLayerReHeightItem[] Presets { get; } + + public OperationLayerReHeightItem SelectedItem { - get => _item; + get => _selectedItem; set { - if(!RaiseAndSetIfChanged(ref _item, value)) return; - RaisePropertyChanged(nameof(AntiAliasing)); + if(!RaiseAndSetIfChanged(ref _selectedItem, value)) return; + RaisePropertyChanged(nameof(CanAntiAliasing)); } } - public bool CanAntiAliasing => _item?.IsMultiply ?? false; + public bool CanAntiAliasing => _selectedItem?.IsMultiply ?? false; - public bool AntiAliasing + public OperationLayerReHeightAntiAliasingType AntiAliasingType { - get => _antiAliasing; - set => RaiseAndSetIfChanged(ref _antiAliasing, value); + get => _antiAliasingType; + set => RaiseAndSetIfChanged(ref _antiAliasingType, value); } @@ -120,7 +135,14 @@ namespace UVtools.Core.Operations public OperationLayerReHeight() { } - public OperationLayerReHeight(FileFormat slicerFile) : base(slicerFile) { } + public OperationLayerReHeight(FileFormat slicerFile) : base(slicerFile) + { + Presets = GetItems(slicerFile.LayerCount, (decimal) slicerFile.LayerHeight); + if (Presets is not null && Presets.Length > 0) + { + _selectedItem = Presets[0]; + } + } #endregion @@ -151,11 +173,11 @@ namespace UVtools.Core.Operations #region Methods protected override bool ExecuteInternally(OperationProgress progress) { - progress.ItemCount = _item.LayerCount; + progress.ItemCount = _selectedItem.LayerCount; - var layers = new Layer[_item.LayerCount]; + var layers = new Layer[_selectedItem.LayerCount]; - if (_item.IsDivision) + if (_selectedItem.IsDivision) { uint newLayerIndex = 0; for (uint layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++) @@ -163,7 +185,7 @@ namespace UVtools.Core.Operations progress.Token.ThrowIfCancellationRequested(); var oldLayer = SlicerFile[layerIndex]; - for (byte i = 0; i < _item.Modifier; i++) + for (byte i = 0; i < _selectedItem.Modifier; i++) { var newLayer = oldLayer.Clone(); //newLayer.Index = newLayerIndex; @@ -176,48 +198,90 @@ namespace UVtools.Core.Operations } else { - var layerIndexes = new uint[SlicerFile.LayerCount / _item.Modifier]; + var layerIndexes = new uint[SlicerFile.LayerCount / _selectedItem.Modifier]; for (uint i = 0; i < layerIndexes.Length; i++) { - layerIndexes[i] = i * _item.Modifier; + layerIndexes[i] = i * _selectedItem.Modifier; } Parallel.ForEach(layerIndexes, layerIndex => { if (progress.Token.IsCancellationRequested) return; var oldLayer = SlicerFile[layerIndex]; - using var mat = oldLayer.LayerMat; - using var original = mat.Clone(); + using var matSum = oldLayer.LayerMat; + Mat matXorSum = null; + using Mat aaAverageSum = new(); + + if (_antiAliasingType == OperationLayerReHeightAntiAliasingType.Average) + { + matSum.ConvertTo(aaAverageSum, DepthType.Cv16U); + } - for (byte i = 1; i < Item.Modifier; i++) + for (byte i = 1; i < SelectedItem.Modifier; i++) { using var nextMat = SlicerFile[layerIndex+i].LayerMat; - CvInvoke.Add(mat, nextMat, mat); + + switch (_antiAliasingType) + { + case OperationLayerReHeightAntiAliasingType.None: + CvInvoke.Add(matSum, nextMat, matSum); + break; + case OperationLayerReHeightAntiAliasingType.Difference: + { + using var previousMat = SlicerFile[layerIndex + i - 1].LayerMat; + var matXor = new Mat(); + //CvInvoke.Threshold(previousMat, previousMat, 127, 255, ThresholdType.Binary); + //CvInvoke.Threshold(nextMat, nextMat, 127, 255, ThresholdType.Binary); + CvInvoke.BitwiseXor(previousMat, nextMat, matXor); + matXor.SetTo(new MCvScalar((byte)(byte.MaxValue / _selectedItem.Modifier)), matXor); + if (matXorSum is null) + { + matXorSum = matXor.Clone(); + } + else + { + CvInvoke.Add(matXorSum, matXorSum, matXorSum); + CvInvoke.Add(matXorSum, matXor, matXorSum); + } + break; + } + case OperationLayerReHeightAntiAliasingType.Average: + nextMat.ConvertTo(nextMat, DepthType.Cv16U); + CvInvoke.Add(aaAverageSum, nextMat, aaAverageSum); + break; + default: + throw new ArgumentOutOfRangeException(); + } } - /*if (_antiAliasing) + switch (_antiAliasingType) { - CvInvoke.Subtract(mat, original, mat); - CvInvoke.PyrDown(mat, mat); - CvInvoke.PyrUp(mat, mat); - CvInvoke.Add(original, mat, mat); - }*/ + case OperationLayerReHeightAntiAliasingType.Difference: + CvInvoke.Add(matSum, matXorSum, matSum); + CvInvoke.PyrDown(matSum, matSum); + CvInvoke.PyrUp(matSum, matSum); + matXorSum.Dispose(); + break; + case OperationLayerReHeightAntiAliasingType.Average: + aaAverageSum.ConvertTo(matSum, DepthType.Cv8U, 1.0 / _selectedItem.Modifier); + CvInvoke.PyrDown(matSum, matSum); + CvInvoke.PyrUp(matSum, matSum); + break; + } var newLayer = oldLayer.Clone(); //newLayer.Index = newLayerIndex; //newLayer.PositionZ = (float)(Item.LayerHeight * (newLayerIndex + 1)); - newLayer.LayerMat = mat; - layers[layerIndex / _item.Modifier] = newLayer; + newLayer.LayerMat = matSum; + layers[layerIndex / _selectedItem.Modifier] = newLayer; progress.LockAndIncrement(); }); - - } - + progress.Token.ThrowIfCancellationRequested(); - SlicerFile.LayerHeight = (float)Item.LayerHeight; + SlicerFile.LayerHeight = (float)SelectedItem.LayerHeight; SlicerFile.LayerManager.Layers = layers; return !progress.Token.IsCancellationRequested; diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs index c3120cf..f2c6804 100644 --- a/UVtools.Core/Operations/OperationMorph.cs +++ b/UVtools.Core/Operations/OperationMorph.cs @@ -7,6 +7,7 @@ */ using System; +using System.ComponentModel; using System.Threading.Tasks; using System.Xml.Serialization; using Emgu.CV; @@ -19,8 +20,28 @@ namespace UVtools.Core.Operations [Serializable] public sealed class OperationMorph : Operation { + #region Enums + public enum MorphOperations + { + [Description("Erode: Contracts the boundaries within the object")] + Erode = MorphOp.Erode, + + [Description("Dilate: Expands the boundaries within the object")] + Dilate = MorphOp.Dilate, + + [Description("Gap Closing - Closes small holes inside the objects")] + Close = MorphOp.Close, + + [Description("Noise Removal - Removes small isolated pixels")] + Open = MorphOp.Open, + + [Description("Gradient - Removes the interior areas of objects")] + Gradient = MorphOp.Gradient, + } + #endregion + #region Members - private MorphOp _morphOperation = MorphOp.Erode; + private MorphOperations _morphOperation = MorphOperations.Erode; private uint _iterationsStart = 1; private uint _iterationsEnd = 1; private bool _chamfer; @@ -43,31 +64,7 @@ namespace UVtools.Core.Operations #endregion #region Properties - - - public static StringTag[] MorphOperations => new[] - { - new StringTag("Erode - Contracts the boundaries within the object", MorphOp.Erode), - new StringTag("Dilate - Expands the boundaries within the object", MorphOp.Dilate), - new StringTag("Gap Closing - Closes small holes inside the objects", MorphOp.Close), - new StringTag("Noise Removal - Removes small isolated pixels", MorphOp.Open), - new StringTag("Gradient - Removes the interior areas of objects", MorphOp.Gradient), - }; - - public byte MorphOperationIndex - { - get - { - for (byte i = 0; i < MorphOperations.Length; i++) - { - if ((MorphOp) MorphOperations[i].Tag == MorphOperation) return i; - } - - return 0; - } - } - - public MorphOp MorphOperation + public MorphOperations MorphOperation { get => _morphOperation; set => RaiseAndSetIfChanged(ref _morphOperation, value); @@ -185,7 +182,7 @@ namespace UVtools.Core.Operations using var original = mat.Clone(); var target = GetRoiOrDefault(mat); - CvInvoke.MorphologyEx(target, target, MorphOperation, Kernel.Matrix, Kernel.Anchor, iterations, BorderType.Reflect101, default); + CvInvoke.MorphologyEx(target, target, (MorphOp) MorphOperation, Kernel.Matrix, Kernel.Anchor, iterations, BorderType.Reflect101, default); ApplyMask(original, mat); return true; } diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs index 0da2586..1771536 100644 --- a/UVtools.Core/Operations/OperationProgress.cs +++ b/UVtools.Core/Operations/OperationProgress.cs @@ -34,7 +34,7 @@ namespace UVtools.Core.Operations public readonly object Mutex = new(); - public CancellationTokenSource TokenSource { get; } = new(); + public CancellationTokenSource TokenSource { get; private set; } public CancellationToken Token => TokenSource.Token; private bool _canCancel = true; @@ -47,14 +47,15 @@ namespace UVtools.Core.Operations public OperationProgress() { + Init(); } - public OperationProgress(string name, uint value = 0) + public OperationProgress(string name, uint value = 0) : this() { Reset(name, value); } - public OperationProgress(bool canCancel) + public OperationProgress(bool canCancel) : this() { _canCancel = canCancel; } @@ -164,6 +165,18 @@ namespace UVtools.Core.Operations return progress; } + public void Init(bool canCancel = true) + { + CanCancel = canCancel; + Title = "Operation"; + ItemName = "Initializing"; + ItemCount = 0; + ProcessedItems = 0; + + TokenSource = new(); + RaisePropertyChanged(nameof(CanCancel)); + } + public void Reset(string name = "", uint itemCount = 0, uint items = 0) { ItemName = name; diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 63a0232..f3e2fba 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.9.0</Version> + <Version>2.9.1</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> diff --git a/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs b/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs index ea9b5fa..8ec2695 100644 --- a/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs +++ b/UVtools.ScriptSample/ScriptAutomateWorkflowSample.cs @@ -73,7 +73,7 @@ namespace UVtools.ScriptSample { OperationMorph morph = new(SlicerFile) { - MorphOperation = MorphOp.Erode, + MorphOperation = OperationMorph.MorphOperations.Erode, Iterations = 4, }; morph.SelectBottomLayers(); diff --git a/UVtools.WPF/App.axaml b/UVtools.WPF/App.axaml index ede140d..6a486e4 100644 --- a/UVtools.WPF/App.axaml +++ b/UVtools.WPF/App.axaml @@ -1,6 +1,11 @@ <Application xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:converters="clr-namespace:UVtools.WPF.Converters" x:Class="UVtools.WPF.App"> + <Application.Resources> + <converters:EnumToCollectionConverter x:Key="EnumToCollectionConverter" /> + <converters:FromValueDescriptionToEnumConverter x:Key="FromValueDescriptionToEnumConverter" /> + </Application.Resources> <Application.Styles> <FluentTheme Mode="Light"/> <StyleInclude Source="avares://ThemeEditor.Controls.ColorPicker/ColorPicker.axaml"/> diff --git a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml index e37b992..e2f466b 100644 --- a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml @@ -17,16 +17,15 @@ <ComboBox Grid.Column="2" HorizontalAlignment="Left" - SelectedIndex="{Binding SelectedAlgorithmIndex}" Width="450" HorizontalContentAlignment="Stretch" - Items="{Binding Operation.BlurTypes}" - /> + Items="{Binding Operation.BlurOperation, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" + SelectedItem="{Binding Operation.BlurOperation, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/> <TextBlock VerticalAlignment="Center" Grid.Row="2" - IsEnabled="{Binding IsSizeEnabled}" + IsEnabled="{Binding Operation.IsSizeEnabled}" Text="Size:"/> <NumericUpDown Grid.Row="2" @@ -34,15 +33,13 @@ HorizontalAlignment="Left" Width="150" Minimum="1" - IsEnabled="{Binding IsSizeEnabled}" + IsEnabled="{Binding Operation.IsSizeEnabled}" Value="{Binding Operation.Size}" /> </Grid> - <uc:KernelControl - Name="KernelCtrl" - IsVisible="{Binding $parent[UserControl].IsKernelVisible}" - /> + <uc:KernelControl Name="KernelCtrl" + IsVisible="{Binding $parent[UserControl].DataContext.Operation.IsKernelVisible}"/> </StackPanel> diff --git a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs index 89f6322..86e5c19 100644 --- a/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolBlurControl.axaml.cs @@ -7,37 +7,9 @@ namespace UVtools.WPF.Controls.Tools { public class ToolBlurControl : ToolControl { - private int _selectedAlgorithmIndex; - private bool _isSizeEnabled = true; - private bool _isKernelVisible; private KernelControl _kernelCtrl; public OperationBlur Operation => BaseOperation as OperationBlur; - public int SelectedAlgorithmIndex - { - get => _selectedAlgorithmIndex; - set - { - if(!RaiseAndSetIfChanged(ref _selectedAlgorithmIndex, value) || value < 0) return; - Operation.BlurOperation = (OperationBlur.BlurAlgorithm) OperationBlur.BlurTypes[_selectedAlgorithmIndex].Tag; - IsKernelVisible = Operation.BlurOperation == OperationBlur.BlurAlgorithm.Filter2D; - IsSizeEnabled = Operation.BlurOperation != OperationBlur.BlurAlgorithm.Pyramid && - Operation.BlurOperation != OperationBlur.BlurAlgorithm.Filter2D; - } - } - - public bool IsSizeEnabled - { - get => _isSizeEnabled; - set => RaiseAndSetIfChanged(ref _isSizeEnabled, value); - } - - public bool IsKernelVisible - { - get => _isKernelVisible; - set => RaiseAndSetIfChanged(ref _isKernelVisible, value); - } - public ToolBlurControl() { InitializeComponent(); @@ -54,17 +26,8 @@ namespace UVtools.WPF.Controls.Tools { Operation.Kernel.Matrix = _kernelCtrl.GetMatrix(); Operation.Kernel.Anchor = _kernelCtrl.Anchor; - return !(Operation.Kernel.Matrix is null); + return Operation.Kernel.Matrix is not null; } - public override void Callback(ToolWindow.Callbacks callback) - { - switch (callback) - { - case ToolWindow.Callbacks.ProfileLoaded: - SelectedAlgorithmIndex = Operation.BlurTypeIndex; - break; - } - } } } diff --git a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs index e88db59..09dc732 100644 --- a/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolControl.axaml.cs @@ -80,6 +80,6 @@ namespace UVtools.WPF.Controls.Tools /// </summary> /// <param name="text"></param> /// <returns></returns> - public async Task<bool> ValidateFormFromString(StringTag text) => await ValidateFormFromString(text?.ToString()); + public async Task<bool> ValidateFormFromString(ValueDescription text) => await ValidateFormFromString(text?.ToString()); } } diff --git a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs index 0fc4755..7381dd6 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerImportControl.axaml.cs @@ -20,7 +20,7 @@ namespace UVtools.WPF.Controls.Tools public class ToolLayerImportControl : ToolControl { private bool _isAutoSortLayersByFileNameChecked; - private StringTag _selectedFile; + private ValueDescription _selectedFile; private Bitmap _previewImage; private ListBox FilesListBox; @@ -60,7 +60,7 @@ namespace UVtools.WPF.Controls.Tools } - public StringTag SelectedFile + public ValueDescription SelectedFile { get => _selectedFile; set @@ -71,12 +71,12 @@ namespace UVtools.WPF.Controls.Tools PreviewImage = null; return; } - if (!_selectedFile.TagString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && - !_selectedFile.TagString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) && - !_selectedFile.TagString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && - !_selectedFile.TagString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && - !_selectedFile.TagString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) return; - PreviewImage = new Bitmap(_selectedFile.TagString); + if (!_selectedFile.ValueAsString.EndsWith(".png", StringComparison.OrdinalIgnoreCase) && + !_selectedFile.ValueAsString.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase) && + !_selectedFile.ValueAsString.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase) && + !_selectedFile.ValueAsString.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) && + !_selectedFile.ValueAsString.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)) return; + PreviewImage = new Bitmap(_selectedFile.ValueAsString); } } @@ -94,8 +94,8 @@ namespace UVtools.WPF.Controls.Tools FilesListBox = this.Find<ListBox>("FilesListBox"); FilesListBox.DoubleTapped += (sender, args) => { - if (!(FilesListBox.SelectedItem is StringTag file)) return; - App.StartProcess(file.TagString); + if (!(FilesListBox.SelectedItem is ValueDescription file)) return; + App.StartProcess(file.ValueAsString); }; FilesListBox.KeyUp += (sender, e) => { @@ -197,7 +197,7 @@ namespace UVtools.WPF.Controls.Tools public void RemoveFiles() { - Operation.Files.RemoveMany(FilesListBox.SelectedItems.OfType<StringTag>()); + Operation.Files.RemoveMany(FilesListBox.SelectedItems.OfType<ValueDescription>()); } public void ClearFiles() diff --git a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml index 3db4d3c..168ac75 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml @@ -4,17 +4,28 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.WPF.Controls.Tools.ToolLayerReHeightControl"> - <StackPanel Orientation="Vertical" Spacing="10"> + + + <StackPanel Orientation="Vertical" Spacing="10"> <TextBlock VerticalAlignment="Center" Text="{Binding CurrentLayers}"/> - <StackPanel Orientation="Horizontal" Spacing="10"> - <TextBlock VerticalAlignment="Center" Text="Modifier:"/> - <ComboBox - MinWidth="300" - SelectedItem="{Binding Operation.Item}" - Items="{Binding Presets}" + <Grid RowDefinitions="Auto,10,Auto" + ColumnDefinitions="Auto,10,Auto"> + + <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="Modifier:"/> + <ComboBox + Grid.Row="0" Grid.Column="2" Width="500" + SelectedItem="{Binding Operation.SelectedItem}" + Items="{Binding Operation.Presets}" /> + + <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Text="Anti-Aliasing:"/> + <ComboBox Grid.Row="2" Grid.Column="2" Width="500" + IsEnabled="{Binding Operation.CanAntiAliasing}" + Items="{Binding Operation.AntiAliasingType, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" + SelectedItem="{Binding Operation.AntiAliasingType, Converter={StaticResource FromValueDescriptionToEnumConverter}}"> + </ComboBox> + </Grid> + </StackPanel> - - </StackPanel> </UserControl> diff --git a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs index 4b6e093..8a848b9 100644 --- a/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerReHeightControl.axaml.cs @@ -8,27 +8,18 @@ namespace UVtools.WPF.Controls.Tools { public OperationLayerReHeight Operation => BaseOperation as OperationLayerReHeight; - public OperationLayerReHeight.OperationLayerReHeightItem[] Presets => App.SlicerFile is null ? null : OperationLayerReHeight.GetItems( - App.SlicerFile.LayerCount, - (decimal)App.SlicerFile.LayerHeight); - public string CurrentLayers => $"Current layers: {App.SlicerFile.LayerCount} at {App.SlicerFile.LayerHeight}mm"; public ToolLayerReHeightControl() { InitializeComponent(); BaseOperation = new OperationLayerReHeight(SlicerFile); - var presets = Presets; - if (presets is null || presets.Length == 0) + if (Operation.SelectedItem is null) { App.MainWindow.MessageBoxInfo("No valid configuration to be able to re-height.\n" + "As workaround clone first or last layer and try re run this tool.", "Not possible to re-height"); CanRun = false; } - else - { - Operation.Item = presets[0]; - } } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml index 5611261..868a47c 100644 --- a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml @@ -56,8 +56,8 @@ Grid.Row="2" Grid.Column="2" HorizontalAlignment="Stretch" - SelectedIndex="{Binding MorphSelectedIndex}" - Items="{Binding Operation.MorphOperations}" + Items="{Binding Operation.MorphOperation, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" + SelectedItem="{Binding Operation.MorphOperation, Converter={StaticResource FromValueDescriptionToEnumConverter}}" /> </Grid> diff --git a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs index 9d17f05..b3ca26d 100644 --- a/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolMorphControl.axaml.cs @@ -1,30 +1,15 @@ -using System; -using System.Diagnostics; -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Markup.Xaml; -using Emgu.CV.CvEnum; using UVtools.Core.Operations; -using UVtools.WPF.Windows; namespace UVtools.WPF.Controls.Tools { public class ToolMorphControl : ToolControl { - private byte _morphSelectedIndex; public OperationMorph Operation => BaseOperation as OperationMorph; private KernelControl _kernelCtrl; - public byte MorphSelectedIndex - { - get => _morphSelectedIndex; - set - { - if(!RaiseAndSetIfChanged(ref _morphSelectedIndex, value)) return; - Operation.MorphOperation = (MorphOp)OperationMorph.MorphOperations[_morphSelectedIndex].Tag; - } - } - public ToolMorphControl() { InitializeComponent(); @@ -41,17 +26,7 @@ namespace UVtools.WPF.Controls.Tools { Operation.Kernel.Matrix = _kernelCtrl.GetMatrix(); Operation.Kernel.Anchor = _kernelCtrl.Anchor; - return !(Operation.Kernel.Matrix is null); - } - - public override void Callback(ToolWindow.Callbacks callback) - { - switch (callback) - { - case ToolWindow.Callbacks.ProfileLoaded: - MorphSelectedIndex = Operation.MorphOperationIndex; - break; - } + return Operation.Kernel.Matrix is not null; } } } diff --git a/UVtools.WPF/Converters/EnumToCollectionConverter.cs b/UVtools.WPF/Converters/EnumToCollectionConverter.cs new file mode 100644 index 0000000..f585f86 --- /dev/null +++ b/UVtools.WPF/Converters/EnumToCollectionConverter.cs @@ -0,0 +1,29 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using Avalonia.Data.Converters; +using UVtools.Core.Extensions; + +namespace UVtools.WPF.Converters +{ + public class EnumToCollectionConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return EnumExtensions.GetAllValuesAndDescriptions(value.GetType()); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return null; + //string parameterString = parameter.ToString(); + //return Enum.Parse(targetType, parameterString); + } + } +} diff --git a/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs b/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs new file mode 100644 index 0000000..e37fb58 --- /dev/null +++ b/UVtools.WPF/Converters/FromValueDescriptionToEnumConverter.cs @@ -0,0 +1,31 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Linq; +using Avalonia.Data.Converters; +using UVtools.Core.Extensions; + +namespace UVtools.WPF.Converters +{ + public class FromValueDescriptionToEnumConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + var list = EnumExtensions.GetAllValuesAndDescriptions(value.GetType()); + return list.FirstOrDefault(vd => vd.Value.Equals(value)); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is null) return null; + var list = EnumExtensions.GetAllValuesAndDescriptions(targetType); + return list.FirstOrDefault(vd => vd.Description == value.ToString())?.Value; + } + } +} diff --git a/UVtools.WPF/Extensions/BitmapExtension.cs b/UVtools.WPF/Extensions/BitmapExtension.cs index 662f9d0..ae5f5ce 100644 --- a/UVtools.WPF/Extensions/BitmapExtension.cs +++ b/UVtools.WPF/Extensions/BitmapExtension.cs @@ -6,15 +6,12 @@ * of this license document, but changing it is not allowed. */ using System; -using System.Diagnostics; -using System.Runtime.InteropServices; using Avalonia; using Avalonia.Media.Imaging; using Avalonia.Platform; using Emgu.CV; using Emgu.CV.CvEnum; using SkiaSharp; -using UVtools.Core.Extensions; namespace UVtools.WPF.Extensions { diff --git a/UVtools.WPF/MainWindow.Information.cs b/UVtools.WPF/MainWindow.Information.cs index 3822b94..8b40065 100644 --- a/UVtools.WPF/MainWindow.Information.cs +++ b/UVtools.WPF/MainWindow.Information.cs @@ -31,15 +31,15 @@ namespace UVtools.WPF { public partial class MainWindow { - public ObservableCollection<SlicerProperty> SlicerProperties { get; } = new ObservableCollection<SlicerProperty>(); + public ObservableCollection<SlicerProperty> SlicerProperties { get; } = new(); public DataGrid PropertiesGrid; public DataGrid CurrentLayerGrid; private uint _visibleThumbnailIndex; private Bitmap _visibleThumbnailImage; - private ObservableCollection<StringTag> _currentLayerProperties = new ObservableCollection<StringTag>(); + private ObservableCollection<ValueDescription> _currentLayerProperties = new(); - public ObservableCollection<StringTag> CurrentLayerProperties + public ObservableCollection<ValueDescription> CurrentLayerProperties { get => _currentLayerProperties; set => RaiseAndSetIfChanged(ref _currentLayerProperties, value); @@ -336,38 +336,33 @@ namespace UVtools.WPF var layer = LayerCache.Layer; CurrentLayerProperties.Clear(); - CurrentLayerProperties.Add(new StringTag(nameof(layer.Index), $"{layer.Index}")); - CurrentLayerProperties.Add(new StringTag(nameof(layer.LayerHeight), $"{Layer.ShowHeight(layer.LayerHeight)}mm")); - //CurrentLayerProperties.Add(new KeyValuePair<string, string>(nameof(layer.Filename), layer.Filename)); - CurrentLayerProperties.Add(new StringTag(nameof(layer.PositionZ), $"{Layer.ShowHeight(layer.PositionZ)}mm")); - CurrentLayerProperties.Add(new StringTag(nameof(layer.IsBottomLayer), layer.IsBottomLayer.ToString())); - CurrentLayerProperties.Add(new StringTag(nameof(layer.IsModified), layer.IsModified.ToString())); - //CurrentLayerProperties.Add(new StringTag(nameof(layer.BoundingRectangle), layer.BoundingRectangle.ToString())); - //CurrentLayerProperties.Add(new StringTag(nameof(layer.NonZeroPixelCount), layer.NonZeroPixelCount.ToString())); - CurrentLayerProperties.Add(new StringTag(nameof(layer.ExposureTime), $"{layer.ExposureTime:F2}s")); + CurrentLayerProperties.Add(new ValueDescription($"{layer.Index}", nameof(layer.Index))); + CurrentLayerProperties.Add(new ValueDescription($"{Layer.ShowHeight(layer.LayerHeight)}mm", nameof(layer.LayerHeight))); + CurrentLayerProperties.Add(new ValueDescription($"{Layer.ShowHeight(layer.PositionZ)}mm", nameof(layer.PositionZ))); + CurrentLayerProperties.Add(new ValueDescription(layer.IsBottomLayer.ToString(), nameof(layer.IsBottomLayer))); + CurrentLayerProperties.Add(new ValueDescription(layer.IsModified.ToString(), nameof(layer.IsModified))); + CurrentLayerProperties.Add(new ValueDescription($"{layer.ExposureTime:F2}s", nameof(layer.ExposureTime))); if (SlicerFile.PrintParameterPerLayerModifiers is not null) { if (SlicerFile.PrintParameterPerLayerModifiers.Contains(FileFormat.PrintParameterModifier.LiftHeight)) - CurrentLayerProperties.Add(new StringTag(nameof(layer.LiftHeight), - $"{layer.LiftHeight.ToString(CultureInfo.InvariantCulture)}mm @ {layer.LiftSpeed.ToString(CultureInfo.InvariantCulture)}mm/min")); - //CurrentLayerProperties.Adnew StringTagg>(nameof(layer.LiftSpeed), $"{layer.LiftSpeed.ToString(CultureInfo.InvariantCulture)}mm/min")); + CurrentLayerProperties.Add(new ValueDescription($"{layer.LiftHeight.ToString(CultureInfo.InvariantCulture)}mm @ {layer.LiftSpeed.ToString(CultureInfo.InvariantCulture)}mm/min", nameof(layer.LiftHeight))); if (SlicerFile.PrintParameterPerLayerModifiers.Contains(FileFormat.PrintParameterModifier.RetractSpeed)) - CurrentLayerProperties.Add(new StringTag(nameof(layer.RetractSpeed), - $"{layer.RetractSpeed}mm/min")); + CurrentLayerProperties.Add(new ValueDescription($"{layer.RetractSpeed}mm/min", + nameof(layer.RetractSpeed))); if (SlicerFile.PrintParameterPerLayerModifiers.Contains(FileFormat.PrintParameterModifier.LightOffDelay)) - CurrentLayerProperties.Add(new StringTag(nameof(layer.LightOffDelay), - $"{layer.LightOffDelay}s")); + CurrentLayerProperties.Add(new ValueDescription($"{layer.LightOffDelay}s", + nameof(layer.LightOffDelay))); if (SlicerFile.PrintParameterPerLayerModifiers.Contains(FileFormat.PrintParameterModifier.LightPWM)) - CurrentLayerProperties.Add(new StringTag(nameof(layer.LightPWM), layer.LightPWM.ToString())); + CurrentLayerProperties.Add(new ValueDescription(layer.LightPWM.ToString(), nameof(layer.LightPWM))); } var materialMillilitersPercent = layer.MaterialMillilitersPercent; if (!float.IsNaN(materialMillilitersPercent)) { - CurrentLayerProperties.Add(new StringTag(nameof(layer.MaterialMilliliters), $"{layer.MaterialMilliliters}ml ({materialMillilitersPercent:F2}%)")); + CurrentLayerProperties.Add(new ValueDescription($"{layer.MaterialMilliliters}ml ({materialMillilitersPercent:F2}%)", nameof(layer.MaterialMilliliters))); } } diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs index 669b180..4d29d55 100644 --- a/UVtools.WPF/MainWindow.Issues.cs +++ b/UVtools.WPF/MainWindow.Issues.cs @@ -111,20 +111,19 @@ namespace UVtools.WPF IsGUIEnabled = false; + ShowProgressWindow("Removing selected issues", false); Clipboard.Snapshot(); var task = await Task.Factory.StartNew(() => { - ShowProgressWindow("Removing selected issues"); - var progress = ProgressWindow.RestartProgress(false); - progress.Reset("Removing selected issues", (uint)processIssues.Count); + Progress.Reset("Removing selected issues", (uint)processIssues.Count); bool result = false; try { Parallel.ForEach(processIssues, layerIssues => { - if (progress.Token.IsCancellationRequested) return; + if (Progress.Token.IsCancellationRequested) return; using (var image = SlicerFile[layerIssues.Key].LayerMat) { var bytes = image.GetPixelSpan<byte>(); @@ -162,7 +161,7 @@ namespace UVtools.WPF } } - progress.LockAndIncrement(); + Progress.LockAndIncrement(); }); if (layersRemove.Count > 0) @@ -275,6 +274,7 @@ namespace UVtools.WPF IsGUIEnabled = false; + ShowProgressWindow("Updating Issues"); var issueList = Issues.ToList(); @@ -291,12 +291,10 @@ namespace UVtools.WPF var resultIssues = await Task.Factory.StartNew(() => { - ShowProgressWindow("Updating Issues"); try { var issues = SlicerFile.LayerManager.GetAllIssues(islandConfig, overhangConfig, resinTrapConfig, - touchingBoundConfig, printHeightConfig, false, IgnoredIssues, - ProgressWindow.RestartProgress()); + touchingBoundConfig, printHeightConfig, false, IgnoredIssues, Progress); issues.RemoveAll(issue => issue.Type != LayerIssue.IssueType.Island && issue.Type != LayerIssue.IssueType.Overhang); // Remove all non islands and overhangs return issues; @@ -450,15 +448,14 @@ namespace UVtools.WPF Issues.Clear(); IsGUIEnabled = false; - + ShowProgressWindow("Computing Issues"); var resultIssues = await Task.Factory.StartNew(() => { - ShowProgressWindow("Computing Issues"); try { var issues = SlicerFile.LayerManager.GetAllIssues(islandConfig, overhangConfig, resinTrapConfig, touchingBoundConfig, - printHeightConfig, emptyLayersConfig, IgnoredIssues, ProgressWindow.RestartProgress()); + printHeightConfig, emptyLayersConfig, IgnoredIssues, Progress); return issues; } catch (OperationCanceledException) @@ -531,6 +528,13 @@ namespace UVtools.WPF } } + public void IssuesClear(bool clearIgnored = true) + { + Issues.Clear(); + if(clearIgnored) IgnoredIssues.Clear(); + UpdateLayerTrackerHighlightIssues(); + } + public IslandDetectionConfiguration GetIslandDetectionConfiguration(bool enable) { diff --git a/UVtools.WPF/MainWindow.PixelEditor.cs b/UVtools.WPF/MainWindow.PixelEditor.cs index e7984cd..1c4f0a4 100644 --- a/UVtools.WPF/MainWindow.PixelEditor.cs +++ b/UVtools.WPF/MainWindow.PixelEditor.cs @@ -378,15 +378,14 @@ namespace UVtools.WPF if (result == ButtonResult.Yes) { IsGUIEnabled = false; - + ShowProgressWindow("Drawing pixels"); Clipboard.Snapshot(); var task = await Task.Factory.StartNew(async () => { - ShowProgressWindow("Drawing pixels"); try { - SlicerFile.LayerManager.DrawModifications(Drawings, ProgressWindow.RestartProgress()); + SlicerFile.LayerManager.DrawModifications(Drawings, Progress); return true; } catch (OperationCanceledException) diff --git a/UVtools.WPF/MainWindow.Progress.cs b/UVtools.WPF/MainWindow.Progress.cs new file mode 100644 index 0000000..6f2d304 --- /dev/null +++ b/UVtools.WPF/MainWindow.Progress.cs @@ -0,0 +1,95 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.Timers; +using Avalonia.Threading; +using UVtools.Core.Operations; +using UVtools.WPF.Structures; + +namespace UVtools.WPF +{ + public partial class MainWindow + { + #region Members + public OperationProgress Progress { get; } = new(); + private Timer _progressTimer = new(200) { AutoReset = true }; + private long _progressLastTotalSeconds; + private LogItem _progressLogItem; + private bool _isProgressVisible; + + #endregion + + #region Properties + + public bool IsProgressVisible + { + get => _isProgressVisible; + set => RaiseAndSetIfChanged(ref _isProgressVisible, value); + } + + #endregion + + public void InitProgress() + { + _progressTimer.Elapsed += (sender, args) => + { + var elapsedSeconds = Progress.StopWatch.ElapsedMilliseconds / 1000; + if (_progressLastTotalSeconds == elapsedSeconds) return; + /*Debug.WriteLine(StopWatch.ElapsedMilliseconds); + Debug.WriteLine(elapsedSeconds); + Debug.WriteLine(_lastTotalSeconds);*/ + _progressLastTotalSeconds = elapsedSeconds; + + + Dispatcher.UIThread.InvokeAsync(() => Progress.TriggerRefresh(), DispatcherPriority.Render); + + }; + } + + public void ProgressOnClickCancel() + { + if (!Progress.CanCancel) return; + DialogResult = DialogResults.Cancel; + Progress.CanCancel = false; + Progress.TokenSource.Cancel(); + } + + public void ProgressShow(string title, bool canCancel = true) + { + IsGUIEnabled = false; + Progress.Init(canCancel); + Progress.Title = title; + _progressLogItem = new(title); + + Progress.StopWatch.Restart(); + _progressLastTotalSeconds = 0; + + if (!_progressTimer.Enabled) + { + _progressTimer.Start(); + } + + Progress.TriggerRefresh(); + + IsProgressVisible = true; + + InvalidateVisual(); + } + + public void ProgressFinish() + { + _progressTimer.Stop(); + Progress.StopWatch.Stop(); + _progressLogItem.ElapsedTime = Math.Round(Progress.StopWatch.Elapsed.TotalSeconds, 2); + App.MainWindow.AddLog(_progressLogItem); + IsProgressVisible = false; + InvalidateVisual(); + } + } +} diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index 815468f..7b2f84e 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -9,10 +9,10 @@ Icon="/Assets/Icons/UVtools.ico" MinWidth="1024" MinHeight="600" - DragDrop.AllowDrop="True" - > - - <DockPanel> + DragDrop.AllowDrop="True"> + + <Grid RowDefinitions="*" ColumnDefinitions="*"> + <DockPanel Grid.Row="0" Grid.Column="0" IsEnabled="{Binding IsGUIEnabled}"> <Menu DockPanel.Dock="Top"> <MenuItem Name="MainMenu.File" Header="_File"> <MenuItem @@ -568,10 +568,10 @@ Items="{Binding CurrentLayerProperties}"> <DataGrid.Columns> <DataGridTextColumn Header="Name" - Binding="{Binding Content}" + Binding="{Binding Description}" Width="Auto" /> <DataGridTextColumn Header="Value" - Binding="{Binding TagString}" + Binding="{Binding Value}" Width="Auto" /> </DataGrid.Columns> @@ -1751,9 +1751,9 @@ <Grid IsEnabled="{Binding IsFileLoaded}" ColumnDefinitions="*" RowDefinitions="Auto,*,Auto" Margin="5"> - <Grid - IsEnabled="{Binding IsFileLoaded}" - ColumnDefinitions="*,Auto" RowDefinitions="Auto" Margin="5"> + <Grid Grid.Row="0" Grid.Column="0" + IsEnabled="{Binding IsFileLoaded}" + ColumnDefinitions="*,Auto" RowDefinitions="Auto" Margin="5"> <WrapPanel HorizontalAlignment="Left" Grid.Row="0" Orientation="Horizontal"> <ToggleButton IsChecked="{Binding ShowLayerImageRotated}" @@ -2037,4 +2037,50 @@ </DockPanel> + <Grid Grid.Row="0" Grid.Column="0" + RowDefinitions="*,Auto,*" ColumnDefinitions="*,Auto,*" + IsEnabled="{Binding IsProgressVisible}" + IsVisible="{Binding IsProgressVisible}"> + <Border Grid.Row="1" Grid.Column="1" MinWidth="450" + Background="White" + BorderBrush="WhiteSmoke" + BorderThickness="5" + CornerRadius="5"> + <Grid RowDefinitions="Auto,Auto,Auto,Auto" + ColumnDefinitions="*"> + <TextBlock + Grid.Row="0" + Margin="10" Text="{Binding Progress.Title}"/> + <TextBlock + Grid.Row="1" + Margin="10,0,10,10" Text="{Binding Progress.ElapsedTimeStr, StringFormat=Elapsed Time: \{0\}}"/> + <TextBlock + Grid.Row="2" + Margin="10,0,10,10" Text="{Binding Progress.Description}" HorizontalAlignment="Center"/> + + <Grid + Grid.Row="3" + RowDefinitions="30" ColumnDefinitions="*,100"> + <ProgressBar + Grid.Column="0" + Minimum="0" + Maximum="100" + VerticalAlignment="Stretch" + IsIndeterminate="{Binding Progress.IsIndeterminate}" + Value="{Binding Progress.ProgressPercent}" ShowProgressText="True"/> + <Button + IsEnabled="{Binding Progress.CanCancel}" + Command="{Binding ProgressOnClickCancel}" + Grid.Column="1" + IsCancel="True" + VerticalAlignment="Stretch" + HorizontalAlignment="Stretch" + VerticalContentAlignment="Center" + HorizontalContentAlignment="Center" + Content="Cancel"/> + </Grid> + </Grid> + </Border> + </Grid> + </Grid> </uc:WindowEx> diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 8ed3006..0f2fbe8 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -48,7 +48,7 @@ namespace UVtools.WPF #region Controls - public ProgressWindow ProgressWindow = new (); + //public ProgressWindow ProgressWindow = new (); public static MenuItem[] MenuTools { get; } = { @@ -328,7 +328,7 @@ namespace UVtools.WPF #region Members - public Stopwatch LastStopWatch = new Stopwatch(); + public Stopwatch LastStopWatch = new(); private bool _isGUIEnabled = true; private uint _savesCount; @@ -352,14 +352,14 @@ namespace UVtools.WPF if (!RaiseAndSetIfChanged(ref _isGUIEnabled, value)) return; if (!_isGUIEnabled) { - ProgressWindow = new ProgressWindow(); + //ProgressWindow = new ProgressWindow(); return; } - //if (ProgressWindow is null) return; - LastStopWatch = ProgressWindow.StopWatch; - ProgressWindow.Close(); - ProgressWindow.Dispose(); + LastStopWatch = Progress.StopWatch; + ProgressFinish(); + //ProgressWindow.Close(DialogResults.OK); + //ProgressWindow.Dispose(); /*if (Dispatcher.UIThread.CheckAccess()) { ProgressWindow.Close(); @@ -441,6 +441,7 @@ namespace UVtools.WPF InitializeComponent(); App.ThemeSelector?.EnableThemes(this); + InitProgress(); InitInformation(); InitIssues(); InitPixelEditor(); @@ -757,9 +758,7 @@ namespace UVtools.WPF SlicerFile = null; SlicerProperties.Clear(); - Issues.Clear(); - IgnoredIssues.Clear(); - _issuesSliderCanvas.Children.Clear(); + IssuesClear(true); Drawings.Clear(); SelectedTabItem = TabInformation; @@ -862,13 +861,13 @@ namespace UVtools.WPF if (result == ButtonResult.Yes) { IsGUIEnabled = false; + ShowProgressWindow($"Downloading: {VersionChecker.Filename}", false); var task = await Task.Factory.StartNew(async () => { - ShowProgressWindow($"Downloading: {VersionChecker.Filename}"); try { - VersionChecker.AutoUpgrade(ProgressWindow.RestartProgress(false)); + VersionChecker.AutoUpgrade(Progress); return true; } catch (OperationCanceledException) @@ -942,13 +941,13 @@ namespace UVtools.WPF if (SlicerFile is null) return; IsGUIEnabled = false; - + ShowProgressWindow($"Opening: {fileNameOnly}"); + var task = await Task.Factory.StartNew( () => { - ShowProgressWindow($"Opening: {fileNameOnly}"); try { - SlicerFile.Decode(fileName, ProgressWindow.RestartProgress()); + SlicerFile.Decode(fileName, Progress); return true; } catch (OperationCanceledException) @@ -985,27 +984,35 @@ namespace UVtools.WPF return; } - if (SlicerFile is SL1File sl1File && Settings.Automations.AutoConvertSL1Files) + if (Settings.Automations.AutoConvertFiles) { - string fileExtension = sl1File.LookupCustomValue<string>(SL1File.Keyword_FileFormat, null); - if (!string.IsNullOrWhiteSpace(fileExtension)) + string convertFileExtension = SlicerFile switch + { + SL1File sl1File => sl1File.LookupCustomValue<string>(SL1File.Keyword_FileFormat, null), + VDTFile vdtFile => vdtFile.LookupCustomValue<string>(SL1File.Keyword_FileFormat, null), + _ => null + }; + + if (!string.IsNullOrWhiteSpace(convertFileExtension)) { - fileExtension = fileExtension.ToLower(CultureInfo.InvariantCulture); - var convertToFormat = FileFormat.FindByExtension(fileExtension); + convertFileExtension = convertFileExtension.ToLower(CultureInfo.InvariantCulture); + var convertToFormat = FileFormat.FindByExtension(convertFileExtension); if (convertToFormat is not null) { - var directory = Path.GetDirectoryName(sl1File.FileFullPath); - var filename = FileFormat.GetFileNameStripExtensions(sl1File.FileFullPath); + var directory = Path.GetDirectoryName(SlicerFile.FileFullPath); + var filename = FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath); FileFormat convertedFile = null; IsGUIEnabled = false; + ShowProgressWindow($"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {convertFileExtension}"); task = await Task.Factory.StartNew(() => { - ShowProgressWindow($"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {fileExtension}"); try { - convertedFile = sl1File.Convert(convertToFormat, Path.Combine(directory, $"{filename}.{fileExtension}"), ProgressWindow.RestartProgress()); + convertedFile = SlicerFile.Convert(convertToFormat, + Path.Combine(directory, $"{filename}.{convertFileExtension}"), + Progress); return true; } catch (OperationCanceledException) @@ -1014,7 +1021,8 @@ namespace UVtools.WPF catch (Exception exception) { Dispatcher.UIThread.InvokeAsync(async () => - await this.MessageBoxError(exception.ToString(), "Error while converting the file")); + await this.MessageBoxError(exception.ToString(), + "Error while converting the file")); } return false; @@ -1181,38 +1189,35 @@ namespace UVtools.WPF } } - private async void ShowProgressWindow(string title) + private async void ShowProgressWindow(string title, bool canCancel = true) { if (Dispatcher.UIThread.CheckAccess()) { - ProgressWindow.SetTitle(title); - await ProgressWindow.ShowDialog(this); + ProgressShow(title, canCancel); + + //ProgressWindow.SetTitle(title); + //await ProgressWindow.ShowDialog(this); } else { await Dispatcher.UIThread.InvokeAsync(async () => { - try + ProgressShow(title, canCancel); + /*try { - ProgressWindow.SetTitle(title); - await ProgressWindow.ShowDialog(this); + + //ProgressWindow.SetTitle(title); + //await ProgressWindow.ShowDialog(this); } catch (Exception e) { Debug.WriteLine(e); - } + }*/ }); } } - private void ShowProgressWindowSync(string title) - { - ProgressWindow = new ProgressWindow(title); - } - - - private async void ConvertToOnTapped(object? sender, RoutedEventArgs e) { if (sender is not MenuItem item) return; @@ -1232,13 +1237,13 @@ namespace UVtools.WPF IsGUIEnabled = false; + ShowProgressWindow($"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {Path.GetExtension(result)}"); var task = await Task.Factory.StartNew(() => { - ShowProgressWindow($"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {Path.GetExtension(result)}"); try { - return SlicerFile.Convert(fileExtension.GetFileFormat(), result, ProgressWindow.RestartProgress()) is not null; + return SlicerFile.Convert(fileExtension.GetFileFormat(), result, Progress) is not null; } catch (OperationCanceledException) { @@ -1309,14 +1314,13 @@ namespace UVtools.WPF var tempFile = filepath + FileFormat.TemporaryFileAppend; IsGUIEnabled = false; + ShowProgressWindow($"Saving {Path.GetFileName(filepath)}"); var task = await Task.Factory.StartNew( () => { - ShowProgressWindow($"Saving {Path.GetFileName(filepath)}"); - try { - SlicerFile.SaveAs(tempFile, ProgressWindow.RestartProgress()); + SlicerFile.SaveAs(tempFile, Progress); if (File.Exists(filepath)) { File.Delete(filepath); @@ -1383,13 +1387,13 @@ namespace UVtools.WPF string finalPath = Path.Combine(result, fileNameNoExt); IsGUIEnabled = false; + ShowProgressWindow($"Extracting {Path.GetFileName(SlicerFile.FileFullPath)}"); await Task.Factory.StartNew(() => { - ShowProgressWindow($"Extracting {Path.GetFileName(SlicerFile.FileFullPath)}"); try { - SlicerFile.Extract(finalPath, true, true, ProgressWindow.RestartProgress()); + SlicerFile.Extract(finalPath, true, true, Progress); } catch (OperationCanceledException) { @@ -1508,16 +1512,15 @@ namespace UVtools.WPF } IsGUIEnabled = false; + ShowProgressWindow(baseOperation.ProgressTitle, baseOperation.CanCancel); Clipboard.Snapshot(); var result = await Task.Factory.StartNew(() => { - ShowProgressWindow(baseOperation.ProgressTitle); - try { - return baseOperation.Execute(ProgressWindow.RestartProgress(baseOperation.CanCancel)); + return baseOperation.Execute(Progress); } catch (OperationCanceledException) { @@ -1544,11 +1547,16 @@ namespace UVtools.WPF CanSave = true; + if(baseOperation.GetType().Name.StartsWith("OperationCalibrate")) + { + IssuesClear(); + } + switch (baseOperation) { // Tools case OperationRepairLayers operation: - OnClickDetectIssues(); + await OnClickDetectIssues(); break; } } diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 524c96d..ccdc2f6 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.9.0</Version> + <Version>2.9.1</Version> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> @@ -55,7 +55,6 @@ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> <None Include="..\CHANGELOG.md" Link="CHANGELOG.md" /> - <None Include="..\CreateRelease.WPF.ps1" Link="CreateRelease.WPF.ps1" /> <None Include="..\CREDITS.md" Link="CREDITS.md" /> <None Include="..\LICENSE"> <Pack>True</Pack> diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs index 9d2123f..288e0fe 100644 --- a/UVtools.WPF/UserSettings.cs +++ b/UVtools.WPF/UserSettings.cs @@ -1121,7 +1121,7 @@ namespace UVtools.WPF public sealed class AutomationsUserSettings : BindableBase { private bool _saveFileAfterModifications = true; - private bool _autoConvertSl1Files = true; + private bool _autoConvertFiles = true; private bool _changeOnlyLightOffDelayIfZero = true; private decimal _lightOffDelay = 2.5m; private decimal _bottomLightOffDelay = 3m; @@ -1132,10 +1132,10 @@ namespace UVtools.WPF set => RaiseAndSetIfChanged(ref _saveFileAfterModifications, value); } - public bool AutoConvertSL1Files + public bool AutoConvertFiles { - get => _autoConvertSl1Files; - set => RaiseAndSetIfChanged(ref _autoConvertSl1Files, value); + get => _autoConvertFiles; + set => RaiseAndSetIfChanged(ref _autoConvertFiles, value); } public bool ChangeOnlyLightOffDelayIfZero diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml b/UVtools.WPF/Windows/SettingsWindow.axaml index 1799cf3..16c1ab1 100644 --- a/UVtools.WPF/Windows/SettingsWindow.axaml +++ b/UVtools.WPF/Windows/SettingsWindow.axaml @@ -1431,13 +1431,14 @@ > <StackPanel Orientation="Vertical"> - <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="PrusaSlicer SL1 files"/> + <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="File convertion"/> <StackPanel Margin="10" Orientation="Vertical" Spacing="10"> - <CheckBox IsChecked="{Binding Settings.Automations.AutoConvertSL1Files}" - ToolTip.Tip="Converts SL1 files to the format specified on 'PrusaSlicer - Printer - Notes' on 'FILEFORMAT_XXX' variable. + <CheckBox IsChecked="{Binding Settings.Automations.AutoConvertFiles}" + ToolTip.Tip="1) Converts the SL1 files to the format specified on 'PrusaSlicer - Printer - Notes' on 'FILEFORMAT_XXX' variable. +
2) Converts the VDT files to the format specified on 'Voxeldance Tango - Printer settings - Notes' on 'FILEFORMAT_XXX' variable. 
A new file with same name but a new extension will be created and overwrite any previous file. 
After a successful conversion the new file will automatically load in instead of the loaded SL1 file." - Content="Auto convert SL1 files to the target format when possible and load it back"/> + Content="Auto convert SL1 and VDT files to the target format when possible and load it back"/> </StackPanel> </StackPanel> diff --git a/UVtools.WPF/Windows/ToolWindow.axaml b/UVtools.WPF/Windows/ToolWindow.axaml index 6019b5f..ac5caa9 100644 --- a/UVtools.WPF/Windows/ToolWindow.axaml +++ b/UVtools.WPF/Windows/ToolWindow.axaml @@ -11,8 +11,7 @@ Title="Tool" Icon="/Assets/Icons/UVtools.ico"> - - <Grid RowDefinitions="Auto,Auto,Auto,Auto,*,Auto"> + <Grid RowDefinitions="Auto,Auto,Auto,Auto,*,Auto"> <!-- Description --> <Border Grid.Row="0" @@ -420,4 +419,5 @@ </Border> </Grid> + </controls:WindowEx> |