diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-05-09 01:37:59 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2021-05-09 01:37:59 +0300 |
commit | dc0e90a61311b2d78104e46ac34c8faf382dcbd3 (patch) | |
tree | 03afc3cceb7dbacbddea34c535700f042d42502e /UVtools.Core | |
parent | ea410468fcb8c8ba93c955b3a69e6654e48f0b2f (diff) |
v2.11.0v2.11.0
- **Tools:**
- (Add) Pixel Arithmetic
- (Add) Layer arithmetic: Operator $ to perform a absolute difference
- (Add) Allow to save and auto restore operation settings per session (#195)
- (Add) Allow to auto select the print volume ROI
- (Add) Allow to export and import operation settings from files
- (Improvement) Calculator - LightOff delay: Hide the bottom properties or the tab if the file format don't support them (#193)
- (Change) 'Arithmetic' to 'Layer arithmetic'
- (Remove) 'Threshold pixels'
- (Fix) Solidfy was unable to save profiles
- (Fix) A redo operation (Ctrl + Shift + Z) wasn't restoring the settings when a default profile is set
- **Operations:**
- (Fix) Passing a roi mat to `ApplyMask` would cause unwanted results
- (Improvement) Allow pass a full/original size mask to `ApplyMask`
- **Scripting:**
- (Add) an script to create an printable file to clean the VAT (#170)
- (Improvement) Allow to change user input properties outside the initialization
- (Improvement) Auto format numerical input box with the fixed decimal cases
- (Add) Settings: Section 'Tools'
- (Improvement) GUI: The 'Lift, Retract and Light-off' at status bar now only shows for the supported formats
- (Fix) Print time estimation calculation was wrong since v2.9.3 due a lacking of parentheses on the logic
Diffstat (limited to 'UVtools.Core')
18 files changed, 755 insertions, 71 deletions
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index ac9a755..a8be672 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -774,7 +775,11 @@ namespace UVtools.Core.FileFormats public virtual float BottomExposureTime { get => _bottomExposureTime; - set => RaiseAndSet(ref _bottomExposureTime, value); + set + { + RaiseAndSet(ref _bottomExposureTime, value); + RaisePropertyChanged(nameof(ExposureRepresentation)); + } } /// <summary> @@ -783,7 +788,11 @@ namespace UVtools.Core.FileFormats public virtual float ExposureTime { get => _exposureTime; - set => RaiseAndSet(ref _exposureTime, value); + set + { + RaiseAndSet(ref _exposureTime, value); + RaisePropertyChanged(nameof(ExposureRepresentation)); + } } /// <summary> @@ -792,7 +801,11 @@ namespace UVtools.Core.FileFormats public virtual float BottomLiftHeight { get => _bottomLiftHeight; - set => RaiseAndSet(ref _bottomLiftHeight, value); + set + { + RaiseAndSet(ref _bottomLiftHeight, value); + RaisePropertyChanged(nameof(LiftRepresentation)); + } } /// <summary> @@ -801,7 +814,11 @@ namespace UVtools.Core.FileFormats public virtual float LiftHeight { get => _liftHeight; - set => RaiseAndSet(ref _liftHeight, value); + set + { + RaiseAndSet(ref _liftHeight, value); + RaisePropertyChanged(nameof(LiftRepresentation)); + } } /// <summary> @@ -810,7 +827,11 @@ namespace UVtools.Core.FileFormats public virtual float BottomLiftSpeed { get => _bottomLiftSpeed; - set => RaiseAndSet(ref _bottomLiftSpeed, value); + set + { + RaiseAndSet(ref _bottomLiftSpeed, value); + RaisePropertyChanged(nameof(LiftRepresentation)); + } } /// <summary> @@ -819,7 +840,11 @@ namespace UVtools.Core.FileFormats public virtual float LiftSpeed { get => _liftSpeed; - set => RaiseAndSet(ref _liftSpeed, value); + set + { + RaiseAndSet(ref _liftSpeed, value); + RaisePropertyChanged(nameof(LiftRepresentation)); + } } /// <summary> @@ -828,7 +853,11 @@ namespace UVtools.Core.FileFormats public virtual float RetractSpeed { get => _retractSpeed; - set => RaiseAndSet(ref _retractSpeed, value); + set + { + RaiseAndSet(ref _retractSpeed, value); + RaisePropertyChanged(nameof(RetractRepresentation)); + } } /// <summary> @@ -837,7 +866,11 @@ namespace UVtools.Core.FileFormats public virtual float BottomLightOffDelay { get => _bottomLightOffDelay; - set => RaiseAndSet(ref _bottomLightOffDelay, value); + set + { + RaiseAndSet(ref _bottomLightOffDelay, value); + RaisePropertyChanged(nameof(LightOffDelayRepresentation)); + } } /// <summary> @@ -846,7 +879,11 @@ namespace UVtools.Core.FileFormats public virtual float LightOffDelay { get => _lightOffDelay; - set => RaiseAndSet(ref _lightOffDelay, value); + set + { + RaiseAndSet(ref _lightOffDelay, value); + RaisePropertyChanged(nameof(LightOffDelayRepresentation)); + } } /// <summary> @@ -867,6 +904,132 @@ namespace UVtools.Core.FileFormats set => RaiseAndSet(ref _lightPwm, value); } + public bool CanUseBottomExposureTime => HavePrintParameterModifier(PrintParameterModifier.BottomExposureSeconds); + public bool CanUseExposureTime => HavePrintParameterModifier(PrintParameterModifier.ExposureSeconds); + public bool CanUseAnyExposureTime => CanUseBottomExposureTime || CanUseExposureTime; + + public bool CanUseBottomLiftHeight => HavePrintParameterModifier(PrintParameterModifier.BottomLiftHeight); + public bool CanUseLiftHeight => HavePrintParameterModifier(PrintParameterModifier.LiftHeight); + public bool CanUseAnyLiftHeight => CanUseBottomLiftHeight || CanUseLiftHeight; + + public bool CanUseBottomLiftSpeed => HavePrintParameterModifier(PrintParameterModifier.BottomLiftSpeed); + public bool CanUseLiftSpeed => HavePrintParameterModifier(PrintParameterModifier.LiftHeight); + public bool CanUseAnyLiftSpeed => CanUseBottomLiftSpeed || CanUseLiftSpeed; + + public bool CanUseRetractSpeed => HavePrintParameterModifier(PrintParameterModifier.RetractSpeed); + + public bool CanUseBottomLightOffDelay => HavePrintParameterModifier(PrintParameterModifier.BottomLightOffDelay); + public bool CanUseLightOffDelay => HavePrintParameterModifier(PrintParameterModifier.LightOffDelay); + public bool CanUseAnyLightOffDelay => CanUseBottomLightOffDelay || CanUseLightOffDelay; + + public bool CanUseBottomLightPWM => HavePrintParameterModifier(PrintParameterModifier.BottomLightPWM); + public bool CanUseLightPWM => HavePrintParameterModifier(PrintParameterModifier.LightPWM); + public bool CanUseAnyLightPWM => CanUseBottomLightPWM || CanUseLightPWM; + + public string ExposureRepresentation + { + get + { + var str = string.Empty; + + if (CanUseBottomExposureTime) + { + str += ExposureTime.ToString(CultureInfo.InvariantCulture); + } + if (CanUseExposureTime) + { + if (!string.IsNullOrEmpty(str)) str += '/'; + str += BottomExposureTime.ToString(CultureInfo.InvariantCulture); + } + + if (!string.IsNullOrEmpty(str)) str += 's'; + + return str; + } + } + + public string LiftRepresentation + { + get + { + var str = string.Empty; + + var haveBottomLiftHeight = CanUseBottomLiftHeight; + var haveLiftHeight = CanUseLiftHeight; + + if (!haveBottomLiftHeight && !haveLiftHeight) return str; + + if (haveBottomLiftHeight) + { + str += BottomLiftHeight.ToString(CultureInfo.InvariantCulture); + } + if (haveLiftHeight) + { + if (!string.IsNullOrEmpty(str)) str += '/'; + str += LiftHeight.ToString(CultureInfo.InvariantCulture); + } + + if (string.IsNullOrEmpty(str)) return str; + + str += "mm @ "; + + var haveBottomLiftSpeed = CanUseBottomLiftSpeed; + var haveLiftSpeed = CanUseLiftSpeed; + if (haveBottomLiftSpeed) + { + str += BottomLiftSpeed.ToString(CultureInfo.InvariantCulture); + } + if (haveLiftSpeed) + { + if (haveBottomLiftSpeed) str += '/'; + str += LiftSpeed.ToString(CultureInfo.InvariantCulture); + } + + str += "mm/min"; + + return str; + } + } + + public string RetractRepresentation + { + get + { + var str = string.Empty; + + if (CanUseRetractSpeed) + { + str += RetractSpeed.ToString(CultureInfo.InvariantCulture); + } + + if (!string.IsNullOrEmpty(str)) str += "mm/min"; + + return str; + } + } + + public string LightOffDelayRepresentation + { + get + { + var str = string.Empty; + + if (CanUseBottomLightOffDelay) + { + str += BottomLightOffDelay.ToString(CultureInfo.InvariantCulture); + } + if (CanUseLightOffDelay) + { + if (!string.IsNullOrEmpty(str)) str += '/'; + str += LightOffDelay.ToString(CultureInfo.InvariantCulture); + } + + if (!string.IsNullOrEmpty(str)) str += 's'; + + return str; + } + } + #endregion /// <summary> @@ -915,7 +1078,7 @@ namespace UVtools.Core.FileFormats } var lightOffDelay = layer.CalculateLightOffDelay(); - time += layer.ExposureTime + lightOffDelay > layer.LightOffDelay ? lightOffDelay : layer.LightOffDelay; + time += layer.ExposureTime + (lightOffDelay > layer.LightOffDelay ? lightOffDelay : layer.LightOffDelay); /*if (lightOffDelay >= layer.LightOffDelay) time += lightOffDelay; else diff --git a/UVtools.Core/Managers/OperationSessionManager.cs b/UVtools.Core/Managers/OperationSessionManager.cs new file mode 100644 index 0000000..f62093d --- /dev/null +++ b/UVtools.Core/Managers/OperationSessionManager.cs @@ -0,0 +1,126 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UVtools.Core.Operations; + +namespace UVtools.Core.Managers +{ + public class OperationSessionManager : IList<Operation> + { + #region Settings + + //public static string FilePath; + #endregion + + #region Singleton + + private static Lazy<OperationSessionManager> _instanceHolder = + new(() => new OperationSessionManager()); + + public static OperationSessionManager Instance => _instanceHolder.Value; + + #endregion + + #region Members + + private readonly List<Operation> _operations = new(); + + #endregion + + #region Properties + + + #endregion + + #region Constructor + private OperationSessionManager() + { + } + #endregion + + #region Methods + + public Operation Find(Type type) + { + return this.FirstOrDefault(operation => operation.GetType() == type); + } + + public Operation Find(Operation fromOperation) => Find(fromOperation.GetType()); + + #endregion + + #region List Implementation + public IEnumerator<Operation> GetEnumerator() + { + return _operations.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable) _operations).GetEnumerator(); + } + + public void Add(Operation item) + { + if (item is null) return; + _operations.RemoveAll(operation => operation.GetType() == item.GetType()); + _operations.Add(item.Clone()); + } + + public void Clear() + { + _operations.Clear(); + } + + public bool Contains(Operation item) + { + return _operations.Contains(item); + } + + public void CopyTo(Operation[] array, int arrayIndex) + { + _operations.CopyTo(array, arrayIndex); + } + + public bool Remove(Operation item) + { + return _operations.Remove(item); + } + + public int Count => _operations.Count; + + public bool IsReadOnly => ((ICollection<Operation>) _operations).IsReadOnly; + + public int IndexOf(Operation item) + { + return _operations.IndexOf(item); + } + + public void Insert(int index, Operation item) + { + if (item is null) return; + _operations.RemoveAll(operation => operation.GetType() == item.GetType()); + _operations.Insert(index, item.Clone()); + } + + public void RemoveAt(int index) + { + _operations.RemoveAt(index); + } + + public Operation this[int index] + { + get => _operations[index]; + set => _operations[index] = value; + } + #endregion + } +}
\ No newline at end of file diff --git a/UVtools.Core/Operations/Operation.cs b/UVtools.Core/Operations/Operation.cs index 54abe03..56e2d12 100644 --- a/UVtools.Core/Operations/Operation.cs +++ b/UVtools.Core/Operations/Operation.cs @@ -13,6 +13,7 @@ using Emgu.CV; using Emgu.CV.Util; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; +using UVtools.Core.Managers; using UVtools.Core.Objects; namespace UVtools.Core.Operations @@ -341,21 +342,37 @@ namespace UVtools.Core.Operations /// </summary> public virtual void InitWithSlicerFile() { } + public void ClearROI() + { + ROI = Rectangle.Empty; + } + + public void ClearROIandMasks() + { + ClearROI(); + ClearMasks(); + } + public void SetROIIfEmpty(Rectangle roi) { if (HaveROI) return; ROI = roi; } - public void SetMasksIfEmpty(Point[][] points) + public Mat GetRoiOrDefault(Mat defaultMat) { - if (HaveMask) return; - MaskPoints = points; + return HaveROI && defaultMat.Size != _roi.Size ? new Mat(defaultMat, _roi) : defaultMat; } - public Mat GetRoiOrDefault(Mat defaultMat) + public void ClearMasks() { - return HaveROI && defaultMat.Size != _roi.Size ? new Mat(defaultMat, _roi) : defaultMat; + MaskPoints = null; + } + + public void SetMasksIfEmpty(Point[][] points) + { + if (HaveMask) return; + MaskPoints = points; } public Mat GetMask(Mat mat) => GetMask(_maskPoints, mat); @@ -379,8 +396,15 @@ namespace UVtools.Core.Operations { resultRoi = GetRoiOrDefault(result); } - resultRoi.CopyTo(originalRoi, mask); - originalRoi.CopyTo(resultRoi); + + if (mask.Size != resultRoi.Size) // Accept a full size mask + { + mask = GetRoiOrDefault(mask); + } + + using var tempMat = originalRoi.Clone(); + resultRoi.CopyTo(tempMat, mask); + tempMat.CopyTo(resultRoi); } /// <summary> @@ -390,7 +414,7 @@ namespace UVtools.Core.Operations /// <param name="result">Result image which will also be modified</param> public void ApplyMask(Mat original, Mat result) { - using var mask = GetMask(result); + using var mask = GetMask(original); ApplyMask(original, result, mask); } @@ -409,6 +433,8 @@ namespace UVtools.Core.Operations if(!string.IsNullOrWhiteSpace(msg)) throw new InvalidOperationException($"{Title} can't execute due some errors:\n{msg}"); } + + progress ??= new OperationProgress(); progress.Reset(ProgressAction, LayerRangeCount); HaveExecuted = true; diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs index c803177..b472d7d 100644 --- a/UVtools.Core/Operations/OperationBlur.cs +++ b/UVtools.Core/Operations/OperationBlur.cs @@ -175,7 +175,7 @@ namespace UVtools.Core.Operations throw new ArgumentOutOfRangeException(); } - ApplyMask(original, mat); + ApplyMask(original, target); return true; } diff --git a/UVtools.Core/Operations/OperationFlip.cs b/UVtools.Core/Operations/OperationFlip.cs index 2badeca..b9befcc 100644 --- a/UVtools.Core/Operations/OperationFlip.cs +++ b/UVtools.Core/Operations/OperationFlip.cs @@ -152,7 +152,7 @@ namespace UVtools.Core.Operations CvInvoke.Flip(target, target, FlipTypeOpenCV); } - ApplyMask(original, mat); + ApplyMask(original, target); return true; } diff --git a/UVtools.Core/Operations/OperationArithmetic.cs b/UVtools.Core/Operations/OperationLayerArithmetic.cs index fd29f51..2ea9bc8 100644 --- a/UVtools.Core/Operations/OperationArithmetic.cs +++ b/UVtools.Core/Operations/OperationLayerArithmetic.cs @@ -13,19 +13,18 @@ using System.Threading.Tasks; using System.Xml.Serialization; using Emgu.CV; using UVtools.Core.FileFormats; -using UVtools.Core.Objects; namespace UVtools.Core.Operations { [Serializable] - public class OperationArithmetic : Operation + public class OperationLayerArithmetic : Operation { #region Members private string _sentence; #endregion #region Enums - public enum ArithmeticOperators : byte + public enum LayerArithmeticOperators : byte { None, Add, @@ -34,7 +33,8 @@ namespace UVtools.Core.Operations Divide, BitwiseAnd, BitwiseOr, - BitwiseXor + BitwiseXor, + AbsDiff } #endregion @@ -42,23 +42,24 @@ namespace UVtools.Core.Operations public sealed class ArithmeticOperation { public uint LayerIndex { get; } - public ArithmeticOperators Operator { get; } + public LayerArithmeticOperators Operator { get; } - public ArithmeticOperation(uint layerIndex, ArithmeticOperators arithmeticOperator) + public ArithmeticOperation(uint layerIndex, LayerArithmeticOperators layerArithmeticOperator) { LayerIndex = layerIndex; - Operator = arithmeticOperator; + Operator = layerArithmeticOperator; } } #endregion #region Overrides - public override string Title => "Arithmetic"; + public override string Title => "Layer arithmetic"; public override string Description => - "Perform arithmetic operations over the layers pixels.\n\n" + + "Perform arithmetic operations over the layers\n" + "Available operators:\n" + " + - * / = Add, Subtract, Multiply, Divide\n" + - " & | ^ = Bitwise AND, OR, XOR\n\n" + + " & | ^ = Bitwise AND, OR, XOR\n" + + " $ = Absolute difference\n\n" + "Syntax: <set_to_layer_indexes> = <layer_index> <operator> <layer_index>\n" + "When: \"<set_to_layer_indexes> =\" is omitted, the result will assign to the first layer on the sentence.\n\n" + "Example 1: 10+11\n" + @@ -115,9 +116,9 @@ namespace UVtools.Core.Operations #region Constructor - public OperationArithmetic() { } + public OperationLayerArithmetic() { } - public OperationArithmetic(FileFormat slicerFile) : base(slicerFile) { } + public OperationLayerArithmetic(FileFormat slicerFile) : base(slicerFile) { } #endregion @@ -155,33 +156,36 @@ namespace UVtools.Core.Operations continue; } - ArithmeticOperators op = ArithmeticOperators.None; + LayerArithmeticOperators op = LayerArithmeticOperators.None; switch (c) { case '+': - op = ArithmeticOperators.Add; + op = LayerArithmeticOperators.Add; break; case '-': - op = ArithmeticOperators.Subtract; + op = LayerArithmeticOperators.Subtract; break; case '*': - op = ArithmeticOperators.Multiply; + op = LayerArithmeticOperators.Multiply; break; case '/': - op = ArithmeticOperators.Divide; + op = LayerArithmeticOperators.Divide; break; case '&': - op = ArithmeticOperators.BitwiseAnd; + op = LayerArithmeticOperators.BitwiseAnd; break; case '|': - op = ArithmeticOperators.BitwiseOr; + op = LayerArithmeticOperators.BitwiseOr; break; case '^': - op = ArithmeticOperators.BitwiseXor; + op = LayerArithmeticOperators.BitwiseXor; + break; + case '$': + op = LayerArithmeticOperators.AbsDiff; break; } - if (op == ArithmeticOperators.None // No valid operator + if (op == LayerArithmeticOperators.None // No valid operator || string.IsNullOrWhiteSpace(layerIndexStr) // Started with a operator instead of layer ) continue; @@ -200,7 +204,7 @@ namespace UVtools.Core.Operations { if (uint.TryParse(layerIndexStr, out var layerIndex)) { - Operations.Add(new ArithmeticOperation(layerIndex, ArithmeticOperators.None)); + Operations.Add(new ArithmeticOperation(layerIndex, LayerArithmeticOperators.None)); } } @@ -227,27 +231,30 @@ namespace UVtools.Core.Operations using var imageMask = GetMask(image); switch (Operations[i - 1].Operator) { - case ArithmeticOperators.Add: + case LayerArithmeticOperators.Add: CvInvoke.Add(resultRoi, imageRoi, resultRoi, imageMask); break; - case ArithmeticOperators.Subtract: + case LayerArithmeticOperators.Subtract: CvInvoke.Subtract(resultRoi, imageRoi, resultRoi, imageMask); break; - case ArithmeticOperators.Multiply: + case LayerArithmeticOperators.Multiply: CvInvoke.Multiply(resultRoi, imageRoi, resultRoi); break; - case ArithmeticOperators.Divide: + case LayerArithmeticOperators.Divide: CvInvoke.Divide(resultRoi, imageRoi, resultRoi); break; - case ArithmeticOperators.BitwiseAnd: + case LayerArithmeticOperators.BitwiseAnd: CvInvoke.BitwiseAnd(resultRoi, imageRoi, resultRoi, imageMask); break; - case ArithmeticOperators.BitwiseOr: + case LayerArithmeticOperators.BitwiseOr: CvInvoke.BitwiseOr(resultRoi, imageRoi, resultRoi, imageMask); break; - case ArithmeticOperators.BitwiseXor: + case LayerArithmeticOperators.BitwiseXor: CvInvoke.BitwiseXor(resultRoi, imageRoi, resultRoi, imageMask); break; + case LayerArithmeticOperators.AbsDiff: + CvInvoke.AbsDiff(resultRoi, imageRoi, resultRoi); + break; } } @@ -272,7 +279,7 @@ namespace UVtools.Core.Operations #endregion #region Equality - protected bool Equals(OperationArithmetic other) + protected bool Equals(OperationLayerArithmetic other) { return _sentence == other._sentence; } @@ -282,7 +289,7 @@ namespace UVtools.Core.Operations if (obj is null) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != GetType()) return false; - return Equals((OperationArithmetic) obj); + return Equals((OperationLayerArithmetic) obj); } public override int GetHashCode() diff --git a/UVtools.Core/Operations/OperationMorph.cs b/UVtools.Core/Operations/OperationMorph.cs index f2c6804..8c5f561 100644 --- a/UVtools.Core/Operations/OperationMorph.cs +++ b/UVtools.Core/Operations/OperationMorph.cs @@ -183,7 +183,7 @@ namespace UVtools.Core.Operations using var original = mat.Clone(); var target = GetRoiOrDefault(mat); CvInvoke.MorphologyEx(target, target, (MorphOp) MorphOperation, Kernel.Matrix, Kernel.Anchor, iterations, BorderType.Reflect101, default); - ApplyMask(original, mat); + ApplyMask(original, target); return true; } diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs new file mode 100644 index 0000000..0ce74e8 --- /dev/null +++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs @@ -0,0 +1,362 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + +using System; +using System.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; + +namespace UVtools.Core.Operations +{ + [Serializable] + public class OperationPixelArithmetic : Operation + { + #region Members + private PixelArithmeticOperators _operator = PixelArithmeticOperators.Set; + private byte _value = byte.MaxValue; + private ThresholdType _thresholdType = ThresholdType.Binary; + private byte _thresholdMaxValue = 255; + private bool _affectBackPixels; + + #endregion + + #region Enums + public enum PixelArithmeticOperators : 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("Exponential: pixels by a brightness")] + //Exponential, + [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("Bitwise Not: invert pixels")] + BitwiseNot, + [Description("Bitwise And: with a brightness")] + BitwiseAnd, + [Description("Bitwise Or: with a brightness")] + BitwiseOr, + [Description("Bitwise Xor: with a brightness")] + BitwiseXor, + [Description("AbsDiff: perform a absolute difference between pixel and brightness")] + AbsDiff, + [Description("Threshold: between a minimum/maximum brightness")] + Threshold, + [Description("Keep Region: in the selected ROI or masks")] + KeepRegion, + [Description("Discard Region: in the selected ROI or masks")] + DiscardRegion + } + #endregion + + #region Overrides + public override string Title => "Pixel arithmetic"; + + public override string Description => + "Perform arithmetic operations over the pixels"; + + public override string ConfirmationText => + $"arithmetic {_operator}" + + (ValueEnabled ? $"={_value}" : string.Empty) + + (_operator is PixelArithmeticOperators.Threshold ? $"/{_thresholdMaxValue}" : string.Empty) + + $" layers from {LayerIndexStart} through {LayerIndexEnd}"; + + public override string ProgressTitle => + $"Arithmetic {_operator}"+ + (ValueEnabled ? $"={_value}" : string.Empty) + +$" layers from {LayerIndexStart} through {LayerIndexEnd}"; + + public override string ProgressAction => "Calculated layers"; + + public override string ValidateInternally() + { + var sb = new StringBuilder(); + if (_operator == PixelArithmeticOperators.KeepRegion && !HaveROI && !HaveMask) + { + sb.AppendLine("The 'Keep' operator requires selected ROI/masks."); + } + else if (_operator == PixelArithmeticOperators.DiscardRegion && !HaveROI && !HaveMask) + { + sb.AppendLine("The 'Discard' operator requires selected ROI/masks."); + } + else if (_operator + is PixelArithmeticOperators.Add + or PixelArithmeticOperators.Subtract + or PixelArithmeticOperators.Maximum + or PixelArithmeticOperators.BitwiseOr + or PixelArithmeticOperators.BitwiseXor + or PixelArithmeticOperators.AbsDiff + && _value == 0) + /*|| + (_operator is PixelArithmeticOperators.Exponential && _value == 1) + )*/ + { + sb.AppendLine($"{_operator} by {_value} will have no effect."); + } + else if (_operator == PixelArithmeticOperators.Divide && _value == 0) + { + sb.AppendLine("Can't divide by 0."); + } + + return sb.ToString(); + } + + public override string ToString() + { + var result = $"[{_operator}: {_value}] [ABP: {_affectBackPixels}]" + + LayerRangeString; + if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; + return result; + } + #endregion + + #region Properties + + public PixelArithmeticOperators Operator + { + get => _operator; + set + { + if(!RaiseAndSetIfChanged(ref _operator, value)) return; + RaisePropertyChanged(nameof(ValueEnabled)); + RaisePropertyChanged(nameof(ThresholdEnabled)); + RaisePropertyChanged(nameof(AffectBackPixelsEnabled)); + } + } + + public byte Value + { + get => _value; + set + { + if(!RaiseAndSetIfChanged(ref _value, value)) return; + RaisePropertyChanged(nameof(ValuePercent)); + } + } + + // 255 - 100 + //value - x + public float ValuePercent => (float) Math.Round(_value * 100f / byte.MaxValue, 2); + + public bool ValueEnabled => _operator + is not PixelArithmeticOperators.BitwiseNot + and not PixelArithmeticOperators.KeepRegion + and not PixelArithmeticOperators.DiscardRegion + ; + + public ThresholdType ThresholdType + { + get => _thresholdType; + set => RaiseAndSetIfChanged(ref _thresholdType, value); + } + + public byte ThresholdMaxValue + { + get => _thresholdMaxValue; + set => RaiseAndSetIfChanged(ref _thresholdMaxValue, value); + } + + public bool ThresholdEnabled => _operator is PixelArithmeticOperators.Threshold; + + public bool AffectBackPixels + { + get => _affectBackPixels; + set => RaiseAndSetIfChanged(ref _affectBackPixels, value); + } + + public bool AffectBackPixelsEnabled => _operator + is not PixelArithmeticOperators.Subtract + and not PixelArithmeticOperators.Multiply + and not PixelArithmeticOperators.Divide + and not PixelArithmeticOperators.BitwiseNot + and not PixelArithmeticOperators.BitwiseAnd + and not PixelArithmeticOperators.KeepRegion + and not PixelArithmeticOperators.DiscardRegion + and not PixelArithmeticOperators.Threshold + ; + + #endregion + + #region Constructor + + public OperationPixelArithmetic() { } + + public OperationPixelArithmetic(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + + #region Methods + + protected override bool ExecuteInternally(OperationProgress progress) + { + var tempMat = GetTempMat(); + + Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using (var mat = SlicerFile[layerIndex].LayerMat) + { + Execute(mat, tempMat); + SlicerFile[layerIndex].LayerMat = mat; + } + + progress.LockAndIncrement(); + }); + + tempMat?.Dispose(); + + return !progress.Token.IsCancellationRequested; + } + + public override bool Execute(Mat mat, params object[] arguments) + { + using var original = mat.Clone(); + var target = GetRoiOrDefault(mat); + + Mat tempMat; + bool needDispose = false; + if (arguments is not null && arguments.Length > 0) + { + tempMat = arguments[0] as Mat; + } + else + { + tempMat = GetTempMat(); + needDispose = true; + } + + switch (_operator) + { + case PixelArithmeticOperators.Set: + tempMat.CopyTo(target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.Add: + CvInvoke.Add(target, tempMat, target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.Subtract: + CvInvoke.Subtract(target, tempMat, target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.Multiply: + CvInvoke.Multiply(target, tempMat, target); + break; + case PixelArithmeticOperators.Divide: + CvInvoke.Divide(target, tempMat, target); + break; + /*case PixelArithmeticOperators.Exponential: + CvInvoke.Pow(target, _value, tempMat); + if(!_affectBackPixels) ApplyMask(original, mat, original); + break;*/ + case PixelArithmeticOperators.Minimum: + CvInvoke.Min(target, tempMat, target); + if (!_affectBackPixels) ApplyMask(original, target, original); + break; + case PixelArithmeticOperators.Maximum: + CvInvoke.Max(target, tempMat, target); + if (!_affectBackPixels) ApplyMask(original, target, original); + break; + case PixelArithmeticOperators.BitwiseNot: + CvInvoke.BitwiseNot(target, target); + break; + case PixelArithmeticOperators.BitwiseAnd: + CvInvoke.BitwiseAnd(target, tempMat, target); + break; + case PixelArithmeticOperators.BitwiseOr: + CvInvoke.BitwiseOr(target, tempMat, target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.BitwiseXor: + CvInvoke.BitwiseXor(target, tempMat, target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.Threshold: + CvInvoke.Threshold(target, target, _value, _thresholdMaxValue, _thresholdType); + break; + case PixelArithmeticOperators.AbsDiff: + CvInvoke.AbsDiff(target, tempMat, target); + if (!_affectBackPixels) ApplyMask(original, target, original); + break; + case PixelArithmeticOperators.KeepRegion: + { + using var targetClone = target.Clone(); + original.SetTo(EmguExtensions.BlackByte); + mat.SetTo(EmguExtensions.BlackByte); + targetClone.CopyTo(target); + break; + } + case PixelArithmeticOperators.DiscardRegion: + target.SetTo(EmguExtensions.BlackByte); + break; + default: + throw new NotImplementedException(); + } + + ApplyMask(original, target); + + if (needDispose) + { + tempMat?.Dispose(); + } + + return true; + } + + public Mat GetTempMat() => _operator + is not PixelArithmeticOperators.BitwiseNot + and not PixelArithmeticOperators.KeepRegion + and not PixelArithmeticOperators.DiscardRegion ? EmguExtensions.InitMat(HaveROI ? ROI.Size : SlicerFile.Resolution, new MCvScalar(_value)) : null; + + public void PresetStripAntiAliasing() + { + Operator = PixelArithmeticOperators.Threshold; + Value = 127; + ThresholdMaxValue = 255; + ThresholdType = ThresholdType.Binary; + } + + public void PresetHalfBrightness() + { + Value = 128; + } + + #endregion + + #region Equality + + protected bool Equals(OperationPixelArithmetic other) + { + return _operator == other._operator && _value == other._value && _thresholdType == other._thresholdType && _thresholdMaxValue == other._thresholdMaxValue && _affectBackPixels == other._affectBackPixels; + } + + 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((OperationPixelArithmetic) obj); + } + + public override int GetHashCode() + { + return HashCode.Combine((int) _operator, _value, (int) _thresholdType, _thresholdMaxValue, _affectBackPixels); + } + + #endregion + } +} diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs index ffbfe6e..93b8104 100644 --- a/UVtools.Core/Operations/OperationRaftRelief.cs +++ b/UVtools.Core/Operations/OperationRaftRelief.cs @@ -242,9 +242,9 @@ namespace UVtools.Core.Operations Parallel.For(_ignoreFirstLayers, firstSupportLayerIndex, layerIndex => { if (progress.Token.IsCancellationRequested) return; - using var result = SlicerFile[layerIndex].LayerMat; - using var original = result.Clone(); - var target = GetRoiOrDefault(result); + using var mat = SlicerFile[layerIndex].LayerMat; + using var original = mat.Clone(); + var target = GetRoiOrDefault(mat); switch (ReliefType) { @@ -268,8 +268,8 @@ namespace UVtools.Core.Operations break; } - ApplyMask(original, result); - SlicerFile[layerIndex].LayerMat = result; + ApplyMask(original, target); + SlicerFile[layerIndex].LayerMat = mat; progress.LockAndIncrement(); }); diff --git a/UVtools.Core/Operations/OperationRedrawModel.cs b/UVtools.Core/Operations/OperationRedrawModel.cs index 06f2c77..34b30e4 100644 --- a/UVtools.Core/Operations/OperationRedrawModel.cs +++ b/UVtools.Core/Operations/OperationRedrawModel.cs @@ -240,7 +240,7 @@ namespace UVtools.Core.Operations if (modified) { - ApplyMask(original, fullMat); + ApplyMask(original, fullMatRoi); SlicerFile[fullMatLayerIndex].LayerMat = fullMat; } diff --git a/UVtools.Core/Operations/OperationResize.cs b/UVtools.Core/Operations/OperationResize.cs index 0565437..01e1c79 100644 --- a/UVtools.Core/Operations/OperationResize.cs +++ b/UVtools.Core/Operations/OperationResize.cs @@ -215,7 +215,7 @@ namespace UVtools.Core.Operations using var original = mat.Clone(); var target = GetRoiOrDefault(mat); target.TransformFromCenter((double) xScale, (double) yScale); - ApplyMask(original, mat); + ApplyMask(original, target); return true; } diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs index 9498246..fe9e215 100644 --- a/UVtools.Core/Operations/OperationRotate.cs +++ b/UVtools.Core/Operations/OperationRotate.cs @@ -101,7 +101,7 @@ namespace UVtools.Core.Operations using var original = mat.Clone(); var target = GetRoiOrDefault(mat); target.Rotate((double)AngleDegrees); - ApplyMask(original, mat); + ApplyMask(original, target); return true; } diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs index 9a97e1b..ea5e389 100644 --- a/UVtools.Core/Operations/OperationSolidify.cs +++ b/UVtools.Core/Operations/OperationSolidify.cs @@ -130,7 +130,7 @@ namespace UVtools.Core.Operations CvInvoke.DrawContours(target, contours, i, EmguExtensions.WhiteByte, -1); } - ApplyMask(original, mat); + ApplyMask(original, target); return true; } diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs index d516b8e..4754f00 100644 --- a/UVtools.Core/Operations/OperationThreshold.cs +++ b/UVtools.Core/Operations/OperationThreshold.cs @@ -101,7 +101,7 @@ namespace UVtools.Core.Operations using var original = mat.Clone(); var target = GetRoiOrDefault(mat); CvInvoke.Threshold(target, target, Threshold, Maximum, Type); - ApplyMask(original, mat); + ApplyMask(original, target); return true; } diff --git a/UVtools.Core/Scripting/ScriptBaseInput.cs b/UVtools.Core/Scripting/ScriptBaseInput.cs index b7f78ff..a7d764c 100644 --- a/UVtools.Core/Scripting/ScriptBaseInput.cs +++ b/UVtools.Core/Scripting/ScriptBaseInput.cs @@ -13,17 +13,17 @@ namespace UVtools.Core.Scripting /// <summary> /// Gets the input label /// </summary> - public string Label { get; init; } + public string Label { get; set; } /// <summary> /// Gets the hover tooltip for this input /// </summary> - public string ToolTip { get; init; } + public string ToolTip { get; set; } /// <summary> /// Gets the value representative unit name /// </summary> - public string Unit { get; init; } + public string Unit { get; set; } } public abstract class ScriptBaseInput<T> : ScriptBaseInput diff --git a/UVtools.Core/Scripting/ScriptNumericalInput.cs b/UVtools.Core/Scripting/ScriptNumericalInput.cs index f894bfa..8ce1a04 100644 --- a/UVtools.Core/Scripting/ScriptNumericalInput.cs +++ b/UVtools.Core/Scripting/ScriptNumericalInput.cs @@ -15,21 +15,21 @@ namespace UVtools.Core.Scripting /// <summary> /// Gets the minimum for this input /// </summary> - public T Minimum { get; init; } + public T Minimum { get; set; } /// <summary> /// Gets the minimum for this input /// </summary> - public T Maximum { get; init; } + public T Maximum { get; set; } /// <summary> /// Gets the increment value for this /// </summary> - public T Increment { get; init; } + public T Increment { get; set; } /// <summary> /// Gets the number of decimal plates to round the value /// </summary> - public byte DecimalPlates { get; init; } = 2; + public byte DecimalPlates { get; set; } = 2; } } diff --git a/UVtools.Core/Scripting/ScriptTextBoxInput.cs b/UVtools.Core/Scripting/ScriptTextBoxInput.cs index 9a31173..ae84e8d 100644 --- a/UVtools.Core/Scripting/ScriptTextBoxInput.cs +++ b/UVtools.Core/Scripting/ScriptTextBoxInput.cs @@ -13,6 +13,6 @@ namespace UVtools.Core.Scripting /// <summary> /// Gets if this input accepts multi lines /// </summary> - public bool MultiLine { get; init; } + public bool MultiLine { get; set; } } } diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 72fcd10..f9e1554 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description> - <Version>2.10.0</Version> + <Version>2.11.0</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> |