From dc0e90a61311b2d78104e46ac34c8faf382dcbd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Sat, 8 May 2021 23:37:59 +0100 Subject: [PATCH] v2.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 --- CHANGELOG.md | 26 +- UVtools.Core/FileFormats/FileFormat.cs | 183 ++++++++- .../Managers/OperationSessionManager.cs | 126 ++++++ UVtools.Core/Operations/Operation.cs | 42 +- UVtools.Core/Operations/OperationBlur.cs | 2 +- UVtools.Core/Operations/OperationFlip.cs | 2 +- ...thmetic.cs => OperationLayerArithmetic.cs} | 69 ++-- UVtools.Core/Operations/OperationMorph.cs | 2 +- .../Operations/OperationPixelArithmetic.cs | 362 ++++++++++++++++++ .../Operations/OperationRaftRelief.cs | 10 +- .../Operations/OperationRedrawModel.cs | 2 +- UVtools.Core/Operations/OperationResize.cs | 2 +- UVtools.Core/Operations/OperationRotate.cs | 2 +- UVtools.Core/Operations/OperationSolidify.cs | 2 +- UVtools.Core/Operations/OperationThreshold.cs | 2 +- UVtools.Core/Scripting/ScriptBaseInput.cs | 6 +- .../Scripting/ScriptNumericalInput.cs | 8 +- UVtools.Core/Scripting/ScriptTextBoxInput.cs | 2 +- UVtools.Core/UVtools.Core.csproj | 2 +- UVtools.ScriptSample/ScriptVATClean.cs | 141 +++++++ .../Assets/Icons/file-export-16x16.png | Bin 0 -> 161 bytes UVtools.WPF/Controls/Helpers.cs | 32 +- .../Tools/ToolCalculatorControl.axaml | 30 +- ...axaml => ToolLayerArithmeticControl.axaml} | 2 +- ...cs => ToolLayerArithmeticControl.axaml.cs} | 8 +- .../Tools/ToolPixelArithmeticControl.axaml | 99 +++++ .../Tools/ToolPixelArithmeticControl.axaml.cs | 20 + .../Tools/ToolScriptingControl.axaml.cs | 15 + UVtools.WPF/MainWindow.Clipboard.cs | 4 +- UVtools.WPF/MainWindow.axaml | 69 +--- UVtools.WPF/MainWindow.axaml.cs | 18 +- UVtools.WPF/Structures/OperationProfiles.cs | 30 +- UVtools.WPF/UVtools.WPF.csproj | 5 +- UVtools.WPF/UserSettings.cs | 39 +- UVtools.WPF/Windows/SettingsWindow.axaml | 34 +- UVtools.WPF/Windows/ToolWindow.axaml | 41 +- UVtools.WPF/Windows/ToolWindow.axaml.cs | 164 +++++++- 37 files changed, 1418 insertions(+), 185 deletions(-) create mode 100644 UVtools.Core/Managers/OperationSessionManager.cs rename UVtools.Core/Operations/{OperationArithmetic.cs => OperationLayerArithmetic.cs} (79%) create mode 100644 UVtools.Core/Operations/OperationPixelArithmetic.cs create mode 100644 UVtools.ScriptSample/ScriptVATClean.cs create mode 100644 UVtools.WPF/Assets/Icons/file-export-16x16.png rename UVtools.WPF/Controls/Tools/{ToolArithmeticControl.axaml => ToolLayerArithmeticControl.axaml} (90%) rename UVtools.WPF/Controls/Tools/{ToolArithmeticControl.axaml.cs => ToolLayerArithmeticControl.axaml.cs} (79%) create mode 100644 UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml create mode 100644 UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 67f50f8..1ae2ad9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,30 @@ # Changelog -## /05/2021 - v2.10.0 +## 08/05/2021 - v2.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 + +## 07/05/2021 - v2.10.0 - **Exposure time finder:** - Add a enable option for each feature 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)); + } } /// @@ -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)); + } } /// @@ -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)); + } } /// @@ -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)); + } } /// @@ -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)); + } } /// @@ -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)); + } } /// @@ -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)); + } } /// @@ -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)); + } } /// @@ -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)); + } } /// @@ -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 /// @@ -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. + * 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 + { + #region Settings + + //public static string FilePath; + #endregion + + #region Singleton + + private static Lazy _instanceHolder = + new(() => new OperationSessionManager()); + + public static OperationSessionManager Instance => _instanceHolder.Value; + + #endregion + + #region Members + + private readonly List _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 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) _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,23 +342,39 @@ namespace UVtools.Core.Operations /// 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 Mat GetRoiOrDefault(Mat defaultMat) + { + return HaveROI && defaultMat.Size != _roi.Size ? new Mat(defaultMat, _roi) : defaultMat; + } + + public void ClearMasks() + { + MaskPoints = null; + } + public void SetMasksIfEmpty(Point[][] points) { if (HaveMask) return; MaskPoints = points; } - public Mat GetRoiOrDefault(Mat defaultMat) - { - return HaveROI && defaultMat.Size != _roi.Size ? new Mat(defaultMat, _roi) : defaultMat; - } - public Mat GetMask(Mat mat) => GetMask(_maskPoints, mat); public Mat GetMask(Point[][] points, Mat 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); } /// @@ -390,7 +414,7 @@ namespace UVtools.Core.Operations /// Result image which will also be modified 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 similarity index 79% rename from UVtools.Core/Operations/OperationArithmetic.cs rename to 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: = \n" + "When: \" =\" 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. + * 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 /// /// Gets the input label /// - public string Label { get; init; } + public string Label { get; set; } /// /// Gets the hover tooltip for this input /// - public string ToolTip { get; init; } + public string ToolTip { get; set; } /// /// Gets the value representative unit name /// - public string Unit { get; init; } + public string Unit { get; set; } } public abstract class ScriptBaseInput : 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 /// /// Gets the minimum for this input /// - public T Minimum { get; init; } + public T Minimum { get; set; } /// /// Gets the minimum for this input /// - public T Maximum { get; init; } + public T Maximum { get; set; } /// /// Gets the increment value for this /// - public T Increment { get; init; } + public T Increment { get; set; } /// /// Gets the number of decimal plates to round the value /// - 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 /// /// Gets if this input accepts multi lines /// - 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 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 2.10.0 + 2.11.0 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 diff --git a/UVtools.ScriptSample/ScriptVATClean.cs b/UVtools.ScriptSample/ScriptVATClean.cs new file mode 100644 index 0000000..aefee4c --- /dev/null +++ b/UVtools.ScriptSample/ScriptVATClean.cs @@ -0,0 +1,141 @@ +/* + * 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.Drawing; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; +using UVtools.Core.Extensions; +using UVtools.Core.Scripting; + +namespace UVtools.ScriptSample +{ + /// + /// Change layer properties to random values + /// + public class ScriptVATClean : ScriptGlobals + { + private ScriptNumericalInput InputInset = new() + { + Label = "Resolution inset", + ToolTip = "Inset image resolution by this value to create a black border", + Unit = "px", + Minimum = 0, + Maximum = ushort.MaxValue, + Increment = 1 + }; + + private ScriptNumericalInput InputExposureTime = new() + { + Label = "Exposure time", + ToolTip = "Time to exposure the layer", + Unit = "s", + Minimum = 0, + Maximum = 50, + DecimalPlates = 2, + Increment = 1 + }; + + /// + /// Set configurations here, this function trigger just after load a script + /// + public void ScriptInit() + { + Script.Name = "Create a file to clean VAT exposing 1 layer"; + Script.Description = "Print this file to clean your VAT by exposing 1 layer and peel it off.\n" + + "1) Load a file for your printer that you previous printed into UVtools\n" + + "2) Configure and run this script\n" + + "3) Go to File -> Save As, and give it a new name\n" + + "4) Remove head/plate\n" + + "5) Place a plastic spatula in the VAT at an angle with the handle laying on the top of the VAT frame\n" + + "6) Print the created file\n" + + "7) When print finish slowly peel the layer with the spatula"; + Script.Author = "Tiago Conceição"; + Script.Version = new Version(0, 1); + + + InputInset.Maximum = (ushort) (Math.Max(SlicerFile.ResolutionX, SlicerFile.ResolutionY) / 2 - 2); + InputExposureTime.Value = SlicerFile.ExposureTime * 2; + + Script.UserInputs.Add(InputInset); + Script.UserInputs.Add(InputExposureTime); + } + + /// + /// Validate user inputs here, this function trigger when user click on execute + /// + /// A error message, empty or null if validation passes. + public string ScriptValidate() + { + return null; + } + + /// + /// Execute the script, this function trigger when when user click on execute and validation passes + /// + /// True if executes successfully to the end, otherwise false. + public bool ScriptExecute() + { + Progress.Reset("Generating layers", 1); // Sets the progress name and number of items to process + + var layer = SlicerFile[0]; + layer.PositionZ = SlicerFile.MachineZ; // Send head to top if possible + + using var mat = EmguExtensions.InitMat(SlicerFile.Resolution); + CvInvoke.Rectangle(mat, new Rectangle( + new Point(InputInset.Value, InputInset.Value), + new Size((int) (SlicerFile.ResolutionX - InputInset.Value*2)-1, (int) (SlicerFile.ResolutionY - InputInset.Value*2)-1) + ), EmguExtensions.WhiteByte, -1, LineType.FourConnected); + layer.LayerMat = mat; + + SlicerFile.SuppressRebuildPropertiesWork(() => + { + SlicerFile.BottomLayerCount = 1; + + SlicerFile.LayerManager.Layers = new[] { layer }; + }); + + SlicerFile.BottomExposureTime = + SlicerFile.ExposureTime = InputExposureTime.Value; + + SlicerFile.BottomLiftSpeed = + SlicerFile.LiftSpeed = + SlicerFile.RetractSpeed = 200; + + SlicerFile.BottomLiftHeight = + SlicerFile.LiftHeight = 1; + + Progress++; + + SlicerFile.SetThumbnails(GetThumbnail()); + + // return true if not cancelled by user + return !Progress.Token.IsCancellationRequested; + } + + public Mat GetThumbnail() + { + Mat thumbnail = EmguExtensions.InitMat(new Size(400, 200), 3); + var fontFace = FontFace.HersheyDuplex; + var fontScale = 1; + var fontThickness = 2; + const byte xSpacing = 45; + const byte ySpacing = 45; + CvInvoke.PutText(thumbnail, "UVtools", new Point(140, 35), fontFace, fontScale, new MCvScalar(255, 27, 245), fontThickness + 1); + CvInvoke.Line(thumbnail, new Point(xSpacing, 0), new Point(xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); + CvInvoke.Line(thumbnail, new Point(xSpacing, ySpacing + 5), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); + CvInvoke.Line(thumbnail, new Point(thumbnail.Width - xSpacing, 0), new Point(thumbnail.Width - xSpacing, ySpacing + 5), new MCvScalar(255, 27, 245), 3); + CvInvoke.PutText(thumbnail, "VAT Clean Utility", new Point(xSpacing, ySpacing * 2), fontFace, fontScale, new MCvScalar(0, 255, 255), fontThickness); + CvInvoke.PutText(thumbnail, $"Exposure time: {SlicerFile.ExposureTime}s", new Point(xSpacing, ySpacing * 3), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + CvInvoke.PutText(thumbnail, $"Use the spatula in!", new Point(xSpacing, ySpacing * 4), fontFace, fontScale, EmguExtensions.White3Byte, fontThickness); + + return thumbnail; + } + } +} diff --git a/UVtools.WPF/Assets/Icons/file-export-16x16.png b/UVtools.WPF/Assets/Icons/file-export-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..885e637fd63f75d599f43177b714fc16e7813cee GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6GCW-zLn;`P6?pzBJ60?3sV-nx z($c`xBILu$=djYEO{Og?hU?p5g)Sx^j<5ws7j(uu+8&N%^W4ML@FkBek4;4VP=jqu zj_D+(1#;{fTi7i{bi{gZcX27qZ9UI>l;`S?TE#wlnGSY_%}o=xtSp^$252XPr>mdK II;Vst0Ly4HGXMYp literal 0 HcmV?d00001 diff --git a/UVtools.WPF/Controls/Helpers.cs b/UVtools.WPF/Controls/Helpers.cs index ca66103..61046f2 100644 --- a/UVtools.WPF/Controls/Helpers.cs +++ b/UVtools.WPF/Controls/Helpers.cs @@ -15,7 +15,7 @@ namespace UVtools.WPF.Controls { public static readonly List ImagesFileFilter = new() { - new FileDialogFilter + new() { Name = "Image Files", Extensions = new List @@ -29,9 +29,9 @@ namespace UVtools.WPF.Controls } }; - public static readonly List PngFileFilter = new List + public static readonly List PngFileFilter = new() { - new FileDialogFilter + new() { Name = "Image Files", Extensions = new List @@ -41,9 +41,9 @@ namespace UVtools.WPF.Controls } }; - public static readonly List TxtFileFilter = new List + public static readonly List TxtFileFilter = new() { - new FileDialogFilter + new() { Name = "Text Files", Extensions = new List @@ -53,9 +53,9 @@ namespace UVtools.WPF.Controls } }; - public static readonly List IniFileFilter = new List + public static readonly List IniFileFilter = new() { - new FileDialogFilter + new() { Name = "Ini Files", Extensions = new List @@ -65,9 +65,21 @@ namespace UVtools.WPF.Controls } }; + public static readonly List OperationSettingFileFilter = new() + { + new() + { + Name = "UVtools operation settings", + Extensions = new List + { + "uvtop", + } + } + }; + public static readonly List ScriptsFileFilter = new() { - new FileDialogFilter + new() { Name = "Script Files", Extensions = new List @@ -87,9 +99,9 @@ namespace UVtools.WPF.Controls public static List ToAvaloniaFilter(string name, string extension) { - return new List(1) + return new(1) { - new FileDialogFilter {Name = name, Extensions = new List(1) {extension}} + new() {Name = name, Extensions = new List(1) {extension}} }; } } diff --git a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml index c179b3b..37a6410 100644 --- a/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml @@ -232,7 +232,7 @@ - + @@ -488,6 +512,8 @@ Grid.Row="10" Grid.Column="6" Grid.ColumnSpan="3" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" HorizontalAlignment="Right" Padding="10" @@ -499,6 +525,8 @@ Grid.Row="12" Grid.Column="6" Grid.ColumnSpan="5" + IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}" + IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding SlicerFile.BottomLightOffDelay, StringFormat=Current value: \{0\}}"/> diff --git a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml similarity index 90% rename from UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml rename to UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml index 9239816..87dff5c 100644 --- a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml @@ -3,7 +3,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="500" d:DesignHeight="450" - x:Class="UVtools.WPF.Controls.Tools.ToolArithmeticControl" + x:Class="UVtools.WPF.Controls.Tools.ToolLayerArithmeticControl" Width="720" > diff --git a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs similarity index 79% rename from UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs rename to UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs index 18ad8ff..fc07e66 100644 --- a/UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs @@ -4,14 +4,14 @@ using UVtools.WPF.Windows; namespace UVtools.WPF.Controls.Tools { - public class ToolArithmeticControl : ToolControl + public class ToolLayerArithmeticControl : ToolControl { - public OperationArithmetic Operation => BaseOperation as OperationArithmetic; + public OperationLayerArithmetic Operation => BaseOperation as OperationLayerArithmetic; - public ToolArithmeticControl() + public ToolLayerArithmeticControl() { InitializeComponent(); - BaseOperation = new OperationArithmetic(SlicerFile); + BaseOperation = new OperationLayerArithmetic(SlicerFile); } private void InitializeComponent() diff --git a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml new file mode 100644 index 0000000..c4ca28e --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +