Loading files
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 System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using ClipperLib;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Drawing.Processing;
using Polygon = System.Collections.Generic.List<ClipperLib.IntPoint>;
using Polygons = System.Collections.Generic.List<System.Collections.Generic.List<ClipperLib.IntPoint>>;
using GerberLibrary.Core;
using GerberLibrary.Core.Primitives;
using Pen = GerberLibrary.Core.Primitives.Pen;
using SolidBrush = GerberLibrary.Core.Primitives.SolidBrush;
using static GerberLibrary.PolyLineSet;
namespace GerberLibrary
{
namespace ArtWork
{
public enum BoxAttachment
{
None,
Left,
Right,
Top,
Bottom,
TopLeft,
BottomLeft,
TopRight,
BottomRight
}
public enum ArtLayerStyle
{
OffsetCurves_GoldfishBoard,
CheckerField,
FlowField,
Flower,
ReactDiffuse,
PrototypeEdge
}
public class Functions
{
public static void CreateArtLayersForFolder(ProgressLog log, string foldername, ArtLayerStyle Style)
{
if (Directory.Exists(foldername) == false) return;
string outlinefile = "";
string topfile = "";
string bottomfile = "";
string bottomsilkfile = "";
string topsilkfile = "";
foreach (string s in Directory.GetFiles(foldername))
{
BoardSide Side;
BoardLayer Layer;
if (s.ToLower().Contains("artlayer") == false)
{
Gerber.DetermineBoardSideAndLayer(s, out Side, out Layer);
if (Layer == BoardLayer.Outline)
{
outlinefile = s;
}
if (Layer == BoardLayer.SolderMask)
{
if (Side == BoardSide.Top)
{
topfile = s;
}
else
{
bottomfile = s;
}
}
if (Layer == BoardLayer.Silk)
{
if (Side == BoardSide.Top)
{
topsilkfile = s;
}
else
{
bottomsilkfile = s;
}
}
}
}
if (outlinefile.Length > 0)
{
if (bottomfile.Length > 0) WriteArtLayerFiles(log, outlinefile, bottomfile, bottomsilkfile, Style);
if (topfile.Length > 0 && Style != ArtLayerStyle.PrototypeEdge) WriteArtLayerFiles(log, outlinefile, topfile, topsilkfile, Style);
}
}
public static void WriteArtLayerFiles(ProgressLog log, string outline, string paste, string silk, ArtLayerStyle Style)
{
BoardSide Side;
BoardLayer Layer;
Gerber.DetermineBoardSideAndLayer(paste, out Side, out Layer);
DoArtLayer(log, outline, paste, Path.Combine(Path.GetDirectoryName(outline), Path.GetFileNameWithoutExtension(outline) + "_artlayer" + ((Side == BoardSide.Top) ? ".gto" : ".gbo")), Style, silk);
}
static void DoArtLayer(ProgressLog log, string outline, string soldermask, string target, ArtLayerStyle Style, string silk = "")
{
switch (Style)
{
case ArtLayerStyle.PrototypeEdge:
ArtGenerator_ProtoEdge(log, outline, soldermask, target, silk);
break;
case ArtLayerStyle.OffsetCurves_GoldfishBoard:
ArtGenerator_OffsetCurves(log, outline, soldermask, target, silk);
break;
case ArtLayerStyle.FlowField:
ArtGenerator_FlowField(log, outline, soldermask, target, silk);
break;
case ArtLayerStyle.CheckerField:
ArtGenerator_CheckerField(log, outline, soldermask, target, silk);
break;
case ArtLayerStyle.Flower:
ArtGenerator_Flower(log, outline, soldermask, target, silk);
break;
case ArtLayerStyle.ReactDiffuse:
ArtGenerator_ReactDiffuse(log, outline, soldermask, target, silk);
break;
}
}
private static void ArtGenerator_ProtoEdge(ProgressLog log, string outline, string soldermask, string target, string silk)
{
Polygons CombinedSoldermask = new Polygons();
log.AddString(String.Format("combining outlines.."));
ParsedGerber Outline = PolyLineSet.LoadGerberFile(log, outline);
//Outline.FixPolygonWindings();
Image<Rgba32> B = Image.Load<Rgba32>("protoedge.png");
double Res = 200.0 / 25.4;
B.Mutate(x => x.Flip(FlipMode.Vertical));
GerberLibrary.ArtWork.Functions.WriteBitmapToGerber(target + "_protoedge.gbo", Outline, Res, B, -128, false, true);
}
private static void FindPixel(int[,] inarray, out int x, out int y)
{
for (int xx = 0; xx < inarray.GetLength(0); xx++)
{
for (int yy = 0; yy < inarray.GetLength(1); yy++)
{
if (inarray[xx, yy] == 1)
{
x = xx;
y = yy;
return;
}
}
}
x = -1;
y = -1;
}
private static int CheckPixel(int[,] inarray, int x, int y)
{
if (x < 0 || x >= inarray.GetLength(0)) return -1;
if (y < 0 || y >= inarray.GetLength(1)) return -1;
if (inarray[x, y] == 1) return 1;
return 0;
}
public static ParsedGerber BuildMazeFromBitmap(string filename)
{
ParsedGerber Gerb = new ParsedGerber();
Gerb.Shapes.Clear();
Image<Rgba32> B = Image.Load<Rgba32>(filename);
double aspect = (double)B.Width / (double)B.Height;
double Scale = 20.0f / (double)B.Width;
if (B.Height > B.Width)
{
Scale = 20.0f / (double)B.Height;
}
int[,] M = new int[150, 150];
int xx = 0;
int PixelCount = 0;
// Helper for brightness (HSL lightness)
float GetBrightness(Rgba32 c)
{
float r = c.R / 255f;
float g = c.G / 255f;
float b = c.B / 255f;
float max = Math.Max(r, Math.Max(g, b));
float min = Math.Min(r, Math.Min(g, b));
return (max + min) / 2;
}
for (int x = 0; x < B.Width; x += B.Width / 50)
{
int yy = 0;
for (int y = 0; y < B.Height; y += B.Height / 50)
{
Rgba32 C = B[x, y];
if (GetBrightness(C) < 0.2)
{
M[xx, yy] = 1;
PixelCount++;
}
else
{
M[xx, yy] = 0;
}
yy++;
}
xx++;
}
// Up();
Random R = new Random();
double ScaleB = 25.0f / 50.0f;
while (PixelCount > 0)
{
PolyLine S = new PolyLine(-6);
int sx = 0;
int sy = 0;
FindPixel(M, out sx, out sy);
S.Vertices.Add(new PointD(sx * ScaleB, -sy * ScaleB / aspect + 50));
M[sx, sy] = 0;
PixelCount--;
//aMove(sx * ScaleB , -sy * ScaleB / aspect + 50);
//Down();
List<Point> Checks = new List<Point>();
if (CheckPixel(M, sx - 1, sy) == 1) Checks.Add(new Point(sx - 1, sy));
if (CheckPixel(M, sx + 1, sy) == 1) Checks.Add(new Point(sx + 1, sy));
if (CheckPixel(M, sx, sy - 1) == 1) Checks.Add(new Point(sx, sy - 1));
if (CheckPixel(M, sx, sy + 1) == 1) Checks.Add(new Point(sx, sy + 1));
int len = 0;
while (Checks.Count > 0 && len < 20)
{
len++;
int idx = R.Next(Checks.Count);
sx = Checks[idx].X;
sy = Checks[idx].Y;
M[sx, sy] = 0;
PixelCount--;
S.Vertices.Add(new PointD(sx * ScaleB, -sy * ScaleB / aspect + 50));
//aMove(sx * ScaleB, -sy * ScaleB / aspect + 50);
Checks.Clear();
if (CheckPixel(M, sx - 1, sy) == 1) Checks.Add(new Point(sx - 1, sy));
if (CheckPixel(M, sx + 1, sy) == 1) Checks.Add(new Point(sx + 1, sy));
if (CheckPixel(M, sx, sy - 1) == 1) Checks.Add(new Point(sx, sy - 1));
if (CheckPixel(M, sx, sy + 1) == 1) Checks.Add(new Point(sx, sy + 1));
}
Gerb.Shapes.Add(S);
}
Gerb.Optimize();
return Gerb;
}
public abstract class BounceInterface
{
public abstract void RD_Bounce(RD_Elem[] FieldA, RD_Elem[] FieldB, int iW, int iH, float feed, float kill, double[] distancefield, float du = 0.2097f, float dv = 0.105f);
public abstract void BounceN(int p, RD_Elem[] FieldA, RD_Elem[] FieldB, int iW, int iH, float feedrate, float killrate, double[] DistanceFieldBlur);
}
public static BounceInterface TheBounceInterface = null;
public class RD_Elem
{
public float R;
public float G;
}
private static void ArtGenerator_ReactDiffuse(ProgressLog log, string outline, string soldermask, string target, string silk)
{
if (TheBounceInterface == null) TheBounceInterface = new BasicBounce();
log.AddString(String.Format("Artlayer {0} started: CheckerField", target));
ParsedGerber Outline ;
ParsedGerber SolderMask;
Outline = PolyLineSet.LoadGerberFile(log, outline, true, false, new GerberParserState() { PreCombinePolygons = true});
Outline.FixPolygonWindings();
SolderMask = PolyLineSet.LoadGerberFile(log, soldermask, false, false, new GerberParserState() { PreCombinePolygons = false, MinimumApertureRadius = 0.1});
SolderMask.FixPolygonWindings();
Polygons CombinedOutline = new Polygons();
Polygons CombinedSoldermask = new Polygons();
log.AddString(String.Format("combining outlines.."));
for (int i = 0; i < Outline.OutlineShapes.Count; i++)
{
Polygons clips = new Polygons();
clips.Add(Outline.OutlineShapes[i].toPolygon());
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctXor, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
log.AddString(String.Format("removing paste curves.."));
if (true)
for (int i = 0; i < SolderMask.OutlineShapes.Count; i++)
{
Polygons clips = new Polygons();
clips.Add(SolderMask.OutlineShapes[i].toPolygon());
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
Polygons CombinedSilk = new Polygons();
Polygons OriginalCombinedSilk = new Polygons();
if (silk.Length > 0)
{
log.AddString(String.Format("removing silk curves.."));
ParsedGerber Silk = PolyLineSet.LoadGerberFile(log, silk, false, false, new GerberParserState() { PreCombinePolygons = false, MinimumApertureRadius = 0.1 });
//Silk.FixPolygonWindings();
log.AddString(String.Format("building big polygon.."));
{
Polygons clips = new Polygons();
for (int i = 0; i < Silk.DisplayShapes.Count; i++)
{
clips.Add(Silk.DisplayShapes[i].toPolygon());
}
Clipper cp = new Clipper();
cp.AddPolygons(CombinedSilk, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctUnion, OriginalCombinedSilk, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
}
log.AddString(String.Format("offsetting big polygon.."));
CombinedSilk = Clipper.OffsetPolygons(OriginalCombinedSilk, 1.0 * 100000.0f, JoinType.jtRound, 0.1 * 100000.0f);
{
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(CombinedSilk, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
}
double W = Outline.BoundingBox.BottomRight.X - Outline.BoundingBox.TopLeft.X;
double H = Outline.BoundingBox.BottomRight.Y - Outline.BoundingBox.TopLeft.Y;
double Res = 200.0 / 25.4;
int iW = (int)(W * Res) + 2;
int iH = (int)(H * Res) + 2;
int Range = 10;// Math.Min(iW, iH) / 2;
Image<Rgba32> B = new Image<Rgba32>(iW, iH);
Image<Rgba32> B2 = new Image<Rgba32>(iW, iH);
Image<Rgba32> B3 = new Image<Rgba32>(iW, iH);
var G2 = new ImageSharpGraphicsInterface(B3);
G2.Clear(Color.Black);
double[] DistanceField = new double[iW * iH];
double[] DistanceFieldBlur = new double[iW * iH];
double[] AngleField = new double[iW * iH];
bool DistMode = false;
for (int i = 0; i < iW * iH; i++) { DistanceField[i] = DistMode ? 0 : 100000; AngleField[i] = 0; };
var G = new ImageSharpGraphicsInterface(B);
G.Clear(Color.Black);
log.AddString(String.Format("Rendering Base Bitmap"));
//G.SmoothingMode = SmoothingMode.AntiAlias;
foreach (var a in CombinedOutline)
{
PolyLine P = new PolyLine(PolyLine.PolyIDs.ArtWork);
P.fromPolygon(a);
List<PointF> Pts = new List<PointF>();
foreach (var V in P.Vertices)
{
Pts.Add(new PointF((float)((V.X - Outline.BoundingBox.TopLeft.X) * Res) + 1, (float)((V.Y - Outline.BoundingBox.TopLeft.Y) * Res) + 1));
}
// G.DrawPolygon(new Pen(Color.White, 4), Pts.ToArray()); // GraphicsInterface has DrawPolygon? No, DrawLines or FillPolygon
// GraphicsInterface.DrawPolygon is not standard? GraphicsInterface has DrawLines.
// ImageSharpGraphicsInterface: DrawLines(Pen, PointF[]). Close loop manually?
var penWhite4 = new Pen(Color.White, 4);
G.DrawLines(penWhite4, Pts.ToArray());
if(Pts.Count > 1) G.DrawLine(penWhite4, Pts.Last(), Pts.First()); // Close loop
P.CheckIfHole();
if (P.Hole)
{
G2.FillPolygon(new SolidBrush(Color.Red), Pts.ToArray());
}
else
{
G2.FillPolygon(new SolidBrush(Color.Red), Pts.ToArray());
}
}
foreach (var a in OriginalCombinedSilk)
{
PolyLine P = new PolyLine(PolyLine.PolyIDs.ArtWork);
P.fromPolygon(a);
List<PointF> Pts = new List<PointF>();
foreach (var V in P.Vertices)
{
Pts.Add(new PointF((float)((V.X - Outline.BoundingBox.TopLeft.X) * Res) + 1, (float)((V.Y - Outline.BoundingBox.TopLeft.Y) * Res) + 1));
}
//G.DrawPolygon(new Pen(Color.Red, 9), Pts.ToArray());
var penRed9 = new Pen(Color.Red, 9);
G.DrawLines(penRed9, Pts.ToArray());
if (Pts.Count > 1) G.DrawLine(penRed9, Pts.Last(), Pts.First());
P.CheckIfHole();
if (P.Hole)
{
G2.FillPolygon(new SolidBrush(Color.Red), Pts.ToArray());
}
else
{
G2.FillPolygon(new SolidBrush(Color.Red), Pts.ToArray());
}
}
B.SaveAsPng(target + "_renderbase.png");
log.AddString(String.Format("Calculating Distance Field"));
{
for (int y = 0; y < iH; y++)
{
for (int x = 0; x < iW; x++)
{
var C = B[x, y];
if (C.R > 10)
{
for (int xx = Math.Max(x - Range, 0); xx < Math.Min(iW, x + Range); xx++)
{
for (int yy = Math.Max(y - Range, 0); yy < Math.Min(iH, y + Range); yy++)
{
if (DistMode)
{
double dist = (xx - x) * (xx - x) + (yy - y) * (yy - y);
DistanceField[xx + yy * iW] += 1.0 / (1.0 + dist * 0.1);
}
else
{
double dist = Math.Sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));
if (dist < DistanceField[xx + yy * iW]) DistanceField[xx + yy * iW] = dist;
}
}
}
}
}
}
}
log.AddString(String.Format("Blurring Distance Field"));
double maxblurval = 0;
double minblurval = 100000000000;
{
for (int y = 0; y < iH; y++)
{
for (int x = 0; x < iW; x++)
{
double T = 0;
double amt = 0;
for (int xx = 0; xx < 1; xx++)
{
for (int yy = 0; yy < 1; yy++)
{
double D = xx * xx + yy * yy;
double M = Math.Exp(0);
if (D > 0)
{
M = Math.Exp(-(xx * xx) * 0.01) * Math.Exp(-(yy * yy) * 0.01); ;
}
amt += M;
T += DistanceField[((x + xx + iW) % iW) + ((y + yy + iH) % iH) * iW] * M;
}
}
double newval = T / amt;
DistanceFieldBlur[x + iW * y] = newval;
if (newval > maxblurval) maxblurval = newval;
if (newval < minblurval) minblurval = newval;
}
}
}
{
// G.Clear(Color.Black);
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
var a = DistanceFieldBlur[x + y * iW];
//a = Math.Sqrt(a);
byte R = (byte)((a - minblurval) * 255.0 / (maxblurval - minblurval));
Color C = Color.FromRgb((byte)(R), (byte)(R), (byte)(R));
DistanceFieldBlur[x + y * iW] = (DistanceFieldBlur[x + y * iW] - minblurval) / (maxblurval - minblurval);
B2[x, y] = C;
}
}
B2.SaveAsPng(target + "_renderDistance.png");
}
B.SaveAsPng(target + "_renderbase_afterdistance.png");
log.AddString(String.Format("Calculating DiffusePasses"));
{
int img = 0;
RD_Elem[] FieldA = new RD_Elem[iW * iH];
RD_Elem[] FieldB = new RD_Elem[iW * iH];
Random R = new Random(0);
for (int y = 0; y < iH; y++)
{
for (int x = 0; x < iW; x++)
{
FieldA[x + y * iW] = new RD_Elem();
FieldB[x + y * iW] = new RD_Elem();
}
}
for (int y = 0; y < iH; y++)
{
for (int x = 0; x < iW; x++)
{
var C = B[x, y];
var C0 = B[(x + iW - 1) % iW, y];
var C1 = B[(x + 1) % iW, y];
var C2 = B[x, (y - 1 + iH) % iH];
var C3 = B[x, (y + 1) % iH];
float Gg = (float)R.NextDouble() > 0.3 ? 0.9f : 0;
float Rr = (float)R.NextDouble() > 0.8 ? 0.9f : 0; ;
if (C.R + C1.R + C2.R + C3.R + C0.R < 200)
{ Rr = 0.9f; };
SetF(FieldA, x, y, iW, iH, Gg, Rr);
SetF(FieldB, x, y, iW, iH, 0, 0);
}
}
float feedrate = 0.037f;
float killrate = 0.06f;
int totalbounce = 1000;
for (int i = 0; i < 1; i++)
{
log.AddString(String.Format("bounce {0}/{1}", i, totalbounce));
TheBounceInterface.BounceN(1000, FieldA, FieldB, iW, iH, feedrate, killrate, DistanceFieldBlur);
//TheBounceInterface.RD_Bounce(FieldA, FieldB, iW, iH, feedrate, killrate, DistanceFieldBlur);
//TheBounceInterface.RD_Bounce(FieldB, FieldA, iW, iH, feedrate, killrate, DistanceFieldBlur);
if (i % 100 == 0)
{
float minR = 1000;
float maxR = 0;
float minG = 1000;
float maxG = 0;
for (int y = 0; y < iH; y++)
{
for (int x = 0; x < iW; x++)
{
var F = GetF(FieldA, x, y, iW, iH);
if (F.R > maxR) maxR = F.R;
if (F.R < minR) minR = F.R;
if (F.G > maxG) maxG = F.G;
if (F.G < minG) minG = F.G;
}
}
float scaleR = (float)(255.0f / Math.Max(0.0001f, (maxR - minR)));
float scaleG = (float)(255.0f / Math.Max(0.0001f, (maxR - minR)));
for (int y = 0; y < iH; y++)
{
for (int x = 0; x < iW; x++)
{
var C = B[x, y];
var C0 = B[(x + iW - 1) % iW, y];
var C1 = B[(x + 1) % iW, y];
var C2 = B[x, (y - 1 + iH) % iH];
var C3 = B[x, (y + 1) % iH];
var F = GetF(FieldA, x, y, iW, iH);
byte c1 = (byte)(255 - ((F.R - minR) * scaleR));
byte c2 = (byte)((F.G - minG) * scaleG);
// c1 =(byte)( c1 >= 200 ? 255 : 0);
B2[x, y] = Color.FromRgb((byte)(c1), (byte)(c1), (byte)(c1));
}
}
B2.SaveAsPng(target + "_" + (img++).ToString() + ".png");
}
/*
if (false)//i>= 1500 && ((i % 400) == 0 ) && i< 2300)
{
forceiter++;
for (int y = 0; y < iH; y++)
{
for (int x = 0; x < iW; x++)
{
var C = B[x, y];
var C0 = B[(x + iW - 1) % iW, y];
var C1 = B[(x + 1) % iW, y];
var C2 = B[x, (y - 1 + iH] % iH);
var C3 = B[x, (y + 1] % iH);
var ggG = C.G + C1.G + C2.G + C3.G + C0.G;
var rrR = C.R + C1.R + C2.R + C3.R + C0.R;
// double Gg = 0;
if (ggG > 200)
{
var F = GetF(FieldA, x, y, iW, iH);
var OG = F.G;
var OR = F.R;
F.G = OG * 0.2f + OR * 0.8f;
F.R = OR * 0.2f + OG * 0.8f; ;
//FieldA[x, y].G -= .002 - (forceiter * 0.0003); ;
}
else
{
if (rrR > 200)
{
var F = GetF(FieldA, x, y, iW, iH);
var OG = F.G;
var OR = F.R;
F.G = OG * 0.1f + OR * 0.9f;
F.R = OR * 0.1f + OG * 0.9f; ;
//FieldA[x, y].G -= .002 - (forceiter * 0.0003); ;
}
}
}
}
}
*/
}
}
log.AddString(String.Format("Converting to gerber.."));
WriteBitmapToGerber(target, Outline, Res, B2, 128);
log.AddString(String.Format("Done"));
}
private static void SetF(RD_Elem[] FieldB, int x, int y, int iW, int iH, float p1, float p2)
{
x = (x + iW) % iW;
y = (y + iH) % iH;
FieldB[x + y * iW].R = p1;
FieldB[x + y * iW].G = p2;
}
static RD_Elem GetF(RD_Elem[] FieldA, int x, int y, int iW, int iH)
{
x = (x + iW) % iW;
y = (y + iH) % iH;
return FieldA[x + y * iW];
}
private static void ArtGenerator_Flower(ProgressLog log, string outline, string soldermask, string target, string silk)
{
log.AddString(String.Format("Artlayer {0} started: CheckerField", target));
ParsedGerber Outline = PolyLineSet.LoadGerberFile(log, outline, true, false, new GerberParserState() { PreCombinePolygons = true});
Outline.FixPolygonWindings();
ParsedGerber SolderMask = PolyLineSet.LoadGerberFile(log, soldermask, false, false, new GerberParserState() {PreCombinePolygons = false, MinimumApertureRadius = 0.1 });
SolderMask.FixPolygonWindings();
//Silk.FixPolygonWindings();
Polygons CombinedOutline = new Polygons();
Polygons CombinedSoldermask = new Polygons();
log.AddString(String.Format("combining outlines.."));
for (int i = 0; i < Outline.OutlineShapes.Count; i++)
{
Polygons clips = new Polygons();
clips.Add(Outline.OutlineShapes[i].toPolygon());
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctXor, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
log.AddString(String.Format("removing paste curves.."));
if (true)
for (int i = 0; i < SolderMask.OutlineShapes.Count; i++)
{
Polygons clips = new Polygons();
clips.Add(SolderMask.OutlineShapes[i].toPolygon());
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
if (silk.Length > 0)
{
log.AddString(String.Format("removing silk curves.."));
ParsedGerber Silk = PolyLineSet.LoadGerberFile(log, silk, false, false, new GerberParserState() {PreCombinePolygons = false, MinimumApertureRadius = 0.1 });
//Silk.FixPolygonWindings();
Polygons CombinedSilk = new Polygons();
log.AddString(String.Format("building big polygon.."));
{
Polygons clips = new Polygons();
for (int i = 0; i < Silk.DisplayShapes.Count; i++)
{
clips.Add(Silk.DisplayShapes[i].toPolygon());
}
Clipper cp = new Clipper();
cp.AddPolygons(CombinedSilk, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctUnion, CombinedSilk, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
}
log.AddString(String.Format("offsetting big polygon.."));
CombinedSilk = Clipper.OffsetPolygons(CombinedSilk, 1.0 * 100000.0f, JoinType.jtRound, 0.1 * 100000.0f);
{
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(CombinedSilk, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
}
double W = Outline.BoundingBox.BottomRight.X - Outline.BoundingBox.TopLeft.X;
double H = Outline.BoundingBox.BottomRight.Y - Outline.BoundingBox.TopLeft.Y;
double Res = 200.0 / 25.4;
int iW = (int)(W * Res) + 10;
int iH = (int)(H * Res) + 10;
int Range = Math.Max(iW, iH);
Image<Rgba32> B = new Image<Rgba32>(iW, iH);
Image<Rgba32> B2 = new Image<Rgba32>(iW, iH);
Image<Rgba32> B3 = new Image<Rgba32>(iW, iH);
var G2 = new ImageSharpGraphicsInterface(B3);
G2.Clear(Color.Black);
double[] DistanceField = new double[iW * iH];
double[] DistanceFieldBlur = new double[iW * iH];
double[] AngleField = new double[iW * iH];
bool DistMode = true;
for (int i = 0; i < iW * iH; i++) { DistanceField[i] = DistMode ? 0 : 100000; AngleField[i] = 0; };
var G = new ImageSharpGraphicsInterface(B);
G.Clear(Color.Black);
log.AddString(String.Format("Rendering Base Bitmap"));
// G.SmoothingMode = SmoothingMode.AntiAlias;
foreach (var a in CombinedOutline)
{
PolyLine P = new PolyLine(PolyLine.PolyIDs.ArtWork);
P.fromPolygon(a);
List<PointF> Pts = new List<PointF>();
foreach (var V in P.Vertices)
{
Pts.Add(new PointF((float)((V.X - Outline.BoundingBox.TopLeft.X) * Res) + 1, (float)((V.Y - Outline.BoundingBox.TopLeft.Y) * Res) + 1));
}
G.DrawPolygon(new Pen(Color.White, 1), Pts.ToArray());
P.CheckIfHole();
if (P.Hole)
{
G2.FillPolygon(new SolidBrush(Color.Green), Pts.ToArray());
}
else
{
G2.FillPolygon(new SolidBrush(Color.Red), Pts.ToArray());
}
}
B.SaveAsPng(target + "_renderbase.png");
log.AddString(String.Format("Calculating Distance Field"));
{
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
var C = B[x, y];
if (C.R > 10)
{
for (int xx = Math.Max(x - Range, 0); xx < Math.Min(iW, x + Range); xx++)
{
for (int yy = Math.Max(y - Range, 0); yy < Math.Min(iH, y + Range); yy++)
{
if (DistMode)
{
double dist = (xx - x) * (xx - x) + (yy - y) * (yy - y);
DistanceField[xx + yy * iW] += 1.0 / (1.0 + dist * 0.1);
}
else
{
double dist = Math.Sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));
if (dist < DistanceField[xx + yy * iW]) DistanceField[xx + yy * iW] = dist;
}
}
}
}
}
}
}
log.AddString(String.Format("Blurring Distance Field"));
double maxblurval = 0;
double minblurval = 100000000000;
{
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
double T = 0;
double amt = 0;
for (int xx = 0; xx < 1; xx++)
{
for (int yy = 0; yy < 1; yy++)
{
double D = xx * xx + yy * yy;
double M = Math.Exp(0);
if (D > 0)
{
M = Math.Exp(-(xx * xx) * 0.01) * Math.Exp(-(yy * yy) * 0.01); ;
}
amt += M;
T += DistanceField[((x + xx + iW) % iW) + ((y + yy + iH) % iH) * iW] * M;
}
}
double newval = T / amt;
DistanceFieldBlur[x + iW * y] = newval;
if (newval > maxblurval) maxblurval = newval;
if (newval < minblurval) minblurval = newval;
}
}
}
{
G.Clear(Color.Black);
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
var a = DistanceFieldBlur[x + y * iW];
//a = Math.Sqrt(a);
byte R = (byte)((a - minblurval) * 255.0 / (maxblurval - minblurval));
Color C = Color.FromRgb((byte)(R / 2 + 127), (byte)(R / 2 + 127), (byte)(R / 2 + 127));
DistanceFieldBlur[x + y * iW] = (DistanceFieldBlur[x + y * iW] - minblurval) / (maxblurval - minblurval);
B[x, y] = C;
}
}
B.SaveAsPng(target + "_renderDistance.png");
}
log.AddString(String.Format("Calculating Flower Field and Artwork"));
GerberArtWriter GOW = new GerberArtWriter();
{
// G.Clear(Color.Black);
for (int x = 0; x < iW - 1; x++)
{
for (int y = 0; y < iH - 1; y++)
{
var a = DistanceFieldBlur[x + y * iW];
var b = DistanceFieldBlur[(x + 1) + y * iW];
var c = DistanceFieldBlur[x + (y + 1) * iW];
V3 AA = new V3(0, 0, Math.Sqrt(a) / (double)Range);
V3 BB = new V3(1, 0, Math.Sqrt(b) / (double)Range);
V3 CC = new V3(0, 1, Math.Sqrt(c) / (double)Range);
var D1 = BB - AA;
var D2 = CC - AA;
var C1 = V3.Cross(D1, D2);
// var DistT = Math.Sqrt(C1.X * C1.X + C1.Y * C1.Y + C1.Z * C1.Z);
var AngleRad = Math.Atan2(C1.Y, C1.X);
AngleField[x + (y * iW)] = AngleRad;
}
}
// G.Clear(Color.Black);
var GG = new ImageSharpGraphicsInterface(B2);
GG.Clear(Color.Gray);
FlowerThing FT = new FlowerThing();
FT.Build(DistanceFieldBlur, AngleField, iW, iH, CombinedOutline);
//GG.DrawImage(B, 0, 0); // Need to check if DrawImage B is feasible directly or need another overload
// Assuming ImageSharpGraphicsInterface has DrawImage(Image<Rgba32> ...)
GG.DrawImage(B, 0, 0, iW, iH);
//GG.SmoothingMode = SmoothingMode.AntiAlias;
//GG.CompositingQuality = CompositingQuality.HighQuality;
// GG.InterpolationMode = InterpolationMode.HighQualityBicubic;
foreach (var a in FT.Items)
{
a.Draw(GG);
}
B2.SaveAsPng(target + "_FlowerPrint.png");
}
GOW.Write(target);
log.AddString(String.Format("Done"));
}
public class FlowerThing
{
public class Item
{
public virtual void Draw(GraphicsInterface G)
{
}
}
public class Leaf : Item
{
public V3 Start;
public double Length;
public double Width;
public double Angle;
public override void Draw(GraphicsInterface G)
{
V3 begin = new V3(Start.X, Start.Y, 0);
V3 end = new V3(begin.X, begin.Y, 0);
V3 Anglev = new V3(Math.Sin(Angle), Math.Cos(Angle), 0);
V3 sideAnglev = new V3(Math.Sin(Angle + Math.PI / 2), Math.Cos(Angle + Math.PI / 2), 0);
begin -= Anglev * Length * 1.2;
end -= Anglev * Length * 0.4;
List<PointF> Points = new List<PointF>();
List<PointF> SymPoints = new List<PointF>();
List<V3> LeftSide = new List<V3>();
List<V3> RightSide = new List<V3>();
for (int i = 0; i < (int)Length; i++)
{
V3 mid = begin + Anglev * i;
V3 left = mid + sideAnglev * Math.Sin(Math.Pow(i / (double)Length, 1.1) * Math.PI) * Length / 5.0;
V3 right = mid - sideAnglev * Math.Sin(Math.Pow(i / (double)Length, 1.2) * Math.PI) * Length / 5.0;
LeftSide.Add(left);
RightSide.Add(right);
}
RightSide.Reverse();
LeftSide.AddRange(RightSide); ;
foreach (var P in LeftSide)
{
Points.Add(new PointF((float)P.X, (float)P.Y));
}
if (Points.Count > 0)
{
Pen Pp = new Pen(Color.Black, 1);
G.FillPolygon(new SolidBrush(Color.White), Points.ToArray());
G.DrawPolygon(Pp, Points.ToArray());
G.DrawLine(Pp, (float)begin.X, (float)begin.Y, (float)end.X, (float)end.Y);
G.DrawLine(Pp, (float)Start.X, (float)Start.Y, (float)end.X, (float)end.Y);
}
}
}
public class Stem : Item
{
public V3 Start;
public V3 End;
public double Width;
public int Mode;
public override void Draw(GraphicsInterface G)
{
Color C = Color.Black;
Color C2 = Color.Black;
switch (Mode)
{
case 0: C = Color.Orange; break;
case 1: C = Color.Red; break;
case 2: C = Color.Blue; break;
case 3: C = Color.Green; break;
case 4: C = Color.Yellow; break;
case 6: C = Color.Purple; break;
}
Pen Pp = new Pen(C, (float)Width);
Pp.StartCap = LineCap.Round;
Pp.EndCap = LineCap.Round;
//Pp.DashCap = DashCap.Round; // Not supported in Pen struct yet
G.DrawLine(Pp, (float)Start.X, (float)Start.Y, (float)End.X, (float)End.Y);
}
}
public List<Item> Items = new List<Item>();
double theWidth = 0;
double theHeight = 0;
int iWidth = 0;
int iHeight = 0;
double[] DistanceField;
double[] AngleField;
public void Build(double[] _DistanceFieldBlur, double[] _AngleField, int iW, int iH, Polygons Outline = null)
{
iWidth = iW;
iHeight = iH;
AngleField = _AngleField;
DistanceField = _DistanceFieldBlur;
theWidth = iW;
theHeight = iH;
for (double x = theWidth * 0.1; x <= theWidth * 0.9; x += theWidth / 5)
{
for (double y = theHeight * 0.1; y <= theHeight * 0.9; y += theHeight / 5)
{
if (Outline != null)
{
bool inside = false;
foreach (var a in Outline)
{
PolyLine PL = new PolyLine(PolyLine.PolyIDs.ArtWork);
PL.fromPolygon(a);
if (Helpers.IsInPolygon(PL.Vertices, new PointD(x, y))) inside = !inside;
} if (inside) Branch(x, y, Math.PI / 2, 80);
}
else
{
Branch(x, y, 0, 80);
}
}
}
}
Random R = new Random();
void Branch(double x, double y, double initialdir, double count, int depth = 0)
{
double LeafScale = 1.5;
List<Item> Stems = new List<Item>();
List<Item> Leafs = new List<Item>();
if (depth > 5) return;
V3 center = new V3(0, 0, 0);
double dir = initialdir;
double ddir = 0;
double MX = x;
double MY = y;
for (double i = 0; i < count; i += 0.6)
{
double W = theWidth;
double H = theHeight;
double nX = center.X + MX;
double nY = center.Y + MY;
int mode = 0;
double maxmarg = 0.95;
double minmarg = 0.05;
if (nX < W * minmarg || nX > W * maxmarg || nY < H * minmarg || nY > H * maxmarg)
{
double Hoek = Math.PI + Math.Atan2(H / 2 - nY, W / 2 - nX);
double DHoek = dir - Hoek;
//dir = Hoek;
// ddir = 0;
// mode = 1;
if (DHoek > Math.PI) DHoek -= Math.PI * 2;
if (DHoek < -Math.PI) DHoek += Math.PI * 2;
if (DHoek > Math.PI / 2) DHoek = Math.PI - DHoek;
if (DHoek < -Math.PI / 2) DHoek = -Math.PI - DHoek;
if (DHoek > Math.PI || DHoek < -Math.PI)
{
mode = 1;
}
else
{
if (DHoek > 0) ddir = 0.4; else ddir = -0.4;
mode = 1;
}
// curveback!
}
if (mode == 0 && DistanceField != null)
{
double Distance = DistanceField[(int)nX + (int)nY * iWidth];
if (Distance > .06)
{
//double Hoek = AngleField[(int)nX + (int)nY * iWidth]-Math.PI/2;
double Hoek = Math.PI + AngleField[(int)nX + (int)nY * iWidth];
double DHoek = dir - Hoek;
if (DHoek > Math.PI) DHoek -= Math.PI * 2;
if (DHoek < -Math.PI) DHoek += Math.PI * 2;
if (DHoek > Math.PI / 2) DHoek = Math.PI - DHoek;
if (DHoek < -Math.PI / 2) DHoek = -Math.PI - DHoek;
mode = 4;
if (DHoek > 0) ddir = 0.4; else ddir = -0.4;
// ddir = 0;
// mode = 4;
}
}
if (mode == 0)
{
if (R.NextDouble() < 0.2)
{
double Hoek = -Math.PI / 2;
double DHoek = dir - Hoek;
if (DHoek < 0) ddir = 0.2; else ddir = -0.2;
mode = 2;
// curveup!
}
else
{
if (R.NextDouble() < 0.2 && AngleField != null)
{
double Hoek = AngleField[(int)nX + (int)nY * iWidth];
double DHoek = dir - Hoek;
if (DHoek > Math.PI) DHoek -= Math.PI * 2;
if (DHoek < -Math.PI) DHoek += Math.PI * 2;
if (DHoek > Math.PI / 2) DHoek = Math.PI - DHoek;
if (DHoek < -Math.PI / 2) DHoek = -Math.PI - DHoek;
mode = 3;
if (DHoek > 0) ddir = 0.4; else ddir = -0.4;
}
else
{
mode = 6;
ddir += (R.NextDouble() - 0.5) * 1;
}
}
ddir *= 0.7;
}
dir += ddir;
// if (dir > Math.PI * 2) dir -= Math.PI;
// if (dir < - Math.PI * 2) dir += Math.PI;
double stemlen = 0.2 * (10 + 0.8 * (count - i));
if (mode == 1) stemlen *= 0.3;
if (mode == 4) stemlen *= 0.4;
V3 newcenter = center + (new V3(Math.Cos(dir), Math.Sin(dir), 0)) * stemlen;
float branchW = (float)((count - i) * 0.06);
Stems.Add(new Stem() { Mode = mode, Start = new V3((float)(MX + newcenter.X), (float)(MY + newcenter.Y), 0), End = new V3((float)(MX + center.X), (float)(MY + center.Y), 0), Width = branchW });
center = newcenter;
double A1 = -(dir + 0.8) + (R.NextDouble() - 0.5) * 0.5;
double A2 = Math.PI - (dir - 0.8) + (R.NextDouble() - 0.5) * 0.5;
if (mode != 1 && mode != 4)
{
if (R.NextDouble() > 0.8)
{
Leafs.Add(new Leaf() { Start = new V3(MX + center.X, MY + center.Y, 0), Angle = A1, Length = (10 + R.NextDouble() * 10) * LeafScale, Width = 0 });
}
if (R.NextDouble() > 0.8)
{
Leafs.Add(new Leaf() { Start = new V3(MX + center.X, MY + center.Y, 0), Angle = A2, Length = (10 + R.NextDouble() * 10) * LeafScale, Width = 0 });
}
if (R.NextDouble() > 0.999)
{
double minbranch = 30 * (Math.PI * 2.0 / 360.0);
double maxbranch = 30 * (Math.PI * 2.0 / 360.0);
double B = (maxbranch - minbranch) * R.NextDouble() + minbranch;
if (R.NextDouble() < 0.5) B = -B;
Branch(center.X + MX, center.Y + MY, dir + B, (count - i) * 0.8, depth + 1);
}
}
Items.AddRange(Stems);
Items.AddRange(Leafs);
// Drawleaf(G, MX - center.X, MY - center.Y, A1, 60 + R.NextDouble() * 20);
// Drawleaf(G, MX - center.X, MY - center.Y, A2, 60 + R.NextDouble() * 20);
// Drawleaf(G, MX - center.X, MY + center.Y, A1, 60 + R.NextDouble() * 20);
// Drawleaf(G, MX - center.X, MY + center.Y, A2, 60 + R.NextDouble() * 20);
}
}
}
private static void ArtGenerator_CheckerField(ProgressLog log, string outline, string soldermask, string target, string silk)
{
log.AddString(String.Format("Artlayer {0} started: CheckerField", target));
ParsedGerber Outline = PolyLineSet.LoadGerberFile(log, outline, true, false, new GerberParserState() { PreCombinePolygons = true });
Outline.FixPolygonWindings();
ParsedGerber SolderMask = PolyLineSet.LoadGerberFile(log, soldermask, false, false, new GerberParserState() { PreCombinePolygons = false, MinimumApertureRadius = 0.1});
SolderMask.FixPolygonWindings();
//Silk.FixPolygonWindings();
Polygons CombinedOutline = new Polygons();
Polygons CombinedSoldermask = new Polygons();
log.AddString(String.Format("combining outlines.."));
for (int i = 0; i < Outline.OutlineShapes.Count; i++)
{
Polygons clips = new Polygons();
clips.Add(Outline.OutlineShapes[i].toPolygon());
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctXor, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
log.AddString(String.Format("removing paste curves.."));
if (true)
for (int i = 0; i < SolderMask.OutlineShapes.Count; i++)
{
Polygons clips = new Polygons();
clips.Add(SolderMask.OutlineShapes[i].toPolygon());
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
if (silk.Length > 0)
{
log.AddString(String.Format("removing silk curves.."));
ParsedGerber Silk = PolyLineSet.LoadGerberFile(log, silk, false, false, new GerberParserState() { PreCombinePolygons = false, MinimumApertureRadius = 0.1 });
//Silk.FixPolygonWindings();
Polygons CombinedSilk = new Polygons();
log.AddString(String.Format("building big polygon.."));
{
Polygons clips = new Polygons();
for (int i = 0; i < Silk.DisplayShapes.Count; i++)
{
clips.Add(Silk.DisplayShapes[i].toPolygon());
}
Clipper cp = new Clipper();
cp.AddPolygons(CombinedSilk, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctUnion, CombinedSilk, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
}
log.AddString(String.Format("offsetting big polygon.."));
CombinedSilk = Clipper.OffsetPolygons(CombinedSilk, 1.0 * 100000.0f, JoinType.jtRound, 0.1 * 100000.0f);
{
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(CombinedSilk, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
}
double W = Outline.BoundingBox.BottomRight.X - Outline.BoundingBox.TopLeft.X;
double H = Outline.BoundingBox.BottomRight.Y - Outline.BoundingBox.TopLeft.Y;
double Res = 200.0 / 25.4;
int iW = (int)(W * Res) + 2;
int iH = (int)(H * Res) + 2;
int Range = Math.Max(iW, iH);
Image<Rgba32> B = new Image<Rgba32>(iW, iH);
Image<Rgba32> B2 = new Image<Rgba32>(iW, iH);
Image<Rgba32> B3 = new Image<Rgba32>(iW, iH);
var G2 = new ImageSharpGraphicsInterface(B3);
G2.Clear(Color.Black);
double[] DistanceField = new double[iW * iH];
double[] DistanceFieldBlur = new double[iW * iH];
double[] AngleField = new double[iW * iH];
bool DistMode = false;
for (int i = 0; i < iW * iH; i++) { DistanceField[i] = DistMode ? 0 : 100000; AngleField[i] = 0; };
var G = new ImageSharpGraphicsInterface(B);
G.Clear(Color.Black);
log.AddString(String.Format("Rendering Base Bitmap"));
// G.SmoothingMode = SmoothingMode.AntiAlias;
foreach (var a in CombinedOutline)
{
PolyLine P = new PolyLine(PolyLine.PolyIDs.ArtWork);
P.fromPolygon(a);
List<PointF> Pts = new List<PointF>();
foreach (var V in P.Vertices)
{
Pts.Add(new PointF((float)((V.X - Outline.BoundingBox.TopLeft.X) * Res) + 1, (float)((V.Y - Outline.BoundingBox.TopLeft.Y) * Res) + 1));
}
var penWhite = new Pen(Color.White, 1);
G.DrawLines(penWhite, Pts.ToArray());
if (Pts.Count > 1) G.DrawLine(penWhite, Pts.Last(), Pts.First());
P.CheckIfHole();
if (P.Hole)
{
G2.FillPolygon(new SolidBrush(Color.Green), Pts.ToArray());
}
else
{
G2.FillPolygon(new SolidBrush(Color.Red), Pts.ToArray());
}
}
B.SaveAsPng(target + "_renderbase.png");
log.AddString(String.Format("Calculating Distance Field"));
{
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
var C = B[x, y];
if (C.R > 10)
{
for (int xx = Math.Max(x - Range, 0); xx < Math.Min(iW, x + Range); xx++)
{
for (int yy = Math.Max(y - Range, 0); yy < Math.Min(iH, y + Range); yy++)
{
if (DistMode)
{
double dist = (xx - x) * (xx - x) + (yy - y) * (yy - y);
DistanceField[xx + yy * iW] += 1.0 / (1.0 + dist * 0.1);
}
else
{
double dist = Math.Sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));
if (dist < DistanceField[xx + yy * iW]) DistanceField[xx + yy * iW] = dist;
}
}
}
}
}
}
}
log.AddString(String.Format("Blurring Distance Field"));
double maxblurval = 0;
double minblurval = 100000000000;
{
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
double T = 0;
double amt = 0;
for (int xx = 0; xx < 1; xx++)
{
for (int yy = 0; yy < 1; yy++)
{
double D = xx * xx + yy * yy;
double M = Math.Exp(0);
if (D > 0)
{
M = Math.Exp(-(xx * xx) * 0.01) * Math.Exp(-(yy * yy) * 0.01); ;
}
amt += M;
T += DistanceField[((x + xx + iW) % iW) + ((y + yy + iH) % iH) * iW] * M;
}
}
double newval = T / amt;
DistanceFieldBlur[x + iW * y] = newval;
if (newval > maxblurval) maxblurval = newval;
if (newval < minblurval) minblurval = newval;
}
}
}
{
G.Clear(Color.Black);
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
var a = DistanceFieldBlur[x + y * iW];
//a = Math.Sqrt(a);
Color C = Color.FromRgb((byte)((a - minblurval) * 255.0 / (maxblurval - minblurval)), (byte)0, (byte)0);
B[x, y] = C;
}
}
B.SaveAsPng(target + "_renderDistance.png");
}
log.AddString(String.Format("Calculating Angle Field and Artwork"));
{
G.Clear(Color.Black);
for (int x = 0; x < iW - 1; x++)
{
for (int y = 0; y < iH - 1; y++)
{
var a = DistanceFieldBlur[x + y * iW];
var b = DistanceFieldBlur[(x + 1) + y * iW];
var c = DistanceFieldBlur[x + (y + 1) * iW];
V3 AA = new V3(0, 0, Math.Sqrt(a) / (double)Range);
V3 BB = new V3(1, 0, Math.Sqrt(b) / (double)Range);
V3 CC = new V3(0, 1, Math.Sqrt(c) / (double)Range);
var D1 = BB - AA;
var D2 = CC - AA;
var C1 = V3.Cross(D1, D2);
var AngleRad = Math.Atan2(C1.Y, C1.X);
var AngleDeg = AngleRad * 360.0 / (Math.PI * 2);
double S = Math.Sin(AngleRad * 7.0 + 0.2);
while (AngleDeg < 0) AngleDeg += 360;
byte vv = (byte)((S > 0.30) ? 255 : 0);
Color C = Color.FromRgb((byte)(255 - Math.Min(255, (int)(AngleDeg * (255.0 / 360.0)))), (byte)0, (byte)0);
if (B3[x, y].R == 255)
{
Color C2 = Color.FromRgb((byte)(vv), (byte)(vv), (byte)(vv));
B2[x, y] = C2;
}
else
{
B2[x, y] = Color.Black;
}
B[x, y] = C;
}
}
B.SaveAsPng(target + "_renderAngle.png");
B2.SaveAsPng(target + "_artwork.png");
}
log.AddString(String.Format("Converting to gerber.."));
WriteBitmapToGerber(target, Outline, Res, B2);
log.AddString(String.Format("Done"));
}
public static void WriteBitmapToGerber(string target, ParsedGerber Outline, double ResX, Image<Rgba32> B2, int threshold = 0, bool fittooutline = true, bool cliptooutline = false)
{
int iW = B2.Width;
int iH = B2.Height;
double ResY = ResX;
if (fittooutline)
{
ResX = 1.0 / ((Outline.BoundingBox.BottomRight.X - Outline.BoundingBox.TopLeft.X) / (B2.Width - 1));
ResY = 1.0 / ((Outline.BoundingBox.BottomRight.Y - Outline.BoundingBox.TopLeft.Y) / (B2.Height - 1.0));
}
else{
if (cliptooutline) {
var w = Outline.BoundingBox.Width();
var h = Outline.BoundingBox.Height();
iW = Math.Min(iW, (int)Math.Ceiling(w * ResX));
iH = Math.Min(iH, (int)Math.Ceiling(h * ResY));
}
}
bool invert = false;
GerberArtWriter GOW = new GerberArtWriter();
if (threshold < 0)
{
threshold = -threshold;
invert = true;
}
int startlevel = 0;
int endlevel;
for (int y = 0; y < iH; y++)
{
double sx = -1;
bool active = false;
for (int x = 0; x < iW; x++)
{
Rgba32 C = B2[x, y];
if ((invert == false && C.R > threshold) || (invert == true && C.R < threshold))
{
if (active == false)
{
sx = x;
startlevel = C.R;
active = true;
}
}
else
{
if (active)
{
endlevel = C.R;
active = false;
PolyLine pL = new PolyLine(PolyLine.PolyIDs.Bitmap);
double offL = ((startlevel - Math.Abs(threshold)) / 128.0) * 1.0 / ResX;
double offR = ((endlevel - Math.Abs(threshold)) / 128.0) * 1.0 / ResX;
if (invert == false)
{
offL = -offL;
offR = -offR;
}
// offL *= -1;
// offR *= -1;
pL.Add(sx / ResX + offL, ((double)y) / ResY);
pL.Add(((double)x) / ResX - offR, ((double)y) / ResY);
if (Outline != null) pL.Translate(Outline.BoundingBox.TopLeft.X, Outline.BoundingBox.TopLeft.Y);
pL.Translate(0, .50 / ResY);
GOW.AddPolyLine(pL, 1.0 / ResY);
}
}
}
if (active)
{
PolyLine pL = new PolyLine(PolyLine.PolyIDs.Bitmap);
double offL = ((startlevel - Math.Abs(threshold)) / 128.0) * .50 / ResX;
pL.Add(sx / ResX + offL, ((double)y) / ResY);
pL.Add((iW) / ResX, ((double)y) / ResY);
if (Outline != null) pL.Translate(Outline.BoundingBox.TopLeft.X, Outline.BoundingBox.TopLeft.Y);
pL.Translate(0, .50 / ResY);
GOW.AddPolyLine(pL, 1.0 / ResY);
}
}
GOW.Write(target,null, .50 / ResX, 1.0 / ResY);
}
private static void ArtGenerator_FlowField(ProgressLog log, string outline, string soldermask, string target, string silk)
{
log.AddString(String.Format("Artlayer {0} started: CheckerField", target));
ParsedGerber Outline = PolyLineSet.LoadGerberFile(log, outline, true, false, new GerberParserState() { PreCombinePolygons = true});
Outline.FixPolygonWindings();
ParsedGerber SolderMask = PolyLineSet.LoadGerberFile(log, soldermask, false, false, new GerberParserState() {PreCombinePolygons = false, MinimumApertureRadius = 0.1 });
SolderMask.FixPolygonWindings();
//Silk.FixPolygonWindings();
Polygons CombinedOutline = new Polygons();
Polygons CombinedSoldermask = new Polygons();
log.AddString(String.Format("combining outlines.."));
for (int i = 0; i < Outline.OutlineShapes.Count; i++)
{
Polygons clips = new Polygons();
clips.Add(Outline.OutlineShapes[i].toPolygon());
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctXor, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
log.AddString(String.Format("removing paste curves.."));
if (true)
for (int i = 0; i < SolderMask.OutlineShapes.Count; i++)
{
Polygons clips = new Polygons();
clips.Add(SolderMask.OutlineShapes[i].toPolygon());
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
if (silk.Length > 0)
{
log.AddString(String.Format("removing silk curves.."));
ParsedGerber Silk = PolyLineSet.LoadGerberFile(log, silk, false, false, new GerberParserState() { PreCombinePolygons = false, MinimumApertureRadius = 0.1 });
//Silk.FixPolygonWindings();
Polygons CombinedSilk = new Polygons();
log.AddString(String.Format("building big polygon.."));
{
Polygons clips = new Polygons();
for (int i = 0; i < Silk.DisplayShapes.Count; i++)
{
clips.Add(Silk.DisplayShapes[i].toPolygon());
}
Clipper cp = new Clipper();
cp.AddPolygons(CombinedSilk, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctUnion, CombinedSilk, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
}
log.AddString(String.Format("offsetting big polygon.."));
CombinedSilk = Clipper.OffsetPolygons(CombinedSilk, 1.0 * 100000.0f, JoinType.jtRound, 0.1 * 100000.0f);
{
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(CombinedSilk, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
}
double W = Outline.BoundingBox.BottomRight.X - Outline.BoundingBox.TopLeft.X;
double H = Outline.BoundingBox.BottomRight.Y - Outline.BoundingBox.TopLeft.Y;
double Res = 200.0 / 25.4;
int iW = (int)(W * Res) + 2;
int iH = (int)(H * Res) + 2;
int Range = Math.Max(iW, iH);
Image<Rgba32> B = new Image<Rgba32>(iW, iH);
Image<Rgba32> B2 = new Image<Rgba32>(iW, iH);
Image<Rgba32> B3 = new Image<Rgba32>(iW, iH);
var G2 = new ImageSharpGraphicsInterface(B3);
G2.Clear(Color.Black);
double[] DistanceField = new double[iW * iH];
double[] DistanceFieldBlur = new double[iW * iH];
double[] AngleField = new double[iW * iH];
bool DistMode = true;
for (int i = 0; i < iW * iH; i++) { DistanceField[i] = DistMode ? 0 : 100000; AngleField[i] = 0; };
var G = new ImageSharpGraphicsInterface(B);
G.Clear(Color.Black);
log.AddString(String.Format("Rendering Base Bitmap"));
// G.SmoothingMode = SmoothingMode.AntiAlias;
foreach (var a in CombinedOutline)
{
PolyLine P = new PolyLine(PolyLine.PolyIDs.ArtWork);
P.fromPolygon(a);
List<PointF> Pts = new List<PointF>();
foreach (var V in P.Vertices)
{
Pts.Add(new PointF((float)((V.X - Outline.BoundingBox.TopLeft.X) * Res) + 1, (float)((V.Y - Outline.BoundingBox.TopLeft.Y) * Res) + 1));
}
G.DrawLines(new Pen(Color.White, 1), Pts.ToArray());
P.CheckIfHole();
if (P.Hole)
{
G2.FillPolygon(new SolidBrush(Color.Green), Pts.ToArray());
}
else
{
G2.FillPolygon(new SolidBrush(Color.Red), Pts.ToArray());
}
}
B.SaveAsPng(target + "_renderbase.png");
log.AddString(String.Format("Calculating Distance Field"));
{
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
var C = B[x, y];
if (C.R > 10)
{
for (int xx = Math.Max(x - Range, 0); xx < Math.Min(iW, x + Range); xx++)
{
for (int yy = Math.Max(y - Range, 0); yy < Math.Min(iH, y + Range); yy++)
{
if (DistMode)
{
double dist = (xx - x) * (xx - x) + (yy - y) * (yy - y);
DistanceField[xx + yy * iW] += 1.0 / (1.0 + dist * 0.1);
}
else
{
double dist = Math.Sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));
if (dist < DistanceField[xx + yy * iW]) DistanceField[xx + yy * iW] = dist;
}
}
}
}
}
}
}
log.AddString(String.Format("Blurring Distance Field"));
double maxblurval = 0;
double minblurval = 100000000000;
{
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
double T = 0;
double amt = 0;
for (int xx = 0; xx < 1; xx++)
{
for (int yy = 0; yy < 1; yy++)
{
double D = xx * xx + yy * yy;
double M = Math.Exp(0);
if (D > 0)
{
M = Math.Exp(-(xx * xx) * 0.01) * Math.Exp(-(yy * yy) * 0.01); ;
}
amt += M;
T += DistanceField[((x + xx + iW) % iW) + ((y + yy + iH) % iH) * iW] * M;
}
}
double newval = T / amt;
DistanceFieldBlur[x + iW * y] = newval;
if (newval > maxblurval) maxblurval = newval;
if (newval < minblurval) minblurval = newval;
}
}
}
{
G.Clear(Color.Black);
for (int x = 0; x < iW; x++)
{
for (int y = 0; y < iH; y++)
{
var a = DistanceFieldBlur[x + y * iW];
//a = Math.Sqrt(a);
Color C = Color.FromRgb((byte)((a - minblurval) * 255.0 / (maxblurval - minblurval)), (byte)0, (byte)0);
DistanceFieldBlur[x + y * iW] = (DistanceFieldBlur[x + y * iW] - minblurval) / (maxblurval - minblurval);
B[x, y] = C;
}
}
B.SaveAsPng(target + "_renderDistance.png");
}
log.AddString(String.Format("Calculating Angle Field and Artwork"));
GerberArtWriter GOW = new GerberArtWriter();
{
G.Clear(Color.Black);
for (int x = 0; x < iW - 1; x += (int)(Res))
{
for (int y = 0; y < iH - 1; y += (int)(Res))
{
var a = DistanceFieldBlur[x + y * iW];
var b = DistanceFieldBlur[(x + 1) + y * iW];
var c = DistanceFieldBlur[x + (y + 1) * iW];
V3 AA = new V3(0, 0, Math.Sqrt(a) / (double)Range);
V3 BB = new V3(1, 0, Math.Sqrt(b) / (double)Range);
V3 CC = new V3(0, 1, Math.Sqrt(c) / (double)Range);
var D1 = BB - AA;
var D2 = CC - AA;
var C1 = V3.Cross(D1, D2);
var DistT = Math.Sqrt(C1.X * C1.X + C1.Y * C1.Y + C1.Z * C1.Z);
var AngleRad = Math.Atan2(C1.Y, C1.X);
var AngleDeg = AngleRad * 360.0 / (Math.PI * 2);
// C1.X /= DistT;
// C1.Y /= DistT;
var Dist = Math.Sqrt(C1.X * C1.X + C1.Y * C1.Y);
while (AngleDeg < 0) AngleDeg += 360;
Color C = Color.FromRgb((byte)(255 - Math.Min(255, (int)(AngleDeg * (255.0 / 360.0)))), (byte)0, (byte)0);
if (B3[x, y].R == 255)
{
PolyLine pL = new PolyLine(PolyLine.PolyIDs.ArtWork);
//pL.Add((x-1) / Res, (y - 1) / Res);
double cx = Math.Sin(-AngleRad) * Res * 0.4 * 0.8 * (1 + Math.Pow(Dist, 0.1));
double cy = Math.Cos(-AngleRad) * Res * 0.4 * 0.8 * (1 + Math.Pow(Dist, 0.1));
pL.Add(((x - 1) + cx) / Res, ((y - 1) + cy) / Res);
pL.Add(((x - 1) - cx) / Res, ((y - 1) - cy) / Res);
pL.Translate(Outline.BoundingBox.TopLeft.X, Outline.BoundingBox.TopLeft.Y);
GOW.AddPolyLine(pL, 1.0 / Res);
}
else
{
}
}
}
}
GOW.Write(target);
log.AddString(String.Format("Done"));
}
private static void ArtGenerator_OffsetCurves(ProgressLog log, string outline, string soldermask, string target, string silk)
{
log.PushActivity("ArtGenerator_OffsetCurves");
log.AddString(String.Format("Artlayer {0} started: OffsetCurves", target));
ParsedGerber Outline = PolyLineSet.LoadGerberFile(log, outline, true, false, new GerberParserState() { PreCombinePolygons = true });
Outline.FixPolygonWindings();
ParsedGerber SolderMask = PolyLineSet.LoadGerberFile(log, soldermask, false, false, new GerberParserState() {PreCombinePolygons = false, MinimumApertureRadius = 0.1 });
SolderMask.FixPolygonWindings();
//Silk.FixPolygonWindings();
Polygons CombinedOutline = new Polygons();
Polygons CombinedSoldermask = new Polygons();
log.AddString(String.Format("combining outlines.."));
for (int i = 0; i < Outline.OutlineShapes.Count; i++)
{
Polygons clips = new Polygons();
clips.Add(Outline.OutlineShapes[i].toPolygon());
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctXor, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
log.AddString(String.Format("removing paste curves.."));
if (true)
{
Polygons clips = new Polygons();
for (int i = 0; i < SolderMask.OutlineShapes.Count; i++)
{
clips.Add(SolderMask.OutlineShapes[i].toPolygon());
}
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
if (silk.Length > 0)
{
log.AddString(String.Format("removing silk curves.."));
ParsedGerber Silk = PolyLineSet.LoadGerberFile(log, silk, false, false, new GerberParserState() {PreCombinePolygons = false, MinimumApertureRadius =0.1 });
//Silk.FixPolygonWindings();
Polygons CombinedSilk = new Polygons();
log.AddString(String.Format("building big polygon.."));
{
Polygons clips = new Polygons();
for (int i = 0; i < Silk.DisplayShapes.Count; i++)
{
clips.Add(Silk.DisplayShapes[i].toPolygon());
}
Clipper cp = new Clipper();
cp.AddPolygons(CombinedSilk, PolyType.ptSubject);
cp.AddPolygons(clips, PolyType.ptClip);
cp.Execute(ClipType.ctUnion, CombinedSilk, PolyFillType.pftNonZero, PolyFillType.pftNonZero);
}
log.AddString(String.Format("offsetting big polygon.."));
CombinedSilk = Clipper.OffsetPolygons(CombinedSilk, 0.5 * 100000.0f, JoinType.jtRound, 0.1 * 100000.0f);
{
Clipper cp = new Clipper();
cp.AddPolygons(CombinedOutline, PolyType.ptSubject);
cp.AddPolygons(CombinedSilk, PolyType.ptClip);
cp.Execute(ClipType.ctDifference, CombinedOutline, PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);
}
}
log.AddString(String.Format("starting offsetcurves.."));
GerberArtWriter GOW = new GerberArtWriter();
double add = 0.5;
for (double i = 0.5; i < 15; i += add)
{
// if (add < (0.25)) add = (0.25 );
log.AddString(String.Format("offset {0}", i));
CombinedOutline = Clipper.OffsetPolygons(CombinedOutline, -add * 100000.0f, JoinType.jtRound, 0.1 * 100000.0f);
foreach (var p in CombinedOutline)
{
PolyLine P = new PolyLine(PolyLine.PolyIDs.ArtWork);
P.fromPolygon(p);
GOW.AddPolyLine(P, add / 4);
}
add += 0.1;
}
GOW.Write(target);
log.PopActivity();
}
}
[Serializable]
public class TextLabel
{
public string Text = "Empty";
public double FontSize = 2.54d;
public string Font = "Arial";
public StringAlign Alignment = StringAlign.BottomLeft;
public BoxAttachment Attachment = BoxAttachment.TopLeft;
public double Rotation = 0;
public XY Position = new XY();
public double StrokeWidth = 0.05;
}
[Serializable]
public class FontDef
{
public string Name = "font";
public string FontFile = "font.xml";
}
[Serializable]
public class GerberDef {
public string FileName = "";
public BoxAttachment Attachment = BoxAttachment.TopLeft;
public XY Position = new XY();
}
[Serializable]
public class ArtSet
{
public List<TextLabel> Labels = new List<TextLabel>();
public List<Graphic> GraphicsInterface = new List<Graphic>();
public List<FontDef> Fonts = new List<FontDef>();
public List<GerberDef> GerberRenders = new List<GerberDef>();
public void Write(string p)
{
XmlSerializer serializer = new XmlSerializer(typeof(ArtSet));
TextWriter writer = new StreamWriter(p);
serializer.Serialize(writer, this);
writer.Close();
}
public static ArtSet Load(string p)
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(ArtSet));
// Declare an object variable of the type to be deserialized.
ArtSet i;
using (Stream reader = new FileStream(p, FileMode.Open))
{
// Call the Deserialize method to restore the object's state.
i = (ArtSet)serializer.Deserialize(reader);
}
return i;
}
catch (Exception)
{
}
return new ArtSet();
}
public Dictionary<string, FontSet> LoadFonts(ProgressLog log)
{
Dictionary<string, FontSet> R = new Dictionary<string, FontSet>();
foreach (var a in Fonts)
{
FontSet F = null;
try
{
F = FontSet.Load(a.FontFile);
R[a.Name.ToLower()] = F;
}
catch (Exception E)
{
log.AddString(String.Format("Font file failed to load: {0} {1}: {2}", a.Name, a.FontFile, E.Message));
}
}
return R;
}
}
[Serializable]
public class Graphic
{
//StringAlign Alignment = StringAlign.BottomLeft;
//BoxAttachment Attachment = BoxAttachment.TopLeft;
public double Rotation = 0;
public XY Position = new XY();
}
}
public class GerberArtWriter
{
public void WriteImageToBounds(Image<Rgba32> B, Bounds sizeofbitmap, Bounds rectangletobindto)
{
RectangleF bmpr = new RectangleF((float)sizeofbitmap.TopLeft.X, (float)sizeofbitmap.TopLeft.Y, (float)sizeofbitmap.Width(), (float)sizeofbitmap.Height());
RectangleF contain = new RectangleF((float)rectangletobindto.TopLeft.X, (float)rectangletobindto.TopLeft.Y, (float)rectangletobindto.Width(), (float)rectangletobindto.Height());
RectangleF section = new RectangleF((float)sizeofbitmap.TopLeft.X, (float)sizeofbitmap.TopLeft.Y, (float)sizeofbitmap.Width(), (float)sizeofbitmap.Height());
int threshold = 128;
if (bmpr.IntersectsWith(contain) == false) return;
section = RectangleF.Intersect(section, contain);
float dpiscaler = bmpr.Width / B.Width;
int YPIXELS = (int)Math.Ceiling(contain.Height / dpiscaler);
int XPIXELS = (int)Math.Ceiling(contain.Width / dpiscaler);
Console.WriteLine("ART LAYER {0} {1} {2}", YPIXELS, XPIXELS, dpiscaler);
int ssx = (int)Math.Floor((contain.X - bmpr.X) / dpiscaler);
int ssy = (int)Math.Floor((contain.Y - bmpr.Y) / dpiscaler);
bool invert = false;
if (threshold < 0)
{
threshold = -threshold;
invert = true;
}
int startlevel = 0;
int endlevel;
for (int y = 0; y < YPIXELS; y++)
{
double sx = -1;
bool active = false;
for (int x = 0; x < XPIXELS; x++)
{
Rgba32 C = B[x + ssx, B.Height - 1 - (y + ssy)];
if ((invert == false && C.R > threshold) || (invert == true && C.R < threshold))
{
if (active == false)
{
sx = x;
startlevel = C.R;
active = true;
}
}
else
{
if (active)
{
endlevel = C.R;
active = false;
PolyLine pL = new PolyLine(PolyLine.PolyIDs.Bitmap);
double offL = ((startlevel - Math.Abs(threshold)) / 128.0) * dpiscaler;
double offR = ((endlevel - Math.Abs(threshold)) / 128.0) * dpiscaler;
// offL *= -1;
// offR *= -1;
pL.Add(contain.X + sx * dpiscaler - offL, contain.Y + ((double)y) * dpiscaler);
pL.Add(contain.X + ((double)x) * dpiscaler+ offR, contain.Y + ((double)y) * dpiscaler);
// if (Outline != null) pL.Translate(Outline.BoundingBox.TopLeft.X, Outline.BoundingBox.TopLeft.Y);
pL.Translate(0, .50 * dpiscaler);
AddPolyLine(pL, 1.0 * dpiscaler);
}
}
}
if (active)
{
PolyLine pL = new PolyLine(PolyLine.PolyIDs.Bitmap);
double offL = ((startlevel - Math.Abs(threshold)) / 128.0) * .50 * dpiscaler;
pL.Add(contain.X + sx * dpiscaler + offL, contain.Y + ((double)y) * dpiscaler);
pL.Add(contain.X + (XPIXELS) * dpiscaler, contain.Y + ((double)y) * dpiscaler);
pL.Translate(0, .50 * dpiscaler);
AddPolyLine(pL, 1.0 * dpiscaler);
}
}
}
Dictionary<double, List<PolyLine>> PolyLines = new Dictionary<double, List<PolyLine>>();
Dictionary<double, int> ApertureLookup = new Dictionary<double, int>();
List<PolyLine> Polygons = new List<PolyLine>();
int LastID = 0;
public void AddPolyLine(PolyLine a, double width = 1)
{
if (PolyLines.ContainsKey(width) == false)
{
PolyLines[width] = new List<PolyLine>();
}
PolyLines[width].Add(a);
}
public void AddFlash(PointD P, double width = 1)
{
PolyLine PL = new PolyLine(LastID++);
PL.Vertices.Add(P);
if (PolyLines.ContainsKey(width) == false)
{
PolyLines[width] = new List<PolyLine>();
}
PolyLines[width].Add(PL);
}
public static PointD DoOffs(PointD inp, PointD offs)
{
return new PointD(inp.X + offs.X, inp.Y + offs.Y);
}
public void Write(string p, PointD pd = null, double rectx = -1, double recty = -1)
{
if (pd == null) pd = new PointD(0, 0);
List<string> lines = new List<string>();
GerberNumberFormat GNF = new GerberNumberFormat();
GNF.DigitsBefore = 3;
GNF.DigitsAfter = 6;
GNF.SetImperialMode();
lines.Add(Gerber.INCH);
lines.Add("%OFA0B0*%");
lines.Add(GNF.BuildGerberFormatLine());
lines.Add("%IPPOS*%");
lines.Add("%LPD*%");
List<GerberApertureType> Apts = new List<GerberApertureType>();
int aptindex = 0;
foreach (var a in PolyLines.Keys)
{
GerberApertureType Apt = new GerberApertureType();
if (rectx > 0)
{
Apt.SetRectangle(rectx, recty,0);
}
else
{
Apt.SetCircle(a);
}
Apt.ID = aptindex + 10;
lines.Add(Apt.BuildGerber(GNF));
Apts.Add(Apt);
ApertureLookup[a] = aptindex;
aptindex++;
}
foreach (var a in PolyLines)
{
lines.Add(Apts[ApertureLookup[a.Key]].BuildSelectGerber());
foreach (var b in a.Value)
{
if (b.Vertices.Count == 1)
{
lines.Add(Gerber.Flash(DoOffs(b.Vertices[0],pd), GNF));
}
else
{
lines.Add(Gerber.MoveTo(DoOffs(b.Vertices[0],pd), GNF));
for (int i = 1; i < b.Vertices.Count; i++)
{
lines.Add(Gerber.LineTo(DoOffs(b.Vertices[i],pd), GNF));
}
}
}
}
foreach (var a in Polygons)
{
if (a.Vertices.Count > 2)
{
lines.Add(Gerber.StartRegion);
lines.Add(Gerber.MoveTo(DoOffs(a.Vertices[0],pd), GNF));
lines.Add(Gerber.LinearInterpolation);
for (int i = 1; i < a.Vertices.Count; i++)
{
lines.Add(Gerber.LineTo(DoOffs(a.Vertices[i],pd), GNF));
}
lines.Add(Gerber.StopRegion);
}
}
lines.Add(Gerber.EOF);
Gerber.WriteAllLines(p, lines);
}
public void DrawString(PointD Start, FontSet FS, string text, double size, double strokewidth, StringAlign SA, bool Reverse = false, double rotation = 0, bool Polygonized = false)
{
//text = "Test1'1234'0+!@\"#$@%#&*(";
if (FS == null)
{
Console.WriteLine("DrawString called with no active fontset (\"{0}\")! Ignoring!", text);
return;
}
//double x = Start.X;
//double y = Start.Y;
double x = 0;
double y = -size;
List<PolyLine> PreRotateTranslate = new List<PolyLine>();
double Scaler = size / FS.CapsHeight;
// y += size;
double W = 0;
for (int i = 0; i < text.Length; i++)
{
char t = text[i];
var R = FS.GetChar(t);
if (R != null)
{
W += R.Advance * Scaler;
}
else
{
W += size;
}
}
if (Reverse)
{
//x += W;
//W = -W;
}
switch (SA)
{
case StringAlign.BottomCenter:
if (Reverse) { x += W / 2; } else { x -= W / 2; };
break;
case StringAlign.TopCenter:
y -= size;
if (Reverse) { x += W / 2; } else { x -= W / 2; };
break;
case StringAlign.CenterCenter:
y -= size / 2;
if (Reverse) { x += W / 2; } else { x -= W / 2; };
break;
case StringAlign.BottomRight:
y += size;
if (Reverse) { x += W; } else { x -= W; };
break;
case StringAlign.TopRight:
if (Reverse) { x += W; } else { x -= W; };
break;
case StringAlign.CenterRight:
y -= size / 2;
if (Reverse) { x += W; } else { x -= W; };
break;
case StringAlign.BottomLeft:
y += size;
break;
case StringAlign.TopLeft:
break;
case StringAlign.CenterLeft:
y -= size / 2;
break;
}
for (int i = 0; i < text.Length; i++)
{
char t = text[i];
var R = FS.GetChar(t);
if (R != null)
{
foreach (var l in R.lines)
{
PolyLine PL = new PolyLine(LastID++);
foreach (var v in l)
{
if (Reverse)
{
PL.Add(-v.X * Scaler + x, -(v.Y * Scaler) + y);
}
else
{
PL.Add(v.X * Scaler + x, -(v.Y * Scaler) + y);
}
}
PL.Close();
PreRotateTranslate.Add(PL);
}
if (Reverse)
{
x -= Scaler * R.Advance;
}
else
{
x += Scaler * R.Advance;
}
}
else
{
if (Reverse)
{
x -= size;
}
else
{
x += size;
}
}
}
double SX = Math.Sin(rotation * (Math.PI / 180.0));
double CX = Math.Cos(rotation * (Math.PI / 180.0));
foreach (var a in PreRotateTranslate)
{
PolyLine PL = new PolyLine(LastID++);
foreach (var v in a.Vertices)
{
double xx = CX * v.X - SX * v.Y;
double yy = SX * v.X + CX * v.Y;
PL.Add(xx + Start.X, yy + Start.Y);
}
if (Polygonized)
{
AddPolygon(PL);
}
else
{
AddPolyLine(PL, strokewidth);
}
}
}
public Bounds MeasureString(PointD Start, FontSet FS, string text, double size, double strokewidth, StringAlign SA, bool Reverse = false, double rotation = 0, bool Polygonized = false)
{
Bounds B = new Bounds();
//text = "Test1'1234'0+!@\"#$@%#&*(";
if (FS == null)
{
Console.WriteLine("DrawString called with no active fontset (\"{0}\")! Ignoring!", text);
return B;
}
//double x = Start.X;
//double y = Start.Y;
double x = 0;
double y = -size;
List<PolyLine> PreRotateTranslate = new List<PolyLine>();
double Scaler = size / FS.CapsHeight;
// y += size;
double W = 0;
for (int i = 0; i < text.Length; i++)
{
char t = text[i];
var R = FS.GetChar(t);
if (R != null)
{
W += R.Advance * Scaler;
}
else
{
W += size;
}
}
if (Reverse)
{
//x += W;
//W = -W;
}
switch (SA)
{
case StringAlign.BottomCenter:
if (Reverse) { x += W / 2; } else { x -= W / 2; };
break;
case StringAlign.TopCenter:
y -= size;
if (Reverse) { x += W / 2; } else { x -= W / 2; };
break;
case StringAlign.CenterCenter:
y -= size / 2;
if (Reverse) { x += W / 2; } else { x -= W / 2; };
break;
case StringAlign.BottomRight:
y += size;
if (Reverse) { x += W; } else { x -= W; };
break;
case StringAlign.TopRight:
if (Reverse) { x += W; } else { x -= W; };
break;
case StringAlign.CenterRight:
y -= size / 2;
if (Reverse) { x += W; } else { x -= W; };
break;
case StringAlign.BottomLeft:
y += size;
break;
case StringAlign.TopLeft:
break;
case StringAlign.CenterLeft:
y -= size / 2;
break;
}
for (int i = 0; i < text.Length; i++)
{
char t = text[i];
var R = FS.GetChar(t);
if (R != null)
{
foreach (var l in R.lines)
{
PolyLine PL = new PolyLine(LastID++);
foreach (var v in l)
{
if (Reverse)
{
PL.Add(-v.X * Scaler + x, -(v.Y * Scaler) + y);
}
else
{
PL.Add(v.X * Scaler + x, -(v.Y * Scaler) + y);
}
}
PL.Close();
PreRotateTranslate.Add(PL);
}
if (Reverse)
{
x -= Scaler * R.Advance;
}
else
{
x += Scaler * R.Advance;
}
}
else
{
if (Reverse)
{
x -= size;
}
else
{
x += size;
}
}
}
double SX = Math.Sin(rotation * (Math.PI / 180.0));
double CX = Math.Cos(rotation * (Math.PI / 180.0));
foreach (var a in PreRotateTranslate)
{
PolyLine PL = new PolyLine(LastID++);
foreach (var v in a.Vertices)
{
double xx = CX * v.X - SX * v.Y;
double yy = SX * v.X + CX * v.Y;
PL.Add(xx + Start.X, yy + Start.Y);
}
if (Polygonized)
{
B.AddPolyLine(PL);
}
else
{
B.AddPolyLine(PL);
}
}
return B;
}
public static PointD AttachPoint(PointD inp, PointD boxtopleft, PointD boxbottomright, GerberLibrary.ArtWork.BoxAttachment attachment)
{
PointD ret = new PointD(inp.X, inp.Y);
switch (attachment)
{
case ArtWork.BoxAttachment.Top:
ret.Y = boxbottomright.Y - inp.Y;
break;
case ArtWork.BoxAttachment.Bottom:
ret.Y = boxtopleft.Y + inp.Y;
break;
case ArtWork.BoxAttachment.Left:
ret.X = boxtopleft.X + inp.X;
break;
case ArtWork.BoxAttachment.Right:
ret.X = boxbottomright.X - inp.X;
break;
case ArtWork.BoxAttachment.TopLeft:
ret.Y = boxbottomright.Y - inp.Y;
ret.X = boxtopleft.X + inp.X;
break;
case ArtWork.BoxAttachment.BottomLeft:
ret.Y = boxtopleft.Y + inp.Y;
ret.X = boxtopleft.X + inp.X;
break;
case ArtWork.BoxAttachment.TopRight:
ret.Y = boxbottomright.Y - inp.Y;
ret.X = boxbottomright.X - inp.X;
break;
case ArtWork.BoxAttachment.BottomRight:
ret.Y = boxtopleft.Y + inp.Y;
ret.X = boxbottomright.X - inp.X;
break;
}
return ret;
}
Dictionary<string, FontSet> LocalFonts = new Dictionary<string, FontSet>();
public void DrawBoardLabelSet(Dictionary<string, FontSet> FS, GerberLibrary.ArtWork.ArtSet AS, PointD boxtopleft, PointD boxbottomright)
{
Console.WriteLine("Writing artwork: dimension: {0} -> {1}", boxtopleft, boxbottomright);
//if (FS.Count == 0) return;
Console.WriteLine("Number of fonts: {0}", AS.Fonts.Count());
foreach(var F in AS.Fonts)
{
try
{
FontSet newFS = FontSet.Load(F.FontFile);
FS[F.Name.ToLower()] = newFS;
Console.WriteLine("Loaded font \"{0}\" from \"{1}\"", F.Name, F.FontFile);
}
catch(Exception e)
{
Console.WriteLine("Failed to load font {0}:{1} -> {2}", F.Name, F.FontFile, e.Message);
}
}
Console.WriteLine("Number of labels: {0}", AS.Labels.Count());
foreach (var T in AS.Labels)
{
Console.WriteLine("Adding label {0} with font {1}, position {2}, alignment {3}, attachment {4}", T.Text, T.Font, T.Position, T.Alignment, T.Attachment);
FontSet F = null;
FS.TryGetValue(T.Font.ToLower(), out F);
if (F == null)
{
LocalFonts.TryGetValue(T.Font.ToLower(), out F);
}
if (F == null)
{
if (FS.Count()>0)
{
F = FS.Values.First();
}
}
if (F == null)
{
Console.WriteLine("Still no font found for {0}!", T.Font);
}
DrawString(AttachPoint(new PointD(T.Position.X, T.Position.Y), boxtopleft, boxbottomright, T.Attachment), F, T.Text, T.FontSize, T.StrokeWidth > 0? T.StrokeWidth: 0.05, T.Alignment, false, T.Rotation);
}
}
public void AddPolygon(PolyLine PLA)
{
Polygons.Add(PLA);
}
public Bounds GetBounds()
{
Bounds B = new Bounds();
foreach(var p in Polygons)
{
B.AddPolyLine(p);
}
foreach (var l in PolyLines)
{
foreach (var p in l.Value)
{
B.AddPolyLine(p, l.Key);
}
}
return B;
}
internal void Arrow(PointD pointD1, PointD pointD2, double headwidth =3, double headlength = 3, double linewidth=0.5)
{
PolyLine Stem = new PolyLine();
Stem.Vertices.Add(pointD1);
var d = pointD2 - pointD1;
d.Normalize();
Stem.Vertices.Add(pointD2 - d* headlength);
var side = d.Rotate(90);
PolyLine ArrowHead = new PolyLine();
var hheadwidth = headwidth / 2;
ArrowHead.MakeTriangle(pointD2, pointD2 - d * headlength + side * hheadwidth, pointD2 - d * headlength - side * hheadwidth);
AddPolygon(ArrowHead);
AddPolyLine(Stem, linewidth);
}
}
}