diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b7ac10a8..67f7de6c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,7 +38,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/devskim.yml b/.github/workflows/devskim.yml index ae93d797..edc80337 100644 --- a/.github/workflows/devskim.yml +++ b/.github/workflows/devskim.yml @@ -23,7 +23,7 @@ jobs: security-events: write steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run DevSkim scanner uses: microsoft/DevSkim-Action@v1 diff --git a/.github/workflows/package-publish.yml b/.github/workflows/package-publish.yml index ed2ef2da..2f2e615a 100644 --- a/.github/workflows/package-publish.yml +++ b/.github/workflows/package-publish.yml @@ -71,7 +71,7 @@ jobs: timeout-minutes: 15 steps: - name: 🛒 Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Verify commit exists in origin/master run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a643552..6b172d77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,21 @@ # Changelog -## /10/2023 - v4.0.3 +## 09/11/2023 - v4.0.4 + +- **FileFormat Anycubic:** + - (Fix) Preview marker size and `PropertyFields` for given file version + - (Fix) Do not cache and reuse layer images if equal on file write, as that is causing "file damage" message (#782, #787) +- **Tools:** + - **Mask:** + - (Improvement) Apply the mask with Multiply instead of BitwiseAnd (#747) + - **Layer and pixel arithmetic**: + - (Improvement) Use the byte scale value (1/255) for multiply operations + - **Infill:** + - (Add) Floor/Ceil thickness (#785) + - (Fix) Remove the debug blocking window when selecting the honeycomb pattern +- (Upgrade) .NET from 6.0.23 to 6.0.24 + +## 19/10/2023 - v4.0.3 - (Change) macOS: Change from Control to Command key to activate shortcuts from main menu (#766) - (Upgrade) .NET from 6.0.22 to 6.0.23 diff --git a/CREDITS.md b/CREDITS.md index 5b526f3e..bbf8e57a 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -78,4 +78,5 @@ - Kevin Bullmann - Robert Redden - Nick Spirov -- Sylvain Chartrand \ No newline at end of file +- Sylvain Chartrand +- Michael Pullen \ No newline at end of file diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 037f86ed..6296823a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,4 +1,13 @@ -- (Change) macOS: Change from Control to Command key to activate shortcuts from main menu (#766) -- (Upgrade) .NET from 6.0.22 to 6.0.23 -- (Upgrade) AvaloniaUI from 11.0.4 to 11.0.5 +- **FileFormat Anycubic:** + - (Fix) Preview marker size and `PropertyFields` for given file version + - (Fix) Do not cache and reuse layer images if equal on file write, as that is causing "file damage" message (#782, #787) +- **Tools:** + - **Mask:** + - (Improvement) Apply the mask with Multiply instead of BitwiseAnd (#747) + - **Layer and pixel arithmetic**: + - (Improvement) Use the byte scale value (1/255) for multiply operations + - **Infill:** + - (Add) Floor/Ceil thickness (#785) + - (Fix) Remove the debug blocking window when selecting the honeycomb pattern +- (Upgrade) .NET from 6.0.23 to 6.0.24 diff --git a/Scripts/010 Editor/PhotonWorkshop.bt b/Scripts/010 Editor/PhotonWorkshop.bt index 7cca4727..05a1d757 100644 --- a/Scripts/010 Editor/PhotonWorkshop.bt +++ b/Scripts/010 Editor/PhotonWorkshop.bt @@ -118,7 +118,7 @@ if(fileMark.PreviewAddress > 0){ } typedef struct(int size) { - BYTE layerDataBlock[size] ; + ubyte layerDataBlock[size] ; } layerRawData; if(fileMark.Version >= 515 && fileMark.LayerImageColorAddress > 0) @@ -298,6 +298,7 @@ if(fileMark.LayerDefinitionAddress > 0){ struct LAYERSDATA{ local uint i; for( i = 0; i < layerDefinition.LayerCount; i++ ){ + FSeek(layers.layerDef[i].DataAddress); layerRawData lD(layers.layerDef[i].DataLength); } } layersData; diff --git a/UVtools.Core/Extensions/DrawingExtensions.cs b/UVtools.Core/Extensions/DrawingExtensions.cs index f0f25bcd..8b0811cf 100644 --- a/UVtools.Core/Extensions/DrawingExtensions.cs +++ b/UVtools.Core/Extensions/DrawingExtensions.cs @@ -6,9 +6,9 @@ namespace UVtools.Core.Extensions; public static class DrawingExtensions { public static Color FactorColor(this Color color, byte pixelColor, byte min = 0, byte max = byte.MaxValue) => - FactorColor(color, pixelColor / 255f, min, max); + FactorColor(color, pixelColor / 255.0, min, max); - public static Color FactorColor(this Color color, float factor, byte min = 0, byte max = byte.MaxValue) + public static Color FactorColor(this Color color, double factor, byte min = 0, byte max = byte.MaxValue) { byte r = (byte)(color.R == 0 ? 0 : Math.Min(Math.Max(min, color.R * factor), max)); diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index 1e383622..327c6cbe 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -39,6 +39,13 @@ public static class EmguExtensions public static readonly Point AnchorCenter = new (-1, -1); public static readonly Mat Kernel3x3Rectangle = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), AnchorCenter); + + /// + /// Gets the scale relation for 0-255 byte value.
+ /// Constant for: 1/255.0 = 0.00392156862745098039 + ///
+ /// Last three digits will drop (...039) + public const double ByteScale = 1 / 255.0; #endregion #region Initializers methods diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index cc776c4a..89b40ab4 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -408,6 +408,7 @@ public override string ToString() public class Preview : AnycubicNamedTable { protected override string DefaultTableName => "PREVIEW"; + protected override bool IncludeBaseTableLength => true; /// /// Gets the image width, in pixels. @@ -568,7 +569,7 @@ public class Machine : AnycubicNamedTable [FieldOrder(0)][FieldLength(96)][SerializeAs(SerializedType.TerminatedString)] public string MachineName { get; set; } = null!; [FieldOrder(1)][FieldLength(16)][SerializeAs(SerializedType.TerminatedString)] public string LayerImageFormat { get; set; } = "pw0img"; [FieldOrder(2)] public uint MaxAntialiasingLevel { get; set; } = 16; - [FieldOrder(3)] public uint PropertyFields { get; set; } = 7; + [FieldOrder(3)] public uint PropertyFields { get; set; } = 1; [FieldOrder(4)] public float DisplayWidth { get; set; } [FieldOrder(5)] public float DisplayHeight { get; set; } [FieldOrder(6)] public float MachineZ { get; set; } @@ -1935,7 +1936,8 @@ protected override void EncodeInternally(OperationProgress progress) MachineSettings.PropertyFields = Version switch { >= VERSION_518 => 15, - _ => 7 + >= VERSION_517 => 7, + _ => 1 }; HeaderSettings.PixelSizeUm = PixelSizeMicronsMax; @@ -2018,7 +2020,7 @@ protected override void EncodeInternally(OperationProgress progress) FileMarkSettings.ExtraAddress = FileMarkSettings.LayerImageAddress; } - var layersHash = new Dictionary(); + //var layersHash = new Dictionary(); foreach (var batch in BatchLayersIndexes()) { @@ -2040,7 +2042,7 @@ protected override void EncodeInternally(OperationProgress progress) var layerDef = LayersDefinition.Layers[layerIndex]; - var hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle); + /*var hash = CryptExtensions.ComputeSHA1Hash(layerDef.EncodedRle); if (layersHash.TryGetValue(hash, out var layerDataHash)) { @@ -2048,16 +2050,16 @@ protected override void EncodeInternally(OperationProgress progress) layerDef.DataLength = layerDataHash.DataLength; } else - { + {*/ layerDef.DataAddress = (uint)outputFile.Position; SubLayersDefinition[layerIndex].SetFrom(layerDef); - layersHash.Add(hash, layerDef); + //layersHash.Add(hash, layerDef); outputFile.WriteBytes(layerDef.EncodedRle); layerDef.EncodedRle = null!; - } + //} } } diff --git a/UVtools.Core/Helpers.cs b/UVtools.Core/Helpers.cs index 50facf7a..5cc47cef 100644 --- a/UVtools.Core/Helpers.cs +++ b/UVtools.Core/Helpers.cs @@ -46,5 +46,5 @@ public static void SwapVariables(ref T var1, ref T var2) (var1, var2) = (var2, var1); } - public static float BrightnessToPercent(byte brightness, byte roundPlates = 2) => (float)Math.Round(brightness * 100 / 255f, roundPlates); + public static float BrightnessToPercent(byte brightness, byte roundPlates = 2) => (float)Math.Round(brightness * 100 / 255.0, roundPlates); } \ No newline at end of file diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs index a40136f2..876b4db5 100644 --- a/UVtools.Core/Operations/OperationInfill.cs +++ b/UVtools.Core/Operations/OperationInfill.cs @@ -17,14 +17,16 @@ using System.Threading.Tasks; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; +using UVtools.Core.Layers; namespace UVtools.Core.Operations; -public sealed class OperationInfill : Operation +public sealed class OperationInfill : Operation, IEquatable { #region Members private InfillAlgorithm _infillType = InfillAlgorithm.CubicCrossAlternating; + private decimal _floorCeilThickness = 3.0m; private ushort _wallThickness = 64; private ushort _infillThickness = 45; private ushort _infillSpacing = 300; @@ -87,6 +89,12 @@ public InfillAlgorithm InfillType set => RaiseAndSetIfChanged(ref _infillType, value); } + public decimal FloorCeilThickness + { + get => _floorCeilThickness; + set => RaiseAndSetIfChanged(ref _floorCeilThickness, value); + } + public ushort WallThickness { get => _wallThickness; @@ -119,7 +127,7 @@ public bool ReinforceInfill public override string ToString() { - var result = $"[{_infillType}] [Wall: {_wallThickness}px] [B: {_infillBrightness}px] [T: {_infillThickness}px] [S: {_infillSpacing}px] [R: {_reinforceInfill}]" + LayerRangeString; + var result = $"[{_infillType}] [Floor/Ceil: {_floorCeilThickness}mm] [Wall: {_wallThickness}px] [B: {_infillBrightness}px] [T: {_infillThickness}px] [S: {_infillSpacing}px] [R: {_reinforceInfill}]" + LayerRangeString; if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; return result; } @@ -136,9 +144,11 @@ public OperationInfill(FileFormat slicerFile) : base(slicerFile) { } #region Equality - private bool Equals(OperationInfill other) + public bool Equals(OperationInfill? other) { - return _infillType == other._infillType && _wallThickness == other._wallThickness && _infillThickness == other._infillThickness && _infillSpacing == other._infillSpacing && _infillBrightness == other._infillBrightness && _reinforceInfill == other._reinforceInfill; + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return _infillType == other._infillType && _floorCeilThickness == other._floorCeilThickness && _wallThickness == other._wallThickness && _infillThickness == other._infillThickness && _infillSpacing == other._infillSpacing && _infillBrightness == other._infillBrightness && _reinforceInfill == other._reinforceInfill; } public override bool Equals(object? obj) @@ -148,7 +158,17 @@ public override bool Equals(object? obj) public override int GetHashCode() { - return HashCode.Combine((int) _infillType, _wallThickness, _infillThickness, _infillSpacing, _infillBrightness, _reinforceInfill); + return HashCode.Combine((int)_infillType, _floorCeilThickness, _wallThickness, _infillThickness, _infillSpacing, _infillBrightness, _reinforceInfill); + } + + public static bool operator ==(OperationInfill? left, OperationInfill? right) + { + return Equals(left, right); + } + + public static bool operator !=(OperationInfill? left, OperationInfill? right) + { + return !Equals(left, right); } #endregion @@ -161,19 +181,21 @@ protected override bool ExecuteInternally(OperationProgress progress) if (_infillType == InfillAlgorithm.Honeycomb) { mask = GetHoneycombMask(GetRoiSizeOrVolumeSize()); - CvInvoke.Imshow("Honeycomb", mask); - CvInvoke.WaitKey(); + //CvInvoke.Imshow("Honeycomb", mask); + //CvInvoke.WaitKey(); } else if (_infillType == InfillAlgorithm.Concentric) { mask = GetConcentricMask(GetRoiSizeOrVolumeSize()); } + var clonedLayers = SlicerFile.CloneLayers(); + Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex => { progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; - Execute(mat, layerIndex, mask!); + Execute(mat, layerIndex, mask!, clonedLayers); SlicerFile[layerIndex].LayerMat = mat; progress.LockAndIncrement(); @@ -184,17 +206,18 @@ protected override bool ExecuteInternally(OperationProgress progress) public override bool Execute(Mat mat, params object[]? arguments) { - if (arguments is null || arguments.Length < 1) return false; + if (arguments is null || arguments.Length < 3) return false; var kernel = EmguExtensions.Kernel3x3Rectangle; + var infillColor = new MCvScalar(_infillBrightness); uint index = Convert.ToUInt32(arguments[0]); uint layerIndex = index - LayerIndexStart; - var infillColor = new MCvScalar(_infillBrightness); + var clonedLayers = (Layer[])arguments[2]; Mat? patternMask = null; - using Mat erode = new (); - using Mat diff = new (); - var target = GetRoiOrVolumeBounds(mat); + using var erode = new Mat(); + using var diff = new Mat(); + using var target = GetRoiOrVolumeBounds(mat); using var mask = GetMask(mat); bool disposeTargetMask = true; @@ -331,8 +354,7 @@ or InfillAlgorithm.CubicCrossAlternating } - CvInvoke.Repeat(infillPattern, target.Rows / infillPattern.Rows + 1, - target.Cols / infillPattern.Cols + 1, matPattern); + CvInvoke.Repeat(infillPattern, target.Rows / infillPattern.Rows + 1, target.Cols / infillPattern.Cols + 1, matPattern); patternMask = matPattern.Roi(target); disposeTargetMask = true; } @@ -466,18 +488,55 @@ or InfillAlgorithm.CubicCrossAlternating disposeTargetMask = true; } + using var surfaceMat = target.Clone(); + + decimal heightAccumulator = (decimal)SlicerFile[index].LayerHeight; + if (_floorCeilThickness > heightAccumulator) + { + for (int floorLayerIndex = (int)(index - 1); heightAccumulator <= _floorCeilThickness && floorLayerIndex >= 0; floorLayerIndex--) + { + using var floorMat = clonedLayers[floorLayerIndex].LayerMat; + using var floorMatRoi = GetRoiOrVolumeBounds(floorMat); + + CvInvoke.BitwiseAnd(surfaceMat, floorMatRoi, surfaceMat); + + heightAccumulator += (decimal)SlicerFile[floorLayerIndex + 1].PositionZ - (decimal)SlicerFile[floorLayerIndex].PositionZ; + } + + if (heightAccumulator <= _floorCeilThickness) return true; + + heightAccumulator = (decimal)SlicerFile[index].LayerHeight; + for (var ceilLayerIndex = index + 1; heightAccumulator <= _floorCeilThickness && ceilLayerIndex <= SlicerFile.LastLayerIndex; ceilLayerIndex++) + { + using var ceilMat = clonedLayers[ceilLayerIndex].LayerMat; + using var ceilMatRoi = GetRoiOrVolumeBounds(ceilMat); + + CvInvoke.BitwiseAnd(surfaceMat, ceilMatRoi, surfaceMat); + + heightAccumulator += (decimal)SlicerFile[ceilLayerIndex].PositionZ - (decimal)SlicerFile[ceilLayerIndex - 1].PositionZ; + } + + if (heightAccumulator <= _floorCeilThickness) return true; + } + //patternMask.Save("D:\\pattern.png"); - CvInvoke.Erode(target, erode, kernel, EmguExtensions.AnchorCenter, WallThickness, BorderType.Reflect101, - default); - CvInvoke.Subtract(target, erode, diff); + CvInvoke.Erode(target, erode, kernel, EmguExtensions.AnchorCenter, WallThickness, BorderType.Reflect101, default); + + CvInvoke.BitwiseAnd(erode, surfaceMat, erode, mask); + patternMask!.CopyTo(target, erode); + //target.SetTo(EmguExtensions.BlackColor, erode); + //erode.CopyTo(target); + //CvInvoke.BitwiseOr(target, patternMask, target, erode); + - CvInvoke.BitwiseAnd(erode, patternMask, target, mask); - CvInvoke.Add(target, diff, target, mask); + //CvInvoke.Subtract(target, erode, diff); + //CvInvoke.BitwiseAnd(erode, patternMask, target, mask); + //CvInvoke.Add(target, diff, target, mask); if (disposeTargetMask) { - patternMask!.Dispose(); + patternMask.Dispose(); } return true; diff --git a/UVtools.Core/Operations/OperationLayerArithmetic.cs b/UVtools.Core/Operations/OperationLayerArithmetic.cs index d4442a32..d41a6ed5 100644 --- a/UVtools.Core/Operations/OperationLayerArithmetic.cs +++ b/UVtools.Core/Operations/OperationLayerArithmetic.cs @@ -13,6 +13,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml.Serialization; +using UVtools.Core.Extensions; using UVtools.Core.FileFormats; namespace UVtools.Core.Operations; @@ -257,7 +258,7 @@ protected override bool ExecuteInternally(OperationProgress progress) { progress.PauseOrCancelIfRequested(); using var image = SlicerFile[operation.Operations[i].LayerIndex].LayerMat; - var imageRoi = GetRoiOrDefault(image); + using var imageRoi = GetRoiOrDefault(image); switch (operation.Operations[i - 1].Operator) { @@ -268,7 +269,7 @@ protected override bool ExecuteInternally(OperationProgress progress) CvInvoke.Subtract(resultRoi, imageRoi, resultRoi, imageMask); break; case LayerArithmeticOperators.Multiply: - CvInvoke.Multiply(resultRoi, imageRoi, resultRoi); + CvInvoke.Multiply(resultRoi, imageRoi, resultRoi, EmguExtensions.ByteScale); break; case LayerArithmeticOperators.Divide: CvInvoke.Divide(resultRoi, imageRoi, resultRoi); @@ -298,7 +299,7 @@ protected override bool ExecuteInternally(OperationProgress progress) if (operation.Operations.Count == 1 || HaveROIorMask) { using var mat = SlicerFile[layerIndex].LayerMat; - var matRoi = GetRoiOrDefault(mat); + using var matRoi = GetRoiOrDefault(mat); resultRoi.CopyTo(matRoi, imageMask); SlicerFile[layerIndex].LayerMat = mat; return; diff --git a/UVtools.Core/Operations/OperationLayerExportGif.cs b/UVtools.Core/Operations/OperationLayerExportGif.cs index 7bb026a7..da4b4fbf 100644 --- a/UVtools.Core/Operations/OperationLayerExportGif.cs +++ b/UVtools.Core/Operations/OperationLayerExportGif.cs @@ -231,7 +231,7 @@ protected override bool ExecuteInternally(OperationProgress progress) var layer = SlicerFile[layerIndex]; using var mat = layer.LayerMat; //using var matOriginal = mat.Clone(); - var matRoi = GetRoiOrDefault(mat); + using var matRoi = GetRoiOrDefault(mat); if (_scale != 100) { diff --git a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs index 31dd9770..2e55c591 100644 --- a/UVtools.Core/Operations/OperationLayerExportHeatMap.cs +++ b/UVtools.Core/Operations/OperationLayerExportHeatMap.cs @@ -168,7 +168,7 @@ protected override bool ExecuteInternally(OperationProgress progress) if (_cropByRoi && HaveROI) { - var sumMatRoi = GetRoiOrDefault(resultMat); + using var sumMatRoi = GetRoiOrDefault(resultMat); sumMatRoi.Save(_filePath); } else diff --git a/UVtools.Core/Operations/OperationLayerExportSkeleton.cs b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs index 3fb2c2e8..f5e95afd 100644 --- a/UVtools.Core/Operations/OperationLayerExportSkeleton.cs +++ b/UVtools.Core/Operations/OperationLayerExportSkeleton.cs @@ -87,7 +87,7 @@ public override void InitWithSlicerFile() protected override bool ExecuteInternally(OperationProgress progress) { using var skeletonSum = EmguExtensions.InitMat(SlicerFile.Resolution); - var skeletonSumRoi = GetRoiOrDefault(skeletonSum); + using var skeletonSumRoi = GetRoiOrDefault(skeletonSum); using var mask = GetMask(skeletonSum); @@ -95,7 +95,7 @@ protected override bool ExecuteInternally(OperationProgress progress) { progress.PauseIfRequested(); using var mat = SlicerFile[layerIndex].LayerMat; - var matRoi = GetRoiOrDefault(mat); + using var matRoi = GetRoiOrDefault(mat); using var skeletonRoi = matRoi.Skeletonize(); lock (progress.Mutex) { diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs index 3d514813..9ecbd845 100644 --- a/UVtools.Core/Operations/OperationMask.cs +++ b/UVtools.Core/Operations/OperationMask.cs @@ -12,6 +12,7 @@ using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; +using UVtools.Core.Extensions; using UVtools.Core.FileFormats; namespace UVtools.Core.Operations; @@ -112,7 +113,8 @@ public override bool Execute(Mat mat, params object[]? arguments) var target = GetRoiOrDefault(mat); using var mask = GetMask(mat); if (Mask!.Size != target.Size) return false; - CvInvoke.BitwiseAnd(target, Mask, target, mask); + //CvInvoke.BitwiseAnd(target, Mask, target, mask); + CvInvoke.Multiply(target, Mask, target, EmguExtensions.ByteScale); return true; } diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs index 35303be3..1166d818 100644 --- a/UVtools.Core/Operations/OperationPixelArithmetic.cs +++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs @@ -588,8 +588,8 @@ protected override bool ExecuteInternally(OperationProgress progress) using (var mat = layer.LayerMat) { using var original = mat.Clone(); - var originalRoi = GetMatRoiCropped(original); - var target = GetMatRoiCropped(mat); + using var originalRoi = GetMatRoiCropped(original); + using var target = GetMatRoiCropped(mat); Mat tempMat; if (_usePattern && IsUsePatternVisible) @@ -727,7 +727,7 @@ protected override bool ExecuteInternally(OperationProgress progress) CvInvoke.Subtract(target, tempMat, target, applyMask); break; case PixelArithmeticOperators.Multiply: - CvInvoke.Multiply(target, tempMat, target); + CvInvoke.Multiply(target, tempMat, target, EmguExtensions.ByteScale); if (_applyMethod != PixelArithmeticApplyMethod.All) ApplyMask(originalRoi, target, applyMask); break; case PixelArithmeticOperators.Divide: diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs index 00bcf29c..a01ec2b1 100644 --- a/UVtools.Core/Operations/OperationPixelDimming.cs +++ b/UVtools.Core/Operations/OperationPixelDimming.cs @@ -237,7 +237,7 @@ public byte Brightness } } - public float BrightnessPercent => (float)Math.Round(_brightness * 100 / 255f, 2); + public float BrightnessPercent => (float)Math.Round(_brightness * 100 / 255.0, 2); public ushort InfillGenThickness diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index b9622172..72267deb 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -2,7 +2,7 @@ AnyCPU;x64 - 4.0.3 + 4.0.4 True ..\documentation\$(AssemblyName).xml @@ -23,8 +23,8 @@ - - + + diff --git a/UVtools.UI/App.axaml b/UVtools.UI/App.axaml index 86d57dad..ba69e2ff 100644 --- a/UVtools.UI/App.axaml +++ b/UVtools.UI/App.axaml @@ -4,29 +4,30 @@ x:Class="UVtools.UI.App" RequestedThemeVariant="Default"> - - + + + - - - - - - - - - - + + + + + + + + + + - - - - + diff --git a/UVtools.UI/Controls/Tools/ToolInfillControl.axaml b/UVtools.UI/Controls/Tools/ToolInfillControl.axaml index ce192e0c..b94006bf 100644 --- a/UVtools.UI/Controls/Tools/ToolInfillControl.axaml +++ b/UVtools.UI/Controls/Tools/ToolInfillControl.axaml @@ -6,7 +6,7 @@ mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="300" x:Class="UVtools.UI.Controls.Tools.ToolInfillControl" x:DataType="tools:ToolInfillControl"> - @@ -19,14 +19,29 @@ SelectedItem="{Binding Operation.InfillType, Converter={StaticResource FromValueDescriptionToEnumConverter}}" HorizontalAlignment="Stretch"/> + + + + + - - - - - - - - diff --git a/UVtools.UI/Converters/NumericUpDownValueConverter .cs b/UVtools.UI/Converters/NumericUpDownValueConverter .cs new file mode 100644 index 00000000..eedbce5e --- /dev/null +++ b/UVtools.UI/Converters/NumericUpDownValueConverter .cs @@ -0,0 +1,55 @@ +/* + * 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.Globalization; +using Avalonia.Data.Converters; + +namespace UVtools.UI.Converters; + +internal class NumericUpDownValueConverter : IValueConverter +{ + public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is null) return default(decimal); + return (decimal?)((IConvertible)value).ToDecimal(culture); + } + + public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is null) + { + if (targetType == typeof(byte)) return byte.MinValue; + if (targetType == typeof(sbyte)) return (sbyte)0; + if (targetType == typeof(ushort)) return ushort.MinValue; + if (targetType == typeof(short)) return (short)0; + if (targetType == typeof(uint)) return uint.MinValue; + if (targetType == typeof(int)) return 0; + if (targetType == typeof(ulong)) return ulong.MinValue; + if (targetType == typeof(long)) return 0L; + if (targetType == typeof(float)) return 0f; + if (targetType == typeof(double)) return 0d; + if (targetType == typeof(decimal)) return 0M; + throw new ArgumentNullException($"Unsupported type: {targetType.FullName}"); + } + + if (targetType == typeof(byte)) return ((IConvertible)value).ToByte(culture); + if (targetType == typeof(sbyte)) return ((IConvertible)value).ToSByte(culture); + if (targetType == typeof(ushort)) return ((IConvertible)value).ToUInt16(culture); + if (targetType == typeof(short)) return ((IConvertible)value).ToInt16(culture); + if (targetType == typeof(uint)) return ((IConvertible)value).ToUInt32(culture); + if (targetType == typeof(int)) return ((IConvertible)value).ToInt32(culture); + if (targetType == typeof(ulong)) return ((IConvertible)value).ToUInt64(culture); + if (targetType == typeof(long)) return ((IConvertible)value).ToInt64(culture); + if (targetType == typeof(float)) return ((IConvertible)value).ToSingle(culture); + if (targetType == typeof(double)) return ((IConvertible)value).ToDouble(culture); + if (targetType == typeof(decimal)) return ((IConvertible)value).ToDecimal(culture); + + throw new ArgumentNullException($"Unsupported type: {targetType.FullName}"); + } +} \ No newline at end of file diff --git a/UVtools.UI/Structures/Color.cs b/UVtools.UI/Structures/Color.cs index d8e53bfa..38316bae 100644 --- a/UVtools.UI/Structures/Color.cs +++ b/UVtools.UI/Structures/Color.cs @@ -65,9 +65,9 @@ public static Color FromArgb(byte a, byte r, byte g, byte b) public static Color Empty => new(0,0,0,0); public Color FactorColor(byte pixelColor, byte min = 0, byte max = byte.MaxValue) => - FactorColor(pixelColor / 255f, min, max); + FactorColor(pixelColor / 255.0, min, max); - public Color FactorColor(float factor, byte min = 0, byte max = byte.MaxValue) + public Color FactorColor(double factor, byte min = 0, byte max = byte.MaxValue) { byte r = (byte)(R == 0 ? 0 : Math.Min(Math.Max(min, R * factor), max)); diff --git a/UVtools.UI/UVtools.UI.csproj b/UVtools.UI/UVtools.UI.csproj index 06c3e72d..a3903702 100644 --- a/UVtools.UI/UVtools.UI.csproj +++ b/UVtools.UI/UVtools.UI.csproj @@ -2,7 +2,7 @@ WinExe UVtools - 4.0.3 + 4.0.4 AnyCPU;x64 true diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml index 5f0df8e2..922a9c61 100644 --- a/documentation/UVtools.Core.xml +++ b/documentation/UVtools.Core.xml @@ -471,6 +471,13 @@ Black color: 0, 0, 0, 255 + + + Gets the scale relation for 0-255 byte value.
+ Constant for: 1/255.0 = 0.00392156862745098039 +
+ Last three digits will drop (...039) +
Create a byte array of size of this