// vim:syntax=c
// $Id: silk_gen.ulp,v 1.4 2005/05/23 08:01:27 mcuddy Exp $
#usage "Generate a user defined silk screen\n"
"
"
"Some board manufacturers want to have at least a width of 8mil "
"for silk screen lines in order to guarantee legible results. "
"EAGLE libraries use 5 mil width for silk screen as default. "
"
"
"This ULP changes all silk screen elements to a minimum "
"width supplied by user. All elements of layers 20, 21, 22, 25, 26 "
"are written into new layers 121(_tsilk) and 122 (_bsilk). "
"Texts are changes as well. The new ratio is set to minimum value "
"that is requred to achieve the silk wire width. If the original "
"text ratio is greater, it is not changed. "
"
"
"Two new layers will be defined and the new silk screen will be "
"generated. For generating GERBER data be aware that you have to "
"activate layers 121 or 122 instead of the original layers."
"
"
"Original authors: Richard Hammerl 26-05-1998, "
"Changed for EAGLE 4.0 26-02-2002, support@cadsoft.de "
"Fixed for EAGLE 4.11, OLIMEX special 04-11-2003, Y.Onodera, "
"Further fixed and modified by Antti Arola 11.03.2005 "
"user interface, general code cleanup, ability to delete new "
"layers before re-creating them, and board-level text support "
"added by M.Cuddy 20.03.2005"
""
// THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED
// Define your own silk screen width here, in mils
real Silkwidth = 8.0; // Default value suggested by dialog
// layer name constants
int dimension = LAYER_DIMENSION,
tplace = LAYER_TPLACE,
bplace = LAYER_BPLACE,
tnames = LAYER_TNAMES,
bnames = LAYER_BNAMES,
tvalues = LAYER_TVALUES,
bvalues = LAYER_BVALUES,
_tsilk = LAYER_TPLACE + LAYER_USER,
_bsilk = LAYER_BPLACE + LAYER_USER;
// name of new, generated layers
string _tsilkName = "_tsilk";
string _bsilkName = "_bsilk";
// boolean flags for which layers to operate on
int do_tplace = 1,
do_bplace = 1,
do_tnames = 1,
do_bnames = 1,
do_tvalues = 1,
do_bvalues = 1,
do_dimension = 1,
do_confirm = 1;
int erase_silk = 1,
show_results = 1,
warn_ratio = 1;
int eraseOnly = 0;
// built-up command string
string cmd = "";
// 'curLayer', 'curRatio', and 'curSize' are cached globals so we don't
// generate more script commands than needed.
int curLayer = -1; // start as an invalid number, so first call will
int curRatio = -1; // always set
real curSize = -1.0;
void setLayer(int dstlay)
{
string h;
if (dstlay == curLayer) return;
curLayer = dstlay;
sprintf(h,"LAYER %d;\n",curLayer);
cmd += h;
}
// return true if currently selected layer is top layer, false otherwise
int destLayerIsTop()
{
if (curLayer == _bsilk) return 0;
return 1;
}
void setRatio(int newRatio)
{
string h;
if (curRatio == newRatio) return;
curRatio = newRatio;
sprintf(h, "CHANGE RATIO %d;\n", curRatio);
cmd += h;
}
void setSize(real newSize)
{
string h;
if (curSize == newSize) return;
curSize = newSize;
sprintf(h, "CHANGE SIZE %5.3f;\n", curSize);
cmd += h;
}
void del_layer(int layer, real x1, real y1, real x2, real y2)
{
string h;
sprintf(h, "DISPLAY NONE %d;\n", layer); cmd += h;
sprintf(h, "GROUP (>%f %f) (%f %f) (%f %f) (%f %f) (>%f %f);\n",
x1, y1, x1, y2, x2, y2, x2, y1, x1, y1 ); cmd += h;
sprintf(h, "DELETE (>%f %f);\n", x1, x2 ); cmd += h;
sprintf(h, "LAYER ?? -%d;\n", layer); cmd += h;
sprintf(h, "WINDOW;\n" ); cmd += h;
}
// generate a string showing all active layers ( but ignore layers mentioned
// in 'ignore' array.
//string findActiveLayers(UL_BOARD B, int ignore[], int nIgnore)
int ignore[];
int nIgnore;
string findActiveLayers(UL_BOARD B)
{
int i;
int skip;
string h, cur_active_layers;
cur_active_layers = "";
B.layers(L) {
skip = 0;
for ( i = 0; i < nIgnore; i++) {
if ( L.number == ignore[i] ) { skip = 1; break; }
}
if (skip || !L.visible) continue;
sprintf(h," %d", L.number); cur_active_layers += h;
}
return cur_active_layers;
}
void erase_silk_layers(UL_BOARD B)
{
string h;
real x1,x2,y1,y2;
// size of board area..
x1 = u2mil(B.area.x1);
x2 = u2mil(B.area.x2);
y1 = u2mil(B.area.y1);
y2 = u2mil(B.area.y2);
// delete silk screen layers
string cur_active_layers = "";
int got_a_silk = 0;
B.layers(L) {
if (L.number == _tsilk || L.number == _bsilk) {
got_a_silk = 1;
break;
}
}
// XXX - can't pass arrays to funcs?
ignore[0] = _tsilk; ignore[1] = _bsilk; nIgnore = 2;
cur_active_layers = findActiveLayers(B);
// find currently active layers, exclude _tsilk and _bsilk from the
// mix.
if (got_a_silk) {
// hide all layers
sprintf(h, "DISPLAY NONE;\n"); cmd += h;
// remove the _tsilk and _bsilk layers
B.layers(L) {
if (L.number == _tsilk || L.number == _bsilk ) {
del_layer(L.number,x1,y1,x2,y2);
}
}
// restore previously visible layers
sprintf(h, "DISPLAY %s;\n", cur_active_layers); cmd += h;
}
}
void do_show_results(UL_BOARD B)
{
string h;
// turn on tsilk, bsilk, tstop and bstop.
sprintf(h, "DISPLAY NONE %d %d %d %d;\n",
_tsilk, _bsilk, LAYER_TSTOP, LAYER_BSTOP );
cmd += h;
}
void header(UL_BOARD B)
{
string h;
if (erase_silk) {
erase_silk_layers(B);
}
// create the new layers.
sprintf(h, "LAYER %d %s;\n", _tsilk, _tsilkName);cmd += h;
sprintf(h, "LAYER %d %s;\n", _bsilk, _bsilkName);cmd += h;
sprintf(h, "SET COLOR_LAYER %d YELLOW;\n", _tsilk);cmd += h; // and
sprintf(h, "SET COLOR_LAYER %d YELLOW;\n", _bsilk);cmd += h; // colors
sprintf(h, "SET WIRE_BEND 2;\n\n");cmd += h;
}
real calcwidth(int size, int ratio) // returns mils
{
return (u2mil(size) * (ratio/100.0));
}
int getreqratio(int size) // returns integer
{
return ceil((Silkwidth / u2mil(size)) * 100);
}
/* given a source layer, return the correct destination layer */
int getDstLayer(int source, int dimrun)
{
int dstlay;
if (source == bvalues ||
source == bplace ||
source == bnames ||
(source == dimension && dimrun == 2)) {
dstlay = _bsilk;
} else {
dstlay = _tsilk;
}
return dstlay;
}
void do_text(string ename, UL_TEXT T)
{
real OrigWidth;
string orient;
int newRatio;
string value;
string h;
if (T.value == "") return;
OrigWidth = calcwidth(T.size, T.ratio);
if (OrigWidth < Silkwidth)
newRatio = getreqratio(T.size);
else
newRatio = T.ratio;
// Check if required ratio is >31 (maximum), give warning
if (newRatio > 31) {
if (warn_ratio) {
sprintf(h, "Element %s non-smashed text \"%s\":\nsize is %5.3f mils, required ratio %d\nUsing maximum ratio (31).\n",
ename, T.value, u2mil(T.size), newRatio);
dlgMessageBox(h);
}
newRatio = 31;
}
setRatio(newRatio);
setSize(u2mil(T.size));
// double-up single quotes in text.
value = T.value;
if (strchr(value,'\'') != -1) {
string ary[];
int n = strsplit(ary,value,'\'');
int i;
value = "";
for (i = 0; i < n; i++) {
if (i != 0) value += "''";
value += ary[i];
}
}
if (destLayerIsTop()) {
orient = "R";
} else {
orient = "MR";
}
sprintf(h, "TEXT '%s' %s%1.0f (%5.3f %5.3f);\n",
value, orient, T.angle, u2mil(T.x), u2mil(T.y));
cmd += h;
}
// make sure curWidth (in mils) to no less than Silkwidth
real clampSilkWidth(real curWidth)
{
if (curWidth < Silkwidth)
return Silkwidth;
return curWidth;
}
void searchWires(UL_WIRE W, int source)
{
real sw;
string h;
// fixed arcs for v4.11
if (W.arc) {
if (W.arc.layer != source) return;
sw = clampSilkWidth(u2mil(W.arc.width));
sprintf(h,
"ARC %5.3f ccw (%5.3f %5.3f) (%5.3f %5.3f) (%5.3f %5.3f);\n",
sw,
u2mil(W.arc.x1), u2mil(W.arc.y1),
u2mil(2*(W.arc.xc)-W.arc.x1),u2mil(2*(W.arc.yc)-W.arc.y1),
u2mil(W.arc.x2), u2mil(W.arc.y2));
cmd += h;
} else {
if (W.layer != source) return;
sw = clampSilkWidth(u2mil(W.width));
sprintf(h, "WIRE %5.3f (%5.3f %5.3f) (%5.3f %5.3f);\n",
Silkwidth, u2mil(W.x1), u2mil(W.y1), u2mil(W.x2), u2mil(W.y2));
cmd += h;
}
}
void searchCircles(UL_CIRCLE C, int source)
{
real sw;
string h;
if (C.layer != source) return;
sw = clampSilkWidth(u2mil(C.width));
sprintf(h, "CIRCLE %5.3f (%5.3f %5.3f) (%5.3f %5.3f);\n",
sw, u2mil(C.x), u2mil(C.y), u2mil(C.x + C.radius), u2mil(C.y));
cmd += h;
}
void searchRectangles(UL_RECTANGLE R, int source)
{
string h;
if (R.layer != source) return;
sprintf(h, "RECT R%1.0f (%5.3f %5.3f) (%5.3f %5.3f);\n",
R.angle, u2mil(R.x1), u2mil(R.y1), u2mil(R.x2), u2mil(R.y2));
cmd += h;
}
void searchPolygons(UL_POLYGON P, int source)
{
string h;
real sw;
int first;
if (P.layer != source) return;
sw = clampSilkWidth(P.width);
first = 1;
P.wires(WP) {
if (first) {
sprintf(h, "POLYGON %5.3f (%5.3f %5.3f)",
sw, u2mil(WP.x1), u2mil(WP.y1));
cmd += h;
} else {
sprintf(h, "\n %+f (%5.3f %5.3f)",
WP.curve, u2mil(WP.x2), u2mil(WP.y2));
cmd += h;
}
first = 0;
}
sprintf(h, ";\n");cmd += h;
}
void searchElements(UL_ELEMENT E, int source)
{
string h;
E.package.wires(W) {
searchWires(W,source);
}
E.package.circles(C) {
searchCircles(C,source);
}
// fixed angle
E.package.rectangles(R) {
searchRectangles(R,source);
}
E.package.polygons(P) {
searchPolygons(P,source);
}
// non-smashed texts
E.package.texts(T) {
if (T.layer == source)
do_text(E.name, T);
}
// smashed texts
E.texts(T) {
if (T.layer == source)
do_text(E.name, T);
}
}
void searchTexts(UL_TEXT T, int source)
{
if (T.layer == source)
do_text("", T);
}
string settingsFile()
{
string fn;
string boardName;
project.board(B) { boardName = B.name; }
fn = filesetext(boardName,".silk");
return fn;
}
void saveSettings()
{
string fn, data;
string lines[], words[];
int nlines, nwords;
fn = settingsFile();
output(fn,"w") {
printf("Silkwidth=%f\n", Silkwidth);
printf("dimension=%d\n", dimension);
printf("tplace=%d\n", tplace);
printf("bplace=%d\n", bplace);
printf("tnames=%d\n", tnames);
printf("bnames=%d\n", bnames);
printf("tvalues=%d\n", tvalues);
printf("bvalues=%d\n", bvalues);
printf("_tsilk=%d\n", _tsilk);
printf("_bsilk=%d\n", _bsilk);
printf("_tsilkName=%s\n", _tsilkName);
printf("_bsilkName=%s\n", _bsilkName);
printf("do_tplace=%d\n", do_tplace);
printf("do_bplace=%d\n", do_bplace);
printf("do_tnames=%d\n", do_tnames);
printf("do_bnames=%d\n", do_bnames);
printf("do_tvalues=%d\n", do_tvalues);
printf("do_bvalues=%d\n", do_bvalues);
printf("do_dimension=%d\n", do_dimension);
printf("do_confirm=%d\n", do_confirm);
printf("show_results=%d\n", show_results);
printf("erase_silk=%d\n", erase_silk);
printf("warn_ratio=%d\n", warn_ratio);
}
}
void loadSettings()
{
string fn, data;
string lines[], words[];
int nlines, nwords, i;
fn = settingsFile();
fileerror(); // clear any previous error.
fileread(data,fn);
// it's okay if the settings file can't be read.
if (fileerror()) return;
nlines = strsplit(lines, data,'\n');
for (i = 0; i < nlines; i++) {
nwords = strsplit(words,lines[i],'=');
if (nwords != 2) continue;
if (words[0] == "Silkwidth") {
Silkwidth = strtod(words[1]);
} else if (words[0] == "dimension") {
dimension = strtol(words[1]);
} else if (words[0] == "tplace") {
tplace = strtol(words[1]);
} else if (words[0] == "bplace") {
bplace = strtol(words[1]);
} else if (words[0] == "tnames") {
tnames = strtol(words[1]);
} else if (words[0] == "bnames") {
bnames = strtol(words[1]);
} else if (words[0] == "tvalues") {
tvalues = strtol(words[1]);
} else if (words[0] == "bvalues") {
bvalues = strtol(words[1]);
} else if (words[0] == "_tsilk") {
_tsilk = strtol(words[1]);
} else if (words[0] == "_bsilk") {
_bsilk = strtol(words[1]);
} else if (words[0] == "_tsilkName") {
_tsilkName = words[1];
} else if (words[0] == "_bsilkName") {
_bsilkName = words[1];
} else if (words[0] == "do_tplace") {
do_tplace = strtol(words[1]);
} else if (words[0] == "do_bplace") {
do_bplace = strtol(words[1]);
} else if (words[0] == "do_tnames") {
do_tnames = strtol(words[1]);
} else if (words[0] == "do_bnames") {
do_bnames = strtol(words[1]);
} else if (words[0] == "do_tvalues") {
do_tvalues = strtol(words[1]);
} else if (words[0] == "do_bvalues") {
do_bvalues = strtol(words[1]);
} else if (words[0] == "do_dimension") {
do_dimension = strtol(words[1]);
} else if (words[0] == "do_confirm") {
do_confirm = strtol(words[1]);
} else if (words[0] == "show_results") {
show_results = strtol(words[1]);
} else if (words[0] == "erase_silk") {
erase_silk = strtol(words[1]);
} else if (words[0] == "warn_ratio") {
warn_ratio = strtol(words[1]);
}
}
}
if (! board) {
dlgMessageBox("\n Start this ULP in a Board \n");
exit (0);
}
board(B) {
string h;
int rc;
loadSettings();
rc = dlgDialog ("Enter silkscreen width") {
dlgVBoxLayout {
dlgHBoxLayout {
dlgLabel ("Minimum silkscreen wire width in mils:");
dlgRealEdit (Silkwidth, 0.0, 99.9);
dlgStretch(1);
}
dlgLabel ("Source layers:");
dlgHBoxLayout {
dlgLabel ("Dimension:");
dlgIntEdit (dimension, 0, 999);
dlgCheckBox ("", do_dimension);
dlgStretch(1);
}
dlgHBoxLayout {
dlgLabel ("Place top:");
dlgIntEdit (tplace, 0, 999);
dlgCheckBox ("", do_tplace);
dlgLabel (" bottom:");
dlgIntEdit (bplace, 0, 999);
dlgCheckBox ("", do_bplace);
}
dlgHBoxLayout {
dlgLabel ("Names top:");
dlgIntEdit (tnames, 0, 999);
dlgCheckBox ("", do_tnames);
dlgLabel (" bottom:");
dlgIntEdit (bnames, 0, 999);
dlgCheckBox ("", do_bnames);
}
dlgHBoxLayout {
dlgLabel ("Values top:");
dlgIntEdit (tvalues, 0, 999);
dlgCheckBox ("", do_tvalues);
dlgLabel (" bottom:");
dlgIntEdit (bvalues, 0, 999);
dlgCheckBox ("", do_bvalues);
}
dlgLabel ("Generated Silk screen layers:");
dlgHBoxLayout {
dlgLabel ("top layer number:");
dlgIntEdit (_tsilk, 100, 999);
dlgLabel (" name:");
dlgStringEdit (_tsilkName);
}
dlgHBoxLayout {
dlgLabel ("bottom layer number:");
dlgIntEdit (_bsilk, 100, 999);
dlgLabel (" name:");
dlgStringEdit (_bsilkName);
}
dlgHBoxLayout {
dlgCheckBox ("Erase existing tSilk/bSilk layer:", erase_silk);
dlgCheckBox ("Warn about invalid text ratios", warn_ratio);
dlgStretch(1);
}
dlgHBoxLayout {
dlgCheckBox ("Show Results", show_results);
dlgCheckBox ("Confirm Script before execution", do_confirm);
dlgStretch(1);
}
dlgHBoxLayout {
dlgPushButton ("+Make Silkscreen layers") dlgAccept (1);
dlgStretch(1);
dlgPushButton ("Erase old silkscreen") dlgAccept(2);
dlgStretch(1);
dlgPushButton("-Cancel") dlgAccept(3);
}
}
};
if (rc == 3) { // abort
exit(0);
} if (rc == 2) {
eraseOnly = 1;
} else {
int err = 0;
eraseOnly = 0;
if (do_tplace+do_bplace+do_tnames+do_bnames+do_tvalues+do_bvalues == 0){
dlgMessageBox("No source layers selected.");
exit(0);
}
// make sure that new layers we're about to create don't exist already
B.layers(L) {
if (L.name == _tsilkName && L.number != _tsilk) {
sprintf(h,"Duplicate top-silk-layer name '%s' with different layer number (%d != %d)\nTry erasing old silk-screen layers first.", L.name, L.number, _tsilk );
dlgMessageBox(h);
err = 1;
}
if (L.name == _bsilkName && L.number != _bsilk) {
sprintf(h,"Duplicate bottom-silk-layer name '%s' with different layer number (%d != %d)\nTry erasing old silk-screen layers first.", L.name, L.number, _tsilk );
dlgMessageBox(h);
err = 1;
}
}
if (err) exit(1);
}
sprintf(h, "\nGRID mil;\n\n");cmd += h;
if (eraseOnly) {
erase_silk_layers(B);
} else {
int src[];
int dst[];
int nLayers;
int i;
nLayers = 0;
if (do_tplace) {
src[nLayers] = tplace;
dst[nLayers] = getDstLayer(tplace,0);
nLayers++;
}
if (do_bplace) {
src[nLayers] = bplace;
dst[nLayers] = getDstLayer(bplace,0);
nLayers++;
}
if (do_tnames) {
src[nLayers] = tnames;
dst[nLayers] = getDstLayer(tnames,0);
nLayers++;
}
if (do_bnames) {
src[nLayers] = bnames;
dst[nLayers] = getDstLayer(bnames,0);
nLayers++;
}
if (do_tvalues) {
src[nLayers] = tvalues;
dst[nLayers] = getDstLayer(tvalues,0);
nLayers++;
}
if (do_bvalues) {
src[nLayers] = bvalues;
dst[nLayers] = getDstLayer(bplace,0);
nLayers++;
}
cmd += "SET UNDO_LOG OFF;\n"; // advisable for speed reasons
header(B);
if (do_dimension) {
// process dimension layers onto top / bottom
// 1 = top, 2 = bottom
for ( i = 1; i <= 2; i++) {
setLayer(getDstLayer(dimension,i));
B.texts(T) {
searchTexts(T,dimension);
}
B.wires(W) {
searchWires(W,dimension);
}
B.elements(E) {
searchElements(E,dimension);
}
}
}
// process source layers...
for (i = 0; i < nLayers; i++) {
setLayer(dst[i]);
B.texts(T) {
searchTexts(T,src[i]);
}
B.wires(W) {
searchWires(W,src[i]);
}
B.elements(E) {
searchElements(E,src[i]);
}
}
if (show_results) do_show_results(B);
cmd += "SET UNDO_LOG ON;\n";
}
}
if (do_confirm) {
int Result = dlgDialog("Script to generate the new silk screen") {
dlgVBoxLayout {
dlgTextEdit(cmd);
dlgHBoxLayout {
dlgSpacing(300);
dlgStretch(1);
dlgPushButton("+Execute") dlgAccept();
dlgPushButton("-Cancel") dlgReject();
}
}
};
if (Result == 0) exit(0);
}
if (!eraseOnly) saveSettings();
exit(cmd);