#usage "
Export data for KCam or any other CNC software
"
"
"
"- Bottom layer will be mirrored by Y AXIS
"
"- It uses only G00, G01 and G02, even for drilling (G82 it's not backlash compensated in KCam)
"
"- If you want holes with complicated shapes in board, use a polygon in dimension layer, with width of drill used to cut the board.
"
"- If Drill is checked, all holes will be drilled.
"
"- If Cut is checked, all holes (from pads, vias etc.) larger than cut_tool will be milled.
"
"
- If you do not have a file called gcode.set next to this ulp, at first run, comment #include gcode.set and uncomment folowing definitions.
"
"
"
"
"
"Things left to be implemented: *sort all elements for minimum travel time; *implement fill function
"
"
"
"Author: eSilviu
"
"Work time: 3 nights
"
//include... OR uncomment below (only at first run)
#include "gcode.set"
/*
int mill_on =1;
int fill_on =1;
real mill_tool =0.5;
real mill_down =0.2;
real mill_up =0.2;
real mill_speed =200;
int drill_on =1;
real drill_down =0.2;
real drill_up =0.2;
real drill_speed =200;
int cut_on =1;
real cut_depth =2.5;
int cut_steps =2;
real cut_gap =3;
real cut_speed =100;
real cut_tool =1;
real cut_up =1;
real tool_up =30;
int layer =-1;*/
string Layers[] = {"Top","Bottom"};
int Selected_layer;
if (layer==1) Selected_layer=0;
if (layer==-1) Selected_layer=1;
string Steps[] = { "1", "2", "3", "4" };
int Sel_Cut;
Sel_Cut=cut_steps-1;
int step_ulp = 1;
string fileName;
string OutlinesSignalName = "_OUTLINES_";
string final_cmd;
if (board) board(B) {fileName = filesetext(B.name, ".gc");}
else {dlgMessageBox("\n Start this ULP in a Board \n"); exit (0);}
//parameters passed to ulp at second run
if (argv[1])
{if (argv[2])
{mill_tool = strtod(argv[2]);
if (argv[3])
{layer = strtol(argv[3]);
if (argv[4])
{fileName = argv[4];
if(argv[5])
{step_ulp = 2;
}}}}}
//*************************** Functions body ***************************
void write_real_param(string name, real value)
{ printf("real %s = %f;\n", name, value); }
void write_int_param(string name, int value)
{ printf("int %s = %d;\n", name, value); }
//error message if exist a signal named _OUTLINES_
void Fatal(string Message, string Details)
{ dlgMessageBox(usage + "
ERROR: " + Message + "\n" + Details); exit(1); }
//Z->up; stop; Z->0.00 (calibrate Z axis) Z->0.50, ready for start
void DeviceChangeBit(void)
{ output(fileName, "at")
{printf ("\nG00 Z%2.3f\n",tool_up);
printf ("G00 X0.000 Y0.000\n");
printf ("M00\n");
printf ("G00 Z0.000\n");
printf ("M00\n");
printf ("G00 Z0.500\n");
printf ("\n(Bit changed, zero set.)\n");
}}
//create file for gcode output, write first lines
void DeviceInit(void)
{output(fileName, "wt")
{printf ("(%s)\n(%s)\n(%s)\n", fileName, Layers[Selected_layer], t2string(time()));
if (layer==-1) printf ("(X/Y/Z home should be at upper left corner of the board, with the tip just touching board)\n");
else printf ("(X/Y/Z home should be at lower left corner of the board, with the tip just touching board)\n");
printf ("(Mm, Absolute mode, lift cutter above work, rapid to X/Y home, spindle on.)\n");
printf ("G21\n");
printf ("G90\n");
}}
//write the necessary stuff for machine STOP
void DeviceEnd(void)
{ output(fileName, "at")
{printf("(Finished.)\n");
printf("G00 Z%.3f\n",tool_up);
printf("M05\n");
printf("G00 X0 Y0\n");
printf("M30\n");
}}
void CutDrillHole(real h_x, real h_y, real h_d, real tool, real cut_depth)
{ real raza=(h_d-tool)/2;
real cut_down=0;
for (int i = 0; i tool)
{printf("G00 X%.3f Y%.3f\n", h_x, (h_y+raza)*layer);
printf("G01 Z-%.3f F%.1f\n",cut_down, cut_speed);
printf("G02 X%.3f Y%.3f R%3f F%.1f\n", h_x+raza*layer, h_y*layer, raza, cut_speed);
printf("G02 X%.3f Y%.3f R%3f F%.1f\n", h_x, (h_y-raza)*layer, raza, cut_speed);
printf("G02 X%.3f Y%.3f R%3f F%.1f\n", h_x-raza*layer, h_y*layer, raza, cut_speed);
printf("G02 X%.3f Y%.3f R%3f F%.1f\n", h_x, (h_y+raza)*layer, raza, cut_speed);
printf("G00 Z%.3f\n\n", cut_up);
}}}
//add a hole to the output file
void AddHole(int x, int y)
{ real x_int, y_int;
x_int = u2mm(x);
y_int = u2mm(y);
printf("G00 X%.3f Y%.3f\n", x_int, y_int*layer);
printf("G01 Z-%.3f F%.1f\n", drill_down, cut_speed);
printf("G00 Z%.3f\n\n", drill_up);
}
//cut the board, as the wires in DIMENSION layer says; gap is used to keep board in place until end
void DeviceCut(void)
{ real a, b, c, d, gap1, gap2, cut_down, x_old=REAL_EPSILON, y_old=REAL_EPSILON;
gap1=(50-cut_gap/2)/100;
gap2=(50+cut_gap/2)/100;
cut_down=0;
output(fileName, "at")
{printf("(Start cutting holes)\n");
board(B)
{B.holes(H) CutDrillHole(u2mm(H.x), u2mm(H.y), u2mm(H.drill), cut_tool, cut_depth);
B.signals(S) S.vias(V) CutDrillHole(u2mm(V.x),u2mm(V.y),u2mm(V.drill),cut_tool, cut_depth);
B.elements(E)
{E.package.contacts(C) if (C.pad) CutDrillHole(u2mm(C.pad.x),u2mm(C.pad.y),u2mm(C.pad.drill),cut_tool, cut_depth);
E.package.holes(H) CutDrillHole(u2mm(H.x),u2mm(H.y),u2mm(H.drill),cut_tool, cut_depth);
}
printf("(Start cutting polygons)\n");
for (int i = 0; i=(pow(abs(u2mm(W.x2)-x_old),2)+pow(abs(u2mm(W.y2)-y_old),2)))
{a=u2mm(W.x2); b=u2mm(W.y2); c=u2mm(W.x1); d=u2mm(W.y1);}
else {a=u2mm(W.x1); b=u2mm(W.y1); c=u2mm(W.x2); d=u2mm(W.y2);};
printf("(x_old=%.3f; X=%.3f; y_old=%.3f; Y=%.3f)",x_old, a, y_old, b);
if ((x_old==a)&&(y_old==b)) {printf("G01 Z-%.3f F%.1f\n",cut_down, cut_speed);}
else {printf("G00 Z%.3f\n\n", cut_up);
printf("G00 X%.3f Y%.3f\n", a, b*layer);
printf("G01 Z-%.3f F%.1f\n",cut_down, cut_speed);};
if (abs(a-c)>=10 || abs(b-d)>=10)
{printf("G01 X%.3f Y%.3f F%.1f\n", a+(c-a)*gap1, (b+(d-b)*gap1)*layer, cut_speed);
printf("G00 Z%.3f\n", cut_up);
printf("G00 X%.3f Y%.3f\n", a+(c-a)*gap2, (b+(d-b)*gap2)*layer);
printf("G01 Z-%.3f F%.1f\n",cut_down, cut_speed);}
printf("G01 X%.3f Y%.3f F%.1f\n", c, d*layer, cut_speed);
x_old=c; y_old=d;
}}
printf("G00 Z%.3f\n\n", cut_up);
}}}}
//function that reads drill positions and call AddHole() function
void DeviceDrill(void)
{ output(fileName, "at")
{printf("(Start drilling)\n");
board(B)
{printf("G00 Z%.3f\n", drill_up);
B.holes(H) AddHole(H.x, H.y);
B.signals(S) S.vias(V) AddHole(V.x, V.y);
B.elements(E)
{E.package.contacts(C)
{if (C.pad)AddHole(C.pad.x, C.pad.y);}
E.package.holes(H) AddHole(H.x, H.y);
}}}}
//function that create a single poligon on selected layer
void GenerateOutlines(void)
{ board(B) {
real f = 0.1,
x1 = u2mm(B.area.x1) - f, y1 = u2mm(B.area.y1) - f,
x2 = u2mm(B.area.x2) + f, y2 = u2mm(B.area.y2) + f;
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.");
}
step_ulp=2;
string Cmd;
int displ_;
if (layer ==1) displ_=1;
if (layer ==-1) displ_=16;
sprintf(Cmd, "grid mm;\n"
"window fit;\n"
"change isolate 0;\n"
"change rank 6;\n"
"change pour solid;\n"
"change orphans on;\n"
"SET WIRE_BEND 2;\n"
"layer %d;\n"
"polygon %s %f (%f %f) (%f %f) (%f %f) (%f %f) (%f %f);\n"
"ratsnest;\n"
"run '%s' argv[1] '%f' '%d' '%s' '%d';",
displ_,
OutlinesSignalName, mill_tool, x1, y1, x2, y1, x2, y2, x1, y2, x1, y1,
argv[0], mill_tool, layer, fileName, step_ulp);
exit(Cmd);
}
}
//function that write outlines in output file
void DeviceDraw(int x1, int y1, int x2, int y2, int state_mill)
{ if (state_mill == 0)
{printf("G00 Z%.3f\n\n",mill_up);
printf("G00 X%.3f Y%.3f\n",u2mm(x1),layer*u2mm(y1));
printf("G01 Z-%f F%.1f\n",mill_down, cut_speed);
printf("G01 X%.3f Y%.3f F%.1f\n",u2mm(x2), layer*u2mm(y2), cut_speed);
}
else printf("G01 X%.3f Y%.3f F%.1f\n", u2mm(x2), layer*u2mm(y2), cut_speed);
}
//function that generate coordinates for outlines (it will be executed right after second run of the ulp)
void WriteOutlines(void)
{
output(fileName, "at") {
board(B){
B.signals(S) {
if (S.name == OutlinesSignalName) {
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);
}
P.contours(W) {
if (first) {
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) {
State = 2;
first = 1;
}
else
State = 1;
if (!FrameWire)
DeviceDraw(W.x1, W.y1, W.x2, W.y2, State);
}
sprintf(final_cmd, "delete (%f %f) (%f %f); window fit;\n", u2mm(x1), u2mm(y1), u2mm(x2), u2mm(y2));
}
break;
}}}}}
void Fill(void)
{real old_x=REAL_EPSILON, old_y=REAL_EPSILON, a, b, c, d;
output(fileName, "at") {
board(B){
B.signals(S) {
if (S.name == OutlinesSignalName) {
S.polygons(P) {
int x1 = INT_MAX, y1 = INT_MAX, x2 = INT_MIN, y2 = INT_MIN;
P.wires(W) {
x1 = min(x1, W.x1);
x2 = max(x2, W.x1);
y1 = min(y1, W.y1);
y2 = max(y2, W.y1);
}
printf("\nStart filling empty spaces\n");
P.fillings(W) {
if ((pow(abs(W.x1-old_x),2)+pow(abs(W.y1-old_y),2))>=(pow(abs(W.x2-old_x),2)+pow(abs(W.y2-old_y),2)))
{a=W.x2; b=W.y2; c=W.x1; d=W.y1;}
else {a=W.x1; b=W.y1; c=W.x2; d=W.y2;}
DeviceDraw(a, b, c, d, 0);
old_x=c; old_y=d;
}
if (!mill_on) sprintf(final_cmd, "delete (%f %f) (%f %f); window fit;\n", u2mm(x1), u2mm(y1), u2mm(x2), u2mm(y2));
}
}}}}}
//---------------------------------GRAPHICAL INTERFACE, will appear only at first run of the ulp----------------------------------------------
if (step_ulp==1)
{ int Result = dlgDialog("Select Parameters")
{dlgHBoxLayout
{dlgLabel("Layer: ");
dlgComboBox(Layers, Selected_layer) {if (Selected_layer==0) layer=1; if (Selected_layer==1) layer=-1;};
dlgStretch(1);
}
dlgHBoxLayout {
dlgGroup("Mill") {
dlgGridLayout {dlgCell(0, 0) dlgCheckBox("&Active", mill_on); dlgCell(0, 1) dlgCheckBox("Clear unused area", fill_on);}
dlgGridLayout {
dlgCell(0, 0) dlgLabel("&Tool");
dlgCell(0, 1) dlgRealEdit(mill_tool, 0.1, 50);
dlgCell(1, 0) dlgLabel("&Depth");
dlgCell(1, 1) dlgRealEdit(mill_down, 0, 50);
dlgCell(2, 0) dlgLabel("&Z up");
dlgCell(2, 1) dlgRealEdit(mill_up, 0, 50);
dlgCell(3, 0) dlgLabel(" ");
dlgCell(4, 0) dlgLabel("&Speed");
dlgCell(4, 1) dlgRealEdit(mill_speed, 0, 500);
}}
dlgVBoxLayout {
dlgGroup("Drill") {
dlgCheckBox("&Active", drill_on);
dlgGridLayout {
dlgCell(0, 0) dlgLabel("&Depth");
dlgCell(0, 1) dlgRealEdit(drill_down, 0, 50);
dlgCell(1, 0) dlgLabel("&Z up");
dlgCell(1, 1) dlgRealEdit(drill_up, 0, 50);
dlgCell(2, 0) dlgLabel("&Speed");
dlgCell(2, 1) dlgRealEdit(drill_speed, 0, 500);
}}
dlgGroup("Misc") {
dlgGridLayout {
dlgCell(0, 1) dlgLabel("&Tool up");
dlgCell(0, 2) dlgRealEdit(tool_up, 0, 50);
dlgCell(0, 3) dlgStretch(500);
}}}
dlgGroup("Cut") {
dlgCheckBox("&Active", cut_on);
dlgGridLayout {
dlgCell(0, 0) dlgLabel("&Tool");
dlgCell(0, 1) dlgRealEdit(cut_tool, 0.1, 50);
dlgCell(1, 0) dlgLabel("&Depth");
dlgCell(1, 1) dlgRealEdit(cut_depth, 0, 50);
dlgCell(2, 0) dlgLabel("&Steps");
dlgCell(2, 1) dlgComboBox(Steps, Sel_Cut) {cut_steps = Sel_Cut+1;};
dlgCell(3, 0) dlgLabel("&Gap [%]");
dlgCell(3, 1) dlgRealEdit(cut_gap, 0, 50);
dlgCell(4, 0) dlgLabel("&Z up");
dlgCell(4, 1) dlgRealEdit(cut_up, 0, 50);
dlgCell(5, 0) dlgLabel("&Speed");
dlgCell(5, 1) dlgRealEdit(cut_speed, 0, 500);
}}}
dlgHBoxLayout {
dlgLabel("Output file:");
dlgStringEdit(fileName);
dlgPushButton("Bro&wse") {string fn = dlgFileSave("Save Output file", fileName);if (fn) fileName = fn;}
}
dlgSpacing(10);
dlgHBoxLayout {
dlgLabel("Note: All dimension are in milimeters. Speed is [mm/min]");
dlgStretch(1);
dlgPushButton("+OK") dlgAccept(1);
dlgPushButton("Cancel") { dlgReject(0); exit(1); }
}
} ;
//save parameters in gcode.set file
if (Result==1)
{ fileerror();
output(path_ulp[0] + "/gcode.set", "wt")
{printf("//List of usual parameters:\n");
write_int_param("mill_on", mill_on);
write_int_param("fill_on", fill_on);
write_real_param("mill_tool", mill_tool);
write_real_param("mill_down", mill_down);
write_real_param("mill_up", mill_up);
write_real_param("mill_speed", mill_speed);
write_int_param("drill_on", drill_on);
write_real_param("drill_down", drill_down);
write_real_param("drill_up", drill_up);
write_real_param("drill_speed", drill_speed);
write_int_param("cut_on", cut_on);
write_real_param("cut_depth", cut_depth);
write_int_param("cut_steps", cut_steps);
write_real_param("cut_tool", cut_tool);
write_real_param("cut_up", cut_up);
write_real_param("cut_gap", cut_gap);
write_real_param("cut_speed", cut_speed);
write_real_param("tool_up", tool_up);
write_int_param("layer", layer);
}
if (fileerror()) {dlgMessageBox("Save param. error"); exit(1);};
} }
//milling part require two run of the ulp
if (mill_on==1 || fill_on==1)
{ if (step_ulp==1)
{DeviceInit();
GenerateOutlines();
}
if (step_ulp==2)
{
if (mill_on) {DeviceChangeBit(); WriteOutlines();}
if (fill_on) {DeviceChangeBit(); Fill();}
if (drill_on==1) {DeviceChangeBit(); DeviceDrill();}
if (cut_on==1) {DeviceChangeBit(); DeviceCut();}
DeviceEnd();
dlgMessageBox(";Succes!");
exit(final_cmd);
}
}
//if no mill is necessary, ulp will run only once
if (mill_on==0)
{ DeviceInit();
if (drill_on==1) {DeviceChangeBit(); DeviceDrill();}
if (cut_on==1) {DeviceChangeBit(); DeviceCut();}
DeviceEnd();
dlgMessageBox(";Succes!");
}