Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
using ClipperLib;
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Path = System.IO.Path;
using System.IO.Compression;
using System.Linq;
using System.Xml.Serialization;
using Polygons = System.Collections.Generic.List<System.Collections.Generic.List<ClipperLib.IntPoint>>;
using GerberLibrary.Core.Primitives;
using Pen = GerberLibrary.Core.Primitives.Pen;
using SolidBrush = GerberLibrary.Core.Primitives.SolidBrush;
using GerberLibrary.Core;
using ICSharpCode.SharpZipLib.Zip;
using TriangleNet;
using TriangleNet.Geometry;
using System.Threading.Tasks;
using System.Threading;
using System.Numerics;
namespace GerberLibrary
{
public abstract class ProgressLog
{
public abstract void AddString(string text, float progress = -1);
public int PushActivity(string activitiy)
{
// AddString("Activity: " + activitiy);
mut.WaitOne();
ActivityStack.Add(activitiy);
int res = ActivityStack.Count; ;
mut.ReleaseMutex();
return res;
}
private static Mutex mut = new Mutex();
public int PopActivity(int CheckVal = -10)
{
mut.WaitOne();
if (CheckVal > -10)
{
if (ActivityStack.Count != CheckVal)
{
Exception E = new Exception("ActivityStack error! ");
throw (E);
}
}
ActivityStack.Remove(ActivityStack.Last());
int res = ActivityStack.Count;
mut.ReleaseMutex();
return res;
}
public string GetDefaultText(string text, float progress)
{
string output = "";
foreach (var a in ActivityStack)
{
output += a + " -> ";
}
if (progress > -1)
{
return (output + text + String.Format("({0}%)", (int)(progress * 100.0f))); ;
}
else
{
return (output + text);
}
}
public List<string> ActivityStack = new List<string>();
}
public class SilentLog : ProgressLog
{
public override void AddString(string text, float progress = -1)
{
}
}
public class StandardConsoleLog : ProgressLog
{
ProgressLog Forward = null;
public StandardConsoleLog(ProgressLog forward = null)
{
Forward = forward;
}
public override void AddString(string text, float progress = -1)
{
if (Forward != null)
{
Forward.AddString(GetDefaultText(text, progress));
}
else
{
Console.WriteLine(GetDefaultText(text, progress));
}
}
}
public class GerberPanel
{
public GerberLayoutSet TheSet;
public GerberPanel(double width = 100, double height = 100)
{
TheSet = new GerberLayoutSet();
TheSet.Width = width;
TheSet.Height = height;
}
public Image<Rgba32> BoardBitmap = new Image<Rgba32>(1000, 1000);
public List<PolyLine> CombinedOutline = new List<PolyLine>();
// GraphicsPath GP = new GraphicsPath(); // Removed
Image<Rgba32> MMGrid = null;
double LastWidth = -1;
double LastHeight = -1;
public List<string> AddGerberFolder(ProgressLog log, string path, bool add = true, bool skipoutlines = false)
{
if (File.Exists(path) && (Path.GetExtension(path).ToLower() == ".zip" || Path.GetExtension(path).ToLower() == "zip"))
{
return AddGerberZip(log, path, add);
}
List<string> res = new List<string>();
if (add) TheSet.LoadedOutlines.Add(path);
string foldername = Path.GetDirectoryName(path + Path.DirectorySeparatorChar);
log.AddString(String.Format("adding folder {0}", foldername));
bool had = false;
if (Directory.Exists(Path.Combine(path, "ultiboard")))
{
log.AddString(String.Format("found ultiboard folder {0}", foldername));
}
string[] FileNames = Directory.GetFiles(foldername);
List<string> outlinefiles = new List<string>();
List<string> millfiles = new List<string>();
List<string> copperfiles = new List<string>();
foreach (var F in FileNames)
{
BoardSide BS = BoardSide.Unknown;
BoardLayer BL = BoardLayer.Unknown;
var FileTypeForFileName = Gerber.FindFileType(F);
if (FileTypeForFileName == BoardFileType.Gerber)
{
Gerber.DetermineBoardSideAndLayer(F, out BS, out BL);
if (BS == BoardSide.Both && BL == BoardLayer.Outline)
{
outlinefiles.Add(F);
}
else
{
if (BS == BoardSide.Both && BL == BoardLayer.Mill)
{
millfiles.Add(F);
}
else
{
if (BL == BoardLayer.Copper)
{
copperfiles.Add(F);
}
}
}
}
}
if (skipoutlines == false)
{
foreach (var a in outlinefiles)
{
GerberOutlines[path] = new GerberOutline(log, a);
if (GerberOutlines[path].TheGerber.DisplayShapes.Count > 0) had = true;
}
if (had == false)
{
foreach (var a in millfiles)
{
GerberOutlines[path] = new GerberOutline(log, a);
if (GerberOutlines[path].TheGerber.DisplayShapes.Count > 0) had = true;
}
}
if (had == false)
{
// TODO: extract an outline from other layers? THIS IS DANGEROUS!
log.AddString(String.Format("Warning: {0} has no outline available?", path));
}
else
{
res.Add(path);
}
}
return res;
}
private List<string> AddGerberZip(ProgressLog log, string path, bool add)
{
List<string> res = new List<string>();
Dictionary<string, MemoryStream> Files = new Dictionary<string, MemoryStream>();
using (ICSharpCode.SharpZipLib.Zip.ZipFile zip1 = new ICSharpCode.SharpZipLib.Zip.ZipFile(path))
{
foreach (ZipEntry e in zip1)
{
if (!e.IsDirectory)
{
MemoryStream MS = new MemoryStream();
using (var stream = zip1.GetInputStream(e))
{
stream.CopyTo(MS);
}
MS.Seek(0, SeekOrigin.Begin);
Files[e.Name] = MS;
}
}
}
if (add) TheSet.LoadedOutlines.Add(path);
string foldername = Path.GetDirectoryName(path + Path.DirectorySeparatorChar);
log.AddString(String.Format("adding zip file {0}", foldername));
bool had = false;
string[] FileNames = Files.Keys.ToArray();
List<string> outlinefiles = new List<string>();
List<string> millfiles = new List<string>();
List<string> copperfiles = new List<string>();
foreach (var F in FileNames)
{
BoardSide BS = BoardSide.Unknown;
BoardLayer BL = BoardLayer.Unknown;
Files[F].Seek(0, SeekOrigin.Begin);
if (Gerber.FindFileTypeFromStream(new StreamReader(Files[F]), F) == BoardFileType.Gerber)
{
Gerber.DetermineBoardSideAndLayer(F, out BS, out BL);
if (BS == BoardSide.Both && BL == BoardLayer.Outline)
{
outlinefiles.Add(F);
}
else
{
if (BS == BoardSide.Both && BL == BoardLayer.Mill)
{
millfiles.Add(F);
}
else
{
if (BL == BoardLayer.Copper)
{
copperfiles.Add(F);
}
}
}
}
}
foreach (var a in outlinefiles)
{
Files[a].Seek(0, SeekOrigin.Begin);
GerberOutlines[path] = new GerberOutline(log, new StreamReader(Files[a]), a);
if (GerberOutlines[path].TheGerber.DisplayShapes.Count > 0) had = true;
}
if (had == false)
{
foreach (var a in millfiles)
{
Files[a].Seek(0, SeekOrigin.Begin);
GerberOutlines[path] = new GerberOutline(log, new StreamReader(Files[a]), a);
if (GerberOutlines[path].TheGerber.DisplayShapes.Count > 0) had = true;
}
}
if (had == false)
{
// TODO: extract an outline from other layers? THIS IS DANGEROUS!
log.AddString(String.Format("{0} has no outline available?", path));
}
else
{
res.Add(path);
}
return res;
}
public void BuildAutoTabs(ProgressLog log, GerberArtWriter GAW = null, GerberArtWriter GAW2 = null)
{
if (TheSet.Instances.Count < 3) return;
List<Vertex> Vertes = new List<Vertex>();
Dictionary<Vertex, GerberLibrary.GerberInstance> InstanceMap = new Dictionary<Vertex, GerberLibrary.GerberInstance>();
foreach (var a in TheSet.Instances)
{
if (a.GerberPath.Contains("???_negative") == false)
{
var outline = GerberOutlines[a.GerberPath];
var P = outline.GetActualCenter();
P = P.Rotate(a.Angle);
P.X += a.Center.X;
P.Y += a.Center.Y;
var V = new Vertex(P.X, P.Y);
InstanceMap[V] = a;
Vertes.Add(V);
}
}
UpdateShape(log);
var M = new TriangleNet.Meshing.GenericMesher();
var R = M.Triangulate(Vertes);
foreach (var a in R.Edges)
{
var A = R.Vertices.ElementAt(a.P0);
var B = R.Vertices.ElementAt(a.P1);
PolyLine P = new PolyLine(PolyLine.PolyIDs.AutoTabs);
P.Add(A.X, A.Y);
P.Add(B.X, B.Y);
GerberLibrary.GerberInstance iA = null;
GerberLibrary.GerberInstance iB = null;
for (int i = 0; i < InstanceMap.Count; i++)
{
var V = InstanceMap.Keys.ElementAt(i);
if (V.ID == A.ID) iA = InstanceMap.Values.ElementAt(i);
if (V.ID == B.ID) iB = InstanceMap.Values.ElementAt(i);
}
if (iA != null && iB != null)
{
PointD vA = new PointD(A.X, A.Y);
PointD vB = new PointD(B.X, B.Y);
PointD diffA = vB;
diffA.X -= iA.Center.X;
diffA.Y -= iA.Center.Y;
diffA = diffA.Rotate(-iA.Angle);
PointD diffB = vA;
diffB.X -= iB.Center.X;
diffB.Y -= iB.Center.Y;
diffB = diffB.Rotate(-iB.Angle);
var outlineA = GerberOutlines[iA.GerberPath];
var outlineB = GerberOutlines[iB.GerberPath];
PointD furthestA = new PointD();
PointD furthestB = new PointD();
double furthestdistA = 0.0;
var acA = outlineA.GetActualCenter();
var acB = outlineB.GetActualCenter();
foreach (var s in outlineA.TheGerber.OutlineShapes)
{
List<PointD> intersect = s.GetIntersections(diffA, acA);
if (intersect != null && intersect.Count > 0)
{
for (int i = 0; i < intersect.Count; i++)
{
double newD = PointD.Distance(acA, intersect[i]);
PolyLine PL = new PolyLine(PolyLine.PolyIDs.AutoTabs);
var CP = intersect[i].Rotate(iA.Angle);
CP.X += iA.Center.X;
CP.Y += iA.Center.Y;
PL.MakeCircle(1);
PL.Translate(CP.X, CP.Y);
if (GAW != null) GAW.AddPolygon(PL);
if (newD > furthestdistA)
{
furthestdistA = newD;
furthestA = intersect[i];
}
}
}
}
double furthestdistB = 0.0;
foreach (var s in outlineB.TheGerber.OutlineShapes)
{
List<PointD> intersect = s.GetIntersections(diffB, acB);
if (intersect != null && intersect.Count > 0)
{
for (int i = 0; i < intersect.Count; i++)
{
double newD = PointD.Distance(acB, intersect[i]);
PolyLine PL = new PolyLine(PolyLine.PolyIDs.AutoTabs);
var CP = intersect[i].Rotate(iB.Angle);
CP.X += iB.Center.X;
CP.Y += iB.Center.Y;
PL.MakeCircle(1);
PL.Translate(CP.X, CP.Y);
if (GAW != null) GAW.AddPolygon(PL);
if (newD > furthestdistB)
{
furthestdistB = newD;
furthestB = intersect[i];
}
}
}
}
if (furthestdistB != 0 && furthestdistA != 0)
{
furthestA = furthestA.Rotate(iA.Angle);
furthestA.X += iA.Center.X;
furthestA.Y += iA.Center.Y;
furthestB = furthestB.Rotate(iB.Angle);
furthestB.X += iB.Center.X;
furthestB.Y += iB.Center.Y;
var Distance = PointD.Distance(furthestA, furthestB);
if (Distance < 7)
{
var CP = new PointD((furthestA.X + furthestB.X) / 2, (furthestA.Y + furthestB.Y) / 2);
var T = AddTab(CP);
T.Radius = (float)Math.Max(Distance / 1.5, 3.2f);
PolyLine PL = new PolyLine(PolyLine.PolyIDs.AutoTabs);
PL.MakeCircle(T.Radius);
PL.Translate(CP.X, CP.Y);
if (GAW2 != null) GAW2.AddPolygon(PL);
}
}
else
{
var T = AddTab(new PointD((A.X + B.X) / 2, (A.Y + B.Y) / 2));
T.Radius = 3.0f;
}
}
if (GAW != null) GAW.AddPolyLine(P, 0.1);
}
UpdateShape(log);
RemoveAllTabs(true);
UpdateShape(log);
}
public Dictionary<string, GerberOutline> GerberOutlines = new Dictionary<string, GerberOutline>();
/// <summary>
/// Create a board-sized square and subtract all the boardshapes from this shape.
/// </summary>
/// <param name="offsetinMM"></param>
/// <param name="retractMM"></param>
/// <returns></returns>
public List<PolyLine> GenerateNegativePolygon(double offsetinMM = 3, double retractMM = 1)
{
Polygons CombinedInstanceOutline = new Polygons();
foreach (var b in TheSet.Instances)
{
Polygons clips = new Polygons();
var a = GerberOutlines[b.GerberPath];
foreach (var c in b.TransformedOutlines)
{
// PolyLine PL = new PolyLine();
// PL.FillTransformed(c, b.Center, b.Angle);
clips.Add(c.toPolygon());
}
Clipper cp = new Clipper();
cp.AddPolygons(CombinedInstanceOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctUnion, CombinedInstanceOutline, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
}
Polygons BoardMinusCombinedInstanceOutline = new Polygons();
PolyLine Board = new PolyLine(PolyLine.PolyIDs.Outline);
Board.Vertices.Add(new PointD(0, 0));
Board.Vertices.Add(new PointD(TheSet.Width, 0));
Board.Vertices.Add(new PointD(TheSet.Width, TheSet.Height));
Board.Vertices.Add(new PointD(0, TheSet.Height));
Board.Vertices.Add(new PointD(0, 0));
BoardMinusCombinedInstanceOutline.Add(Board.toPolygon());
foreach (var b in CombinedInstanceOutline)
{
Polygons clips = new Polygons();
clips.Add(b);
Polygons clips2 = Clipper.OffsetPolygons(clips, offsetinMM * 100000.0f, JoinType.jtMiter);
Clipper cp = new Clipper();
cp.AddPolygons(BoardMinusCombinedInstanceOutline, PolyType.ptSubject);
cp.AddPolygons(clips2, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, BoardMinusCombinedInstanceOutline, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
}
//CombinedOutline.Clear();
Polygons shrunk = Clipper.OffsetPolygons(BoardMinusCombinedInstanceOutline, -retractMM * 100000.0f, JoinType.jtRound);
Polygons expanded = Clipper.OffsetPolygons(shrunk, retractMM * 100000.0f, JoinType.jtRound);
//CombinedOutline.Clear();
//foreach (var b in TheSet.Tabs)
//{
// Polygons clips = new Polygons();
// PolyLine Circle = new PolyLine();
// Circle.MakeCircle(b.Radius);
// PolyLine PL = new PolyLine();
// PL.FillTransformed(Circle, b.Center, b.Angle);
// clips.Add(PL.toPolygon());
// Clipper cp = new Clipper();
// cp.AddPolygons(expanded, PolyType.ptSubject);
// cp.AddPolygons(clips, PolyType.ptClip);
// cp.Execute(ClipType.ctUnion, expanded, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
//}
//foreach (var b in CombinedInstanceOutline)
//{
// Polygons clips = new Polygons();
// clips.Add(b);
// Clipper cp = new Clipper();
// cp.AddPolygons(expanded, PolyType.ptSubject);
// cp.AddPolygons(clips, PolyType.ptClip);
// cp.Execute(ClipType.ctUnion, expanded, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
//}
List<PolyLine> Res = new List<PolyLine>();
foreach (var s in expanded)
{
PolyLine PL = new PolyLine(PolyLine.PolyIDs.Negative);
PL.fromPolygon(s);
Res.Add(PL);
}
return Res;
}
public void UpdateShape(ProgressLog log)
{
CombinedOutline.Clear();
foreach (var a in TheSet.Instances)
{
if (a.IgnoreOutline == false)
{
if (GerberOutlines.ContainsKey(a.GerberPath))
{
bool doit = false;
if (a.LastCenter == null) doit = true;
if (doit || (PointD.Distance(a.Center, a.LastCenter) != 0 || a.Angle != a.LastAngle))
{
a.RebuildTransformed(GerberOutlines[a.GerberPath], TheSet.ExtraTabDrillDistance);
}
}
}
}
if (TheSet.ConstructNegativePolygon)
{
RemoveInstance("???_negative");
var Neg = GenerateNegativePolygon(TheSet.FillOffset+0.001, TheSet.Smoothing);
var G = new GerberOutline(log, "");
G.TheGerber.Name = "???_negative";
G.TheGerber.OutlineShapes = Neg;
G.TheGerber.DisplayShapes = Neg;
G.TheGerber.FixPolygonWindings();
GerberOutlines["???_negative"] = G;
AddInstance("???_negative", new PointD(0, 0), true);
}
foreach (var aa in TheSet.Instances)
{
if (aa.IgnoreOutline == false) aa.Tabs.Clear();
}
FindOutlineIntersections(log);
}
private void RemoveInstance(string path)
{
foreach (var a in TheSet.Instances)
{
if (a.GerberPath == path)
{
RemoveInstance(a);
return;
}
}
}
public void RenderInstanceHoles(GraphicsInterface G, float PW, Color C, AngledThing b, bool errors = false, bool active = false, bool hover = false)
{
if (b == null) return;
var T = G.Transform;
G.TranslateTransform((float)b.Center.X, (float)b.Center.Y);
G.RotateTransform(b.Angle);
if (b.GetType() == typeof(GerberInstance))
{
GerberInstance GI = b as GerberInstance;
if (GerberOutlines.ContainsKey(GI.GerberPath))
{
var a = GerberOutlines[GI.GerberPath];
foreach (var Shape in a.TheGerber.OutlineShapes)
{
if (Shape.Hole == true)
{
FillShape(G, new SolidBrush(Color.FromRgba(0, 0, 0, 100)), Shape);
}
}
}
}
G.Transform = T;
}
/// <summary>
/// Render a gerber instance to a graphics interface
/// </summary>
/// <param name="G"></param>
/// <param name="PW"></param>
/// <param name="C"></param>
/// <param name="b"></param>
/// <param name="errors"></param>
/// <param name="active"></param>
public void RenderInstance(GraphicsInterface G, float PW, Color C, AngledThing b, bool errors = false, bool active = false, bool hover = false, double GlobalZoom = 1.0)
{
if (b == null) return;
var T = G.Transform;
G.TranslateTransform((float)b.Center.X, (float)b.Center.Y);
G.RotateTransform(b.Angle);
Pen P = new Pen(C, PW) { LineJoin = LineJoin.Round, EndCap = LineCap.Round, StartCap = LineCap.Round };
Pen ActiveP = new Pen(Color.Blue, PW * 3) { LineJoin = LineJoin.Round, EndCap = LineCap.Round, StartCap = LineCap.Round };
Pen ActivePD = new Pen(Color.Green, PW * 2) { LineJoin = LineJoin.Round, EndCap = LineCap.Round, StartCap = LineCap.Round };
Pen ErrorP = new Pen(Color.Red, PW * 2.5f) { LineJoin = LineJoin.Round, EndCap = LineCap.Round, StartCap = LineCap.Round };
if (b.GetType() == typeof(BreakTab))
{
BreakTab BT = b as BreakTab;
if (BT.Errors.Count > 0) errors = true;
DrawMarkerBG(errors, G, new PointD(0, 0), 1, PW, errors ? ErrorP : P);
PolyLine Circle = new PolyLine(PolyLine.PolyIDs.GFXTemp);
Circle.MakeCircle((b as BreakTab).Radius);
if (errors)
{
DrawShapeBG(G, ErrorP, Circle);
for (int i = 0; i < BT.Errors.Count; i++)
{
G.DrawString(new PointD(BT.Radius + 1, PW * 2 * i), BT.Errors[i], 20.0 / GlobalZoom, false);
}
}
else
{
DrawShapeBG(G, P, Circle);
}
if (active)
{
DrawShapeBG(G, errors ? ErrorP : ActivePD, Circle);
DrawMarkerBG(errors, G, new PointD(0, 0), 1, PW, errors ? ErrorP : ActivePD);
DrawShapeBG(G, ActiveP, Circle);
DrawMarkerBG(errors, G, new PointD(0, 0), 1, PW, ActiveP);
}
if (hover)
{
DrawMarker(errors, G, new PointD(0, 0), 1, PW, new Pen(Color.Blue, PW * 2));
}
}
if (b.GetType() == typeof(GerberInstance))
{
GerberInstance GI = b as GerberInstance;
if (GerberOutlines.ContainsKey(GI.GerberPath))
{
var a = GerberOutlines[GI.GerberPath];
// a.BuildShapeCache();
float R = 0;
float Gf = 0;
float B = 0;
float A = 0.8f;
switch (GI.Tabs.Count)
{
case 0:
R = .70f;
break;
case 1:
R = .70f; Gf = 0.35f;
break;
case 2:
R = .70f; Gf = 0.70f;
break;
default:
Gf = 1.0f;
break;
}
//G.FillTriangles(a.TheGerber.ShapeCacheTriangles, Color.FromArgb((byte)(A * 255.0), (byte)(R * 255.0), (byte)(Gf * 255.0), (byte)(B * 255.0)));
foreach (var Shape in a.TheGerber.OutlineShapes)
{
if (Shape.Hole == false)
{
//FillShape(G, new SolidBrush(Color.FromArgb(100, 255, 255, 255)), Shape);
}
else
{
// FillShape(G, new SolidBrush(Color.FromArgb(100, 0, 0, 0)), Shape);
}
if (active)
{
DrawShapeBG(G, ActivePD, Shape);
// DrawShapeNormals(G, ActivePD, Shape);
}
else
{
DrawShape(G, active ? ActiveP : P, Shape);
}
}
foreach (var Shape in a.TheGerber.DisplayShapes)
{
DrawShape(G, active ? ActiveP : P, Shape);
}
var width = (int)(Math.Ceiling(a.TheGerber.BoundingBox.BottomRight.X - a.TheGerber.BoundingBox.TopLeft.X));
var height = (int)(Math.Ceiling(a.TheGerber.BoundingBox.BottomRight.Y - a.TheGerber.BoundingBox.TopLeft.Y));
double ox = (float)(a.TheGerber.TranslationSinceLoad.X + a.TheGerber.BoundingBox.TopLeft.X) + width / 2;
double oy = (float)(a.TheGerber.TranslationSinceLoad.Y + a.TheGerber.BoundingBox.TopLeft.Y + height / 2);
PointD Ext = G.MeasureString(Path.GetFileName(GI.GerberPath));
double Z = 1;
if (Ext.X > width) Z = width / Ext.X;
if (Ext.Y * Z > height) Z = height / Ext.Y;
if (GI.GerberPath.Contains("???_negative") == false)
G.DrawString(new PointD(ox, oy), Path.GetFileName(GI.GerberPath), 20.0/GlobalZoom, true, R, Gf, B, A);
}
G.Transform = T;
if (active)
{
DrawMarker(false, G, b.Center, 1, PW, ActivePD);
DrawMarker(false, G, b.Center, 1, PW, ActiveP);
}
}
G.Transform = T;
}
private void DrawShapeNormals(GraphicsInterface G, Pen P, PolyLine Shape)
{
PointF[] Points = new PointF[2];
for (int j = 0; j < Shape.Count() - 1; j++)
{
var P1 = Shape.Vertices[j];
var P2 = Shape.Vertices[j + 1];
Points[0].X = (float)((P1.X + P2.X) / 2);
Points[0].Y = (float)((P1.Y + P2.Y) / 2);
var DP = (P2 - P1);
if (DP.Length() > 0)
{
DP.Normalize();
DP = DP.Rotate(90);
Points[1].X = (float)((Points[0].X + DP.X * 2));
Points[1].Y = (float)((Points[0].Y + DP.Y * 2));
G.DrawLines(P, Points);
}
}
}
/// <summary>
/// Render a full panel to a graphics interface
/// </summary>
/// <param name="PW"></param>
/// <param name="G"></param>
/// <param name="targetwidth"></param>
/// <param name="targetheight"></param>
/// <param name="SelectedInstance"></param>
public void DrawBoardBitmap(float PW = 1, GraphicsInterface G = null, int targetwidth = 0, int targetheight = 0, AngledThing SelectedInstance = null, AngledThing Hoverinstance = null, double snapdistance = 1, double GlobalZoom = 1.0)
{
if (G == null)
{
return;
}
G.Clear(Color.ParseHex("#888885"));
RectangleF RR = G.ClipBounds;
if (G.IsFast)
{
G.FillRectangle(new SolidBrush(Color.ParseHex("#f5f4e8")), 0, 0, (int)TheSet.Width, (int)TheSet.Height);
Helpers.DrawMMGrid(G, PW, (float)TheSet.Width, (float)TheSet.Height, (float)snapdistance, (float)snapdistance * 10.0f);
}
else
if ((MMGrid == null || MMGrid.Width != targetwidth || MMGrid.Height != targetheight || LastWidth != TheSet.Width || LastHeight != TheSet.Height))
{
MMGrid = new Image<Rgba32>(targetwidth, targetheight);
LastWidth = TheSet.Width;
LastHeight = TheSet.Height;
var G2 = new ImageSharpGraphicsInterface(MMGrid);
//G2.SmoothingMode = SmoothingMode.HighQuality; // ImageSharp default is usually high quality or set in options
G2.Transform = G.Transform;
Helpers.DrawMMGrid(G2, PW, (float)TheSet.Width, (float)TheSet.Height, (float)snapdistance, (float)snapdistance * 10.0f);
Console.WriteLine("building new millimeter grid!");
}
G.InterpolationMode = GraphicsInterpolationMode.High;
// Draw MMGrid using ImageSharp
G.DrawImage(MMGrid, RR.Left, RR.Bottom, RR.Width, -RR.Height); // Note: Height negative? Check logic.
foreach (var b in TheSet.Tabs)
{
if (b.Errors.Count > 0)
{
RenderInstance(G, PW, Color.Gray, b, true, false, false, GlobalZoom);
}
else
{
RenderInstance(G, PW, Color.Gray, b, false, false, false, GlobalZoom);
}
}
foreach (var b in TheSet.Instances)
{
RenderInstance(G, PW, Color.DarkGray, b, false, false, false, GlobalZoom);
}
foreach (var b in TheSet.Instances)
{
RenderInstanceHoles(G, PW, Color.DarkGray, b);
}
Pen OO = new Pen(Color.Orange, PW) { LineJoin = LineJoin.Round, EndCap = LineCap.Round, StartCap = LineCap.Round };
Pen OO3 = new Pen(Color.FromRgba(40, 40, 30, 140), PW) { LineJoin = LineJoin.Round, EndCap = LineCap.Round, StartCap = LineCap.Round };
SolidBrush BR = new SolidBrush(Color.FromRgba(0, 100, 0, 180));
//BR.Color.A = 180;
//GP.Reset(); // Removed
foreach (var FinalPoly in FinalPolygonsWithTabs)
{
PolyLine PL2 = new PolyLine(PolyLine.PolyIDs.Temp);
PL2.Vertices = FinalPoly.Vertices;
DrawShape(G, OO3, PL2);
// DrawMarker(G, PL2.Vertices.First(), 1, PW);
// DrawMarker(G, PL2.Vertices.Last(), 1, PW);
}
//
PolyLine PL = new PolyLine(PolyLine.PolyIDs.Temp);
PL.MakeSquare(0.15);
foreach (var s in DrillHoles)
{
var T = G.Transform;
G.TranslateTransform((float)s.X, (float)s.Y);
FillShape(G, new SolidBrush(Color.FromRgba(0, 0, 0, 140)), PL);
DrawShape(G, new Pen(Color.Black), PL);
G.Transform = T;
}
if (Hoverinstance != null)
{
RenderInstance(G, PW, Color.Blue, Hoverinstance, false, false, false, GlobalZoom);
}
if (SelectedInstance != null)
{
RenderInstance(G, PW * 2, Color.Black, null, false, true, false, GlobalZoom);
RenderInstance(G, PW, Color.Black, SelectedInstance, false, true, false, GlobalZoom);
}
}
/// <summary>
/// Render a solid polygon
/// </summary>
/// <param name="G"></param>
/// <param name="BR"></param>
/// <param name="Shape"></param>
private void FillShape(GraphicsInterface G, SolidBrush BR, PolyLine Shape)
{
// todo: cache the triangulated polygon!
//G.FillShape(BR, Shape);
}
private static void DrawMarkerBG(bool cross, GraphicsInterface G, PointD pointD, float crossize, float PW, Pen? P = null)
{
Pen realP = P ?? new Pen(Color.White, PW);
DrawMarker(cross, G, pointD, crossize, PW, realP);
}
/// <summary>
/// Render a marker-cross
/// </summary>
/// <param name="cross">Cross or plus shape?</param>
/// <param name="G">Target graphics interface</param>
/// <param name="pointD">Cross position</param>
/// <param name="crossize">Cross size</param>
/// <param name="PW">Global Pen width</param>
/// <param name="P">Pen to use (white/PW is default)</param>
private static void DrawMarker(bool cross, GraphicsInterface G, PointD pointD, float crossize, float PW, Pen? P = null)
{
if (P == null)
P = new Pen(Color.White, PW);
if (cross)
{
G.DrawLine(P.Value, (float)pointD.X - crossize, (float)pointD.Y - crossize,
(float)pointD.X + crossize, (float)pointD.Y + crossize);
G.DrawLine(P.Value, (float)pointD.X + crossize, (float)pointD.Y - crossize,
(float)pointD.X - crossize, (float)pointD.Y + crossize);
}
else // plus
{
G.DrawLine(P.Value, (float)pointD.X, (float)pointD.Y - crossize,
(float)pointD.X, (float)pointD.Y + crossize);
G.DrawLine(P.Value, (float)pointD.X + crossize, (float)pointD.Y,
(float)pointD.X - crossize, (float)pointD.Y);
}
}
private static void DrawShapeBG(GraphicsInterface G, Pen P, PolyLine Shape)
{
// DrawShape(G, new Pen(Color.FromArgb(50,0,0,0), P.Width * 3), Shape);
DrawShape(G, P, Shape);
}
/// <summary>
/// Draw an open polygon
/// </summary>
/// <param name="G">Target graphicsinterface</param>
/// <param name="P">Pen to use</param>
/// <param name="Shape"></param>
private static void DrawShape(GraphicsInterface G, Pen P, PolyLine Shape)
{
PointF[] Points = new PointF[Shape.Count()];
for (int j = 0; j < Shape.Count(); j++)
{
var P1 = Shape.Vertices[j];
Points[j].X = (float)((P1.X));
Points[j].Y = (float)((P1.Y));
}
if (Points.Count() > 1)
G.DrawLines(P, Points);
}
List<PointD> DrillHoles = new List<PointD>();
public class TabIntersection
{
public bool Start;
public double Angle;
public PointD Location = new PointD();
public PointD Direction = new PointD();
public override string ToString()
{
return String.Format("{0},{1} , {2}", Start ? "start" : "end", Angle, Location);
}
}
List<PolyLine> GeneratedArcs = new List<PolyLine>();
bool GenerateTabArcs(List<TabIntersection> intersects, BreakTab t, double drillradius = 1)
{
if (t.Errors.Count > 0) return false;
if (intersects.Count < 4)
{
t.Valid = false;
t.AddError("not enough intersections!");
return false;
}
PointD center = t.Center;
bool result = true;
intersects = (from i in intersects orderby i.Angle select i).ToList();
//intersects.Sort(x => x.Angle);
int had = 0;
int current = 0;
while (intersects[current].Start == true) current = (current + 1) % intersects.Count;
int startcurrent = current;
while (had < intersects.Count)
{
var PStart = intersects[current].Location;
PointD StartDir = intersects[current].Direction;
current = (current + 1) % intersects.Count;
had++;
PointD EndDir = intersects[current].Direction;
var PEnd = intersects[current].Location;
current = (current + 1) % intersects.Count;
had++;
PointD StartNorm = StartDir.Rotate(90);
PointD EndNorm = EndDir.Rotate(90);
if ((center - PStart).Dot(StartNorm) < 0)
{
// StartNorm = StartNorm * -1;
}
if ((center - PEnd).Dot(EndNorm) < 0)
{
// EndNorm = EndNorm * -1;
}
if (Helpers.Distance(PStart, PEnd) < drillradius * 2)
{
t.AddError("distance closer than drillradius");
result = false;
}
var C1 = PStart + StartNorm * drillradius;
var C2 = PEnd + EndNorm * drillradius;
var DF = C2 - C1;
DF.Normalize();
var DR = DF.Rotate(90);
var A1 = C1 + DR * drillradius;
var A2 = C2 + DR * drillradius;
double AS = Helpers.AngleBetween(C1, PStart);
double AE = Helpers.AngleBetween(C1, A1);
double BS = Helpers.AngleBetween(C2, PEnd);
double BE = Helpers.AngleBetween(C2, A2);
if (AE > AS) AE -= Math.PI * 2;
if (BE < BS) BE += Math.PI * 2;
if (Math.Abs(AE - AS) >= Math.PI * 1.9)
{
t.AddError("angle too big");
result = false;
}
if (Math.Abs(BE - BS) >= Math.PI * 1.9)
{
t.AddError("angle too big");
result = false;
}
}
if (result == false) return false;
current = startcurrent;
had = 0;
while (had < intersects.Count)
{
var PStart = intersects[current].Location;
PointD StartDir = intersects[current].Direction;
current = (current + 1) % intersects.Count;
had++;
PointD EndDir = intersects[current].Direction;
var PEnd = intersects[current].Location;
current = (current + 1) % intersects.Count;
had++;
PointD StartNorm = StartDir.Rotate(90);
PointD EndNorm = EndDir.Rotate(90);
if ((center - PStart).Dot(StartNorm) < 0)
{
// StartNorm = StartNorm * -1;
}
if ((center - PEnd).Dot(EndNorm) < 0)
{
// EndNorm = EndNorm * -1;
}
if (Helpers.Distance(PStart, PEnd) < drillradius * 2)
{
t.AddError("distance closer than drillradius");
result = false;
}
var C1 = PStart + StartNorm * drillradius;
var C2 = PEnd + EndNorm * drillradius;
//if centers are at the same location then the linesegment between them does not exist. This results in a generating a bad unit vector and
//therfore bad angles and bad arcs. If the centers are at the same location use the linesgment between the start and end points instead.
var DF = (C1 != C2) ? C2 - C1 : PEnd - PStart;
DF.Normalize();
var DR = DF.Rotate(90);
var A1 = C1 + DR * drillradius;
var A2 = C2 + DR * drillradius;
double AS = Helpers.AngleBetween(C1, PStart);
double AE = Helpers.AngleBetween(C1, A1);
double BS = Helpers.AngleBetween(C2, PEnd);
double BE = Helpers.AngleBetween(C2, A2);
if (AE > AS) AE -= Math.PI * 2;
if (BE < BS) BE += Math.PI * 2;
if (Math.Abs(AE - AS) >= Math.PI * 2)
{
t.AddError("angle too big");
}
if (Math.Abs(BE - BS) >= Math.PI * 2)
{
t.AddError("angle too big");
}
//while (AS < 0 || AE < 0) { AS += Math.PI * 2; AE += Math.PI * 2; };
//while (BS < 0 || BE < 0) { BS += Math.PI * 2; BE += Math.PI * 2; };
//if (Math.Abs(BE - BS) > Math.PI)
//{
// double Dif = BE - BS;
// BE = Math.PI * 2 - Dif;
//}
//if (Math.Abs(AE - AS) > Math.PI)
//{
// double Dif = AE - AS;
// AE = Math.PI * 2 - Dif;
// //AE -= Math.PI * 2;
//}
// while (BE < BS) BE += Math.PI * 2;
List<PointD> Arc1 = new List<PointD>();
List<PointD> Arc2 = new List<PointD>();
Arc1.Add(PStart);
Arc2.Add(PEnd);
for (double T = 0; T <= 1; T += 1.0 / 20)
{
double P = AS + (AE - AS) * T;
PointD newP = C1 + new PointD(-Math.Cos(P) * drillradius, -Math.Sin(P) * drillradius);
Arc1.Add(newP);
double P2 = BS + (BE - BS) * T;
PointD newP2 = C2 + new PointD(-Math.Cos(P2) * drillradius, -Math.Sin(P2) * drillradius);
Arc2.Add(newP2);
}
// Arc1.Add(A1.ToF());
// Arc2.Add(A2.ToF());
PolyLine Middle = new PolyLine(PolyLine.PolyIDs.AutoTabs);
Middle.Vertices.Add(A1);
Middle.Vertices.Add(A2);
PolyLine Combined = new PolyLine(PolyLine.PolyIDs.AutoTabs);
// GeneratedArcs.Add(PL);
// GeneratedArcs.Add(PL2);
// GeneratedArcs.Add(Middle);
Arc2.Reverse();
Combined.Vertices.AddRange(Arc1);
Combined.Vertices.AddRange(Middle.Vertices);
Combined.Vertices.AddRange(Arc2);
GeneratedArcs.Add(Combined);
//PolyLine N1 = new PolyLine();
//N1.Vertices.Add(PStart);
//N1.Vertices.Add(PStart + StartNorm *4);
//GeneratedArcs.Add(N1);
//PolyLine N2 = new PolyLine();
//N2.Vertices.Add(PEnd);
//N2.Vertices.Add(PEnd + EndNorm * 4);
//GeneratedArcs.Add(N2);
// var P2 = PStart - StartDir * drillradius + StartNorm * drillradius;
// var P3 = PEnd - EndDir * drillradius + EndNorm * drillradius;
// GP.AddBezier(PStart.ToF(), P2.ToF(), P3.ToF(), PEnd.ToF());
}
return result;
}
public void RemoveAllTabs(bool errortabsonly = false)
{
if (errortabsonly)
{
var T = (from i in TheSet.Tabs where i.Errors.Count > 0 select i).ToList();
if (T.Count() > 0)
{
foreach (var bt in T)
{
TheSet.Tabs.Remove(bt);
}
}
}
else
TheSet.Tabs.Clear();
}
public void GenerateTabLocations()
{
RemoveAllTabs();
foreach (var a in TheSet.Instances)
{
if (GerberOutlines.ContainsKey(a.GerberPath) && a.GerberPath.Contains("???_negative") == false)
{
var g = GerberOutlines[a.GerberPath];
var TabsLocs = PolyLineSet.FindOptimalBreaktTabLocations(g.TheGerber);
foreach (var b in TabsLocs)
{
PointD loc = b.Item1 - (b.Item2 * TheSet.MarginBetweenBoards * 0.5);
loc = loc.Rotate(a.Angle);
loc += a.Center;
if (loc.X >= 0 && loc.X <= TheSet.Width && loc.Y >= 0 && loc.Y <= TheSet.Height)
{
var BT = AddTab(loc);
BT.Radius = (float)TheSet.MarginBetweenBoards + 2;
}
}
}
}
}
class CutUpLine
{
public List<List<PointD>> Lines = new List<List<PointD>>();
public void AddVertex(PointD D)
{
if (Lines.Count == 0)
{
Lines.Add(new List<PointD>());
}
Lines.Last().Add(D);
}
public void NewLine()
{
if (Lines.Count > 0 && Lines.Last().Count == 0) return;
Lines.Add(new List<PointD>());
}
}
List<CutUpLine> CutLines = new List<CutUpLine>();
class LineSeg
{
public PointD PStart = new PointD();
public PointD PEnd = new PointD();
public override string ToString()
{
return PStart.ToString() + " - " + PEnd.ToString() + String.Format(" ({0}mm)", (PEnd - PStart).Length());
}
}
List<LineSeg> FinalSegs = new List<LineSeg>();
void CutUpOutlines()
{
CutLines.Clear();
FinalSegs.Clear();
foreach (var b in TheSet.Instances)
{
// Polygons clips = new Polygons();
if (GerberOutlines.ContainsKey(b.GerberPath))
{
var a = GerberOutlines[b.GerberPath];
foreach (var c in b.TransformedOutlines)
{
// PolyLine PL = new PolyLine();
// PL.FillTransformed(c, b.Center, b.Angle);
SplitPolyLineAndAddSegs(c);
}
}
}
foreach (var b in CombinedOutline)
{
// SplitPolyLineAndAddSegs(b);
}
foreach (var a in FinalSegs)
{
PolyLine PL = new PolyLine(PolyLine.PolyIDs.Outline);
PL.Vertices.Add(a.PStart);
PL.Vertices.Add(a.PEnd);
GeneratedArcs.Add(PL);
}
}
private void SplitPolyLineAndAddSegs(PolyLine PL)
{
int adjust = 0;
if (PL.Vertices.First() == PL.Vertices.Last()) adjust = 1;
List<LineSeg> Segs = new List<LineSeg>(Math.Max(1, PL.Vertices.Count - adjust + 100));
for (int i = 0; i < PL.Vertices.Count - adjust; i++)
{
Segs.Add(new LineSeg() { PStart = PL.Vertices[i].Copy(), PEnd = PL.Vertices[(i + 1) % PL.Vertices.Count].Copy() });
}
foreach (var t in TheSet.Tabs.Where(x => x.Errors.Count == 0))
{
PointD center = t.Center;
List<LineSeg> ToDelete = new List<LineSeg>();
List<LineSeg> ToAdd = new List<LineSeg>();
foreach (var s in Segs)
{
bool StartInside = Helpers.Distance(s.PStart, center) < t.Radius;
bool EndInside = Helpers.Distance(s.PEnd, center) < t.Radius;
if (StartInside && EndInside)
{
ToDelete.Add(s);
}
else
{
PointD I1;
PointD I2;
int ints = Helpers.FindLineCircleIntersections(center.X, center.Y, t.Radius, s.PStart, s.PEnd, out I1, out I2);
switch (ints)
{
case 0: // skip;
break;
case 1: // adjust 1 point
if (StartInside)
{
s.PStart = I1.Copy();
}
else
{
s.PEnd = I1.Copy();
}
break;
case 2: // split and add 1
LineSeg NS1 = new LineSeg();
LineSeg NS2 = new LineSeg();
NS1.PEnd = s.PEnd.Copy();
NS1.PStart = I1.Copy();
NS2.PEnd = s.PStart.Copy();
NS2.PStart = I2.Copy();
ToAdd.Add(NS1);
ToAdd.Add(NS2);
ToDelete.Add(s);
break;
}
}
}
foreach (var s in ToDelete)
{
Segs.Remove(s);
}
foreach (var s in ToAdd)
{
Segs.Add(s);
}
}
FinalSegs.AddRange(Segs);
}
private void FindOutlineIntersections(ProgressLog log)
{
GeneratedArcs.Clear();
DrillHoles.Clear();
CutLines.Clear();
foreach (var t in TheSet.Tabs)
{
t.EvenOdd = 0;
t.Errors.Clear();
List<TabIntersection> Intersections = new List<TabIntersection>();
float R2 = t.Radius * t.Radius;
foreach (var b in TheSet.Instances.Where(x => x.IgnoreOutline == false))
{
// Polygons clips = new Polygons();
// if (b.GerberPath.Contains("???") == false)
{
GerberOutline a = null;
if (GerberOutlines.ContainsKey(b.GerberPath))
{
a = GerberOutlines[b.GerberPath];
}
else
{
foreach (var k in GerberOutlines.Keys)
{
if (k.EndsWith(b.GerberPath))
{
a = GerberOutlines[k];
}
}
}
if (a != null)
{
var C = t.Center.Copy();
C.X -= b.Center.X;
C.Y -= b.Center.Y;
C = C.Rotate(-b.Angle);
var Box2 = a.TheGerber.BoundingBox.Grow(t.Radius * 2);
if (Box2.Contains(C) || b.GerberPath.Contains("???_negative") == true)
{
//Console.WriteLine("{0},{1}", a.TheGerber.BoundingBox, C);
for (int i = 0; i < b.TransformedOutlines.Count; i++)
{
var c = b.TransformedOutlines[i];
// var poly = c.toPolygon();
// bool winding = Clipper.Orientation(poly);
// PolyLine PL = new PolyLine();
// PL.FillTransformed(c, b.Center, b.Angle);
if (Helpers.IsInPolygon(c.Vertices, t.Center, false))
{
t.EvenOdd++;
// t.Errors.Add("inside a polygon!");
// t.Valid = false;
}
if (TheSet.DoNotGenerateMouseBites == false) BuildDrillsForTabAndPolyLine(t, c, b.OffsetOutlines[i]);
//bool inside = false;
//bool newinside = false;
// List<PolyLine> Lines = new List<PolyLine>();
//PolyLine Current = null;
if (AddIntersectionsForTabAndPolyLine(t, Intersections, c))
{
b.Tabs.Add(t);
}
}
}
}
}
}
if (t.EvenOdd % 2 == 1)
{
t.AddError("inside a polygon!");
t.Valid = false;
}
foreach (var a in CombinedOutline)
{
AddIntersectionsForTabAndPolyLine(t, Intersections, a);
}
bool Succes = GenerateTabArcs(Intersections, t); ;
}
CutUpOutlines();
CombineGeneratedArcsAndCutlines(log);
}
private static bool AddIntersectionsForTabAndPolyLine(BreakTab t, List<TabIntersection> Intersections, PolyLine PL)
{
// Polygons clips = new Polygons();
//var poly = PL.toPolygon();
//bool winding = Clipper.Orientation(poly);
bool ret = false;
for (int i = 0; i < PL.Vertices.Count; i++)
{
PointD V1 = PL.Vertices[i];
PointD V2 = PL.Vertices[(i + 1) % PL.Vertices.Count];
// if (winding == false)
// {
// var V3 = V1;
// V1 = V2;
// V2 = V3;
// }
PointD I1 = new PointD();
PointD I2 = new PointD();
double Len = Helpers.Distance(V1, V2);
int ints = Helpers.FindLineCircleIntersections(t.Center.X, t.Center.Y, t.Radius, V1, V2, out I1, out I2);
if (ints > 0)
{
ret = true;
if (ints == 1)
{
TabIntersection TI = new TabIntersection();
TI.Location = I1;
TI.Direction.X = (V1.X - V2.X) / Len;
TI.Direction.Y = (V1.Y - V2.Y) / Len;
TI.Angle = Helpers.AngleBetween(t.Center, I1);
//bool addedV1 = false;
bool V1Inside = Helpers.Distance(new PointD(t.Center.X, t.Center.Y), V1) < t.Radius;
bool V2Inside = Helpers.Distance(new PointD(t.Center.X, t.Center.Y), V2) < t.Radius;
if (Helpers.Distance(new PointD(t.Center.X, t.Center.Y), V1) < t.Radius)
{
// addedV1 = true;
TI.Start = true;
}
if (Helpers.Distance(new PointD(t.Center.X, t.Center.Y), V2) < t.Radius)
{
TI.Start = false;
}
Intersections.Add(TI);
}
if (ints == 2)
{
TabIntersection TI1 = new TabIntersection();
TI1.Start = true;
TI1.Location = I1;
TI1.Angle = Helpers.AngleBetween(t.Center, I1);
TI1.Direction.X = (V1.X - V2.X) / Len;
TI1.Direction.Y = (V1.Y - V2.Y) / Len;
Intersections.Add(TI1);
TabIntersection TI2 = new TabIntersection();
TI2.Start = false;
TI2.Location = I2;
TI2.Direction.X = TI1.Direction.X;
TI2.Direction.Y = TI1.Direction.Y;
TI2.Angle = Helpers.AngleBetween(t.Center, I2);
Intersections.Add(TI2);
}
}
}
return ret;
}
private void BuildDrillsForTabAndPolyLine(BreakTab t, PolyLine PL, List<PolyLine> Offsetted)
{
foreach (var sub in Offsetted)
{
// PolyLine sub = new PolyLine();
// sub.fromPolygon(subpoly);
double Len = 0;
PointD last = sub.Vertices.Last();
for (int i = 0; i < sub.Vertices.Count; i++)
{
double dx = sub.Vertices[i].X - last.X;
double dy = sub.Vertices[i].Y - last.Y;
double seglen = Math.Sqrt(dx * dx + dy * dy);
if (Len + seglen < 1)
{
Len += seglen;
}
else
{
double ndx = dx / seglen;
double ndy = dy / seglen;
double left = 1 - Len;
double had = left;
PointD place = new PointD(last.X + ndx * had, last.Y + ndy * had);
if (Helpers.Distance(place, new PointD(t.Center.X, t.Center.Y)) < t.Radius)
{
DrillHoles.Add(place);
}
seglen -= left;
while (seglen > 0)
{
double amt = Math.Min(1, seglen);
seglen -= amt;
had += amt;
if (amt == 1)
{
place = new PointD(last.X + ndx * had, last.Y + ndy * had);
if (Helpers.Distance(place, new PointD(t.Center.X, t.Center.Y)) < t.Radius)
{
DrillHoles.Add(place);
}
// DrillHoles.Add(new PointD(last.X + ndx * had, last.Y + ndy * had));
}
else
{
Len = amt;
}
}
}
last = sub.Vertices[i];
}
}
}
List<PathDefWithClosed> FinalPolygonsWithTabs = new List<PathDefWithClosed>();
private void CombineGeneratedArcsAndCutlines(ProgressLog log)
{
List<PathDefWithClosed> SourceLines = new List<PathDefWithClosed>();
foreach (var a in GeneratedArcs)
{
SourceLines.Add(new PathDefWithClosed() { Vertices = a.Vertices, Width = 0 });
}
foreach (var cl in CutLines)
{
foreach (var l in cl.Lines)
{
SourceLines.Add(new PathDefWithClosed() { Vertices = l, Width = 0 });
}
}
FinalPolygonsWithTabs = Helpers.LineSegmentsToPolygons(log, SourceLines, true);
}
public List<string> SaveOutlineTo(string p, string combinedfilename, ProgressLog TheLog)
{
TheLog.PushActivity("SaveOutlineTo");
List<string> R = new List<string>();
string DrillFile = Path.Combine(p, combinedfilename + "_tabdrills.TXT");
string OutlineFile = Path.Combine(p, combinedfilename + "_blended_outline.GKO");
R.Add(DrillFile);
//R.Add(OutlineFile);
// R.Add(OutlineFile);
ExcellonFile EF = new ExcellonFile();
EF.Tools[1] = new ExcellonTool() { ID = 1, Radius = 0.25 };
foreach (var a in DrillHoles)
{
EF.Tools[1].Drills.Add(a);
}
TheLog.AddString(String.Format("Drillfile: {0}", DrillFile));
TheLog.AddString(String.Format("DrillfileComb: {0}", Path.Combine(p, DrillFile)));
EF.Write( DrillFile, 0, 0, 0, 0);
GerberOutlineWriter GOW = new GerberOutlineWriter();
int id = 0;
foreach (var a in FinalPolygonsWithTabs)
{
PolyLine PL = new PolyLine(id++);
PL.Vertices = a.Vertices;
// todo: check if closed/opened things need special treatment here.
// width is defaulted to 0
GOW.AddPolyLine(PL);
}
GOW.Write(OutlineFile);
TheLog.PopActivity();
//R.Add(OutlineFile);
return R;
}
public List<String> SaveGerbersToFolder(string BaseName, string targetfolder, ProgressLog Logger, bool OutlineToTopSilkscreen = false, bool SaveOutline = true, bool GenerateImages = true, bool DeleteGenerated = true, string combinedfilename = "combined")
{
var logcheck = Logger.PushActivity("SaveGerbersToFolder");
Logger.AddString("Starting export to " + targetfolder);
List<string> GeneratedFiles = TheSet.SaveTo(targetfolder, GerberOutlines, Logger);
List<String> FinalFiles = new List<string>();
List<string> removedoutlines = new List<string>();
string finalblended = "";
if (SaveOutline)
{
foreach(var s in GeneratedFiles)
{
BoardLayer layer;
BoardSide Side;
Gerber.DetermineBoardSideAndLayer(s, out Side, out layer);
if (layer == BoardLayer.Outline && Side == BoardSide.Both)
{
removedoutlines.Add(s);
}
}
GeneratedFiles.AddRange(SaveOutlineTo(targetfolder, combinedfilename, Logger));
finalblended = Path.Combine(targetfolder, combinedfilename + "_blended_outline.gko");
FinalFiles.Add(finalblended);
}
else
{
// skip.
}
// TODO: use the new Gerber.DetermineFile to actually group based on layer/type instead of extentions only!
Dictionary<string, List<string>> FilesPerExt = new Dictionary<string, List<string>>();
Dictionary<string, BoardFileType> FileTypePerExt = new Dictionary<string, BoardFileType>();
if (OutlineToTopSilkscreen)
{
FilesPerExt[".gto"] = new List<string>();
}
foreach (var s in GeneratedFiles)
{
string ext = Path.GetExtension(s).ToLower(); ;
if (ext == ".xln") ext = ".txt";
if (ext == ".drl") ext = ".txt";
if (TheSet.MergeFileTypes)
{
BoardLayer layer;
BoardSide Side;
Gerber.DetermineBoardSideAndLayer(s, out Side, out layer);
if (layer == BoardLayer.Unknown || Side == BoardSide.Unknown)
{
var T = Gerber.FindFileType(s);
if (T == BoardFileType.Drill)
{
layer = BoardLayer.Drill;
Side = BoardSide.Both;
}
}
ext = Gerber.GetStandardFileExt(layer, Side);
}
if (FilesPerExt.ContainsKey(ext) == false)
{
FilesPerExt[ext] = new List<string>();
}
if (OutlineToTopSilkscreen)
{
if (ext == ".gko")
FilesPerExt[".gto"].Add(s);
}
FileTypePerExt[ext] = Gerber.FindFileType(s);
FilesPerExt[ext].Add(s);
}
int count = 0;
object finallock = new object();
Parallel.ForEach(FilesPerExt, (a) =>
{
ProgressLog L = new StandardConsoleLog();
lock (finallock)
{
count++;
L.AddString("merging *" + a.Key.ToLower(), ((float)count / (float)FilesPerExt.Keys.Count) * 0.5f + 0.3f);
}
switch (FileTypePerExt[a.Key])
{
case BoardFileType.Drill:
{
string Filename = Path.Combine(targetfolder, combinedfilename + a.Key);
lock (finallock)
{
FinalFiles.Add(Filename);
}
ExcellonFile.MergeAll(a.Value, Filename, L);
}
break;
case BoardFileType.Gerber:
{
if (a.Key.ToLower() != ".outline.gko")
{
string Filename = Path.Combine(targetfolder, combinedfilename + a.Key);
lock (finallock)
{
FinalFiles.Add(Filename);
}
GerberMerger.MergeAll(a.Value, Filename, L);
}
}
break;
}
});
//Logger.AddString("Writing source material zipfile", 0.80f);
//string SeparateZipFile = Path.Combine(targetfolder, BaseName + ".separate_boards.zip");
//if (File.Exists(SeparateZipFile)) File.Delete(SeparateZipFile);
//ZipArchive zip = ZipFile.Open(SeparateZipFile, ZipArchiveMode.Create);
//foreach (string file in GeneratedFiles)
//{
// zip.CreateEntryFromFile(file, Path.GetFileName(file), CompressionLevel.Optimal);
//}
//zip.Dispose();
Logger.AddString("Writing combined zipfile", 0.85f);
string CombinedZipFile = Path.Combine(targetfolder, BaseName + ".combined_boards.zip");
if (File.Exists(CombinedZipFile)) File.Delete(CombinedZipFile);
using (FileStream fsOut = File.Create(CombinedZipFile))
using (ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipStream = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(fsOut))
{
zipStream.SetLevel(9);
byte[] buffer = new byte[4096];
foreach (string file in FinalFiles)
{
BoardLayer layer;
BoardSide Side;
Gerber.DetermineBoardSideAndLayer(file, out Side, out layer);
bool add = true;
if (Side == BoardSide.Both && layer == BoardLayer.Outline && file != finalblended)
{
add = false;
}
if (add)
{
ICSharpCode.SharpZipLib.Zip.ZipEntry entry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(Path.GetFileName(file));
entry.DateTime = DateTime.Now;
zipStream.PutNextEntry(entry);
using (FileStream fs = File.OpenRead(file))
{
int sourceBytes;
do
{
sourceBytes = fs.Read(buffer, 0, buffer.Length);
zipStream.Write(buffer, 0, sourceBytes);
} while (sourceBytes > 0);
}
zipStream.CloseEntry();
}
}
zipStream.Finish();
}
Logger.AddString("Deleting tempfiles", 0.9f);
if (DeleteGenerated)
{
foreach (var a in GeneratedFiles)
{
File.Delete(a);
}
}
if (GenerateImages)
{
try
{
Logger.AddString("Writing board bitmaps", 0.95f);
GerberImageCreator GIC = new GerberImageCreator();
GIC.AddBoardsToSet(FinalFiles, new SilentLog());
GIC.WriteImageFiles(Path.Combine(targetfolder, BaseName), 200, Gerber.DirectlyShowGeneratedBoardImages, false, true, Logger);
}
catch (Exception E)
{
Logger.AddString("Some errors while exporting board images.. but this should be no problem?");
Logger.AddString(String.Format("The exception: {0}", E.Message));
}
}
Logger.AddString("Done", 1);
return GeneratedFiles;
}
public void SaveFile(string FileName)
{
try
{
XmlSerializer SerializerObj = new XmlSerializer(typeof(GerberLayoutSet));
// Create a new file stream to write the serialized object to a file
TextWriter WriteFileStream = new StreamWriter(FileName);
SerializerObj.Serialize(WriteFileStream, TheSet);
// Cleanup
WriteFileStream.Close();
}
catch (Exception)
{
}
}
public void RemoveInstance(AngledThing angledThing)
{
if (angledThing.GetType() == typeof(GerberInstance))
{
TheSet.Instances.Remove(angledThing as GerberInstance);
}
else
{
if (angledThing.GetType() == typeof(BreakTab))
{
TheSet.Tabs.Remove(angledThing as BreakTab);
}
}
}
public GerberInstance AddInstance(string path, PointD coord, bool generateTransformed = false)
{
GerberInstance GI = new GerberInstance() { GerberPath = path, Center = coord };
TheSet.Instances.Add(GI);
if (generateTransformed)
{
if (GerberOutlines.ContainsKey(path))
{
var GO = GerberOutlines[path];
foreach (var b in GO.TheGerber.OutlineShapes)
{
PolyLine PL = new PolyLine(PolyLine.PolyIDs.Outline);
PL.FillTransformed(b, GI.Center, GI.Angle);
GI.TransformedOutlines.Add(PL);
}
GI.CreateOffsetLines(TheSet.ExtraTabDrillDistance);
}
}
return GI;
}
public void LoadFile(ProgressLog log, string filename)
{
XmlSerializer SerializerObj = new XmlSerializer(typeof(GerberLayoutSet));
FileStream ReadFileStream = null;
try
{
ReadFileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
// Load the object saved above by using the Deserialize function
GerberLayoutSet newset = (GerberLayoutSet)SerializerObj.Deserialize(ReadFileStream);
if (newset != null)
{
foreach (var a in newset.LoadedOutlines)
{
AddGerberFolder(log, a, false);
}
TheSet = newset;
}
}
catch (Exception)
{
}
// Cleanup
if (ReadFileStream != null) ReadFileStream.Close();
}
public AngledThing FindOutlineUnderPoint(PointD pt)
{
foreach (var b in TheSet.Tabs)
{
if (Helpers.Distance(b.Center, pt) <= b.Radius)
{
return b;
}
}
foreach (var b in TheSet.Instances)
{
// Polygons clips = new Polygons();
if (GerberOutlines.ContainsKey(b.GerberPath))
{
var a = GerberOutlines[b.GerberPath];
int cc = 0;
foreach (var c in b.TransformedOutlines)
{
if (Helpers.IsInPolygon(c.Vertices, pt))
{
cc++;
}
}
if (cc % 2 == 1) return b;
}
}
return null;
}
public void RectanglePack()
{
RectanglePacker RP = new RectanglePacker(TheSet.Width + TheSet.MarginBetweenBoards, TheSet.Height + TheSet.MarginBetweenBoards);
List<Tuple<GerberInstance, double>> Instances = new List<Tuple<GerberInstance, double>>();
foreach (var a in TheSet.Instances)
{
if (GerberOutlines.ContainsKey(a.GerberPath))
{
var OL = GerberOutlines[a.GerberPath];
var Width = OL.TheGerber.BoundingBox.BottomRight.X - OL.TheGerber.BoundingBox.TopLeft.X;
var Height = OL.TheGerber.BoundingBox.BottomRight.Y - OL.TheGerber.BoundingBox.TopLeft.Y;
Instances.Add(new Tuple<GerberInstance, double>(a, Math.Max(Width, Height)));
}
}
foreach (var a in Instances.OrderByDescending(x => x.Item2))
{
if (GerberOutlines.ContainsKey(a.Item1.GerberPath))
{
var OL = GerberOutlines[a.Item1.GerberPath];
RectangleD RD = new RectangleD();
RD.Width = OL.TheGerber.BoundingBox.BottomRight.X - OL.TheGerber.BoundingBox.TopLeft.X;
RD.Height = OL.TheGerber.BoundingBox.BottomRight.Y - OL.TheGerber.BoundingBox.TopLeft.Y;
var Coord = RP.findCoords(RD.Width + TheSet.MarginBetweenBoards, RD.Height + TheSet.MarginBetweenBoards);
// Console.WriteLine("empty space: {0}", RP.GetEmptySpace());
if (Coord != null)
{
a.Item1.Angle = 0;
a.Item1.Center = (Coord - OL.TheGerber.BoundingBox.TopLeft);
// a.Item1.Center.X += 1;
// a.Item1.Center.Y += 1;
}
else
{
Coord = RP.findCoords(RD.Height + TheSet.MarginBetweenBoards, RD.Width + TheSet.MarginBetweenBoards);
// Console.WriteLine("empty space: {0}", RP.GetEmptySpace());
if (Coord != null)
{
a.Item1.Angle = 90;
a.Item1.Center = (Coord - OL.TheGerber.BoundingBox.TopLeft);
a.Item1.Center.Y += (float)RD.Width;
// a.Item1.Center.X += 1;
// a.Item1.Center.Y += 1;
}
else
{
a.Item1.Center.X = -20;
a.Item1.Center.Y = -20;
}
}
}
}
}
Random R = new Random();
public bool MaxRectPack(MaxRectPacker.FreeRectChoiceHeuristic strategy = MaxRectPacker.FreeRectChoiceHeuristic.RectBestAreaFit, double randomness = 0, bool allowrotation = true)
{
bool Succes = true;
//allowrotation = true;
MaxRectPacker MRP = new MaxRectPacker((int)(TheSet.Width + TheSet.MarginBetweenBoards), (int)(TheSet.Height + TheSet.MarginBetweenBoards), allowrotation); // mm is base unit for packing!
List<Tuple<GerberInstance, double>> Instances = new List<Tuple<GerberInstance, double>>();
foreach (var a in TheSet.Instances)
{
if (GerberOutlines.ContainsKey(a.GerberPath))
{
var OL = GerberOutlines[a.GerberPath];
var Width = OL.TheGerber.BoundingBox.BottomRight.X - OL.TheGerber.BoundingBox.TopLeft.X;
var Height = OL.TheGerber.BoundingBox.BottomRight.Y - OL.TheGerber.BoundingBox.TopLeft.Y;
Instances.Add(new Tuple<GerberInstance, double>(a, Math.Max(Width, Height) * (1 - randomness) + randomness * R.NextDouble()));
}
}
List<MaxRectPacker.Rect> InputRects = new List<MaxRectPacker.Rect>();
List<MaxRectPacker.Rect> OutputRects = new List<MaxRectPacker.Rect>();
foreach (var a in Instances.OrderByDescending(x => x.Item2))
{
if (GerberOutlines.ContainsKey(a.Item1.GerberPath))
{
var OL = GerberOutlines[a.Item1.GerberPath];
MaxRectPacker.Rect RD = new MaxRectPacker.Rect() { refobject = a };
RD.width = (int)(Math.Ceiling(OL.TheGerber.BoundingBox.BottomRight.X - OL.TheGerber.BoundingBox.TopLeft.X));
RD.height = (int)(Math.Ceiling(OL.TheGerber.BoundingBox.BottomRight.Y - OL.TheGerber.BoundingBox.TopLeft.Y));
InputRects.Add(RD);
var R = MRP.Insert((int)(RD.width + TheSet.MarginBetweenBoards), (int)(RD.height + TheSet.MarginBetweenBoards), strategy);
if (R.height == 0)
{
// Console.WriteLine("{0} not packed - too big!", a.Item1.GerberPath);
a.Item1.Center.X = -20;
a.Item1.Center.Y = -20;
return false;
}
else
{
if (R.height == (int)(RD.height + TheSet.MarginBetweenBoards))
{
a.Item1.Center = (new PointD(R.x, R.y) - OL.TheGerber.BoundingBox.TopLeft);
a.Item1.Angle = 0;
// regular
}
else
{
a.Item1.Center = (new PointD(R.x, R.y));// - OL.TheGerber.TopLeft).ToF();
a.Item1.Center.X += (float)OL.TheGerber.BoundingBox.TopLeft.Y;
a.Item1.Center.Y -= (float)OL.TheGerber.BoundingBox.TopLeft.X;
a.Item1.Angle = 90;
// a.Item1.Center.Y += RD.width;
a.Item1.Center.X += RD.height;
}
// Console.WriteLine("{0},{1} {2},{3}", R.x, R.y, R.width, R.height);
}
}
}
//MaxRectPacker MRP2 = new MaxRectPacker((int)(TheSet.Width + 2), (int)(TheSet.Height + 2), false); // mm is base unit for packing!
//MRP2.Insert(InputRects, OutputRects, MaxRectPacker.FreeRectChoiceHeuristic.RectBestAreaFit);
//foreach(var a in OutputRects)
//{
// Console.WriteLine("{0}", a.refobject as GerberInstance);
//}
return Succes;
}
public BreakTab AddTab(PointD center)
{
BreakTab BT = new BreakTab() { Radius = 3, Center = center };
TheSet.Tabs.Add(BT);
return BT;
}
public void MergeOverlappingTabs()
{
RemoveAllTabs(true);
List<bool> Tabs = new List<bool>();
List<BreakTab> Removethese = new List<BreakTab>();
for (int i = 0; i < TheSet.Tabs.Count; i++)
{
Tabs.Add(false);
}
for (int i = 0; i < TheSet.Tabs.Count; i++)
{
PointD A = TheSet.Tabs[i].Center;
for (int j = i + 1; j < TheSet.Tabs.Count; j++)
{
if (Tabs[j] == false)
{
PointD B = TheSet.Tabs[j].Center;
if (PointD.Distance(A, B) < (TheSet.Tabs[j].Radius + TheSet.Tabs[i].Radius) * 0.75)
{
Tabs[j] = true;
TheSet.Tabs[i].Center = ((A + B) * 0.5);
Removethese.Add(TheSet.Tabs[j]);
}
}
}
}
foreach (var L in Removethese)
{
TheSet.Tabs.Remove(L);
}
}
}
public class AngledThing
{
public PointD Center = new PointD(); // float for serializer... need to investigate
public float Angle;
}
public class GerberInstance : AngledThing
{
public string GerberPath;
public bool Generated = false;
[System.Xml.Serialization.XmlIgnore]
public List<PolyLine> TransformedOutlines = new List<PolyLine>();
[System.Xml.Serialization.XmlIgnore]
public List<List<PolyLine>> OffsetOutlines = new List<List<PolyLine>>();
[System.Xml.Serialization.XmlIgnore]
internal float LastAngle;
[System.Xml.Serialization.XmlIgnore]
internal PointD LastCenter;
[System.Xml.Serialization.XmlIgnore]
public List<BreakTab> Tabs = new List<BreakTab>();
[System.Xml.Serialization.XmlIgnore]
public Bounds BoundingBox = new Bounds();
public bool IgnoreOutline = false;
internal void CreateOffsetLines(double extradrilldistance)
{
OffsetOutlines = new List<List<PolyLine>>(TransformedOutlines.Count);
for (int i = 0; i < TransformedOutlines.Count; i++)
{
var L = new List<PolyLine>();
Polygons clips = new Polygons();
var poly = TransformedOutlines[i].toPolygon();
bool winding = Clipper.Orientation(poly);
clips.Add(poly);
double offset = 0.25 * 100000.0f + extradrilldistance;
if (winding == false) offset *= -1;
Polygons clips2 = Clipper.OffsetPolygons(clips, offset, JoinType.jtRound);
foreach (var a in clips2)
{
PolyLine P = new PolyLine(PolyLine.PolyIDs.OffsetLine);
P.fromPolygon(a);
L.Add(P);
}
OffsetOutlines.Add(L);
}
}
public void RebuildTransformed(GerberOutline gerberOutline, double extra)
{
BoundingBox.Reset();
LastAngle = Angle;
LastCenter = new PointD(Center.X, Center.Y);
TransformedOutlines = new List<PolyLine>();
var GO = gerberOutline;
foreach (var b in GO.TheGerber.OutlineShapes)
{
PolyLine PL = new PolyLine(-2);
PL.FillTransformed(b, Center, Angle);
TransformedOutlines.Add(PL);
BoundingBox.AddPolyLine(PL);
}
CreateOffsetLines(extra);
}
}
public class BreakTab : AngledThing
{
public float Radius;
public bool Valid;
[System.Xml.Serialization.XmlIgnore]
public List<string> Errors = new List<string>();
[System.Xml.Serialization.XmlIgnore]
public int EvenOdd;
internal void AddError(string v)
{
foreach(var a in Errors)
{
if (a == v) return;
}
Errors.Add(v);
}
}
public class GerberLayoutSet
{
public List<string> LoadedOutlines = new List<string>();
public List<GerberInstance> Instances = new List<GerberInstance>();
public List<BreakTab> Tabs = new List<BreakTab>();
public double Width = 100;
public double Height = 100;
public double MarginBetweenBoards = 2;
public bool ConstructNegativePolygon = false;
public double FillOffset = 3;
public double Smoothing = 1;
public double ExtraTabDrillDistance = 0;
public bool ClipToOutlines = false;
public string LastExportFolder = "";
public bool DoNotGenerateMouseBites = false;
public bool MergeFileTypes = false;
public bool CopyOutlineToTopSilkscreen = false;
public GerberOutline GetOutline(Dictionary<string, GerberOutline> GerberOutlines, string gerberPath)
{
if (GerberOutlines.ContainsKey(gerberPath))
{
return GerberOutlines[gerberPath];
}
else
{
foreach (var k in GerberOutlines.Keys)
{
if (k.EndsWith(gerberPath))
{
return GerberOutlines[k];
}
}
}
return null;
}
public List<string> SaveTo(string OutputFolder, Dictionary<string, GerberOutline> GerberOutlines, ProgressLog Logger)
{
Logger.PushActivity("LayoutSet-SaveTo");
LastExportFolder = OutputFolder;
List<string> GeneratedFiles = new List<string>();
List<String> UnzippedList = new List<string>();
int instanceID = 1;
int current = 0;
BOM MasterBom = new BOM();
BOMNumberSet set = new BOMNumberSet();
foreach (var a in Instances)
{
current++;
BOM InstanceBom = BOM.ScanFolderForBoms(a.GerberPath, Logger);
if (InstanceBom != null && InstanceBom.GetPartCount() > 0)
{
MasterBom.MergeBOM(InstanceBom, set, a.Center.X, a.Center.Y, 0, 0, a.Angle);
}
Logger.AddString("writing " + a.GerberPath, ((float)current / (float)Instances.Count) * 0.3f);
if (a.GerberPath.Contains("???_negative") == false)
{
var outline = GetOutline(GerberOutlines, a.GerberPath);
if (outline != null)
{
List<String> FileList = new List<string>();
if (Directory.Exists(a.GerberPath))
{
FileList = Directory.GetFiles(a.GerberPath).ToList();
}
else
{
if (File.Exists(a.GerberPath) && (Path.GetExtension(a.GerberPath).ToLower() == ".zip" || Path.GetExtension(a.GerberPath).ToLower() == "zip"))
{
string BaseUnzip = Path.Combine(OutputFolder, "unzipped");
if (Directory.Exists(BaseUnzip) == false)
{
Directory.CreateDirectory(BaseUnzip);
}
using (ICSharpCode.SharpZipLib.Zip.ZipFile zip1 = new ICSharpCode.SharpZipLib.Zip.ZipFile(a.GerberPath))
{
foreach (ZipEntry e in zip1)
{
if (!e.IsDirectory)
{
string Unzipped = Path.Combine(BaseUnzip, (instanceID++).ToString() + "_" + Path.GetFileName(e.Name));
if (File.Exists(Unzipped)) File.Delete(Unzipped);
FileList.Add(Unzipped);
UnzippedList.Add(Unzipped);
using (FileStream FS = new FileStream(Unzipped, FileMode.CreateNew))
using (var stream = zip1.GetInputStream(e))
{
stream.CopyTo(FS);
}
}
}
}
}
}
instanceID = AddFilesForInstance(a, OutputFolder, a.Center.X, a.Center.Y, a.Angle, FileList, instanceID, GeneratedFiles, outline, Logger);
instanceID++;
}
}
}
foreach (var a in UnzippedList)
{
try
{
File.Delete(a);
}
catch (Exception)
{
Logger.AddString(String.Format("warning: {0} not deleted: it was locked?", a));
}
}
if (MasterBom.GetPartCount() > 0)
{
string BomFile = Path.Combine(OutputFolder, "MergedBom.csv");
string CentroidFile = Path.Combine(OutputFolder, "MergedCentroids.csv");
MasterBom.SaveBom(BomFile);
MasterBom.SaveCentroids(CentroidFile);
MasterBom.WriteJLCCSV(OutputFolder, "MergedSet_for_JLC", false);
// GeneratedFiles.Add(CentroidFile);
// GeneratedFiles.Add(BomFile);
}
Logger.AddString("done saving?");
Logger.PopActivity();
return GeneratedFiles;
}
private int AddFilesForInstance(GerberInstance inst, string p, double x, double y, double angle, List<string> FileList, int isntid, List<string> GeneratedFiles, GerberOutline outline, ProgressLog Logger)
{
GerberImageCreator GIC = new GerberImageCreator();
GIC.AddBoardsToSet(FileList, new SilentLog());
Logger.PushActivity("AddFilesForInstance");
foreach (var f in FileList)
{
var FileType = Gerber.FindFileType(f);
switch (FileType)
{
case BoardFileType.Drill:
try
{
double scaler = GIC.GetDrillScaler(f);
ExcellonFile EF = new ExcellonFile();
EF.Load(Logger, f, scaler);
string Filename = Path.Combine(p, (isntid++).ToString() + "_" + Path.GetFileName(f));
EF.Write(Filename, x, y, outline.TheGerber.TranslationSinceLoad.X, outline.TheGerber.TranslationSinceLoad.Y, angle);
GeneratedFiles.Add(Filename);
}
catch (Exception E)
{
while (E != null)
{
Logger.AddString("Exception: " + E.Message);
E = E.InnerException;
}
}
break;
case BoardFileType.Gerber:
try
{
string ext = Path.GetExtension(f).ToLower();
string Filename = Path.Combine(p, (isntid++).ToString() + "_" + Path.GetFileName(f));
string sourcefile = f;
string tempfile = "";
BoardSide Side = BoardSide.Unknown;
BoardLayer Layer = BoardLayer.Unknown;
Gerber.DetermineBoardSideAndLayer(f, out Side, out Layer);
if (Layer == BoardLayer.Outline && inst.IgnoreOutline == true)
{
Logger.AddString("Skipping outline");
}
else
{
if (ClipToOutlines)
{
if (Layer == BoardLayer.Silk)
{
tempfile = Path.Combine(p, (isntid++).ToString() + "_" + Path.GetFileName(f));
GerberImageCreator GIC2 = new GerberImageCreator();
GIC2.AddBoardsToSet(FileList, new SilentLog());
GIC2.ClipBoard(f, tempfile, Logger);
sourcefile = tempfile;
}
}
GerberTransposer.Transform(Logger, sourcefile, Filename, x, y, outline.TheGerber.TranslationSinceLoad.X, outline.TheGerber.TranslationSinceLoad.Y, angle);
GeneratedFiles.Add(Filename);
if (tempfile.Length > 0) File.Delete(tempfile);
}
}
catch (Exception E)
{
while (E != null)
{
Logger.AddString("Exception: " + E.Message);
E = E.InnerException;
}
}
break;
}
}
Logger.PopActivity();
return isntid;
}
internal void ClearTransformedOutlines()
{
foreach (var a in Instances)
{
a.TransformedOutlines = new List<PolyLine>();
}
}
}
public class GerberOutline
{
public ParsedGerber TheGerber;
public GerberOutline(ProgressLog log, string filename)
{
if (filename.Length > 0)
{
TheGerber = PolyLineSet.LoadGerberFile(log, filename, true, false, new GerberParserState() { PreCombinePolygons = false });
TheGerber.FixPolygonWindings();
foreach (var a in TheGerber.OutlineShapes)
{
a.CheckIfHole();
}
}
else
{
TheGerber = new ParsedGerber();
}
}
public GerberOutline(ProgressLog log, StreamReader sr, string originalfilename)
{
TheGerber = PolyLineSet.LoadGerberFileFromStream(log, sr, originalfilename, true, false, new GerberParserState() { PreCombinePolygons = false });
TheGerber.FixPolygonWindings();
foreach (var a in TheGerber.OutlineShapes)
{
a.CheckIfHole();
}
}
public PointD GetActualCenter()
{
return TheGerber.BoundingBox.Middle();
}
internal void BuildShapeCache()
{
TheGerber.BuildShapeCache();
}
}
}
5203ce801This file is pinned to the selected release. Download the original to open it in your design tool.