#usage "Export milling data for a board layout\n"
" "
"Press button Reference to generate a reference Package to place
"
"
Distance Copper/Dimension (see DRC) this is the distance between
"
"the polygon and outline of the board. If the value for 'Copper/Dimension'
"
"in the DRC settings is set higher than the radius of the mill tool#2, the
"
"board dimensions will be milled larger by the difference of these two
"
"respective values. For the best result, make sure the value for
"
"Copper/Dimension in DRC Distance is set on 0.01 mm, or use the
"
"same value in the Design Rule's settings as the radius of the milling tool#2
"
"is, before running this ULP!
"
"refernece holes on PCB for exact mirroring the bottom side of PCB on milling machine.
"
"
" +
"REFERENCE circles in Layer Holes.
" +
"Place it in the layout for milling and
" +
"mirroring (bottom side) with offset and
" +
"start this ULP again.
";
"The position of the circles are defined
" +
"in the NC drill data file which comes
" +
"from the NC machine (eg. Excellon).
"; // // The various output devices // int defaultdevice = 2; // set default device int SelectedDevice = 0; string Device; enum { devScript = 1, devHPGL }; string DeviceNames[] = { "Select a device", "SCRIPT", "HPGL" }; string DeviceExt[] = { ".$$$" , ".scr", ".plt" }; string DrillExt[] = { ".$$$" , "_drl.scr" , "" }; string DefaultSuffix = DeviceExt[0]; string DrlDefaultSuffix = DrillExt[0]; string DrillLabel = "D&rill file"; string DrBrowse = "Bro&wse"; // // Parmameters // // *** The milling tool diameter *** real MillToolOutl = 0.2; real MillToolIsolate = 0.0; // int OverlapOutlPercent = 20; // percent, to avoid fine copper lines while milling out the area real MillToolFree = 0.8; // Blow up & free pouring int millfreeyes = 1; // free pouring on/off int OverlapRubOut = 20; // percent, to avoid fine copper lines while milling out the area real DrillPad = 0.8; real DrillVia = 0.8; real DrillHole = 0.8; int onlydrill = 0; // output only drill on HPGL int generatedrills = 1; // aktivate drills only in one export file // while drills crashed in 2. run. real DimensionMillTool = 2.0; // milling board outline from holder real Distance_Copper_Dimension = 1.016; // default value real Holder_Spacing = 10.0; // in mm, the distance to make a holder spacing for outline miling. // The HPGL PEN list enum { PadDrill = 1, ViaDrill, Contour, BlowUp_RubOut, HoleDrill, DimensionLine } // HPGL Pen select "SP.." string PenList[]; PenList[PadDrill] = "PadDrill"; PenList[ViaDrill] = "ViaDrill"; PenList[Contour] = "Contour"; PenList[BlowUp_RubOut] = "Blow-Up/Rub-Out"; //PenList[RubOut] = "Rub-Out"; PenList[HoleDrill] = "HoleDrill"; PenList[DimensionLine] = "DimensionLine"; string sToolValue[]; // Tool diameter as String enum { HPGLsolution = 1016 } // Inch/1016 = 0.025 mm real Mirror = 1.0; string ref_pac = "_REFERENCE_HOLE_"; // special package for mirror milling data int mirr_offsetx = 0; int ref_null_offsetX = 0; int ref_null_offsetY = 0; int ref_offsetX[]; int ref_offsetY[]; int ref_cnto = 0; int Layer = 0; string fillstyle = "Interleave"; // 9 Interleave string lOutl; // 1. milling tool string lOutl1; // 2. milling tool 26.07.2006 string lOutl2; // 3. milling tool string trueOutline_coordinate; // the generatet polygon outlines for the milling polygon string OutlinesSignalName = "_OUTLINES_"; // reserved Signal name for special polygon string OutlineMillSignal = "$_OUTLINEMILL_$"; string PassDimensionPoly = "PASSDIMESION"; // the true output line defined by Layer 20 Dimension string Pass2 = "PASS_2"; string PassPour = "PASS_POUR"; string PassOutmill = "PASS_OUTLINE"; string InPassDimensionPoly; string InPass2; string InPassPour; string InPassOutmill; int menu = 0; string showpic[]; string info; string infotext; string path; string File, FileName; string DrFile, DrillFile; int test = 0; // *** Functions *** void viewtest(string txt) { dlgDialog("test") { dlgHBoxLayout { dlgTextEdit(txt); dlgVBoxLayout dlgSpacing(150); } dlgHBoxLayout { dlgPushButton("OK") dlgAccept(); dlgPushButton("Cancel") { dlgReject(); exit(0); } dlgSpacing(400); } }; return; } void Warning(string Message, string Details) { dlgMessageBox("Warning: " + Message + " " + Details, "OK"); return; } void Fatal(string Message, string Details) { dlgMessageBox("ERROR: " + Message + "
\n" + Details); exit(1); } void Error(string Message, string Details) { dlgMessageBox("ERROR: " + Message + "
\n" + Details);
}
if (!board) Fatal("No board!", "This program can only work in the board editor.");
void dirtest(void) {
string a[];
int n = fileglob(a, "*.*");
if (n) {
dlgMessageBox("Working directory:\n" + filedir(a[0]), "OK");
}
return;
}
string vunit(int val, int unit) {
string s;
if (unit) sprintf(s, "%.4f", u2mm(val));
else sprintf(s, "%.6f",u2inch(val));
return s;
}
string getval(string line, real exf, real refholediameter) {
int Y = strchr(line, 'Y');
string s;
sprintf(s, "(%.4f %.4f) (%.4f %.4f);\n",
strtol(strsub(line, 1)) * exf,
strtol(strsub(line, Y+1)) * exf,
strtol(strsub(line, 1)) * exf + refholediameter/2 * exf,
strtol(strsub(line,Y+1)) * exf );
return s;
}
void setZerroReference(void) {
int refunit = 0;
int format = 0;
string ReferenceUnit[] = { "INCH", "MM", "INCH", "MM" };
real exf[] = { 0.001, 0.0001, 0.001, 0.0001 };
// Drillformat 2.3 2.4 2.3 2.4
// Unit Inch Inch MM MM
real refhdiameter[] = { 118, 1180, 3000, 30000 };
int Result = dlgDialog(filename(argv[0]) + " - generate refernece and place on Board") {
dlgLabel(showpic[17]);
dlgLabel(infoREFERENCE);
dlgHBoxLayout {
dlgGroup("Unit") {
dlgHBoxLayout {
dlgRadioButton("INCH", refunit);
dlgRadioButton("MM", refunit);
dlgStretch(1);
}
}
dlgStretch(1);
}
dlgGroup("Format") {
dlgHBoxLayout {
dlgRadioButton("2.3", format);
dlgRadioButton("2.4", format);
dlgStretch(1);
}
}
dlgHBoxLayout {
dlgPushButton("+OK") dlgAccept();
dlgStretch(1);
dlgPushButton("-Cancel") dlgReject();
}
};
if (Result) {
board(B) {
string usedlibraries = "USE -*;\n";
int u = 0;
do {
if (used_libraries[u]) {
usedlibraries += "USE '" + used_libraries[u] + "';\n";
u++;
}
} while(used_libraries[u]);
string used_scr = filedir(B.name) + "used-lbrs-" + filesetext(filename(B.name), ".scr");
output(used_scr, "wt") {
printf("%s", usedlibraries);
}
dlgMessageBox(" " +
"Spacers will be set on vertical & horizontal and in HPGL-Format only. " +
"The width of the spacer depends on the diameter of tool#2.";
}
}
dlgVBoxLayout dlgSpacing (400); // high of Cell
dlgStretch(0);
dlgVBoxLayout {
dlgStretch(0);
dlgLabel(info, 1);
dlgStretch(0);
dlgLabel(infotext, 1);
dlgStretch(1);
}
dlgStretch(1);
}
dlgStretch(1);
dlgHBoxLayout {
dlgStretch(0);
dlgLabel("Mill fil&e");
dlgStretch(0);
dlgStringEdit(File);
dlgStretch(0);
dlgPushButton("&Browse") {
string fn = dlgFileSave("Save Outlines file", File);
if (fn) {
File = fn;
FileName = fn;
info = showpic[1];
}
}
dlgStretch(0);
}
dlgHBoxLayout {
dlgStretch(0);
dlgLabel(DrillLabel);
dlgStretch(0);
dlgStringEdit(DrFile);
dlgStretch(0);
dlgPushButton(DrBrowse) {
string fd = dlgFileSave("Save Drill file", DrFile);
if (fd) {
DrFile = fd;
DrillFile = fd;
info = showpic[3];
}
}
dlgStretch(0);
}
dlgStretch(1);
dlgHBoxLayout dlgSpacing(600);
dlgHBoxLayout {
dlgStretch(0);
dlgPushButton("+OK") {
if (!SelectedDevice) Error("No device selected!", "Please select a device.");
else {
int fault = 0;
real distanceDimension = DimensionMillTool / 2 - Distance_Copper_Dimension;
if (DimensionMillTool) { // **** if used ? ****
if (distanceDimension < 0) {
string d;
sprintf(d, "%.4f", distanceDimension * -1);
fault = dlgMessageBox(" "
"Check/change Distance Copper/Dimension in Design Rules DRC "
"or accept a distance of " + d +
"mm to board dimension.
" +
used_scr +
"
to set the used libraries back.", "OK");
string cmd;
string ref[];
string reference_file = dlgFileOpen("Select drill reference file (Holes)", "", "*.ncd\n*.*");
if (!reference_file) return;
int unit_format = refunit * 2 + format;
int l;
l = fileread(ref, reference_file);
string lbrname;
lbrname = filesetext(B.name, "$$$ref_tmp.lbr");
sprintf( cmd, "OPEN '%s';\n", lbrname );
cmd += "Edit '" + ref_pac + ".PAC';\nCHANGE LAYER 45;\nGRID " + ReferenceUnit[refunit] + ";\n";
for (int n = 0; n <= l; n++) {
string s = ref[n];
if(s[0] == 'X') cmd += "CIRCLE 0 " + getval(ref[n], exf[unit_format], refhdiameter[unit_format]);
}
cmd += "WRITE;\n";
cmd += "EDIT '" + B.name + "';\n";
cmd += "DISPLAY 45;\n";
cmd += "GRID " + ReferenceUnit[refunit] + ";\n";
cmd += "WIN FIT; WIN (" +
vunit((B.area.x1 + B.area.x2 )/2, refunit) + " " +
vunit((B.area.y1 + B.area.y2 )/2, refunit) + ") (" +
vunit((B.area.x1 + B.area.x2 )/2 + 1000, refunit) + " " +
vunit((B.area.y1 + B.area.y2 )/2 + 1000, refunit) + ") (" +
vunit((B.area.x1 + B.area.x2 )/2 + 150 , refunit) + " " +
vunit((B.area.y1 + B.area.y2 )/2 + 150 , refunit) + ");\n",
cmd += "USE -*; USE '" + lbrname + "';\n";
cmd += "ADD " + ref_pac + "\n";
if (test) viewtest(cmd);
exit(cmd);
}
}
return;
}
// *** Tools in EXCELLON and GERBER (mm)
void toolFiles(void) {
board(B) {
output(filesetext(B.name, ".rac")) {
printf("T01 %.1f mm\n", DrillPad);
printf("T02 %.1f mm\n", DrillVia);
printf("T03 %.1f mm\n", MillToolOutl);
printf("T04 %.1f mm\n", MillToolFree);
printf("T05 %.1f mm\n", DrillHole);
printf("T06 %.1f mm\n", DimensionMillTool);
}
output(filesetext(B.name, ".whl")) {
printf(";aperture wheel file generated by %s %s\n", EAGLE_SIGNATURE, filename(argv[0]) );
printf(";remove the above line to prevent this file from being overwritten!\n");
printf("D11 round %6.4fmm\n", DrillPad);
printf("D12 round %6.4fmm\n", DrillVia);
printf("D13 round %6.4fmm\n", MillToolOutl);
printf("D14 round %6.4fmm\n", MillToolFree);
printf("D15 round %6.4fmm\n", DrillHole);
printf("D16 round %6.4fmm\n", DimensionMillTool);
}
}
return;
}
void showRackFile(void) {
board(B) {
string text;
int nChars = fileread(text, filesetext(B.name, ".rac"));
dlgDialog("Show rack file") {
dlgTextView(text);
dlgHBoxLayout {
dlgPushButton("+&OK") dlgAccept();
dlgStretch(1);
}
};
}
return;
}
void showHPGLinfo(void) {
string text;
int nChars = fileread(text, filesetext(FileName, ".pli"));
dlgDialog("Show plot info") {
dlgTextView(text);
dlgHBoxLayout {
dlgPushButton("+&OK") dlgAccept();
dlgStretch(1);
dlgSpacing(200);
}
};
return;
}
void ValueInit(void) {
sprintf( sToolValue[PadDrill], "%.2f", DrillPad);
sprintf( sToolValue[ViaDrill], "%.2f", DrillVia);
sprintf( sToolValue[Contour], "%.2f", MillToolOutl);
sprintf( sToolValue[BlowUp_RubOut], "%.2f", MillToolFree);
sprintf( sToolValue[HoleDrill], "%.2f", DrillHole);
sprintf( sToolValue[DimensionLine], "%.2f", DimensionMillTool);
return;
}
void startlineprint( int x1, int y1, int x2, int y2) {
printf("\nPA%.0f,%.0f;PD;\nPA%.0f,%.0f;", Mirror * u2inch(x1 + mirr_offsetx)*HPGLsolution, u2inch(y1 + ref_null_offsetY)*HPGLsolution, Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution);
return;
}
void nextlineprint( int x2, int y2) {
printf("\nPA%.0f,%.0f;", Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution );
return;
}
void endlineprint( int x2, int y2) {
printf("\nPA%.0f,%.0f;PU;", Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution);
return;
}
void fullineprint( int x1, int y1, int x2, int y2) {
printf("\nPA%.0f,%.0f;PD;\nPA%.0f,%.0f;PU;", Mirror * u2inch(x1 + mirr_offsetx)*HPGLsolution, u2inch(y1 + ref_null_offsetY)*HPGLsolution, Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution );
return;
}
real WireLength(real x1,real x2,real y1,real y2) {
real WL = sqrt(pow(x2-x1,2) + pow(y2-y1,2)); // calculate Wire length WL
return WL;
}
// if the wire longer as longdistance and vertical or horizontal
// then make a holder spacing
void checkBridge(int x1, int y1, int x2, int y2, int state) {
real WL = WireLength(u2mm(x2),u2mm(x1),u2mm(y2),u2mm(y1));
if (WL >= Holder_Spacing && (x1 == x2 || y1 == y2) ) {
int bridgewidth = DimensionMillTool * 10000;
int xa, xb, ya, yb;
switch (state) {
case 0:
startlineprint(x1, y1, x2, y2);
break;
case 1:
if (x2 > x1 && x2 >= 0) {
xa = x2 - (2.5 * bridgewidth);
xb = x2 - bridgewidth;
}
if (x2 < x1 && x2 >= 0) {
xa = x2 + (2.5 * bridgewidth);
xb = x2 + bridgewidth;
}
if (x2 > x1 && x2 < 0) {
xa = x2 - (2.5 * bridgewidth);
xb = x2 - bridgewidth;
}
if (x2 < x1 && x2 < 0) {
xa = x2 + (2.5 * bridgewidth);
xb = x2 + bridgewidth;
}
if (y2 > y1 && y2 >= 0 ) {
ya = y2 - (2.5 * bridgewidth);
yb = y2 - bridgewidth;
}
if (y2 < y1 && y2 >= 0) {
ya = y2 + (2.5 * bridgewidth);
yb = y2 + bridgewidth;
}
if (y2 > y1 && y2 < 0 ) {
ya = y2 - (2.5 * bridgewidth);
yb = y2 - bridgewidth;
}
if (y2 < y1 && y2 < 0) {
ya = y2 + (2.5 * bridgewidth);
yb = y2 + bridgewidth;
}
if (x1 == x2) {
endlineprint(x1, ya);
startlineprint(x1, yb, x1, y2);
}
else {
endlineprint(xa, y1);
startlineprint(xb, y1, x2, y2);
}
break;
case 2:
endlineprint(x2, y2);
break;
}
return;
}
else {
switch (state) {
case 0:
startlineprint(x1, y1, x2, y2);
break;
case 1:
nextlineprint(x2, y2);
break;
case 2:
endlineprint(x2, y2);
break;
}
return;
}
}
void DeviceDraw(int x1, int y1, int x2, int y2, int state) {
// Actually draw a line on the output device.
// 'state' is defined as
// 0 = this is the first line of a partial polygon
// 1 = this is a "normal" line (neither the first nor the last one)
// 2 = this is the last line of a partial polygon
// 3 = this is a drill coordinate
// 4 = this is one line
switch (SelectedDevice) {
case devScript:
switch (state) {
case 0:
printf("WIRE (%.4f %.4f) (%.4f %.4f)\n", Mirror * u2mm(x1 + mirr_offsetx), u2mm(y1 + ref_null_offsetY), Mirror * u2mm(x2 + mirr_offsetx) , u2mm(y2 + ref_null_offsetY));
break;
case 1:
printf("(%.4f %.4f)\n", Mirror * u2mm(x2 + mirr_offsetx), u2mm(y2 + ref_null_offsetY));
break;
case 2:
printf("(%.4f %.4f);\n", Mirror * u2mm(x2 + mirr_offsetx), u2mm(y2 + ref_null_offsetY));
break;
case 3:
printf("HOLE (%.4f %.4f);\n", Mirror * u2mm(x2 + mirr_offsetx), u2mm(y2 + ref_null_offsetY));
break;
case 4:
printf("WIRE (%.4f %.4f) (%.4f %.4f)\n", Mirror * u2mm(x1 + mirr_offsetx), u2mm(y1 + ref_null_offsetY), Mirror * u2mm(x2 + mirr_offsetx), u2mm(y2 + ref_null_offsetY));
break;
}
break;
case devHPGL:
switch (state) {
case 0:
if (InPassOutmill) checkBridge(x1, y1, x2, y2, state);
else startlineprint(x1, y1, x2, y2);
break;
case 1:
if (InPassOutmill) checkBridge(x1, y1, x2, y2, state);
else nextlineprint(x2, y2);
break;
case 2:
if (InPassOutmill) checkBridge(x1, y1, x2, y2, state);
else endlineprint(x2, y2);
break;
case 3: // drilling tool
printf("\nPA%.0f,%.0f;PD;", Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution);
printf("\nPA%.0f,%.0f;PU;", Mirror * u2inch(x2 + mirr_offsetx)*HPGLsolution, u2inch(y2 + ref_null_offsetY)*HPGLsolution);
break;
case 4: // polygon filling
fullineprint(x1, y1, x2, y2);
break;
}
break;
}
return;
}
void CircleDraw(int centerx, int centery, int diam, int drilltool) {
switch (SelectedDevice) {
case devScript:
printf("CHANGE WIDTH %.4f;\n", DrillHole);
printf("CIRCLE (%.4f %.4f) (%.4f %.4f);\n", Mirror * u2mm(centerx + mirr_offsetx), u2mm(centery + ref_null_offsetY), Mirror * u2mm((centerx + diam / 2 - drilltool/2) + mirr_offsetx), u2mm(centery + ref_null_offsetY));
break;
case devHPGL:
// for HPGL 1. Start point arc,
// 2. Center point arc
// 3. angel
printf("\nPA%.0f,%.0f;PD;", Mirror * (u2inch((centerx + diam / 2 - drilltool / 2) + mirr_offsetx)) * HPGLsolution, u2inch(centery + ref_null_offsetY) * HPGLsolution);
printf("\nAA%.0f,%.0f,360;PU;", Mirror * u2inch(centerx + mirr_offsetx) * HPGLsolution, u2inch(centery + ref_null_offsetY) * HPGLsolution );
break;
}
return;
}
/*
void ArcDraw(int startarcx, int startarcy, int endarcx, int endarcy, int centerarcx, int centerarcy, real angle) {
switch (SelectedDevice) {
case devScript:
int rx = 2 * (startarcx - centerarcx);
int ry = 2 * (startarcy - centerarcy);
if (Mirror == 1) printf("ARC CCW ");
else printf("ARC CW ");
printf("(%.4f %.4f) (%.4f %.4f) (%.4f %.4f);\n",
Mirror * u2mm(startarcx + mirr_offsetx), u2mm(startarcy + ref_null_offsetY),
Mirror * u2mm(startarcx - rx + mirr_offsetx), u2mm(startarcy - ry + ref_null_offsetY),
Mirror * u2mm(endarcx + mirr_offsetx), u2mm(endarcy + ref_null_offsetY) );
break;
case devHPGL:
printf("\nPA%.0f,%.0f;PD;", Mirror * u2inch(startarcx + mirr_offsetx)*HPGLsolution, u2inch(startarcy + ref_null_offsetY)*HPGLsolution);
printf("\nAA%.0f,%.0f,%.2f;PU;", Mirror * u2inch(centerarcx + mirr_offsetx)*HPGLsolution, u2inch(centerarcy + ref_null_offsetY)*HPGLsolution, Mirror * angle);
break;
}
return;
}
*/
void scriptHeader(void) {
printf("GRID mm;\n");
printf("SET OPTIMIZING OFF;\nSET UNDO_LOG OFF;\nSET WIRE_BEND 2;\n");
printf("LAYER %d %s;\nSET FILL_LAYER %d %s;\n",
Layer + 100, lOutl, Layer + 100, fillstyle);
// 26.07.2006
printf("LAYER %d %s;\nSET FILL_LAYER %d %s;\n",
Layer + 101, lOutl1, Layer + 101, fillstyle);
printf("LAYER %d %s;\nSET FILL_LAYER %d %s;\n",
Layer + 102, lOutl2, Layer + 102, fillstyle);
printf("CHANGE LAYER %d;\n", Layer + 100 );
return;
}
void DeviceInit(int tool) {
// Do anything necessary to initialize the output device
switch (SelectedDevice) {
case devScript:
// TODO make the layer user definable?
if (InPassPour) {
real overlap = MillToolFree * OverlapOutlPercent / 100;
printf("CHANGE WIDTH %.4f;\n", MillToolFree);
printf("SET FILL_LAYER %d %s;\n", Layer + 100, fillstyle);
printf("CHANGE LAYER %d;\n", Layer + 101); // 26.07.2006
}
break;
case devHPGL:
output(filesetext(FileName,".pli"), "at") {
// printf("Pen #%d = %s mm\t%s\n", tool, sToolValue[tool], PenList[tool]);
}
if (tool = Contour) {
printf ("IN; IP 0,0,100,100;SC 0,100,0,100;");
}
break;
}
return;
}
void DeviceReInit(int tool) {
// Do anything necessary the secondary initialize the output device
switch (SelectedDevice) {
case devScript:
// TODO make the layer user definable?
switch (tool) {
case PadDrill:
printf("\nCHANGE DRILL %.2f;\n", DrillPad);
break;
case ViaDrill:
printf("\nCHANGE DRILL %.2f;\n", DrillVia);
break;
case HoleDrill:
printf("\nCHANGE DRILL %.2f;\n", DrillHole );
break;
case Contour:
printf("\nCHANGE WIDTH %.4f;\n", MillToolOutl);
break;
case BlowUp_RubOut:
printf("\nCHANGE WIDTH %.4f;\n", MillToolFree);
printf("\nchange layer %d;\n", Layer+101); // 26.07.2006
break;
case DimensionLine:
printf("\nCHANGE WIDTH %.4f;\n", DimensionMillTool);
printf("\nCHANGE LAYER %d;\n", Layer+102); // 26.07.2006
break;
default:
break;
}
break;
case devHPGL:
printf ("\nPU;\nSP%d;\nPA0,0;", tool); // pen select
if(test) printf(" # %s", PenList[tool]);
output(filesetext(FileName,".pli"), "at") {
if (onlydrill && tool == Contour);
printf("Pen #%d = %s mm\t%s\n", tool, sToolValue[tool], PenList[tool]);
}
break;
}
return;
}
void DeviceEnd(void) {
// Do anything necessary to end output to the device
switch (SelectedDevice) {
case devScript:
break;
case devHPGL:
printf("\nPU;\nSP0;");
break;
}
return;
}
// TRUE OUTLINE ***
void trueOutlineDraw(string SignalName) {
board(B) {
B.signals(S) {
if (S.name == SignalName) {
S.polygons(P) {
int x1 = INT_MAX, y1 = INT_MAX, x2 = INT_MIN, y2 = INT_MIN;
int x0, y0, first = 1;
int FrameWire;
string s;
P.wires(W) {
x1 = min(x1, W.x1);
x2 = max(x2, W.x1);
y1 = min(y1, W.y1);
y2 = max(y2, W.y1);
}
string lasts;
P.contours(W) {
if (first) {
// a new partial polygon is starting
x0 = W.x1;
y0 = W.y1;
FrameWire = (x1 == x0 || x2 == x0) && (y1 == y0 || y2 == y0);
sprintf(s, " (%.4f %.4f)", u2mm(W.x1), u2mm(W.y1) );
trueOutline_coordinate = s;
lasts = s;
first = 0;
}
else if (W.x2 == x0 && W.y2 == y0) {
// this was the last wire of the partial polygon,
// so the next wire (if any) will be the first wire
// of the next partial polygon
sprintf(s, " (%.4f %.4f)", u2mm(W.x2), u2mm(W.y2) );
if (lasts != s) trueOutline_coordinate += s;
lasts = s;
first = 1;
}
else ;
if (!FrameWire) {
sprintf(s, " (%.4f %.4f)", u2mm(W.x2), u2mm(W.y2) );
if (lasts != s) trueOutline_coordinate += s;
lasts = s;
}
}
}
}
}
return ;
}
}
// *** the return string to start rekursiv ***
string RUN_pass(string run_Pass) {
string s;
sprintf(s, "RUN '%s' '%s' '%.4f' '%.4f' '%.4f' '%d' '%s' '%s' '%s' '%.1f' '%.2f' '%.2f' '%.2f' '%d' '%d' '%.2f' '%.4f' '%d' '%.4f' '%d' '%d' '%s';\n",
argv[0], Device, MillToolOutl, MillToolIsolate, MillToolFree, Layer, FileName, DrillFile, run_Pass,
Mirror, DrillPad, DrillVia, DrillHole, OverlapOutlPercent, OverlapRubOut, Distance_Copper_Dimension,
DimensionMillTool, millfreeyes, Holder_Spacing, onlydrill, generatedrills, trueOutline_coordinate);
return s;
}
// place a VIA outside the board with the same signal name
// for used polygon to calculate the filling with orpahns off
// and get no more polygon contours
void generateTruePolygonOutlines() { // the true outlines of board
board(B) {
real x1 = u2mm(B.area.x1) - DimensionMillTool, y1 = u2mm(B.area.y1) - DimensionMillTool,
x2 = u2mm(B.area.x2) + DimensionMillTool, y2 = u2mm(B.area.y2) + DimensionMillTool;
real distanceDimension = (DimensionMillTool / 2 - Distance_Copper_Dimension) * 2;
if (distanceDimension < 0) distanceDimension = 0.001;
string Cmd;
sprintf(Cmd, "GRID mm FINEST;\n"
"CHANGE DRILL 0.3;\nCHANGE DIAMETER 0.5;\nCHANGE SHAPE ROUND;\n"
"SET POLYGON_RATSNEST ON;\n"
"SET WIRE_BEND 0;\n"
"CHANGE RANK 1;\n" // 26.07.2006 to generate the true outline of dimension
"CHANGE POUR SOLID;\n"
"CHANGE THERMAL OFF;\n"
"CHANGE LAYER %d;\n"
"DISPLAY NONE 17 %d;\n",
Layer, Layer);
// make the 1st virtual Net for Normal Polygon Orphens OFF
// to generate the true outline for place the spacial polygon
// make a partial WIA for generate Polygon with orphans off
string millout;
sprintf(millout, "VIA '%s' (%.4f %.4f);\n", OutlineMillSignal, x1 - Distance_Copper_Dimension - 1, y1 - Distance_Copper_Dimension - 1);
Cmd += millout;
sprintf(millout, "CHANGE ISOLATE 0;\n");
Cmd += millout;
sprintf(millout, "CHANGE ORPHANS OFF;\n");
Cmd += millout;
// Width = MillFree-Diameter/2 minus "Distance Copper/Dimension"
sprintf(millout, "POLYGON '%s' %.4f (%.4f %.4f) (%.4f %.4f)(%.4f %.4f);\nRATSNEST;\n",
OutlineMillSignal, distanceDimension,
x1 - DimensionMillTool - Distance_Copper_Dimension - 2, // 2mm outside
y1 - DimensionMillTool - Distance_Copper_Dimension - 2,
x2 + DimensionMillTool + Distance_Copper_Dimension + 2,
y2 + DimensionMillTool + Distance_Copper_Dimension + 2,
x1 - DimensionMillTool - Distance_Copper_Dimension - 2,
y1 - DimensionMillTool - Distance_Copper_Dimension - 2);
Cmd += millout;
Cmd += "WINDOW FIT;\n";
Cmd += RUN_pass(PassDimensionPoly);
if (test) output(filesetext(B.name, "-cmd.txt"), "wt") printf("%s", Cmd);
exit (Cmd);
}
}
//
// The actual outlines generator
void GenerateOutlines(void) {
board(B) {
trueOutlineDraw(OutlineMillSignal);
string s;
real x1 = u2mm(B.area.x1) - MillToolFree, y1 = u2mm(B.area.y1) - MillToolFree,
x2 = u2mm(B.area.x2) + MillToolFree, y2 = u2mm(B.area.y2) + MillToolFree;
real distanceDimension = MillToolFree / 2 - Distance_Copper_Dimension;
if (distanceDimension < 0) distanceDimension = 0.001;
string Cmd = "SET WIRE_BEND 2;\n";
// delete virtual Polygon and VIA
B.signals(S) {
if (S.name == OutlineMillSignal) { // 2008.05.05
S.vias(V) {
sprintf( s, "RIPUP (%.4f %.4f);\n",
u2mm(V.x), u2mm(V.y) );
Cmd += s;
break;
}
S.polygons(POL) {
POL.wires(W) {
sprintf( s, "DELETE (S%.4f %.4f);\n",
u2mm(W.x1), u2mm(W.y1) );
Cmd += s;
break;
}
break;
}
}
}
sprintf(s, "CHANGE ISOLATE %.4f;\n", MillToolIsolate); // 26.07.2006
Cmd += s;
sprintf(s, "CHANGE RANK 6;\n"); // 26.07.2006
Cmd += s;
sprintf(s, "POLYGOn %s %.4f %s;\nRATSNEST;\n",
OutlinesSignalName, MillToolOutl, trueOutline_coordinate);
Cmd += s;
Cmd += RUN_pass(Pass2);
if (test) output(filesetext(B.name, "-cmd.txt"), "at") printf("%s", Cmd);
if (test) viewtest(Cmd);
exit(Cmd);
}
}
string WriteOutlines(string SignalName) { // OutlinesSignalName
board(B) {
if (InPassPour && MillToolFree == 0) return "";
if (InPassOutmill && !DimensionMillTool) return ""; // 0 = generate no Dimension with spacing
string Cmd;
B.signals(S) {
if (S.name == SignalName && !onlydrill) {
S.polygons(P) {
int x1 = INT_MAX, y1 = INT_MAX, x2 = INT_MIN, y2 = INT_MIN;
int x0, y0, first = 1;
int FrameWire;
int State;
P.wires(W) {
x1 = min(x1, W.x1);
x2 = max(x2, W.x1);
y1 = min(y1, W.y1);
y2 = max(y2, W.y1);
}
if (InPass2) {
DeviceReInit(Contour);
}
if (InPassPour) DeviceReInit(BlowUp_RubOut);
if (InPassOutmill) DeviceReInit(DimensionLine);
P.contours(W) {
if (first) {
// a new partial polygon is starting
x0 = W.x1;
y0 = W.y1;
FrameWire = (x1 == x0 || x2 == x0) && (y1 == y0 || y2 == y0);
State = 0;
first = 0;
}
else if (W.x2 == x0 && W.y2 == y0) {
// this was the last wire of the partial polygon,
// so the next wire (if any) will be the first wire
// of the next partial polygon
State = 2;
first = 1;
}
else State = 1;
if (!FrameWire) {
DeviceDraw(W.x1, W.y1, W.x2, W.y2, State);
}
}
if (InPassPour && millfreeyes) {
if (SelectedDevice == devScript) {
printf("# pouring\n");
}
DeviceReInit(BlowUp_RubOut);
State = 4;
int fx1[], fy1[], fx2[], fy2[];
int fcnt = 0;
P.fillings(F) {
fx1[fcnt] = F.x1;
fy1[fcnt] = F.y1;
fx2[fcnt] = F.x2;
fy2[fcnt] = F.y2;
fcnt++;
}
int diry = fy1[0];
for (int m = 0; m <= fcnt; m++) {
if (diry == fy1[m]) {
DeviceDraw(fx1[m], fy1[m], fx2[m], fy2[m], State);
}
else {
// ****** milling reverse ******
diry = fy1[m]; // next y_line
for (int mb = m; mb <= fcnt; mb++) {
if (diry == fy1[mb]);
else break;
}
for(int mback = mb -1 ; mback >= m; mback--) {
DeviceDraw(fx2[mback], fy2[mback], fx1[mback], fy1[mback], State);
// reverse milling 08.05.2002 alf@cadsoft.de
}
m = mb;
diry = fy1[m]; // next y_line
m--;
}
}
}
DeviceEnd();
}
break;
}
}
return Cmd;
}
}
void Pad(UL_PAD P) {
DeviceDraw(0, 0, P.x, P.y, 3);
}
void Package(UL_PACKAGE P) {
P.contacts(C) {
if (C.pad) Pad(C.pad);
}
return;
}
void Element(UL_ELEMENT E) {
Package(E.package);
return;
}
void PackageHole(UL_PACKAGE P) {
P.holes(H) {
if (u2mm(H.drill) > DrillHole) {
CircleDraw(H.x, H.y, H.drill, DrillHole*10000); // 2007.09.21 der Fraesweg fuer grosse Bohrungen wurde falsch berechnet
}
else DeviceDraw(0, 0, H.x, H.y, 3);
}
return;
}
void ElementHole(UL_ELEMENT E) {
PackageHole(E.package);
return;
}
void WriteDrills(void) {
board(B) {
DeviceReInit(PadDrill);
B.elements(E) Element(E);
DeviceReInit(ViaDrill);
B.signals(S) {
S.vias(V) {
DeviceDraw(0, 0, V.x, V.y, 3);
}
}
}
return;
}
void WriteHoles(void) {
board(B) {
DeviceReInit(HoleDrill);
B.elements(E) ElementHole(E);
B.holes(H) {
if (u2mm(H.drill) > DrillHole) {
CircleDraw(H.x, H.y, H.drill, DrillHole*10000); // internal unit
}
else DeviceDraw(0, 0, H.x, H.y, 3);
}
}
return;
}
// *** Pen assaign
void penAssign(void) {
dlgDialog("Mill outlines: HPGL Pen Assignment") {
string pen_assign = "\n";
string t;
PenList[PadDrill] = "PadDrill";
PenList[ViaDrill] = "ViaDrill";
PenList[Contour] = "Contour";
PenList[BlowUp_RubOut] = "Blow-Up/Rub-Out";
PenList[HoleDrill] = "HoleDrill";
PenList[DimensionLine] = "DimensionLine";
sprintf(t, "
";
dlgLabel(pen_assign);
dlgHBoxLayout {
dlgStretch(0);
dlgPushButton("OK") dlgAccept();
dlgStretch(1);
}
};
return;
}
void selectDevice(void) {
File = filesetext(FileName, DeviceExt[SelectedDevice]);
if (SelectedDevice != devHPGL) {
info = showpic[12 + SelectedDevice];
DrFile = filesetext(DrillFile, DrillExt[SelectedDevice]);
DrillLabel = "D&rill file";
DrBrowse = "Bro&wse";
}
else {
info = showpic[12 + SelectedDevice];
DrFile ="";
DrillLabel = "D&rill file";
DrBrowse = "Bro&wse";
}
return;
}
void setMillOffset(void) {
board(B) {
path = filedir(B.name);
B.elements(E) {
if (E.package.name == ref_pac) {
ref_offsetX[ref_cnto] = E.x;
ref_offsetY[ref_cnto] = E.y;
ref_cnto++;
E.package.circles(C) {
if (C.layer == 45) {
ref_offsetX[ref_cnto] = C.x;
ref_offsetY[ref_cnto] = C.y;
ref_cnto++;
}
}
if(ref_offsetX[1] > ref_offsetX[2]) { // if first X > second X then swap
ref_offsetX[3] = ref_offsetX[2];
ref_offsetX[2] = ref_offsetX[1];
ref_offsetX[1] = ref_offsetX[3];
}
if (ref_cnto == 3) {
ref_null_offsetX = ref_offsetX[0] * -1;
ref_null_offsetY = ref_offsetY[0] * -1;
if (Mirror == -1) {
mirr_offsetx = (ref_offsetX[2] + ref_offsetX[1] + ref_null_offsetX) * -1;
}
else {
mirr_offsetx = ref_null_offsetX;
}
}
else {
ref_cnto = 0; // more then 2 circles can't use as reference
if (dlgMessageBox("The reference Hole-Package contains more then 2 Holes\n" +
"generate outlines without reference-offset", "accept", "break") != 0) exit(0);
}
}
}
}
return;
}
void setblowinfo(void) {
if (MillToolFree) {
if (millfreeyes) { // 14.05.2002 alf
info = showpic[18];
infotext = "PadDrill: PEN %d %.1f mm \n", PadDrill, DrillPad);
pen_assign += t;
sprintf(t, " ViaDrill: PEN %d %.1f mm \n", ViaDrill, DrillVia);
pen_assign += t;
sprintf(t, " Contour: PEN %d %.1f mm \n", Contour, MillToolOutl);
pen_assign += t;
sprintf(t, " BlowUp/RubOut: PEN %d %.1f mm \n", BlowUp_RubOut, MillToolFree);
pen_assign += t;
sprintf(t, " HoleDrill: PEN %d %.1f mm \n", HoleDrill, DrillHole);
pen_assign += t;
sprintf(t, " Mill outline (Dimension): PEN %d %.1f mm \n", DimensionLine, DimensionMillTool);
pen_assign += t;
pen_assign += "
If value set to 0, no Blow-Up (second isolate)
and Rub-Out (free milling) is generated.
Tool diameter for sec. isolate
If value set, Blow-Up (second isolate) is generated.
";
string Layers[];
int SelectedLayer = -1;
int ForceDialog = (!Device || !MillToolOutl);
board(B) {
B.signals(S) {
if (S.name == OutlinesSignalName)
Fatal("There is already a signal named " + OutlinesSignalName + " in this board!", "Please make sure that there is no such signal in this board.");
// *** Check Rank - 26.07.2006 ***
string s;
int is_polygon = 0;
S.polygons(P) {
is_polygon++;
if (P.rank == 6) {
P.contours(W) {
sprintf(s, "!%s Polygon in Layer %d at (%.4f %.4f)mm has Rank 6.\n\nDo not use Rank 6 while working with this ULP.\n" +
"Change the rank to 1..5 before using this ULP again.", S.name, P.layer, u2mm(W.x1), u2mm(W.y1) );
break;
}
dlgMessageBox(s, "OK");
exit(0);
}
}
if (is_polygon) { // *** 01.08.2006 check if Polygon used in Layout
// wenn ein füllendes Polygon im Board plaziert ist, dann muß die Clearance um 0.005mm größer
// sein als der Fräser, damit bei Leiterbahnen die nicht im 0° 45° oder 90° Winkel also mit
// beliebigen Winkel verlegt sind, keine Füllprobleme wegen Rechenungenauigkeit auftauchen.
B.signals(S) {
if (u2mm(S.class.clearance) < MillToolOutl + 0.005) {
sprintf(s, "!Clearance from CLASS %d %s = %.4f.\nMust be 0.005mm > Tool#1 %.4f", S.class.number, S.class.name, u2mm(S.class.clearance), MillToolOutl);
dlgMessageBox(s, "OK");
exit(0);
}
}
}
}
int n;
B.layers(L) {
if (L.number <= 16 && L.visible) {
if (Layer == L.number) SelectedLayer = n;
sprintf(Layers[n++], "%d %s", L.number, L.name);
}
}
if (n == 0) Fatal("No signal layer active!", "Please activate the signal layer to generate outlines for.");
if (!Layer) {
if (n > 1) ForceDialog = 1;
SelectedLayer = 0;
}
if (SelectedLayer < 0) {
string s;
sprintf(s, "%d", Layer);
Fatal("Invalid layer: " + s, "The layer was not found or is not active.");
}
if (ForceDialog) {
SelectedDevice = defaultdevice;
File = FileName;
DrFile = DrillFile;
DrillLabel = "D&rill file";
selectDevice();
int mir = 0;
dlgDialog(Version) {
dlgHBoxLayout {
dlgGridLayout {
dlgCell(0, 0) dlgLabel("&Device");
dlgCell(0, 1) dlgComboBox(DeviceNames, SelectedDevice) {
if (SelectedDevice) selectDevice();
else {
File = "";
DrFile = "";
}
// 26.07.2006
if (SelectedDevice == devHPGL) infotext = "";
else if (SelectedDevice == devScript) {
infotext = "
" +
"Layer+101 to read copper pouring milling,
" +
"Layer+102 to read real outlines milling (Dimension)
" +
"into board as wire.";
}
}
dlgCell(0, 3) dlgCheckBox("onl&y drills (HPGL)", onlydrill) {
if (onlydrill) {
generatedrills = 1;
infotext = "
";
}
}
dlgCell(6, 4) dlgPushButton("Pad info") { info = showpic[5]; infotext = "Pad drill diameter"; };
dlgCell(7, 0) dlgLabel("&Via drill");
dlgCell(7, 1) dlgRealEdit(DrillVia, 0.005, 10);
dlgCell(7, 2) dlgLabel("mm");
dlgCell(7, 4) dlgPushButton("Via info"){
info = showpic[6];
infotext = "Via drill diameter.";
}
dlgCell(8, 0) dlgLabel("&Hole drill");
dlgCell(8, 1) dlgRealEdit(DrillHole, 0.005, 10);
dlgCell(8, 2) dlgLabel("mm");
dlgCell(8, 4) dlgPushButton("Hole info"){
info = showpic[7];
infotext = "Hole drill diameter.";
}
dlgCell(9, 0) dlgLabel("Dist. &Copper/Dim");
dlgCell(9, 1) dlgRealEdit(Distance_Copper_Dimension, 0.0, 20);
dlgCell(9, 2) dlgLabel("mm");
dlgCell(9, 4) dlgPushButton("Distance info") {
info = showpic[10];
infotext = infotext = DRCinfo + usage +
"
";
}
dlgCell(10, 0) dlgLabel("Mill Board/D&im");
dlgCell(10, 1) dlgRealEdit(DimensionMillTool, 0.0, 5);
dlgCell(10, 2) dlgLabel("mm");
dlgCell(10, 3) dlgLabel(" (0 = OFF)");
dlgCell(10, 4) dlgPushButton("Dimension info") {
info = showpic[16];
if (DimensionMillTool) {
infotext = "
Set value 0 for switch OFF this function.
Set value for switch ON this function.