Optimize paths in Eagle PCB files for faster CAM processing of GERBER files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.xml; | |
import std.file; | |
import std.stdio; | |
import std.conv; | |
import std.math; | |
real EPSILON = 1e-2; | |
real distance_to_line(real fx, real fy, real tx, real ty, real px, real py) { | |
const vx = tx - fx; | |
const vy = ty - fy; | |
const dist = vy*px - vx*py + tx*fy - ty*fx; | |
const len = sqrt(vx*vx+vy*vy); | |
return fabs(dist/len); | |
} | |
real optimize_vertices(in Tag from, in Tag pto, in Tag point) { | |
assert(from.name == "vertex"); | |
assert(pto.name == "vertex"); | |
assert(point.name == "vertex"); | |
const fx = to!real(from.attr["x"]); | |
const fy = to!real(from.attr["y"]); | |
const tx = to!real(pto.attr["x"]); | |
const ty = to!real(pto.attr["y"]); | |
const px = to!real(point.attr["x"]); | |
const py = to!real(point.attr["y"]); | |
return distance_to_line(fx, fy, tx, ty, px, py); | |
} | |
real optimize_wire(in Tag wire0, in Tag wire1) { | |
assert(wire0.name == "wire"); | |
assert(wire1.name == "wire"); | |
if (wire0.attr["x2"] != wire1.attr["x1"] || wire0.attr["y2"] != wire1.attr["y1"]) { | |
return EPSILON; | |
} | |
const fx = to!real(wire0.attr["x1"]); | |
const fy = to!real(wire0.attr["y1"]); | |
const px = to!real(wire0.attr["x2"]); | |
const py = to!real(wire0.attr["y2"]); | |
const tx = to!real(wire1.attr["x2"]); | |
const ty = to!real(wire1.attr["y2"]); | |
return distance_to_line(fx, fy, tx, ty, px, py); | |
} | |
void deleteChild(Element e, int i) { | |
e.elements[i] = null; | |
e.elements = e.elements[0..i] ~ e.elements[i+1..$]; | |
} | |
void optimize_polygon(Element b) { | |
assert(b.tag.name == "polygon"); | |
int count; | |
writeln(" Polygon with ",b.elements.length," vertices"); | |
int last = 0; | |
for (int t=last+2; t<b.elements.length; ++t) { | |
const dist = optimize_vertices(b.elements[last].tag, b.elements[t].tag, b.elements[t-1].tag); | |
if (dist < EPSILON) { | |
// remove the point | |
b.deleteChild(--t); | |
count++; | |
} | |
else { | |
last = t-1; | |
} | |
} | |
if (count) { | |
writeln(" removed ",count," vertices; ",b.elements.length," left."); | |
assert(b.elements.length >= 3, "Polygon needs at least 3 vertices."); | |
b.items = toItems(b.elements); | |
} | |
} | |
void optimize_library(Element library) { | |
assert(library.tag.name == "library"); | |
assert(library.elements[0].tag.name == "packages"); | |
foreach (a; library.elements[0].elements) | |
{ | |
assert(a.tag.name == "package"); | |
writeln(" Optimizing package ", a.tag.attr["name"]); | |
foreach (ii,b; a.elements) { | |
if (b.tag.name == "polygon") { | |
optimize_polygon(b); | |
} | |
} | |
} | |
} | |
Item[] toItems(Element[] elements) { | |
auto newline = new Text("\n"); | |
Item[] items; | |
foreach (e; elements) { | |
items ~= newline; | |
items ~= e; | |
} | |
return items ~ newline; | |
} | |
void optimize_board(Element board) { | |
assert(board.tag.name == "board"); | |
assert(board.elements[0].tag.name == "plain"); | |
writeln("Optimizing board"); | |
int count; | |
auto plain = board.elements[0]; | |
for(int i=0; i<plain.elements.length; ++i) { | |
auto w = plain.elements[i]; | |
if (w.tag.name == "wire") { | |
while (++i < plain.elements.length) { | |
auto w1 = plain.elements[i]; | |
if (w1.tag.name != "wire") break; | |
const dist = optimize_wire(w.tag, w1.tag); | |
if (dist < EPSILON) { | |
w.tag.attr["x2"] = w1.tag.attr["x2"]; | |
w.tag.attr["y2"] = w1.tag.attr["y2"]; | |
plain.deleteChild(i--); | |
count++; | |
} | |
else { | |
w = w1; | |
} | |
} | |
} | |
} | |
if (count) { | |
writeln(" removed ",count," wires"); | |
// FIXME: this removes newlines | |
plain.items = toItems(plain.elements); | |
} | |
/* | |
if (board.elements[1].tag.name == "libraries") { | |
foreach (e; board.elements[1].elements) { | |
writeln("Optimizing library ", e.tag.attr["name"]); | |
optimize_library(e); | |
} | |
} | |
*/ | |
} | |
void main(string[] args) | |
{ | |
if (args.length < 2) { | |
writeln("Usage: optimize_eagle BRD_OR_LBR [EPSILON]"); | |
return; | |
} | |
if (args.length > 2) EPSILON = to!real(args[2]); | |
writeln("EPSILON=",EPSILON); | |
writeln("Reading ",args[1]); | |
string s = cast(string)std.file.read(args[1]); | |
auto doc = new Document(s); | |
std.file.write(args[1]~"~", doc.toString); | |
assert(doc.elements[0].tag.name == "drawing", "Root must be 'drawing'; not an Eagle file?"); | |
if (doc.elements[0].elements[3].tag.name == "library") { | |
optimize_library(doc.elements[0].elements[3]); | |
} | |
else { | |
optimize_board(doc.elements[0].elements[3]); | |
} | |
writeln("Writing ",args[1]); | |
std.file.write(args[1], doc.toString); | |
writeln("Done."); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment