diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-03-21 06:43:24 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-03-21 06:43:24 +0300 |
commit | a5bef99372d4e130491e39bd5b0eaa43ac76072a (patch) | |
tree | 06a68a41b6334bbfd63affc40a79d414a976d6f4 /UVtools.Core | |
parent | a3f907fda84683a097dd7a456f8cabf9193f9d1d (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.cs | 2 | ||||
-rw-r--r-- | UVtools.Core/Extensions/ZipArchiveExtensions.cs | 2 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileFormat.cs | 8 | ||||
-rw-r--r-- | UVtools.Core/Layers/Layer.cs | 64 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationRaftRelief.cs | 183 | ||||
-rw-r--r-- | UVtools.Core/SystemOS/SystemAware.cs | 21 | ||||
-rw-r--r-- | UVtools.Core/UVtools.Core.csproj | 2 |
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> |