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

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2022-03-21 06:43:24 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2022-03-21 06:43:24 +0300
commita5bef99372d4e130491e39bd5b0eaa43ac76072a (patch)
tree06a68a41b6334bbfd63affc40a79d414a976d6f4 /UVtools.Core
parenta3f907fda84683a097dd7a456f8cabf9193f9d1d (diff)
v3.1.1
- (Add) Raft relief: Tabs type - Creates tabs around the raft to easily insert a tool under it and detach the raft from build plate - (Add) Linux AppImage binaries (You won't get them with auto-update, please download AppImage once before can use auto-update feature in the future) - (Change) Rename "layer compression method" to "layer compression codec", please redefine the codec setting if you changed before - (Improvement) Linux and macOS releases are now compiled, published and packed under Linux (WSL). Windows release still and must be published under windows.
Diffstat (limited to 'UVtools.Core')
-rw-r--r--UVtools.Core/CoreSettings.cs2
-rw-r--r--UVtools.Core/Extensions/ZipArchiveExtensions.cs2
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs8
-rw-r--r--UVtools.Core/Layers/Layer.cs64
-rw-r--r--UVtools.Core/Operations/OperationRaftRelief.cs183
-rw-r--r--UVtools.Core/SystemOS/SystemAware.cs21
-rw-r--r--UVtools.Core/UVtools.Core.csproj2
7 files changed, 234 insertions, 48 deletions
diff --git a/UVtools.Core/CoreSettings.cs b/UVtools.Core/CoreSettings.cs
index d6ef78a..f048af2 100644
--- a/UVtools.Core/CoreSettings.cs
+++ b/UVtools.Core/CoreSettings.cs
@@ -71,7 +71,7 @@ public static class CoreSettings
/// <summary>
/// Gets or sets the default compression type for layers
/// </summary>
- public static Layer.LayerCompressionMethod DefaultLayerCompressionMethod { get; set; } = Layer.LayerCompressionMethod.Png;
+ public static Layer.LayerCompressionCodec DefaultLayerCompressionCodec { get; set; } = Layer.LayerCompressionCodec.Png;
/// <summary>
/// Gets the default folder to save the settings
diff --git a/UVtools.Core/Extensions/ZipArchiveExtensions.cs b/UVtools.Core/Extensions/ZipArchiveExtensions.cs
index c021783..0d8154d 100644
--- a/UVtools.Core/Extensions/ZipArchiveExtensions.cs
+++ b/UVtools.Core/Extensions/ZipArchiveExtensions.cs
@@ -103,7 +103,7 @@ public static class ZipArchiveExtensions
{
//Gets the complete path for the destination file, including any
//relative paths that were in the zip file
- var destFileName = Path.Combine(destinationPath, file.FullName);
+ var destFileName = Path.GetFullPath(Path.Combine(destinationPath, file.FullName));
var fullDestDirPath = Path.GetFullPath(destinationPath + Path.DirectorySeparatorChar);
if (!destFileName.StartsWith(fullDestDirPath)) return; // Entry is outside the target dir
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 2e28b44..442b111 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -4490,16 +4490,16 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor
/// <summary>
/// Changes the compression method of all layers to a new method
/// </summary>
- /// <param name="to">The new method to change to</param>
+ /// <param name="newCodec">The new method to change to</param>
/// <param name="progress"></param>
- public void ChangeLayersCompressionMethod(Layer.LayerCompressionMethod to, OperationProgress? progress = null)
+ public void ChangeLayersCompressionMethod(Layer.LayerCompressionCodec newCodec, OperationProgress? progress = null)
{
- progress ??= new OperationProgress($"Changing layers compress method to {to}");
+ progress ??= new OperationProgress($"Changing layers compression codec to {newCodec}");
progress.Reset("Layers", LayerCount);
Parallel.ForEach(this, CoreSettings.GetParallelOptions(progress), layer =>
{
- layer.CompressionMethod = to;
+ layer.CompressionCodec = newCodec;
progress.LockAndIncrement();
});
}
diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs
index 5bd6c70..f5b49aa 100644
--- a/UVtools.Core/Layers/Layer.cs
+++ b/UVtools.Core/Layers/Layer.cs
@@ -30,7 +30,7 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
{
#region Enums
- public enum LayerCompressionMethod : byte
+ public enum LayerCompressionCodec : byte
{
[Description("PNG: Compression=High Speed=Slow (Use with low RAM)")]
Png,
@@ -56,7 +56,7 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
#region Members
public object Mutex = new();
- private LayerCompressionMethod _compressionMethod;
+ private LayerCompressionCodec _compressionCodec;
private byte[]? _compressedBytes;
private uint _nonZeroPixelCount;
private Rectangle _boundingRectangle = Rectangle.Empty;
@@ -593,21 +593,21 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
/// <summary>
/// Gets or sets the compression method used to cache the image
/// </summary>
- public LayerCompressionMethod CompressionMethod
+ public LayerCompressionCodec CompressionCodec
{
- get => _compressionMethod;
+ get => _compressionCodec;
set
{
if (!HaveImage)
{
- RaiseAndSetIfChanged(ref _compressionMethod, value);
+ RaiseAndSetIfChanged(ref _compressionCodec, value);
return;
}
// Handle conversion
- if (_compressionMethod == value) return;
+ if (_compressionCodec == value) return;
using var mat = LayerMat;
- _compressionMethod = value;
+ _compressionCodec = value;
_compressedBytes = CompressMat(mat, value);
RaisePropertyChanged();
}
@@ -634,7 +634,7 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
get
{
if (_compressedBytes is null) return null;
- if (_compressionMethod == LayerCompressionMethod.Png) return _compressedBytes;
+ if (_compressionCodec == LayerCompressionCodec.Png) return _compressedBytes;
using var mat = LayerMat;
return mat.GetPngByes();
@@ -656,17 +656,17 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
if (!HaveImage) return null!;
Mat mat;
- switch (_compressionMethod)
+ switch (_compressionCodec)
{
- case LayerCompressionMethod.Png:
+ case LayerCompressionCodec.Png:
mat = new Mat();
CvInvoke.Imdecode(_compressedBytes, ImreadModes.Grayscale, mat);
break;
- case LayerCompressionMethod.Lz4:
+ case LayerCompressionCodec.Lz4:
mat = new Mat(SlicerFile.Resolution, DepthType.Cv8U, 1);
LZ4Codec.Decode(_compressedBytes.AsSpan(), mat.GetDataByteSpan());
break;
- case LayerCompressionMethod.GZip:
+ case LayerCompressionCodec.GZip:
{
mat = new(SlicerFile.Resolution, DepthType.Cv8U, 1);
var matSpan = mat.GetDataByteSpan();
@@ -683,7 +683,7 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
break;
}
- case LayerCompressionMethod.Deflate:
+ case LayerCompressionCodec.Deflate:
{
mat = new(SlicerFile.Resolution, DepthType.Cv8U, 1);
var matSpan = mat.GetDataByteSpan();
@@ -715,7 +715,7 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
{
if (value is not null)
{
- CompressedBytes = CompressMat(value, _compressionMethod);
+ CompressedBytes = CompressMat(value, _compressionCodec);
}
GetBoundingRectangle(value, true);
@@ -815,10 +815,10 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
#region Constructor
- public Layer(uint index, FileFormat slicerFile, LayerCompressionMethod? compressionMethod = null)
+ public Layer(uint index, FileFormat slicerFile, LayerCompressionCodec? compressionMethod = null)
{
- compressionMethod ??= CoreSettings.DefaultLayerCompressionMethod;
- _compressionMethod = compressionMethod.Value;
+ compressionMethod ??= CoreSettings.DefaultLayerCompressionCodec;
+ _compressionCodec = compressionMethod.Value;
SlicerFile = slicerFile;
_index = index;
@@ -828,9 +828,9 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
ResetParameters();
}
- public Layer(uint index, byte[] pngBytes, FileFormat slicerFile, LayerCompressionMethod? compressionMethod = null) : this(index, slicerFile, compressionMethod)
+ public Layer(uint index, byte[] pngBytes, FileFormat slicerFile, LayerCompressionCodec? compressionMethod = null) : this(index, slicerFile, compressionMethod)
{
- if (_compressionMethod == LayerCompressionMethod.Png)
+ if (_compressionCodec == LayerCompressionCodec.Png)
{
CompressedBytes = pngBytes;
}
@@ -844,25 +844,25 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
_isModified = false;
}
- public Layer(uint index, Mat layerMat, FileFormat slicerFile, LayerCompressionMethod? compressionMethod = null) : this(index, slicerFile, compressionMethod)
+ public Layer(uint index, Mat layerMat, FileFormat slicerFile, LayerCompressionCodec? compressionMethod = null) : this(index, slicerFile, compressionMethod)
{
LayerMat = layerMat;
_isModified = false;
}
- public Layer(uint index, Stream stream, FileFormat slicerFile, LayerCompressionMethod? compressionMethod = null) : this(index, stream.ToArray(), slicerFile, compressionMethod)
+ public Layer(uint index, Stream stream, FileFormat slicerFile, LayerCompressionCodec? compressionMethod = null) : this(index, stream.ToArray(), slicerFile, compressionMethod)
{ }
- public Layer(FileFormat slicerFile, LayerCompressionMethod? compressionMethod = null) : this(0, slicerFile, compressionMethod)
+ public Layer(FileFormat slicerFile, LayerCompressionCodec? compressionMethod = null) : this(0, slicerFile, compressionMethod)
{ }
- public Layer(byte[] pngBytes, FileFormat slicerFile, LayerCompressionMethod? compressionMethod = null) : this(0, pngBytes, slicerFile, compressionMethod)
+ public Layer(byte[] pngBytes, FileFormat slicerFile, LayerCompressionCodec? compressionMethod = null) : this(0, pngBytes, slicerFile, compressionMethod)
{ }
- public Layer(Mat layerMat, FileFormat slicerFile, LayerCompressionMethod? compressionMethod = null) : this(0, layerMat, slicerFile, compressionMethod)
+ public Layer(Mat layerMat, FileFormat slicerFile, LayerCompressionCodec? compressionMethod = null) : this(0, layerMat, slicerFile, compressionMethod)
{ }
- public Layer(Stream stream, FileFormat slicerFile, LayerCompressionMethod? compressionMethod = null) : this(0, stream, slicerFile, compressionMethod) { }
+ public Layer(Stream stream, FileFormat slicerFile, LayerCompressionCodec? compressionMethod = null) : this(0, stream, slicerFile, compressionMethod) { }
#endregion
@@ -1504,20 +1504,20 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
return clonedLayers;
}
- public static byte[] CompressMat(Mat mat, LayerCompressionMethod method)
+ public static byte[] CompressMat(Mat mat, LayerCompressionCodec codec)
{
- switch (method)
+ switch (codec)
{
- case LayerCompressionMethod.Png:
+ case LayerCompressionCodec.Png:
return mat.GetPngByes();
- case LayerCompressionMethod.Lz4:
+ case LayerCompressionCodec.Lz4:
{
var span = mat.GetDataByteSpan();
var target = new byte[LZ4Codec.MaximumOutputSize(span.Length)];
var encodedLength = LZ4Codec.Encode(span, target.AsSpan());
return target[..encodedLength];
}
- case LayerCompressionMethod.GZip:
+ case LayerCompressionCodec.GZip:
{
/*var matSpan = mat.GetDataByteSpan();
var compressedBytes = new byte[matSpan.Length];
@@ -1537,7 +1537,7 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
gZipStream.Write(mat.GetDataByteSpan());
return compressedStream.ToArray();
}
- case LayerCompressionMethod.Deflate:
+ case LayerCompressionCodec.Deflate:
{
using var compressedStream = new MemoryStream();
using var deflateStream = new DeflateStream(compressedStream, CompressionLevel.Fastest);
@@ -1547,7 +1547,7 @@ public class Layer : BindableBase, IEquatable<Layer>, IEquatable<uint>
/*case LayerCompressionMethod.None:
return mat.GetBytes();*/
default:
- throw new ArgumentOutOfRangeException(nameof(method));
+ throw new ArgumentOutOfRangeException(nameof(codec));
}
}
diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs
index 7a65f6f..8314d75 100644
--- a/UVtools.Core/Operations/OperationRaftRelief.cs
+++ b/UVtools.Core/Operations/OperationRaftRelief.cs
@@ -10,8 +10,12 @@ using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using System;
+using System.ComponentModel;
using System.Drawing;
+using System.Text;
using System.Threading.Tasks;
+using Emgu.CV.Util;
+using UVtools.Core.EmguCV;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
@@ -23,9 +27,17 @@ public class OperationRaftRelief : Operation
#region Enums
public enum RaftReliefTypes : byte
{
+ [Description("Relief: Drill raft to relief pressure and remove some mass")]
Relief,
+
+ [Description("Dimming: Darkens the raft to cure it less")]
Dimming,
- Decimate
+
+ [Description("Decimate: Remove the raft and keep supports only on the plate")]
+ Decimate,
+
+ [Description("Tabs: Creates tabs around the raft to easily insert a tool under it and detach the raft from build plate")]
+ Tabs
}
#endregion
@@ -38,6 +50,9 @@ public class OperationRaftRelief : Operation
private byte _wallMargin = 40; // +/- 2mm
private byte _holeDiameter = 80; // +/- 4mm
private byte _holeSpacing = 40; // +/- 2mm
+ private byte _tabBrightness = byte.MaxValue;
+ private ushort _tabTriangleBase = 200;
+ private ushort _tabTriangleHeight = 250;
#endregion
@@ -57,7 +72,20 @@ public class OperationRaftRelief : Operation
public override Enumerations.LayerRangeSelection StartLayerRangeSelection =>
Enumerations.LayerRangeSelection.None;
-
+
+ public override string? ValidateInternally()
+ {
+ var sb = new StringBuilder();
+ if (_reliefType == RaftReliefTypes.Tabs)
+ {
+ if(_tabTriangleHeight == 0) sb.AppendLine("The tab height can't be 0");
+ if(_tabTriangleBase == 0) sb.AppendLine("The tab base can't be 0");
+ if(_tabBrightness == 0) sb.AppendLine("The tab brightness can't be 0");
+ }
+
+ return sb.ToString();
+ }
+
public override string ToString()
{
var result = $"[{_reliefType}] [Mask layer: {_maskLayerIndex}] [Ignore: {_ignoreFirstLayers}] [B: {_brightness}] [Dilate: {_dilateIterations}] [Wall margin: {_wallMargin}] [Hole diameter: {_holeDiameter}] [Hole spacing: {_holeSpacing}]";
@@ -75,8 +103,6 @@ public class OperationRaftRelief : Operation
#endregion
#region Properties
- public static Array RaftReliefItems => Enum.GetValues(typeof(RaftReliefTypes));
-
public RaftReliefTypes ReliefType
{
get => _reliefType;
@@ -86,12 +112,15 @@ public class OperationRaftRelief : Operation
RaisePropertyChanged(nameof(IsRelief));
RaisePropertyChanged(nameof(IsDimming));
RaisePropertyChanged(nameof(IsDecimate));
+ RaisePropertyChanged(nameof(IsTabs));
+ RaisePropertyChanged(nameof(BrightnessPercent));
}
}
public bool IsRelief => _reliefType == RaftReliefTypes.Relief;
public bool IsDimming => _reliefType == RaftReliefTypes.Dimming;
public bool IsDecimate => _reliefType == RaftReliefTypes.Decimate;
+ public bool IsTabs => _reliefType == RaftReliefTypes.Tabs;
public uint MaskLayerIndex
{
@@ -115,7 +144,7 @@ public class OperationRaftRelief : Operation
}
}
- public decimal BrightnessPercent => Math.Round(_brightness * 100 / 255M, 2);
+ public decimal BrightnessPercent => Math.Round((_reliefType == RaftReliefTypes.Tabs ? _tabBrightness : _brightness) * 100 / 255M, 2);
public byte DilateIterations
{
@@ -140,13 +169,36 @@ public class OperationRaftRelief : Operation
get => _holeSpacing;
set => RaiseAndSetIfChanged(ref _holeSpacing, value);
}
+
+ public byte TabBrightness
+ {
+ get => _tabBrightness;
+ set => RaiseAndSetIfChanged(ref _tabBrightness, Math.Max((byte)1, value));
+ }
+
+ public ushort TabTriangleBase
+ {
+ get => _tabTriangleBase;
+ set => RaiseAndSetIfChanged(ref _tabTriangleBase, Math.Max((ushort)5, value));
+ }
+
+ public ushort TabTriangleHeight
+ {
+ get => _tabTriangleHeight;
+ set
+ {
+ if (!RaiseAndSetIfChanged(ref _tabTriangleHeight, Math.Max((ushort)5, value))) return;
+ RaisePropertyChanged(nameof(BrightnessPercent));
+ }
+ }
+
#endregion
#region Equality
protected bool Equals(OperationRaftRelief other)
{
- return _reliefType == other._reliefType && _maskLayerIndex == other._maskLayerIndex && _ignoreFirstLayers == other._ignoreFirstLayers && _brightness == other._brightness && _dilateIterations == other._dilateIterations && _wallMargin == other._wallMargin && _holeDiameter == other._holeDiameter && _holeSpacing == other._holeSpacing;
+ return _reliefType == other._reliefType && _maskLayerIndex == other._maskLayerIndex && _ignoreFirstLayers == other._ignoreFirstLayers && _brightness == other._brightness && _dilateIterations == other._dilateIterations && _wallMargin == other._wallMargin && _holeDiameter == other._holeDiameter && _holeSpacing == other._holeSpacing && _tabTriangleBase == other._tabTriangleBase && _tabTriangleHeight == other._tabTriangleHeight;
}
public override bool Equals(object? obj)
@@ -159,7 +211,18 @@ public class OperationRaftRelief : Operation
public override int GetHashCode()
{
- return HashCode.Combine((int) _reliefType, _maskLayerIndex, _ignoreFirstLayers, _brightness, _dilateIterations, _wallMargin, _holeDiameter, _holeSpacing);
+ var hashCode = new HashCode();
+ hashCode.Add((int) _reliefType);
+ hashCode.Add(_maskLayerIndex);
+ hashCode.Add(_ignoreFirstLayers);
+ hashCode.Add(_brightness);
+ hashCode.Add(_dilateIterations);
+ hashCode.Add(_wallMargin);
+ hashCode.Add(_holeDiameter);
+ hashCode.Add(_holeSpacing);
+ hashCode.Add(_tabTriangleBase);
+ hashCode.Add(_tabTriangleHeight);
+ return hashCode.ToHashCode();
}
#endregion
@@ -176,7 +239,6 @@ public class OperationRaftRelief : Operation
var anchor = new Point(-1, -1);
var kernel = EmguExtensions.Kernel3x3Rectangle;
-
uint firstSupportLayerIndex = _maskLayerIndex;
if (firstSupportLayerIndex <= 0)
{
@@ -185,13 +247,13 @@ public class OperationRaftRelief : Operation
for (; firstSupportLayerIndex < layerCount; firstSupportLayerIndex++)
{
progress.ThrowIfCancellationRequested();
- progress++;
supportsMat = GetRoiOrDefault(SlicerFile[firstSupportLayerIndex].LayerMat);
var circles = CvInvoke.HoughCircles(supportsMat, HoughModes.Gradient, 1, 5, 80, 35, 5, 200);
if (circles.Length >= minSupportsRequired) break;
supportsMat.Dispose();
supportsMat = null;
+ progress++;
}
}
else
@@ -265,6 +327,109 @@ public class OperationRaftRelief : Operation
case RaftReliefTypes.Decimate:
supportsMat.CopyTo(target);
break;
+ case RaftReliefTypes.Tabs:
+ {
+ using var contours = mat.FindContours(RetrType.External);
+ var span = mat.GetDataByteSpan();
+
+ var minX = 10;
+ var maxX = mat.Size.Width - 10;
+ var minY = 10;
+ var maxY = mat.Size.Height - 10;
+
+ var triangleBaseRadius = _tabTriangleBase / 2;
+ var triangleHeightRadius = _tabTriangleHeight / 2;
+
+ var directions = new[]
+ {
+ new Point(0, -1), // Up
+ new Point(1, 0), // Right
+ new Point(0, 1), // Down
+ new Point(-1, 0), // Left
+ };
+
+ var color = new MCvScalar(_tabBrightness);
+
+ for (var i = 0; i < contours.Size; i++)
+ {
+ using var contour = new EmguContour(contours[i]);
+ if(contour.Centroid.IsAnyNegative() || contour.Area < 10000) continue;
+
+ for (var dir = 0; dir < directions.Length; dir++)
+ {
+ var direction = directions[dir];
+
+ var foundFirstWhite = false;
+ var foundPoint = false;
+
+ var x = contour.Centroid.X;
+ var y = contour.Centroid.Y;
+
+ while (!foundPoint
+ && x >= minX && x <= maxX && y >= minY && y <= maxY
+ && contour.Bounds.Contains(x, y))
+ {
+ var pixel = span[mat.GetPixelPos(x, y)];
+
+ if (pixel > 0)
+ {
+ if (!foundFirstWhite)
+ {
+ foundFirstWhite = true;
+ continue;
+ }
+
+ if (CvInvoke.PointPolygonTest(contours[i], new PointF(x, y), false) == 0) // Must be on edge
+ {
+ foundPoint = true;
+ break;
+ }
+ }
+
+
+ x += direction.X;
+ y += direction.Y;
+ }
+
+ if(!foundPoint) continue;
+
+ var polygon = new Point[3];
+
+ switch (dir)
+ {
+ case 0: // Up
+ polygon[0] = new Point(Math.Max(10, x - triangleBaseRadius), y);
+ polygon[1] = new Point(Math.Min(mat.Width - 10, x + triangleBaseRadius), y);
+ polygon[2] = new Point(x, Math.Max(10, y - triangleHeightRadius));
+ break;
+ case 1: // Right
+ polygon[0] = new Point(x, Math.Max(10, y - triangleBaseRadius));
+ polygon[1] = new Point(x, Math.Min(mat.Width - 10, y + triangleBaseRadius));
+ polygon[2] = new Point(Math.Min(mat.Width - 10, x + triangleHeightRadius), y);
+ break;
+ case 2: // Down
+ polygon[0] = new Point(Math.Max(10, x - triangleBaseRadius), y);
+ polygon[1] = new Point(Math.Min(mat.Width - 10, x + triangleBaseRadius), y);
+ polygon[2] = new Point(x, Math.Min(mat.Height - 10, y + triangleHeightRadius));
+ break;
+ case 3: // Left
+ polygon[0] = new Point(x, Math.Max(10, y - triangleBaseRadius));
+ polygon[1] = new Point(x, Math.Min(mat.Width - 10, y + triangleBaseRadius));
+ polygon[2] = new Point(Math.Max(10, x - triangleHeightRadius), y);
+ break;
+ }
+
+ CvInvoke.Ellipse(mat, new Point(x, y), new Size(triangleBaseRadius, (int)(triangleBaseRadius / 1.5)), 90 * dir, 0, 180, EmguExtensions.WhiteColor, -1, LineType.AntiAlias);
+ using var vec = new VectorOfPoint(polygon);
+ CvInvoke.FillPoly(mat, vec, color, LineType.AntiAlias);
+ }
+
+ }
+
+ break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException(nameof(ReliefType));
}
ApplyMask(original, target);
diff --git a/UVtools.Core/SystemOS/SystemAware.cs b/UVtools.Core/SystemOS/SystemAware.cs
index b4b0e9c..0430a24 100644
--- a/UVtools.Core/SystemOS/SystemAware.cs
+++ b/UVtools.Core/SystemOS/SystemAware.cs
@@ -271,4 +271,25 @@ public static class SystemAware
return stringBuilder.ToString();
}
+ /// <summary>
+ /// Gets if is running under Linux and under AppImage format
+ /// </summary>
+ public static bool IsRunningLinuxAppImage(out string? path)
+ {
+ path = null;
+ if (!OperatingSystem.IsLinux()) return false;
+ path = Environment.GetEnvironmentVariable("APPIMAGE");
+ return !string.IsNullOrWhiteSpace(path);
+ }
+
+ /// <summary>
+ /// Gets if is running under Linux and under AppImage format
+ /// </summary>
+ /// <returns></returns>
+ public static bool IsRunningLinuxAppImage() => IsRunningLinuxAppImage(out _);
+
+ /// <summary>
+ /// Gets if is running under MacOS and under app format
+ /// </summary>
+ public static bool IsRunningMacOSApp => OperatingSystem.IsMacOS() && AppContext.BaseDirectory.EndsWith(".app/Contents/MacOS");
} \ No newline at end of file
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index ea250dc..6a9120b 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description>
- <Version>3.1.0</Version>
+ <Version>3.1.1</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>