Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
5203ce801This file is pinned to the selected release. Download the original to open it in your design tool.
using GerberLibrary;
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Processing;
using System.Numerics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using GerberLibrary.Core;
using GerberLibrary.Core.Primitives;
using Pen = GerberLibrary.Core.Primitives.Pen;
using SolidBrush = GerberLibrary.Core.Primitives.SolidBrush;
using Path = System.IO.Path;
using ICSharpCode.SharpZipLib.Zip;
using ClipperLib;
using Polygons = System.Collections.Generic.List<System.Collections.Generic.List<ClipperLib.IntPoint>>;
namespace GerberLibrary.Core
{
public class GerberSet
{
public Bounds BoundingBox = new Bounds();
public List<String> Errors = new List<string>();
public bool HasLoadedOutline = false;
public List<ParsedGerber> PLSs = new List<ParsedGerber>();
public List<ExcellonFile> Excellons = new List<ExcellonFile>();
public int Count()
{
return PLSs.Count;
}
public Dictionary<string, MemoryStream> Streams = new Dictionary<string, MemoryStream>();
public void AddBoardsToSet(List<string> FileList, bool fixgroup = true, ProgressLog Logger = null)
{
foreach (var a in FileList)
{
BoardSide aSide = BoardSide.Unknown;
BoardLayer aLayer = BoardLayer.Unknown;
string ext = Path.GetExtension(a);
if (ext == ".zip")
{
using (ZipFile zip1 = new ZipFile(a))
{
foreach (ZipEntry e in zip1)
{
MemoryStream MS = new MemoryStream();
if (e.IsDirectory == false)
{
// e.Extract(MS);
// MS.Seek(0, SeekOrigin.Begin);
Gerber.DetermineBoardSideAndLayer(e.Name, out aSide, out aLayer);
if (aLayer == BoardLayer.Outline) HasLoadedOutline = true;
// AddFileStream(MS, e.Name, drillscaler);
}
}
}
}
else
{
Gerber.DetermineBoardSideAndLayer(a, out aSide, out aLayer);
}
if (aLayer == BoardLayer.Outline) HasLoadedOutline = true;
}
foreach (var a in FileList)
{
if (Logger != null) Logger.AddString(String.Format("Loading {0}", Path.GetFileName(a)));
string ext = Path.GetExtension(a);
if (ext == ".zip")
{
using (ZipFile zip1 = new ZipFile(a))
{
foreach (ZipEntry e in zip1)
{
if (!e.IsDirectory)
{
if (Logger != null) Logger.AddString(String.Format("Loading inside zip: {0}", Path.GetFileName(e.Name)));
MemoryStream MS = new MemoryStream();
using (var stream = zip1.GetInputStream(e))
{
stream.CopyTo(MS);
}
MS.Seek(0, SeekOrigin.Begin);
AddFileToSet(MS, e.Name, Logger);
}
}
}
}
else
{
MemoryStream MS2 = new MemoryStream();
FileStream FS = File.OpenRead(a);
FS.CopyTo(MS2);
MS2.Seek(0, SeekOrigin.Begin);
AddFileToSet(MS2, a, Logger);
}
}
if (fixgroup)
{
if (Logger != null) Logger.AddString("Checking for common file format mistakes.");
FixEagleDrillExportIssues(Logger);
CheckRelativeBoundingBoxes(Logger);
CheckForOutlineFiles(Logger);
CheckRelativeBoundingBoxes(Logger);
}
}
public ParsedGerber AddBoardToSet(ProgressLog log, string _originalfilename, bool forcezerowidth = false, bool precombinepolygons = false, double drillscaler = 1.0)
{
if (Streams.ContainsKey(_originalfilename))
{
return AddBoardToSet(log, Streams[_originalfilename], _originalfilename, forcezerowidth, precombinepolygons, drillscaler) ;
}
return null;
}
public ParsedGerber AddBoardToSet(ProgressLog log, MemoryStream MS, string _originalfilename, bool forcezerowidth = false, bool precombinepolygons = false, double drillscaler = 1.0)
{
Streams[_originalfilename] = MS;
try
{
// string[] filesplit = originalfilename.Split('.');
// string ext = filesplit[filesplit.Count() - 1].ToLower();
var FileType = Gerber.FindFileTypeFromStream(new StreamReader(MS), _originalfilename);
MS.Seek(0, SeekOrigin.Begin);
if (FileType == BoardFileType.Unsupported)
{
if (Gerber.ExtremelyVerbose) Console.WriteLine("Warning: {1}: files with extension {0} are not supported!", Path.GetExtension(_originalfilename), Path.GetFileName(_originalfilename));
return null;
}
ParsedGerber PLS;
GerberParserState State = new GerberParserState() { PreCombinePolygons = precombinepolygons };
if (FileType == BoardFileType.Drill)
{
if (Gerber.ExtremelyVerbose) Console.WriteLine("Log: Drill file: {0}", _originalfilename);
PLS = PolyLineSet.LoadExcellonDrillFileFromStream(log, new StreamReader(MS), _originalfilename, false, drillscaler);
MS.Seek(0, SeekOrigin.Begin);
ExcellonFile EF = new ExcellonFile();
EF.Load(log, new StreamReader(MS), drillscaler);
Excellons.Add(EF);
}
else
{
if (Gerber.ExtremelyVerbose) Console.WriteLine("Log: Gerber file: {0}", _originalfilename);
BoardSide Side = BoardSide.Unknown;
BoardLayer Layer = BoardLayer.Unknown;
Gerber.DetermineBoardSideAndLayer(_originalfilename, out Side, out Layer);
if (Layer == BoardLayer.Outline)
{
forcezerowidth = true;
precombinepolygons = true;
}
State.PreCombinePolygons = precombinepolygons;
PLS = PolyLineSet.LoadGerberFileFromStream(log, new StreamReader(MS), _originalfilename, forcezerowidth, false, State);
MS.Seek(0, SeekOrigin.Begin);
PLS.Side = State.Side;
PLS.Layer = State.Layer;
if (Layer == BoardLayer.Outline)
{
PLS.FixPolygonWindings();
}
}
PLS.CalcPathBounds();
BoundingBox.AddBox(PLS.BoundingBox);
Console.WriteLine("Progress: Loaded {0}: {1:N1} x {2:N1} mm", Path.GetFileName(_originalfilename), PLS.BoundingBox.BottomRight.X - PLS.BoundingBox.TopLeft.X, PLS.BoundingBox.BottomRight.Y - PLS.BoundingBox.TopLeft.Y);
PLSs.Add(PLS);
// }
// catch (Exception)
// {
// }
return PLS;
}
catch (Exception E)
{
while (E != null)
{
Console.WriteLine("Exception adding board: {0}", E.Message);
E = E.InnerException;
}
}
return null;
}
public void AddFileToSet(string aname, ProgressLog Logger, double drillscaler = 1.0)
{
if (Streams.ContainsKey(aname))
{
AddFileToSet(Streams[aname], aname, Logger, drillscaler);
}
else
{
Logger.AddString(String.Format("[ERROR] no stream for {0}!!!", aname));
}
}
public void AddFileToSet(MemoryStream MS, string aname, ProgressLog Logger, double drillscaler = 1.0)
{
Streams[aname] = MS;
///string[] filesplit = a.Split('.');
bool zerowidth = false;
bool precombine = false;
BoardSide aSide;
BoardLayer aLayer;
Gerber.DetermineBoardSideAndLayer(aname, out aSide, out aLayer);
if (aLayer == BoardLayer.Outline || (aLayer == BoardLayer.Mill && HasLoadedOutline == false))
{
zerowidth = true;
precombine = true;
}
AddBoardToSet(Logger, MS, aname, zerowidth, precombine, drillscaler);
}
public void CheckForOutlineFiles(ProgressLog Logger)
{
List<ParsedGerber> Outlines = new List<ParsedGerber>();
List<ParsedGerber> Mills = new List<ParsedGerber>();
List<ParsedGerber> Unknowns = new List<ParsedGerber>();
foreach (var a in PLSs)
{
if (a.Side == BoardSide.Both && (a.Layer == BoardLayer.Outline))
{
Outlines.Add(a);
}
if (a.Side == BoardSide.Both && (a.Layer == BoardLayer.Mill))
{
Mills.Add(a);
}
if (a.Side == BoardSide.Unknown && a.Layer == BoardLayer.Unknown)
{
Unknowns.Add(a);
Errors.Add(String.Format("Unknown file in set:{0}", Path.GetFileName(a.Name)));
if (Logger != null) Logger.AddString(String.Format("Unknown file in set:{0}", Path.GetFileName(a.Name)));
}
}
if (Outlines.Count == 0)
{
if (Unknowns.Count == 0)
{
Errors.Add(String.Format("No outline file found and all other files accounted for! "));
if (Logger != null) Logger.AddString(String.Format("No outline file found and all other files accounted for! "));
// if (Mills.Count == 1)
// {
// Mills[0].Layer = BoardLayer.Outline;
// Errors.Add(String.Format("Elevating mill file to outline!"));
// if (Logger != null) Logger.AddString(String.Format("Elevating mill file to outline!"));
// }
// else
// if (!InventOutlineFromMill())
{
CreateBoxOutline();
}
}
else
{
CreateBoxOutline();
return;
//InventOutline();
//return;
//foreach (var a in Unknowns)
//{
// PLSs.Remove(a);
// hasgko = true;
// a.Layer = BoardLayer.Outline;
// a.Side = BoardSide.Both;
// Console.WriteLine("Note: Using {0} as outline file", Path.GetFileName(a.Name));
// if (Logger != null) Logger.AddString(String.Format("Note: Using {0} as outline file", Path.GetFileName(a.Name)));
// bool zerowidth = true;
// bool precombine = true;
// var b = AddBoardToSet(a.Name, zerowidth, precombine, 1.0);
// b.Layer = BoardLayer.Outline;
// b.Side = BoardSide.Both;
//}
}
}
}
public void CheckRelativeBoundingBoxes(ProgressLog Logger)
{
List<ParsedGerber> DrillFiles = new List<ParsedGerber>();
List<ParsedGerber> DrillFilesToReload = new List<ParsedGerber>();
Bounds BB = new Bounds();
foreach (var a in PLSs)
{
if (a.Layer == BoardLayer.Drill)
{
DrillFiles.Add(a);
}
else
{
BB.AddBox(a.BoundingBox);
}
}
foreach (var a in DrillFiles)
{
if (a.BoundingBox.Intersects(BB) == false)
{
Errors.Add(String.Format("Drill file {0} does not seem to touch the main bounding box!", Path.GetFileName(a.Name)));
if (Logger != null) Logger.AddString(String.Format("Drill file {0} does not seem to touch the main bounding box!", Path.GetFileName(a.Name)));
PLSs.Remove(a);
}
}
BoundingBox = new Bounds();
foreach (var a in PLSs)
{
// Console.WriteLine("Progress: Adding board {6} to box::{0:N2},{1:N2} - {2:N2},{3:N2} -> {4:N2},{5:N2}", a.BoundingBox.TopLeft.X, a.BoundingBox.TopLeft.Y, a.BoundingBox.BottomRight.X, a.BoundingBox.BottomRight.Y, a.BoundingBox.Width(), a.BoundingBox.Height(), Path.GetFileName(a.Name));
//Console.WriteLine("adding box for {0}:{1},{2}", a.Name, a.BoundingBox.Width(), a.BoundingBox.Height());
BoundingBox.AddBox(a.BoundingBox);
}
}
public void FixEagleDrillExportIssues(ProgressLog Logger)
{
List<ParsedGerber> DrillFiles = new List<ParsedGerber>();
List<Tuple<double, ParsedGerber>> DrillFilesToReload = new List<Tuple<double, ParsedGerber>>();
Bounds BB = new Bounds();
foreach (var a in PLSs)
{
if (a.Layer == BoardLayer.Drill)
{
DrillFiles.Add(a);
}
else
{
BB.AddBox(a.BoundingBox);
}
}
foreach (var a in DrillFiles)
{
var b = a.BoundingBox;
if (b.Width() > BB.Width() * 1.5 || b.Height() > BB.Height() * 1.5)
{
var MaxRatio = Math.Max(b.Width() / BB.Width(), b.Height() / BB.Height());
if (Logger != null) Logger.AddString(String.Format("Note: Really large drillfile found({0})-fix your export scripts!", a.Name));
Console.WriteLine("Note: Really large drillfile found ({0})- fix your export scripts!", a.Name);
DrillFilesToReload.Add(new Tuple<double, ParsedGerber>(MaxRatio, a));
}
}
foreach (var a in DrillFilesToReload)
{
PLSs.Remove(a.Item2);
var scale = 1.0;
if (Double.IsInfinity(a.Item1) || Double.IsNaN(a.Item1))
{
Errors.Add("Drill file size reached infinity - ignoring it");
if (Logger != null) Logger.AddString("Drill file size reached infinity - ignoring it");
}
else
{
var R = a.Item1;
while (R >= 1.5)
{
R /= 10;
scale /= 10;
}
AddFileToSet(a.Item2.Name, Logger, scale);
}
}
BoundingBox = new Bounds();
foreach (var a in PLSs)
{
//Console.WriteLine("Progress: Adding board {6} to box::{0:N2},{1:N2} - {2:N2},{3:N2} -> {4:N2},{5:N2}", a.BoundingBox.TopLeft.X, a.BoundingBox.TopLeft.Y, a.BoundingBox.BottomRight.X, a.BoundingBox.BottomRight.Y, a.BoundingBox.Width(), a.BoundingBox.Height(), Path.GetFileName( a.Name));
//Console.WriteLine("adding box for {0}:{1},{2}", a.Name, a.BoundingBox.Width(), a.BoundingBox.Height());
BoundingBox.AddBox(a.BoundingBox);
}
}
public Bounds GetOutlineBoundingBox()
{
Bounds B = new Bounds();
int i = 0;
foreach (var a in PLSs)
{
if (a.Layer == BoardLayer.Mill || a.Layer == BoardLayer.Outline)
{
B.AddBox(a.BoundingBox);
i++;
}
}
if (i == 0) return BoundingBox;
return B;
}
private bool InventOutline(ProgressLog log)
{
double largest = 0;
ParsedGerber Largest = null;
PolyLine Outline = null;
foreach (var a in PLSs)
{
var P = a.FindLargestPolygon();
if (P != null)
{
if (P.Item1 > largest)
{
largest = P.Item1;
Largest = a;
Outline = P.Item2;
}
}
}
if (largest < BoundingBox.Area() / 3.0) return false;
bool zerowidth = true;
bool precombine = true;
log.AddString(String.Format("Note: Using {0} to extract outline file", Path.GetFileName(Largest.Name)));
if (Largest.Layer == BoardLayer.Mill)
{
Largest.OutlineShapes.Remove(Outline);
Largest.Shapes.Remove(Outline);
}
var b = AddBoardToSet(log, Largest.Name, zerowidth, precombine, 1.0);
b.Layer = BoardLayer.Outline;
b.Side = BoardSide.Both;
b.DisplayShapes.Clear();
b.OutlineShapes.Clear();
b.Shapes.Clear();
Outline.Close();
b.Shapes.Add(Outline);
b.OutlineShapes.Add(Outline);
//b.DisplayShapes.Add(Outline);
//b.BuildBoundary();
b.FixPolygonWindings();
b.CalcPathBounds();
return true;
}
private bool InventOutlineFromMill(ProgressLog log)
{
double largest = 0;
ParsedGerber Largest = null;
PolyLine Outline = null;
foreach (var a in PLSs.Where(x => x.Layer == BoardLayer.Mill))
{
var P = a.FindLargestPolygon();
if (P != null)
{
if (P.Item1 > largest)
{
largest = P.Item1;
Largest = a;
Outline = P.Item2;
}
}
}
if (Largest == null) return false;
// if (largest < BoundingBox.Area() / 3.0) return false;
bool zerowidth = true;
bool precombine = true;
log.AddString (String.Format("Note: Using {0} to extract outline file", Path.GetFileName(Largest.Name)));
var b = AddBoardToSet(log, Largest.Name, zerowidth, precombine, 1.0);
b.Layer = BoardLayer.Outline;
b.Side = BoardSide.Both;
//b.DisplayShapes.Clear();
//b.OutlineShapes.Clear();
//b.Shapes.Clear();
// Outline.Close();
// b.Shapes.Add(Outline);
// b.OutlineShapes.Add(Outline);
//b.DisplayShapes.Add(Outline);
//b.BuildBoundary();
// b.FixPolygonWindings();
// b.CalcPathBounds();
return true;
}
public void CreateBoxOutline()
{
PolyLine Box = new PolyLine( PolyLine.PolyIDs.Outline);
Box.MakeRectangle(BoundingBox.Width(), BoundingBox.Height());
Box.Translate(BoundingBox.TopLeft.X + BoundingBox.Width() / 2.0, BoundingBox.TopLeft.Y + BoundingBox.Height() / 2.0);
Box.Hole = false;
//Box.Close();
// Box.Vertices.Reverse();
ParsedGerber PLS = new ParsedGerber();
PLS.Name = "Generated BoundingBox";
PLS.DisplayShapes.Add(Box);
PLS.OutlineShapes.Add(Box);
PLS.Shapes.Add(Box);
PLS.Layer = BoardLayer.Outline;
PLS.Side = BoardSide.Both;
// PLS.FixPolygonWindings();
PLS.CalcPathBounds();
PLSs.Add(PLS);
}
}
public class SickOfBeige : GerberSet
{
public string MinimalDXFSave(string outputfile, double offset = 3.0, double holediameter = 3.2)
{
if (Directory.Exists(outputfile))
{
outputfile = Path.Combine(outputfile, "SickOfBeige");
}
PolyLine Biggest = null;
double BiggestArea = 0;
List<PointD> Holes = new List<PointD>();
var holeradius = holediameter / 2.0;
double Circumference = 2 * Math.PI * holeradius;
foreach (var a in PLSs.Where(x => x.Layer == BoardLayer.Outline))
{
foreach (var b in a.OutlineShapes)
{
var A = b.toPolygon();
double LRatio = (b.OutlineLength() / Circumference);
double Lperc = Math.Abs(LRatio - 1);
if (Lperc < 0.1)
{
if (b.Vertices.Count > 5)
{
var C = b.GetCentroid();
bool round = true;
foreach(var v in b.Vertices)
{
var L = (C - v).Length();
if (Math.Abs(L - holeradius)> 0.2)
{
// not very round!
round = false;
}
}
if (round)
{
Console.WriteLine("Hole detected in outline:{0} {1} {2} {3} {4} {5}", a.Layer, a.Side, C, LRatio, Lperc, b.Vertices.Count);
Holes.Add(C);
}
}
// might be hole!
}
var Area = Clipper.Area(A);
if (Area > BiggestArea)
{
Biggest = b;
BiggestArea = Area;
}
}
}
Polygons Offsetted = new Polygons();
List<String> Lines = new List<string>();
Lines.Add("0");
Lines.Add("SECTION");
Lines.Add("2 ");
Lines.Add("ENTITIES");
if (Biggest != null)
{
Polygons clips = new Polygons();
clips.Add(Biggest.toPolygon());
Offsetted = Clipper.OffsetPolygons(clips, offset * 100000.0f, JoinType.jtRound);
foreach (var poly in Offsetted)
{
PolyLine P = new PolyLine(PolyLine.PolyIDs.Temp);
P.fromPolygon(poly);
for (int i = 0; i < P.Vertices.Count; i++)
{
var V1 = P.Vertices[i];
var V2 = P.Vertices[(i + 1) % P.Vertices.Count];
Lines.Add("0");
Lines.Add("LINE");
Lines.Add("8");
Lines.Add("Outline");
Lines.Add("10");
Lines.Add(V1.X.ToString().Replace(',', '.'));
Lines.Add("20");
Lines.Add(V1.Y.ToString().Replace(',', '.'));
Lines.Add("11");
Lines.Add(V2.X.ToString().Replace(',', '.'));
Lines.Add("21");
Lines.Add(V2.Y.ToString().Replace(',', '.'));
}
}
}
else
{
Errors.Add("No longest outline found - not generating offset curve");
}
foreach (var a in Excellons)
{
foreach (var t in a.Tools)
{
var R = t.Value.Radius;
if (Math.Abs(R * 2 - holediameter) < 0.05)
{
foreach (var h in t.Value.Drills)
{
Holes.Add(h);
}
}
}
}
foreach(var a in Holes)
{
for (int i = 0; i < 40; i++)
{
double P = i * Math.PI * 2.0 / 40.0;
double P2 = (i + 1) * Math.PI * 2.0 / 40.0;
var C1 = Math.Cos(P) * holeradius;
var C2 = Math.Cos(P2) * holeradius;
var S1 = Math.Sin(P) * holeradius;
var S2 = Math.Sin(P2) * holeradius;
double x1 = a.X + C1;
double y1 = a.Y + S1;
double x2 = a.X + C2;
double y2 = a.Y + S2;
Lines.Add("0");
Lines.Add("LINE");
Lines.Add("8");
Lines.Add("Holes");
Lines.Add("10");
Lines.Add(x1.ToString().Replace(',', '.'));
Lines.Add("20");
Lines.Add(y1.ToString().Replace(',', '.'));
Lines.Add("11");
Lines.Add(x2.ToString().Replace(',', '.'));
Lines.Add("21");
Lines.Add(y2.ToString().Replace(',', '.'));
}
}
Lines.Add("0");
Lines.Add("ENDSEC");
Lines.Add("0");
Lines.Add("EOF");
File.WriteAllLines(outputfile + ".dxf", Lines);
float scalefac = 10;
Console.WriteLine("Report: {0} holes created in case ({1} spacers and {1} screws needed!)", Holes.Count, Holes.Count * 2);
{
var BB = new GerberLibrary.Bounds();
BB.AddPolygons(Offsetted);
BB.AddPolyLine(Biggest);
int w = (int)((BB.Width() ) * scalefac) + 6;
int h = (int)((BB.Height()) * scalefac) + 6;
using(Image<Rgba32> B = new Image<Rgba32>(w, h))
{
B.Mutate(G => {
G.Fill(Color.White);
Matrix3x2 matrix = Matrix3x2.CreateTranslation((float)-BB.TopLeft.X, (float)-BB.TopLeft.Y) *
Matrix3x2.CreateScale(scalefac) *
Matrix3x2.CreateTranslation(3, 3);
DrawingOptions options = new DrawingOptions { Transform = matrix };
var isPen1 = new SolidPen(Color.Black, 0.1f);
var isPen2 = new SolidPen(Color.FromRgba(160, 160, 160, 255), 0.1f);
// GerberImageCreator.ApplyAASettings(G); // ImageSharp defaults to high quality
foreach (var a in Holes)
{
// EllipsePolygon(x, y, w, h) -> Centered at x,y with width/height w,h
G.Draw(options, isPen1, new EllipsePolygon((float)a.X, (float)a.Y, (float)holediameter, (float)holediameter));
}
foreach (var poly in Offsetted)
{
PolyLine Pl = new PolyLine(PolyLine.PolyIDs.Temp);
Pl.fromPolygon(poly);
var Points = new List<PointF>(Pl.Vertices.Count);
for (int i = 0; i < Pl.Vertices.Count; i++)
{
Points.Add(Pl.Vertices[i].ToF());
}
Points.Add(Pl.Vertices[0].ToF());
var path = new PathBuilder().AddLines(Points).Build();
G.Draw(options, isPen1, path);
}
{
PolyLine Pl = Biggest;
var Points = new List<PointF>(Pl.Vertices.Count);
for (int i = 0; i < Pl.Vertices.Count; i++)
{
Points.Add(Pl.Vertices[i].ToF());
}
Points.Add(Pl.Vertices[0].ToF());
var path2 = new PathBuilder().AddLines(Points).Build(); G.Draw(options, isPen2, path2);
}
});
var ImagePNG = outputfile + ".png";
B.SaveAsPng(ImagePNG);
return ImagePNG;
}
}
}
}
}