#usage "Export outlines data\n" "

" "Generates outlines data for a board layout." "

" "Usage: RUN outlines [ device [ width [ layer [ filename ]]]]" "

" "" "" "" "" "" "
device:Script | HPGL
width:outlines width [mm]
layer:1..16 (0 = active layer)
filename:output file name
" "

" "Author: support@cadsoft.de" // THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED /* 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()' */ 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 // string DefaultSuffix = ".out"; string Device; real Width = 0; int Layer = 0; string FileName; 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 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 }; string DeviceNames[] = { "Select a device", "Script", "HPGL" }; int SelectedDevice; void DeviceInit(void) { // Do anything necessary to initialize the output device switch (SelectedDevice) { case devScript: // TODO make the layer user definable? printf("layer %d;\nset wire_bend 2; grid mm; change width %f;\n", Layer + 100, Width); break; case devHPGL: break; } } 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 switch (SelectedDevice) { case devScript: if (state == 0) printf("wire (%f %f) (%f %f)", u2mm(x1), u2mm(y1), u2mm(x2), u2mm(y2)); else { printf(" (%f %f)", u2mm(x2), u2mm(y2)); if (state == 2) printf(";\n"); } break; case devHPGL: if (state == 0) printf("PU; PA %f %f; PD; PA %f %f;", u2mm(x1), u2mm(y1), u2mm(x2), u2mm(y2)); else { printf(" PA %f %f", u2mm(x2), u2mm(y2)); if (state == 2) printf("\n"); } break; } } void DeviceEnd(void) { // Do anything necessary to end output to the device switch (SelectedDevice) { case devScript: break; case devHPGL: printf("PU;\n"); break; } } // // The actual outlines generator // string OutlinesSignalName = "_OUTLINES_"; string Pass2 = "PASS_2"; int InPass2 = argv[5] == Pass2; void GenerateOutlines(void) { board(B) { real f = 20, // mm frame 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); 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."); } } if (ForceDialog) { dlgDialog("Outlines Generator") { dlgGridLayout { dlgCell(0, 0) dlgLabel("&Device"); dlgCell(0, 1) dlgComboBox(DeviceNames, SelectedDevice) { // TODO should we generalize this? if (FileName && SelectedDevice == devScript) FileName = filesetext(FileName, ".scr"); } dlgCell(1, 0) dlgLabel("&Width"); dlgCell(1, 1) dlgRealEdit(Width, 0, 10); dlgCell(2, 0) dlgLabel("&Layer"); dlgCell(2, 1) dlgComboBox(Layers, SelectedLayer); } dlgHBoxLayout { dlgLabel("&Output file"); dlgStringEdit(FileName); dlgPushButton("&Browse") { string fn = dlgFileSave("Save Outlines file", FileName); if (fn) FileName = fn; } } dlgStretch(1); dlgHBoxLayout { dlgStretch(1); dlgPushButton("+Ok") { if (!SelectedDevice) Error("No device selected!", "Please select a device."); else if (!Width) Error("Illegal width: 0", "The width must be greater than zero."); else dlgAccept(); } dlgPushButton("-Cancel") { dlgReject(); exit(1); } dlgPushButton("About") dlgMessageBox(usage); } }; } Device = DeviceNames[SelectedDevice]; Layer = strtol(Layers[SelectedLayer]); GenerateOutlines(); } else WriteOutlines();