From 67fbec1fff373a6ca6ae9f512eb28fe3fc379bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Fri, 24 Jun 2022 04:53:54 +0100 Subject: v3.5.1 - **PCB Exposure:** - (Fix) Able to omit X or Y coordinate and use last known value in place - (Fix) Reusing macros in apertures was causing to use the last aperture values for all previous apertures that share the same macro name - (Fix) Implement the inch measurement (%MOIN*%) - (Fix) Crash when redo the operation (Ctrl + Shift + Z) - **UI - Issue list:** - (Add) Context menu when right click issues to select an action - (Add) Option to solidify suction cups when right click on the issue - (Improvement) Better confirmation text when click on remove issue(s) with detailed list of actions --- CHANGELOG.md | 12 ++ RELEASE_NOTES.md | 23 ++-- UVtools.Core/Gerber/Apertures/Aperture.cs | 32 ++++-- UVtools.Core/Gerber/Apertures/CircleAperture.cs | 7 +- UVtools.Core/Gerber/Apertures/EllipseAperture.cs | 10 +- UVtools.Core/Gerber/Apertures/MacroAperture.cs | 9 +- UVtools.Core/Gerber/Apertures/PoygonAperture.cs | 6 +- UVtools.Core/Gerber/Apertures/RectangleAperture.cs | 12 +- UVtools.Core/Gerber/GerberDocument.cs | 43 ++++++- UVtools.Core/Gerber/Macro.cs | 11 ++ .../Gerber/Primitives/CenterLinePrimitive.cs | 7 +- UVtools.Core/Gerber/Primitives/CirclePrimitive.cs | 5 +- UVtools.Core/Gerber/Primitives/CommentPrimitive.cs | 2 +- UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs | 13 ++- UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs | 7 +- UVtools.Core/Gerber/Primitives/Primitive.cs | 5 +- .../Gerber/Primitives/VectorLinePrimitive.cs | 9 +- UVtools.Core/Layers/MainIssue.cs | 9 ++ UVtools.Core/Operations/OperationPCBExposure.cs | 6 + UVtools.Core/Operations/OperationSolidify.cs | 2 +- UVtools.Core/UVtools.Core.csproj | 2 +- UVtools.InstallerMM/UVtools.InstallerMM.wxs | 2 +- .../Controls/Tools/ToolPCBExposureControl.axaml.cs | 2 +- UVtools.WPF/MainWindow.Issues.cs | 124 +++++++++++++++++++-- UVtools.WPF/MainWindow.axaml | 73 ++++++------ UVtools.WPF/UVtools.WPF.csproj | 2 +- 26 files changed, 325 insertions(+), 110 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 714d4f4..47408b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 24/06/2022 - v3.5.1 + +- **PCB Exposure:** + - (Fix) Able to omit X or Y coordinate and use last known value in place + - (Fix) Reusing macros in apertures was causing to use the last aperture values for all previous apertures that share the same macro name + - (Fix) Implement the inch measurement (%MOIN*%) + - (Fix) Crash when redo the operation (Ctrl + Shift + Z) +- **UI - Issue list:** + - (Add) Context menu when right click issues to select an action + - (Add) Option to solidify suction cups when right click on the issue + - (Improvement) Better confirmation text when click on remove issue(s) with detailed list of actions + ## 19/06/2022 - v3.5.0 - **PCB Exposure:** diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 9c7b302..0dd9480 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,17 +1,10 @@ - **PCB Exposure:** - - (Add) Able to select multiple files and create a layer per file or merge them into one layer - - (Add) Able to import files from a zip file - - (Improvement) Round pixel coordinates and line thickness - - (Improvement) Better position precision for primitives - - (Improvement) Disable the ok button if no files were selected - - (Change) Do not auto mirror based on printer lcd mirror type - - (Fix) Limit line thickness to 1px minimum - - (Fix) Allow leading zero omit from XY coordinates (#492) - - (Fix) Mirror option was shifting the board position -- (Add) Calibrate - Blooming effect: Generates test models with various strategies and increments to measure the blooming effect -- (Add) Setting: Issues - Default order by, changes the default order on the issues list (#482) -- (Improvement) CTBv4 and encrypted: Fetch `BottomWaitTimes` virtual property from first bottom layer that has at least 2 pixels (#483) -- (Fix) Linux: Enable desktop integration for AppImages (#490) -- (Fix) Extracting zip contents inside folders would cause a error and not extract those contents -- (Upgrade) AvaloniaUI from 0.10.14 to 0.10.15 [Fixes auto-size problems] + - (Fix) Able to omit X or Y coordinate and use last known value in place + - (Fix) Reusing macros in apertures was causing to use the last aperture values for all previous apertures that share the same macro name + - (Fix) Implement the inch measurement (%MOIN*%) + - (Fix) Crash when redo the operation (Ctrl + Shift + Z) +- **UI - Issue list:** + - (Add) Context menu when right click issues to select an action + - (Add) Option to solidify suction cups when right click on the issue + - (Improvement) Better confirmation text when click on remove issue(s) with detailed list of actions diff --git a/UVtools.Core/Gerber/Apertures/Aperture.cs b/UVtools.Core/Gerber/Apertures/Aperture.cs index 68cb3c6..39bff36 100644 --- a/UVtools.Core/Gerber/Apertures/Aperture.cs +++ b/UVtools.Core/Gerber/Apertures/Aperture.cs @@ -13,6 +13,7 @@ using System.Text.RegularExpressions; using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; +using UVtools.Core.Extensions; namespace UVtools.Core.Gerber.Apertures; @@ -29,13 +30,24 @@ public abstract class Aperture /// public string Name { get; set; } = string.Empty; + public GerberDocument Document { get; set; } = null!; + #endregion protected Aperture() { } - protected Aperture(int index) { Index = index; } - protected Aperture(string name) { Name = name; } - protected Aperture(int index, string name) : this(index) { Name = name; } + protected Aperture(GerberDocument document, int index) + { + Document = document; + Index = index; + } + + protected Aperture(GerberDocument document, string name) + { + Document = document; + Name = name; + } + protected Aperture(GerberDocument document, int index, string name) : this(document, index) { Name = name; } public abstract void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected); @@ -47,13 +59,12 @@ public abstract class Aperture if (!int.TryParse(match.Groups[1].Value, out var index)) return null; //if (!char.TryParse(match.Groups[2].Value, out var type)) return null; - switch (match.Groups[2].Value) { case "C": { if (!double.TryParse(match.Groups[3].Value, out var diameter)) return null; - return new CircleAperture(index, diameter); + return new CircleAperture(document, index, diameter); } case "O": { @@ -62,7 +73,7 @@ public abstract class Aperture if (!float.TryParse(split[0], out var width)) return null; if (!float.TryParse(split[1], out var height)) return null; - return new EllipseAperture(index, width, height); + return new EllipseAperture(document, index, width, height); } case "R": { @@ -71,7 +82,7 @@ public abstract class Aperture if (!float.TryParse(split[0], out var width)) return null; if (!float.TryParse(split[1], out var height)) return null; - return new RectangleAperture(index, width, height); + return new RectangleAperture(document, index, width, height); } case "P": { @@ -80,11 +91,12 @@ public abstract class Aperture if (!double.TryParse(split[0], out var diameter)) return null; if (!ushort.TryParse(split[1], out var vertices)) return null; - return new PolygonAperture(index, diameter, vertices); + return new PolygonAperture(document, index, diameter, vertices); } default: // macro { 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; @@ -92,10 +104,10 @@ public abstract class Aperture var args = new[] {"0"}.Concat(parseLine.Split('X', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)).ToArray(); foreach (var primitive in macro) { - primitive.ParseExpressions(args); + primitive.ParseExpressions(document, args); } - return new MacroAperture(index, macro); + return new MacroAperture(document, index, macro); } } } diff --git a/UVtools.Core/Gerber/Apertures/CircleAperture.cs b/UVtools.Core/Gerber/Apertures/CircleAperture.cs index 2f00787..0071b0f 100644 --- a/UVtools.Core/Gerber/Apertures/CircleAperture.cs +++ b/UVtools.Core/Gerber/Apertures/CircleAperture.cs @@ -6,7 +6,6 @@ * of this license document, but changing it is not allowed. */ -using System; using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; @@ -22,11 +21,11 @@ public class CircleAperture : Aperture #endregion #region Constructor - public CircleAperture() : base("Circle") { } + public CircleAperture(GerberDocument document) : base(document, "Circle") { } - public CircleAperture(int index, double diameter) : base(index, "Circle") + public CircleAperture(GerberDocument document, int index, double diameter) : base(document, index, "Circle") { - Diameter = diameter; + Diameter = document.GetMillimeters(diameter); } #endregion diff --git a/UVtools.Core/Gerber/Apertures/EllipseAperture.cs b/UVtools.Core/Gerber/Apertures/EllipseAperture.cs index 7ac6f2e..c2d967e 100644 --- a/UVtools.Core/Gerber/Apertures/EllipseAperture.cs +++ b/UVtools.Core/Gerber/Apertures/EllipseAperture.cs @@ -20,16 +20,16 @@ public class EllipseAperture : Aperture #endregion #region Constructor - public EllipseAperture() : base("Ellipse") { } + public EllipseAperture(GerberDocument document) : base(document, "Ellipse") { } - public EllipseAperture(int index, float width, float height) : base(index, "Ellipse") + public EllipseAperture(GerberDocument document, int index, float width, float height) : this(document, index, new SizeF(width, height)) { - Axes = new SizeF(width, height); + } - public EllipseAperture(int index, SizeF axes) : base(index, "Ellipse") + public EllipseAperture(GerberDocument document, int index, SizeF axes) : base(document, index, "Ellipse") { - Axes = axes; + Axes = document.GetMillimeters(axes); } #endregion diff --git a/UVtools.Core/Gerber/Apertures/MacroAperture.cs b/UVtools.Core/Gerber/Apertures/MacroAperture.cs index 1f4cea1..af403cb 100644 --- a/UVtools.Core/Gerber/Apertures/MacroAperture.cs +++ b/UVtools.Core/Gerber/Apertures/MacroAperture.cs @@ -21,9 +21,9 @@ public class MacroAperture : Aperture #endregion #region Constructor - public MacroAperture() : base("Macro") { } + public MacroAperture(GerberDocument document) : base(document, "Macro") { } - public MacroAperture(int index, Macro macro) : base(index, "Macro") + public MacroAperture(GerberDocument document, int index, Macro macro) : base(document, index, "Macro") { Macro = macro; } @@ -31,9 +31,10 @@ public class MacroAperture : Aperture public override void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected) { - foreach (var macro in Macro) + foreach (var primitive in Macro) { - macro.DrawFlashD3(mat, xyPpmm, at, color, lineType); + //if(primitive.Name == "Comment") continue; + primitive.DrawFlashD3(mat, xyPpmm, at, color, lineType); } } } \ No newline at end of file diff --git a/UVtools.Core/Gerber/Apertures/PoygonAperture.cs b/UVtools.Core/Gerber/Apertures/PoygonAperture.cs index e10c1c2..0b8bb9a 100644 --- a/UVtools.Core/Gerber/Apertures/PoygonAperture.cs +++ b/UVtools.Core/Gerber/Apertures/PoygonAperture.cs @@ -23,11 +23,11 @@ public class PolygonAperture : Aperture #endregion #region Constructor - public PolygonAperture() : base("Polygon") { } + public PolygonAperture(GerberDocument document) : base(document, "Polygon") { } - public PolygonAperture(int index, double diameter, ushort vertices) : base(index, "Polygon") + public PolygonAperture(GerberDocument document, int index, double diameter, ushort vertices) : base(document, index, "Polygon") { - Diameter = diameter; + Diameter = document.GetMillimeters(diameter); Vertices = vertices; } #endregion diff --git a/UVtools.Core/Gerber/Apertures/RectangleAperture.cs b/UVtools.Core/Gerber/Apertures/RectangleAperture.cs index 5dbd5a2..3be7bc4 100644 --- a/UVtools.Core/Gerber/Apertures/RectangleAperture.cs +++ b/UVtools.Core/Gerber/Apertures/RectangleAperture.cs @@ -21,16 +21,14 @@ public class RectangleAperture : Aperture #endregion #region Constructor - public RectangleAperture() : base("Rectangle") { } + public RectangleAperture(GerberDocument document) : base(document, "Rectangle") { } - public RectangleAperture(int index, float width, float height) : base(index, "Rectangle") - { - Size = new SizeF(width, height); - } + public RectangleAperture(GerberDocument document, int index, float width, float height) : this(document, index, new SizeF(width, height)) + { } - public RectangleAperture(int index, SizeF size) : base(index, "Rectangle") + public RectangleAperture(GerberDocument document, int index, SizeF size) : base(document, index, "Rectangle") { - Size = size; + Size = document.GetMillimeters(size); } #endregion diff --git a/UVtools.Core/Gerber/GerberDocument.cs b/UVtools.Core/Gerber/GerberDocument.cs index e150e24..3bd3887 100644 --- a/UVtools.Core/Gerber/GerberDocument.cs +++ b/UVtools.Core/Gerber/GerberDocument.cs @@ -13,6 +13,7 @@ using System.IO; using System.Text.RegularExpressions; using Emgu.CV; using Emgu.CV.CvEnum; +using Emgu.CV.Structure; using Emgu.CV.Util; using UVtools.Core.Extensions; using UVtools.Core.Gerber.Apertures; @@ -166,6 +167,8 @@ public class GerberDocument using var vec = new VectorOfPoint(regionPoints.ToArray()); CvInvoke.FillPoly(mat, vec, document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor, enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected); } + //CvInvoke.Imshow("G37", mat); + //CvInvoke.WaitKey(); regionPoints.Clear(); continue; } @@ -228,9 +231,15 @@ public class GerberDocument var fraction = matchX.Groups[1].Value.Substring(matchX.Groups[1].Value.Length - document.CoordinateXFractionalDigits, document.CoordinateXFractionalDigits); double.TryParse($"{integers}.{fraction}", out nowX); } + + nowX = document.GetMillimeters(nowX); } } } + else + { + nowX = currentX; + } if (matchY.Success && matchY.Groups.Count >= 2) { @@ -248,9 +257,14 @@ public class GerberDocument var fraction = matchY.Groups[1].Value.Substring(matchY.Groups[1].Value.Length - document.CoordinateYFractionalDigits, document.CoordinateYFractionalDigits); double.TryParse($"{integers}.{fraction}", out nowY); } + nowY = document.GetMillimeters(nowY); } } } + else + { + nowY = currentY; + } if (insideRegion) { @@ -294,7 +308,7 @@ public class GerberDocument var fraction = matchI.Groups[1].Value.Substring(matchI.Groups[1].Value.Length - document.CoordinateXFractionalDigits, document.CoordinateXFractionalDigits); double.TryParse($"{integers}.{fraction}", out xOffset); } - + xOffset = document.GetMillimeters(xOffset); } } @@ -312,6 +326,7 @@ public class GerberDocument var fraction = matchJ.Groups[1].Value.Substring(matchJ.Groups[1].Value.Length - document.CoordinateYFractionalDigits, document.CoordinateYFractionalDigits); double.TryParse($"{integers}.{fraction}", out yOffset); } + yOffset = document.GetMillimeters(yOffset); } } @@ -354,6 +369,8 @@ public class GerberDocument { currentAperture.DrawFlashD3(mat, xyPpmm, new PointF((float) nowX, (float) nowY), document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor, enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected); + //CvInvoke.Imshow("G37", mat); + //CvInvoke.WaitKey(); } } @@ -366,6 +383,30 @@ public class GerberDocument return document; } + public float GetMillimeters(float size) + { + if (UnitType == GerberUnitType.Millimeter) return size; + return size * 25.4f; + } + + public double GetMillimeters(double size) + { + if (UnitType == GerberUnitType.Millimeter) return size; + return size * 25.4; + } + + public SizeF GetMillimeters(SizeF size) + { + if (UnitType == GerberUnitType.Millimeter) return size; + return new SizeF(size.Width * 25.4f, size.Height * 25.4f); + } + + public PointF GetMillimeters(PointF point) + { + if (UnitType == GerberUnitType.Millimeter) return point; + return new PointF(point.X * 25.4f, point.Y * 25.4f); + } + public static Point PositionMmToPx(PointF atMm, SizeF xyPpmm) => new((int)Math.Round(atMm.X * xyPpmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round(atMm.Y * xyPpmm.Height, MidpointRounding.AwayFromZero)); diff --git a/UVtools.Core/Gerber/Macro.cs b/UVtools.Core/Gerber/Macro.cs index 6d9a013..6a5d3c1 100644 --- a/UVtools.Core/Gerber/Macro.cs +++ b/UVtools.Core/Gerber/Macro.cs @@ -89,6 +89,17 @@ public class Macro : IReadOnlyList } } + public Macro Clone() + { + var macro = new Macro(Name); + foreach (var primitive in Primitives) + { + macro.Primitives.Add(primitive.Clone()); + } + + return macro; + } + public static Macro? Parse(string line) { diff --git a/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs b/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs index 5ac51fd..6e6d751 100644 --- a/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/CenterLinePrimitive.cs @@ -6,7 +6,6 @@ * of this license document, but changing it is not allowed. */ -using System; using System.Data; using System.Drawing; using System.Text.RegularExpressions; @@ -104,7 +103,7 @@ public class CenterLinePrimitive : Primitive //CvInvoke.Rectangle(mat, rectangle, color, -1, lineType); } - public override void ParseExpressions(params string[] args) + public override void ParseExpressions(GerberDocument document, params string[] args) { string csharpExp, result; float num; @@ -126,6 +125,7 @@ public class CenterLinePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out var val)) Width = val; } + Width = document.GetMillimeters(Width); if (float.TryParse(HeightExpression, out num)) Height = num; else @@ -135,6 +135,7 @@ public class CenterLinePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out var val)) Height = val; } + Height = document.GetMillimeters(Height); if (float.TryParse(CenterXExpression, out num)) CenterX = num; else @@ -144,6 +145,7 @@ public class CenterLinePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) CenterX = num; } + CenterX = document.GetMillimeters(CenterX); if (float.TryParse(CenterYExpression, out num)) CenterY = num; else @@ -153,6 +155,7 @@ public class CenterLinePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) CenterY = num; } + CenterY = document.GetMillimeters(CenterY); if (float.TryParse(RotationExpression, out num)) Rotation = (short)num; else diff --git a/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs b/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs index 601cf0f..5364e0d 100644 --- a/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/CirclePrimitive.cs @@ -92,7 +92,7 @@ public class CirclePrimitive : Primitive GerberDocument.SizeMmToPx(Diameter / 2, xyPpmm.Max()), color, -1, lineType); } - public override void ParseExpressions(params string[] args) + public override void ParseExpressions(GerberDocument document, params string[] args) { string csharpExp, result; float num; @@ -114,6 +114,7 @@ public class CirclePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) Diameter = num; } + Diameter = document.GetMillimeters(Diameter); if (float.TryParse(CenterXExpression, out num)) CenterX = num; else @@ -123,6 +124,7 @@ public class CirclePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) CenterX = num; } + CenterX = document.GetMillimeters(CenterX); if (float.TryParse(CenterYExpression, out num)) CenterY = num; else @@ -132,6 +134,7 @@ public class CirclePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) CenterY = num; } + CenterY = document.GetMillimeters(CenterY); if (float.TryParse(RotationExpression, out num)) Rotation = (short)num; diff --git a/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs b/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs index 25a9b07..a909911 100644 --- a/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/CommentPrimitive.cs @@ -50,7 +50,7 @@ public class CommentPrimitive : Primitive } - public override void ParseExpressions(params string[] args) + public override void ParseExpressions(GerberDocument document, params string[] args) { } } \ No newline at end of file diff --git a/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs b/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs index 93a505a..80ecfaf 100644 --- a/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/OutlinePrimitive.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Data; using System.Drawing; +using System.Linq; using System.Text.RegularExpressions; using Emgu.CV; using Emgu.CV.CvEnum; @@ -94,7 +95,7 @@ public class OutlinePrimitive : Primitive CvInvoke.FillPoly(mat, vec, color, lineType); } - public override void ParseExpressions(params string[] args) + public override void ParseExpressions(GerberDocument document, params string[] args) { string csharpExp, result; float num; @@ -125,7 +126,7 @@ public class OutlinePrimitive : Primitive } else { - coordinates.Add(new PointF(x.Value, num)); + coordinates.Add(document.GetMillimeters(new PointF(x.Value, num))); x = null; } } @@ -143,4 +144,12 @@ public class OutlinePrimitive : Primitive IsParsed = true; } + + public override Primitive Clone() + { + var primitive = MemberwiseClone() as OutlinePrimitive; + primitive!.CoordinatesExpression = primitive.CoordinatesExpression.ToArray(); + primitive.Coordinates = primitive.Coordinates.ToArray(); + return primitive; + } } \ No newline at end of file diff --git a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs index 1657336..2bb7289 100644 --- a/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/PolygonPrimitive.cs @@ -6,7 +6,6 @@ * of this license document, but changing it is not allowed. */ -using System; using System.Data; using System.Drawing; using System.Text.RegularExpressions; @@ -101,7 +100,7 @@ public class PolygonPrimitive : Primitive color, 0, -1, lineType); } - public override void ParseExpressions(params string[] args) + public override void ParseExpressions(GerberDocument document, params string[] args) { string csharpExp, result; float num; @@ -132,6 +131,7 @@ public class PolygonPrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) CenterX = num; } + CenterX = document.GetMillimeters(CenterX); if (float.TryParse(CenterYExpression, out num)) CenterY = num; else @@ -141,6 +141,7 @@ public class PolygonPrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) CenterY = num; } + CenterY = document.GetMillimeters(CenterY); if (float.TryParse(DiameterExpression, out num)) Diameter = num; else @@ -150,7 +151,7 @@ public class PolygonPrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) Diameter = num; } - + Diameter = document.GetMillimeters(Diameter); if (float.TryParse(RotationExpression, out num)) Rotation = (short)num; else diff --git a/UVtools.Core/Gerber/Primitives/Primitive.cs b/UVtools.Core/Gerber/Primitives/Primitive.cs index 9658a24..0a7740f 100644 --- a/UVtools.Core/Gerber/Primitives/Primitive.cs +++ b/UVtools.Core/Gerber/Primitives/Primitive.cs @@ -6,6 +6,7 @@ * of this license document, but changing it is not allowed. */ +using System; using System.Drawing; using Emgu.CV; using Emgu.CV.CvEnum; @@ -26,5 +27,7 @@ public abstract class Primitive public abstract void DrawFlashD3(Mat mat, SizeF xyPpmm, PointF at, MCvScalar color, LineType lineType = LineType.EightConnected); - public abstract void ParseExpressions(params string[] args); + public abstract void ParseExpressions(GerberDocument document, params string[] args); + + public virtual Primitive Clone() => (Primitive)MemberwiseClone(); } \ No newline at end of file diff --git a/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs b/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs index 94f712a..9916a1d 100644 --- a/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs +++ b/UVtools.Core/Gerber/Primitives/VectorLinePrimitive.cs @@ -6,7 +6,6 @@ * of this license document, but changing it is not allowed. */ -using System; using System.Data; using System.Drawing; using System.Text.RegularExpressions; @@ -112,7 +111,7 @@ public class VectorLinePrimitive : Primitive //CvInvoke.Rectangle(mat, rectangle, color, -1, lineType); } - public override void ParseExpressions(params string[] args) + public override void ParseExpressions(GerberDocument document, params string[] args) { string csharpExp, result; float num; @@ -134,6 +133,7 @@ public class VectorLinePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out var val)) LineWidth = val; } + LineWidth = document.GetMillimeters(LineWidth); if (float.TryParse(StartXExpression, out num)) StartX = num; else @@ -143,6 +143,7 @@ public class VectorLinePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) StartX = num; } + StartX = document.GetMillimeters(StartX); if (float.TryParse(EndXExpression, out num)) EndX = num; else @@ -152,6 +153,7 @@ public class VectorLinePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) EndX = num; } + EndX = document.GetMillimeters(EndX); if (float.TryParse(StartYExpression, out num)) StartY = num; else @@ -161,6 +163,7 @@ public class VectorLinePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) StartY = num; } + StartY = document.GetMillimeters(StartY); if (float.TryParse(EndYExpression, out num)) EndY = num; else @@ -170,7 +173,7 @@ public class VectorLinePrimitive : Primitive result = exp.Compute(csharpExp, null).ToString()!; if (float.TryParse(result, out num)) EndY = num; } - + EndY = document.GetMillimeters(EndY); if (float.TryParse(RotationExpression, out num)) Rotation = (short)num; else diff --git a/UVtools.Core/Layers/MainIssue.cs b/UVtools.Core/Layers/MainIssue.cs index fbc9ec9..8b7cf7e 100644 --- a/UVtools.Core/Layers/MainIssue.cs +++ b/UVtools.Core/Layers/MainIssue.cs @@ -30,6 +30,15 @@ public class MainIssue : IReadOnlyList //HoleSandwich, } + public bool IsIsland => Type == IssueType.Island; + public bool IsOverhang => Type == IssueType.Overhang; + public bool IsResinTrap => Type == IssueType.ResinTrap; + public bool IsSuctionCup => Type == IssueType.SuctionCup; + public bool IsTouchingBound => Type == IssueType.TouchingBound; + public bool IsPrintHeight => Type == IssueType.PrintHeight; + public bool IsEmptyLayer => Type == IssueType.EmptyLayer; + public bool IsDebug => Type == IssueType.Debug; + /// /// Gets the issue type associated /// diff --git a/UVtools.Core/Operations/OperationPCBExposure.cs b/UVtools.Core/Operations/OperationPCBExposure.cs index 7df3085..96fd82d 100644 --- a/UVtools.Core/Operations/OperationPCBExposure.cs +++ b/UVtools.Core/Operations/OperationPCBExposure.cs @@ -313,6 +313,12 @@ public class OperationPCBExposure : Operation if (mergeMat is not null) { using var croppedMat = mergeMat.CropByBounds(20); + /*if (_mirror) + { + var flip = SlicerFile.DisplayMirror; + if (flip == FlipDirection.None) flip = FlipDirection.Horizontally; + CvInvoke.Flip(croppedMat, croppedMat, (FlipType)flip); + }*/ using var bgrMat = new Mat(); CvInvoke.CvtColor(croppedMat, bgrMat, ColorConversion.Gray2Bgr); SlicerFile.SetThumbnails(bgrMat); diff --git a/UVtools.Core/Operations/OperationSolidify.cs b/UVtools.Core/Operations/OperationSolidify.cs index ba2426e..c3e68e3 100644 --- a/UVtools.Core/Operations/OperationSolidify.cs +++ b/UVtools.Core/Operations/OperationSolidify.cs @@ -34,7 +34,7 @@ public sealed class OperationSolidify : Operation #region Overrides - public override string IconClass => "fa-solid fa-circle"; + public override string IconClass => "fa-solid fa-diamond"; public override string Title => "Solidify"; public override string Description => diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 1b93a05..eb96200 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.5.0 + 3.5.1 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 diff --git a/UVtools.InstallerMM/UVtools.InstallerMM.wxs b/UVtools.InstallerMM/UVtools.InstallerMM.wxs index 848eea0..03b3cc7 100644 --- a/UVtools.InstallerMM/UVtools.InstallerMM.wxs +++ b/UVtools.InstallerMM/UVtools.InstallerMM.wxs @@ -2,7 +2,7 @@ - + diff --git a/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml.cs index f729167..4bcebac 100644 --- a/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolPCBExposureControl.axaml.cs @@ -77,7 +77,7 @@ namespace UVtools.WPF.Controls.Tools }; _timer.Stop(); _timer.Start(); - ParentWindow.ButtonOkEnabled = Operation.FileCount > 0; + if(ParentWindow is not null) ParentWindow.ButtonOkEnabled = Operation.FileCount > 0; Operation.Files.CollectionChanged += (sender, e) => ParentWindow.ButtonOkEnabled = Operation.FileCount > 0; break; } diff --git a/UVtools.WPF/MainWindow.Issues.cs b/UVtools.WPF/MainWindow.Issues.cs index 6141baf..08435ff 100644 --- a/UVtools.WPF/MainWindow.Issues.cs +++ b/UVtools.WPF/MainWindow.Issues.cs @@ -115,20 +115,65 @@ public partial class MainWindow return retValue; }*/ - public async void OnClickIssueRemove() + public async void RemoveRepairIssues(IEnumerable issues, bool promptConfirmation = true, bool suctionCupDrill = true) { - if (IssuesGrid.SelectedItems.Count == 0) return; + if (!issues.Any()) return; + + uint emptyLayers = 0; + uint islands = 0; + uint resinTraps = 0; + uint suctionCups = 0; + foreach (MainIssue mainIssue in issues) + { + switch (mainIssue.Type) + { + case MainIssue.IssueType.Island: + islands++; + break; + case MainIssue.IssueType.Overhang: + // No action + break; + case MainIssue.IssueType.ResinTrap: + resinTraps++; + break; + case MainIssue.IssueType.SuctionCup: + suctionCups++; + break; + case MainIssue.IssueType.TouchingBound: + // No action + break; + case MainIssue.IssueType.PrintHeight: + // No action + break; + case MainIssue.IssueType.EmptyLayer: + emptyLayers++; + break; + case MainIssue.IssueType.Debug: + // No action + break; + default: + throw new ArgumentOutOfRangeException(); + } + } - if (await this.MessageBoxQuestion($"Are you sure you want to remove all selected {IssuesGrid.SelectedItems.Count} issues?\n\n" + - "Warning: Removing an island can cause other issues to appear if there is material present in the layers above it.\n" + - "Always check previous and next layers before performing an island removal.", $"Remove {IssuesGrid.SelectedItems.Count} Issues?") != ButtonResult.Yes) return; + if (emptyLayers == 0 && islands == 0 && resinTraps == 0 && suctionCups == 0) return; + + if (promptConfirmation && await this.MessageBoxQuestion( + $"Are you sure you want to remove and/or repair all selected {issues.Count()} issues?\n\n" + + "If any, this option will:\n" + + (emptyLayers > 0 ? $"- Remove {emptyLayers} empty layer(s)\n" : string.Empty) + + (islands > 0 ? $"- Remove {emptyLayers} island(s)\n" : string.Empty) + + (resinTraps > 0 ? $"- Fill/solidify {resinTraps} resin trap(s)\n" : string.Empty) + + (suctionCups > 0 ? $"- Drill {suctionCups} suction cup(s) at it's center\n" : string.Empty) + + "\nWarning: Removing an island can cause other issues to appear if there is material present in the layers above it.\n" + + "Always check previous and next layers before performing an island removal.", $"Remove {issues.Count()} Issues?") != ButtonResult.Yes) return; var processParallelIssues = new Dictionary>(); var processSuctionCups = new List(); var layersToRemove = new List(); - foreach (MainIssue mainIssue in IssuesGrid.SelectedItems) + foreach (MainIssue mainIssue in issues) { switch (mainIssue.Type) { @@ -148,6 +193,22 @@ public partial class MainWindow } continue; case MainIssue.IssueType.SuctionCup: + if (!suctionCupDrill) + { + foreach (var issue in mainIssue) + { + // Islands and resin traps + if (!processParallelIssues.TryGetValue(issue.LayerIndex, out var issueList)) + { + issueList = new List(); + processParallelIssues.Add(issue.LayerIndex, issueList); + } + + issueList.Add(issue); + + } + continue; + } if(mainIssue.StartLayerIndex == 0) continue; processSuctionCups.Add(mainIssue); continue; @@ -192,7 +253,7 @@ public partial class MainWindow edited = true; } - else if (issue.Type == MainIssue.IssueType.ResinTrap) + else if (issue.Type == MainIssue.IssueType.ResinTrap || (issue.Type == MainIssue.IssueType.SuctionCup && !suctionCupDrill)) { var issueOfContours = (IssueOfContours)issue; using var contours = new VectorOfVectorOfPoint(issueOfContours.Contours); @@ -219,13 +280,13 @@ public partial class MainWindow OperationLayerRemove.RemoveLayers(SlicerFile, layersToRemove); } - issueRemoveList.AddRange(SlicerFile.IssueManager.DrillSuctionCupsForIssues(processSuctionCups, UserSettings.Instance.LayerRepair.SuctionCupsVentHole, Progress)); + if(suctionCupDrill) issueRemoveList.AddRange(SlicerFile.IssueManager.DrillSuctionCupsForIssues(processSuctionCups, UserSettings.Instance.LayerRepair.SuctionCupsVentHole, Progress)); } catch (Exception ex) { Dispatcher.UIThread.InvokeAsync(async () => - await this.MessageBoxError(ex.ToString(), "Removal failed")); + await this.MessageBoxError(ex.ToString(), "Removal/repair failed")); return false; } @@ -245,8 +306,14 @@ public partial class MainWindow // Update GUI - foreach (MainIssue issue in IssuesGrid.SelectedItems) + foreach (MainIssue issue in issues) { + if (issue.IsSuctionCup && !suctionCupDrill) + { + issueRemoveList.Add(issue); + continue; + } + if (issue.Type is not MainIssue.IssueType.Island and not MainIssue.IssueType.ResinTrap @@ -256,7 +323,7 @@ public partial class MainWindow issueRemoveList.Add(issue); - if (issue.Type == MainIssue.IssueType.Island) + if (issue.IsIsland) { var nextLayer = issue.StartLayerIndex + 1; if (nextLayer >= SlicerFile.LayerCount) continue; @@ -289,6 +356,41 @@ public partial class MainWindow CanSave = true; } + public void OnClickIssueRemove() + { + RemoveRepairIssues(IssuesGrid.SelectedItems.OfType(), true); + } + + public void SelectedIssuesIslandRemove() + { + if (IssuesGrid.SelectedItem is null) return; + RemoveRepairIssues(IssuesGrid.SelectedItems.OfType().Where(mainIssue => mainIssue.IsIsland), false); + } + + public void SelectedIssuesResinTrapSolidify() + { + if (IssuesGrid.SelectedItem is null) return; + RemoveRepairIssues(IssuesGrid.SelectedItems.OfType().Where(mainIssue => mainIssue.IsResinTrap), false); + } + + public void SelectedIssuesSuctionCupDrill() + { + if (IssuesGrid.SelectedItem is null) return; + RemoveRepairIssues(IssuesGrid.SelectedItems.OfType().Where(mainIssue => mainIssue.IsSuctionCup), false); + } + + public void SelectedIssuesSuctionCupSolidify() + { + if (IssuesGrid.SelectedItem is null) return; + RemoveRepairIssues(IssuesGrid.SelectedItems.OfType().Where(mainIssue => mainIssue.IsSuctionCup), false, false); + } + + public void SelectedIssuesEmptyLayerRemove() + { + if (IssuesGrid.SelectedItem is null) return; + RemoveRepairIssues(IssuesGrid.SelectedItems.OfType().Where(mainIssue => mainIssue.IsEmptyLayer), false); + } + public async void OnClickIssueIgnore() { if ((_globalModifiers & KeyModifiers.Alt) != 0) diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index 7534f69..ed04fb6 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -236,7 +236,7 @@ - + + Text="{Binding SlicerFile.PrintTimeString, StringFormat=Print time: {0}}"/> + Text="{Binding SlicerFile.MaterialMilliliters, StringFormat=Used material: {0}ml}"/> - + + Text="{Binding SlicerFile.MaterialName, StringFormat=Material: {0}}"/> + Text="{Binding SlicerFile.MachineName, StringFormat=Machine: {0}}"/> @@ -385,13 +385,13 @@ + Text="{Binding SlicerProperties.Count, StringFormat=Properties: {0}}"/> + Text="{Binding SlicerFile.Configs.Length, StringFormat=Groups: {0}}"/> @@ -510,12 +510,12 @@ - @@ -640,19 +640,17 @@ - + -