mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-08 17:42:31 +02:00
v3.5.2
- **PCB Exposure:** - (Add) Gerber file extensions: gko, gtl, gto, gts, gbl, gbo, gbs, gml - (Fix) Trailing zeros suppression mode and improve the parsing of the coordinate string (#492) - (Fix) Allow coordinates without number `XYD0*` to indicate `X=0` and `Y=0` - (Fix) Do not try to fetch apertures lower than index 10 when a `D02` (closed shutter) is found alone
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 25/06/2022 - v3.5.2
|
||||
|
||||
- **PCB Exposure:**
|
||||
- (Add) Gerber file extensions: gko, gtl, gto, gts, gbl, gbo, gbs, gml
|
||||
- (Fix) Trailing zeros suppression mode and improve the parsing of the coordinate string (#492)
|
||||
- (Fix) Allow coordinates without number `XYD0*` to indicate `X=0` and `Y=0`
|
||||
- (Fix) Do not try to fetch apertures lower than index 10 when a `D02` (closed shutter) is found alone
|
||||
|
||||
## 24/06/2022 - v3.5.1
|
||||
|
||||
- **PCB Exposure:**
|
||||
|
||||
+4
-8
@@ -1,10 +1,6 @@
|
||||
- **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
|
||||
- (Add) Gerber file extensions: gko, gtl, gto, gts, gbl, gbo, gbs, gml
|
||||
- (Fix) Trailing zeros suppression mode and improve the parsing of the coordinate string (#492)
|
||||
- (Fix) Allow coordinates without number `XYD0*` to indicate `X=0` and `Y=0`
|
||||
- (Fix) Do not try to fetch apertures lower than index 10 when a `D02` (closed shutter) is found alone
|
||||
|
||||
|
||||
@@ -7,6 +7,22 @@
|
||||
*/
|
||||
namespace UVtools.Core.Gerber;
|
||||
|
||||
public enum GerberZerosSuppressionType : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Do not omit zeros
|
||||
/// </summary>
|
||||
NoSuppression,
|
||||
/// <summary>
|
||||
/// Omit left zeros
|
||||
/// </summary>
|
||||
Leading,
|
||||
/// <summary>
|
||||
/// Omit right zeros
|
||||
/// </summary>
|
||||
Trail
|
||||
}
|
||||
|
||||
public enum GerberPositionType : byte
|
||||
{
|
||||
Absolute,
|
||||
|
||||
@@ -13,30 +13,35 @@ 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;
|
||||
|
||||
namespace UVtools.Core.Gerber;
|
||||
|
||||
/// <summary>
|
||||
/// https://www.ucamco.com/files/downloads/file_en/456/gerber-layer-format-specification-revision-2022-02_en.pdf?ac97011bf6bce9aaf0b1aac43d84b05f
|
||||
/// </summary>
|
||||
public class GerberDocument
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public GerberZerosSuppressionType ZerosSuppressionType { get; set; } = GerberZerosSuppressionType.NoSuppression;
|
||||
public GerberPositionType PositionType { get; set; } = GerberPositionType.Absolute;
|
||||
public GerberUnitType UnitType { get; set; } = GerberUnitType.Millimeter;
|
||||
public GerberPolarityType Polarity { get; set; } = GerberPolarityType.Dark;
|
||||
public GerberMoveType MoveType { get; set; } = GerberMoveType.Linear;
|
||||
|
||||
public bool LeadingZeroOmitted { get; set; } = true;
|
||||
|
||||
public byte CoordinateXIntegers { get; set; } = 3;
|
||||
public byte CoordinateXFractionalDigits { get; set; } = 6;
|
||||
|
||||
public byte CoordinateXLength => (byte)(CoordinateXIntegers + CoordinateXFractionalDigits);
|
||||
|
||||
public byte CoordinateYIntegers { get; set; } = 3;
|
||||
public byte CoordinateYFractionalDigits { get; set; } = 6;
|
||||
|
||||
public byte CoordinateYLength => (byte)(CoordinateYIntegers + CoordinateYFractionalDigits);
|
||||
|
||||
public Dictionary<int, Aperture> Apertures { get; } = new();
|
||||
public Dictionary<string, Macro> Macros { get; } = new();
|
||||
|
||||
@@ -101,11 +106,11 @@ public class GerberDocument
|
||||
{
|
||||
// %FSLAX34Y34*%
|
||||
// 0123456789
|
||||
document.LeadingZeroOmitted = line[3] switch
|
||||
document.ZerosSuppressionType = line[3] switch
|
||||
{
|
||||
'L' => true,
|
||||
'T' => false,
|
||||
_ => document.LeadingZeroOmitted
|
||||
'L' => GerberZerosSuppressionType.Leading,
|
||||
'T' => GerberZerosSuppressionType.Trail,
|
||||
_ => document.ZerosSuppressionType
|
||||
};
|
||||
document.PositionType = line[4] switch
|
||||
{
|
||||
@@ -113,6 +118,7 @@ public class GerberDocument
|
||||
'I' => GerberPositionType.Relative,
|
||||
_ => document.PositionType
|
||||
};
|
||||
if (document.PositionType == GerberPositionType.Relative) throw new NotImplementedException("Relative positions are not implemented yet.\nPlease use Absolute position for now.");
|
||||
if (line[5] != 'X') continue;
|
||||
if (byte.TryParse(line[6].ToString(), out var x1)) document.CoordinateXIntegers = x1;
|
||||
if (byte.TryParse(line[7].ToString(), out var x2)) document.CoordinateXFractionalDigits = x2;
|
||||
@@ -191,6 +197,7 @@ public class GerberDocument
|
||||
continue;
|
||||
}
|
||||
|
||||
// Aperture selector
|
||||
if (line[0] == 'D')
|
||||
{
|
||||
var matchD = Regex.Match(line, @"D(\d+)");
|
||||
@@ -198,15 +205,17 @@ public class GerberDocument
|
||||
|
||||
if (!int.TryParse(matchD.Groups[1].Value, out var d)) continue;
|
||||
|
||||
if(d < 10) continue;
|
||||
if (!document.Apertures.TryGetValue(d, out currentAperture)) continue;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (line[0] == 'X' || line[0] == 'Y')
|
||||
{
|
||||
var matchX = Regex.Match(line, @"X-?(\d+)");
|
||||
var matchY = Regex.Match(line, @"Y-?(\d+)");
|
||||
var matchX = Regex.Match(line, @"X-?(\d+)?");
|
||||
var matchY = Regex.Match(line, @"Y-?(\d+)?");
|
||||
var matchD = Regex.Match(line, @"D(\d+)");
|
||||
|
||||
double nowX = 0;
|
||||
@@ -215,25 +224,20 @@ public class GerberDocument
|
||||
if (!matchD.Success || matchD.Groups.Count < 2) continue;
|
||||
if (!int.TryParse(matchD.Groups[1].Value, out var d)) continue;
|
||||
|
||||
if (matchX.Success && matchX.Groups.Count >= 2)
|
||||
if (matchX.Success)
|
||||
{
|
||||
if (double.TryParse(matchX.Groups[1].Value, out nowX))
|
||||
if (matchX.Groups.Count >= 2)
|
||||
{
|
||||
if (nowX != 0)
|
||||
var valueStr = document.ZerosSuppressionType switch
|
||||
{
|
||||
if (document.CoordinateXFractionalDigits > matchX.Groups[1].Value.Length) // Leading zero omitted
|
||||
{
|
||||
double.TryParse($"0.{matchX.Groups[1].Value}", out nowX);
|
||||
}
|
||||
else
|
||||
{
|
||||
var integers = matchX.Groups[1].Value[..^document.CoordinateXFractionalDigits];
|
||||
var fraction = matchX.Groups[1].Value.Substring(matchX.Groups[1].Value.Length - document.CoordinateXFractionalDigits, document.CoordinateXFractionalDigits);
|
||||
double.TryParse($"{integers}.{fraction}", out nowX);
|
||||
}
|
||||
GerberZerosSuppressionType.Trail => matchX.Groups[1].Value.PadRight(document.CoordinateXLength, '0'),
|
||||
_ => matchX.Groups[1].Value.PadLeft(document.CoordinateXLength, '0'),
|
||||
};
|
||||
|
||||
nowX = document.GetMillimeters(nowX);
|
||||
}
|
||||
var integers = valueStr[..document.CoordinateXIntegers];
|
||||
var fraction = valueStr[document.CoordinateXIntegers..];
|
||||
double.TryParse($"{integers}.{fraction}", out nowX);
|
||||
nowX = document.GetMillimeters(nowX);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -241,24 +245,21 @@ public class GerberDocument
|
||||
nowX = currentX;
|
||||
}
|
||||
|
||||
if (matchY.Success && matchY.Groups.Count >= 2)
|
||||
if (matchY.Success)
|
||||
{
|
||||
if (double.TryParse(matchY.Groups[1].Value, out nowY))
|
||||
if (matchY.Groups.Count >= 2)
|
||||
{
|
||||
if (nowY != 0)
|
||||
var valueStr = document.ZerosSuppressionType switch
|
||||
{
|
||||
if (document.CoordinateYFractionalDigits > matchY.Groups[1].Value.Length) // Leading zero omitted
|
||||
{
|
||||
double.TryParse($"0.{matchY.Groups[1].Value}", out nowY);
|
||||
}
|
||||
else
|
||||
{
|
||||
var integers = matchY.Groups[1].Value[..^document.CoordinateYFractionalDigits];
|
||||
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);
|
||||
}
|
||||
GerberZerosSuppressionType.Trail => matchY.Groups[1].Value.PadRight(document.CoordinateYLength, '0'),
|
||||
_ => matchY.Groups[1].Value.PadLeft(document.CoordinateYLength, '0'),
|
||||
};
|
||||
|
||||
var integers = valueStr[..document.CoordinateYIntegers];
|
||||
var fraction = valueStr[document.CoordinateYIntegers..];
|
||||
double.TryParse($"{integers}.{fraction}", out nowY);
|
||||
nowY = document.GetMillimeters(nowY);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -289,46 +290,38 @@ public class GerberDocument
|
||||
{
|
||||
if (document.MoveType is GerberMoveType.Arc or GerberMoveType.ArcCounterClockwise)
|
||||
{
|
||||
double xOffset = 0;
|
||||
double yOffset = 0;
|
||||
var matchI = Regex.Match(line, @"I(-?\d+)");
|
||||
var matchJ = Regex.Match(line, @"J(-?\d+)");
|
||||
if(!matchI.Success || !matchJ.Success || matchI.Groups.Count < 2 || matchJ.Groups.Count < 2) continue;
|
||||
|
||||
|
||||
if (double.TryParse(matchI.Groups[1].Value, out var xOffset))
|
||||
var valueStr = document.ZerosSuppressionType switch
|
||||
{
|
||||
if (xOffset != 0)
|
||||
{
|
||||
if (document.CoordinateXFractionalDigits > matchI.Groups[1].Value.Length) // Leading zero omitted
|
||||
{
|
||||
double.TryParse($"0.{matchI.Groups[1].Value}", out xOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
var integers = matchI.Groups[1].Value[..^document.CoordinateXFractionalDigits];
|
||||
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);
|
||||
}
|
||||
}
|
||||
GerberZerosSuppressionType.Trail => matchI.Groups[1].Value.PadRight(document.CoordinateXLength, '0'),
|
||||
_ => matchI.Groups[1].Value.PadLeft(document.CoordinateXLength, '0'),
|
||||
};
|
||||
|
||||
if (double.TryParse(matchJ.Groups[1].Value, out var yOffset))
|
||||
|
||||
var integers = valueStr[..document.CoordinateXIntegers];
|
||||
var fraction = valueStr[document.CoordinateXIntegers..];
|
||||
double.TryParse($"{integers}.{fraction}", out xOffset);
|
||||
xOffset = document.GetMillimeters(xOffset);
|
||||
|
||||
|
||||
valueStr = document.ZerosSuppressionType switch
|
||||
{
|
||||
if(yOffset != 0)
|
||||
{
|
||||
if (document.CoordinateYFractionalDigits > matchJ.Groups[1].Value.Length) // Leading zero omitted
|
||||
{
|
||||
double.TryParse($"0.{matchJ.Groups[1].Value}", out yOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
var integers = matchJ.Groups[1].Value[..^document.CoordinateYFractionalDigits];
|
||||
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);
|
||||
}
|
||||
}
|
||||
GerberZerosSuppressionType.Trail => matchJ.Groups[1].Value.PadRight(document.CoordinateYLength, '0'),
|
||||
_ => matchJ.Groups[1].Value.PadLeft(document.CoordinateYLength, '0'),
|
||||
};
|
||||
|
||||
|
||||
integers = valueStr[..document.CoordinateYIntegers];
|
||||
fraction = valueStr[document.CoordinateYIntegers..];
|
||||
double.TryParse($"{integers}.{fraction}", out yOffset);
|
||||
yOffset = document.GetMillimeters(yOffset);
|
||||
|
||||
|
||||
if (currentX == nowX && currentY == nowY) // Closed circle
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Emgu.CV;
|
||||
using Emgu.CV.CvEnum;
|
||||
@@ -25,6 +26,22 @@ namespace UVtools.Core.Operations;
|
||||
[Serializable]
|
||||
public class OperationPCBExposure : Operation
|
||||
{
|
||||
#region
|
||||
|
||||
public static string[] ValidExtensions => new[]
|
||||
{
|
||||
"gbr", // Gerber
|
||||
"gko", // Board outline layer
|
||||
"gtl", // Top layer
|
||||
"gto", // Top silkscreen layer
|
||||
"gts", // Top solder mask layer
|
||||
"gbl", // Bottom layer
|
||||
"gbo", // Bottom silkscreen layer
|
||||
"gbs", // Bottom solder mask layer
|
||||
"gml", // Mechanical layer
|
||||
};
|
||||
#endregion
|
||||
|
||||
#region Members
|
||||
private RangeObservableCollection<ValueDescription> _files = new();
|
||||
|
||||
@@ -189,7 +206,8 @@ public class OperationPCBExposure : Operation
|
||||
var tmpPath = PathExtensions.GetTemporaryDirectory($"{About.Software}.");
|
||||
foreach (var entry in zip.Entries)
|
||||
{
|
||||
if(!entry.Name.EndsWith(".gbr")) continue;
|
||||
if(!ValidExtensions.Any(extension => entry.Name.EndsWith($".{extension}", StringComparison.InvariantCultureIgnoreCase))) continue;
|
||||
|
||||
var filePath = entry.ImprovedExtractToFile(tmpPath, false);
|
||||
if (!string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
|
||||
@@ -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.1</Version>
|
||||
<Version>3.5.2</Version>
|
||||
<Copyright>Copyright © 2020 PTRTECH</Copyright>
|
||||
<PackageIcon>UVtools.png</PackageIcon>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
|
||||
@@ -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.1"?>
|
||||
<?define SourceDir="..\publish\UVtools_win-x64_v3.5.2"?>
|
||||
<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">
|
||||
|
||||
@@ -92,7 +92,8 @@ namespace UVtools.WPF.Controls.Tools
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!_selectedFile.ValueAsString.EndsWith(".gbr", StringComparison.OrdinalIgnoreCase) || !File.Exists(_selectedFile.ValueAsString)) return;
|
||||
|
||||
if (!OperationPCBExposure.ValidExtensions.Any(extension => _selectedFile.ValueAsString.EndsWith($".{extension}", StringComparison.InvariantCultureIgnoreCase)) || !File.Exists(_selectedFile.ValueAsString)) return;
|
||||
using var mat = Operation.GetMat(_selectedFile.ValueAsString);
|
||||
using var matCropped = mat.CropByBounds(20);
|
||||
_previewImage?.Dispose();
|
||||
@@ -115,7 +116,7 @@ namespace UVtools.WPF.Controls.Tools
|
||||
new()
|
||||
{
|
||||
Name = "Gerber files",
|
||||
Extensions = new List<string>{ "gbr" }
|
||||
Extensions = new List<string>(OperationPCBExposure.ValidExtensions)
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<Version>3.5.1</Version>
|
||||
<Version>3.5.2</Version>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<PackageIcon>UVtools.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
|
||||
Reference in New Issue
Block a user