mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-15 21:06:19 +02:00
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
This commit is contained in:
+25
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// </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 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);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace UVtools.Core.Operations
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
ApplyMask(original, mat);
|
||||
ApplyMask(original, target);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace UVtools.Core.Operations
|
||||
CvInvoke.Flip(target, target, FlipTypeOpenCV);
|
||||
}
|
||||
|
||||
ApplyMask(original, mat);
|
||||
ApplyMask(original, target);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+38
-31
@@ -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()
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -240,7 +240,7 @@ namespace UVtools.Core.Operations
|
||||
|
||||
if (modified)
|
||||
{
|
||||
ApplyMask(original, fullMat);
|
||||
ApplyMask(original, fullMatRoi);
|
||||
SlicerFile[fullMatLayerIndex].LayerMat = fullMat;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace UVtools.Core.Operations
|
||||
CvInvoke.DrawContours(target, contours, i, EmguExtensions.WhiteByte, -1);
|
||||
}
|
||||
|
||||
ApplyMask(original, mat);
|
||||
ApplyMask(original, target);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 161 B |
@@ -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}}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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\}}"/>
|
||||
|
||||
+1
-1
@@ -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"
|
||||
>
|
||||
|
||||
+4
-4
@@ -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()
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 =>
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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=" | "/>
|
||||
<TextBlock Text="{Binding SlicerFile.BottomLayerCount, StringFormat=Bottom layers: {0}}"/>
|
||||
|
||||
<TextBlock Text=" | Exposures: "/>
|
||||
<TextBlock Text=" | "/>
|
||||
<TextBlock Text="{Binding SlicerFile.ExposureRepresentation, StringFormat=Exposure: {0}}"/>
|
||||
|
||||
<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.CanUseAnyLiftHeight}" Text=" | "/>
|
||||
<TextBlock IsVisible="{Binding SlicerFile.CanUseAnyLiftHeight}"
|
||||
Text="{Binding SlicerFile.LiftRepresentation, StringFormat=Lift: {0}}"/>
|
||||
|
||||
<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 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.CanUseRetractSpeed}" Text=" | "/>
|
||||
<TextBlock IsVisible="{Binding SlicerFile.CanUseRetractSpeed}"
|
||||
Text="{Binding SlicerFile.RetractRepresentation, StringFormat=Retract: {0}}"/>
|
||||
|
||||
<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 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}"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))]
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1413,8 +1413,38 @@
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Automations" VerticalContentAlignment="Center">
|
||||
<TabItem Header="Tools" VerticalContentAlignment="Center">
|
||||
<ScrollViewer Name="ScrollViewer5">
|
||||
<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.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"
|
||||
@@ -1513,6 +1543,8 @@
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
|
||||
|
||||
</TabControl>
|
||||
|
||||
<Border Grid.Row="1" Classes="FooterActions">
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user