diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-07-15 01:24:39 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2022-07-15 01:24:39 +0300 |
commit | 43c1a6a944c643bff7293de2975237d347c31ef4 (patch) | |
tree | 5ef7e65a20e8bbf5422cb2d957c6f021d14a16aa | |
parent | 54d0cc1776282cf951861d71c7b35caed15870e2 (diff) |
v3.5.4v3.5.4
- **PCB Exposure:**
- (Add) Parse of deprecated commands (G70, G71, G90, G91)
- (Fix) Able to have parameterless macro apertures (#503)
- **UI:**
- (Add) Menu -> File -> Free unused RAM: Force the garbage collection of all unused objects within the program to free unused memory (RAM).
It's never required for the end user run this. The program will automatically take care of it when required.
This function is for debug purposes.
- (Improvement) Window title bar: Show elapsed minutes and seconds instead of total seconds minutes and second
- (Fix) Tool - Mask: Loaded image resolution shows as (unloaded)
- (Fix) Applying a large set of modifications in layer depth with pixel editor cause huge memory spike due layer aggregation without disposing, leading to program crash on most cases where RAM is insufficient (#506)
- (Upgrade) AvaloniaUI from 0.10.15 to 0.10.16
-rw-r--r-- | CHANGELOG.md | 15 | ||||
-rw-r--r-- | RELEASE_NOTES.md | 16 | ||||
-rw-r--r-- | UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj | 2 | ||||
-rw-r--r-- | UVtools.Core/Extensions/UnitExtensions.cs | 7 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileFormat.cs | 203 | ||||
-rw-r--r-- | UVtools.Core/Gerber/Apertures/Aperture.cs | 22 | ||||
-rw-r--r-- | UVtools.Core/Gerber/GerberDocument.cs | 34 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationMask.cs | 23 | ||||
-rw-r--r-- | UVtools.Core/UVtools.Core.csproj | 2 | ||||
-rw-r--r-- | UVtools.InstallerMM/UVtools.InstallerMM.wxs | 6 | ||||
-rw-r--r-- | UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs | 28 | ||||
-rw-r--r-- | UVtools.WPF/MainWindow.axaml | 18 | ||||
-rw-r--r-- | UVtools.WPF/MainWindow.axaml.cs | 8 | ||||
-rw-r--r-- | UVtools.WPF/UVtools.WPF.csproj | 10 |
14 files changed, 321 insertions, 73 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 770a007..fe32973 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 14/07/2022 - v3.5.4 + +- **PCB Exposure:** + - (Add) Parse of deprecated commands (G70, G71, G90, G91) + - (Fix) Able to have parameterless macro apertures (#503) +- **UI:** + - (Add) Menu -> File -> Free unused RAM: Force the garbage collection of all unused objects within the program to free unused memory (RAM). + It's never required for the end user run this. The program will automatically take care of it when required. + This function is for debug purposes. + - (Improvement) Window title bar: Show elapsed minutes and seconds instead of total seconds minutes and second +- (Fix) Tool - Mask: Loaded image resolution shows as (unloaded) +- (Fix) Applying a large set of modifications in layer depth with pixel editor cause huge memory spike due layer aggregation without disposing, leading to program crash on most cases where RAM is insufficient (#506) +- (Upgrade) AvaloniaUI from 0.10.15 to 0.10.16 +- (Upgrade) .NET from 6.0.6 to 6.0.7 + ## 07/07/2022 - v3.5.3 - (Add) Layer action - Export layers to HTML: Export file information and layers to an html file diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e79a7ea..7292e31 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,4 +1,12 @@ -- (Add) Layer action - Export layers to HTML: Export file information and layers to an html file -- (Change) CXDLP: Validate header and footer value must start with "CXSW3D" instead of force equal to "CXSW3DV2" (#501) -- (Upgrade) .NET from 6.0.5 to 6.0.6 - +- **PCB Exposure:** + - (Add) Parse of deprecated commands (G70, G71, G90, G91) + - (Fix) Able to have parameterless macro apertures (#503) +- **UI:** + - (Add) Menu -> File -> Free unused RAM: Force the garbage collection of all unused objects within the program to free unused memory (RAM). + It's never required for the end user run this. The program will automatically take care of it when required. + This function is for debug purposes. + - (Improvement) Window title bar: Show elapsed minutes and seconds instead of total seconds minutes and second +- (Fix) Tool - Mask: Loaded image resolution shows as (unloaded) +- (Fix) Applying a large set of modifications in layer depth with pixel editor cause huge memory spike due layer aggregation without disposing, leading to program crash on most cases where RAM is insufficient (#506) +- (Upgrade) AvaloniaUI from 0.10.15 to 0.10.16 +- (Upgrade) .NET from 6.0.6 to 6.0.7 diff --git a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj index c41bc1d..b177036 100644 --- a/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj +++ b/UVtools.AvaloniaControls/UVtools.AvaloniaControls.csproj @@ -38,7 +38,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Avalonia" Version="0.10.15" /> + <PackageReference Include="Avalonia" Version="0.10.16" /> </ItemGroup> <ItemGroup> diff --git a/UVtools.Core/Extensions/UnitExtensions.cs b/UVtools.Core/Extensions/UnitExtensions.cs index 9e4f355..ddc3295 100644 --- a/UVtools.Core/Extensions/UnitExtensions.cs +++ b/UVtools.Core/Extensions/UnitExtensions.cs @@ -11,7 +11,12 @@ namespace UVtools.Core.Extensions; public static class UnitExtensions { /// <summary> + /// 1 inch to 1 mm + /// </summary> + public const double InchToMillimeter = 25.4; + + /// <summary> /// 1 mm to 1 inch /// </summary> - public const double MillimeterInInch = 0.0393700787; + public const double MillimeterToInch = 0.0393700787; }
\ No newline at end of file diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 6609773..e62310d 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -11,7 +11,6 @@ using Emgu.CV.CvEnum; using Emgu.CV.Structure; using System; using System.Collections; -using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -1470,7 +1469,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor } /// <summary> - /// Gets the image width resolution + /// Gets the image width and height resolution /// </summary> public Size Resolution { @@ -1533,7 +1532,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor /// <summary> /// Gets the display diagonal in inch's /// </summary> - public float DisplayDiagonalInches => (float)Math.Round(Math.Sqrt(Math.Pow(DisplayWidth, 2) + Math.Pow(DisplayHeight, 2)) * UnitExtensions.MillimeterInInch, 2); + public float DisplayDiagonalInches => (float)Math.Round(Math.Sqrt(Math.Pow(DisplayWidth, 2) + Math.Pow(DisplayHeight, 2)) * UnitExtensions.MillimeterToInch, 2); /// <summary> /// Gets the display ratio @@ -5534,6 +5533,178 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor progress ??= new OperationProgress(); progress.Reset("Drawings", (uint)drawings.Count); + var group1 = drawings + .Where(operation => operation.OperationType + is PixelOperation.PixelOperationType.Drawing + or PixelOperation.PixelOperationType.Text + or PixelOperation.PixelOperationType.Eraser) + .GroupBy(operation => operation.LayerIndex); + + Parallel.ForEach(group1, CoreSettings.GetParallelOptions(progress), layerOperationGroup => + { + var layer = this[layerOperationGroup.Key]; + using var mat = layer.LayerMat; + + foreach (var operation in layerOperationGroup) + { + if (operation.OperationType == PixelOperation.PixelOperationType.Drawing) + { + var operationDrawing = (PixelDrawing)operation; + + if (operationDrawing.BrushSize == 1) + { + mat.SetByte(operation.Location.X, operation.Location.Y, operationDrawing.Brightness); + continue; + } + + mat.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize / 2, operationDrawing.Location, + new MCvScalar(operationDrawing.Brightness), operationDrawing.RotationAngle, operationDrawing.Thickness, operationDrawing.LineType); + /*switch (operationDrawing.BrushShape) + { + case PixelDrawing.BrushShapeType.Square: + CvInvoke.Rectangle(mat, operationDrawing.Rectangle, new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType); + break; + case PixelDrawing.BrushShapeType.Circle: + CvInvoke.Circle(mat, operation.Location, operationDrawing.BrushSize / 2, + new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType); + break; + default: + throw new ArgumentOutOfRangeException(); + }*/ + } + else if (operation.OperationType == PixelOperation.PixelOperationType.Text) + { + var operationText = (PixelText)operation; + mat.PutTextRotated(operationText.Text, operationText.Location, operationText.Font, operationText.FontScale, new MCvScalar(operationText.Brightness), operationText.Thickness, operationText.LineType, operationText.Mirror, operationText.LineAlignment, operationText.Angle); + } + else if (operation.OperationType == PixelOperation.PixelOperationType.Eraser) + { + using var layerContours = mat.FindContours(out var hierarchy, RetrType.Tree); + + if (mat.GetByte(operation.Location) >= 10) + { + using var vec = EmguContours.GetContoursInside(layerContours, hierarchy, operation.Location); + + if (vec.Size > 0) + { + CvInvoke.DrawContours(mat, vec, -1, new MCvScalar(operation.PixelBrightness), -1); + } + } + } + } + + layer.LayerMat = mat; + progress.LockAndIncrement(); + }); + + var group2 = drawings + .Where(operation => operation.OperationType + is PixelOperation.PixelOperationType.Supports + or PixelOperation.PixelOperationType.DrainHole) + .GroupBy(operation => operation.LayerIndex) + .OrderByDescending(group => group.Key); + + if (group2.Any()) + { + using var matCache = new MatCacheManager(this, 0, group2.First().Key) + { + AutoDispose = true, + Direction = false + }; + foreach (var layerOperationGroup in group2) + { + var toProcess = layerOperationGroup.ToList(); + var drawnSupportLayers = 0; + var drawnDrainHoleLayers = 0; + for (int operationLayer = (int)layerOperationGroup.Key - 1; operationLayer >= 0 && toProcess.Count > 0; operationLayer--) + { + progress.ThrowIfCancellationRequested(); + var layer = this[operationLayer]; + var mat = matCache.Get1((uint) operationLayer); + var isMatModified = false; + + for (var i = toProcess.Count-1; i >= 0; i--) + { + var operation = toProcess[i]; + if (operation.OperationType == PixelOperation.PixelOperationType.Supports) + { + var operationSupport = (PixelSupport) operation; + + int radius = (operationLayer > 10 + ? Math.Min(operationSupport.TipDiameter + drawnSupportLayers, operationSupport.PillarDiameter) + : operationSupport.BaseDiameter) / 2; + uint whitePixels; + + int yStart = Math.Max(0, operation.Location.Y - operationSupport.TipDiameter / 2); + int xStart = Math.Max(0, operation.Location.X - operationSupport.TipDiameter / 2); + + using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationSupport.TipDiameter, operationSupport.TipDiameter))) + { + using var matCircleMask = matCircleRoi.NewBlank(); + CvInvoke.Circle(matCircleMask, + new Point(operationSupport.TipDiameter / 2, operationSupport.TipDiameter / 2), + operationSupport.TipDiameter / 2, new MCvScalar(operation.PixelBrightness), -1); + CvInvoke.BitwiseAnd(matCircleRoi, matCircleMask, matCircleMask); + whitePixels = (uint) CvInvoke.CountNonZero(matCircleMask); + } + + if (whitePixels >= Math.Pow(operationSupport.TipDiameter, 2) / 3) + { + //CvInvoke.Circle(mat, operation.Location, radius, new MCvScalar(255), -1); + if (drawnSupportLayers == 0) continue; // Supports nonexistent, keep digging + toProcess.RemoveAt(i); + continue; // White area end supporting + } + + CvInvoke.Circle(mat, operation.Location, radius, new MCvScalar(operation.PixelBrightness), -1, operationSupport.LineType); + isMatModified = true; + drawnSupportLayers++; + } + else if (operation.OperationType == PixelOperation.PixelOperationType.DrainHole) + { + var operationDrainHole = (PixelDrainHole) operation; + + int radius = operationDrainHole.Diameter / 2; + uint blackPixels; + + int yStart = Math.Max(0, operation.Location.Y - radius); + int xStart = Math.Max(0, operation.Location.X - radius); + + using (var matCircleRoi = new Mat(mat, new Rectangle(xStart, yStart, operationDrainHole.Diameter, operationDrainHole.Diameter))) + { + using var matCircleRoiInv = new Mat(); + CvInvoke.Threshold(matCircleRoi, matCircleRoiInv, 100, 255, ThresholdType.BinaryInv); + using var matCircleMask = matCircleRoi.NewBlank(); + CvInvoke.Circle(matCircleMask, new Point(radius, radius), radius, EmguExtensions.WhiteColor, -1); + CvInvoke.BitwiseAnd(matCircleRoiInv, matCircleMask, matCircleMask); + blackPixels = (uint) CvInvoke.CountNonZero(matCircleMask); + } + + if (blackPixels >= Math.Pow(operationDrainHole.Diameter, 2) / 3) // Enough area to drain? + { + if (drawnDrainHoleLayers == 0) continue; // Drill not found a target yet, keep digging + toProcess.RemoveAt(i); + continue; // Stop drill drain found! + } + + CvInvoke.Circle(mat, operation.Location, radius, EmguExtensions.BlackColor, -1, operationDrainHole.LineType); + isMatModified = true; + drawnDrainHoleLayers++; + } + } + + if (isMatModified) + { + layer.LayerMat = mat; + } + } + + progress += (uint)layerOperationGroup.Count(); + } + } + + /* + // Old and memory hunger code ConcurrentDictionary<uint, Mat> modifiedLayers = new(); for (var i = 0; i < drawings.Count; i++) { @@ -5552,18 +5723,18 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor mat.DrawPolygon((byte)operationDrawing.BrushShape, operationDrawing.BrushSize / 2, operationDrawing.Location, new MCvScalar(operationDrawing.Brightness), operationDrawing.RotationAngle, operationDrawing.Thickness, operationDrawing.LineType); - /*switch (operationDrawing.BrushShape) - { - case PixelDrawing.BrushShapeType.Square: - CvInvoke.Rectangle(mat, operationDrawing.Rectangle, new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType); - break; - case PixelDrawing.BrushShapeType.Circle: - CvInvoke.Circle(mat, operation.Location, operationDrawing.BrushSize / 2, - new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType); - break; - default: - throw new ArgumentOutOfRangeException(); - }*/ + //switch (operationDrawing.BrushShape) + //{ + // case PixelDrawing.BrushShapeType.Square: + // CvInvoke.Rectangle(mat, operationDrawing.Rectangle, new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType); + // break; + // case PixelDrawing.BrushShapeType.Circle: + // CvInvoke.Circle(mat, operation.Location, operationDrawing.BrushSize / 2, + // new MCvScalar(operationDrawing.Brightness), operationDrawing.Thickness, operationDrawing.LineType); + // break; + // default: + // throw new ArgumentOutOfRangeException(); + //} } else if (operation.OperationType == PixelOperation.PixelOperationType.Text) { @@ -5666,7 +5837,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable<FileFor progress.LockAndIncrement(); }); - + */ } #endregion }
\ No newline at end of file diff --git a/UVtools.Core/Gerber/Apertures/Aperture.cs b/UVtools.Core/Gerber/Apertures/Aperture.cs index 39bff36..071ef7f 100644 --- a/UVtools.Core/Gerber/Apertures/Aperture.cs +++ b/UVtools.Core/Gerber/Apertures/Aperture.cs @@ -53,8 +53,8 @@ public abstract class Aperture public static Aperture? Parse(string line, GerberDocument document) { - var match = Regex.Match(line, @"\%ADD(\d+)(\S+),(\S+)\*\%"); - if (!match.Success || match.Groups.Count < 4) return null; + var match = Regex.Match(line, @"\%ADD(\d+)(\w+),?(\S+)?\*\%"); + if (!match.Success || match.Groups.Count < 3) return null; if (!int.TryParse(match.Groups[1].Value, out var index)) return null; //if (!char.TryParse(match.Groups[2].Value, out var type)) return null; @@ -63,11 +63,13 @@ public abstract class Aperture { case "C": { + if (match.Groups.Count < 4) return null; if (!double.TryParse(match.Groups[3].Value, out var diameter)) return null; return new CircleAperture(document, index, diameter); } case "O": { + if (match.Groups.Count < 4) return null; var split = match.Groups[3].Value.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (split.Length < 2) return null; if (!float.TryParse(split[0], out var width)) return null; @@ -77,6 +79,7 @@ public abstract class Aperture } case "R": { + if (match.Groups.Count < 4) return null; var split = match.Groups[3].Value.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (split.Length < 2) return null; if (!float.TryParse(split[0], out var width)) return null; @@ -86,6 +89,7 @@ public abstract class Aperture } case "P": { + if (match.Groups.Count < 4) return null; var split = match.Groups[3].Value.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (split.Length < 2) return null; if (!double.TryParse(split[0], out var diameter)) return null; @@ -97,11 +101,15 @@ public abstract class Aperture { if (!document.Macros.TryGetValue(match.Groups[2].Value, out var macro)) return null; macro = macro.Clone(); - var parseLine = line.TrimEnd('%', '*'); - var commaIndex = parseLine.IndexOf(',')+1; - if (commaIndex == 0) return null; - parseLine = parseLine[commaIndex..]; - var args = new[] {"0"}.Concat(parseLine.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)).ToArray(); + //var parseLine = line.TrimEnd('%', '*'); + //var commaIndex = parseLine.IndexOf(',')+1; + //parseLine = parseLine[commaIndex..]; + string[] args = { "0" }; + if (match.Groups.Count >= 4) + { + args = args.Concat(match.Groups[3].Value.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)).ToArray(); + } + foreach (var primitive in macro) { primitive.ParseExpressions(document, args); diff --git a/UVtools.Core/Gerber/GerberDocument.cs b/UVtools.Core/Gerber/GerberDocument.cs index 5bde5bf..2e20db4 100644 --- a/UVtools.Core/Gerber/GerberDocument.cs +++ b/UVtools.Core/Gerber/GerberDocument.cs @@ -102,6 +102,32 @@ public class GerberDocument continue; } + if (line.StartsWith("G70")) + { + document.UnitType = GerberUnitType.Inch; + continue; + } + + + if (line.StartsWith("G71")) + { + document.UnitType = GerberUnitType.Millimeter; + continue; + } + + if (line.StartsWith("G90")) + { + document.PositionType = GerberPositionType.Absolute; + continue; + } + + + if (line.StartsWith("G91")) + { + document.PositionType = GerberPositionType.Relative; + continue; + } + if (line.StartsWith("%FS") && line.Length >= FSlength) { // %FSLAX34Y34*% @@ -379,25 +405,25 @@ public class GerberDocument public float GetMillimeters(float size) { if (UnitType == GerberUnitType.Millimeter) return size; - return size * 25.4f; + return size * (float)UnitExtensions.InchToMillimeter; } public double GetMillimeters(double size) { if (UnitType == GerberUnitType.Millimeter) return size; - return size * 25.4; + return size * UnitExtensions.InchToMillimeter; } public SizeF GetMillimeters(SizeF size) { if (UnitType == GerberUnitType.Millimeter) return size; - return new SizeF(size.Width * 25.4f, size.Height * 25.4f); + return new SizeF(size.Width * (float)UnitExtensions.InchToMillimeter, size.Height * (float)UnitExtensions.InchToMillimeter); } public PointF GetMillimeters(PointF point) { if (UnitType == GerberUnitType.Millimeter) return point; - return new PointF(point.X * 25.4f, point.Y * 25.4f); + return new PointF(point.X * (float)UnitExtensions.InchToMillimeter, point.Y * (float)UnitExtensions.InchToMillimeter); } public static Point PositionMmToPx(PointF atMm, SizeF xyPpmm) diff --git a/UVtools.Core/Operations/OperationMask.cs b/UVtools.Core/Operations/OperationMask.cs index c22a104..348f3de 100644 --- a/UVtools.Core/Operations/OperationMask.cs +++ b/UVtools.Core/Operations/OperationMask.cs @@ -8,9 +8,12 @@ using Emgu.CV; using System; +using System.Drawing; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; +using Emgu.CV.CvEnum; +using Emgu.CV.DepthAI; using UVtools.Core.FileFormats; namespace UVtools.Core.Operations; @@ -66,6 +69,26 @@ public class OperationMask : Operation #region Methods + /// <summary> + /// Loads mask from a image file + /// </summary> + /// <param name="filePath"></param> + /// <param name="invertMask"></param> + /// <param name="maskSize"></param> + public void LoadFromFile(string filePath, bool invertMask = false, Size maskSize = default) + { + Mask = CvInvoke.Imread(filePath, ImreadModes.Grayscale); + if (maskSize.Width > 0 && maskSize.Height > 0 && Mask.Size != maskSize) + { + CvInvoke.Resize(Mask, Mask, maskSize); + } + + if (invertMask) + { + InvertMask(); + } + } + public void InvertMask() { if (!HaveInputMask) return; diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 19602bf..11a0667 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.5.3</Version> + <Version>3.5.4</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs index b211267..f227341 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs @@ -2,7 +2,7 @@ <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <?define ComponentRules="OneToOne"?> <!-- SourceDir instructs IsWiX the location of the directory that contains files for this merge module --> - <?define SourceDir="..\publish\UVtools_win-x64_v3.5.3"?> + <?define SourceDir="..\publish\UVtools_win-x64_v3.5.4"?> <Module Id="UVtools" Language="1033" Version="1.0.0.0"> <Package Id="12aaa1cf-ff06-4a02-abd5-2ac01ac4f83b" Manufacturer="PTRTECH" InstallerVersion="200" Keywords="MSLA, DLP" Description="MSLA/DLP, file analysis, repair, conversion and manipulation" InstallScope="perMachine" Platform="x64" /> <Directory Id="TARGETDIR" Name="SourceDir"> @@ -1557,8 +1557,8 @@ <Component Id="owc3110926C9437495884D1A256F611E7C5" Guid="3110926C-9437-4958-84D1-A256F611E7C5"> <File Id="owf3110926C9437495884D1A256F611E7C5" Source="$(var.SourceDir)\Microsoft.Toolkit.HighPerformance.dll" KeyPath="yes" /> </Component> - <Component Id="owcCD68D9AAF66447D9A67F431CEFC402E2" Guid="CD68D9AA-F664-47D9-A67F-431CEFC402E2"> - <File Id="owfCD68D9AAF66447D9A67F431CEFC402E2" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_6.0.622.26707.dll" KeyPath="yes" /> + <Component Id="owc6FEB071413A045D9BB5F9C092F36EA8A" Guid="6FEB0714-13A0-45D9-BB5F-9C092F36EA8A"> + <File Id="owf6FEB071413A045D9BB5F9C092F36EA8A" Source="$(var.SourceDir)\mscordaccore_amd64_amd64_6.0.722.32202.dll" KeyPath="yes" /> </Component> </Directory> <Directory Id="ProgramMenuFolder"> diff --git a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs index 16d5911..40b7923 100644 --- a/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolMaskControl.axaml.cs @@ -53,7 +53,7 @@ public class ToolMaskControl : ToolControl } public string InfoPrinterResolutionStr => $"Printer resolution: {App.SlicerFile.Resolution}"; - public string InfoMaskResolutionStr => $"Mask resolution: "+ (Operation.HaveMask ? Operation.Mask.Size.ToString() : "(Unloaded)"); + public string InfoMaskResolutionStr => $"Mask resolution: "+ (Operation.HaveInputMask ? Operation.Mask.Size.ToString() : "(Unloaded)"); public Bitmap MaskImage { @@ -106,28 +106,7 @@ public class ToolMaskControl : ToolControl try { - Operation.Mask = CvInvoke.Imread(result[0], ImreadModes.Grayscale); - var roi = App.MainWindow.ROI; - if (roi.IsEmpty) - { - if (Operation.Mask.Size != App.SlicerFile.Resolution) - { - CvInvoke.Resize(Operation.Mask, Operation.Mask, App.SlicerFile.Resolution); - } - } - else - { - if (Operation.Mask.Size != roi.Size) - { - CvInvoke.Resize(Operation.Mask, Operation.Mask, roi.Size); - } - } - - if (_isMaskInverted) - { - Operation.InvertMask(); - } - + Operation.LoadFromFile(result[0], _isMaskInverted, App.MainWindow.ROI.Size.IsEmpty ? SlicerFile.Resolution : App.MainWindow.ROI.Size); MaskImage = Operation.Mask.ToBitmap(); } catch (Exception e) @@ -164,8 +143,7 @@ public class ToolMaskControl : ToolControl CvInvoke.Circle(Operation.Mask, center, (int)i, new MCvScalar(color), 2); } - if (_isMaskInverted) - Operation.InvertMask(); + if (_isMaskInverted) Operation.InvertMask(); MaskImage = Operation.Mask.ToBitmap(); } }
\ No newline at end of file diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index ed04fb6..9aeb018 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -115,13 +115,21 @@ Items="{Binding MenuFileConvertItems}" i:MenuItem.Icon="fa-solid fa-exchange-alt"/> + <MenuItem Name="MainMenu.File.FreeUnusedRAM" + Header="Free unused RAM" + ToolTip.Tip="Force the garbage collection of all unused objects within the program to free unused memory (RAM). +
It's never required for the end user run this. The program will automatically take care of it when required. +
This function is for debug purposes." + Command="{Binding OnMenuFileFreeUnusedRAM}" + i:MenuItem.Icon="fa-solid fa-memory"/> + <Separator/> - <MenuItem Name="MainMenu.File.Fullscreen" - Header="_Fullscreen" - InputGesture="F11" HotKey="F11" - Command="{Binding OnMenuFileFullscreen}" - i:MenuItem.Icon="fa-solid fa-window-maximize"/> + <MenuItem Name="MainMenu.File.Fullscreen" + Header="_Fullscreen" + InputGesture="F11" HotKey="F11" + Command="{Binding OnMenuFileFullscreen}" + i:MenuItem.Icon="fa-solid fa-window-maximize"/> <MenuItem Name="MainMenu.File.Settings" Header="_Settings" diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index db22d89..e5c7d0c 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -1069,6 +1069,12 @@ public partial class MainWindow : WindowEx ResetDataContext(); } + public void OnMenuFileFreeUnusedRAM() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + public void OnMenuFileFullscreen() { WindowState = WindowState == WindowState.FullScreen ? WindowState.Maximized : WindowState.FullScreen; @@ -1233,7 +1239,7 @@ public partial class MainWindow : WindowEx if (IsFileLoaded) { - title += $"File: {SlicerFile.Filename} ({Math.Round(LastStopWatch.ElapsedMilliseconds / 1000m, 2)}s) "; + title += $"File: {SlicerFile.Filename} ({LastStopWatch.Elapsed.Minutes}m {LastStopWatch.Elapsed.Seconds}s) "; } title += $"Version: {About.VersionStr} RAM: {SizeExtensions.SizeSuffix(Environment.WorkingSet)}"; diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index fe54b0d..e7f2cf2 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -12,7 +12,7 @@ <PackageLicenseFile>LICENSE</PackageLicenseFile> <RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl> <RepositoryType>Git</RepositoryType> - <Version>3.5.3</Version> + <Version>3.5.4</Version> <Platforms>AnyCPU;x64</Platforms> <PackageIcon>UVtools.png</PackageIcon> <PackageReadmeFile>README.md</PackageReadmeFile> @@ -39,10 +39,10 @@ <NoWarn>1701;1702;</NoWarn> </PropertyGroup> <ItemGroup> - <PackageReference Include="Avalonia" Version="0.10.15" /> - <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.15" /> - <PackageReference Include="Avalonia.Desktop" Version="0.10.15" /> - <PackageReference Include="Avalonia.Diagnostics" Version="0.10.15" /> + <PackageReference Include="Avalonia" Version="0.10.16" /> + <PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.16" /> + <PackageReference Include="Avalonia.Desktop" Version="0.10.16" /> + <PackageReference Include="Avalonia.Diagnostics" Version="0.10.16" /> <PackageReference Include="MessageBox.Avalonia" Version="2.0.1" /> <PackageReference Include="Projektanker.Icons.Avalonia" Version="5.1.0" /> <PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" Version="5.1.0" /> |