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'