#usage "Statisic of copper
\n"
"Create and evaluate bitmap(s) to calculate the copper area of layers."
"
"
"Author: support@cadsoft.de"
// THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED
string info_en = ";Be sure that there are no elements outside the " +
"board contour which is drawn in layer 20, Dimension.
" +
"Otherwise it may come to incorrect results. " +
"Calculating the board area:
" +
"Holes and other kinds of openings will not be taken into consideration.
" +
"Calculating copper areas:
" +
"Blind and buried vias will not be marked in the reference image as it is " +
"the case with drills and holes.
This leads to slight inaccuracies in the result.
";
string info_de = ";Bei Benutzung dieses ULPs darf kein Element ausserhalb "+
"des Platinenumrisses im
Layer 20 Dimension " +
"gezeichnet sein. Andernfalls kommt es zu fehlerhaften Ergebnissen." +
"Berechnung der Platinenfläche:
" +
"Bohrungen und Durchbrüche innerhalb der Platine werden nicht berücksichtigt.
" +
"Berechnung der Kupferfläche:
"+
"Bei Verwendung von Blind- und Burried-Vias werden im Referenzbild die Bohrungen
" +
"nicht wie bei Pads und Holes markiert, wodurch es zu minimalen Ungenauigkeiten
" +
"im Ergebnis kommen kann.
";
string infotext = info_en;
if (language() == "de") infotext = info_de;
string MSG_en_de[] = {
"en\v"
"de\v"
,
"Statistics\v"
"Statistik\v"
,
"Delete temporary files (bitmaps)?\v"
"Temporäre Dateien (Bitmaps) löschen?\v"
,
"Layer\tplane (Cu)\t% Cu\v"
"Layer\tFläche (Cu)\t% Cu\v"
,
"Statistics Copper Area:\v"
"Statistik Kupferflächen:\v"
,
"+Ok\v"
"+Ok\v"
,
"-Cancel\v"
"-Abbrechen\v"
,
"+Yes\v"
"+Ja\v"
,
"-No\v"
"-Nein\v"
,
"&Save\v"
"&Speichern\v"
,
"Start this ULP from a Board!\v"
"Starten sie das ULP in einem Board!\v"
,
"Calculate board area\v"
"Berechnen der Platinen-Fläche(n)\n"
,
"Only board area\v"
"Nur Platinenfläche\v"
,
"Also copper layers\v"
"Auch Kupferlayer\v"
,
"Save statistic\v"
"Statistik speichern\v"
"\v"
"\v"
};
int Language = strstr(MSG_en_de[0], language()) / 2;
string tr(string s) {
string t = lookup(MSG_en_de, s, Language, '\v');
return t ? t : s;
}
string UlpVersion = "1.0.1"; // 2010-05-04 alf@cadsoft.de - get current background color
string brdfile;
int cntlayerfiles;
string fileBitmap;
int resolution = 254; // resolution pixel per inch
real pmm2 = (25.4/resolution) * (25.4/resolution);
int nBytes = 0; // count bytes of file (fileName)
int ColorBits = 0; // used bits for color
int AdrStart, AdrEnd = 0; // Start & End of BITMAP-Data
int length = 0; // length of bmp-Data
int Byte4Group = 0; // bmp-Data organized as 4-byte groups
char c[];
real brd_mm2;
int p2[]; // pixel quadrat pro layer
int BackgroundColor;
int L_dimension[]; // Left dimension of pixel line
int R_dimension[]; // Right dimension of pixel line
int Y = 0; // count pixels y
int X = 0; // count pixels x
char copper = 'C'; // reference copper flag
char nonecopper = 'N'; // reference none-copper flag (holes/drills)
char nopcb = copper -1; // reference no printed-circuit-board (outside)
char Bit[] = { nonecopper, copper };
string planeBit0[]; // byte (character) array for pcb plane
int mBit[];
string info;
string Header;
int nsl = 0;
numeric string plane_statistic[];
int fill_drilllayer = 199;
string fill_drill_name = "FillDrill";
string cmd, h;
string ldisplay; // reset display layers
void wait(int w) {
int t = time();
do {
} while (t+w > time());
return;
}
// ** delete temporary circles and layer **
string remove_layer(int x1, int y1, int x2, int y2) {
string s;
sprintf(s, "DISPLAY NONE %d;\nGROUP (%.4f %.4f) (%.4f %.4f) (%.4f %.4f) (%.4f %.4f) (%.4f %.4f);\nDELETE (>%.4f %.4f);\nLAYER -%d;\nGRID LAST;\n",
fill_drilllayer,
u2mm(x1), u2mm(y1),
u2mm(x2), u2mm(y1),
u2mm(x2), u2mm(y2),
u2mm(x1), u2mm(y2),
u2mm(x1), u2mm(y1),
u2mm(x1)+1, u2mm(y1)+1, fill_drilllayer );
return s;
}
string center(int x, int y, int drill) {
string s;
sprintf(s, "circle 0 (%.4f %.4f) (%.4f %.4f) ;\n",
u2mm(x), u2mm(y), u2mm(x) + u2mm(drill)/2, u2mm(y) );
return s;
}
// ** calculate left and right side **
void PCB_planeColom(int Line) {
int pixel2, z;
BackgroundColor = mBit[0];
int Copper = 0;
int Dimension = 0;
int copper_cnt;
int zL, zR;
for(z = 0; z <= X; z++) {
planeBit0[Line][z] = Bit[mBit[z]];
}
for( zL = 0; zL < X; zL++) {
if(mBit[zL] != BackgroundColor) {
L_dimension[Line] = zL;
break;
}
}
for( zR = X; zR >= 0; zR--) {
if(mBit[zR] != BackgroundColor) {
R_dimension[Line] = zR;
break;
}
}
return;
}
// ** define outside dimension **
// ** set flag in pcb array **
int PCB_planeRow(void) {
status("Mark right and left outline");
int y, x;
// * mark from left and from right do dimension *
for ( y = 0; y <= Y; y++) {
if (L_dimension[y]) {
for( x = 0; x < X; x++) {
if(planeBit0[y][x] != copper) break;
else planeBit0[y][x]--; // mark pcb outside from left side
}
for( x = X; x > 0; x--) {
if(planeBit0[y][x] != copper) break;
else planeBit0[y][x]--; // mark pcb outside from right side
}
}
}
status("Mark top and bottom outline");
// * mark from top and from bottom to dimension *
for ( x = 0; x <= X; x++) {
for( y = 0; y < Y; y++) {
if(planeBit0[y][x] != copper) break;
else planeBit0[y][x]--; // mark pcb outside from bottom side
}
for( y = Y; y > 0; y--) {
if(planeBit0[y][x] != copper) break;
else planeBit0[y][x]--; // mark pcb outside from top side
}
}
status("calculate board area");
int pp2; // count pcb area pixel
for (y = 0; y < Y; y++) {
for (x = L_dimension[y]; x < R_dimension[y]; x++ ) {
if (planeBit0[y][x] != nopcb) pp2++; // komplete pcb plane
}
}
return pp2;
}
int calculateLine(int Line, int layer) {
string m;
sprintf(m, "Layer %d: Line %d of %d", layer, Line, Y);
status(m);
int pixel2, x;
BackgroundColor = mBit[1];
int Copper = 0;
int Dimension = 0;
if (!layer) {
PCB_planeColom(Line);
}
else {
for( x = L_dimension[Line]; x <= R_dimension[Line]; x++) {
if(mBit[x] != BackgroundColor && planeBit0[Line][x] == copper) pixel2++;
}
}
return pixel2;
}
void get_pixel_info( int layer) {
int xByte = 4 * Byte4Group;
int bmpBits;
for(int yRead = 0; yRead <= Y; yRead++) {
for(int xRead = 0; xRead < xByte; xRead ++) {
bmpBits = c[AdrStart + yRead * xByte + xRead];
switch (ColorBits) {
case 1 : for(int bitcnt = 7; bitcnt > -1; bitcnt--) {
mBit[(xRead * 8) + (7 - bitcnt)] = bmpBits;
mBit[(xRead * 8) + (7 - bitcnt)] >>= bitcnt;
mBit[(xRead * 8) + (7 - bitcnt)] &= 0X1;
}
break;
case 4 : dlgMessageBox("16 colors, not accepted!", "OK");
exit(-4);
mBit[xRead * 2 ] = bmpBits;
mBit[xRead * 2 + 1] = bmpBits;
mBit[xRead * 2 ] >>= 4;
mBit[xRead * 2 + 1] &= 0x0f;
break;
case 8 : dlgMessageBox("256 colors, not accepted!", "OK");
exit(-8);
mBit[xRead] = bmpBits;
break;
}
}
p2[layer] += calculateLine(yRead, layer);
}
// ** calculate array outside outlines **
if (!layer) {
p2[0] = PCB_planeRow(); // calculate area outside dimension line
}
return;
}
// generate bitmaps and start this ulp again
void genereate_bitmaps(UL_BOARD B) {
status("Generate return script, please wait!");
string cmd, l, lfill, h;
string ratsnest = "RATSNEST;\n";
B.signals(S) {
if (ratsnest) {
S.polygons(P) {
if (ratsnest) {
P.fillings(F) {
ratsnest ="";
break;
}
}
else break;
}
}
else break;
}
cmd += ratsnest;
int cntlayer = 2;
sprintf(h, "GRID mm;\nLAYER %d %s;\nCHANGE Layer %d;\n", fill_drilllayer, fill_drill_name, fill_drilllayer);
cmd += h;
// ** temporary fill drills with circle **
B.holes(L) {
cmd += center(L.x, L.y, L.drill);
}
B.elements(E) {
E.package.holes(H) {
cmd += center(H.x, H.y, H.drill);
}
E.package.contacts(C) {
if (C.pad) {
cmd += center(C.pad.x, C.pad.y, C.pad.drill);
}
}
}
string fvs;
B.signals(S) {
if (cntlayer > 2) break;
S.vias(V) {
fvs += center(V.x, V.y, V.drill);
if (V.start > 1 || V.end < 16) cntlayer ++;
if (cntlayer > 2) break;
}
}
if (cntlayer < 3) {
cmd += fvs;
}
sprintf(h, "SET FILL_LAYER 17 1;\nSET FILL_LAYER 18 1;\nSET FILL_LAYER 20 1;\nDISPLAY NONE 20 %d;\nEXPORT IMAGE '%s_0.bmp' MONOCHROME %d;\n",
fill_drilllayer, brdfile, resolution);
cmd += h;
// sprintf(h, "%s_0.bmp", brdfile);
// output( h, "wtD"); // temporary file, delete if eagle closed
int allcupperlayer = dlgDialog(tr("Statistic")) {
dlgLabel(tr("Calculate board area"));
dlgPushButton( tr("Also copper layers")) dlgAccept();
dlgPushButton( tr("Only board area")) dlgReject();
};
if (allcupperlayer) {
B.layers(L) {
if (L.number >= 1 && L.number <= 16) {
if (L.used) {
sprintf(h, "SET FILL_LAYER %d 1;\nDISPLAY NONE %d 17 18;\nEXPORT IMAGE '%s_%d.bmp' MONOCHROME %d;\n",
L.number, L.number, brdfile, L.number, resolution);
l += h;
sprintf(h, "SET FILL_LAYER %d %d;\n", L.number, L.fill);
lfill += h;
// sprintf(h, "%s_%d.bmp", brdfile, L.number);
// output( h, "wtD"); // temporary file, delete if eagle close
}
}
if (L.number == 17 || L.number == 18) {
sprintf(h, "SET COLOR_LAYER %d %d;\nSET FILL_LAYER %d %d;\n", L.number, L.color, L.number, L.fill);
lfill += h;
}
}
}
if (l) {
cmd += "SET COLOR_LAYER 17 0;\nSET COLOR_LAYER 18 0;\n" + l; // set color Via layer to 0 (black)
}
ldisplay = "DISPLAY NONE ";
B.layers(L) {
if (L.visible) {
sprintf( h, " %d", L.number);
ldisplay += h;
}
}
cmd += lfill; // restore layer filling
sprintf(h, "RUN '%s' 1;\n", argv[0]);
cmd += h;
cmd += remove_layer( B.area.x1, B.area.y1, B.area.x2, B.area.y2);
cmd += ldisplay + ";\n"; // restore layer display
status("EXPORT bitmaps, please wait!");
exit(cmd);
}
// ** read bitmap file **
void pixel_map(string f) {
nBytes = fileread(c, f); // read file in array
int point = strstr( f, ".bmp");
int underline = strstr( f, "_", point-4);
int layer = strtol(strsub(f, underline+1, point-underline));
if (!layer) {
// up to 31 bytes - not all used
if(c[0] != 'B') {
dlgMessageBox(f + ":\nis not a bmp file.\n\nist keine bmp-Datei.", "OK");
exit(0);
}
if(c[1] != 'M') {
dlgMessageBox(f + ":\nis not a bmp file.\n\nist keine bmp-Datei.", "OK");
exit(0);
}
if(c[21] > 0) {
dlgMessageBox(f + ":\nToo many pixels in x direction\n"
+ "\nAnzahl der Pixel in X zu gross\n", "OK");
exit (0);
}
if(c[25] > 0) {
dlgMessageBox(f + ":\nToo many pixels y direction\n"
+ "\nAnzahl der Pixel in Y zu gross\n", "OK");
exit (0);
}
ColorBits = c[28]; // counter of ColorBits
if(ColorBits > 1) {
dlgMessageBox("falsche Farbtiefe", "OK");
exit(0);
}
AdrEnd = c[2] + c[3] * 256 + c[4] * 256 * 256 + c[5] * 256 * 256 * 256;
AdrStart = c[10]+ c[11] * 256 + c[12] * 256 * 256 + c[13] * 256 * 256 * 256;
X = (c[18] + c[19] * 256 + c[20] * 65536 + c[21] * 256 * 256 * 256) - 1;
Y = (c[22] + c[23] * 256 + c[24] * 65536 + c[25] * 256 * 256 * 256) - 1;
length = AdrEnd - AdrStart; // bitmap legth
Byte4Group = length / Y / 4;
}
int bitcolor;
bitcolor = c[AdrStart] &= 0X80; // 2010-05-04 alf get background color
if (!bitcolor) { // 2010-05-04 swap color
Bit[0] = copper;
Bit[1] = nonecopper;
}
get_pixel_info(layer);
if (!layer) {
brd_mm2 = p2[0] * pmm2;
sprintf(plane_statistic[nsl], "0_PCB\t%.2f mm²\t --\t%d", brd_mm2, p2[layer] );
nsl++;
}
else {
real pcbplane_mm2 = p2[0] * pmm2;
real copper_mm2 = p2[layer] * pmm2;
real percent = copper_mm2 / (pcbplane_mm2 / 100);
sprintf(plane_statistic[nsl], "%d\t%.2f mm²\t%.1f", layer, copper_mm2, percent);
nsl++;
}
return;
}
string save(string file) {
int n;
sort( cntlayerfiles-1, plane_statistic);
string sfile = dlgFileSave(tr("Save statistic"), file, "*.cst");
if (sfile) {
output(sfile, "wt") {
printf("%s\n%s - Version %s\n%s\n\n%s\n\n%s\n",
EAGLE_SIGNATURE,
filename(argv[0]),
UlpVersion,
t2string(time()),
tr("Statistic Copper Plane:"),
Header );
do {
printf("%s\n", plane_statistic[n]);
n++;
} while(plane_statistic[n]);
}
}
return sfile;
}
// ***************** main ****************
if (board) {
board(B) {
string remove_tempfile;
string file = filesetext(B.name, "");
brdfile = file + "~~~";
if (argc == 1) {
if (dlgMessageBox( infotext, tr("-Ok"), tr("-Cancel")) != 0) exit(0);
genereate_bitmaps(B);
}
fileBitmap = brdfile;
numeric string BMPlayerfiles[];
cntlayerfiles = fileglob(BMPlayerfiles, fileBitmap + "*.bmp");
Header = tr("Layer\tplane (Cu)\t% Cu");
sort( cntlayerfiles, BMPlayerfiles);
for (int n = 0; n < cntlayerfiles; n++) {
status("Load: " + BMPlayerfiles[n]);
pixel_map(BMPlayerfiles[n]);
sprintf(h, "REMOVE '%s';\n", BMPlayerfiles[n]);
remove_tempfile += h;
}
status(" ");
wait(1);
file += ".cst";
int select;
dlgDialog( tr("Statistics")) {
dlgListView(Header, plane_statistic, select);
dlgLabel(file);
dlgHBoxLayout {
dlgPushButton(tr("+OK")) dlgAccept();
dlgPushButton(tr("&Save")) if (save(file)) dlgAccept();
dlgStretch(1);
}
};
cmd += cmd += "GRID LAST;";
if (dlgMessageBox(tr("Delete temporary files (bitmaps)?"), tr("+Yes"), tr("-No") ) == 0) {
cmd += remove_tempfile;
}
exit(cmd);
}
}
else {
dlgMessageBox(tr("Start this ULP from a Board!"), "Ok");
exit(0);
}