diff --git a/CHANGELOG.md b/CHANGELOG.md index 27353d0f..45e38d59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 14/09/2024 - v4.4.2 + +- (Add) Tool - Redraw model: Add multiple operator modes (#926) +- (Fix) Tool - Redraw model: Redo (Ctrl + Shift + Z) would cause a crash +- (Fix) Pixel Editor: Make the content scrollable when the window is resized to a smaller size +- (Fix) Calibration - Exposure time finder: When the "Multiple exposures" panel is collapsed it become disabled and unusable +- (Fix) Layer preview - Difference: Fixes the white background over black pixels +- (Fix) macOS: Change title "Avalonia Application" to "UVtools" on some app managers + ## 19/08/2024 - v4.4.1 - (Add) Pixel Editor: Fill tool and merge into Erase section, left click fills and right click erases diff --git a/Directory.Build.props b/Directory.Build.props index 3abb727e..32a9094c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -31,7 +31,7 @@ $(MSBuildThisFileDirectory)publish - 4.4.1 + 4.4.2 11.1.3 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 0c69cfce..1ab91a1b 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,7 @@ -- (Add) Pixel Editor: Fill tool and merge into Erase section, left click fills and right click erases -- (Improvement) SL1: Implement missing material override properties -- (Fix) File formats: Error while trying to generate a thumbnail for a file that have a empty first layer (#912) -- (Upgrade) AvaloniaUI from 11.1.1 to 11.1.3 -- (Upgrade) .NET from 6.0.32 to 6.0.33 +- (Add) Tool - Redraw model: Add multiple operator modes (#926) +- (Fix) Tool - Redraw model: Redo (Ctrl + Shift + Z) would cause a crash +- (Fix) Pixel Editor: Make the content scrollable when the window is resized to a smaller size +- (Fix) Calibration - Exposure time finder: When the "Multiple exposures" panel is collapsed it become disabled and unusable +- (Fix) Layer preview - Difference: Fixes the white background over black pixels +- (Fix) macOS: Change title "Avalonia Application" to "UVtools" on some app managers diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index b719744b..cf8c03b9 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -67,15 +67,6 @@ public static byte[] CreateByteArray(this Mat mat) public static Mat New(this Mat mat) => new(mat.Size, mat.Depth, mat.NumberOfChannels); - /// - /// Creates a new with same size and type of the source - /// - /// - /// - /// - public static Mat New(this Mat src, MCvScalar color) - => InitMat(src.Size, color, src.NumberOfChannels, src.Depth); - /// /// Creates a new blanked (All zeros) with same size and type of the source /// @@ -97,9 +88,10 @@ public static UMat NewZeros(this UMat mat) /// /// /// + /// /// - public static Mat NewSetTo(this Mat mat, MCvScalar color) - => InitMat(mat.Size, color, mat.NumberOfChannels, mat.Depth); + public static Mat NewSetTo(this Mat mat, MCvScalar color, IInputArray? mask = null) + => InitMat(mat.Size, color, mat.NumberOfChannels, mat.Depth, mask); /// @@ -134,12 +126,13 @@ public static UMat InitUMat(Size size, int channels = 1, DepthType depthType = D /// /// /// + /// /// - public static Mat InitMat(Size size, MCvScalar color, int channels = 1, DepthType depthType = DepthType.Cv8U) + public static Mat InitMat(Size size, MCvScalar color, int channels = 1, DepthType depthType = DepthType.Cv8U, IInputArray? mask = null) { if (size.IsEmpty) return new(); var mat = new Mat(size, depthType, channels); - mat.SetTo(color); + mat.SetTo(color, mask); return mat; } diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs index 2aeaf4ab..5d2059d4 100644 --- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -1293,7 +1293,7 @@ public Mat[] GetLayers(bool isPreview = false) var bars = Bars; var bulleyes = BullsEyes; var textSize = TextSize; - + int featuresMarginX = (int)(Xppmm * _featuresMargin); int featuresMarginY = (int)(Yppmm * _featuresMargin); ushort startCaseThickness = StaircaseThickness; diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs index e6912282..e5f3f936 100644 --- a/UVtools.Core/Operations/OperationRedrawModel.cs +++ b/UVtools.Core/Operations/OperationRedrawModel.cs @@ -10,6 +10,7 @@ using Emgu.CV.CvEnum; using Emgu.CV.Structure; using System; +using System.ComponentModel; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; @@ -23,6 +24,29 @@ namespace UVtools.Core.Operations; public class OperationRedrawModel : Operation #pragma warning restore CS0659 // Type overrides Object.Equals(object o) but does not override Object.GetHashCode() { + #region Enums + public enum RedrawModelOperators : byte + { + [Description("Set: to a brightness")] + Set, + [Description("Add: with a brightness")] + Add, + [Description("Subtract: with a brightness")] + Subtract, + [Description("Multiply: with a brightness")] + Multiply, + [Description("Divide: with a brightness")] + Divide, + [Description("Minimum: set to a brightness if is lower than the current pixel")] + Minimum, + [Description("Maximum: set to a brightness if is higher than the current pixel")] + Maximum, + [Description("AbsDiff: perform a absolute difference between pixel and brightness")] + AbsDiff, + } + + #endregion + #region Members private string _filePath = null!; @@ -30,6 +54,7 @@ public class OperationRedrawModel : Operation private bool _contactPointsOnly = true; private RedrawTypes _redrawType = RedrawTypes.Supports; private bool _ignoreContactLessPixels = true; + private RedrawModelOperators _operator = RedrawModelOperators.Minimum; #endregion @@ -44,7 +69,7 @@ public class OperationRedrawModel : Operation "Note: Run this tool prior to any made modification. You must find the optimal exposure/brightness combo, or supports can fail."; public override string ConfirmationText => "redraw the "+ (_redrawType == RedrawTypes.Supports ? "supports" : "model") + - $" with an brightness of {_brightness}?"; + " with an"+ (_redrawType == RedrawTypes.Model || !_contactPointsOnly ? $" {_operator}" : string.Empty) + $" brightness of {_brightness}?"; public override string ProgressTitle => "Redrawing " + (_redrawType == RedrawTypes.Supports ? "supports" : "model"); @@ -59,13 +84,30 @@ public class OperationRedrawModel : Operation sb.AppendLine("The selected file is not valid."); } + if (_redrawType == RedrawTypes.Model || _contactPointsOnly) + { + switch (_operator) + { + case RedrawModelOperators.Add: + case RedrawModelOperators.Subtract: + case RedrawModelOperators.Maximum: + case RedrawModelOperators.AbsDiff: + if (_brightness == 0) sb.AppendLine($"{_operator} with a brightness of 0 yield no result, please use a value larger than 0."); + break; + case RedrawModelOperators.Divide: + if (_brightness == 0) sb.AppendLine($"{_operator} with a brightness of 0 is not valid, please use a value larger than 0."); + else if (_brightness == 1) sb.AppendLine($"{_operator} with a brightness of 0 yield no result, please use a value larger than 0."); + break; + } + } + return sb.ToString(); } public override string ToString() { - var result = $"[{_redrawType}] [B: {_brightness}] [CS: {_contactPointsOnly}] [ICLP: {_ignoreContactLessPixels}]"; + var result = $"[{_redrawType}] [B: {_brightness}] [OP: {_operator}] [CS: {_contactPointsOnly}] [ICLP: {_ignoreContactLessPixels}]"; if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; return result; } @@ -104,7 +146,11 @@ public RedrawTypes RedrawType set => RaiseAndSetIfChanged(ref _redrawType, value); } - public static Array RedrawTypesItems => Enum.GetValues(typeof(RedrawTypes)); + public RedrawModelOperators Operator + { + get => _operator; + set => RaiseAndSetIfChanged(ref _operator, value); + } public byte Brightness { @@ -169,6 +215,11 @@ protected override bool ExecuteInternally(OperationProgress progress) if (startLayerIndex < 0) return false; Parallel.For(0, otherFile.LayerCount, CoreSettings.GetParallelOptions(progress), layerIndex => { + if (SlicerFile[layerIndex].IsEmpty) + { + progress.LockAndIncrement(); + return; + } progress.PauseIfRequested(); var fullMatLayerIndex = startLayerIndex + layerIndex; using var fullMat = SlicerFile[fullMatLayerIndex].LayerMat; @@ -176,7 +227,6 @@ protected override bool ExecuteInternally(OperationProgress progress) using var bodyMat = otherFile[layerIndex].LayerMat; using var fullMatRoi = GetRoiOrDefault(fullMat); using var bodyMatRoi = GetRoiOrDefault(bodyMat); - using var patternMat = EmguExtensions.InitMat(fullMatRoi.Size, new MCvScalar(255 - _brightness)); using var supportsMat = new Mat(); bool modified = false; @@ -229,9 +279,41 @@ protected override bool ExecuteInternally(OperationProgress progress) case RedrawTypes.Model: CvInvoke.BitwiseAnd(fullMatRoi, bodyMatRoi, supportsMat); // Model break; + default: + throw new ArgumentOutOfRangeException(nameof(RedrawType), _redrawType, null); } - CvInvoke.Subtract(fullMatRoi, patternMat, fullMatRoi, supportsMat); + using var patternMat = fullMatRoi.NewSetTo(new MCvScalar(_brightness), supportsMat); + + switch (_operator) + { + case RedrawModelOperators.Set: + patternMat.CopyTo(fullMatRoi, fullMatRoi); + break; + case RedrawModelOperators.Add: + CvInvoke.Add(fullMatRoi, patternMat, fullMatRoi, supportsMat); + break; + case RedrawModelOperators.Subtract: + CvInvoke.Subtract(fullMatRoi, patternMat, fullMatRoi, supportsMat); + break; + case RedrawModelOperators.Multiply: + CvInvoke.Multiply(fullMatRoi, patternMat, fullMatRoi, EmguExtensions.ByteScale); + break; + case RedrawModelOperators.Divide: + CvInvoke.Divide(fullMatRoi, patternMat, fullMatRoi); + break; + case RedrawModelOperators.Minimum: + CvInvoke.Min(fullMatRoi, patternMat, fullMatRoi); + break; + case RedrawModelOperators.Maximum: + CvInvoke.Max(fullMatRoi, patternMat, fullMatRoi); + break; + case RedrawModelOperators.AbsDiff: + CvInvoke.AbsDiff(fullMatRoi, patternMat, fullMatRoi); + break; + default: + throw new ArgumentOutOfRangeException(nameof(Operator), _operator, null); + } modified = true; } diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 8bf390b2..0b1de9a8 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -22,8 +22,8 @@ - - + + diff --git a/UVtools.UI/App.axaml b/UVtools.UI/App.axaml index 422d5518..e6007934 100644 --- a/UVtools.UI/App.axaml +++ b/UVtools.UI/App.axaml @@ -1,9 +1,12 @@  + diff --git a/UVtools.UI/Controls/Calibrators/CalibrateExposureFinderControl.axaml b/UVtools.UI/Controls/Calibrators/CalibrateExposureFinderControl.axaml index 53aba0c3..33094add 100644 --- a/UVtools.UI/Controls/Calibrators/CalibrateExposureFinderControl.axaml +++ b/UVtools.UI/Controls/Calibrators/CalibrateExposureFinderControl.axaml @@ -746,7 +746,7 @@ + IsExpanded="{Binding $self.IsEnabled, Mode=OneTime}"> diff --git a/UVtools.UI/Controls/Tools/ToolRedrawModelControl.axaml b/UVtools.UI/Controls/Tools/ToolRedrawModelControl.axaml index 1e86459c..53bc7647 100644 --- a/UVtools.UI/Controls/Tools/ToolRedrawModelControl.axaml +++ b/UVtools.UI/Controls/Tools/ToolRedrawModelControl.axaml @@ -4,25 +4,24 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="https://github.com/projektanker/icons.avalonia" xmlns:tools="clr-namespace:UVtools.UI.Controls.Tools" + xmlns:op="clr-namespace:UVtools.Core.Operations;assembly=UVtools.Core" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="UVtools.UI.Controls.Tools.ToolRedrawModelControl" x:DataType="tools:ToolRedrawModelControl"> - - -