Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2021-05-09 01:37:59 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2021-05-09 01:37:59 +0300
commitdc0e90a61311b2d78104e46ac34c8faf382dcbd3 (patch)
tree03afc3cceb7dbacbddea34c535700f042d42502e
parentea410468fcb8c8ba93c955b3a69e6654e48f0b2f (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
-rw-r--r--CHANGELOG.md26
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs183
-rw-r--r--UVtools.Core/Managers/OperationSessionManager.cs126
-rw-r--r--UVtools.Core/Operations/Operation.cs42
-rw-r--r--UVtools.Core/Operations/OperationBlur.cs2
-rw-r--r--UVtools.Core/Operations/OperationFlip.cs2
-rw-r--r--UVtools.Core/Operations/OperationLayerArithmetic.cs (renamed from UVtools.Core/Operations/OperationArithmetic.cs)69
-rw-r--r--UVtools.Core/Operations/OperationMorph.cs2
-rw-r--r--UVtools.Core/Operations/OperationPixelArithmetic.cs362
-rw-r--r--UVtools.Core/Operations/OperationRaftRelief.cs10
-rw-r--r--UVtools.Core/Operations/OperationRedrawModel.cs2
-rw-r--r--UVtools.Core/Operations/OperationResize.cs2
-rw-r--r--UVtools.Core/Operations/OperationRotate.cs2
-rw-r--r--UVtools.Core/Operations/OperationSolidify.cs2
-rw-r--r--UVtools.Core/Operations/OperationThreshold.cs2
-rw-r--r--UVtools.Core/Scripting/ScriptBaseInput.cs6
-rw-r--r--UVtools.Core/Scripting/ScriptNumericalInput.cs8
-rw-r--r--UVtools.Core/Scripting/ScriptTextBoxInput.cs2
-rw-r--r--UVtools.Core/UVtools.Core.csproj2
-rw-r--r--UVtools.ScriptSample/ScriptVATClean.cs141
-rw-r--r--UVtools.WPF/Assets/Icons/file-export-16x16.pngbin0 -> 161 bytes
-rw-r--r--UVtools.WPF/Controls/Helpers.cs32
-rw-r--r--UVtools.WPF/Controls/Tools/ToolCalculatorControl.axaml30
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml (renamed from UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml)2
-rw-r--r--UVtools.WPF/Controls/Tools/ToolLayerArithmeticControl.axaml.cs (renamed from UVtools.WPF/Controls/Tools/ToolArithmeticControl.axaml.cs)8
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml99
-rw-r--r--UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs20
-rw-r--r--UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs15
-rw-r--r--UVtools.WPF/MainWindow.Clipboard.cs4
-rw-r--r--UVtools.WPF/MainWindow.axaml77
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs18
-rw-r--r--UVtools.WPF/Structures/OperationProfiles.cs30
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj5
-rw-r--r--UVtools.WPF/UserSettings.cs39
-rw-r--r--UVtools.WPF/Windows/SettingsWindow.axaml34
-rw-r--r--UVtools.WPF/Windows/ToolWindow.axaml41
-rw-r--r--UVtools.WPF/Windows/ToolWindow.axaml.cs164
37 files changed, 1422 insertions, 189 deletions
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));
+ }
}
/// <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>
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. <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.Drawing;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
+using UVtools.Core.Extensions;
+using UVtools.Core.Scripting;
+
+namespace UVtools.ScriptSample
+{
+ /// <summary>
+ /// Change layer properties to random values
+ /// </summary>
+ public class ScriptVATClean : ScriptGlobals
+ {
+ private ScriptNumericalInput<ushort> 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<float> InputExposureTime = new()
+ {
+ Label = "Exposure time",
+ ToolTip = "Time to exposure the layer",
+ Unit = "s",
+ Minimum = 0,
+ Maximum = 50,
+ DecimalPlates = 2,
+ Increment = 1
+ };
+
+ /// <summary>
+ /// Set configurations here, this function trigger just after load a script
+ /// </summary>
+ 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);
+ }
+
+ /// <summary>
+ /// Validate user inputs here, this function trigger when user click on execute
+ /// </summary>
+ /// <returns>A error message, empty or null if validation passes.</returns>
+ public string ScriptValidate()
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Execute the script, this function trigger when when user click on execute and validation passes
+ /// </summary>
+ /// <returns>True if executes successfully to the end, otherwise false.</returns>
+ 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 0000000..885e637
--- /dev/null
+++ b/UVtools.WPF/Assets/Icons/file-export-16x16.png
Binary files differ
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<FileDialogFilter> ImagesFileFilter = new()
{
- new FileDialogFilter
+ new()
{
Name = "Image Files",
Extensions = new List<string>
@@ -29,9 +29,9 @@ namespace UVtools.WPF.Controls
}
};
- public static readonly List<FileDialogFilter> PngFileFilter = new List<FileDialogFilter>
+ public static readonly List<FileDialogFilter> PngFileFilter = new()
{
- new FileDialogFilter
+ new()
{
Name = "Image Files",
Extensions = new List<string>
@@ -41,9 +41,9 @@ namespace UVtools.WPF.Controls
}
};
- public static readonly List<FileDialogFilter> TxtFileFilter = new List<FileDialogFilter>
+ public static readonly List<FileDialogFilter> TxtFileFilter = new()
{
- new FileDialogFilter
+ new()
{
Name = "Text Files",
Extensions = new List<string>
@@ -53,9 +53,9 @@ namespace UVtools.WPF.Controls
}
};
- public static readonly List<FileDialogFilter> IniFileFilter = new List<FileDialogFilter>
+ public static readonly List<FileDialogFilter> IniFileFilter = new()
{
- new FileDialogFilter
+ new()
{
Name = "Ini Files",
Extensions = new List<string>
@@ -65,9 +65,21 @@ namespace UVtools.WPF.Controls
}
};
+ public static readonly List<FileDialogFilter> OperationSettingFileFilter = new()
+ {
+ new()
+ {
+ Name = "UVtools operation settings",
+ Extensions = new List<string>
+ {
+ "uvtop",
+ }
+ }
+ };
+
public static readonly List<FileDialogFilter> ScriptsFileFilter = new()
{
- new FileDialogFilter
+ new()
{
Name = "Script Files",
Extensions = new List<string>
@@ -87,9 +99,9 @@ namespace UVtools.WPF.Controls
public static List<FileDialogFilter> ToAvaloniaFilter(string name, string extension)
{
- return new List<FileDialogFilter>(1)
+ return new(1)
{
- new FileDialogFilter {Name = name, Extensions = new List<string>(1) {extension}}
+ new() {Name = name, Extensions = new List<string>(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 @@
</TabItem>
- <TabItem Header="Light-off delay">
+ <TabItem Header="Light-off delay" IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}">
<Grid RowDefinitions="Auto,10,Auto">
<Border
@@ -403,12 +403,16 @@
<TextBlock
Grid.Row="0"
Grid.Column="6"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Text="Bottom lift height:"/>
<NumericUpDown
Grid.Row="0"
Grid.Column="8"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
Minimum="0"
Maximum="1000"
@@ -418,18 +422,24 @@
<TextBlock
Grid.Row="0"
Grid.Column="10"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
Text="mm"/>
<TextBlock
Grid.Row="2"
Grid.Column="6"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Text="Bottom lift speed:"/>
<NumericUpDown
Grid.Row="2"
Grid.Column="8"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
Minimum="0"
Maximum="1000"
@@ -439,18 +449,24 @@
<TextBlock
Grid.Row="2"
Grid.Column="10"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
Text="mm/min"/>
<TextBlock
Grid.Row="6"
Grid.Column="6"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Text="Bottom desired wait time:"/>
<NumericUpDown
Grid.Row="6"
Grid.Column="8"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
Minimum="0"
Maximum="1000"
@@ -460,12 +476,16 @@
<TextBlock
Grid.Row="6"
Grid.Column="10"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
Text="s"/>
<TextBlock
Grid.Row="8"
Grid.Column="6"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
HorizontalAlignment="Right"
FontWeight="Bold"
@@ -473,6 +493,8 @@
<TextBox
Grid.Row="8"
Grid.Column="8"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
VerticalAlignment="Center"
IsReadOnly="true"
FontWeight="Bold"
@@ -480,6 +502,8 @@
<TextBlock
Grid.Row="8"
Grid.Column="10"
+ IsVisible="{Binding SlicerFile.CanUseBottomLightOffDelay}"
+ IsEnabled="{Binding SlicerFile.CanUseBottomLightOffDelay}"
FontWeight="Bold"
VerticalAlignment="Center"
Text="s"/>
@@ -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
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
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 @@
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="UVtools.WPF.Controls.Tools.ToolPixelArithmeticControl">
+
+ <StackPanel Spacing="10">
+ <Grid RowDefinitions="Auto,10,Auto,10,Auto,10,Auto"
+ ColumnDefinitions="Auto,10,Auto,20,Auto,10,Auto,20,Auto,10,Auto">
+
+ <TextBlock Grid.Row="0" Grid.Column="0"
+ VerticalAlignment="Center"
+ Text="Operator:"/>
+
+ <ComboBox Grid.Row="0" Grid.Column="2"
+ Grid.ColumnSpan="9"
+ Width="610"
+ Items="{Binding Operation.Operator, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding Operation.Operator, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="0"
+ VerticalAlignment="Center"
+ IsVisible="{Binding Operation.ValueEnabled}"
+ IsEnabled="{Binding Operation.ValueEnabled}"
+ Text="Brightness:"/>
+
+ <StackPanel Grid.Row="2" Grid.Column="2"
+ VerticalAlignment="Center"
+ Orientation="Horizontal" Spacing="5"
+ IsVisible="{Binding Operation.ValueEnabled}"
+ IsEnabled="{Binding Operation.ValueEnabled}">
+
+ <NumericUpDown
+ Minimum="0"
+ Maximum="255"
+ Value="{Binding Operation.Value}"/>
+
+ <TextBlock VerticalAlignment="Center"
+ Text="{Binding Operation.ValuePercent, StringFormat={}{0}%}"/>
+
+ </StackPanel>
+
+ <CheckBox Grid.Row="2" Grid.Column="4"
+ Content="Affect empty/black pixels"
+ IsVisible="{Binding Operation.AffectBackPixelsEnabled}"
+ IsChecked="{Binding Operation.AffectBackPixels}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="4"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Right"
+ IsVisible="{Binding Operation.ThresholdEnabled}"
+ IsEnabled="{Binding Operation.ThresholdEnabled}"
+ Text="Max.:"/>
+
+ <NumericUpDown Grid.Row="2" Grid.Column="6"
+ Minimum="0"
+ Maximum="255"
+ IsVisible="{Binding Operation.ThresholdEnabled}"
+ IsEnabled="{Binding Operation.ThresholdEnabled}"
+ Value="{Binding Operation.ThresholdMaxValue}"/>
+
+ <TextBlock Grid.Row="2" Grid.Column="8"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Right"
+ IsVisible="{Binding Operation.ThresholdEnabled}"
+ IsEnabled="{Binding Operation.ThresholdEnabled}"
+ Text="Threshold:"/>
+
+ <ComboBox Grid.Row="2" Grid.Column="10"
+ Width="130"
+ IsVisible="{Binding Operation.ThresholdEnabled}"
+ IsEnabled="{Binding Operation.ThresholdEnabled}"
+ Items="{Binding Operation.ThresholdType, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}"
+ SelectedItem="{Binding Operation.ThresholdType, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/>
+
+
+ <TextBlock Grid.Row="4" Grid.Column="0"
+ VerticalAlignment="Center"
+ Text="Presets:"/>
+
+ <StackPanel Grid.Row="4" Grid.Column="2"
+ Grid.ColumnSpan="9"
+ VerticalAlignment="Center"
+ Orientation="Horizontal" Spacing="5">
+ <Button
+ Command="{Binding Operation.PresetStripAntiAliasing}"
+ Content="Strip anti-aliasing"/>
+
+ <Button
+ IsVisible="{Binding Operation.ValueEnabled}"
+ Command="{Binding Operation.PresetHalfBrightness}"
+ Content="Half brightness"/>
+ </StackPanel>
+
+ </Grid>
+ </StackPanel>
+
+</UserControl>
diff --git a/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs
new file mode 100644
index 0000000..eda6fcd
--- /dev/null
+++ b/UVtools.WPF/Controls/Tools/ToolPixelArithmeticControl.axaml.cs
@@ -0,0 +1,20 @@
+using Avalonia.Markup.Xaml;
+using UVtools.Core.Operations;
+
+namespace UVtools.WPF.Controls.Tools
+{
+ public partial class ToolPixelArithmeticControl : ToolControl
+ {
+ public OperationPixelArithmetic Operation => BaseOperation as OperationPixelArithmetic;
+ public ToolPixelArithmeticControl()
+ {
+ InitializeComponent();
+ BaseOperation = new OperationPixelArithmetic(SlicerFile);
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs
index eaff935..f68e6f2 100644
--- a/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs
+++ b/UVtools.WPF/Controls/Tools/ToolScriptingControl.axaml.cs
@@ -383,6 +383,11 @@ namespace UVtools.WPF.Controls.Tools
MinWidth = 150
};
+ if (numFLOAT.DecimalPlates > 0)
+ {
+ control.FormatString = $"F{numFLOAT.DecimalPlates}";
+ }
+
var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
valueProperty.Subscribe(value =>
{
@@ -407,6 +412,11 @@ namespace UVtools.WPF.Controls.Tools
MinWidth = 150
};
+ if (numDOUBLE.DecimalPlates > 0)
+ {
+ control.FormatString = $"F{numDOUBLE.DecimalPlates}";
+ }
+
var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
valueProperty.Subscribe(value =>
{
@@ -431,6 +441,11 @@ namespace UVtools.WPF.Controls.Tools
MinWidth = 150
};
+ if (numDECIMAL.DecimalPlates > 0)
+ {
+ control.FormatString = $"F{numDECIMAL.DecimalPlates}";
+ }
+
var valueProperty = control.GetObservable(NumericUpDown.ValueProperty);
valueProperty.Subscribe(value =>
{
diff --git a/UVtools.WPF/MainWindow.Clipboard.cs b/UVtools.WPF/MainWindow.Clipboard.cs
index bcbf66c..32784c7 100644
--- a/UVtools.WPF/MainWindow.Clipboard.cs
+++ b/UVtools.WPF/MainWindow.Clipboard.cs
@@ -70,7 +70,7 @@ namespace UVtools.WPF
return;
}
if (clip?.Operation is null) return;
- if (clip.Operation.HaveROI)
+ /*if (clip.Operation.HaveROI)
{
ROI = GetTransposedRectangle(clip.Operation.ROI);
}
@@ -78,7 +78,7 @@ namespace UVtools.WPF
if (clip.Operation.HaveMask)
{
AddMaskPoints(clip.Operation.MaskPoints);
- }
+ }*/
var operation = await ShowRunOperation(clip.Operation.GetType(), clip.Operation);
if (operation is null)
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index 019700e..01577b8 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -284,67 +284,24 @@
VerticalAlignment="Center">
<TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: \{0\}mm}"/>
- <TextBlock Text=" | Bottom layers: "/>
- <TextBlock Text="{Binding SlicerFile.BottomLayerCount}"/>
-
- <TextBlock Text=" | Exposures: "/>
-
- <TextBlock>
- <TextBlock.Text>
- <MultiBinding StringFormat="\{0\}s/\{1\}s">
- <Binding Path="SlicerFile.BottomExposureTime"/>
- <Binding Path="SlicerFile.ExposureTime"/>
- </MultiBinding>
- </TextBlock.Text>
- </TextBlock>
-
- <TextBlock IsVisible="{Binding SlicerFile.BottomLiftHeight}" Text=" | Lift: "/>
- <TextBlock IsVisible="{Binding SlicerFile.BottomLiftHeight}">
- <TextBlock.Text>
- <MultiBinding StringFormat="\{0\}/\{1\}mm @ \{2\}/\{3\}mm/min">
- <Binding Path="SlicerFile.BottomLiftHeight"/>
- <Binding Path="SlicerFile.LiftHeight"/>
- <Binding Path="SlicerFile.BottomLiftSpeed"/>
- <Binding Path="SlicerFile.LiftSpeed"/>
- </MultiBinding>
- </TextBlock.Text>
- </TextBlock>
+ <TextBlock Text=" | "/>
+ <TextBlock Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/>
- <!--
- <TextBlock IsVisible="{Binding SlicerFile.BottomLiftHeight}" Text="| Bottom lift:"/>
- <TextBlock IsVisible="{Binding SlicerFile.BottomLiftHeight}">
- <TextBlock.Text>
- <MultiBinding StringFormat="\{0\}mm @ \{1\}mm/min">
- <Binding Path="SlicerFile.BottomLiftHeight"/>
- <Binding Path="SlicerFile.BottomLiftSpeed"/>
- </MultiBinding>
- </TextBlock.Text>
- </TextBlock>
-
- <TextBlock IsVisible="{Binding SlicerFile.LiftHeight}" Text="| Lift:"/>
- <TextBlock IsVisible="{Binding SlicerFile.LiftHeight}">
- <TextBlock.Text>
- <MultiBinding StringFormat="\{0\}mm @ \{1\}mm/min">
- <Binding Path="SlicerFile.LiftHeight"/>
- <Binding Path="SlicerFile.LiftSpeed"/>
- </MultiBinding>
- </TextBlock.Text>
- </TextBlock>
- !-->
-
- <TextBlock IsVisible="{Binding SlicerFile.RetractSpeed}" Text=" | "/>
- <TextBlock IsVisible="{Binding SlicerFile.RetractSpeed}"
- Text="{Binding SlicerFile.RetractSpeed, StringFormat=Retract Speed: \{0\}mm/min}"/>
-
- <TextBlock IsVisible="{Binding SlicerFile.LightOffDelay}" Text=" | Light-off: "/>
- <TextBlock IsVisible="{Binding SlicerFile.LightOffDelay}">
- <TextBlock.Text>
- <MultiBinding StringFormat="\{0\}s/\{1\}s">
- <Binding Path="SlicerFile.BottomLightOffDelay"/>
- <Binding Path="SlicerFile.LightOffDelay"/>
- </MultiBinding>
- </TextBlock.Text>
- </TextBlock>
+ <TextBlock Text=" | "/>
+ <TextBlock Text="{Binding SlicerFile.ExposureRepresentation, StringFormat=Exposure: {0}}"/>
+
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}" Text=" | "/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}"
+ Text="{Binding SlicerFile.LiftRepresentation, StringFormat=Lift: {0}}"/>
+
+
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseRetractSpeed}" Text=" | "/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseRetractSpeed}"
+ Text="{Binding SlicerFile.RetractRepresentation, StringFormat=Retract: {0}}"/>
+
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}" Text=" | "/>
+ <TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLightOffDelay}"
+ Text="{Binding SlicerFile.LightOffDelayRepresentation, StringFormat=Light-off: {0}}"/>
<TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}"
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index 84d074a..fd456c1 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -132,17 +132,25 @@ namespace UVtools.WPF
Source = new Bitmap(App.GetAsset("/Assets/Icons/code-branch-16x16.png"))
}
},
- new()
+ /*new()
{
Tag = new OperationThreshold(),
Icon = new Avalonia.Controls.Image
{
Source = new Bitmap(App.GetAsset("/Assets/Icons/th-16x16.png"))
}
+ },*/
+ new()
+ {
+ Tag = new OperationLayerArithmetic(),
+ Icon = new Avalonia.Controls.Image
+ {
+ Source = new Bitmap(App.GetAsset("/Assets/Icons/square-root-16x16.png"))
+ }
},
new()
{
- Tag = new OperationArithmetic(),
+ Tag = new OperationPixelArithmetic(),
Icon = new Avalonia.Controls.Image
{
Source = new Bitmap(App.GetAsset("/Assets/Icons/square-root-16x16.png"))
@@ -792,6 +800,9 @@ namespace UVtools.WPF
ClearROIAndMask();
+ if(!Settings.Tools.LastUsedSettingsKeepOnCloseFile)
+ OperationSessionManager.Instance.Clear();
+
ResetDataContext();
}
@@ -1550,7 +1561,8 @@ namespace UVtools.WPF
return false;
});
-
+ OperationSessionManager.Instance.Add(baseOperation);
+
IsGUIEnabled = true;
if (result)
diff --git a/UVtools.WPF/Structures/OperationProfiles.cs b/UVtools.WPF/Structures/OperationProfiles.cs
index ff1e78e..b7fd8e2 100644
--- a/UVtools.WPF/Structures/OperationProfiles.cs
+++ b/UVtools.WPF/Structures/OperationProfiles.cs
@@ -24,31 +24,25 @@ namespace UVtools.WPF.Structures
/// </summary>
private static string FilePath => Path.Combine(UserSettings.SettingsFolder, "operation_profiles.xml");
- [XmlElement(typeof(OperationArithmetic))]
- [XmlElement(typeof(OperationBlur))]
- //[XmlElement(typeof(OperationCalculator))]
- [XmlElement(typeof(OperationChangeResolution))]
- //[XmlElement(typeof(OperationEditParameters))]
+ [XmlElement(typeof(OperationResize))]
[XmlElement(typeof(OperationFlip))]
- //[XmlElement(typeof(OperationLayerClone))]
- //[XmlElement(typeof(OperationLayerImport))]
- [XmlElement(typeof(OperationDynamicLayerHeight))]
- [XmlElement(typeof(OperationDynamicLifts))]
- //[XmlElement(typeof(OperationLayerReHeight))]
- //[XmlElement(typeof(OperationLayerRemove))]
- //[XmlElement(typeof(OperationMask))]
+ [XmlElement(typeof(OperationRotate))]
+ [XmlElement(typeof(OperationSolidify))]
[XmlElement(typeof(OperationMorph))]
[XmlElement(typeof(OperationRaftRelief))]
[XmlElement(typeof(OperationRedrawModel))]
- //[XmlElement(typeof(OperationMove))]
- //[XmlElement(typeof(OperationPattern))]
+ [XmlElement(typeof(OperationThreshold))]
+ [XmlElement(typeof(OperationLayerArithmetic))]
+ [XmlElement(typeof(OperationPixelArithmetic))]
[XmlElement(typeof(OperationPixelDimming))]
[XmlElement(typeof(OperationInfill))]
- //[XmlElement(typeof(OperationRepairLayers))]
- [XmlElement(typeof(OperationResize))]
- [XmlElement(typeof(OperationRotate))]
- [XmlElement(typeof(OperationThreshold))]
+ [XmlElement(typeof(OperationBlur))]
+ [XmlElement(typeof(OperationDynamicLayerHeight))]
+ [XmlElement(typeof(OperationDynamicLifts))]
+ [XmlElement(typeof(OperationChangeResolution))]
+
[XmlElement(typeof(OperationLayerExportGif))]
+
[XmlElement(typeof(OperationCalibrateExposureFinder))]
[XmlElement(typeof(OperationCalibrateElephantFoot))]
[XmlElement(typeof(OperationCalibrateXYZAccuracy))]
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index e0bae71..1793bcc 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
- <Version>2.10.0</Version>
+ <Version>2.11.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -74,6 +74,9 @@
<AvaloniaResource Include="Assets\Icons\*" />
</ItemGroup>
<ItemGroup>
+ <Compile Update="Controls\Tools\ToolLayerArithmeticControl.axaml.cs">
+ <DependentUpon>ToolLayerArithmeticControl.axaml</DependentUpon>
+ </Compile>
<Compile Update="Windows\PrusaSlicerManagerWindow.axaml.cs">
<DependentUpon>PrusaSlicerManagerWindow.axaml</DependentUpon>
</Compile>
diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs
index 2ce94f2..26fc181 100644
--- a/UVtools.WPF/UserSettings.cs
+++ b/UVtools.WPF/UserSettings.cs
@@ -22,7 +22,7 @@ namespace UVtools.WPF
public sealed class UserSettings : BindableBase
{
#region Constants
- public const ushort SETTINGS_VERSION = 2;
+ public const ushort SETTINGS_VERSION = 3;
#endregion
#region Sub classes
@@ -1122,6 +1122,36 @@ namespace UVtools.WPF
}
#endregion
+ #region Tools
+
+ [Serializable]
+ public sealed class ToolsUserSettings : BindableBase
+ {
+ private bool _restoreLastUsedSettings;
+ private bool _lastUsedSettingsKeepOnCloseFile = true;
+ private bool _lastUsedSettingsPriorityOverDefaultProfile = true;
+
+ public bool RestoreLastUsedSettings
+ {
+ get => _restoreLastUsedSettings;
+ set => RaiseAndSetIfChanged(ref _restoreLastUsedSettings, value);
+ }
+
+ public bool LastUsedSettingsKeepOnCloseFile
+ {
+ get => _lastUsedSettingsKeepOnCloseFile;
+ set => RaiseAndSetIfChanged(ref _lastUsedSettingsKeepOnCloseFile, value);
+ }
+
+ public bool LastUsedSettingsPriorityOverDefaultProfile
+ {
+ get => _lastUsedSettingsPriorityOverDefaultProfile;
+ set => RaiseAndSetIfChanged(ref _lastUsedSettingsPriorityOverDefaultProfile, value);
+ }
+ }
+
+ #endregion
+
#region Automations
[Serializable]
@@ -1214,6 +1244,7 @@ namespace UVtools.WPF
private IssuesUserSettings _issues;
private PixelEditorUserSettings _pixelEditor;
private LayerRepairUserSettings _layerRepair;
+ private ToolsUserSettings _tools;
private AutomationsUserSettings _automations;
private ushort _settingsVersion = SETTINGS_VERSION;
private string _appVersion;
@@ -1255,6 +1286,12 @@ namespace UVtools.WPF
set => _layerRepair = value;
}
+ public ToolsUserSettings Tools
+ {
+ get => _tools ??= new ToolsUserSettings();
+ set => _tools = value;
+ }
+
public AutomationsUserSettings Automations
{
get => _automations ??= new AutomationsUserSettings();
diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml b/UVtools.WPF/Windows/SettingsWindow.axaml
index a300e3f..c12fcb8 100644
--- a/UVtools.WPF/Windows/SettingsWindow.axaml
+++ b/UVtools.WPF/Windows/SettingsWindow.axaml
@@ -1413,7 +1413,7 @@
</ScrollViewer>
</TabItem>
- <TabItem Header="Automations" VerticalContentAlignment="Center">
+ <TabItem Header="Tools" VerticalContentAlignment="Center">
<ScrollViewer Name="ScrollViewer5">
<StackPanel Orientation="Vertical" Spacing="5">
<Border
@@ -1423,6 +1423,36 @@
<StackPanel Orientation="Vertical">
<TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Common"/>
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
+ <CheckBox IsChecked="{Binding Settings.Tools.RestoreLastUsedSettings}"
+ Content="Keep and restore the last used settings on operations per user session/instance"/>
+
+ <ToggleSwitch IsChecked="{Binding Settings.Tools.LastUsedSettingsKeepOnCloseFile}"
+ OffContent="Discard the session when closing or loading files"
+ OnContent="Keep the session when closing or loading files"/>
+
+ <ToggleSwitch IsChecked="{Binding Settings.Tools.LastUsedSettingsPriorityOverDefaultProfile}"
+ IsEnabled="{Binding Settings.Tools.RestoreLastUsedSettings}"
+ OffContent="Default profile will priority over the session"
+ OnContent="Session will priority over the default profile"/>
+ </StackPanel>
+
+ </StackPanel>
+ </Border>
+
+ </StackPanel>
+ </ScrollViewer>
+ </TabItem>
+
+ <TabItem Header="Automations" VerticalContentAlignment="Center">
+ <ScrollViewer Name="ScrollViewer6">
+ <StackPanel Orientation="Vertical" Spacing="5">
+ <Border
+ Classes="GroupBox"
+ Margin="5">
+
+ <StackPanel Orientation="Vertical">
+ <TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Common"/>
+ <StackPanel Margin="10" Orientation="Vertical" Spacing="10">
<CheckBox IsChecked="{Binding Settings.Automations.SaveFileAfterModifications}" Content="Auto save the file after apply any automation(s)"/>
</StackPanel>
@@ -1513,6 +1543,8 @@
</ScrollViewer>
</TabItem>
+
+
</TabControl>
<Border Grid.Row="1" Classes="FooterActions">
diff --git a/UVtools.WPF/Windows/ToolWindow.axaml b/UVtools.WPF/Windows/ToolWindow.axaml
index 7e8f766..ff1936d 100644
--- a/UVtools.WPF/Windows/ToolWindow.axaml
+++ b/UVtools.WPF/Windows/ToolWindow.axaml
@@ -365,6 +365,44 @@
<Grid RowDefinitions="Auto"
ColumnDefinitions="*">
<StackPanel Spacing="10" Orientation="Horizontal">
+ <Button
+ Command="{Binding #OptionsContextMenu.Open}"
+ Padding="10"
+ Content="☰">
+ <Button.ContextMenu>
+ <ContextMenu Name="OptionsContextMenu" PlacementMode="Top">
+ <MenuItem
+ IsVisible="{Binding CanROI}"
+ Command="{Binding SelectVolumeBoundingRectangle}"
+ Header="Select print volume ROI">
+ <MenuItem.Icon>
+ <Image Source="/Assets/Icons/expand-16x16.png"/>
+ </MenuItem.Icon>
+ </MenuItem>
+
+ <Separator IsVisible="{Binding CanROI}"/>
+
+ <MenuItem
+ IsVisible="{Binding CanHaveProfiles}"
+ Command="{Binding ImportSettings}"
+ Header="Import settings">
+ <MenuItem.Icon>
+ <Image Source="/Assets/Icons/file-import-16x16.png"/>
+ </MenuItem.Icon>
+ </MenuItem>
+
+ <MenuItem
+ IsVisible="{Binding CanHaveProfiles}"
+ Command="{Binding ExportSettings}"
+ Header="Export settings">
+ <MenuItem.Icon>
+ <Image Source="/Assets/Icons/file-export-16x16.png"/>
+ </MenuItem.Icon>
+ </MenuItem>
+ </ContextMenu>
+ </Button.ContextMenu>
+ </Button>
+
<Button
Padding="10"
IsDefault="True"
@@ -388,7 +426,8 @@
Spacing="10"
HorizontalAlignment="Right"
Orientation="Horizontal">
- <Button
+
+ <Button
Padding="10"
IsDefault="True"
IsVisible="{Binding ButtonOkVisible}"
diff --git a/UVtools.WPF/Windows/ToolWindow.axaml.cs b/UVtools.WPF/Windows/ToolWindow.axaml.cs
index eba5086..de40575 100644
--- a/UVtools.WPF/Windows/ToolWindow.axaml.cs
+++ b/UVtools.WPF/Windows/ToolWindow.axaml.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.ObjectModel;
using System.Drawing;
+using System.IO;
+using System.Xml.Serialization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
@@ -9,12 +11,13 @@ using Avalonia.Threading;
using MessageBox.Avalonia.Enums;
using UVtools.Core;
using UVtools.Core.Extensions;
+using UVtools.Core.Managers;
using UVtools.Core.Operations;
using UVtools.WPF.Controls;
using UVtools.WPF.Controls.Tools;
using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
-using Point = Avalonia.Point;
+using Helpers = UVtools.WPF.Controls.Helpers;
namespace UVtools.WPF.Windows
{
@@ -285,7 +288,20 @@ namespace UVtools.WPF.Windows
}
}
- public Rectangle ROI => App.MainWindow.ROI;
+ public bool CanROI => ToolControl.BaseOperation.CanROI;
+
+ public Rectangle ROI
+ {
+ get => App.MainWindow.ROI;
+ set
+ {
+ App.MainWindow.ROI = App.MainWindow.GetTransposedRectangle(value);
+ ToolControl.BaseOperation.ROI = value;
+ IsROIVisible = !value.IsEmpty;
+ RaisePropertyChanged();
+ }
+ }
+
public System.Drawing.Point[][] Masks => App.MainWindow.MaskPoints?.ToArray();
public bool ClearROIAndMaskAfterOperation
@@ -299,8 +315,8 @@ namespace UVtools.WPF.Windows
if (await this.MessageBoxQuestion("Are you sure you want to clear the current ROI?\n" +
"This action can not be reverted, to select another ROI you must quit this window and select it on layer preview.",
"Clear the current ROI?") != ButtonResult.Yes) return;
- IsROIVisible = false;
- App.MainWindow.ClearROI();
+
+ ROI = Rectangle.Empty;
ToolControl?.Callback(Callbacks.ClearROI);
}
@@ -311,12 +327,21 @@ namespace UVtools.WPF.Windows
"Clear the all masks?") != ButtonResult.Yes) return;
IsMasksVisible = false;
App.MainWindow.ClearMask();
+ ToolControl.BaseOperation.ClearMasks();
ToolControl?.Callback(Callbacks.ClearROI);
}
+ public void SelectVolumeBoundingRectangle()
+ {
+ ROI = SlicerFile.BoundingRectangle;
+ }
+
#endregion
#region Profiles
+
+ public bool CanHaveProfiles => ToolControl.BaseOperation.CanHaveProfiles;
+
public bool IsProfilesVisible
{
get => _isProfilesVisible;
@@ -565,8 +590,42 @@ namespace UVtools.WPF.Windows
_buttonOkText = toolControl.BaseOperation.ButtonOkText;
_buttonOkVisible = ButtonOkEnabled = toolControl.BaseOperation.HaveAction;
- if (toolControl.BaseOperation.HaveExecuted) // Come from a redo or something
+ bool fromSession = false;
+ if (!toolControl.BaseOperation.HaveExecuted && Settings.Tools.RestoreLastUsedSettings)
+ {
+ var operation = OperationSessionManager.Instance.Find(toolControl.BaseOperation.GetType());
+ if (operation is not null)
+ {
+ toolControl.BaseOperation = operation.Clone();
+ toolControl.BaseOperation.ClearROIandMasks();
+
+ switch (operation.LayerRangeSelection)
+ {
+ case Enumerations.LayerRangeSelection.None:
+ LayerIndexStart = operation.LayerIndexStart;
+ LayerIndexEnd = operation.LayerIndexEnd;
+ break;
+ default:
+ SelectLayers(operation.LayerRangeSelection);
+ break;
+ }
+
+ fromSession = true;
+ }
+ }
+
+ if (toolControl.BaseOperation.HaveExecuted) // Come from a redo or session
{
+ if (toolControl.BaseOperation.HaveROI)
+ {
+ ROI = toolControl.BaseOperation.ROI;
+ }
+
+ if (toolControl.BaseOperation.HaveMask)
+ {
+ App.MainWindow.AddMaskPoints(toolControl.BaseOperation.MaskPoints);
+ }
+
LayerIndexStart = toolControl.BaseOperation.LayerIndexStart;
LayerIndexEnd = toolControl.BaseOperation.LayerIndexEnd;
}
@@ -575,25 +634,38 @@ namespace UVtools.WPF.Windows
SelectLayers(toolControl.BaseOperation.StartLayerRangeSelection);
}
- //RaisePropertyChanged(nameof(IsContentVisible));
- //RaisePropertyChanged(nameof(IsROIVisible));
-
if (ToolControl.BaseOperation.CanHaveProfiles)
{
+ _isProfilesVisible = true;
var profiles = OperationProfiles.GetOperations(ToolControl.BaseOperation.GetType());
- Profiles.AddRange(profiles);
- IsProfilesVisible = true;
+ _profiles.AddRange(profiles);
- foreach (var operation in Profiles)
+ if (!toolControl.BaseOperation.HaveExecuted ||
+ (toolControl.BaseOperation.HaveExecuted && fromSession && !Settings.Tools.LastUsedSettingsPriorityOverDefaultProfile))
{
- if (operation.ProfileIsDefault)
+ //Operation profile = _profiles.FirstOrDefault(operation => operation.ProfileIsDefault);
+ foreach (var operation in Profiles)
{
- SelectedProfileItem = operation;
- break;
+ if (operation.ProfileIsDefault)
+ {
+ SelectedProfileItem = operation;
+ break;
+ }
}
}
}
+ if (!ReferenceEquals(toolControl.BaseOperation.SlicerFile, SlicerFile)) // Sanitize
+ {
+ toolControl.BaseOperation.SlicerFile = SlicerFile;
+ }
+
+
+ //RaisePropertyChanged(nameof(IsContentVisible));
+ //RaisePropertyChanged(nameof(IsROIVisible));
+
+
+
// Ensure the description don't stretch window
DispatcherTimer.Run(() =>
{
@@ -729,5 +801,69 @@ namespace UVtools.WPF.Windows
if (parent is null) return;
menu.Open(parent);
}
+
+ public async void ExportSettings()
+ {
+ if (ToolControl.BaseOperation is null) return;
+ var dialog = new SaveFileDialog
+ {
+ Filters = Helpers.OperationSettingFileFilter,
+ InitialFileName = ToolControl.BaseOperation.Id
+ };
+
+ var file = await dialog.ShowAsync(this);
+
+ if (string.IsNullOrWhiteSpace(file)) return;
+
+ try
+ {
+ XmlSerializer serializer = new(ToolControl.BaseOperation.GetType());
+ await using StreamWriter writer = new(file);
+ serializer.Serialize(writer, ToolControl.BaseOperation);
+ }
+ catch (Exception e)
+ {
+ await this.MessageBoxError(e.ToString(), "Error while trying to export the settings");
+ }
+ }
+
+ public async void ImportSettings()
+ {
+ var dialog = new OpenFileDialog
+ {
+ AllowMultiple = false,
+ Filters = Helpers.OperationSettingFileFilter
+ };
+
+ var files = await dialog.ShowAsync(this);
+
+ if (files is null || files.Length == 0) return;
+
+ try
+ {
+ XmlSerializer serializer = new(ToolControl.BaseOperation.GetType());
+ await using var stream = File.OpenRead(files[0]);
+ var operation = (Operation)serializer.Deserialize(stream);
+
+ operation.SlicerFile = SlicerFile;
+ ToolControl.BaseOperation = operation;
+ switch (operation.LayerRangeSelection)
+ {
+ case Enumerations.LayerRangeSelection.None:
+ LayerIndexStart = operation.LayerIndexStart;
+ LayerIndexEnd = operation.LayerIndexEnd;
+ break;
+ default:
+ SelectLayers(operation.LayerRangeSelection);
+ break;
+ }
+
+ ToolControl.Callback(Callbacks.ProfileLoaded);
+ }
+ catch (Exception e)
+ {
+ await this.MessageBoxError(e.ToString(), "Error while trying to import the settings");
+ }
+ }
}
}