Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
using GerberLibrary;
using System;
using System.Collections.Generic;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Drawing.Processing;
using System.Numerics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using GerberLibrary.Core;
using GerberLibrary.Core.Primitives;
using Pen = GerberLibrary.Core.Primitives.Pen;
using SolidBrush = GerberLibrary.Core.Primitives.SolidBrush; using GraphicsInterpolationMode = GerberLibrary.Core.Primitives.GraphicsInterpolationMode;
using ICSharpCode.SharpZipLib.Zip;
using TriangleNet;
namespace GerberLibrary
{
public class GerberImageCreator
{
public static bool AA = true;
public Bounds BoundingBox = new Bounds();
public List<String> Errors = new List<string>();
public double scale = 25.0d / 25.4d; // dpi
private BoardRenderColorSet ActiveColorSet = new BoardRenderColorSet();
Dictionary<string, MemoryStream> Streams = new Dictionary<string, MemoryStream>();
public Dictionary<string, double> DrillFileScale = new Dictionary<string, double>();
bool hasgko = false;
public List<ParsedGerber> PLSs = new List<ParsedGerber>();
public ParsedGerber GetGerberByName(string name)
{
foreach (var a in PLSs)
{
if (a.Name == name) return a;
}
return null;
}
public void ClipBoard(string infile, string outputfile, ProgressLog log)
{
var toclip = GetGerberByName(infile);
if (toclip == null)
{
log.AddString(String.Format("file {0} not loaded - not clipping!", infile));
}
var ols = (from a in PLSs where (a.Side == BoardSide.Both && (a.Layer == BoardLayer.Outline || a.Layer == BoardLayer.Mill) && a.Generated == false) select a).ToList();
if (IsInPolygons(toclip, ols) == true)
{
File.Copy(infile, outputfile);
log.AddString("file not clipped - just copied");
return;
}
GerberArtWriter GAW = new GerberArtWriter();
int lineID = 0;
foreach (var a in toclip.DisplayShapes)
{
ClipperLib.Clipper CP = new ClipperLib.Clipper();
foreach (var ol in ols)
{
var R = ol.FindLargestPolygon();
if (R != null)
{
var poly = R.Item2.toPolygon();
if (ClipperLib.Clipper.Orientation(poly) == false)
{
//Console.WriteLine("pos");
}
else
{
poly.Reverse();
//Console.WriteLine("neg");
}
CP.AddPolygon(poly, ClipperLib.PolyType.ptClip);
}
}
CP.AddPolygon(a.toPolygon(), ClipperLib.PolyType.ptSubject);
List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>();
CP.Execute(ClipperLib.ClipType.ctIntersection, solution);
foreach (var p in solution)
{
PolyLine P = new PolyLine(lineID++);
P.fromPolygon(p);
GAW.AddPolygon(P);
}
}
GAW.Write(outputfile);
}
public void Translate(double x, double y)
{
BoundingBox.Reset();
foreach (var f in PLSs)
{
f.Translate(new PointD(x,y));
f.CalcPathBounds();
BoundingBox.AddBox(f.BoundingBox);
}
}
public void FlipXY()
{
BoundingBox.Reset();
foreach (var f in PLSs)
{
f.FlipXY();
f.CalcPathBounds();
BoundingBox.AddBox(f.BoundingBox);
}
}
public void FlipX()
{
BoundingBox.Reset();
foreach (var f in PLSs)
{
f.FlipX();
f.CalcPathBounds();
BoundingBox.AddBox(f.BoundingBox);
}
}
public void SetBottomRightToZero()
{
double dx = this.BoundingBox.BottomRight.X;
double dy = this.BoundingBox.TopLeft.Y;
BoundingBox.Reset();
foreach (var f in PLSs)
{
f.Translate(new PointD(-dx, -dy));
f.CalcPathBounds();
BoundingBox.AddBox(f.BoundingBox);
}
}
private bool IsInPolygons(ParsedGerber toclip, List<ParsedGerber> ols)
{
foreach (var o in ols)
{
foreach (var oo in o.DisplayShapes)
{
foreach (var p in toclip.DisplayShapes)
{
foreach (var pt in p.Vertices)
{
if (Helpers.IsInPolygon(oo.Vertices, pt) == false)
{
return false;
}
}
}
}
}
return true;
}
public void CopyFrom(GerberImageCreator set)
{
foreach(var p in set.PLSs)
{
ParsedGerber PG = new ParsedGerber();
PG.CopyFrom(p);
PG.Side = p.Side;
PG.Layer = p.Layer;
PG.CalcPathBounds();
BoundingBox.AddBox(PG.BoundingBox);
PLSs.Add(PG);
}
}
public void SetBottomLeftToZero()
{
double dx = this.BoundingBox.TopLeft.X;
double dy = this.BoundingBox.TopLeft.Y;
BoundingBox.Reset();
foreach(var f in PLSs)
{
f.Translate(new PointD(-dx, -dy));
f.CalcPathBounds();
BoundingBox.AddBox(f.BoundingBox);
}
}
public int Count()
{
return PLSs.Count;
}
public static void ApplyAASettings(GraphicsInterface G)
{
if (G != null) G.AntiAlias = AA;
// We can toggle a flag if needed, but for now we rely on defaults.
if (AA)
{
G.InterpolationMode = GraphicsInterpolationMode.HighQualityBicubic;
}
else
{
G.InterpolationMode = GraphicsInterpolationMode.NearestNeighbor;
}
}
public static Color Darker(Color color, double Fac)
{
Rgba32 c = color.ToPixel<Rgba32>();
float correctionFactor = 1.0f - (float)Fac;
byte r = (byte)(c.R * correctionFactor);
byte g = (byte)(c.G * correctionFactor);
byte b = (byte)(c.B * correctionFactor);
return Color.FromRgba(r, g, b, c.A);
}
public static Color Lighter(Color color, double Fac)
{
Rgba32 c = color.ToPixel<Rgba32>();
float correctionFactor = (float)Fac;
byte r = (byte)((255 - c.R) * correctionFactor + c.R);
byte g = (byte)((255 - c.G) * correctionFactor + c.G);
byte b = (byte)((255 - c.B) * correctionFactor + c.B);
return Color.FromRgba(r, g, b, c.A);
}
public void AddBoardsToSet(List<string> FileList, ProgressLog Logger , bool fixgroup = true, bool forcezerowidth = false)
{
Logger.PushActivity("AddBoardsToSet");
foreach (var a in FileList)
{
Logger.AddString(String.Format("adding {0}", a));
BoardSide aSide = BoardSide.Unknown;
BoardLayer aLayer = BoardLayer.Unknown;
string ext = Path.GetExtension(a);
if (ext == ".zip")
{
using (ZipFile zip1 = new ZipFile(a))
{
foreach (ZipEntry e in zip1)
{
MemoryStream MS = new MemoryStream();
if (e.IsDirectory == false)
{
// e.Extract(MS);
// MS.Seek(0, SeekOrigin.Begin);
Gerber.DetermineBoardSideAndLayer(e.Name, out aSide, out aLayer);
if (aLayer == BoardLayer.Outline) hasgko = true;
// AddFileStream(MS, e.Name, drillscaler);
}
}
}
}
else
{
Gerber.DetermineBoardSideAndLayer(a, out aSide, out aLayer);
}
if (aLayer == BoardLayer.Outline) hasgko = true;
}
foreach (var a in FileList)
{
if (Logger != null) Logger.AddString(String.Format("Loading {0}", Path.GetFileName(a)));
string ext = Path.GetExtension(a);
if (ext == ".zip")
{
using (ZipFile zip1 = new ZipFile(a))
{
foreach (ZipEntry e in zip1)
{
if (!e.IsDirectory)
{
if (Logger != null) Logger.AddString(String.Format("Loading inside zip: {0}", Path.GetFileName(e.Name)));
MemoryStream MS = new MemoryStream();
using (var stream = zip1.GetInputStream(e))
{
stream.CopyTo(MS);
}
MS.Seek(0, SeekOrigin.Begin);
AddFileToSet(MS, e.Name, Logger, 1, forcezerowidth);
}
}
}
}
else
{
try
{
MemoryStream MS2 = new MemoryStream();
FileStream FS = File.OpenRead(a);
FS.CopyTo(MS2);
MS2.Seek(0, SeekOrigin.Begin);
AddFileToSet(MS2, a, Logger,1, forcezerowidth);
}
catch (Exception E)
{
Logger.AddString(String.Format("Failed to add file! {0},{1}", a, E));
}
}
}
if (fixgroup)
{
if (Logger != null) Logger.AddString("Checking for common file format mistakes.");
FixEagleDrillExportIssues(Logger);
CheckRelativeBoundingBoxes(Logger);
CheckForOutlineFiles(Logger);
CheckRelativeBoundingBoxes(Logger);
}
Logger.PopActivity();
}
public ParsedGerber AddBoardToSet(string _originalfilename, ProgressLog log, bool forcezerowidth = false, bool precombinepolygons = false, double drillscaler = 1.0)
{
if (Streams.ContainsKey(_originalfilename))
{
return AddBoardToSet(Streams[_originalfilename], _originalfilename, log, forcezerowidth, precombinepolygons, drillscaler);
}
return null;
}
public ParsedGerber AddBoardToSet(MemoryStream MS, string _originalfilename, ProgressLog log, bool forcezerowidth = false, bool precombinepolygons = false, double drillscaler = 1.0)
{
log.PushActivity("AddboardToSet");
Streams[_originalfilename] = MS;
try
{
// string[] filesplit = originalfilename.Split('.');
// string ext = filesplit[filesplit.Count() - 1].ToLower();
var FileType = Gerber.FindFileTypeFromStream(new StreamReader(MS), _originalfilename);
MS.Seek(0, SeekOrigin.Begin);
if (FileType == BoardFileType.Unsupported)
{
if (Gerber.ExtremelyVerbose) log.AddString(String.Format("Warning: {1}: files with extension {0} are not supported!", Path.GetExtension(_originalfilename), Path.GetFileName(_originalfilename)));
log.PopActivity();
return null;
}
ParsedGerber PLS;
GerberParserState State = new GerberParserState() { PreCombinePolygons = precombinepolygons };
if (FileType == BoardFileType.Drill)
{
if (Gerber.ExtremelyVerbose) log.AddString(String.Format("Drill file: {0}", _originalfilename));
PLS = PolyLineSet.LoadExcellonDrillFileFromStream(log, new StreamReader(MS), _originalfilename, false, drillscaler);
MS.Seek(0, SeekOrigin.Begin);
PLS.Side = BoardSide.Both;
PLS.Layer = BoardLayer.Drill;
// ExcellonFile EF = new ExcellonFile();
// EF.Load(a);
}
else
{
if (Gerber.ExtremelyVerbose) Console.WriteLine("Log: Gerber file: {0}", _originalfilename);
BoardSide Side = BoardSide.Unknown;
BoardLayer Layer = BoardLayer.Unknown;
Gerber.DetermineBoardSideAndLayer(_originalfilename, out Side, out Layer);
if (Layer == BoardLayer.Outline || Layer == BoardLayer.Mill)
{
forcezerowidth = true;
precombinepolygons = true;
}
State.PreCombinePolygons = precombinepolygons;
if (Layer == BoardLayer.Silk)
{
State.IgnoreZeroWidth = true;
}
PLS = PolyLineSet.LoadGerberFileFromStream(log, new StreamReader(MS), _originalfilename, forcezerowidth, false, State);
MS.Seek(0, SeekOrigin.Begin);
PLS.Side = State.Side;
PLS.Layer = State.Layer;
if (Layer == BoardLayer.Outline)
{
PLS.FixPolygonWindings();
}
}
PLS.CalcPathBounds();
BoundingBox.AddBox(PLS.BoundingBox);
log.AddString(String.Format ("Loaded {0}: {1:N1} x {2:N1} mm", Path.GetFileName(_originalfilename), PLS.BoundingBox.BottomRight.X - PLS.BoundingBox.TopLeft.X, PLS.BoundingBox.BottomRight.Y - PLS.BoundingBox.TopLeft.Y));
PLSs.Add(PLS);
// }
// catch (Exception)
// {
// }
log.PopActivity();
return PLS;
}
catch (Exception E)
{
while (E != null)
{
Console.WriteLine("Exception adding board: {0}", E.Message);
E = E.InnerException;
}
}
log.PopActivity();
return null;
}
public Matrix3x2 BuildMatrix(int w, int h)
{
var OutlineBoundingBox = GetOutlineBoundingBox();
// Replicating:
// Translate(0, h)
// Scale(1, -1)
// Translate(1, 1)
// Scale(scale, scale)
// Translate(-Outline.TL.X, -Outline.TL.Y)
// Matrix3x2 order is Prepend (default mult) ? No, ImageSharp use Matrix3x2 which is row-major usually, but let's check operations.
// .NET Matrix3x2 operations are "Post-Multiply" by default? CreateTranslation creates a matrix.
// If M is current, M * T applies T "after" M (so T is applied to the result of M).
// Standard affine: P' = P * M.
// GDI+ usage: G.TranslateTransform... appends to the world transform.
Matrix3x2 T = Matrix3x2.Identity;
T = T * Matrix3x2.CreateTranslation((float)-OutlineBoundingBox.TopLeft.X, (float)-OutlineBoundingBox.TopLeft.Y);
T = T * Matrix3x2.CreateScale((float)scale, (float)scale);
T = T * Matrix3x2.CreateTranslation(1, 1);
T = T * Matrix3x2.CreateScale(1, -1);
T = T * Matrix3x2.CreateTranslation(0, h);
return T;
}
public void CreateBoxOutline()
{
PolyLine Box = new PolyLine(PolyLine.PolyIDs.Outline);
Box.MakeRectangle(BoundingBox.Width(), BoundingBox.Height());
Box.Translate(BoundingBox.TopLeft.X + BoundingBox.Width() / 2.0, BoundingBox.TopLeft.Y + BoundingBox.Height() / 2.0);
Box.Hole = false;
//Box.Close();
// Box.Vertices.Reverse();
ParsedGerber PLS = new ParsedGerber();
PLS.Name = "Generated BoundingBox";
PLS.Generated = true;
PLS.DisplayShapes.Add(Box);
PLS.OutlineShapes.Add(Box);
PLS.Shapes.Add(Box);
PLS.Layer = BoardLayer.Outline;
PLS.Side = BoardSide.Both;
// PLS.FixPolygonWindings();
PLS.CalcPathBounds();
PLSs.Add(PLS);
}
public void DrawAllFiles(string v1, double dpi, ProgressLog Logger = null)
{
scale = dpi / 25.4d; // dpi
var OutlineBoundingBox = GetOutlineBoundingBox();
double bw = Math.Abs(OutlineBoundingBox.BottomRight.X - OutlineBoundingBox.TopLeft.X);
double bh = Math.Abs(OutlineBoundingBox.BottomRight.Y - OutlineBoundingBox.TopLeft.Y);
int width = (int)((bw * scale));
int height = (int)((bh * scale));
int w = width + 3;
int h = height + 3;
Matrix3x2 TransformCopy = Matrix3x2.Identity;
// Build Matrix
{
var T = Matrix3x2.Identity;
T = T * Matrix3x2.CreateTranslation(0, h);
T = T * Matrix3x2.CreateScale(1, -1);
T = T * Matrix3x2.CreateTranslation(1, 1);
T = T * Matrix3x2.CreateScale((float)scale, (float)scale);
T = T * Matrix3x2.CreateTranslation((float)-OutlineBoundingBox.TopLeft.X, (float)-OutlineBoundingBox.TopLeft.Y);
TransformCopy = T;
}
foreach (var L in PLSs)
{
string FileName = v1 + "_" + L.Layer.ToString() + "_" + L.Side.ToString() + ".png";
if (Logger != null) Logger.AddString(String.Format("Rendering {0}-{1}", L.Layer.ToString(), L.Side.ToString()));
Image<Rgba32> B2 = new Image<Rgba32>(w + 3, h + 3);
ApplyAASettings(null); // No-op now/handled internally
var G3 = new ImageSharpGraphicsInterface(B2);
G3.Clear(Color.White);
G3.Transform = TransformCopy; // Matrix3x2 is struct, copy is automatic
GerberLibrary.Core.Primitives.Pen P = new GerberLibrary.Core.Primitives.Pen(Color.Black, 1.0f / (float)(scale));
int Shapes = 0;
Shapes += DrawLayerToGraphics(Color.Black, true, G3, P, L, false);
B2.Save(FileName);
}
}
public void DrawXRayToFile(string basefilename, double dpi = 400, bool showimage = true, bool bottomside = false, ProgressLog Logger = null)
{
string filenamet= basefilename + "_top_XRAY.png";
string filenameb = basefilename +"_bottom_XRAY.png";
DrawXRayBoard(filenamet, filenameb, dpi, ActiveColorSet, basefilename, Logger, bottomside);
if (showimage)
{
System.Diagnostics.Process.Start(filenamet);
System.Diagnostics.Process.Start(filenameb);
}
}
public string DrawToFile(string basefilename, BoardSide CurrentLayer, double dpi = 400, bool showimage = true, ProgressLog Logger = null)
{
Image<Rgba32> B = DrawBoard(dpi, CurrentLayer, ActiveColorSet, basefilename, Logger);
string filename = basefilename + "_Combined_" + CurrentLayer.ToString() + ".png";
B.Save(filename);
if (showimage) System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(filename) { UseShellExecute = true });
return filename;
}
public Image<Rgba32> RenderToImage(int w, int h, Matrix3x2 Transform, Color foregroundcolor, Color backgroundcolor, ParsedGerber PLS, bool fill, bool forcefill = false)
{
Image<Rgba32> B2;
try
{
B2 = new Image<Rgba32>(w + 3, h + 3);
}
catch (Exception)
{
Console.WriteLine("Error: Failed to create image of size {0}x{1}", w, h);
return null;
}
var G3 = new ImageSharpGraphicsInterface(B2);
G3.Clear(backgroundcolor);
G3.Transform = Transform;
ApplyAASettings(G3);
GerberLibrary.Core.Primitives.Pen P = new GerberLibrary.Core.Primitives.Pen(foregroundcolor, 1.0f / (float)(scale));
int Shapes = 0;
Shapes += DrawLayerToGraphics(foregroundcolor, fill, G3, P, PLS, forcefill);
// if (Shapes == 0) return null;
return B2;
}
public void SetColors(BoardRenderColorSet colors)
{
ActiveColorSet = colors;
}
public void WriteImageFiles(string TargetFileBaseName, double dpi = 200, bool showimage = true, bool xray = true, bool normal = true, ProgressLog Logger = null)
{
if (normal)
{
if (Logger != null) Logger.AddString(String.Format("Build top layer image at {0} dpi", dpi));
DrawToFile(TargetFileBaseName, BoardSide.Top, dpi, showimage, Logger);
if (Logger != null) Logger.AddString(String.Format("Build bottom layer image at {0} dpi", dpi));
DrawToFile(TargetFileBaseName, BoardSide.Bottom, dpi, showimage, Logger);
}
if (xray)
{
if (Logger != null) Logger.AddString(String.Format("Build xray images at {0} dpi", dpi));
DrawXRayToFile(TargetFileBaseName, dpi, showimage, true, Logger);
}
}
private void AddFileToSet(string aname, ProgressLog Logger, double drillscaler = 1.0, bool forcezerowidth = false)
{
if (Streams.ContainsKey(aname))
{
AddFileToSet(Streams[aname], aname, Logger, drillscaler, forcezerowidth);
}
else
{
Logger.AddString(String.Format("[ERROR] no stream for {0}!!!", aname));
}
}
private void AddFileToSet(MemoryStream MS, string aname, ProgressLog Logger, double drillscaler = 1.0, bool forcezerowidth = false)
{
Streams[aname] = MS;
///string[] filesplit = a.Split('.');
bool zerowidth = forcezerowidth;
bool precombine = false;
BoardSide aSide;
BoardLayer aLayer;
Gerber.DetermineBoardSideAndLayer(aname, out aSide, out aLayer);
if (aLayer == BoardLayer.Outline || (aLayer == BoardLayer.Mill && hasgko == false))
{
zerowidth = true;
precombine = true;
}
MS.Seek(0, SeekOrigin.Begin);
AddBoardToSet(MS, aname, Logger, zerowidth, precombine, drillscaler);
}
private void ApplyBumpMapping(Image<Rgba32> _Target, Image<Rgba32> _Bump, int w, int h)
{
// Direct access is slow without ProcessPixelRows, but let's stick to simple first for correctness.
// Loop x,y
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
Rgba32 TargetPixel = _Target[x, y];
Rgba32 B1 = _Bump[x, y];
if (true)//B1.A > 0)
{
Rgba32 B2 = B1;
Rgba32 B4 = B1;
if (x < w - 1)
{
B2 = _Bump[x + 1, y];
B4 = B2;
}
Rgba32 B3 = B1;
if (y < h - 1) B3 = _Bump[x, y + 1];
if (y < h - 1 && x < w - 1)
{
B4 = _Bump[x + 1, y + 1];
}
// GetBrightness is not on Rgba32.
// Helper: 0.2126*R + 0.7152*G + 0.0722*B
float GetBrightness(Rgba32 c) => (0.2126f * c.R + 0.7152f * c.G + 0.0722f * c.B) / 255.0f;
float dx1 = (GetBrightness(B1) - GetBrightness(B2));
float dx2 = (GetBrightness(B3) - GetBrightness(B4));
float dy1 = (GetBrightness(B1) - GetBrightness(B3));
float dy2 = (GetBrightness(B2) - GetBrightness(B4));
float dx = dx1 + dx2;
float dy = dy1 + dy2;
if (dx == 0 && dy == 0)
{
//skip
}
else
{
double ang = Math.Atan2(dy, dx);
double dist = Math.Sqrt(dx * dx + dy * dy);
double L = Math.Sin(ang - 1.4);
// Convert Rgba32 to Color to use Lighter/Darker
Color TC = Color.FromRgba(TargetPixel.R, TargetPixel.G, TargetPixel.B, TargetPixel.A);
if (L > 0) _Target[x, y] = Lighter(TC, L * 0.04).ToPixel<Rgba32>();
else
{
_Target[x, y] = Darker(TC, Math.Abs(L * 0.04)).ToPixel<Rgba32>();
}
}
}
}
}
}
private void CarveOutlineAndMillInnerPolygonsFromImage(string basefilename, int w, int h, ImageSharpGraphicsInterface G, Image<Rgba32> _Target, Matrix3x2 TransformCopy)
{
var T = G.Transform;
G.Transform = TransformCopy;
var L = from i in PLSs where (i.Layer == BoardLayer.Outline || i.Layer == BoardLayer.Mill) && i.Side == BoardSide.Both select i;
if (L.Count() == 0) return;
List<PolyLine> ShapesList = new List<PolyLine>();
Dictionary<int, PolyLine> InsidePolygons = new Dictionary<int, PolyLine>();
foreach (var l in L)
{
l.FixPolygonWindings();
ShapesList.AddRange(l.OutlineShapes);
}
List<int> AddedIds = new List<int>();
// Helpers.LineSegmentsToPolygons()
for (int i = 0; i < ShapesList.Count; i++)
{
if (ClipperLib.Clipper.Orientation(ShapesList[i].toPolygon()) == false)
{
InsidePolygons[i] = ShapesList[i];
}
}
Image<Rgba32> B2 = new Image<Rgba32>(w, h); // Black transparent by default?
var G2 = new ImageSharpGraphicsInterface(B2);
// G2.Clear(Color.Black); // Clear black opague? Orig code: G2.Clear(Color.Black);
G2.Clear(Color.Black); // Rgba32(0,0,0,255)
G2.Transform = TransformCopy;
ApplyAASettings(G2);
//G.CompositingMode = CompositingMode.SourceCopy;
foreach (var a in InsidePolygons.Values)
{
List<PointF> Points = new List<PointF>();
for (int j = 0; j < a.Count(); j++)
{
var P1 = a.Vertices[j];
Points.Add(new PointF((float)((P1.X)), (float)((P1.Y))));
}
// FillPolygon(new SolidBrush(Color.Red)...)
G2.FillPolygon(new GerberLibrary.Core.Primitives.SolidBrush(Color.Red), Points.ToArray());
}
if (Gerber.SaveIntermediateImages) B2.Save("11 outlines_redimages.png");
// Carve logic
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
var S = B2[x, y];
if (S.R > 0)
{
var O = _Target[x, y];
// Target.SetPixelIDX(idx, Color.FromArgb(255 - S.R, Target.GetPixelIDX(idx)));
// It seems the intent is to reduce alpha?
// Orig: Color.FromArgb(255 - S.R, Target...) -> sets Alpha = 255 - S.R.
// So if S.R is 255 (Red), Alpha becomes 0.
_Target[x, y] = Color.FromRgba(O.R, O.G, O.B, (byte)(255 - S.R)).ToPixel<Rgba32>();
}
}
}
if (Gerber.SaveIntermediateImages) _Target.Save("22 outlines_carved.png");
// G.CompositingMode = CompositingMode.SourceOver;
G.Transform = T;
if (Gerber.ExtremelyVerbose) Console.WriteLine("polygons #: {0} total, {1} carved", ShapesList.Count, InsidePolygons.Count);
G2.Clear(Color.Black);
G2.Transform = TransformCopy;
foreach (var a in ShapesList)
{
List<PointF> Points = new List<PointF>();
for (int j = 0; j < a.Count(); j++)
{
var P1 = a.Vertices[j];
Points.Add(new PointF((float)((P1.X)), (float)((P1.Y))));
}
G2.DrawLines(new GerberLibrary.Core.Primitives.Pen(Color.Transparent, 1.0f / (float)scale), Points.ToArray());
// Wait, DrawPolygon with Transparent pen?
// Orig: G2.DrawPolygon(new Pen(Color.Transparent, ...)) ?
// If Transparent, it does nothing?
}
if (Gerber.SaveIntermediateImages) B2.Save("33 outlines_3aftershapelist.png");
foreach (var a in InsidePolygons.Values)
{
List<PointF> Points = new List<PointF>();
for (int j = 0; j < a.Count(); j++)
{
var P1 = a.Vertices[j];
Points.Add(new PointF((float)((P1.X)), (float)((P1.Y))));
}
// new Pen(Color.FromArgb(200, 255, 255, 0), ...)
G2.DrawLines(new GerberLibrary.Core.Primitives.Pen(Color.FromRgba(255, 255, 0, 200), 1.0f / (float)scale), Points.ToArray());
}
if (Gerber.SaveIntermediateImages) B2.Save("44 outlines_4afterinside.png");
}
private void CheckForOutlineFiles(ProgressLog Logger)
{
List<ParsedGerber> Outlines = new List<ParsedGerber>();
List<ParsedGerber> Mills = new List<ParsedGerber>();
List<ParsedGerber> Unknowns = new List<ParsedGerber>();
foreach (var a in PLSs)
{
if (a.Side == BoardSide.Both && (a.Layer == BoardLayer.Outline))
{
Outlines.Add(a);
}
if (a.Side == BoardSide.Both && (a.Layer == BoardLayer.Mill))
{
Mills.Add(a);
}
if (a.Side == BoardSide.Unknown && a.Layer == BoardLayer.Unknown)
{
Unknowns.Add(a);
Errors.Add(String.Format("Unknown file in set:{0}", Path.GetFileName(a.Name)));
if (Logger != null) Logger.AddString(String.Format("Unknown file in set:{0}", Path.GetFileName(a.Name)));
}
}
if (Outlines.Count == 0)
{
if (Unknowns.Count == 0)
{
Errors.Add(String.Format("No outline file found and all other files accounted for! "));
if (Logger != null) Logger.AddString(String.Format("No outline file found and all other files accounted for! "));
// if (Mills.Count == 1)
// {
// Mills[0].Layer = BoardLayer.Outline;
// Errors.Add(String.Format("Elevating mill file to outline!"));
// if (Logger != null) Logger.AddString(String.Format("Elevating mill file to outline!"));
// }
// else
// if (!InventOutlineFromMill())
{
CreateBoxOutline();
}
}
else
{
CreateBoxOutline();
return;
//InventOutline();
//return;
//foreach (var a in Unknowns)
//{
// PLSs.Remove(a);
// hasgko = true;
// a.Layer = BoardLayer.Outline;
// a.Side = BoardSide.Both;
// Console.WriteLine("Note: Using {0} as outline file", Path.GetFileName(a.Name));
// if (Logger != null) Logger.AddString(String.Format("Note: Using {0} as outline file", Path.GetFileName(a.Name)));
// bool zerowidth = true;
// bool precombine = true;
// var b = AddBoardToSet(a.Name, zerowidth, precombine, 1.0);
// b.Layer = BoardLayer.Outline;
// b.Side = BoardSide.Both;
//}
}
}
}
private void CheckRelativeBoundingBoxes(ProgressLog Logger)
{
List<ParsedGerber> DrillFiles = new List<ParsedGerber>();
List<ParsedGerber> DrillFilesToReload = new List<ParsedGerber>();
Bounds BB = new Bounds();
foreach (var a in PLSs)
{
if (a.Layer == BoardLayer.Drill)
{
DrillFiles.Add(a);
}
else
{
BB.AddBox(a.BoundingBox);
}
}
foreach (var a in DrillFiles)
{
if (a.BoundingBox.Intersects(BB) == false)
{
Errors.Add(String.Format("Drill file {0} does not seem to touch the main bounding box!", Path.GetFileName(a.Name)));
if (Logger != null) Logger.AddString(String.Format("Drill file {0} does not seem to touch the main bounding box!", Path.GetFileName(a.Name)));
PLSs.Remove(a);
}
}
BoundingBox = new Bounds();
foreach (var a in PLSs)
{
// Console.WriteLine("Progress: Adding board {6} to box::{0:N2},{1:N2} - {2:N2},{3:N2} -> {4:N2},{5:N2}", a.BoundingBox.TopLeft.X, a.BoundingBox.TopLeft.Y, a.BoundingBox.BottomRight.X, a.BoundingBox.BottomRight.Y, a.BoundingBox.Width(), a.BoundingBox.Height(), Path.GetFileName(a.Name));
//Console.WriteLine("adding box for {0}:{1},{2}", a.Name, a.BoundingBox.Width(), a.BoundingBox.Height());
BoundingBox.AddBox(a.BoundingBox);
}
}
Image<Rgba32> DrawBoard(double dpi, BoardSide CurrentLayer, BoardRenderColorSet Colors, string basefilename = null, ProgressLog Logger = null, bool ForceWhite = false)
{
if (Logger != null) Logger.PushActivity("DrawBoard");
try
{
scale = dpi / 25.4d; // dpi
var OutlineBoundingBox = GetOutlineBoundingBox();
double bw = Math.Abs(OutlineBoundingBox.BottomRight.X - OutlineBoundingBox.TopLeft.X);
double bh = Math.Abs(OutlineBoundingBox.BottomRight.Y - OutlineBoundingBox.TopLeft.Y);
int width = (int)((bw * scale));
int height = (int)((bh * scale));
int w = width + 3;
int h = height + 3;
Image<Rgba32> _Final = new Image<Rgba32>(w, h); // Transparent by default
Image<Rgba32> _BoardPlate = new Image<Rgba32>(w, h);
Image<Rgba32> _SilkMask = new Image<Rgba32>(w, h);
Matrix3x2 TransformCopy = Matrix3x2.Identity;
{
// Build transform manually since we are not using 'G'
// Replicate logic:
// Translate(0, h)
// Scale(1, -1)
if (CurrentLayer == BoardSide.Bottom)
{
// Translate(w, 0)
// Scale(-1, 1)
}
// Translate(1, 1)
// Scale(scale, scale)
// Translate(-TL.X, -TL.Y)
var T = Matrix3x2.Identity;
T = T * Matrix3x2.CreateTranslation(0, h);
T = T * Matrix3x2.CreateScale(1, -1);
if (CurrentLayer == BoardSide.Bottom)
{
T = T * Matrix3x2.CreateTranslation(w, 0);
T = T * Matrix3x2.CreateScale(-1, 1);
}
T = T * Matrix3x2.CreateTranslation(1, 1);
T = T * Matrix3x2.CreateScale((float)scale, (float)scale);
T = T * Matrix3x2.CreateTranslation((float)-OutlineBoundingBox.TopLeft.X, (float)-OutlineBoundingBox.TopLeft.Y);
TransformCopy = T;
}
if (Logger != null) Logger.AddString("Drawing outline files");
// Color.FromArgb(a,r,g,b) -> Red=0, Green=0, Blue=0, Alpha=20? No, System.Drawing Color.FromArgb(alpha, basecolor) ?
// Color.FromArgb(20, 0, 0, 0) -> A=20, R=0,G=0,B=0.
Image<Rgba32> _OutlineBase = DrawIfExists(width, height, TransformCopy, Colors.BoardRenderBaseMaterialColor, BoardLayer.Outline, BoardSide.Both, basefilename, true, 1, true);
if (Logger != null) Logger.AddString("Drawing mill files");
Image<Rgba32> _OutlineMill = DrawIfExists(width, height, TransformCopy, Colors.BoardRenderBaseMaterialColor, BoardLayer.Mill, BoardSide.Both, basefilename, true, 1, true);
if (Logger != null) Logger.AddString("Drawing copper files");
Image<Rgba32> _Copper = DrawIfExists(width, height, TransformCopy, Color.FromRgb(80, 80, 0), BoardLayer.Copper, CurrentLayer, basefilename, true);
if (Logger != null) Logger.AddString("Drawing silk files");
Image<Rgba32> _Silk = DrawIfExists(width, height, TransformCopy, Colors.BoardRenderSilkColor, BoardLayer.Silk, CurrentLayer, basefilename, true, 0.2f);
if (Logger != null) Logger.AddString("Drawing soldermask files");
Image<Rgba32> _SolderMaskHoles = DrawIfExists(width, height, TransformCopy, Colors.BoardRenderPadColor, BoardLayer.SolderMask, CurrentLayer, basefilename, true, 0.2f);
if (Logger != null) Logger.AddString("Drawing drill files");
Image<Rgba32> _DrillHoles = DrawIfExists(width, height, TransformCopy, Color.Black, BoardLayer.Drill, BoardSide.Both, basefilename, true, 1.0f);
if (Gerber.SaveIntermediateImages == true)
{
Console.WriteLine("Progress: Writing intermediate images:");
if (_Copper != null) { _Copper.Save(CurrentLayer.ToString() + "_copper.png"); Console.WriteLine("Progress: Copper"); }
if (_SolderMaskHoles != null) { _SolderMaskHoles.Save(CurrentLayer.ToString() + "_soldermaskholes.png"); Console.WriteLine("Progress: SolderMask"); }
if (_DrillHoles != null) { _DrillHoles.Save(CurrentLayer.ToString() + "_drill.png"); Console.WriteLine("Progress: Drill"); }
if (_OutlineBase != null) { _OutlineBase.Save(CurrentLayer.ToString() + "_base.png"); Console.WriteLine("Progress: Base"); }
if (_OutlineMill != null) { _OutlineMill.Save(CurrentLayer.ToString() + "_mill.png"); Console.WriteLine("Progress: Mill"); }
}
// Composition logic
{
{
// Draw OutlineBase and Mill onto _BoardPlate
// Using ImageSharp DrawImage for simple overlay
var G = new ImageSharpGraphicsInterface(_BoardPlate);
G.Clear(Color.Transparent);
if (_OutlineBase != null) _BoardPlate.Mutate(ctx => ctx.DrawImage(_OutlineBase, 1.0f));
if (_OutlineMill != null) _BoardPlate.Mutate(ctx => ctx.DrawImage(_OutlineMill, 1.0f));
if (Logger != null) Logger.AddString("Carving inner polygons from board");
if (Gerber.SaveIntermediateImages == true) _BoardPlate.Save("00 Outlines before carve.png");
CarveOutlineAndMillInnerPolygonsFromImage(basefilename, w, h, G, _BoardPlate, TransformCopy);
if (Gerber.SaveIntermediateImages == true) _BoardPlate.Save("66 OutlinesCarved.png");
// Clear _Final
_Final.Mutate(ctx => ctx.Clear(Color.Transparent));
if (Logger != null) Logger.AddString("Carving drills from board");
if (_DrillHoles != null)
{
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
var O = _BoardPlate[x, y];
var Drill = _DrillHoles[x, y];
if (Drill.A > 0)
{
float OA = 1.0f - (Drill.A / 255.0f);
float DA = O.A / 255.0f; // Original alpha normalized
// New Alpha = OA * DA * 255
// RGB preserved?
_BoardPlate[x, y] = new Rgba32(O.R, O.G, O.B, (byte)Math.Round(OA * DA * 255.0f));
}
}
}
}
// Draw BoardPlate onto Final?
// G.DrawImage(_BoardPlate...) -> _Final?
// Original code: G.DrawImage(_BoardPlate, ...) onto _Final context?
// Wait, logic was:
// G = Graphics.FromImage(_Final);
// G.DrawImage(_BoardPlate...)
_Final.Mutate(ctx => ctx.DrawImage(_BoardPlate, 1.0f));
if (Gerber.SaveIntermediateImages == true) _BoardPlate.Save("BoardPlateAfterDrills.png");
}
if (Logger != null) Logger.AddString("Layering copper on board");
if (_Copper != null)
{
// Loop to composite Copper onto Final
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
var O = _Final[x, y];
var C = _Copper[x, y];
if (O.A > 0)
{
if (C.A > 0)
{
// Mix copper color with board color?
Rgba32 CopperColor = Colors.BoardRenderPadColor.ToPixel<Rgba32>();
float A = (C.A / 255.0f);
float IA = 1 - A;
// Original Logic:
// newDC = CopperColor * A + O * IA
// Alpha preserved from O
_Final[x, y] = new Rgba32(
(byte)Math.Round(CopperColor.R * A + O.R * IA),
(byte)Math.Round(CopperColor.G * A + O.G * IA),
(byte)Math.Round(CopperColor.B * A + O.B * IA),
O.A
);
}
}
}
}
if (Gerber.SaveIntermediateImages == true) _Final.Save("FinalAfterCopper.png");
}
{
// Draw BoardPlate onto SilkMask
_SilkMask.Mutate(ctx => ctx.Clear(Color.Transparent));
_SilkMask.Mutate(ctx => ctx.DrawImage(_BoardPlate, 1.0f));
}
if (Logger != null) Logger.AddString("Applying soldermask to board");
if (_SolderMaskHoles != null)
{
// Pixel loop for Soldermask, Copper, SilkMask, BoardPlate
// Need generic access
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
var O = _Final[x, y];
var Mask = _SolderMaskHoles[x, y];
if (Mask.A > 0)
{
// SilkMask Update
var OSM = _SilkMask[x, y];
float OA = 1.0f - (Mask.A / 255.0f);
float DA = O.A / 255.0f;
_SilkMask[x, y] = new Rgba32(O.R, O.G, O.B, (byte)Math.Round(OA*DA*255.0f));
}
Rgba32 Cop = new Rgba32(0,0,0,0);
if (_Copper != null) Cop = _Copper[x, y];
var BmP = _BoardPlate[x, y];
if (Cop.A > 0 && Mask.A > 0)
{
Rgba32 SurfaceFinish = Colors.BoardRenderPadColor.ToPixel<Rgba32>();
// Use BaseColor = O
float A = (Cop.A / 255.0f * Mask.A / 255.0f * (BmP.A / 255.0f)) * 0.85f;
float IA = 1.0f - A;
// Composite O with SurfaceFinish
byte newR = (byte)Math.Round(SurfaceFinish.R * A + O.R * IA);
byte newG = (byte)Math.Round(SurfaceFinish.G * A + O.G * IA);
byte newB = (byte)Math.Round(SurfaceFinish.B * A + O.B * IA);
// Update O (which is used in next calculation?)
// Original logic updates 'O' variable, then uses it to calculate newC.
// Also modulate Alpha?
// O = Color.FromArgb((byte)Math.Round(O.A * BmP.A / 255.0f), ...)
byte newA = (byte)Math.Round(O.A * BmP.A / 255.0f);
O = new Rgba32(newR, newG, newB, newA);
}
// Calculate newC
float OA2 = (Mask.A / 255.0f) * 0.9f + 0.1f;
float IOA = 1.0f - OA2;
Rgba32 S = Colors.BoardRenderColor.ToPixel<Rgba32>();
if (Cop.A > 0)
{
// Interpolate S with TraceColor
Rgba32 Trace = Colors.BoardRenderTraceColor.ToPixel<Rgba32>();
float CA = Cop.A / 255.0f;
// Simple mix
S = new Rgba32(
(byte)(S.R * (1-CA) + Trace.R * CA),
(byte)(S.G * (1-CA) + Trace.G * CA),
(byte)(S.B * (1-CA) + Trace.B * CA),
S.A
);
}
_Final[x, y] = new Rgba32(
(byte)Math.Round(O.R * OA2 + S.R * IOA),
(byte)Math.Round(O.G * OA2 + S.G * IOA),
(byte)Math.Round(O.B * OA2 + S.B * IOA),
O.A
);
}
}
if (Gerber.SaveIntermediateImages == true) _Final.Save("FinalAfterSoldermask.png");
}
if (Logger != null) Logger.AddString("Applying silkscreen to board");
if (_Silk != null)
{
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
var SilkPixel = _Silk[x, y];
float AS = SilkPixel.A / 255.0f;
if (AS > 0)
{
var OutputPixel = _Final[x, y];
var Mask = _SilkMask[x, y];
// float AO = O.A / 255.0f;
float AM = (Mask.A / 255.0f) * (1 - AS);
if (Mask.A < 255 && _Copper != null)
{
var CopperPixel = _Copper[x, y];
if (CopperPixel.A > 0)
{
AM = AM * (1 - (CopperPixel.A / 255.0f)) + 1 * (CopperPixel.A / 255.0f);
}
}
float iAM = 1.0f - AM;
_Final[x, y] = new Rgba32(
(byte)Math.Round(OutputPixel.R * AM + SilkPixel.R * iAM),
(byte)Math.Round(OutputPixel.G * AM + SilkPixel.G * iAM),
(byte)Math.Round(OutputPixel.B * AM + SilkPixel.B * iAM),
OutputPixel.A);
}
}
}
if (Gerber.SaveIntermediateImages == true) _Final.Save("FinalAfterSilk.png");
}
if (_Copper != null && Gerber.GerberRenderBumpMapOutput)
{
ApplyBumpMapping(_Final, _Copper, w, h);
}
}
if (ForceWhite)
{
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
if (_Final[x, y].A == 0) _Final[x, y] = new Rgba32(255, 255, 255, 255);
}
}
}
if (Logger != null) Logger.PopActivity();
return _Final;
}
catch(Exception e)
{
if (Logger != null) Logger.PopActivity();
Console.WriteLine("DrawBoard Error: " + e.Message);
}
return null;
}
void DrawXRayBoard(string ftop, string fbot, double dpi, BoardRenderColorSet Colors, string basefilename = null, ProgressLog Logger = null, bool XRayFromBottom = false)
{
if (Logger != null) Logger.PushActivity("DrawXRayBoard");
scale = dpi / 25.4d; // dpi
var OutlineBoundingBox = GetOutlineBoundingBox();
double bw = Math.Abs(OutlineBoundingBox.BottomRight.X - OutlineBoundingBox.TopLeft.X);
double bh = Math.Abs(OutlineBoundingBox.BottomRight.Y - OutlineBoundingBox.TopLeft.Y);
int width = (int)((bw * scale));
int height = (int)((bh * scale));
int w = width + 3;
int h = height + 3;
Image<Rgba32> _Final = new Image<Rgba32>(w, h);
Image<Rgba32> _FinalBot = new Image<Rgba32>(w, h);
Image<Rgba32> _BoardPlate = new Image<Rgba32>(w, h); // Unused in current commented code? Keep for safety.
Matrix3x2 TransformCopy = Matrix3x2.Identity;
{
_Final.Mutate(ctx => ctx.Clear(Color.Transparent));
var T = Matrix3x2.Identity;
T = T * Matrix3x2.CreateTranslation(0, h);
T = T * Matrix3x2.CreateScale(1, -1);
if (XRayFromBottom)
{
T = T * Matrix3x2.CreateTranslation(w, 0);
T = T * Matrix3x2.CreateScale(-1, 1);
}
T = T * Matrix3x2.CreateTranslation(1, 1);
T = T * Matrix3x2.CreateScale((float)scale, (float)scale);
T = T * Matrix3x2.CreateTranslation((float)-OutlineBoundingBox.TopLeft.X, (float)-OutlineBoundingBox.TopLeft.Y);
TransformCopy = T;
}
if (Logger != null) Logger.AddString("Drawing outline files");
Image<Rgba32> _OutlineBase = DrawIfExists(width, height, TransformCopy, Color.FromRgba(0, 0, 0, 20), BoardLayer.Outline, BoardSide.Both, basefilename, true, 1, true);
if (Logger != null) Logger.AddString("Drawing mill files");
Image<Rgba32> _OutlineMill = DrawIfExists(width, height, TransformCopy, Color.FromRgba(255, 255, 255, 255), BoardLayer.Mill, BoardSide.Both, basefilename, true, 1, true);
if (Logger != null) Logger.AddString("Drawing copper files");
Image<Rgba32> _Copper = RenderRainbowToBitmap(width, height, TransformCopy, 64, BoardLayer.Any, BoardSide.Either, true, false, false);
Image<Rgba32> _CopperBot = RenderRainbowToBitmap(width, height, TransformCopy, 64, BoardLayer.Any, BoardSide.Either, true,false,true);
if (Logger != null) Logger.AddString("Drawing drill files");
Image<Rgba32> _DrillHoles = DrawIfExists(width, height, TransformCopy, Color.Black, BoardLayer.Drill, BoardSide.Both, basefilename, true, 1.0f);
if (Gerber.SaveIntermediateImages == true)
{
if (Logger != null) Logger.AddString("Writing intermediate images");
if (_Copper != null) { _Copper.Save("xray_copper.png"); if (Logger != null) Logger.AddString("Copper"); }
if (_DrillHoles != null) { _DrillHoles.Save("xray_drill.png"); if (Logger != null) Logger.AddString("Drill"); }
if (_OutlineBase != null) { _OutlineBase.Save("xray_base.png"); if (Logger != null) Logger.AddString("Base"); }
if (_OutlineMill != null) { _OutlineMill.Save("xray_mill.png"); if (Logger != null) Logger.AddString("Mill"); }
}
{
if (Logger != null) Logger.AddString("Layering copper on plate");
if (_Copper != null)
{
// Composition Loop
// Use simple loops for now (accessing image[x,y])
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
int idxinv = (w - x - 1); // x index for Bot?
// Logic: idxinv = (y * Width + (w-x-1)) * 4 in original code.
// So we just access [w-x-1, y]
var O = _Final[x, y];
var C = _Copper[x, y];
// CopperBot logic appears to use same loop?
// Yes, uses parallel logic for FinalBot.
var CB = new Rgba32(0,0,0,0);
if (_CopperBot != null && x < _CopperBot.Width && y < _CopperBot.Height) // bounding check safety
CB = _CopperBot[x, y];
if (true) // Original had if (P.A > 0) commented code wrapper? No, scope wrapper?
{
if (true) // if (C.A > 0) logic
{
float A = (C.A / 255.0f);
float IA = 1 - A;
// newDC = C * A + O * IA.
// Note: O is initially Transparent? Yes.
// C is Rainbow output (opaque colors).
// So this is C blended over O.
_Final[x, y] = new Rgba32(
(byte)Math.Round(C.R * A + O.R * IA),
(byte)Math.Round(C.G * A + O.G * IA),
(byte)Math.Round(C.B * A + O.B * IA),
255 // Alpha fixed to 255? Orig: Color.FromArgb(255, ...)
);
// FinalBot
// newDCR = CB * A + O * IA??
// Orig uses O.R * IA. O comes from Final[x,y] (top).
// So Top affects Bot?
// "O" is Final (Top) pixel.
// So Bot image is composite of CopperBot and Final(Top)?
// Yes, XRay view usually shows layers through.
// Ensure bounds for FinalBot
if (idxinv >= 0 && idxinv < w)
{
_FinalBot[idxinv, y] = new Rgba32(
(byte)Math.Round(CB.R * A + O.R * IA),
(byte)Math.Round(CB.G * A + O.G * IA),
(byte)Math.Round(CB.B * A + O.B * IA),
255
);
}
}
}
}
}
if (Gerber.SaveIntermediateImages == true) _Final.Save("XRayFinalAfterCopper.png");
}
if (_DrillHoles != null)
{
// Composite Drill Holes
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
var O = _Final[x, y];
var Drill = _DrillHoles[x, y];
if (Drill.A > 0)
{
float OA = 1.0f - (Drill.A / 255.0f);
float DA = O.A / 255.0f;
// Reduce Alpha? But XRay uses opaque.
// Orig code for XRay just saved images?
// Orig code had commented out DrillHole logic in XRay?
// It seems XRay function ends at line 1475 in orig (Wait, I only read up to 1450).
// Let's check further down before committing.
}
}
}
}
}
if (ftop.Length > 0) _Final.Save(ftop);
if (fbot.Length > 0) _FinalBot.Save(fbot);
if (Gerber.SaveIntermediateImages)
{
// ...
}
if (Logger != null) Logger.PopActivity();
}
private Image<Rgba32> DrawIfExists(int w, int h, Matrix3x2 TransformCopy, Color color, BoardLayer boardLayer, BoardSide boardSide, string filename = null, bool fill = true, float alpha = 0.83f, bool forcefill = false)
{
Image<Rgba32> B2 = RenderToImage(w, h, TransformCopy, color, boardLayer, boardSide, fill, forcefill);
if (B2 != null)
{
if (filename != null)
{
// B2.Save(filename + "_Layer_" + boardSide.ToString() + "_" + boardLayer.ToString() + ".png");
}
// Alpha blending is handled by ImageSharp during composition if needed, or we can mutate here.
// Replicating ColorMatrix alpha scaling logic on the whole image:
if (Math.Abs(alpha - 1.0f) > 0.01f)
{
// Multiply alpha on all pixels
B2.Mutate(ctx => ctx.ProcessPixelRowsAsVector4(row => {
for(int i=0;i<row.Length;i++)
{
row[i].W *= alpha;
}
}));
}
}
return B2;
}
public static int CountLayerShapes(ParsedGerber l)
{
int shapes = 0;
foreach (var Shape in l.DisplayShapes)
{
if (Shape.Vertices.Count > 1)
{
//if (Shape.Thin == false)
shapes++;
}
}
return shapes;
}
private int DrawLayerToGraphics(Color color, bool fill, GraphicsInterface G2, Pen P, ParsedGerber l, bool forcefill = false)
{
int RenderedShapes = 0;
foreach (var Shape in l.DisplayShapes)
{
if (Shape.Vertices.Count > 1)
{
//if (Shape.Thin == false)
DrawShape(G2, P, color, Shape, (fill && (Shape.Thin == false)) || forcefill, l.TranslationSinceLoad.X, l.TranslationSinceLoad.Y);
RenderedShapes++;
}
}
return RenderedShapes;
}
private void DrawShape(GraphicsInterface G, Pen P, Color c, PolyLine Shape, bool fill, double dx, double dy)
{
List<PointF> Points = new List<PointF>();
for (int j = 0; j < Shape.Count(); j++)
{
var P1 = Shape.Vertices[j];
Points.Add(new PointF((float)((P1.X - dx)), (float)((P1.Y - dy))));
}
if (Points.Count() > 1)
{
if (fill)
{
if (Points[0] == Points[Points.Count() - 1])
{
Points.Remove(Points.Last());
}
if (Shape.ClearanceMode)
{
G.CompositingMode = CompositingMode.SourceCopy;
G.FillPolygon(new SolidBrush(Color.Transparent), Points.ToArray());
G.CompositingMode = CompositingMode.SourceOver;
}
else
{
G.FillPolygon(new SolidBrush(c), Points.ToArray());
// G.DrawLines(new Pen(c, P.Width / 4), Points.ToArray());
}
}
else
{
G.DrawLines(P, Points.ToArray());
}
}
}
private void FixEagleDrillExportIssues(ProgressLog Logger)
{
if (Gerber.SkipEagleDrillFix == true) { Logger.AddString("skipping eagle fix"); return; };
List<ParsedGerber> DrillFiles = new List<ParsedGerber>();
List<Tuple<double, ParsedGerber>> DrillFilesToReload = new List<Tuple<double, ParsedGerber>>();
Bounds BB = new Bounds();
foreach (var a in PLSs)
{
if (a.Layer == BoardLayer.Drill)
{
DrillFiles.Add(a);
DrillFileScale[a.Name] = 1.0;
}
else
{
BB.AddBox(a.BoundingBox);
}
}
foreach (var a in DrillFiles)
{
var b = a.BoundingBox;
if (b.Width() > BB.Width() * 1.5 || b.Height() > BB.Height() * 1.5)
{
var MaxRatio = Math.Max(b.Width() / BB.Width(), b.Height() / BB.Height());
if (Logger != null) Logger.AddString(String.Format("Note: Really large drillfile found({0})-fix your export scripts!", a.Name));
Console.WriteLine("Note: Really large drillfile found ({0})- fix your export scripts!", a.Name);
DrillFilesToReload.Add(new Tuple<double, ParsedGerber>(MaxRatio, a));
}
}
foreach (var a in DrillFilesToReload)
{
PLSs.Remove(a.Item2);
var scale = 1.0;
if (Double.IsInfinity(a.Item1) || Double.IsNaN(a.Item1))
{
Errors.Add("Drill file size reached infinity - ignoring it");
if (Logger != null) Logger.AddString("Drill file size reached infinity - ignoring it");
}
else
{
var R = a.Item1;
while (R >= 1.5)
{
R /= 10;
scale /= 10;
}
DrillFileScale[a.Item2.Name] = scale;
AddFileToSet(a.Item2.Name, Logger, scale);
}
}
BoundingBox = new Bounds();
foreach (var a in PLSs)
{
//Console.WriteLine("Progress: Adding board {6} to box::{0:N2},{1:N2} - {2:N2},{3:N2} -> {4:N2},{5:N2}", a.BoundingBox.TopLeft.X, a.BoundingBox.TopLeft.Y, a.BoundingBox.BottomRight.X, a.BoundingBox.BottomRight.Y, a.BoundingBox.Width(), a.BoundingBox.Height(), Path.GetFileName( a.Name));
//Console.WriteLine("adding box for {0}:{1},{2}", a.Name, a.BoundingBox.Width(), a.BoundingBox.Height());
BoundingBox.AddBox(a.BoundingBox);
}
}
public Bounds GetOutlineBoundingBox()
{
Bounds B = new Bounds();
int i = 0;
foreach (var a in PLSs)
{
if (a.Layer == BoardLayer.Mill || a.Layer == BoardLayer.Outline)
{
B.AddBox(a.BoundingBox);
i++;
}
}
if (i == 0) return BoundingBox;
return B;
}
private bool InventOutline(ProgressLog log)
{
double largest = 0;
ParsedGerber Largest = null;
PolyLine Outline = null;
foreach (var a in PLSs)
{
var P = a.FindLargestPolygon();
if (P != null)
{
if (P.Item1 > largest)
{
largest = P.Item1;
Largest = a;
Outline = P.Item2;
}
}
}
if (largest < BoundingBox.Area() / 3.0) return false;
bool zerowidth = true;
bool precombine = true;
Console.WriteLine("Note: Using {0} to extract outline file", Path.GetFileName(Largest.Name));
if (Largest.Layer == BoardLayer.Mill)
{
Largest.OutlineShapes.Remove(Outline);
Largest.Shapes.Remove(Outline);
}
var b = AddBoardToSet(Largest.Name,log, zerowidth, precombine, 1.0);
b.Layer = BoardLayer.Outline;
b.Side = BoardSide.Both;
b.DisplayShapes.Clear();
b.OutlineShapes.Clear();
b.Shapes.Clear();
Outline.Close();
b.Shapes.Add(Outline);
b.OutlineShapes.Add(Outline);
//b.DisplayShapes.Add(Outline);
//b.BuildBoundary();
b.FixPolygonWindings();
b.CalcPathBounds();
return true;
}
private bool InventOutlineFromMill(ProgressLog log)
{
log.PushActivity("InventOutlineFromMill");
double largest = 0;
ParsedGerber Largest = null;
PolyLine Outline = null;
foreach (var a in PLSs.Where(x => x.Layer == BoardLayer.Mill))
{
var P = a.FindLargestPolygon();
if (P != null)
{
if (P.Item1 > largest)
{
largest = P.Item1;
Largest = a;
Outline = P.Item2;
}
}
}
if (Largest == null)
{
log.PopActivity();
return false;
}
// if (largest < BoundingBox.Area() / 3.0) return false;
bool zerowidth = true;
bool precombine = true;
Console.WriteLine("Note: Using {0} to extract outline file", Path.GetFileName(Largest.Name));
var b = AddBoardToSet(Largest.Name,log, zerowidth, precombine, 1.0);
b.Layer = BoardLayer.Outline;
b.Side = BoardSide.Both;
//b.DisplayShapes.Clear();
//b.OutlineShapes.Clear();
//b.Shapes.Clear();
// Outline.Close();
// b.Shapes.Add(Outline);
// b.OutlineShapes.Add(Outline);
//b.DisplayShapes.Add(Outline);
//b.BuildBoundary();
// b.FixPolygonWindings();
// b.CalcPathBounds();
log.PopActivity();
return true;
}
private Image<Rgba32> RenderToImage(int w, int h, Matrix3x2 T, Color color, BoardLayer boardLayer, BoardSide boardSide, bool fill, bool forcefill = false)
{
var L = from i in PLSs where i.Layer == boardLayer && (i.Side == boardSide || boardSide == BoardSide.Either) select i;
//if (L.Count() == 0) return null;
Image<Rgba32> B2 = new Image<Rgba32>(w + 3, h + 3);
var G3 = new ImageSharpGraphicsInterface(B2);
G3.Clear(Color.Transparent);
G3.Transform = T;
Pen P = new Pen(color, 1.0f / (float)(scale));
int Shapes = 0;
foreach (var l in L)
{
Shapes += DrawLayerToGraphics(color, fill, G3, P, l, forcefill);
}
if (Shapes == 0) return null;
return B2;
}
private Image<Rgba32> RenderRainbowToBitmap(int w, int h, Matrix3x2 T, int alpha, BoardLayer boardLayer, BoardSide boardSide, bool fill, bool forcefill = false, bool invertedorder = false)
{
var L =( from i in PLSs where (i.Layer == boardLayer || boardLayer == BoardLayer.Any) && (i.Side == boardSide || boardSide == BoardSide.Either) orderby Helpers.LayerOrdering(i.Side, i.Layer) select i).ToList();
if (!invertedorder) L.Reverse();
L = (from i in L where CountLayerShapes(i) > 0 select i).ToList();
//if (L.Count() == 0) return null;
Image<Rgba32> B2 = new Image<Rgba32>(w + 3, h + 3);
//Graphics G2 = Graphics.FromImage(B2);
Image<Rgba32> TB2 = new Image<Rgba32>(w + 3, h + 3);
//Graphics TG2 = Graphics.FromImage(TB2);
//ApplyAASettings(G2);
var G3 = new ImageSharpGraphicsInterface(B2);
G3.Clear(Color.White);
// G3.Transform = T.Clone();
//ApplyAASettings(TG2);
var TG3 = new ImageSharpGraphicsInterface(TB2);
TG3.Clear(Color.White);
TG3.Transform = T;
int Shapes = 0;
int total = L.Count();
int current = 0;
foreach (var l in L)
{
// ImageAttributes logic removed (alpha blending).
// We will manually fade the image if needed when drawing B2 onto TB2 or vice versa.
// The original code drew TB2 onto B2 with alpha.
TG3.Clear(Color.FromRgba(255, 255, 255, 0)); // Transparent
var color = Helpers.RefractionNormalized(current / (float)(total - 1));
Pen P = new Pen(color, 1.0f / (float)(scale));
current++;
bool localfill = forcefill;
if (l.Layer == BoardLayer.Outline) localfill = false;
int NewShapes = DrawLayerToGraphics(color, fill, TG3, P, l, localfill);
if (NewShapes > 0)
{
Shapes += NewShapes;
// Draw TB2 onto B2 with opacity 0.7f?
// Original code used matrixItems for alpha 0.7f.
B2.Mutate(ctx => ctx.DrawImage(TB2, 0.7f)); // using opacituy
}
}
if (Shapes == 0) return null;
return B2;
}
public double GetDrillScaler(string f)
{
if (DrillFileScale.ContainsKey(f)) return DrillFileScale[f];
return 1.0;
}
}
}
bdcd7ef8dThis file is pinned to the selected release. Download the original to open it in your design tool.