From a5bef99372d4e130491e39bd5b0eaa43ac76072a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Mon, 21 Mar 2022 03:43:24 +0000 Subject: [PATCH] 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. --- .github/workflows/WingetAutoSubmit.yml | 21 -- .github/workflows/package-publish.yml | 47 +++++ CHANGELOG.md | 7 + UVtools.Core/CoreSettings.cs | 2 +- .../Extensions/ZipArchiveExtensions.cs | 2 +- UVtools.Core/FileFormats/FileFormat.cs | 8 +- UVtools.Core/Layers/Layer.cs | 64 +++--- .../Operations/OperationRaftRelief.cs | 183 +++++++++++++++++- UVtools.Core/SystemOS/SystemAware.cs | 21 ++ UVtools.Core/UVtools.Core.csproj | 2 +- .../Tools/ToolRaftReliefControl.axaml | 111 ++++++++--- UVtools.WPF/MainWindow.axaml.cs | 8 +- UVtools.WPF/Program.cs | 1 + UVtools.WPF/Structures/AppVersionChecker.cs | 117 ++++++----- UVtools.WPF/UVtools.WPF.csproj | 3 +- UVtools.WPF/UserSettings.cs | 12 +- UVtools.WPF/Windows/BenchmarkWindow.axaml.cs | 8 +- UVtools.WPF/Windows/SettingsWindow.axaml | 8 +- build/createRelease.ps1 | 2 +- 19 files changed, 460 insertions(+), 167 deletions(-) delete mode 100644 .github/workflows/WingetAutoSubmit.yml create mode 100644 .github/workflows/package-publish.yml diff --git a/.github/workflows/WingetAutoSubmit.yml b/.github/workflows/WingetAutoSubmit.yml deleted file mode 100644 index bce837d..0000000 --- a/.github/workflows/WingetAutoSubmit.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Submit UVtools package to Windows Package Manager Community Repository - -on: - release: - types: [published] - -jobs: - - winget: - name: Publish winget package - runs-on: windows-latest - steps: - - name: Submit package to Windows Package Manager Community Repository - run: | - iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - $github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json - $installerUrl = $github.release.assets | Where-Object -Property name -match '.+win-x64.+\.msi' | Select -ExpandProperty browser_download_url -First 1 - if($null -eq $installerUrl){ exit 1 } - $version = $github.release.tag_name.Replace('v', '') - if($version.Length -lt 5){ exit 2 } - .\wingetcreate.exe update PTRTECH.UVtools --version $version --urls $installerUrl --token ${{ secrets.WINGET_TOKEN }} --submit diff --git a/.github/workflows/package-publish.yml b/.github/workflows/package-publish.yml new file mode 100644 index 0000000..087ca30 --- /dev/null +++ b/.github/workflows/package-publish.yml @@ -0,0 +1,47 @@ +name: Winget and Nuget package publish + +on: + release: + types: [published] + +jobs: + + winget: + name: Winget - Pull request + runs-on: windows-latest + timeout-minutes: 10 + steps: + - name: Submit package to Windows Package Manager Community Repository + run: | + iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe + $installerUrl = $github.event.release.assets | Where-Object -Property name -match '.+win-x64.+\.msi' | Select -ExpandProperty browser_download_url -First 1 + if($null -eq $installerUrl){ exit 1 } + $version = $github.event.release.tag_name.Replace('v', '') + if($version.Length -lt 5){ exit 2 } + .\wingetcreate.exe update PTRTECH.UVtools --version $version --urls $installerUrl --token ${WINGET_TOKEN} --submit + env: + WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }} + + nuget: + name: Nuget - Publish package + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Verify commit exists in origin/main + run: | + git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/* + git branch --remote --contains | grep origin/main + - name: Set VERSION variable from tag + run: | + echo "VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV + echo "VERSION=${VERSION:1}" >> $GITHUB_ENV + - name: Build + run: dotnet build --configuration Release + - name: Pack + run: dotnet pack UVtools.Core --configuration Release --no-build --output . + - name: Push nuget.org + run: dotnet nuget push UVtools.Core.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN} + env: + NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index f1dc8d9..c52af78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 21/03/2022 - 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. + ## 17/03/2022 - v3.1.0 - **Benchmark:** 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 /// /// Gets or sets the default compression type for layers /// - public static Layer.LayerCompressionMethod DefaultLayerCompressionMethod { get; set; } = Layer.LayerCompressionMethod.Png; + public static Layer.LayerCompressionCodec DefaultLayerCompressionCodec { get; set; } = Layer.LayerCompressionCodec.Png; /// /// 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 /// Changes the compression method of all layers to a new method /// - /// The new method to change to + /// The new method to change to /// - 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, IEquatable { #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, IEquatable #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, IEquatable /// /// Gets or sets the compression method used to cache the image /// - 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, IEquatable 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, IEquatable 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, IEquatable 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, IEquatable { 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, IEquatable #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, IEquatable 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, IEquatable _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, IEquatable 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, IEquatable 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, IEquatable /*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(); } + /// + /// Gets if is running under Linux and under AppImage format + /// + public static bool IsRunningLinuxAppImage(out string? path) + { + path = null; + if (!OperatingSystem.IsLinux()) return false; + path = Environment.GetEnvironmentVariable("APPIMAGE"); + return !string.IsNullOrWhiteSpace(path); + } + + /// + /// Gets if is running under Linux and under AppImage format + /// + /// + public static bool IsRunningLinuxAppImage() => IsRunningLinuxAppImage(out _); + + /// + /// Gets if is running under MacOS and under app format + /// + 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 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 3.1.0 + 3.1.1 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 diff --git a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml index 41a1cf1..71323e1 100644 --- a/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolRaftReliefControl.axaml @@ -15,8 +15,8 @@ + Items="{Binding Operation.ReliefType, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" + SelectedItem="{Binding Operation.ReliefType, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/> - + Value="{Binding Operation.Brightness}"> + + + + + + + + + - + + + + + + + + - + Text="Dilate supports by:" VerticalAlignment="Center"/> + + + Minimum="0" + Maximum="255" + IsVisible="{Binding !Operation.IsTabs}" + Value="{Binding Operation.DilateIterations}"/> - + + + + + + + + + + + + + + Value="{Binding Operation.WallMargin}"> + + + + + + + - + + + + @@ -121,8 +171,7 @@ you must manually input the layer index of the last raft where it ends and suppo + IsVisible="{Binding Operation.IsRelief}"/> { try { - SlicerFile.ChangeLayersCompressionMethod(Settings.General.LayerCompressionMethod, Progress); + SlicerFile.ChangeLayersCompressionMethod(Settings.General.LayerCompressionCodec, Progress); return true; } catch (OperationCanceledException) diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs index 5b518d4..92ae882 100644 --- a/UVtools.WPF/Program.cs +++ b/UVtools.WPF/Program.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Globalization; +using System.IO; using System.Runtime.ExceptionServices; using Avalonia; using Projektanker.Icons.Avalonia; diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs index 68a9d78..458ebf0 100644 --- a/UVtools.WPF/Structures/AppVersionChecker.cs +++ b/UVtools.WPF/Structures/AppVersionChecker.cs @@ -13,6 +13,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Runtime.InteropServices; using System.Text.Json.Nodes; +using System.Text.RegularExpressions; using System.Threading.Tasks; using Avalonia.Threading; using UVtools.Core; @@ -43,7 +44,9 @@ public class AppVersionChecker : BindableBase var package = File.ReadAllText(file); if (!string.IsNullOrWhiteSpace(package) && (package.EndsWith("-x64") || package.EndsWith("-arm64"))) { - return $"{About.Software}_{package}_v{_version}.zip"; + return SystemAware.IsRunningLinuxAppImage() + ? $"{About.Software}_{package}_v{_version}.AppImage" + : $"{About.Software}_{package}_v{_version}.zip"; } } catch (Exception e) @@ -58,20 +61,22 @@ public class AppVersionChecker : BindableBase } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - return $"{About.Software}_linux-x64_v{_version}.zip"; + return SystemAware.IsRunningLinuxAppImage() + ? $"{About.Software}_linux-x64_v{_version}.AppImage" + : $"{About.Software}_linux-x64_v{_version}.zip"; } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - if (RuntimeInformation.ProcessArchitecture is Architecture.Arm or Architecture.Arm64) return $"{About.Software}_osx-arm64_v{_version}.zip"; - - return $"{About.Software}_osx-x64_v{_version}.zip"; + return RuntimeInformation.ProcessArchitecture is Architecture.Arm or Architecture.Arm64 + ? $"{About.Software}_osx-arm64_v{_version}.zip" + : $"{About.Software}_osx-x64_v{_version}.zip"; } return $"{About.Software}_universal-x86-x64_v{_version}.zip"; } } - public string Runtime + /*public string Runtime { get { @@ -90,7 +95,7 @@ public class AppVersionChecker : BindableBase return "universal-x86-x64"; } - } + }*/ public string Version { @@ -180,8 +185,9 @@ public class AppVersionChecker : BindableBase progress.ItemName = "Megabytes"; try { + var downloadFilename = Filename; var path = Path.GetTempPath(); - DownloadedFile = Path.Combine(path, Filename); + DownloadedFile = Path.Combine(path, downloadFilename); Debug.WriteLine($"Downloading to: {DownloadedFile}"); progress.ItemName = "Megabytes"; @@ -203,53 +209,70 @@ public class AppVersionChecker : BindableBase } else { - string upgradeFolder = "UPDATED_VERSION"; - var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder); - await using (var stream = File.Open(DownloadedFile, FileMode.Open)) + // Linux AppImage + if (downloadFilename.EndsWith(".AppImage") && SystemAware.IsRunningLinuxAppImage(out var appImagePath)) { - using ZipArchive zip = new(stream, ZipArchiveMode.Read); - zip.ExtractToDirectory(targetDir, true); + var directory = Path.GetDirectoryName(appImagePath); + var oldFileName = Path.GetFileName(appImagePath); + // Try to keep same filename logic if user renamed the file, like UVtools.AppImage would keep same same + var newFilename = Regex.Replace(oldFileName, @"v\d.\d.\d", $"v{_version}"); + var newFullPath = Path.Combine(directory, newFilename); + + if (File.Exists(appImagePath)) File.Delete(appImagePath); + File.Move(DownloadedFile, newFullPath, true); + SystemAware.StartProcess("chmod", $"a+x \"{newFullPath}\""); + SystemAware.StartProcess(newFullPath); } - - File.Delete(DownloadedFile); - - string upgradeFileName = $"{About.Software}_upgrade.sh"; - var upgradeFile = Path.Combine(App.ApplicationPath, upgradeFileName); - await using (var stream = File.CreateText(upgradeFile)) + else // others { - await stream.WriteLineAsync("#!/bin/bash"); - await stream.WriteLineAsync($"echo {About.Software} v{App.Version} updater script"); - await stream.WriteLineAsync($"cd '{App.ApplicationPath}'"); - await stream.WriteLineAsync($"killall {About.Software}"); - await stream.WriteLineAsync("sleep 0.5"); - - - //stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &"); - if (OperatingSystem.IsMacOS() && App.ApplicationPath.EndsWith(".app/Contents/MacOS")) + var upgradeFolder = "UPDATED_VERSION"; + var targetDir = Path.Combine(App.ApplicationPath, upgradeFolder); + await using (var stream = File.Open(DownloadedFile, FileMode.Open)) { - await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* ../../../"); - await stream.WriteLineAsync($"open ../../../{About.Software}.app"); - } - else - { - await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* ."); - await stream.WriteLineAsync($"if [ -f '{About.Software}' ]; then"); - await stream.WriteLineAsync($" ./{About.Software} &"); - await stream.WriteLineAsync("else"); - await stream.WriteLineAsync($" dotnet {About.Software}.dll &"); - await stream.WriteLineAsync("fi"); + using ZipArchive zip = new(stream, ZipArchiveMode.Read); + zip.ExtractToDirectory(targetDir, true); } - await stream.WriteLineAsync($"rm -fr {upgradeFolder}"); - await stream.WriteLineAsync("sleep 0.5"); - await stream.WriteLineAsync($"rm -f {upgradeFileName}"); - //stream.WriteLine("exit"); - stream.Close(); + File.Delete(DownloadedFile); + + var upgradeFileName = $"{About.Software}_upgrade.sh"; + var upgradeFile = Path.Combine(App.ApplicationPath, upgradeFileName); + await using (var stream = File.CreateText(upgradeFile)) + { + stream.NewLine = "\n"; + await stream.WriteLineAsync("#!/bin/bash"); + await stream.WriteLineAsync($"echo {About.Software} v{App.Version} updater script"); + await stream.WriteLineAsync($"cd '{App.ApplicationPath}'"); + await stream.WriteLineAsync($"killall {About.Software}"); + await stream.WriteLineAsync("sleep 0.5"); + + + //stream.WriteLine($"[ -f {About.Software} ] && {App.AppExecutableQuoted} & || dotnet {About.Software}.dll &"); + if (SystemAware.IsRunningMacOSApp) + { + await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* ../../../"); + await stream.WriteLineAsync($"open ../../../{About.Software}.app"); + } + else + { + await stream.WriteLineAsync($"cp -fR {upgradeFolder}/* ."); + await stream.WriteLineAsync($"if [ -f '{About.Software}' ]; then"); + await stream.WriteLineAsync($" ./{About.Software} &"); + await stream.WriteLineAsync("else"); + await stream.WriteLineAsync($" dotnet {About.Software}.dll &"); + await stream.WriteLineAsync("fi"); + } + + await stream.WriteLineAsync($"rm -fr {upgradeFolder}"); + await stream.WriteLineAsync("sleep 0.5"); + await stream.WriteLineAsync($"rm -f {upgradeFileName}"); + //stream.WriteLine("exit"); + } + + SystemAware.StartProcess("bash", $"\"{upgradeFile}\""); + //App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath); } - SystemAware.StartProcess("bash", $"\"{upgradeFile}\""); - - //App.NewInstance(App.MainWindow.SlicerFile?.FileFullPath); Environment.Exit(0); } } diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 6ebb64e..d240df3 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ LICENSE https://github.com/sn4k3/UVtools Git - 3.1.0 + 3.1.1 AnyCPU;x64 UVtools.png README.md @@ -43,6 +43,7 @@ + diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs index 1cb8af0..d764ed9 100644 --- a/UVtools.WPF/UserSettings.cs +++ b/UVtools.WPF/UserSettings.cs @@ -43,7 +43,7 @@ public sealed class UserSettings : BindableBase private bool _loadDemoFileOnStartup = true; private bool _loadLastRecentFileOnStartup; private int _maxDegreeOfParallelism = -1; - private Layer.LayerCompressionMethod _layerCompressionMethod = CoreSettings.DefaultLayerCompressionMethod; + private Layer.LayerCompressionCodec _layerCompressionCodec = CoreSettings.DefaultLayerCompressionCodec; private bool _windowsCanResize; private bool _windowsTakeIntoAccountScreenScaling = true; @@ -102,10 +102,10 @@ public sealed class UserSettings : BindableBase set => RaiseAndSetIfChanged(ref _maxDegreeOfParallelism, Math.Min(value, Environment.ProcessorCount)); } - public Layer.LayerCompressionMethod LayerCompressionMethod + public Layer.LayerCompressionCodec LayerCompressionCodec { - get => _layerCompressionMethod; - set => RaiseAndSetIfChanged(ref _layerCompressionMethod, value); + get => _layerCompressionCodec; + set => RaiseAndSetIfChanged(ref _layerCompressionCodec, value); } public bool WindowsCanResize @@ -1641,7 +1641,7 @@ public sealed class UserSettings : BindableBase } CoreSettings.MaxDegreeOfParallelism = _instance.General.MaxDegreeOfParallelism; - CoreSettings.DefaultLayerCompressionMethod = _instance.General.LayerCompressionMethod; + CoreSettings.DefaultLayerCompressionCodec = _instance.General.LayerCompressionCodec; if (_instance.Network.RemotePrinters.Count == 0) { @@ -1864,7 +1864,7 @@ public sealed class UserSettings : BindableBase Instance.SavesCount++; _instance.ModifiedDateTime = DateTime.Now; CoreSettings.MaxDegreeOfParallelism = _instance.General.MaxDegreeOfParallelism; - CoreSettings.DefaultLayerCompressionMethod = _instance.General.LayerCompressionMethod; + CoreSettings.DefaultLayerCompressionCodec = _instance.General.LayerCompressionCodec; try { XmlExtensions.SerializeToFile(_instance, FilePath, XmlExtensions.SettingsIndent); diff --git a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs index 1ada2c2..46ead58 100644 --- a/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs +++ b/UVtools.WPF/Windows/BenchmarkWindow.axaml.cs @@ -526,7 +526,7 @@ public class BenchmarkWindow : WindowEx public void TestPNGCompress(BenchmarkResolution resolution) { - Layer.CompressMat(Mats[resolution], Layer.LayerCompressionMethod.Png); + Layer.CompressMat(Mats[resolution], Layer.LayerCompressionCodec.Png); } public void TestPNGDecompress(BenchmarkResolution resolution) @@ -534,7 +534,7 @@ public class BenchmarkWindow : WindowEx public void TestGZipCompress(BenchmarkResolution resolution) { - Layer.CompressMat(Mats[resolution], Layer.LayerCompressionMethod.GZip); + Layer.CompressMat(Mats[resolution], Layer.LayerCompressionCodec.GZip); } public void TestGZipDecompress(BenchmarkResolution resolution) @@ -542,7 +542,7 @@ public class BenchmarkWindow : WindowEx public void TestDeflateCompress(BenchmarkResolution resolution) { - Layer.CompressMat(Mats[resolution], Layer.LayerCompressionMethod.Deflate); + Layer.CompressMat(Mats[resolution], Layer.LayerCompressionCodec.Deflate); } public void TestDeflateDecompress(BenchmarkResolution resolution) @@ -550,7 +550,7 @@ public class BenchmarkWindow : WindowEx public void TestLZ4Compress(BenchmarkResolution resolution) { - Layer.CompressMat(Mats[resolution], Layer.LayerCompressionMethod.Lz4); + Layer.CompressMat(Mats[resolution], Layer.LayerCompressionCodec.Lz4); } public void TestLZ4Decompress(BenchmarkResolution resolution) diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml b/UVtools.WPF/Windows/SettingsWindow.axaml index 25eae57..d1013d4 100644 --- a/UVtools.WPF/Windows/SettingsWindow.axaml +++ b/UVtools.WPF/Windows/SettingsWindow.axaml @@ -126,15 +126,15 @@ + Text="Layer compression codec:"/> + Items="{Binding Settings.General.LayerCompressionCodec, Converter={StaticResource EnumToCollectionConverter}, Mode=OneTime}" + SelectedItem="{Binding Settings.General.LayerCompressionCodec, Converter={StaticResource FromValueDescriptionToEnumConverter}}"/> diff --git a/build/createRelease.ps1 b/build/createRelease.ps1 index ef2e5b7..2a9fdea 100644 --- a/build/createRelease.ps1 +++ b/build/createRelease.ps1 @@ -178,7 +178,7 @@ Set-Location $PSScriptRoot\.. ### Configuration ### #################################### $enableMSI = $true -$buildOnly = 'win-x64' +#$buildOnly = 'win-x64' #$buildOnly = 'linux-x64' #$buildOnly = 'osx-x64' #$buildOnly = 'osx-arm64'