- **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
This commit is contained in:
Tiago Conceição
2022-06-24 04:53:54 +01:00
parent 97c78a8d8c
commit 67fbec1fff
26 changed files with 325 additions and 110 deletions
+12
View File
@@ -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:**
+8 -15
View File
@@ -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
+22 -10
View File
@@ -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
/// </summary>
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);
}
}
}
@@ -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
@@ -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
@@ -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);
}
}
}
@@ -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
@@ -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
+42 -1
View File
@@ -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));
+11
View File
@@ -89,6 +89,17 @@ public class Macro : IReadOnlyList<Primitive>
}
}
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)
{
@@ -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
@@ -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;
@@ -50,7 +50,7 @@ public class CommentPrimitive : Primitive
}
public override void ParseExpressions(params string[] args)
public override void ParseExpressions(GerberDocument document, params string[] args)
{
}
}
@@ -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;
}
}
@@ -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
+4 -1
View File
@@ -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();
}
@@ -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
+9
View File
@@ -30,6 +30,15 @@ public class MainIssue : IReadOnlyList<Issue>
//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;
/// <summary>
/// Gets the issue type associated
/// </summary>
@@ -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);
+1 -1
View File
@@ -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 =>
+1 -1
View File
@@ -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.0</Version>
<Version>3.5.1</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
+1 -1
View File
@@ -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.0"?>
<?define SourceDir="..\publish\UVtools_win-x64_v3.5.1"?>
<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">
@@ -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;
}
+113 -11
View File
@@ -115,20 +115,65 @@ public partial class MainWindow
return retValue;
}*/
public async void OnClickIssueRemove()
public async void RemoveRepairIssues(IEnumerable<MainIssue> issues, bool promptConfirmation = true, bool suctionCupDrill = true)
{
if (IssuesGrid.SelectedItems.Count == 0) return;
if (!issues.Any()) return;
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;
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 (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<uint, List<Issue>>();
var processSuctionCups = new List<MainIssue>();
var layersToRemove = new List<uint>();
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<Issue>();
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<MainIssue>(), true);
}
public void SelectedIssuesIslandRemove()
{
if (IssuesGrid.SelectedItem is null) return;
RemoveRepairIssues(IssuesGrid.SelectedItems.OfType<MainIssue>().Where(mainIssue => mainIssue.IsIsland), false);
}
public void SelectedIssuesResinTrapSolidify()
{
if (IssuesGrid.SelectedItem is null) return;
RemoveRepairIssues(IssuesGrid.SelectedItems.OfType<MainIssue>().Where(mainIssue => mainIssue.IsResinTrap), false);
}
public void SelectedIssuesSuctionCupDrill()
{
if (IssuesGrid.SelectedItem is null) return;
RemoveRepairIssues(IssuesGrid.SelectedItems.OfType<MainIssue>().Where(mainIssue => mainIssue.IsSuctionCup), false);
}
public void SelectedIssuesSuctionCupSolidify()
{
if (IssuesGrid.SelectedItem is null) return;
RemoveRepairIssues(IssuesGrid.SelectedItems.OfType<MainIssue>().Where(mainIssue => mainIssue.IsSuctionCup), false, false);
}
public void SelectedIssuesEmptyLayerRemove()
{
if (IssuesGrid.SelectedItem is null) return;
RemoveRepairIssues(IssuesGrid.SelectedItems.OfType<MainIssue>().Where(mainIssue => mainIssue.IsEmptyLayer), false);
}
public async void OnClickIssueIgnore()
{
if ((_globalModifiers & KeyModifiers.Alt) != 0)
+41 -32
View File
@@ -236,7 +236,7 @@
<WrapPanel
Orientation="Horizontal"
VerticalAlignment="Center">
<TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: \{0\}mm}"/>
<TextBlock Text="{Binding SlicerFile.LayerHeight, StringFormat=Layer height: {0}mm}"/>
<TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}" Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.CanUseBottomLayerCount}"
@@ -266,28 +266,28 @@
<TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}" Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.PrintTimeHours}"
Text="{Binding SlicerFile.PrintTimeString, StringFormat=Print time: \{0\}}"/>
Text="{Binding SlicerFile.PrintTimeString, StringFormat=Print time: {0}}"/>
<TextBlock IsVisible="{Binding SlicerFile.MaterialMilliliters}" Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.MaterialMilliliters}"
Text="{Binding SlicerFile.MaterialMilliliters, StringFormat=Used material: \{0\}ml}"/>
Text="{Binding SlicerFile.MaterialMilliliters, StringFormat=Used material: {0}ml}"/>
<TextBlock IsVisible="{Binding SlicerFile.MaterialCost}" Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.MaterialCost}" Text="{Binding SlicerFile.MaterialCost, StringFormat=Material cost: \{0\}€}"/>
<TextBlock IsVisible="{Binding SlicerFile.MaterialCost}" Text="{Binding SlicerFile.MaterialCost, StringFormat=Material cost: {0}€}"/>
<TextBlock IsVisible="{Binding SlicerFile.MaterialName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.MaterialName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Text="{Binding SlicerFile.MaterialName, StringFormat=Material: \{0\}}"/>
Text="{Binding SlicerFile.MaterialName, StringFormat=Material: {0}}"/>
<TextBlock IsVisible="{Binding SlicerFile.MachineName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Text=" | "/>
<TextBlock IsVisible="{Binding SlicerFile.MachineName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
Text="{Binding SlicerFile.MachineName, StringFormat=Machine: \{0\}}"/>
Text="{Binding SlicerFile.MachineName, StringFormat=Machine: {0}}"/>
</WrapPanel>
</Border>
@@ -385,13 +385,13 @@
<TextBlock
VerticalAlignment="Center"
Text="{Binding SlicerProperties.Count, StringFormat=Properties: \{0\}}"/>
Text="{Binding SlicerProperties.Count, StringFormat=Properties: {0}}"/>
<TextBlock VerticalAlignment="Center" Text="|"/>
<TextBlock
VerticalAlignment="Center"
Text="{Binding SlicerFile.Configs.Length, StringFormat=Groups: \{0\}}"/>
Text="{Binding SlicerFile.Configs.Length, StringFormat=Groups: {0}}"/>
</StackPanel>
@@ -510,12 +510,12 @@
<Grid RowDefinitions="Auto,*">
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="5">
<TextBlock
Text="{Binding GCodeLines, StringFormat=Lines: \{0\}}"
Text="{Binding GCodeLines, StringFormat=Lines: {0}}"
VerticalAlignment="Center"/>
<TextBlock Text="|" VerticalAlignment="Center"/>
<TextBlock Text="{Binding #GCodeText.Text.Length, StringFormat=Chars: \{0\}}"
<TextBlock Text="{Binding #GCodeText.Text.Length, StringFormat=Chars: {0}}"
VerticalAlignment="Center"/>
</StackPanel>
@@ -640,19 +640,17 @@
</StackPanel>
<StackPanel
Grid.Row="0"
Orientation="Horizontal"
Spacing="2"
HorizontalAlignment="Right"
VerticalAlignment="Center">
<StackPanel Grid.Row="0"
Orientation="Horizontal"
Spacing="2"
HorizontalAlignment="Right"
VerticalAlignment="Center">
<Button
IsEnabled="{Binding IsFileLoaded}"
ToolTip.Tip="Attempt to repair issues"
VerticalAlignment="Stretch"
i:Attached.Icon="fa-solid fa-toolbox"
Command="{Binding OnClickRepairIssues}"/>
<Button IsEnabled="{Binding IsFileLoaded}"
ToolTip.Tip="Attempt to repair issues"
VerticalAlignment="Stretch"
i:Attached.Icon="fa-solid fa-toolbox"
Command="{Binding OnClickRepairIssues}"/>
<uc:ButtonWithIcon VerticalAlignment="Stretch"
ToolTip.Tip="Compute Issues.
@@ -736,7 +734,18 @@
IsReadOnly="True"
ClipboardCopyMode="None"
Items="{Binding IssuesGridItems}">
<DataGrid.Columns>
<DataGrid.ContextMenu>
<ContextMenu IsVisible="{Binding #IssuesGrid.SelectedItem, Converter={x:Static ObjectConverters.IsNotNull}}">
<MenuItem i:MenuItem.Icon="fa-solid fa-eye-slash" Command="{Binding OnClickIssueIgnore}" Header="Ignore issue(s)"/>
<Separator/>
<MenuItem i:MenuItem.Icon="fa-solid fa-trash-can" Command="{Binding SelectedIssuesIslandRemove}" IsVisible="{Binding #IssuesGrid.SelectedItem.IsIsland}" Header="Remove island(s)"/>
<MenuItem i:MenuItem.Icon="fa-solid fa-diamond" Command="{Binding SelectedIssuesResinTrapSolidify}" IsVisible="{Binding #IssuesGrid.SelectedItem.IsResinTrap}" Header="Fill/solidify resin trap(s)"/>
<MenuItem i:MenuItem.Icon="fa-solid fa-circle-dot" Command="{Binding SelectedIssuesSuctionCupDrill}" IsVisible="{Binding #IssuesGrid.SelectedItem.IsSuctionCup}" Header="Drill suction cup(s)"/>
<MenuItem i:MenuItem.Icon="fa-solid fa-diamond" Command="{Binding SelectedIssuesSuctionCupSolidify}" IsVisible="{Binding #IssuesGrid.SelectedItem.IsSuctionCup}" Header="Fill/solidify suction cup(s)"/>
<MenuItem i:MenuItem.Icon="fa-solid fa-trash-can" Command="{Binding SelectedIssuesEmptyLayerRemove}" IsVisible="{Binding #IssuesGrid.SelectedItem.IsEmptyLayer}" Header="Remove empty layer(s)"/>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<DataGridTextColumn Header="Type"
Binding="{Binding Type}"
@@ -1003,7 +1012,7 @@
Grid.Row="10"
Grid.Column="4"
VerticalAlignment="Center"
Text="{Binding DrawingPixelDrawing.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" />
Text="{Binding DrawingPixelDrawing.RemovePixelBrightnessPercent, StringFormat={}{0:0}%}" />
<TextBlock
Grid.Row="12"
@@ -1022,7 +1031,7 @@
Grid.Row="12"
Grid.Column="4"
VerticalAlignment="Center"
Text="{Binding DrawingPixelDrawing.PixelBrightnessPercent, StringFormat=\{0:0\}%}" />
Text="{Binding DrawingPixelDrawing.PixelBrightnessPercent, StringFormat={}{0:0}%}" />
<TextBlock
Grid.Row="14"
@@ -1187,7 +1196,7 @@
Grid.Row="16"
Grid.Column="4"
VerticalAlignment="Center"
Text="{Binding DrawingPixelText.RemovePixelBrightnessPercent, StringFormat=\{0:0\}%}" />
Text="{Binding DrawingPixelText.RemovePixelBrightnessPercent, StringFormat={}{0:0}%}" />
<TextBlock
Grid.Row="18"
@@ -1205,7 +1214,7 @@
Grid.Row="18"
Grid.Column="4"
VerticalAlignment="Center"
Text="{Binding DrawingPixelText.PixelBrightnessPercent, StringFormat=\{0:0\}%}" />
Text="{Binding DrawingPixelText.PixelBrightnessPercent, StringFormat={}{0:0}%}" />
<TextBlock
@@ -1269,7 +1278,7 @@
Grid.Row="0"
Grid.Column="4"
VerticalAlignment="Center"
Text="{Binding DrawingPixelEraser.PixelBrightnessPercent, StringFormat=\{0:0\}%}" />
Text="{Binding DrawingPixelEraser.PixelBrightnessPercent, StringFormat={}{0:0}%}" />
<TextBlock
Grid.Row="2"
@@ -1365,7 +1374,7 @@
Grid.Row="6"
Grid.Column="4"
VerticalAlignment="Center"
Text="{Binding DrawingPixelSupport.PixelBrightnessPercent, StringFormat=\{0:0\}%}" />
Text="{Binding DrawingPixelSupport.PixelBrightnessPercent, StringFormat={}{0:0}%}" />
</Grid>
</StackPanel>
@@ -1434,7 +1443,7 @@
<uc:ButtonWithIcon IsEnabled="{Binding Drawings.Count}"
Icon="fa-solid fa-check"
Spacing="5"
Text="{Binding Drawings.Count, StringFormat=Apply \{0\} operations}"
Text="{Binding Drawings.Count, StringFormat=Apply {0} operations}"
Command="{Binding DrawModifications}"
CommandParameter="false"/>
</StackPanel>
@@ -2072,7 +2081,7 @@
<TextBlock
ToolTip.Tip="Layer preview computation time."
Margin="5,0,0,0"
VerticalAlignment="Center" Text="{Binding ShowLayerRenderMs, StringFormat=\{0\}ms}"/>
VerticalAlignment="Center" Text="{Binding ShowLayerRenderMs, StringFormat={}{0}ms}"/>
</WrapPanel>
</Grid>
</Grid>
@@ -2095,7 +2104,7 @@
Margin="10" Text="{Binding Progress.Title}"/>
<TextBlock
Grid.Row="1"
Margin="10,0,10,10" Text="{Binding Progress.ElapsedTimeStr, StringFormat=Elapsed Time: \{0\}}"/>
Margin="10,0,10,10" Text="{Binding Progress.ElapsedTimeStr, StringFormat=Elapsed Time: {0}}"/>
<TextBlock
Grid.Row="2"
Margin="10,0,10,10" Text="{Binding Progress.Description}" HorizontalAlignment="Center"/>
+1 -1
View File
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<Version>3.5.0</Version>
<Version>3.5.1</Version>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>UVtools.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>