#usage "Outlines Generator with GCode support using mm

\n" "Generates outlines data for a board layout. You can use this .ulp " "to generate etching paths for making PCB on a CNC milling machine

\n" "Usage: outlines [ device [ width [ layer [ filename ]]]]

" "\n" "\n" "\n" "\n" "\n" "
device:Script | HPGL | GCode
width:outlines width [mm]
layer:1..16 (0 = active layer)
filename:output file name
\n" " Modified from: outlines_gcode.ulp " " by: profmanoel@barretos.com.br<\author> (January, 29th, 2002) " " Besides the devices listed below, another called Scatter was introduced " " and a beta test for reversing the bottom layer. " " There is a Global variable called YLIMIT which is set to 4.0 mms " " Every time an Y coodinate value is to be adjusted it verifies if it is " " bottom layer. If so, the new Y value is taken by Ynew = YLIMITE - Y " " Feel free to send suggestions and comments " "Author: joechiu@joechiu.com

" "Modified from outlines.ulp originally by support@cadsoft.de

\n" "Changes include:\n" "

\n" "Changes not yet implemented:\n" "

\n" /* Complete the following steps to add a new output device definition: 1. Add a new member to the 'enum { devScript = 1, ...' 2. Add the new (unique!) device name to 'DeviceNames[]' 3. Add the necessary 'case dev...' branches to 'DeviceInit()', 'DeviceDraw()' and 'DeviceEnd()' */ ////////////////////////////////////////////////////// //////////////////// ユーザー設定 //////////////////// ////////////////////////////////////////////////////// string DefaultSuffix = "_bot.tap"; //お使いのCNC似合わせた拡張子に変更してください real Width = .3; //実際に削る線幅を指定してください real Lift = 1; //空中移動時の高さを指定してください real Depth = .1; //実際に削る深さを指定してください int Z_F_value = 25; //Z軸のF値を指定してください int F_value = 200; //XY軸のF値を指定してください /////////////////////////////////////////////////////// ////////////////////// ここまで /////////////////////// /////////////////////////////////////////////////////// void Fatal(string Message, string Details) { dlgMessageBox(usage + "


ERROR: " + Message + "

\n" + Details); exit(1); } void Error(string Message, string Details) { dlgMessageBox("ERROR: " + Message + "

\n" + Details); } // // Parmameters // // Global YLIMIT for New Y origin real YLIMIT = 0.0; string FileName; string Device = "GCode"; int Layer = 16; if (!board) Fatal("No board!", "This program can only work in the board editor."); if (argv[1]) { Device = argv[1]; if (argv[2]) { Width = strtod(argv[2]); if (Width <= 0) Fatal("Illegal width: " + argv[2], "The width (in mm) must be greater than zero."); if (argv[3]) { Layer = strtol(argv[3]); if (Layer < 0 || Layer > 16) Fatal("Illegal layer: " + argv[3], "The layer must be one of 1..16 or 0 to use the current layer."); if (argv[4]) { FileName = argv[4]; } } } } if (!FileName) board(B) FileName = filesetext(B.name, DefaultSuffix); // // The various output devices // enum { devScript = 1, devHPGL, devGCode,devScatter }; string DeviceNames[] = { "Select a device", "Script", "HPGL", "GCode","Scatter"}; int SelectedDevice; void DeviceInit(void) { // Do anything necessary to initialize the output device printf ("(This GCode generated with outlines_gcode_excel_mirrored.ulp program)\n"); printf ("(running under Eagle CAD.)\n"); printf ("(Use with mechanical etching/engraving bits, such as the)\n"); printf ("(Think&Tinker Mechanical Etching Bit with 60-degree tip angle)\n"); printf ("(X/Y/Z home should be at lower left corner of the board, with the tip just touching)\n"); printf ("(%s layer %d)\n", FileName, Layer); printf ("\n"); printf ("(mm, Absolute mode, lift cutter above work, rapid to X/Y home, turn spindle on.)\n"); // printf ("G20\n"); printf ("G90\n"); printf ("G0Z%2.3f\n",Lift); // printf ("G0Z0.23\n"); printf ("G0X0Y0\n"); printf ("M03\n"); printf ("\n"); printf ("(Begin)\n"); } 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 if (state == 0) { printf("G0Z2\n"); if ( Layer == 16 ) { // Check if bottom layer printf("G0X%fY%f\n",(u2mm(x1)),YLIMIT - u2mm(y1)); printf("G1Z-%2.3f f%d\n",Depth, Z_F_value); // printf("G1Z-0.007f25\n"); printf("G1X%fY%f f%d\n",(u2mm(x2)), YLIMIT - u2mm(y2), F_value); } else { printf("G0X%fY%f\n",u2mm(x1),u2mm(y1)); printf("G1Z-%2.3f f%d\n",Depth, Z_F_value); // printf("G1Z-0.007f25\n"); printf("G1X%fY%f f%d\n",u2mm(x2), u2mm(y2), F_value); } } else { if ( Layer == 16 ) // Check if bottom layer printf("G1X%fY%f\n", (u2mm(x2)), YLIMIT - u2mm(y2)); else printf("G1X%fY%f f%d\n",u2mm(x2), u2mm(y2), F_value); if (state == 2) printf("\n"); } } void DeviceEnd(void) { // Do anything necessary to end output to the device printf("(Finished. Raise cutter, turn spindle off, reset.)\n"); printf ("G0Z%2.3f\n",Lift); // printf("G0Z0.23\n"); printf ("G0X0Y0\n"); printf("M05\n"); printf("M30\n"); } // // The actual outlines generator // string OutlinesSignalName = "_OUTLINES_"; string Pass2 = "PASS_2"; int InPass2 = argv[5] == Pass2; void GenerateOutlines(void) { board(B) { real f = 0.1, // mm frame - 100 mil offset 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."); } string Cmd; sprintf(Cmd, "grid mm;\n" "window fit;\n" "change isolate 0;\n" "change rank 6;\n" "change pour solid;\n" "change orphans on;\n" "layer %d;\n" "polygon %s %f (%f %f) (%f %f) (%f %f) (%f %f) (%f %f);\n" "ratsnest;\n" "run '%s' '%s' '%f' '%d' '%s' '%s';", Layer, OutlinesSignalName, Width, x1, y1, x2, y1, x2, y2, x1, y2, x1, y1, argv[0], Device, Width, Layer, FileName, Pass2); exit(Cmd); } } void WriteOutlines(void) { board(B) { output(FileName) { string Cmd; 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); } DeviceInit(); 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); } DeviceEnd(); sprintf(Cmd, "delete (%f %f) (%f %f); window fit;\n", u2mm(x1), u2mm(y1), u2mm(x2), u2mm(y2)); } break; } } exit(Cmd); } } } // // Main program: // if (Device) { int n; while (DeviceNames[n]) { if (strupr(DeviceNames[n]) == strupr(Device)) { SelectedDevice = n; break; } n++; } if (!SelectedDevice) Fatal("Illegal device: " + Device, "Please select one of the known devices."); } if (!InPass2) { string Layers[]; int SelectedLayer = -1; // int ForceDialog = (!Device || !Width); int ForceDialog = 1; board(B) { 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."); } } Device = DeviceNames[SelectedDevice]; Layer = strtol(Layers[SelectedLayer]); GenerateOutlines(); } else WriteOutlines();