// vim:syntax=c
#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;
int erase_silk = 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;
}
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;
}
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;
// find currently active layers, exclude _tsilk and _bsilk from the
// mix.
B.layers(L) {
if (L.number == _tsilk || L.number == _bsilk ) {
got_a_silk = 1;
continue;
} else if (L.visible) {
sprintf(h," %d", L.number); cur_active_layers += h;
}
}
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 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);
}
void do_text(string ename, UL_TEXT T, int dstlay, string orient)
{
real OrigWidth;
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;
}
setLayer(dstlay);
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];
}
}
sprintf(h, "TEXT '%s' %s%1.0f (%5.3f %5.3f);\n",
value, orient, T.angle, u2mil(T.x), u2mil(T.y));
cmd += h;
}
/* 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;
}
// make sure curWidth (in mils) to no less than Silkwidth
real clampSilkWidth(real curWidth)
{
if (curWidth < Silkwidth)
return Silkwidth;
return curWidth;
}
void searchElements(UL_ELEMENT E, int source, int dimrun, string orient)
{
int dstlay = getDstLayer(source, dimrun);
real sw;
string h;
// iterate over wires in element
E.package.wires(W) {
// fixed arcs for v4.11
if (W.arc) {
if (W.arc.layer != source) continue;
sw = clampSilkWidth(u2mil(W.arc.width));
setLayer(dstlay);
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) continue;
sw = clampSilkWidth(u2mil(W.width));
setLayer(dstlay);
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;
}
}
E.package.circles(C) {
if (C.layer != source) continue;
sw = clampSilkWidth(u2mil(C.width));
setLayer(dstlay);
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;
}
// fixed angle
// Removed TextOrientation string, always "R"
E.package.rectangles(R) {
if (R.layer != source) continue;
setLayer(dstlay);
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;
}
E.package.polygons(P) {
int first;
if (P.layer != source) continue;
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;
}
// non-smashed texts
E.package.texts(T) {
if (T.layer == source)
do_text(E.name, T, dstlay, orient);
}
// smashed texts
E.texts(T) {
if (T.layer == source)
do_text(E.name, T, dstlay, orient);
}
}
void searchTexts(UL_TEXT T, int source, int dimrun, string orient)
{
int dstlay = getDstLayer(source, dimrun);
if (T.layer == source)
do_text("", T, dstlay, orient);
}
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("erase_silk=%d\n", dimension);
printf("warn_ratio=%d\n", dimension);
}
}
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] == "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);
}
dlgLabel ("Source layers:");
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);
}
dlgHBoxLayout {
dlgPushButton ("+Make Silkscreen layers") dlgAccept (1);
dlgPushButton ("Erase old silkscreen") dlgAccept(2);
}
}
};
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 {
cmd += "SET UNDO_LOG OFF;\n"; // advisable for speed reasons
header(B);
// board level text.
B.texts(T) {
searchTexts(T,dimension,1,"R");
// draw dimensions for bottom side
searchTexts(T,dimension,2,"MR");
if (do_tplace)
searchTexts(T,tplace,0,"R");
if (do_bplace)
searchTexts(T,bplace,0,"MR");
if (do_tnames)
searchTexts(T,tnames,0,"R");
if (do_bnames)
searchTexts(T,bnames,0,"MR");
if (do_tvalues)
searchTexts(T,tvalues,0,"R");
if (do_bvalues)
searchTexts(T,bvalues,0,"MR");
}
B.elements(E) {
// draw dimensions for top side
searchElements(E,dimension,1,"R");
// draw dimensions for bottom side
searchElements(E,dimension,2,"MR");
if (do_tplace)
searchElements(E,tplace,0,"R");
if (do_bplace)
searchElements(E,bplace,0,"MR");
if (do_tnames)
searchElements(E,tnames,0,"R");
if (do_bnames)
searchElements(E,bnames,0,"MR");
if (do_tvalues)
searchElements(E,tvalues,0,"R");
if (do_bvalues)
searchElements(E,bvalues,0,"R");
}
cmd += "SET UNDO_LOG ON;\n";
}
}
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);