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:** diff --git a/README.md b/README.md index d21e628..21f8070 100644 --- a/README.md +++ b/README.md @@ -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. + * 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. + * 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 GetAllValuesAndDescriptions(Type t) + { + if (!t.IsEnum) + throw new ArgumentException($"{nameof(t)} must be an enum type"); + + return Enum.GetValues(t).Cast().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 /// 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 rawData = new List(); + List 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); + 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; + } - 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; - 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)) + using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write); + outputFile.WriteSerialize(HeaderSettings); + outputFile.WriteBytes(PreviewEncode(Thumbnails[0])); + outputFile.WriteSerialize(LayerSettings); + + var layerData = new LayerData[LayerCount]; + + Parallel.For(0, LayerCount, layerIndex => { - outputFile.WriteSerialize(HeaderSettings); - outputFile.WriteBytes(PreviewEncode(Thumbnails[0])); - LayerSettings.LayerCount = LayerCount; - outputFile.WriteSerialize(LayerSettings); - - LayerData[] layerData = new LayerData[LayerCount]; - - 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); - } - - progress.LockAndIncrement(); - }); - - 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++; + layerData[layerIndex] = new LayerData(); + layerData[layerIndex].Encode(mat); } + + progress.LockAndIncrement(); + }); + + 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++; } 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
(inputFile); + if (HeaderSettings.Tag1 != Header.TAG1 || HeaderSettings.Tag2 != Header.TAG2) { - HeaderSettings = Helpers.Deserialize
(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(inputFile); - currentOffset += (uint)Helpers.Serializer.SizeOf(LayerSettings); + LayerSettings = Helpers.Deserialize(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(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(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(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(); + 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(); + } #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. - * 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, IComparable - { - 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 - { - 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 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. + * 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 + { + 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 _files = new(); + private ObservableCollection _files = new(); #endregion #region Overrides @@ -161,7 +161,7 @@ namespace UVtools.Core.Operations } [XmlIgnore] - public ObservableCollection Files + public ObservableCollection 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)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)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(); - for (byte i = 1; i < Item.Modifier; i++) + if (_antiAliasingType == OperationLayerReHeightAntiAliasingType.Average) { - using var nextMat = SlicerFile[layerIndex+i].LayerMat; - CvInvoke.Add(mat, nextMat, mat); + matSum.ConvertTo(aaAverageSum, DepthType.Cv16U); } - /*if (_antiAliasing) + for (byte i = 1; i < SelectedItem.Modifier; i++) { - CvInvoke.Subtract(mat, original, mat); - CvInvoke.PyrDown(mat, mat); - CvInvoke.PyrUp(mat, mat); - CvInvoke.Add(original, mat, mat); - }*/ + using var nextMat = SlicerFile[layerIndex+i].LayerMat; + + 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(); + } + } + + switch (_antiAliasingType) + { + 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 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 2.9.0 + 2.9.1 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 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 @@  + + + + 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 @@ + Items="{Binding Operation.BlurOperation, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" + SelectedItem="{Binding Operation.BlurOperation, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/> - + 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 /// /// /// - public async Task ValidateFormFromString(StringTag text) => await ValidateFormFromString(text?.ToString()); + public async Task 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("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()); + Operation.Files.RemoveMany(FilesListBox.SelectedItems.OfType()); } 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"> - + + + - - - + + + + + + + + + - - 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}}" /> 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. + * 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. + * 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 SlicerProperties { get; } = new ObservableCollection(); + public ObservableCollection SlicerProperties { get; } = new(); public DataGrid PropertiesGrid; public DataGrid CurrentLayerGrid; private uint _visibleThumbnailIndex; private Bitmap _visibleThumbnailImage; - private ObservableCollection _currentLayerProperties = new ObservableCollection(); + private ObservableCollection _currentLayerProperties = new(); - public ObservableCollection CurrentLayerProperties + public ObservableCollection 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(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(); @@ -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. + * 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" - > - - + DragDrop.AllowDrop="True"> + + + @@ -1751,9 +1751,9 @@ - + + + + + + + + + + +