Files
UVtools/UVtools.Core/Gerber/GerberDocument.cs
T
Tiago Conceição 9711bb7474 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
2022-06-25 02:02:59 +01:00

624 lines
28 KiB
C#

/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text.RegularExpressions;
using Emgu.CV;
using Emgu.CV.CvEnum;
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 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();
#endregion
public GerberDocument()
{
}
public GerberDocument(string filePath)
{
}
public static GerberDocument ParseAndDraw(string filePath, Mat mat, SizeF xyPpmm, bool enableAntialiasing = false)
{
using var file = new StreamReader(filePath);
var document = new GerberDocument();
int FSlength = "%FSLAX46Y46*%".Length;
int MOlength = "%MOMM*%".Length;
int LPlength = "%LPD*%".Length;
double currentX = 0;
double currentY = 0;
Aperture? currentAperture = null;
Macro? currentMacro = null;
var regionPoints = new List<Point>();
bool insideRegion = false;
string? line;
while ((line = file.ReadLine()) is not null)
{
line = line.Trim();
if(line == string.Empty) continue;
if (line.StartsWith("M02")) break;
var accumulatedLine = line;
while (!accumulatedLine.Contains('*') && (line = file.ReadLine()) is not null)
{
line = line.Trim();
if (line == string.Empty) continue;
accumulatedLine += line;
}
line = accumulatedLine;
if(currentMacro is not null)
{
currentMacro.ParsePrimitive(line);
if (line[^1] == '%') currentMacro = null;
continue;
}
if (line.StartsWith("%MO") && line.Length >= MOlength)
{
if(line[3] == 'M' && line[4] == 'M') document.UnitType = GerberUnitType.Millimeter;
else if(line[3] == 'I' && line[4] == 'N') document.UnitType = GerberUnitType.Inch;
continue;
}
if (line.StartsWith("%FS") && line.Length >= FSlength)
{
// %FSLAX34Y34*%
// 0123456789
document.ZerosSuppressionType = line[3] switch
{
'L' => GerberZerosSuppressionType.Leading,
'T' => GerberZerosSuppressionType.Trail,
_ => document.ZerosSuppressionType
};
document.PositionType = line[4] switch
{
'A' => GerberPositionType.Absolute,
'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;
if (line[8] != 'Y') continue;
if (byte.TryParse(line[9].ToString(), out var y1)) document.CoordinateYIntegers = y1;
if (byte.TryParse(line[10].ToString(), out var y2)) document.CoordinateYFractionalDigits = y2;
continue;
}
if (line.StartsWith("%LP") && line.Length >= LPlength)
{
document.Polarity = line[3] switch
{
'D' => GerberPolarityType.Dark,
'C' => GerberPolarityType.Clear,
_ => document.Polarity
};
continue;
}
if (line.StartsWith("G01"))
{
document.MoveType = GerberMoveType.Linear;
continue;
}
if (line.StartsWith("G02"))
{
document.MoveType = GerberMoveType.Arc;
continue;
}
if (line.StartsWith("G03"))
{
document.MoveType = GerberMoveType.ArcCounterClockwise;
continue;
}
if (line.StartsWith("G36"))
{
insideRegion = true;
regionPoints.Clear();
continue;
}
if (line.StartsWith("G37"))
{
insideRegion = false;
if (regionPoints.Count > 0)
{
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;
}
if (line.StartsWith("%AM"))
{
currentMacro = Macro.Parse(line);
if (currentMacro is null) continue;
document.Macros.Add(currentMacro.Name, currentMacro);
//document.Apertures.Add(aperture.Index, aperture);
continue;
}
if (line.StartsWith("%ADD"))
{
var aperture = Aperture.Parse(line, document);
if (aperture is null) continue;
currentAperture = aperture;
document.Apertures.Add(aperture.Index, aperture);
continue;
}
// Aperture selector
if (line[0] == 'D')
{
var matchD = Regex.Match(line, @"D(\d+)");
if (!matchD.Success || matchD.Groups.Count < 2) continue;
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 matchD = Regex.Match(line, @"D(\d+)");
double nowX = 0;
double nowY = 0;
if (!matchD.Success || matchD.Groups.Count < 2) continue;
if (!int.TryParse(matchD.Groups[1].Value, out var d)) continue;
if (matchX.Success)
{
if (matchX.Groups.Count >= 2)
{
var valueStr = document.ZerosSuppressionType switch
{
GerberZerosSuppressionType.Trail => matchX.Groups[1].Value.PadRight(document.CoordinateXLength, '0'),
_ => matchX.Groups[1].Value.PadLeft(document.CoordinateXLength, '0'),
};
var integers = valueStr[..document.CoordinateXIntegers];
var fraction = valueStr[document.CoordinateXIntegers..];
double.TryParse($"{integers}.{fraction}", out nowX);
nowX = document.GetMillimeters(nowX);
}
}
else
{
nowX = currentX;
}
if (matchY.Success)
{
if (matchY.Groups.Count >= 2)
{
var valueStr = document.ZerosSuppressionType switch
{
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
{
nowY = currentY;
}
if (insideRegion)
{
if (d == 2)
{
if (regionPoints.Count > 0)
{
using var vec = new VectorOfPoint(regionPoints.ToArray());
CvInvoke.FillPoly(mat, vec, document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor, enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
}
regionPoints.Clear();
}
var pt = PositionMmToPx(nowX, nowY, xyPpmm);
if (regionPoints.Count == 0 || (regionPoints.Count > 0 && regionPoints[^1] != pt)) regionPoints.Add(pt);
}
else if(currentAperture is not null)
{
if (d == 1)
{
if (currentAperture is CircleAperture circleAperture)
{
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;
var valueStr = document.ZerosSuppressionType switch
{
GerberZerosSuppressionType.Trail => matchI.Groups[1].Value.PadRight(document.CoordinateXLength, '0'),
_ => matchI.Groups[1].Value.PadLeft(document.CoordinateXLength, '0'),
};
var integers = valueStr[..document.CoordinateXIntegers];
var fraction = valueStr[document.CoordinateXIntegers..];
double.TryParse($"{integers}.{fraction}", out xOffset);
xOffset = document.GetMillimeters(xOffset);
valueStr = document.ZerosSuppressionType switch
{
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
{
CvInvoke.Ellipse(mat,
PositionMmToPx(nowX + xOffset, nowY + yOffset, xyPpmm),
SizeMmToPx(Math.Abs(xOffset), Math.Abs(xOffset), xyPpmm),
0, 0, 360.0, document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor,
SizeMmToPx(circleAperture.Diameter, xyPpmm.Max()),
enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected
);
}
else
{
// TODO: Fix this
throw new NotImplementedException("Partial arcs are not yet implemented (G03)\nTo be fixed in the future...");
/*CvInvoke.Ellipse(mat, new Point((int)((nowX + xOffset) * xyPpmm.Width), (int)((currentY) * xyPpmm.Height)),
new Size((int)(Math.Abs(xOffset) * xyPpmm.Width), (int)(Math.Abs(yOffset) * xyPpmm.Width)),
0, Math.Abs(currentY - nowY), 360.0 / Math.Abs(currentX - nowX), document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor,
(int)(circleAperture.Diameter * xyPpmm.Max()),
enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected
);*/
}
}
else
{
CvInvoke.Line(mat,
PositionMmToPx(currentX, currentY, xyPpmm),
PositionMmToPx(nowX, nowY, xyPpmm),
document.Polarity == GerberPolarityType.Dark ? EmguExtensions.WhiteColor : EmguExtensions.BlackColor,
SizeMmToPx(circleAperture.Diameter, xyPpmm.Max()),
enableAntialiasing ? LineType.AntiAlias : LineType.EightConnected);
}
}
}
else if (d == 3)
{
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();
}
}
currentX = nowX;
currentY = nowY;
continue;
}
}
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));
public static Point PositionMmToPx(double atXmm, double atYmm, SizeF xyPpmm)
=> new((int)Math.Round(atXmm * xyPpmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round(atYmm * xyPpmm.Height, MidpointRounding.AwayFromZero));
public static Point PositionMmToPx(float atXmm, float atYmm, SizeF xyPpmm)
=> new((int)Math.Round(atXmm * xyPpmm.Width, MidpointRounding.AwayFromZero), (int)Math.Round(atYmm * xyPpmm.Height, MidpointRounding.AwayFromZero));
public static Size SizeMmToPx(SizeF sizeMm, SizeF xyPpmm)
=> new((int)Math.Max(1, Math.Round(sizeMm.Width * xyPpmm.Width, MidpointRounding.AwayFromZero)),
(int)Math.Max(1, Math.Round(sizeMm.Height * xyPpmm.Height, MidpointRounding.AwayFromZero)));
public static Size SizeMmToPx(double sizeXmm, double sizeYmm, SizeF xyPpmm)
=> new((int)Math.Max(1, Math.Round(sizeXmm * xyPpmm.Width, MidpointRounding.AwayFromZero)),
(int)Math.Max(1, Math.Round(sizeYmm * xyPpmm.Height, MidpointRounding.AwayFromZero)));
public static Size SizeMmToPx(float sizeXmm, float sizeYmm, SizeF xyPpmm)
=> new((int)Math.Max(1, Math.Round(sizeXmm * xyPpmm.Width, MidpointRounding.AwayFromZero)),
(int)Math.Max(1, Math.Round(sizeYmm * xyPpmm.Height, MidpointRounding.AwayFromZero)));
public static int SizeMmToPx(float sizeMm, float ppmm)
=> (int) Math.Max(1, Math.Round(sizeMm * ppmm, MidpointRounding.AwayFromZero));
public static int SizeMmToPx(double sizeMm, float ppmm)
=> (int)Math.Max(1, Math.Round(sizeMm * ppmm, MidpointRounding.AwayFromZero));
}
/* KIDCAD
var document = File.ReadAllLines(@"D:\Tiago\Desktop\kisample\kisample.kicad_pcb");
System.Drawing.PointF location = PointF.Empty;
using var mat = EmguExtensions.InitMat(new System.Drawing.Size(2440, 1440));
const byte pixelsPerMm = 20;
var drillPoints = new List<KeyValuePair<Point, int>>();
foreach (var line in document)
{
var parseLine = line.Trim();
if (parseLine.StartsWith("(footprint "))
{
location = PointF.Empty;
continue;
}
if (location.IsEmpty && parseLine.StartsWith("(at "))
{
parseLine = parseLine.Substring(4, parseLine.Length-5);
var split = parseLine.Split(' ');
location = new PointF(float.Parse(split[0]), float.Parse(split[1]));
continue;
}
if (parseLine.StartsWith("(segment ") || parseLine.StartsWith("(gr_line "))
{
var layerMatch = Regex.Match(parseLine, @"\S.Cu");
if (!layerMatch.Success || layerMatch.Groups.Count < 1) continue;
var startMatch = Regex.Match(parseLine, @"\(start\s+(\S+)\s+(\S+)\)");
if(!startMatch.Success || startMatch.Groups.Count < 3) continue;
var endMatch = Regex.Match(parseLine, @"\(end\s+(\S+)\s+(\S+)\)");
if (!endMatch.Success || endMatch.Groups.Count < 3) continue;
var widthMatch = Regex.Match(parseLine, @"\(width\s+(\S+)\)");
if (!widthMatch.Success || widthMatch.Groups.Count < 2) continue;
var startXf = new PointF(float.Parse(startMatch.Groups[1].Value), float.Parse(startMatch.Groups[2].Value));
var endXf = new PointF(float.Parse(endMatch.Groups[1].Value), float.Parse(endMatch.Groups[2].Value));
var widthf = float.Parse(widthMatch.Groups[1].Value);
var startX = new System.Drawing.Point((int)(startXf.X * pixelsPerMm), (int)(startXf.Y * pixelsPerMm));
var endX = new System.Drawing.Point((int)(endXf.X * pixelsPerMm), (int)(endXf.Y * pixelsPerMm));
var width = (int) (widthf * pixelsPerMm);
CvInvoke.Line(mat, startX, endX, EmguExtensions.WhiteColor, width);
continue;
}
if (parseLine.StartsWith("(via "))
{
var layerMatches = Regex.Matches(parseLine, @"\S.Cu");
if (layerMatches.Count < 1) continue;
var atMatch = Regex.Match(parseLine, @"\(at\s+(\S+)\s+(\S+)\)");
if (!atMatch.Success || atMatch.Groups.Count < 3) continue;
var drillMatch = Regex.Match(parseLine, @"\(drill\s+(\S+)\)");
var atf = new PointF(float.Parse(atMatch.Groups[1].Value), float.Parse(atMatch.Groups[2].Value));
//var sizef = new SizeF(float.Parse(sizeMatch.Groups[1].Value), float.Parse(sizeMatch.Groups[2].Value));
var at = new System.Drawing.Point((int)(atf.X * pixelsPerMm), (int)(atf.Y * pixelsPerMm));
if (!drillMatch.Success || drillMatch.Groups.Count < 2) continue;
var drillf = float.Parse(drillMatch.Groups[1].Value);
var drill = (int) (drillf * pixelsPerMm / 2);
CvInvoke.Circle(mat, at, drill, EmguExtensions.WhiteColor, -1);
continue;
}
if (parseLine.StartsWith("(gr_circle "))
{
var layerMatch = Regex.Match(parseLine, @"\S.Cu");
if (!layerMatch.Success || layerMatch.Groups.Count < 1) continue;
var atMatch = Regex.Match(parseLine, @"\(center\s+(\S+)\s+(\S+)\)");
if (!atMatch.Success || atMatch.Groups.Count < 3) continue;
var endMatch = Regex.Match(parseLine, @"\(end\s+(\S+)\s+(\S+)\)");
if (!endMatch.Success || endMatch.Groups.Count < 3) continue;
var widthMatch = Regex.Match(parseLine, @"\(width\s+(\S+)\)");
if (!widthMatch.Success || widthMatch.Groups.Count < 2) continue;
var atf = new PointF(float.Parse(atMatch.Groups[1].Value), float.Parse(atMatch.Groups[2].Value));
var at = new System.Drawing.Point((int)(atf.X * pixelsPerMm), (int)(atf.Y * pixelsPerMm));
var endf = new PointF(float.Parse(endMatch.Groups[1].Value), float.Parse(endMatch.Groups[2].Value));
var radius = (int)(Math.Max(Math.Abs(atf.X - endf.X), Math.Abs(atf.Y - endf.Y)) * pixelsPerMm);
var widthf = float.Parse(widthMatch.Groups[1].Value);
var width = (int)(widthf * pixelsPerMm);
CvInvoke.Circle(mat, at, radius, EmguExtensions.WhiteColor, width);
if (parseLine.Contains("fill solid"))
{
CvInvoke.Circle(mat, at, radius, EmguExtensions.WhiteColor, -1);
}
continue;
}
if (parseLine.StartsWith("(gr_rect "))
{
var layerMatch = Regex.Match(parseLine, @"\S.Cu");
if (!layerMatch.Success || layerMatch.Groups.Count < 1) continue;
var startMatch = Regex.Match(parseLine, @"\(start\s+(\S+)\s+(\S+)\)");
if (!startMatch.Success || startMatch.Groups.Count < 3) continue;
var endMatch = Regex.Match(parseLine, @"\(end\s+(\S+)\s+(\S+)\)");
if (!endMatch.Success || endMatch.Groups.Count < 3) continue;
var widthMatch = Regex.Match(parseLine, @"\(width\s+(\S+)\)");
if (!widthMatch.Success || widthMatch.Groups.Count < 2) continue;
var startf = new PointF(float.Parse(startMatch.Groups[1].Value), float.Parse(startMatch.Groups[2].Value));
var endf = new PointF(float.Parse(endMatch.Groups[1].Value), float.Parse(endMatch.Groups[2].Value));
var widthf = float.Parse(widthMatch.Groups[1].Value);
var start = new System.Drawing.Point((int)(startf.X * pixelsPerMm), (int)(startf.Y * pixelsPerMm));
var end = new System.Drawing.Point((int)(endf.X * pixelsPerMm), (int)(endf.Y * pixelsPerMm));
var width = (int)(widthf * pixelsPerMm);
CvInvoke.Rectangle(mat, new Rectangle(start, new System.Drawing.Size(end.X - start.X, end.Y - start.Y)), EmguExtensions.WhiteColor, width);
if (parseLine.Contains("fill solid"))
{
CvInvoke.Rectangle(mat, new Rectangle(start, new System.Drawing.Size(end.X - start.X, end.Y - start.Y)), EmguExtensions.WhiteColor, -1);
}
continue;
}
if (location.IsEmpty) continue;
if (parseLine.StartsWith("(pad "))
{
var layerMatch = Regex.Match(parseLine, @"\S.Cu");
if (!layerMatch.Success || layerMatch.Groups.Count < 1) continue;
var atMatch = Regex.Match(parseLine, @"\(at\s+(\S+)\s+(\S+)\)");
if (!atMatch.Success || atMatch.Groups.Count < 3) continue;
var sizeMatch = Regex.Match(parseLine, @"\(size\s+(\S+)\s+(\S+)\)");
if (!sizeMatch.Success || sizeMatch.Groups.Count < 3) continue;
var drillMatch = Regex.Match(parseLine, @"\(drill\s+(\S+)\)");
var atf = new PointF(float.Parse(atMatch.Groups[1].Value), float.Parse(atMatch.Groups[2].Value));
var sizef = new SizeF(float.Parse(sizeMatch.Groups[1].Value), float.Parse(sizeMatch.Groups[2].Value));
var at = new System.Drawing.Point((int)(location.X * pixelsPerMm + atf.X * pixelsPerMm), (int)(location.Y * pixelsPerMm + atf.Y * pixelsPerMm));
if (parseLine.Contains(" rect ") || parseLine.Contains(" roundrect "))
{
var size = new System.Drawing.Size((int)(sizef.Width * pixelsPerMm), (int)(sizef.Height * pixelsPerMm));
var rect = new Rectangle(at, size);
rect.Offset(-size.Width / 2, -size.Height / 2);
CvInvoke.Rectangle(mat, rect, EmguExtensions.WhiteColor, -1);
}
else if (parseLine.Contains(" oval ") || parseLine.Contains(" circle "))
{
var size = new System.Drawing.Size((int)(sizef.Width / 2 * pixelsPerMm), (int)(sizef.Height / 2 * pixelsPerMm));
CvInvoke.Ellipse(mat, at, size, 0, 0, 360, EmguExtensions.WhiteColor, -1);
}
if (drillMatch.Success && drillMatch.Groups.Count >= 2)
{
var drillf = float.Parse(drillMatch.Groups[1].Value);
var drill = (int)(drillf * pixelsPerMm / 2);
drillPoints.Add(new KeyValuePair<Point, int>(at, drill));
}
continue;
}
}
foreach (var pair in drillPoints)
{
CvInvoke.Circle(mat, pair.Key, pair.Value, EmguExtensions.BlackColor, -1);
}
CvInvoke.Imshow("asd", mat);
CvInvoke.WaitKey();
return;
*/