* (Add) Auto-updater: When a new version is detected UVtools still show the same green button at top,
on click, it will prompt for auto or manual update.
On Linux and Mac the script will kill all UVtools instances and auto-upgrade.
On Windows the user must close all instances and continue with the shown MSI installation
* (Add) Tool profiles: Create and remove named presets for some tools
* (Add) Event handler for handling non-UI thread exceptions
* (Fix) Mac: File - Open in a new window was not working
* (Fix) Tool - Rotate: Allow negative angles
* (Fix) Tool - Rotate: The operation was inverting the angle
* (Fix) Tools: Select normal layers can crash the program with small files with low layer count, eg: 3 layers total
This commit is contained in:
Tiago Conceição
2020-11-05 18:27:44 +00:00
parent 50ce7d8f12
commit 5da3c7e173
55 changed files with 1342 additions and 119 deletions
+13
View File
@@ -1,5 +1,18 @@
# Changelog # Changelog
## 05/11/2020 - v1.1.3
* (Add) Auto-updater: When a new version is detected UVtools still show the same green button at top,
on click, it will prompt for auto or manual update.
On Linux and Mac the script will kill all UVtools instances and auto-upgrade.
On Windows the user must close all instances and continue with the shown MSI installation
* (Add) Tool profiles: Create and remove named presets for some tools
* (Add) Event handler for handling non-UI thread exceptions
* (Fix) Mac: File - Open in a new window was not working
* (Fix) Tool - Rotate: Allow negative angles
* (Fix) Tool - Rotate: The operation was inverting the angle
* (Fix) Tools: Select normal layers can crash the program with small files with low layer count, eg: 3 layers total
## 02/11/2020 - v1.1.2 ## 02/11/2020 - v1.1.2
* (Add) Program start elapsed seconds on Log * (Add) Program start elapsed seconds on Log
+1 -1
View File
@@ -685,7 +685,7 @@ namespace UVtools.Core
var halfHeight = target.Height / 2.0f; var halfHeight = target.Height / 2.0f;
using (var translateTransform = new Matrix<double>(2, 3)) using (var translateTransform = new Matrix<double>(2, 3))
{ {
CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), (double) operation.AngleDegrees, 1.0, translateTransform); CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), (double) -operation.AngleDegrees, 1.0, translateTransform);
/*var rect = new RotatedRect(PointF.Empty, mat.Size, (float) angle).MinAreaRect(); /*var rect = new RotatedRect(PointF.Empty, mat.Size, (float) angle).MinAreaRect();
translateTransform[0, 2] += rect.Width / 2.0 - mat.Cols / 2.0; translateTransform[0, 2] += rect.Width / 2.0 - mat.Cols / 2.0;
translateTransform[0, 2] += rect.Height / 2.0 - mat.Rows / 2.0;*/ translateTransform[0, 2] += rect.Height / 2.0 - mat.Rows / 2.0;*/
+2
View File
@@ -6,11 +6,13 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Drawing; using System.Drawing;
using Emgu.CV; using Emgu.CV;
namespace UVtools.Core.Objects namespace UVtools.Core.Objects
{ {
[Serializable]
public sealed class Kernel public sealed class Kernel
{ {
public Matrix<byte> Matrix { get; set; } public Matrix<byte> Matrix { get; set; }
+58 -3
View File
@@ -6,17 +6,22 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Drawing; using System.Drawing;
using System.Xml.Serialization;
using Emgu.CV; using Emgu.CV;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public abstract class Operation : BindableBase public abstract class Operation : BindableBase
{ {
private Rectangle _roi = Rectangle.Empty; private Rectangle _roi = Rectangle.Empty;
private uint _layerIndexEnd; private uint _layerIndexEnd;
private uint _layerIndexStart; private uint _layerIndexStart;
private string _profileName;
private Enumerations.LayerRangeSelection _layerRangeSelection = Enumerations.LayerRangeSelection.All;
public const byte ClassNameLength = 9; public const byte ClassNameLength = 9;
/// <summary> /// <summary>
@@ -24,7 +29,16 @@ namespace UVtools.Core.Operations
/// </summary> /// </summary>
public string Id => GetType().Name.Remove(0, ClassNameLength); public string Id => GetType().Name.Remove(0, ClassNameLength);
public virtual Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.All; public virtual Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.All;
/// <summary>
/// Gets the last used layer range selection, returns none if custom
/// </summary>
public Enumerations.LayerRangeSelection LayerRangeSelection
{
get => _layerRangeSelection;
set => RaiseAndSetIfChanged(ref _layerRangeSelection, value);
}
/// <summary> /// <summary>
/// Gets if this operation should set layer range to the actual layer index on layer preview /// Gets if this operation should set layer range to the actual layer index on layer preview
@@ -32,9 +46,14 @@ namespace UVtools.Core.Operations
public virtual bool PassActualLayerIndex => false; public virtual bool PassActualLayerIndex => false;
/// <summary> /// <summary>
/// Gets if this control can make use of ROI /// Gets if this operation can make use of ROI
/// </summary> /// </summary>
public virtual bool CanROI { get; set; } = true; public virtual bool CanROI => true;
/// <summary>
/// Gets if this operation can store profiles
/// </summary>
public virtual bool CanHaveProfiles => true;
/// <summary> /// <summary>
/// Gets if this operation supports cancellation /// Gets if this operation supports cancellation
@@ -105,9 +124,19 @@ namespace UVtools.Core.Operations
public uint LayerRangeCount => LayerIndexEnd - LayerIndexStart + 1; public uint LayerRangeCount => LayerIndexEnd - LayerIndexStart + 1;
/// <summary>
/// Gets the name for this profile
/// </summary>
public string ProfileName
{
get => _profileName;
set => RaiseAndSetIfChanged(ref _profileName, value);
}
/// <summary> /// <summary>
/// Gets or sets an ROI to process this operation /// Gets or sets an ROI to process this operation
/// </summary> /// </summary>
[XmlIgnore]
public Rectangle ROI public Rectangle ROI
{ {
get => _roi; get => _roi;
@@ -120,5 +149,31 @@ namespace UVtools.Core.Operations
{ {
return HaveROI ? new Mat(defaultMat, ROI) : defaultMat; return HaveROI ? new Mat(defaultMat, ROI) : defaultMat;
} }
public virtual Operation Clone()
{
return MemberwiseClone() as Operation;
}
public override string ToString()
{
if (!string.IsNullOrEmpty(ProfileName)) return ProfileName;
var result = $"{Title}: {LayerRangeString}";
return result;
}
public virtual string LayerRangeString
{
get
{
if (LayerRangeSelection == Enumerations.LayerRangeSelection.None)
{
return $" [Layers: {LayerIndexStart}-{LayerIndexEnd}]";
}
return $" [Layers: {LayerRangeSelection}]";
}
}
} }
} }
+26 -2
View File
@@ -9,11 +9,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Xml.Serialization;
using Emgu.CV; using Emgu.CV;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationArithmetic : Operation public class OperationArithmetic : Operation
{ {
private string _sentence; private string _sentence;
@@ -85,8 +87,10 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _sentence, value); set => RaiseAndSetIfChanged(ref _sentence, value);
} }
[XmlIgnore]
public List<ArithmeticOperation> Operations { get; } = new List<ArithmeticOperation>(); public List<ArithmeticOperation> Operations { get; } = new List<ArithmeticOperation>();
[XmlIgnore]
public List<uint> SetLayers { get; } = new List<uint>(); public List<uint> SetLayers { get; } = new List<uint>();
public bool IsValid => SetLayers.Count > 0 & Operations.Count > 0; public bool IsValid => SetLayers.Count > 0 & Operations.Count > 0;
@@ -181,9 +185,29 @@ namespace UVtools.Core.Operations
return true; return true;
} }
public OperationArithmetic() public override string ToString()
{ {
var result = $"{_sentence}" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
protected bool Equals(OperationArithmetic other)
{
return _sentence == other._sentence;
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((OperationArithmetic) obj);
}
public override int GetHashCode()
{
return (_sentence != null ? _sentence.GetHashCode() : 0);
} }
} }
} }
+43 -1
View File
@@ -6,11 +6,14 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Text; using System.Text;
using System.Xml.Serialization;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public sealed class OperationBlur : Operation public sealed class OperationBlur : Operation
{ {
private BlurAlgorithm _blurOperation = BlurAlgorithm.Blur; private BlurAlgorithm _blurOperation = BlurAlgorithm.Blur;
@@ -80,6 +83,19 @@ namespace UVtools.Core.Operations
new StringTag("Filter 2D: Applies an arbitrary linear filter to an image", BlurAlgorithm.Filter2D), new StringTag("Filter 2D: Applies an arbitrary linear filter to an image", BlurAlgorithm.Filter2D),
}; };
public byte BlurTypeIndex
{
get
{
for (byte i = 0; i < BlurTypes.Length; i++)
{
if ((BlurAlgorithm)BlurTypes[i].Tag == BlurOperation) return i;
}
return 0;
}
}
public BlurAlgorithm BlurOperation public BlurAlgorithm BlurOperation
{ {
get => _blurOperation; get => _blurOperation;
@@ -92,10 +108,36 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _size, value); set => RaiseAndSetIfChanged(ref _size, value);
} }
[XmlIgnore]
public Kernel Kernel { get; set; } = new Kernel(); public Kernel Kernel { get; set; } = new Kernel();
public override string ToString()
{
var result = $"[{_blurOperation}] [Size: {_size}]" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#endregion #endregion
#region Equality
private bool Equals(OperationBlur other)
{
return _blurOperation == other._blurOperation && _size == other._size;
}
public override bool Equals(object obj)
{
return ReferenceEquals(this, obj) || obj is OperationBlur other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
return ((int) _blurOperation * 397) ^ (int) _size;
}
}
#endregion
} }
} }
@@ -12,6 +12,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationCalculator : Operation public class OperationCalculator : Operation
{ {
public override string Title => "Calculator"; public override string Title => "Calculator";
@@ -23,9 +24,11 @@ namespace UVtools.Core.Operations
public override string ProgressAction => null; public override string ProgressAction => null;
public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None; public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override bool CanROI => false; public override bool CanROI => false;
public override bool CanHaveProfiles => false;
public MillimetersToPixels CalcMillimetersToPixels { get; set; } public MillimetersToPixels CalcMillimetersToPixels { get; set; }
public LightOffDelayC CalcLightOffDelay { get; set; } public LightOffDelayC CalcLightOffDelay { get; set; }
@@ -13,6 +13,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public sealed class OperationChangeResolution : Operation public sealed class OperationChangeResolution : Operation
{ {
private uint _newResolutionX; private uint _newResolutionX;
@@ -48,8 +49,8 @@ namespace UVtools.Core.Operations
#region Overrides #region Overrides
public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None; public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override bool CanROI { get; set; } = false; public override bool CanROI => false;
public override string Title => "Change print resolution"; public override string Title => "Change print resolution";
public override string Description => public override string Description =>
"Crops or resizes all layer images to fit an alternate print area\n\n" + "Crops or resizes all layer images to fit an alternate print area\n\n" +
@@ -145,6 +146,35 @@ namespace UVtools.Core.Operations
public static Resolution[] Presets => GetResolutions(); public static Resolution[] Presets => GetResolutions();
public override string ToString()
{
var result = $"{_newResolutionX} x {_newResolutionY}";
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#endregion
#region Equality
private bool Equals(OperationChangeResolution other)
{
return _newResolutionX == other._newResolutionX && _newResolutionY == other._newResolutionY;
}
public override bool Equals(object obj)
{
return ReferenceEquals(this, obj) || obj is OperationChangeResolution other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
return ((int) _newResolutionX * 397) ^ (int) _newResolutionY;
}
}
#endregion #endregion
} }
@@ -6,6 +6,7 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UVtools.Core.FileFormats; using UVtools.Core.FileFormats;
@@ -13,13 +14,14 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationEditParameters : Operation public class OperationEditParameters : Operation
{ {
private bool _perLayerOverride; private bool _perLayerOverride;
public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None; public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override bool CanROI { get; set; } = false; public override bool CanROI => false;
public override string Title => "Edit print parameters"; public override string Title => "Edit print parameters";
@@ -58,6 +60,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Changing print parameters"; public override string ProgressAction => "Changing print parameters";
public override bool CanHaveProfiles => false;
public override StringTag Validate(params object[] parameters) public override StringTag Validate(params object[] parameters)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
+47 -3
View File
@@ -11,8 +11,12 @@ using Emgu.CV.CvEnum;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationFlip : Operation public class OperationFlip : Operation
{ {
private bool _makeCopy;
private Enumerations.FlipDirection _flipDirection = Enumerations.FlipDirection.Horizontally;
public override string Title => "Flip"; public override string Title => "Flip";
public override string Description => public override string Description =>
"Flip the layers of the model vertically and/or horizontally."; "Flip the layers of the model vertically and/or horizontally.";
@@ -29,9 +33,19 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Flipped layers"; public override string ProgressAction => "Flipped layers";
public Enumerations.FlipDirection FlipDirection { get; set; } = Enumerations.FlipDirection.Horizontally; public Enumerations.FlipDirection FlipDirection
{
get => _flipDirection;
set => RaiseAndSetIfChanged(ref _flipDirection, value);
}
public bool MakeCopy { get; set; } public bool MakeCopy
{
get => _makeCopy;
set => RaiseAndSetIfChanged(ref _makeCopy, value);
}
public static Array FlipDirections => Enum.GetValues(typeof(Enumerations.FlipDirection));
public FlipType FlipTypeOpenCV public FlipType FlipTypeOpenCV
{ {
@@ -55,6 +69,36 @@ namespace UVtools.Core.Operations
} }
} }
public static Array FlipDirections => Enum.GetValues(typeof(Enumerations.FlipDirection)); public override string ToString()
{
var result = $"[{_flipDirection}] [Blend: {_makeCopy}]" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#region Equality
protected bool Equals(OperationFlip other)
{
return _makeCopy == other._makeCopy && _flipDirection == other._flipDirection;
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((OperationFlip) obj);
}
public override int GetHashCode()
{
unchecked
{
return (_makeCopy.GetHashCode() * 397) ^ (int) _flipDirection;
}
}
#endregion
} }
} }
@@ -5,19 +5,22 @@
* Everyone is permitted to copy and distribute verbatim copies * Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Text; using System.Text;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public sealed class OperationLayerClone : Operation public sealed class OperationLayerClone : Operation
{ {
private uint _clones = 1; private uint _clones = 1;
#region Overrides #region Overrides
public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.Current; public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Current;
public override bool CanROI { get; set; } = false; public override bool CanROI => false;
public override bool PassActualLayerIndex => true; public override bool PassActualLayerIndex => true;
public override string Title => "Clone layers"; public override string Title => "Clone layers";
@@ -34,6 +37,8 @@ namespace UVtools.Core.Operations
public override bool CanCancel => false; public override bool CanCancel => false;
public override bool CanHaveProfiles => false;
public override StringTag Validate(params object[] parameters) public override StringTag Validate(params object[] parameters)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
@@ -20,6 +20,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public sealed class OperationLayerImport : Operation public sealed class OperationLayerImport : Operation
{ {
private uint _insertAfterLayerIndex; private uint _insertAfterLayerIndex;
@@ -31,8 +32,8 @@ namespace UVtools.Core.Operations
#region Overrides #region Overrides
public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None; public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override bool CanROI { get; set; } = false; public override bool CanROI => false;
public override string Title => "Import Layers"; public override string Title => "Import Layers";
public override string Description => public override string Description =>
@@ -51,6 +52,8 @@ namespace UVtools.Core.Operations
public override uint LayerIndexStart => InsertAfterLayerIndex + (ReplaceStartLayer ? 0u : 1); public override uint LayerIndexStart => InsertAfterLayerIndex + (ReplaceStartLayer ? 0u : 1);
public override uint LayerIndexEnd => (uint)(LayerIndexStart + Count - 1); public override uint LayerIndexEnd => (uint)(LayerIndexStart + Count - 1);
public override bool CanHaveProfiles => false;
public override StringTag Validate(params object[] parameters) public override StringTag Validate(params object[] parameters)
{ {
var result = new ConcurrentBag<StringTag>(); var result = new ConcurrentBag<StringTag>();
@@ -14,14 +14,15 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public sealed class OperationLayerReHeight : Operation public sealed class OperationLayerReHeight : Operation
{ {
private OperationLayerReHeightItem _item; private OperationLayerReHeightItem _item;
#region Overrides #region Overrides
public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.None; public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.None;
public override bool CanROI { get; set; } = false; public override bool CanROI => false;
public override string Title => "Adjust layer height"; public override string Title => "Adjust layer height";
public override string Description => public override string Description =>
"Adjust the layer height of the model\n\n" + "Adjust the layer height of the model\n\n" +
@@ -38,6 +39,8 @@ namespace UVtools.Core.Operations
public override bool CanCancel => false; public override bool CanCancel => false;
public override bool CanHaveProfiles => false;
public override StringTag Validate(params object[] parameters) public override StringTag Validate(params object[] parameters)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
@@ -5,16 +5,19 @@
* Everyone is permitted to copy and distribute verbatim copies * Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Text; using System.Text;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public sealed class OperationLayerRemove : Operation public sealed class OperationLayerRemove : Operation
{ {
#region Overrides #region Overrides
public override Enumerations.LayerRangeSelection LayerRangeSelection => Enumerations.LayerRangeSelection.Current; public override Enumerations.LayerRangeSelection StartLayerRangeSelection => Enumerations.LayerRangeSelection.Current;
public override bool CanROI { get; set; } = false; public override bool CanROI => false;
public override bool PassActualLayerIndex => true; public override bool PassActualLayerIndex => true;
public override string Title => "Remove layers"; public override string Title => "Remove layers";
@@ -32,6 +35,8 @@ namespace UVtools.Core.Operations
public override bool CanCancel => false; public override bool CanCancel => false;
public override bool CanHaveProfiles => false;
#endregion #endregion
#region Properties #region Properties
+6
View File
@@ -6,13 +6,16 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Text; using System.Text;
using System.Xml.Serialization;
using Emgu.CV; using Emgu.CV;
using Emgu.CV.CvEnum; using Emgu.CV.CvEnum;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationMask : Operation public class OperationMask : Operation
{ {
public override string Title => "Mask"; public override string Title => "Mask";
@@ -30,6 +33,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Masked layers"; public override string ProgressAction => "Masked layers";
public override bool CanHaveProfiles => false;
public override StringTag Validate(params object[] parameters) public override StringTag Validate(params object[] parameters)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
@@ -41,6 +46,7 @@ namespace UVtools.Core.Operations
return new StringTag(sb.ToString()); return new StringTag(sb.ToString());
} }
[XmlIgnore]
public Mat Mask { get; set; } public Mat Mask { get; set; }
public bool HaveMask => !(Mask is null); public bool HaveMask => !(Mask is null);
+50
View File
@@ -6,11 +6,14 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Xml.Serialization;
using Emgu.CV.CvEnum; using Emgu.CV.CvEnum;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public sealed class OperationMorph : Operation public sealed class OperationMorph : Operation
{ {
private MorphOp _morphOperation = MorphOp.Erode; private MorphOp _morphOperation = MorphOp.Erode;
@@ -46,6 +49,19 @@ namespace UVtools.Core.Operations
new StringTag("Gradient - Removes the interior areas of objects", MorphOp.Gradient), new StringTag("Gradient - Removes the interior areas of objects", MorphOp.Gradient),
}; };
public byte MorphOperationIndex
{
get
{
for (byte i = 0; i < MorphOperations.Length; i++)
{
if ((MorphOp) MorphOperations[i].Tag == MorphOperation) return i;
}
return 0;
}
}
public MorphOp MorphOperation public MorphOp MorphOperation
{ {
get => _morphOperation; get => _morphOperation;
@@ -70,8 +86,42 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _fadeInOut, value); set => RaiseAndSetIfChanged(ref _fadeInOut, value);
} }
[XmlIgnore]
public Kernel Kernel { get; set; } = new Kernel(); public Kernel Kernel { get; set; } = new Kernel();
public override string ToString()
{
var result = $"[{_morphOperation}] [Iterations: {_iterationsStart}/{_iterationsEnd}] [Fade: {_fadeInOut}]" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#endregion
#region Equality
private bool Equals(OperationMorph other)
{
return _morphOperation == other._morphOperation && _iterationsStart == other._iterationsStart && _iterationsEnd == other._iterationsEnd && _fadeInOut == other._fadeInOut;
}
public override bool Equals(object obj)
{
return ReferenceEquals(this, obj) || obj is OperationMorph other && Equals(other);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (int) _morphOperation;
hashCode = (hashCode * 397) ^ (int) _iterationsStart;
hashCode = (hashCode * 397) ^ (int) _iterationsEnd;
hashCode = (hashCode * 397) ^ _fadeInOut.GetHashCode();
return hashCode;
}
}
#endregion #endregion
} }
} }
+3
View File
@@ -13,6 +13,7 @@ using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationMove : Operation public class OperationMove : Operation
{ {
public override string Title => "Move"; public override string Title => "Move";
@@ -29,6 +30,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => (IsCutMove ? "Moved" : "Copied")+" layers"; public override string ProgressAction => (IsCutMove ? "Moved" : "Copied")+" layers";
public override bool CanHaveProfiles => false;
public override StringTag Validate(params object[] parameters) public override StringTag Validate(params object[] parameters)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
@@ -5,12 +5,15 @@
* Everyone is permitted to copy and distribute verbatim copies * Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Drawing; using System.Drawing;
using System.Text; using System.Text;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationPattern : Operation public class OperationPattern : Operation
{ {
private Enumerations.Anchor _anchor = Enumerations.Anchor.None; private Enumerations.Anchor _anchor = Enumerations.Anchor.None;
@@ -37,6 +40,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Patterned layers"; public override string ProgressAction => "Patterned layers";
public override bool CanHaveProfiles => false;
public override StringTag Validate(params object[] parameters) public override StringTag Validate(params object[] parameters)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
@@ -6,12 +6,15 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Text; using System.Text;
using System.Xml.Serialization;
using Emgu.CV; using Emgu.CV;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationPixelDimming : Operation public class OperationPixelDimming : Operation
{ {
private uint _borderSize = 5; private uint _borderSize = 5;
@@ -68,12 +71,14 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _bordersOnly, value); set => RaiseAndSetIfChanged(ref _bordersOnly, value);
} }
[XmlIgnore]
public Matrix<byte> EvenPattern public Matrix<byte> EvenPattern
{ {
get => _evenPattern; get => _evenPattern;
set => RaiseAndSetIfChanged(ref _evenPattern, value); set => RaiseAndSetIfChanged(ref _evenPattern, value);
} }
[XmlIgnore]
public Matrix<byte> OddPattern public Matrix<byte> OddPattern
{ {
get => _oddPattern; get => _oddPattern;
@@ -81,5 +86,37 @@ namespace UVtools.Core.Operations
} }
#endregion #endregion
public override string ToString()
{
var result = $"[Border: {_borderSize}px] [Only borders: {_bordersOnly}]" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#region Equality
protected bool Equals(OperationPixelDimming other)
{
return _borderSize == other._borderSize && _bordersOnly == other._bordersOnly;
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((OperationPixelDimming) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((int) _borderSize * 397) ^ _bordersOnly.GetHashCode();
}
}
#endregion
} }
} }
@@ -6,12 +6,15 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Xml.Serialization;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationRepairLayers : Operation public class OperationRepairLayers : Operation
{ {
private bool _repairIslands = true; private bool _repairIslands = true;
@@ -21,7 +24,7 @@ namespace UVtools.Core.Operations
private ushort _removeIslandsRecursiveIterations = 4; private ushort _removeIslandsRecursiveIterations = 4;
private uint _gapClosingIterations = 1; private uint _gapClosingIterations = 1;
private uint _noiseRemovalIterations = 0; private uint _noiseRemovalIterations = 0;
public override bool CanROI { get; set; } = false; public override bool CanROI => false;
public override string Title => "Repair layers and issues"; public override string Title => "Repair layers and issues";
public override string Description => null; public override string Description => null;
@@ -32,6 +35,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Repaired layers"; public override string ProgressAction => "Repaired layers";
public override bool CanHaveProfiles => false;
public override StringTag Validate(params object[] parameters) public override StringTag Validate(params object[] parameters)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
@@ -86,8 +91,10 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _noiseRemovalIterations, value); set => RaiseAndSetIfChanged(ref _noiseRemovalIterations, value);
} }
[XmlIgnore]
public IslandDetectionConfiguration IslandDetectionConfig { get; set; } public IslandDetectionConfiguration IslandDetectionConfig { get; set; }
[XmlIgnore]
public List<LayerIssue> Issues { get; set; } public List<LayerIssue> Issues { get; set; }
} }
+40 -1
View File
@@ -5,11 +5,14 @@
* Everyone is permitted to copy and distribute verbatim copies * Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
using System.Text; using System.Text;
using UVtools.Core.Objects; using UVtools.Core.Objects;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationResize : Operation public class OperationResize : Operation
{ {
private decimal _x = 100; private decimal _x = 100;
@@ -31,7 +34,7 @@ namespace UVtools.Core.Operations
public override string ProgressTitle => public override string ProgressTitle =>
$"Resizing from layers {LayerIndexStart} to {LayerIndexEnd} at X:{X}% Y:{Y}% "; $"Resizing from layers {LayerIndexStart} to {LayerIndexEnd} at X:{X}% Y:{Y}% ";
public override string ProgressAction => "Reiszed layers"; public override string ProgressAction => "Resized layers";
public override StringTag Validate(params object[] parameters) public override StringTag Validate(params object[] parameters)
{ {
@@ -86,5 +89,41 @@ namespace UVtools.Core.Operations
public OperationResize() public OperationResize()
{ {
} }
public override string ToString()
{
var result = $"[X: {_x}%, Y: {_y}%] [Fade: {_isFade}] [Constrain: {_constrainXy}]"+ LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#region Equality
protected bool Equals(OperationResize other)
{
return _x == other._x && _y == other._y && _constrainXy == other._constrainXy && _isFade == other._isFade;
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((OperationResize) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = _x.GetHashCode();
hashCode = (hashCode * 397) ^ _y.GetHashCode();
hashCode = (hashCode * 397) ^ _constrainXy.GetHashCode();
hashCode = (hashCode * 397) ^ _isFade.GetHashCode();
return hashCode;
}
}
#endregion
} }
} }
@@ -10,6 +10,7 @@ using System;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationRotate : Operation public class OperationRotate : Operation
{ {
private decimal _angleDegrees = 90; private decimal _angleDegrees = 90;
@@ -30,5 +31,34 @@ namespace UVtools.Core.Operations
get => _angleDegrees; get => _angleDegrees;
set => RaiseAndSetIfChanged(ref _angleDegrees, value); set => RaiseAndSetIfChanged(ref _angleDegrees, value);
} }
public override string ToString()
{
var result = $"[Angle: {Math.Abs(_angleDegrees)}º {(AngleDegrees < 0 ? "CCW" : "CW")}]" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#region Equality
protected bool Equals(OperationRotate other)
{
return _angleDegrees == other._angleDegrees;
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((OperationRotate) obj);
}
public override int GetHashCode()
{
return _angleDegrees.GetHashCode();
}
#endregion
} }
} }
@@ -6,8 +6,11 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public sealed class OperationSolidify : Operation public sealed class OperationSolidify : Operation
{ {
#region Overrides #region Overrides
@@ -26,6 +29,8 @@ namespace UVtools.Core.Operations
public override string ProgressAction => "Solidified layers"; public override string ProgressAction => "Solidified layers";
public override bool CanHaveProfiles => false;
#endregion #endregion
} }
} }
@@ -11,6 +11,7 @@ using Emgu.CV.CvEnum;
namespace UVtools.Core.Operations namespace UVtools.Core.Operations
{ {
[Serializable]
public class OperationThreshold : Operation public class OperationThreshold : Operation
{ {
private byte _threshold = 127; private byte _threshold = 127;
@@ -51,5 +52,40 @@ namespace UVtools.Core.Operations
} }
public static Array ThresholdTypes => Enum.GetValues(typeof(ThresholdType)); public static Array ThresholdTypes => Enum.GetValues(typeof(ThresholdType));
public override string ToString()
{
var result = $"[{_type} = {_threshold} / {_maximum}]" + LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#region Equality
protected bool Equals(OperationThreshold other)
{
return _threshold == other._threshold && _maximum == other._maximum && _type == other._type;
}
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((OperationThreshold) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = _threshold.GetHashCode();
hashCode = (hashCode * 397) ^ _maximum.GetHashCode();
hashCode = (hashCode * 397) ^ (int) _type;
return hashCode;
}
}
#endregion
} }
} }
+1 -1
View File
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl> <PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description> <Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description>
<Version>1.1.2</Version> <Version>1.1.3</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright> <Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon> <PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
+13 -11
View File
@@ -20,7 +20,9 @@ using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.ThemeManager; using Avalonia.ThemeManager;
using Emgu.CV; using Emgu.CV;
using UVtools.Core;
using UVtools.Core.FileFormats; using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
using UVtools.WPF.Extensions; using UVtools.WPF.Extensions;
using UVtools.WPF.Structures; using UVtools.WPF.Structures;
@@ -49,6 +51,8 @@ namespace UVtools.WPF
UserSettings.Load(); UserSettings.Load();
UserSettings.SetVersion(); UserSettings.SetVersion();
OperationProfiles.Load();
ThemeSelector = Avalonia.ThemeManager.ThemeSelector.Create(Path.Combine(ApplicationPath, "Assets", "Themes")); ThemeSelector = Avalonia.ThemeManager.ThemeSelector.Create(Path.Combine(ApplicationPath, "Assets", "Themes"));
ThemeSelector.LoadSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme")); ThemeSelector.LoadSelectedTheme(Path.Combine(UserSettings.SettingsFolder, "selected.theme"));
if (ThemeSelector.SelectedTheme.Name == "UVtoolsDark" || ThemeSelector.SelectedTheme.Name == "Light") if (ThemeSelector.SelectedTheme.Name == "UVtoolsDark" || ThemeSelector.SelectedTheme.Name == "Light")
@@ -87,25 +91,24 @@ namespace UVtools.WPF
} }
#region Utilities #region Utilities
public static string AppExecutable = Path.Combine(ApplicationPath, About.Software);
public static string AppExecutableQuoted = $"\"{AppExecutable}\"";
public static void NewInstance(string filePath) public static void NewInstance(string filePath)
{ {
try try
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
var info = new ProcessStartInfo("UVtools.exe", $"\"{filePath}\"") StartProcess($"{AppExecutable}.exe", $"\"{filePath}\"");
{
UseShellExecute = true
};
Process.Start(info).Dispose();
} }
else if(File.Exists("UVtools")) else if(File.Exists(AppExecutable)) // Direct execute
{ {
Process.Start("UVtools", $"\"{filePath}\"").Dispose(); StartProcess(AppExecutable, $"\"{filePath}\"");
} }
else else
{ {
Process.Start("dotnet", $"UVtools.dll \"{filePath}\"").Dispose(); StartProcess("dotnet", $"UVtools.dll \"{filePath}\"");
} }
} }
catch (Exception e) catch (Exception e)
@@ -142,11 +145,11 @@ namespace UVtools.WPF
} }
public static void StartProcess(string name) public static void StartProcess(string name, string arguments = null)
{ {
try try
{ {
using (Process.Start(new ProcessStartInfo(name) using (Process.Start(new ProcessStartInfo(name, arguments)
{ {
UseShellExecute = true UseShellExecute = true
})) { } })) { }
@@ -155,7 +158,6 @@ namespace UVtools.WPF
{ {
Debug.WriteLine(e); Debug.WriteLine(e);
} }
} }
public static Stream GetAsset(string url) public static Stream GetAsset(string url)
@@ -6,12 +6,12 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolArithmeticControl : ToolControl public class ToolArithmeticControl : ToolControl
{ {
public OperationArithmetic Operation { get; } public OperationArithmetic Operation => BaseOperation as OperationArithmetic;
public ToolArithmeticControl() public ToolArithmeticControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationArithmetic(); BaseOperation = new OperationArithmetic();
Operation.PropertyChanged += (sender, e) => Operation.PropertyChanged += (sender, e) =>
{ {
if (e.PropertyName == nameof(Operation.Sentence)) if (e.PropertyName == nameof(Operation.Sentence))
@@ -10,7 +10,7 @@ namespace UVtools.WPF.Controls.Tools
private bool _isSizeEnabled = true; private bool _isSizeEnabled = true;
private bool _isKernelVisible; private bool _isKernelVisible;
private KernelControl _kernelCtrl; private KernelControl _kernelCtrl;
public OperationBlur Operation { get; } public OperationBlur Operation => BaseOperation as OperationBlur;
public int SelectedAlgorithmIndex public int SelectedAlgorithmIndex
{ {
@@ -40,7 +40,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolBlurControl() public ToolBlurControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationBlur(); BaseOperation = new OperationBlur();
_kernelCtrl = this.Find<KernelControl>("KernelCtrl"); _kernelCtrl = this.Find<KernelControl>("KernelCtrl");
} }
@@ -6,14 +6,14 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolCalculatorControl : ToolControl public class ToolCalculatorControl : ToolControl
{ {
public OperationCalculator Operation { get; } public OperationCalculator Operation => BaseOperation as OperationCalculator;
public FileFormat SlicerFile => App.SlicerFile; public FileFormat SlicerFile => App.SlicerFile;
public ToolCalculatorControl() public ToolCalculatorControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationCalculator BaseOperation = new OperationCalculator
{ {
CalcMillimetersToPixels = new OperationCalculator.MillimetersToPixels(SlicerFile.Resolution, SlicerFile.Display), CalcMillimetersToPixels = new OperationCalculator.MillimetersToPixels(SlicerFile.Resolution, SlicerFile.Display),
CalcLightOffDelay = new OperationCalculator.LightOffDelayC( CalcLightOffDelay = new OperationCalculator.LightOffDelayC(
@@ -8,7 +8,7 @@ namespace UVtools.WPF.Controls.Tools
public class ToolChangeResolutionControl : ToolControl public class ToolChangeResolutionControl : ToolControl
{ {
private OperationChangeResolution.Resolution _selectedPresetItem; private OperationChangeResolution.Resolution _selectedPresetItem;
public OperationChangeResolution Operation { get; } public OperationChangeResolution Operation => BaseOperation as OperationChangeResolution;
public OperationChangeResolution.Resolution SelectedPresetItem public OperationChangeResolution.Resolution SelectedPresetItem
{ {
@@ -34,7 +34,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolChangeResolutionControl() public ToolChangeResolutionControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationChangeResolution(App.SlicerFile.Resolution, App.SlicerFile.LayerManager.BoundingRectangle); BaseOperation = new OperationChangeResolution(App.SlicerFile.Resolution, App.SlicerFile.LayerManager.BoundingRectangle);
} }
private void InitializeComponent() private void InitializeComponent()
@@ -9,7 +9,19 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolControl : UserControlEx public class ToolControl : UserControlEx
{ {
public Operation BaseOperation = null; private Operation _baseOperation;
public Operation BaseOperation
{
get => _baseOperation;
set
{
if(!RaiseAndSetIfChanged(ref _baseOperation, value)) return;
if (DataContext is null) return;
ResetDataContext();
}
}
public ToolWindow ParentWindow { get; set; } = null; public ToolWindow ParentWindow { get; set; } = null;
public bool CanRun { get; set; } = true; public bool CanRun { get; set; } = true;
@@ -33,6 +45,12 @@ namespace UVtools.WPF.Controls.Tools
public virtual bool UpdateOperation() => true; public virtual bool UpdateOperation() => true;
/*public virtual void SetOperation(Operation operation)
{
BaseOperation = operation;
ResetDataContext();
}*/
/// <summary> /// <summary>
/// Validates if is safe to continue with operation /// Validates if is safe to continue with operation
/// </summary> /// </summary>
@@ -16,7 +16,7 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolEditParametersControl : ToolControl public class ToolEditParametersControl : ToolControl
{ {
public OperationEditParameters Operation { get; } public OperationEditParameters Operation => BaseOperation as OperationEditParameters;
public bool SupportPerLayerSettings => App.SlicerFile.SupportPerLayerSettings; public bool SupportPerLayerSettings => App.SlicerFile.SupportPerLayerSettings;
public RowControl[] RowControls; public RowControl[] RowControls;
@@ -110,7 +110,7 @@ namespace UVtools.WPF.Controls.Tools
InitializeComponent(); InitializeComponent();
App.SlicerFile.RefreshPrintParametersModifiersValues(); App.SlicerFile.RefreshPrintParametersModifiersValues();
BaseOperation = Operation = new OperationEditParameters(App.SlicerFile.PrintParameterModifiers); BaseOperation = new OperationEditParameters(App.SlicerFile.PrintParameterModifiers);
if (Operation.Modifiers is null || Operation.Modifiers.Length == 0) if (Operation.Modifiers is null || Operation.Modifiers.Length == 0)
{ {
@@ -5,12 +5,12 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolFlipControl : ToolControl public class ToolFlipControl : ToolControl
{ {
public OperationFlip Operation { get; } public OperationFlip Operation => BaseOperation as OperationFlip;
public ToolFlipControl() public ToolFlipControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationFlip(); BaseOperation = new OperationFlip();
} }
private void InitializeComponent() private void InitializeComponent()
@@ -6,7 +6,7 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolLayerCloneControl : ToolControl public class ToolLayerCloneControl : ToolControl
{ {
public OperationLayerClone Operation { get; } public OperationLayerClone Operation => BaseOperation as OperationLayerClone;
public uint ExtraLayers => (uint)Math.Max(0, ((int)Operation.LayerIndexEnd - Operation.LayerIndexStart + 1) * Operation.Clones); public uint ExtraLayers => (uint)Math.Max(0, ((int)Operation.LayerIndexEnd - Operation.LayerIndexStart + 1) * Operation.Clones);
@@ -31,7 +31,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolLayerCloneControl() public ToolLayerCloneControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationLayerClone(); BaseOperation = new OperationLayerClone();
Operation.PropertyChanged += (sender, args) => Operation.PropertyChanged += (sender, args) =>
{ {
RaisePropertyChanged(nameof(InfoLayersStr)); RaisePropertyChanged(nameof(InfoLayersStr));
@@ -2,7 +2,6 @@
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
@@ -12,7 +11,6 @@ using UVtools.Core.Objects;
using UVtools.Core.Operations; using UVtools.Core.Operations;
using UVtools.WPF.Extensions; using UVtools.WPF.Extensions;
using UVtools.WPF.Windows; using UVtools.WPF.Windows;
using Path = System.IO.Path;
namespace UVtools.WPF.Controls.Tools namespace UVtools.WPF.Controls.Tools
{ {
@@ -23,8 +21,8 @@ namespace UVtools.WPF.Controls.Tools
private Bitmap _previewImage; private Bitmap _previewImage;
private ListBox FilesListBox; private ListBox FilesListBox;
public OperationLayerImport Operation { get; } public OperationLayerImport Operation => BaseOperation as OperationLayerImport;
public uint MaximumLayer => App.SlicerFile.LastLayerIndex; public uint MaximumLayer => App.SlicerFile.LastLayerIndex;
@@ -83,7 +81,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolLayerImportControl() public ToolLayerImportControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationLayerImport(App.SlicerFile.Resolution); BaseOperation = new OperationLayerImport(App.SlicerFile.Resolution);
Operation.Files.CollectionChanged += (sender, args) => RefreshGUI(); Operation.Files.CollectionChanged += (sender, args) => RefreshGUI();
Operation.PropertyChanged += (sender, args) => RefreshGUI(); Operation.PropertyChanged += (sender, args) => RefreshGUI();
FilesListBox = this.Find<ListBox>("FilesListBox"); FilesListBox = this.Find<ListBox>("FilesListBox");
@@ -6,7 +6,7 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolLayerReHeightControl : ToolControl public class ToolLayerReHeightControl : ToolControl
{ {
public OperationLayerReHeight Operation { get; } public OperationLayerReHeight Operation => BaseOperation as OperationLayerReHeight;
public OperationLayerReHeight.OperationLayerReHeightItem[] Presets { get; } = OperationLayerReHeight.GetItems( public OperationLayerReHeight.OperationLayerReHeightItem[] Presets { get; } = OperationLayerReHeight.GetItems(
App.SlicerFile.LayerCount, App.SlicerFile.LayerCount,
@@ -17,7 +17,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolLayerReHeightControl() public ToolLayerReHeightControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationLayerReHeight(); BaseOperation = new OperationLayerReHeight();
if (Presets.Length == 0) if (Presets.Length == 0)
{ {
@@ -6,7 +6,7 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolLayerRemoveControl : ToolControl public class ToolLayerRemoveControl : ToolControl
{ {
public OperationLayerRemove Operation { get; } public OperationLayerRemove Operation => BaseOperation as OperationLayerRemove;
public uint ExtraLayers => (uint)Math.Max(0, (int)Operation.LayerIndexEnd - Operation.LayerIndexStart + 1); public uint ExtraLayers => (uint)Math.Max(0, (int)Operation.LayerIndexEnd - Operation.LayerIndexStart + 1);
@@ -31,7 +31,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolLayerRemoveControl() public ToolLayerRemoveControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationLayerRemove(); BaseOperation = new OperationLayerRemove();
Operation.PropertyChanged += (sender, args) => Operation.PropertyChanged += (sender, args) =>
{ {
RaisePropertyChanged(nameof(InfoLayersStr)); RaisePropertyChanged(nameof(InfoLayersStr));
@@ -20,7 +20,7 @@ namespace UVtools.WPF.Controls.Tools
private byte _genMaximumBrightness = byte.MaxValue; private byte _genMaximumBrightness = byte.MaxValue;
private uint _genDiameter; private uint _genDiameter;
private Bitmap _maskImage; private Bitmap _maskImage;
public OperationMask Operation { get; } public OperationMask Operation => BaseOperation as OperationMask;
public bool IsMaskInverted public bool IsMaskInverted
{ {
@@ -69,7 +69,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolMaskControl() public ToolMaskControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationMask(); BaseOperation = new OperationMask();
} }
private void InitializeComponent() private void InitializeComponent()
@@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Emgu.CV.CvEnum; using Emgu.CV.CvEnum;
@@ -10,7 +11,7 @@ namespace UVtools.WPF.Controls.Tools
public class ToolMorphControl : ToolControl public class ToolMorphControl : ToolControl
{ {
private byte _morphSelectedIndex; private byte _morphSelectedIndex;
public OperationMorph Operation { get; } public OperationMorph Operation => BaseOperation as OperationMorph;
private KernelControl _kernelCtrl; private KernelControl _kernelCtrl;
@@ -27,7 +28,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolMorphControl() public ToolMorphControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationMorph(); BaseOperation = new OperationMorph();
_kernelCtrl = this.Find<KernelControl>("KernelCtrl"); _kernelCtrl = this.Find<KernelControl>("KernelCtrl");
} }
@@ -7,7 +7,7 @@ namespace UVtools.WPF.Controls.Tools
public class ToolMoveControl : ToolControl public class ToolMoveControl : ToolControl
{ {
private bool _isMiddleCenterChecked = true; private bool _isMiddleCenterChecked = true;
public OperationMove Operation { get; } public OperationMove Operation => BaseOperation as OperationMove;
public bool IsMiddleCenterChecked public bool IsMiddleCenterChecked
{ {
@@ -19,7 +19,7 @@ namespace UVtools.WPF.Controls.Tools
{ {
InitializeComponent(); InitializeComponent();
var roi = App.MainWindow.ROI; var roi = App.MainWindow.ROI;
BaseOperation = Operation = new OperationMove(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, (uint)App.MainWindow.LayerCache.Image.Width, BaseOperation = new OperationMove(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, (uint)App.MainWindow.LayerCache.Image.Width,
(uint)App.MainWindow.LayerCache.Image.Height); (uint)App.MainWindow.LayerCache.Image.Height);
Operation.PropertyChanged += (sender, e) => Operation.PropertyChanged += (sender, e) =>
@@ -8,7 +8,7 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolPatternControl : ToolControl public class ToolPatternControl : ToolControl
{ {
public OperationPattern Operation { get; } public OperationPattern Operation => BaseOperation as OperationPattern;
private bool _isDefaultAnchorChecked = true; private bool _isDefaultAnchorChecked = true;
public bool IsDefaultAnchorChecked public bool IsDefaultAnchorChecked
@@ -21,7 +21,7 @@ namespace UVtools.WPF.Controls.Tools
{ {
InitializeComponent(); InitializeComponent();
var roi = App.MainWindow.ROI; var roi = App.MainWindow.ROI;
BaseOperation = Operation = new OperationPattern(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, App.SlicerFile.Resolution); BaseOperation = new OperationPattern(roi.IsEmpty ? App.SlicerFile.LayerManager.BoundingRectangle : roi, App.SlicerFile.Resolution);
if (Operation.MaxRows < 2 && Operation.MaxCols < 2) if (Operation.MaxRows < 2 && Operation.MaxCols < 2)
{ {
@@ -28,7 +28,7 @@ namespace UVtools.WPF.Controls.Tools
private byte _dimGenBrightness = 127; private byte _dimGenBrightness = 127;
private ushort _infillGenThickness = 10; private ushort _infillGenThickness = 10;
private ushort _infillGenSpacing = 20; private ushort _infillGenSpacing = 20;
public OperationPixelDimming Operation { get; } public OperationPixelDimming Operation => BaseOperation as OperationPixelDimming;
public string PatternText public string PatternText
{ {
@@ -63,7 +63,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolPixelDimmingControl() public ToolPixelDimmingControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationPixelDimming(); BaseOperation = new OperationPixelDimming();
GeneratePixelDimming("Chessboard"); GeneratePixelDimming("Chessboard");
} }
@@ -6,12 +6,12 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolRepairLayersControl : ToolControl public class ToolRepairLayersControl : ToolControl
{ {
public OperationRepairLayers Operation { get; } public OperationRepairLayers Operation => BaseOperation as OperationRepairLayers;
public ToolRepairLayersControl() public ToolRepairLayersControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationRepairLayers BaseOperation = new OperationRepairLayers
{ {
RepairIslands = UserSettings.Instance.LayerRepair.RepairIslands, RepairIslands = UserSettings.Instance.LayerRepair.RepairIslands,
RepairResinTraps = UserSettings.Instance.LayerRepair.RepairResinTraps, RepairResinTraps = UserSettings.Instance.LayerRepair.RepairResinTraps,
@@ -1,17 +1,16 @@
using Avalonia.Controls; using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml;
using UVtools.Core.Operations; using UVtools.Core.Operations;
namespace UVtools.WPF.Controls.Tools namespace UVtools.WPF.Controls.Tools
{ {
public class ToolResizeControl : ToolControl public class ToolResizeControl : ToolControl
{ {
public OperationResize Operation { get; } public OperationResize Operation => BaseOperation as OperationResize;
public ToolResizeControl() public ToolResizeControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationResize(); BaseOperation = new OperationResize();
} }
private void InitializeComponent() private void InitializeComponent()
@@ -9,7 +9,7 @@
<NumericUpDown <NumericUpDown
MinWidth="100" MinWidth="100"
MaxWidth="150" MaxWidth="150"
Minimum="0.01" Minimum="-359.99"
Maximum="359.99" Maximum="359.99"
Increment="0.01" Increment="0.01"
FormatString="{}{0:#,0.00}" FormatString="{}{0:#,0.00}"
@@ -5,12 +5,12 @@ namespace UVtools.WPF.Controls.Tools
{ {
public class ToolRotateControl : ToolControl public class ToolRotateControl : ToolControl
{ {
public OperationRotate Operation { get; } public OperationRotate Operation => BaseOperation as OperationRotate;
public ToolRotateControl() public ToolRotateControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationRotate(); BaseOperation = new OperationRotate();
} }
private void InitializeComponent() private void InitializeComponent()
@@ -10,7 +10,7 @@ namespace UVtools.WPF.Controls.Tools
private bool _isThresholdEnabled = true; private bool _isThresholdEnabled = true;
private bool _isMaximumEnabled = true; private bool _isMaximumEnabled = true;
private bool _isTypeEnabled = true; private bool _isTypeEnabled = true;
public OperationThreshold Operation { get; } public OperationThreshold Operation => BaseOperation as OperationThreshold;
public string[] Presets => new[] public string[] Presets => new[]
{ {
@@ -74,7 +74,7 @@ namespace UVtools.WPF.Controls.Tools
public ToolThresholdControl() public ToolThresholdControl()
{ {
InitializeComponent(); InitializeComponent();
BaseOperation = Operation = new OperationThreshold(); BaseOperation = new OperationThreshold();
} }
private void InitializeComponent() private void InitializeComponent()
+43 -1
View File
@@ -688,7 +688,49 @@ namespace UVtools.WPF
await new PrusaSlicerManager().ShowDialog(this); await new PrusaSlicerManager().ShowDialog(this);
} }
public void MenuNewVersionClicked() => App.OpenBrowser(VersionChecker.Url); public async void MenuNewVersionClicked()
{
var result =
await this.MessageBoxQuestion(
$"Do you like to auto-update {About.Software} v{AppSettings.Version} to v{VersionChecker.Version}?\n\n" +
"Yes: Auto update\n" +
"No: Manual update\n" +
"Cancel: No action", "Update UVtools?", ButtonEnum.YesNoCancel);
if (result == ButtonResult.Yes)
{
IsGUIEnabled = false;
var task = await Task.Factory.StartNew(async () =>
{
ShowProgressWindow($"Downloading: {VersionChecker.Filename}");
try
{
VersionChecker.AutoUpgrade(ProgressWindow.RestartProgress(false));
return true;
}
catch (OperationCanceledException)
{
}
catch (Exception exception)
{
Dispatcher.UIThread.InvokeAsync(async () =>
await this.MessageBoxError(exception.ToString(), "Error opening the file"));
}
return false;
});
IsGUIEnabled = true;
return;
}
if (result == ButtonResult.No)
{
App.OpenBrowser(VersionChecker.UrlLatestRelease);
return;
}
}
#endregion #endregion
+20
View File
@@ -1,6 +1,8 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using Avalonia; using Avalonia;
using Avalonia.Threading;
using UVtools.WPF.Extensions;
namespace UVtools.WPF namespace UVtools.WPF
{ {
@@ -18,10 +20,28 @@ namespace UVtools.WPF
ProgramStartupTime = Stopwatch.StartNew(); ProgramStartupTime = Stopwatch.StartNew();
Args = args; Args = args;
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
BuildAvaloniaApp() BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args); .StartWithClassicDesktopLifetime(args);
} }
private static async void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
try
{
Exception ex = (Exception)e.ExceptionObject;
string errorMsg = "An application error occurred. Please contact the administrator with the following information:\n\n" +
$"{ex}";
await App.MainWindow.MessageBoxError(errorMsg, "Fatal Non-UI Error");
}
catch (Exception exc)
{
}
}
// Avalonia configuration, don't remove; also used by visual designer. // Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>() => AppBuilder.Configure<App>()
+183 -5
View File
@@ -6,19 +6,72 @@
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System; using System;
using System.Collections.Specialized;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net; using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using ABI.Windows.Data.Json;
using Avalonia.Threading; using Avalonia.Threading;
using Newtonsoft.Json;
using UVtools.Core; using UVtools.Core;
using UVtools.Core.Objects; using UVtools.Core.Objects;
using UVtools.Core.Operations;
using UVtools.WPF.Extensions;
namespace UVtools.WPF.Structures namespace UVtools.WPF.Structures
{ {
public class AppVersionChecker : BindableBase public class AppVersionChecker : BindableBase
{ {
public const string GitHubReleaseApi = "https://api.github.com/repos/sn4k3/UVtools/releases/latest";
private string _version; private string _version;
private string _url; private string _url;
public string Filename
{
get
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return $"{About.Software}_win-x64_v{_version}.msi";
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return $"{About.Software}_linux-x64_v{_version}.zip";
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return $"{About.Software}_osx-x64_v{_version}.zip";
}
return $"{About.Software}_universal-x86-x64_v{_version}.zip";
}
}
public string Runtime
{
get
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "win-x64";
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return "linux-x64";
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return "osx-x64";
}
return "universal-x86-x64";
}
}
public string Version public string Version
{ {
get => _version; get => _version;
@@ -30,19 +83,46 @@ namespace UVtools.WPF.Structures
} }
} }
public string VersionAnnouncementText => $"New version {_version} is available!"; public string VersionAnnouncementText => $"New version v{_version} is available!";
public string Url => $"{About.Website}/releases/tag/{_version}"; public string UrlLatestRelease => $"{About.Website}/releases/latest";
public string DownloadLink => string.IsNullOrEmpty(Filename) ? null : $"{About.Website}/releases/download/v{_version}/{Filename}";
public bool HaveNewVersion => !string.IsNullOrEmpty(Version); public bool HaveNewVersion => !string.IsNullOrEmpty(Version);
public string DownloadedFile { get; private set; }
public bool Check() public bool Check()
{ {
try try
{ {
using (WebClient client = new WebClient()) using (WebClient client = new WebClient
{ {
string htmlCode = client.DownloadString($"{About.Website}/releases"); Headers = new WebHeaderCollection
{
{HttpRequestHeader.Accept, "application/json"},
{HttpRequestHeader.UserAgent, "Request"}
}
})
{
var response = client.DownloadString(GitHubReleaseApi);
dynamic json = JsonConvert.DeserializeObject(response);
string tag_name = json.tag_name;
if (string.IsNullOrEmpty(tag_name)) return false;
tag_name = tag_name.Trim(' ', 'v', 'V');
Debug.WriteLine($"Version checker: v{AppSettings.VersionStr} <=> v{tag_name}");
if (string.Compare(tag_name, AppSettings.VersionStr, StringComparison.OrdinalIgnoreCase) > 0)
{
Dispatcher.UIThread.InvokeAsync(() =>
{
Version = tag_name;
Debug.WriteLine($"New version detected: {DownloadLink}");
});
return true;
}
/*string htmlCode = client.DownloadString($"{About.Website}/releases");
const string searchFor = "/releases/tag/"; const string searchFor = "/releases/tag/";
var startIndex = htmlCode.IndexOf(searchFor, StringComparison.InvariantCultureIgnoreCase) + var startIndex = htmlCode.IndexOf(searchFor, StringComparison.InvariantCultureIgnoreCase) +
searchFor.Length; searchFor.Length;
@@ -55,7 +135,7 @@ namespace UVtools.WPF.Structures
Version = version;; Version = version;;
}); });
return true; return true;
} }*/
} }
} }
catch (Exception e) catch (Exception e)
@@ -63,6 +143,104 @@ namespace UVtools.WPF.Structures
Debug.WriteLine(e.ToString()); Debug.WriteLine(e.ToString());
} }
return false;
}
public bool AutoUpgrade(OperationProgress progress)
{
if (!HaveNewVersion) return false;
progress.ItemName = "Megabytes";
try
{
using (var client = new WebClient())
{
var path = Path.GetTempPath();
DownloadedFile = Path.Combine(path, Filename);
Debug.WriteLine($"Downloading to: {DownloadedFile}");
client.DownloadProgressChanged += (sender, e) =>
{
progress.Reset("Megabytes", (uint)e.TotalBytesToReceive / 1048576, (uint)e.BytesReceived / 1048576);
};
client.DownloadFileCompleted += (sender, e) =>
{
progress.Reset("Extracting");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
App.StartProcess(DownloadedFile);
Environment.Exit(0);
}
else
{
string upgradeFolder = "UPDATED_VERSION";
var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder);
using (var stream = File.Open(DownloadedFile, FileMode.Open))
{
using (ZipArchive zip = new ZipArchive(stream, ZipArchiveMode.Read))
{
zip.ExtractToDirectory(targetDir, true);
}
}
File.Delete(DownloadedFile);
string upgradeFileName = $"{About.Software}_upgrade.sh";
var upgradeFile = Path.Combine(App.ApplicationPath, upgradeFileName);
using (var stream = File.CreateText(upgradeFile))
{
stream.WriteLine("#!/bin/bash");
stream.WriteLine($"echo {About.Software} v{AppSettings.Version} updater script");
stream.WriteLine($"cd '{App.ApplicationPath}'");
stream.WriteLine($"killall {About.Software}");
stream.WriteLine("sleep 0.5");
stream.WriteLine($"cp -fR {upgradeFolder}/* .");
stream.WriteLine($"rm -fr {upgradeFolder}");
stream.WriteLine("sleep 0.5");
//stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &");
stream.WriteLine($"if [ -f '{About.Software}' ]; then");
stream.WriteLine($" ./{About.Software} &");
stream.WriteLine("else");
stream.WriteLine($" dotnet {About.Software}.dll &");
stream.WriteLine("fi");
stream.WriteLine($"rm -f {upgradeFileName}");
//stream.WriteLine("exit");
stream.Close();
}
App.StartProcess("bash", $"\"{upgradeFile}\"");
//App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath);
Environment.Exit(0);
}
lock (e.UserState)
{
//releases blocked thread
Monitor.Pulse(e.UserState);
}
};
var syncObject = new object();
lock (syncObject)
{
client.DownloadFileAsync(new Uri(DownloadLink), DownloadedFile, syncObject);
//This would block the thread until download completes
Monitor.Wait(syncObject);
}
}
}
catch (Exception e)
{
Dispatcher.UIThread.InvokeAsync(async () =>
await App.MainWindow.MessageBoxError(e.ToString(), "Error downloading the file"));
return false;
}
return false; return false;
} }
} }
+270
View File
@@ -0,0 +1,270 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using UVtools.Core.Operations;
namespace UVtools.WPF.Structures
{
[Serializable]
public class OperationProfiles //: IList<Operation>
{
#region Properties
/// <summary>
/// Default filepath for store <see cref="UserSettings"/>
/// </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(OperationFlip))]
//[XmlElement(typeof(OperationLayerClone))]
//[XmlElement(typeof(OperationLayerImport))]
//[XmlElement(typeof(OperationLayerReHeight))]
//[XmlElement(typeof(OperationLayerRemove))]
//[XmlElement(typeof(OperationMask))]
[XmlElement(typeof(OperationMorph))]
//[XmlElement(typeof(OperationMove))]
//[XmlElement(typeof(OperationPattern))]
[XmlElement(typeof(OperationPixelDimming))]
//[XmlElement(typeof(OperationRepairLayers))]
[XmlElement(typeof(OperationResize))]
[XmlElement(typeof(OperationRotate))]
[XmlElement(typeof(OperationThreshold))]
public List<Operation> Operations { get; internal set; } = new List<Operation>();
[XmlIgnore]
public static List<Operation> Profiles
{
get => Instance.Operations;
internal set => Instance.Operations = value;
}
#endregion
#region Singleton
private static OperationProfiles _instance;
/// <summary>
/// Instance of <see cref="UserSettings"/> (singleton)
/// </summary>
public static OperationProfiles Instance
{
get => _instance ??= new OperationProfiles();
internal set => _instance = value;
}
//public static List<Operation> Operations => _instance.Operations;
#endregion
#region Constructor
private OperationProfiles()
{ }
#endregion
#region Indexers
public Operation this[uint index]
{
get => Operations[(int) index];
set => Operations[(int) index] = value;
}
public Operation this[int index]
{
get => Operations[index];
set => Operations[index] = value;
}
#endregion
#region Enumerable
public IEnumerator<Operation> GetEnumerator()
{
return Operations.GetEnumerator();
}
/*IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}*/
#endregion
#region List Implementation
public void Add(Operation item)
{
Operations.Add(item);
}
public int IndexOf(Operation item)
{
return Operations.IndexOf(item);
}
public void Insert(int index, Operation item)
{
Operations.Insert(index, item);
}
public bool Remove(Operation item)
{
return Operations.Remove(item);
}
public void RemoveAt(int index)
{
Operations.RemoveAt(index);
}
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 int Count => Operations.Count;
public bool IsReadOnly => false;
#endregion
#region Static Methods
/// <summary>
/// Clear all profiles
/// </summary>
/// <param name="save">True to save settings on file, otherwise false</param>
public static void ClearProfiles(bool save = true)
{
Instance.Clear();
if (save) Save();
}
/// <summary>
/// Clear all profiles given a type
/// </summary>
/// <param name="type">Type profiles to clear</param>
/// <param name="save">True to save settings on file, otherwise false</param>
public static void ClearProfiles(Type type, bool save = true)
{
Profiles.RemoveAll(operation => operation.GetType() == type);
if (save) Save();
}
/// <summary>
/// Load settings from file
/// </summary>
public static void Load()
{
if (!File.Exists(FilePath))
{
return;
}
var serializer = new XmlSerializer(Instance.GetType());
try
{
using var myXmlReader = new StreamReader(FilePath);
_instance = (OperationProfiles)serializer.Deserialize(myXmlReader);
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
ClearProfiles();
}
}
/// <summary>
/// Save settings to file
/// </summary>
public static void Save()
{
var serializer = new XmlSerializer(Instance.GetType());
try
{
using var myXmlWriter = new StreamWriter(FilePath);
serializer.Serialize(myXmlWriter, Instance);
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
public static Operation FindByName(Operation baseOperation, string profileName)
{
return Profiles.Find(operation =>
operation.GetType() == baseOperation.GetType() &&
(
!string.IsNullOrWhiteSpace(profileName) && operation.ProfileName == profileName
|| operation.Equals(baseOperation)
));
}
/// <summary>
/// Adds a profile
/// </summary>
/// <param name="operation"></param>
/// <param name="save"></param>
public static void AddProfile(Operation operation, bool save = true)
{
Profiles.Insert(0, operation);
if(save) Save();
}
/// <summary>
/// Removes a profile
/// </summary>
/// <param name="operation"></param>
/// <param name="save"></param>
public static void RemoveProfile(Operation operation, bool save = true)
{
Instance.Remove(operation);
if (save) Save();
}
/// <summary>
/// Get all profiles within a type
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static List<Operation> GetOperations(Type type)
=> Profiles.Where(operation => operation.GetType() == type).ToList();
/// <summary>
/// Get all profiles within a type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static List<T> GetOperations<T>()
{
var result = new List<T>();
foreach (var operation in _instance)
{
if (operation is T operationCast)
{
result.Add(operationCast);
}
}
return result;
}
#endregion
}
}
+1 -2
View File
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<Version>1.1.2</Version> <Version>1.1.3</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -29,7 +29,6 @@
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.0-preview6" /> <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.0-preview6" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.0-preview6" /> <PackageReference Include="Avalonia.Desktop" Version="0.10.0-preview6" />
<PackageReference Include="Avalonia.ThemeManager" Version="0.10.0-preview6" /> <PackageReference Include="Avalonia.ThemeManager" Version="0.10.0-preview6" />
<PackageReference Include="Emgu.CV.runtime.raspbian" Version="4.4.0.4099" />
<PackageReference Include="Emgu.CV.runtime.ubuntu" Version="4.4.0.4099" /> <PackageReference Include="Emgu.CV.runtime.ubuntu" Version="4.4.0.4099" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.4.0.4099" /> <PackageReference Include="Emgu.CV.runtime.windows" Version="4.4.0.4099" />
<PackageReference Include="MessageBox.Avalonia" Version="0.10.0-prev2" /> <PackageReference Include="MessageBox.Avalonia" Version="0.10.0-prev2" />
+10 -2
View File
@@ -9,7 +9,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Xml.Serialization; using System.Xml.Serialization;
using Avalonia.Media; using Avalonia.Media;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -951,7 +950,16 @@ namespace UVtools.WPF
get get
{ {
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), About.Software); var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), About.Software);
Directory.CreateDirectory(path); try
{
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return path; return path;
} }
} }
+100
View File
@@ -202,6 +202,106 @@
</StackPanel> </StackPanel>
</Border> </Border>
<!-- Profiles -->
<Border
Background="WhiteSmoke"
Margin="5"
BorderBrush="LightBlue"
BorderThickness="4"
IsVisible="{Binding IsProfilesVisible}"
>
<StackPanel Orientation="Vertical">
<Grid
ColumnDefinitions="Auto,*" Background="LightBlue">
<TextBlock
Padding="10" FontWeight="Bold"
VerticalAlignment="Center"
Text="{Binding Profiles.Count, StringFormat=Profiles: \{0\}}" />
<StackPanel Orientation="Horizontal"
Grid.Column="1"
HorizontalAlignment="Right"
Spacing="2">
<Button
VerticalAlignment="Center"
ToolTip.Tip="Clear all profiles"
IsEnabled="{Binding Profiles.Count}"
Command="{Binding ClearProfiles}"
>
<Image Width="16" Height="16" Source="/Assets/Icons/delete-16x16.png" />
</Button>
</StackPanel>
</Grid>
<Grid
RowDefinitions="Auto,Auto"
ColumnDefinitions="*,5,Auto"
Margin="15"
>
<ComboBox
Margin="0,0,0,10"
IsEnabled="{Binding Profiles.Count}"
IsVisible="{Binding Profiles.Count}"
SelectedItem="{Binding SelectedProfileItem}"
Items="{Binding Profiles}" />
<Button
Grid.Row="0"
Grid.Column="2"
Margin="0,0,0,10"
VerticalAlignment="Center"
ToolTip.Tip="Remove the selected profile"
IsEnabled="{Binding SelectedProfileItem, Converter={x:Static ObjectConverters.IsNotNull}}"
IsVisible="{Binding Profiles.Count}"
Command="{Binding RemoveSelectedProfile}"
>
<Image Source="/Assets/Icons/trash-16x16.png" />
</Button>
<TextBox
Grid.Row="1"
Grid.Column="0"
IsEnabled="{Binding ButtonOkEnabled}"
Text="{Binding ProfileText}"
Watermark="Profile name or leave empty for auto name"
/>
<Button
Grid.Row="1"
Grid.Column="2"
VerticalAlignment="Center"
ToolTip.Tip="Add a new profile with the current set values"
IsEnabled="{Binding ButtonOkEnabled}"
Command="{Binding AddProfile}"
>
<Image Source="/Assets/Icons/plus-16x16.png" />
</Button>
</Grid>
<!--
<StackPanel Spacing="20" Margin="15,0,15,15" Orientation="Horizontal">
<CheckBox
Content="Clear ROI after perform the operation"
IsChecked="{Binding ClearROIAfterOperation}"
/>
<Button
Padding="5"
Content="Clear ROI"
Command="{Binding ClearROI}"/>
</StackPanel>
!-->
</StackPanel>
</Border>
<!-- Content --> <!-- Content -->
<Border <Border
+164 -27
View File
@@ -1,14 +1,20 @@
using System; using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Threading; using Avalonia.Threading;
using DynamicData;
using MessageBox.Avalonia.Enums; using MessageBox.Avalonia.Enums;
using UVtools.Core; using UVtools.Core;
using UVtools.Core.Extensions;
using UVtools.Core.Operations;
using UVtools.WPF.Controls; using UVtools.WPF.Controls;
using UVtools.WPF.Controls.Tools; using UVtools.WPF.Controls.Tools;
using UVtools.WPF.Extensions; using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
namespace UVtools.WPF.Windows namespace UVtools.WPF.Windows
{ {
@@ -29,8 +35,14 @@ namespace UVtools.WPF.Windows
private uint _layerIndexStart; private uint _layerIndexStart;
private uint _layerIndexEnd; private uint _layerIndexEnd;
private bool _isROIVisible; private bool _isROIVisible;
private bool _clearRoiAfterOperation; private bool _clearRoiAfterOperation;
private bool _isProfilesVisible;
private ObservableCollection<Operation> _profiles = new ObservableCollection<Operation>();
private Operation _selectedProfileItem;
private string _profileText;
private IControl _contentControl; private IControl _contentControl;
private bool _isButton1Visible; private bool _isButton1Visible;
private string _button1Text = "Reset to defaults"; private string _button1Text = "Reset to defaults";
@@ -42,6 +54,8 @@ namespace UVtools.WPF.Windows
private string _buttonOkText = "Ok"; private string _buttonOkText = "Ok";
private bool _buttonOkVisible = true; private bool _buttonOkVisible = true;
private double _scrollViewerMaxHeight=double.PositiveInfinity; private double _scrollViewerMaxHeight=double.PositiveInfinity;
public double ScrollViewerMaxHeight public double ScrollViewerMaxHeight
{ {
@@ -92,8 +106,12 @@ namespace UVtools.WPF.Windows
set set
{ {
if (!(ToolControl?.BaseOperation is null)) if (!(ToolControl?.BaseOperation is null))
{
ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
ToolControl.BaseOperation.LayerIndexStart = value; ToolControl.BaseOperation.LayerIndexStart = value;
}
value = value.Clamp(0, App.SlicerFile.LastLayerIndex);
if (!RaiseAndSetIfChanged(ref _layerIndexStart, value)) return; if (!RaiseAndSetIfChanged(ref _layerIndexStart, value)) return;
RaisePropertyChanged(nameof(LayerStartMM)); RaisePropertyChanged(nameof(LayerStartMM));
RaisePropertyChanged(nameof(LayerRangeCountStr)); RaisePropertyChanged(nameof(LayerRangeCountStr));
@@ -115,8 +133,12 @@ namespace UVtools.WPF.Windows
set set
{ {
if (!(ToolControl?.BaseOperation is null)) if (!(ToolControl?.BaseOperation is null))
{
ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.None;
ToolControl.BaseOperation.LayerIndexEnd = value; ToolControl.BaseOperation.LayerIndexEnd = value;
}
value = value.Clamp(0, App.SlicerFile.LastLayerIndex);
if (!RaiseAndSetIfChanged(ref _layerIndexEnd, value)) return; if (!RaiseAndSetIfChanged(ref _layerIndexEnd, value)) return;
RaisePropertyChanged(nameof(LayerEndMM)); RaisePropertyChanged(nameof(LayerEndMM));
RaisePropertyChanged(nameof(LayerRangeCountStr)); RaisePropertyChanged(nameof(LayerRangeCountStr));
@@ -141,33 +163,74 @@ namespace UVtools.WPF.Windows
{ {
LayerIndexStart = 0; LayerIndexStart = 0;
LayerIndexEnd = MaximumLayerIndex; LayerIndexEnd = MaximumLayerIndex;
if(!(ToolControl is null))
ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.All;
} }
public void SelectCurrentLayer() public void SelectCurrentLayer()
{ {
LayerIndexStart = LayerIndexEnd = App.MainWindow.ActualLayer; LayerIndexStart = LayerIndexEnd = App.MainWindow.ActualLayer;
if (!(ToolControl is null))
ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Current;
} }
public void SelectBottomLayers() public void SelectBottomLayers()
{ {
LayerIndexStart = 0; LayerIndexStart = 0;
LayerIndexEnd = App.SlicerFile.BottomLayerCount-1u; LayerIndexEnd = App.SlicerFile.BottomLayerCount-1u;
if (!(ToolControl is null))
ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Bottom;
} }
public void SelectNormalLayers() public void SelectNormalLayers()
{ {
LayerIndexStart = App.SlicerFile.BottomLayerCount; LayerIndexStart = App.SlicerFile.BottomLayerCount;
LayerIndexEnd = MaximumLayerIndex; LayerIndexEnd = MaximumLayerIndex;
if (!(ToolControl is null))
ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Normal;
} }
public void SelectFirstLayer() public void SelectFirstLayer()
{ {
LayerIndexStart = LayerIndexEnd = 0; LayerIndexStart = LayerIndexEnd = 0;
if (!(ToolControl is null))
ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.First;
} }
public void SelectLastLayer() public void SelectLastLayer()
{ {
LayerIndexStart = LayerIndexEnd = MaximumLayerIndex; LayerIndexStart = LayerIndexEnd = MaximumLayerIndex;
if (!(ToolControl is null))
ToolControl.BaseOperation.LayerRangeSelection = Enumerations.LayerRangeSelection.Last;
}
public void SelectLayers(Enumerations.LayerRangeSelection range)
{
switch (range)
{
case Enumerations.LayerRangeSelection.None:
break;
case Enumerations.LayerRangeSelection.All:
SelectAllLayers();
break;
case Enumerations.LayerRangeSelection.Current:
SelectCurrentLayer();
break;
case Enumerations.LayerRangeSelection.Bottom:
SelectBottomLayers();
break;
case Enumerations.LayerRangeSelection.Normal:
SelectNormalLayers();
break;
case Enumerations.LayerRangeSelection.First:
SelectFirstLayer();
break;
case Enumerations.LayerRangeSelection.Last:
SelectLastLayer();
break;
default:
throw new ArgumentOutOfRangeException();
}
} }
#endregion #endregion
@@ -203,6 +266,98 @@ namespace UVtools.WPF.Windows
#endregion #endregion
#region Profiles
public bool IsProfilesVisible
{
get => _isProfilesVisible;
set => RaiseAndSetIfChanged(ref _isProfilesVisible, value);
}
public ObservableCollection<Operation> Profiles
{
get => _profiles;
set => RaiseAndSetIfChanged(ref _profiles, value);
}
public Operation SelectedProfileItem
{
get => _selectedProfileItem;
set
{
if(!RaiseAndSetIfChanged(ref _selectedProfileItem, value) || value is null) return;
var operation = _selectedProfileItem.Clone();
operation.ProfileName = null;
ToolControl.BaseOperation = operation;
SelectLayers(operation.LayerRangeSelection);
if (operation is OperationMorph operationMorph && ToolControl is ToolMorphControl toolMorphControl)
{
toolMorphControl.MorphSelectedIndex = operationMorph.MorphOperationIndex;
}
else if (operation is OperationBlur operationBlur && ToolControl is ToolBlurControl toolBlurControl)
{
toolBlurControl.SelectedAlgorithmIndex = operationBlur.BlurTypeIndex;
}
}
}
public string ProfileText
{
get => _profileText;
set => RaiseAndSetIfChanged(ref _profileText, value);
}
public async void AddProfile()
{
var name = string.IsNullOrWhiteSpace(_profileText) ? null : _profileText.Trim();
var operation = OperationProfiles.FindByName(ToolControl.BaseOperation, name);
if (!(operation is null))
{
if (await this.MessageBoxQuestion(
$"A profile with same name or settings already exists.\nDo you want to overwrite:\n{operation}",
"Overwrite profile?") != ButtonResult.Yes) return;
/*var index = OperationProfiles.Instance.IndexOf(operation);
OperationProfiles.Profiles[index] = ToolControl.BaseOperation;
index = Profiles.IndexOf(operation);
Profiles[index] = ToolControl.BaseOperation;*/
OperationProfiles.RemoveProfile(operation, false);
Profiles.Remove(operation);
}
var toAdd = ToolControl.BaseOperation.Clone();
toAdd.ProfileName = string.IsNullOrWhiteSpace(_profileText) ? null : _profileText.Trim();
OperationProfiles.AddProfile(toAdd);
Profiles.Insert(0, toAdd);
ProfileText = null;
}
public async void RemoveSelectedProfile()
{
if (_selectedProfileItem is null) return;
if (await this.MessageBoxQuestion(
$"Are you sure you want to remove the selected profile?\n{_selectedProfileItem}",
"Remove selected profile?") != ButtonResult.Yes) return;
OperationProfiles.RemoveProfile(_selectedProfileItem);
Profiles.Remove(_selectedProfileItem);
SelectedProfileItem = null;
}
public async void ClearProfiles()
{
if (Profiles.Count == 0) return;
if (await this.MessageBoxQuestion(
$"Are you sure you want to clear all the {Profiles.Count} profiles?",
"Clear all profiles?") != ButtonResult.Yes) return;
OperationProfiles.ClearProfiles(Profiles[0].GetType());
Profiles.Clear();
}
#endregion
#region Content #region Content
public bool IsContentVisible => ContentControl is null || ContentControl.IsVisible; public bool IsContentVisible => ContentControl is null || ContentControl.IsVisible;
@@ -300,41 +455,23 @@ namespace UVtools.WPF.Windows
toolControl.Margin = new Thickness(15); toolControl.Margin = new Thickness(15);
Title = toolControl.BaseOperation.Title; Title = toolControl.BaseOperation.Title;
LayerRangeVisible = toolControl.BaseOperation.LayerRangeSelection != Enumerations.LayerRangeSelection.None; LayerRangeVisible = toolControl.BaseOperation.StartLayerRangeSelection != Enumerations.LayerRangeSelection.None;
//IsROIVisible = toolControl.BaseOperation.CanROI; //IsROIVisible = toolControl.BaseOperation.CanROI;
ContentControl = toolControl; ContentControl = toolControl;
ButtonOkText = toolControl.BaseOperation.ButtonOkText; ButtonOkText = toolControl.BaseOperation.ButtonOkText;
ButtonOkVisible = ButtonOkEnabled = toolControl.BaseOperation.HaveAction; ButtonOkVisible = ButtonOkEnabled = toolControl.BaseOperation.HaveAction;
switch (toolControl.BaseOperation.LayerRangeSelection) SelectLayers(toolControl.BaseOperation.StartLayerRangeSelection);
{
case Enumerations.LayerRangeSelection.None:
break;
case Enumerations.LayerRangeSelection.All:
SelectAllLayers();
break;
case Enumerations.LayerRangeSelection.Current:
SelectCurrentLayer();
break;
case Enumerations.LayerRangeSelection.Bottom:
SelectBottomLayers();
break;
case Enumerations.LayerRangeSelection.Normal:
SelectNormalLayers();
break;
case Enumerations.LayerRangeSelection.First:
SelectFirstLayer();
break;
case Enumerations.LayerRangeSelection.Last:
SelectLastLayer();
break;
default:
throw new ArgumentOutOfRangeException();
}
//RaisePropertyChanged(nameof(IsContentVisible)); //RaisePropertyChanged(nameof(IsContentVisible));
//RaisePropertyChanged(nameof(IsROIVisible)); //RaisePropertyChanged(nameof(IsROIVisible));
if (ToolControl.BaseOperation.CanHaveProfiles)
{
var profiles = OperationProfiles.GetOperations(ToolControl.BaseOperation.GetType());
Profiles.AddRange(profiles);
IsProfilesVisible = true;
}
// Ensure the description don't stretch window // Ensure the description don't stretch window
DispatcherTimer.Run(() => DispatcherTimer.Run(() =>