// // Generate g-code for milling PC boards. // // Copyright 2004-2013 by John Johnson Software, LLC. // See readme.html for copyright information. // // See pcb-defaults.h, gcode-defaults.h and pcb-machine.h for options. // #include "source/pcb-gcode.h" #include "source/pcb-gcode-stack.h" #include "settings/pcb-defaults.h" #include "settings/pcb-machine.h" #include "settings/gcode-defaults.h" #include "settings/pcb-gcode-options.h" #include "settings/user-gcode.h" #include "source/math.h" #include "source/drill.h" #include "source/library.h" #include "source/pcb-file-utils.h" #include "source/filename_subs.h" #usage "
pcb-gcode™ Gcode Generation Utility
" " Version 3.6.0.4

" "Copyright© 2004 - 2013 by John Johnson Software, LLC
" "All Rights Reserved

" "

" "Join the Yahoo! pcb-gcode group " "http://groups.yahoo.com/group/pcb-gcode" "
or contact the author at pcbgcode@pcbgcode.org" "
" "


" "This program generates g-code for 'mechanically etching' PC boards. " "Using a CNC router or milling machine, you can make PC boards without using etching chemicals.

" "It will create files for the outlines of tracks, drilling holes, and milling cutouts in the board." "
See the readme.html file for more info.

" "There are many options in the setup program.
" "To setup which files are generated, and how, use the following command:
" "run pcb-gcode --setup

" "

" "If you want to produce a set of files without changing options, you can run pcb-gcode directly." "Usage is as follows:
" "run pcb-gcode [option] [file]
" "Where:\n" "" "" "" "" "" "" "
OptionFunction
--helpShow this help screen
--setupRun the setup / configuration program
fileIs the optional root filename.\n
If not given, the board name is used as the root filename.
" "" "" "


" g_width = DEFAULT_WIDTH; // Filename to output to. string m_file_name = ""; // Which side we're currently working on. g_side = TOP; // Tool currently being used. int m_current_tool = 0; // Amount to isolate traces. real m_isolate = 0.0; // Used to pass the pass info to the next pass of the program. int m_pass_num; // The layer being worked on. int m_layer; // Used to create command strings. string g_cmd; string g_cmd_temp; // Save the maximum and minimum coordinates. real m_max_x; real m_max_y; real m_min_x; real m_min_y; void save_extents(real x, real y) { m_max_x = max(m_max_x, x); m_max_y = max(m_max_y, y); m_min_x = min(m_min_x, x); m_min_y = min(m_min_y, y); } void preview() { if (SHOW_PREVIEW) { switch(get_os()) { case OS_MACOSX: system(g_path + "/viewer/application.macosx/viewer.app/Contents/MacOS/JavaApplicationStub"); break; case OS_LINUX: system(g_path + "/source/viewer.linux.sh"); break; case OS_WINDOWS: system(g_path + "/viewer/application.windows/viewer.exe"); dlgMessageBox("Close this window when you have finished with the preview"); break; default: Fatal("Oops!", "Can't figure out which OS you have."); } } } // // Determine the next phase, and set g_phase accordingly. // // Params: none // Return: none // void next_phase(void) { if (g_phase == PH_TOP_OUT_GEN && GENERATE_TOP_OUTLINES != YES) { g_phase += 2; return; } if (g_phase == PH_BOTTOM_OUT_GEN && GENERATE_BOTTOM_OUTLINES != YES) { g_phase += 2; return; } if (g_phase == PH_TOP_OUT_WRITE || g_phase == PH_BOTTOM_OUT_WRITE) { if (m_isolate < ISO_MAX && !SINGLE_PASS) { m_isolate += ISO_STEP; m_pass_num++; g_phase--; } else { m_isolate = ISO_MIN; m_pass_num = 0; g_phase++; preview(); } } else { g_phase++; } } ////////////////////////////////////////////////// // // Hole drilling routines. // ////////////////////////////////////////////////// // // Add a hole drilling command to the command string. // // Params: // Size Drill size. // x x coordinate. // y y coordinate. // Returns: // none // Changes: // the stack // void add_hole(int req_size, int x, int y) { string tempstr; int drill_size; drill_size = get_drill_for(req_size); sprintf(tempstr, "%06d\t%06d\t%06d", drill_size, x, y); stack_push(tempstr); } /* * Create a drill file for the desired side. * * Params: * which_side Side to produce the file for. * suffix Suffix to use for the file name (i.e. bd). * Returns: * none * Changes: * side */ void drill(int which_side, string suffix) { int num_lines; int i; real last_size; string drill_args[]; m_current_tool = 0; real drill_size_mm; real drill_size_inch; real drill_size; real drill_x; real drill_y; int drill_tool_num; g_side = which_side; // // Build a stack with all the holes. // board(B) { B.holes(H) add_hole(H.drill, H.x, H.y); B.signals(S) S.vias(V) add_hole(V.drill, V.x, V.y); B.elements(E) { E.package.contacts(C) { if (C.pad) { add_hole(C.pad.drill, C.pad.x, C.pad.y); } } E.package.holes(H) add_hole(H.drill, H.x, H.y); } // B.elements // Sorts the drills by size, x and y. stack_sort(); // // Create the drill g-code file. // last_size = -1; output(get_filename(), "wt") { output_file_preamble(); // // Create a tool table at the beginning of the file. // out(TOOL_CHANGE_TABLE_HEADER); for (i=stack_fwd_iter(); i != END_OF_STACK; i=stack_fwd_next()) { if (i==END_OF_STACK) break; strsplit(drill_args, stack_elem(i), '\t'); drill_size_inch = u2inch(my_strtol(drill_args[DRILL_SIZE])); drill_size_mm = u2mm(my_strtol(drill_args[DRILL_SIZE])); drill_size = my_strtol(drill_args[DRILL_SIZE]); if (drill_size != last_size) { ++m_current_tool; drill_tool_num = get_tool_num_for(my_strtol(drill_args[DRILL_SIZE]), m_current_tool); out(TOOL_CHANGE_TABLE_FORMAT(drill_tool_num, drill_size_mm, drill_size_inch, g_mins[drill_tool_num], g_maxs[drill_tool_num], g_drill_sub_cnt[drill_tool_num])); } last_size = drill_size; } // for // Set the mode for the user's UOM. out(get_mode()); out(ABSOLUTE_MODE); // // Generate drill code. // m_current_tool = 0; for (i=stack_fwd_iter(); i != END_OF_STACK; i=stack_fwd_next()) { strsplit(drill_args, stack_elem(i), '\t'); drill_size = units(my_strtol(drill_args[DRILL_SIZE])); drill_x = scale_x(my_strtol(drill_args[DRILL_X])); drill_y = scale_y(my_strtol(drill_args[DRILL_Y])); save_extents(drill_x, drill_y); if (drill_size == last_size) { output_drill_hole(drill_x, drill_y, DRILL_DEPTH); } else { m_current_tool++; // Tool change routine output_tool_change_begin(); out(SPINDLE_OFF); rz(TOOL_CHANGE_POS_Z); rxy(TOOL_CHANGE_POS_X, TOOL_CHANGE_POS_Y); out(fir(TOOL_CHANGE, get_tool_num_for(my_strtol(drill_args[DRILL_SIZE]), m_current_tool), drill_size)); output_tool_changed(); if (DO_TOOL_CHANGE_WITH_ZERO_STEP == YES) { output_tool_zero_begin(); fzr(0.000, FEED_RATE_Z); // fixme: wondering about the following line out(fir(TOOL_CHANGE, m_current_tool, drill_size)); output_tool_zero_end(); } rz(DEFAULT_Z_UP); out(fr(SPINDLE_ON, SPINDLE_ON_TIME)); output_tool_change_end(); output_drill_first_hole(drill_x, drill_y, DRILL_DEPTH); last_size = drill_size; } // if drill_size != last_size } // for output_file_postamble(); // End of file // change back to tool 1 out(fi(TOOL_CODE + EOL, 1)); end_gcode(); } // output } // Board } // drill int m_first_spot_drill = YES; void spot_drill_hole(int x, int y) { real drill_x; real drill_y; drill_x = scale_x(x); drill_y = scale_y(y); save_extents(drill_x, drill_y); if (m_first_spot_drill) { output_drill_first_hole(drill_x, drill_y, SPOT_DRILL_DEPTH); m_first_spot_drill = NO; } else { output_drill_hole(drill_x, drill_y, SPOT_DRILL_DEPTH); } } void spot_drill(UL_BOARD B) { if (SPOT_DRILL == NO) { return; } B.holes(H) spot_drill_hole(H.x, H.y); B.signals(S) S.vias(V) spot_drill_hole(V.x, V.y); B.elements(E) { E.package.contacts(C) { if (C.pad) { spot_drill_hole(C.pad.x, C.pad.y); } } E.package.holes(H) spot_drill_hole(H.x, H.y); } // B.elements } string m_lines; string LINE_SEP = "\n"; string COORD_SEP = ","; string COORD_FMT = "%8.5f"; string coords(real x1, real y1, real x2, real y2) { return frrrr(COORD_FMT + COORD_SEP + COORD_FMT + COORD_SEP + COORD_FMT + COORD_SEP + COORD_FMT + LINE_SEP, x1, y1, x2, y2); } // // "Draw" on the output device, i.e. the gcode file. // // Params: // x1,y1 Start of the line. // x2,y2 End of the line. // state // z_down // Returns: // none // real m_arc_begin_x; real m_arc_begin_y; int pair_count = 0; int MAX_COORDS_PER_LINE = 4; void device_draw(int x1, int y1, int x2, int y2, int state, real z_down) { real rx1, ry1, rx2, ry2; rx1 = scale_x(x1); ry1 = scale_y(y1); rx2 = scale_x(x2); ry2 = scale_y(y2); save_extents(rx1, ry1); save_extents(rx2, ry2); // Output g-code based on the current state. switch(state) { // Start of a new line. case ST_START_LINE: user_track_begin(rx1, ry1, rx2, ry2); m_lines += coords(rx1, ry1, rx2, ry2); rz(DEFAULT_Z_UP); rxy(rx1, ry1); fzr(z_down, FEED_RATE_Z); fxyr(rx2, ry2, FEED_RATE); pair_count = 0; break; // A fill line. case ST_FILL: Fatal("Programmer Error", "The Fill functions are no longer supported."); break; // Continue a line. // End a line. case ST_CONTINUE_LINE: user_track_continue(rx1, ry1, rx2, ry2); m_lines += coords(rx1, ry1, rx2, ry2); if (COMPACT_GCODE == YES) { if (pair_count == 0) { fxy(rx2, ry2); } else { xy(rx2, ry2); } pair_count++; } else { fxy(rx2, ry2); } break; case ST_END_LINE: user_track_end(rx1, ry1, rx2, ry2); m_lines += coords(rx1, ry1, rx2, ry2); if (COMPACT_GCODE == YES) { xy(rx2, ry2); pair_count = 0; } else { fxy(rx2, ry2); } break; // Drill a hole. // todo is this valid? case ST_DRILL: printf("cause an error if used %d"); // printf("drill (%f, %f)\n", rx2, ry2); break; // Create an arc. case ST_ARC_BEGIN: user_arc_begin(rx1, ry1, rx2, ry2); m_arc_begin_x = rx1; m_arc_begin_y = ry1; break; // Finish an arc. case ST_ARC_END: user_arc_end(rx1, ry1, rx2, ry2); real cx = rx2; real cy = ry2; real end_x = rx1; real end_y = ry1; if (1 /* USE_IJ_RELATIVE */) { cx = rx2 - rx1; cy = ry2 - ry1; } rz(DEFAULT_Z_UP); rxy(end_x, end_y); fzr(z_down, FEED_RATE_Z); if (g_side == TOP || MIRROR_BOTTOM == YES) { out(frrrr(CIRCLE_TOP, m_arc_begin_x, m_arc_begin_y, cx, cy)); } else { out(frrrr(CIRCLE_BOTTOM, m_arc_begin_x, m_arc_begin_y, cx, cy)); } break; } // switch(state) } // device_draw // // Generate polygons for outline or fill. // This creates a series of commands for Eagle and puts them // in g_cmd to be passed when we exit from this phase of pcb-gcode. // // Params: // which_side The side to operate on. // Return: // none // void generate_outlines(int which_side) { string cmd_temp = ""; g_cmd = ""; if (which_side == TOP) m_layer = TOP_LAYER; else m_layer = BOTTOM_LAYER; board(B) { real f = BORDER_SIZE; real x1 = units(B.area.x1) - f; real y1 = units(B.area.y1) - f; real x2 = units(B.area.x2) + f; real y2 = units(B.area.y2) + f; B.signals(S) { if (S.name == OUTLINES_SIGNAL_NAME) { sprintf(cmd_temp, "delete (%f %f) (%f %f);\n", x1, y1, x2, y2); g_cmd = g_cmd + cmd_temp; } } sprintf(cmd_temp, "grid %s;\n" "change isolate %f;\n" "change rank 6;\n" "change pour solid;\n" "change width %f;\n" "change orphans on;\n" "layer %d;\n" "polygon %s %f (%f %f) (%f %f) (%f %f) (%f %f) (%f %f);\n" "ratsnest;\n" , UNIT_OF_MEASURE, m_isolate, g_width, m_layer, OUTLINES_SIGNAL_NAME, g_width, x1, y1, x2, y1, x2, y2, x1, y2, x1, y1 ); g_cmd = g_cmd + cmd_temp; return; } // board(B) } // generate_outlines void out_lines(string path, string mode) { output(path, mode) { if (m_pass_num == 0) { printf("# board=%s\n", elided_path(get_filename(), 30)); printf("# tool size=%f\n", g_width); } printf("# pass=%d\n", m_pass_num + 1); printf(m_lines); } } // // Write text from the Milling layer to a text engraving file. // // Params: // file_name Output filename. // Return: // none // void output_text_code(string file_name) { int state; int old_x, old_y; board(B) { output(get_filename(), "wt") { user_file_opened(get_filename(), "wt"); output_file_preamble(); // Initialize the device (i.e. the output file). begin_gcode(); // Process all the wires. B.texts(T) T.wires(W) { if ((T.mirror && g_side == BOTTOM) || (! T.mirror && g_side == TOP)) { // If this is a text layer, check if we should continue // a line, or start a new one. if (W.layer == TEXT_LAYER) { if (W.x1 == old_x && W.y1 == old_y) { state = ST_CONTINUE_LINE; } else { state = ST_START_LINE; } // if this is an arc, it is treated specially. if (W.arc) { device_draw( W.arc.x1, W.arc.y1, W.arc.x2, W.arc.y2, ST_ARC_BEGIN, TEXT_DEPTH); device_draw( W.arc.x2, W.arc.y2, W.arc.xc, W.arc.yc, ST_ARC_END, TEXT_DEPTH); save_extents(W.x1, W.y1); save_extents(W.x2, W.y2); state = ST_START_LINE; } else { // Draw the line (i.e. output the gcode for the line). device_draw(W.x1, W.y1, W.x2, W.y2, state, TEXT_DEPTH); old_x = W.x2; old_y = W.y2; save_extents(W.x1, W.y1); save_extents(W.x2, W.y2); } // if (W.arc) } // if (W.layer == TEXT_LAYER) } // if (top and bottom check) } // T.wires output_file_postamble(); end_gcode(); user_file_closing(); } // output user_file_closed(get_filename(), "wt"); } // board g_width = abs(TEXT_DEPTH); out_lines(g_path + "/viewer/data/optimize_me.txt", "wt"); out_lines(g_path + "/viewer/applet/data/optimize_me.txt", "wt"); out_lines(g_path + "/viewer/application.macosx/data/optimize_me.txt", "wt"); out_lines(g_path + "/viewer/application.linux/data/optimize_me.txt", "wt"); out_lines(g_path + "/viewer/application.windows/data/optimize_me.txt", "wt"); preview(); m_lines = ""; } // output_text_code // // Write layer data to mill files. // // Params: // file_name Output filename. // Return: // none // void output_mill_code(string file_name) { int state; int old_x, old_y; board(B) { output(get_filename(), "wt") { user_file_opened(get_filename(), "wt"); output_file_preamble(); // Initialize the device (i.e. the output file). begin_gcode(); // Process all the wires. B.wires(W) W.pieces(P) { // If this is a mill layer, check if we should continue // a line, or start a new one. if (P.layer == MILL_LAYER) { if (P.x1 == old_x && P.y1 == old_y) { state = ST_CONTINUE_LINE; } else { state = ST_START_LINE; } // if this is an arc, it is treated specially. if (P.arc) { device_draw( P.arc.x1, P.arc.y1, P.arc.x2, P.arc.y2, ST_ARC_BEGIN, MILLING_DEPTH); device_draw( P.arc.x2, P.arc.y2, P.arc.xc, P.arc.yc, ST_ARC_END, MILLING_DEPTH); save_extents(P.x1, P.y1); save_extents(P.x2, P.y2); state = ST_START_LINE; } else { // Draw the line (i.e. output the gcode for the line). device_draw(P.x1, P.y1, P.x2, P.y2, state, MILLING_DEPTH); old_x = P.x2; old_y = P.y2; save_extents(P.x1, P.y1); save_extents(P.x2, P.y2); } // if (P.arc) } // if (P.layer == MILL_LAYER) } // pieces output_file_postamble(); end_gcode(); user_file_closing(); } // output user_file_closed(get_filename(), "wt"); } // board g_width = abs(MILLING_DEPTH); out_lines(g_path + "/viewer/data/optimize_me.txt", "wt"); out_lines(g_path + "/viewer/applet/data/optimize_me.txt", "wt"); out_lines(g_path + "/viewer/application.macosx/data/optimize_me.txt", "wt"); out_lines(g_path + "/viewer/application.linux/data/optimize_me.txt", "wt"); out_lines(g_path + "/viewer/application.windows/data/optimize_me.txt", "wt"); preview(); m_lines = ""; } // output_mill_code // // Write polygon lines to the file. // // Params: // which_side Side of the board to work on. // suffix Suffix of the file. // task // Return: // none // void write_outlines(int which_side, string suffix, int task) { string mode; int x1 = INT_MAX, y1 = INT_MAX, x2 = INT_MIN, y2 = INT_MIN; int x0, y0; int first = 1; int frame_wire; int state; if (m_pass_num == 0) { mode = "wt"; } else { mode = "at"; } g_side = which_side; switch (which_side) { case TOP: m_layer = TOP_LAYER; break; case BOTTOM: m_layer = BOTTOM_LAYER; break; case MILL: m_layer = MILL_LAYER; break; case TEXT: m_layer = TEXT_LAYER; break; default: sprintf(g_debug, "Illegal which_side %d in write_outlines", which_side); Fatal("Sorry...", g_debug); } if (m_layer == MILL_LAYER || m_layer == TEXT_LAYER) { if (which_side == MILL) { g_side = TOP; output_mill_code(m_file_name + "mt" + DEFAULT_EXTENSION); g_side = BOTTOM; output_mill_code(m_file_name + "mb" + DEFAULT_EXTENSION); return; } else { g_side = TOP; output_text_code(m_file_name + "tt" + DEFAULT_EXTENSION); g_side = BOTTOM; output_text_code(m_file_name + "tb" + DEFAULT_EXTENSION); return; } } // if m_layer == MILL_LAYER output(get_filename(), mode) { board(B) { user_file_opened(get_filename(), mode); int rubout_layer = -1; B.layers(L) { if (strstr(strlwr(L.name), "rubout") != -1) { if (strstr(strlwr(L.name), "top") != -1 && which_side == TOP) { rubout_layer = L.number; } else if (strstr(strlwr(L.name), "bot") != -1 && which_side == BOTTOM) { rubout_layer = L.number; } } } if (rubout_layer != -1) { B.wires(W) { if (W.layer == rubout_layer) { device_draw(W.x1, W.y1, W.x2, W.y2, ST_START_LINE, DEFAULT_Z_DOWN); device_draw(W.x1, W.y1, W.x2, W.y2, ST_END_LINE, DEFAULT_Z_DOWN); // fixme needed? } } } B.signals(S) { if (S.name == OUTLINES_SIGNAL_NAME) { S.polygons(P) { P.wires(W) { x1 = min(x1, W.x1); x2 = max(x2, W.x1); y1 = min(y1, W.y1); y2 = max(y2, W.y1); } if (m_pass_num == 0) { output_file_preamble(); begin_gcode(); } else { output_between_outline_passes(); } switch (task) { case TASK_OUTLINES: // P.contours jtj P.contours(W) { if (first) { x0 = W.x1; y0 = W.y1; frame_wire = (x0 == x1 || x0 == x2) && (y0 == y1 || y0 == y2); state = ST_START_LINE; first = 0; } else if (W.x2 == x0 && W.y2 == y0) { state = ST_END_LINE; first = 1; } else { state = ST_CONTINUE_LINE; } if (!frame_wire) { device_draw(W.x1, W.y1, W.x2, W.y2, state, DEFAULT_Z_DOWN); } } // P.contours(W) break; case TASK_FILL: P.fillings(W) { device_draw(W.x1, W.y1, W.x2, W.y2, ST_START_LINE, DEFAULT_Z_DOWN); } break; } // switch(task) if (m_isolate >= ISO_MAX || SINGLE_PASS || task == TASK_FILL) { output_file_postamble(); rz(DEFAULT_Z_UP); spot_drill(B); end_gcode(); } sprintf(g_cmd, "delete (%f %f) (%f %f);\n", units(x1), units(y1), units(x2), units(y2) ); } // polygons } // if(S.name == OUTLINES_SIGNAL_NAME) } // signals } // board user_file_closing(); } // output user_file_closed(get_filename(), mode); out_lines(g_path + "/viewer/data/optimize_me.txt", mode); out_lines(g_path + "/viewer/applet/data/optimize_me.txt", mode); out_lines(g_path + "/viewer/application.macosx/data/optimize_me.txt", mode); out_lines(g_path + "/viewer/application.linux/data/optimize_me.txt", mode); out_lines(g_path + "/viewer/application.windows/data/optimize_me.txt", mode); } // write_outlines /* string get_phase_name(int phase) { return PHASE_NAME[phase]; } */ void gen_progress_menu(int phase) { output(path_scr[0] + '/' + "source/pcb-gcode-prg.scr") { printf("MENU \\\n"); for(int i = 1; i < PH_LAST_PHASE; i++) { if (i == phase) { printf("--%s-- \\\n", get_phase_name(i)); // " (for TextMate) } else { printf(" %s \\\n", get_phase_name(i)); } } printf(";\n"); } } // Make sure a board is available to work on. // todo move towards bottom if (!board) { Fatal("No board!", "Please run this program from the board editor"); } // // Process command line arguments. // switch (OUTPUT_UNITS) { case U_MICRONS: UNIT_OF_MEASURE = "mic"; break; case U_MILLIMETERS: UNIT_OF_MEASURE = "mm"; break; case U_MILS: UNIT_OF_MEASURE = "mil"; break; case U_INCHES: UNIT_OF_MEASURE = "inch"; break; } if (argv[FILENAME_ARG] == "--setup") { exit("run pcb-gcode-setup"); } if (argv[FILENAME_ARG] == "--attr") { string attrs; board(B) { B.attributes(A) { attrs = attrs + A.name + "\t" + A.value + "\n"; } } dlgMessageBox(attrs); exit(0); } if (argv[FILENAME_ARG] == "--help") { int choice = dlgMessageBox(usage, "+Return to Eagle", "Run Setup Now"); switch (choice) { // return to eagle case 0: exit(0); break; // Run setup now case 1: exit("run pcb-gcode-setup"); break; } exit(0); } // Be sure a filename is specified. if (argv[FILENAME_ARG]) { m_file_name = argv[FILENAME_ARG]; if (filedir(m_file_name) == "") { board(B) m_file_name = filedir(B.name) + m_file_name; } } else { board(B) m_file_name = filesetext(B.name, ""); } if (FILENAMES_8_CHARACTERS) { m_file_name = filedir(m_file_name) + strsub(filename(m_file_name), 0, 5); } // Use a default width if one is not given. if (argv[WIDTH_ARG]) { g_width = strtod(argv[WIDTH_ARG]); } else { g_width = DEFAULT_WIDTH; } // Get the isolate parameter or use the default. if (argv[ISO_ARG]) { m_isolate = strtod(argv[ISO_ARG]); } else { m_isolate = ISO_MIN; } // Get the pass number, or default to first pass (0). if (argv[PASS_ARG]) { m_pass_num = strtol(argv[PASS_ARG]); } else { m_pass_num = 0; } // Get the phase we are processing, or default to the first one. if (argv[PHASE_ARG]) { g_phase = strtol(argv[PHASE_ARG]); } else { g_phase = PH_TOP_OUT_GEN; } get_current_profile(); if (g_phase == PH_TOP_OUT_GEN && m_pass_num == 0 && program_is_setup() == NO) { exit("run pcb-gcode-setup"); } // // Do what we need to for this phase. // switch (g_phase) { case PH_TOP_OUT_GEN: if (GENERATE_TOP_OUTLINES == YES) generate_outlines(TOP); break; case PH_TOP_OUT_WRITE: if (GENERATE_TOP_OUTLINES == YES) write_outlines(TOP, "top", OUTLINES); break; case PH_TOP_FILL_GEN: if (GENERATE_TOP_FILL == YES) generate_outlines(TOP); break; case PH_TOP_FILL_WRITE: if (GENERATE_TOP_FILL == YES) write_outlines(TOP, "tf", FILL); break; case PH_BOTTOM_OUT_GEN: if (GENERATE_BOTTOM_OUTLINES == YES) generate_outlines(BOTTOM); break; case PH_BOTTOM_OUT_WRITE: if (GENERATE_BOTTOM_OUTLINES == YES) write_outlines(BOTTOM, "bot", OUTLINES); break; case PH_BOTTOM_FILL_GEN: if (GENERATE_BOTTOM_FILL == YES) generate_outlines(BOTTOM); break; case PH_BOTTOM_FILL_WRITE: if (GENERATE_BOTTOM_FILL == YES) write_outlines(BOTTOM, "bf", FILL); break; case PH_TOP_DRILL: if (GENERATE_TOP_DRILL == YES) drill(TOP, "td"); break; case PH_BOTTOM_DRILL: if (GENERATE_BOTTOM_DRILL == YES) drill(BOTTOM, "bd"); break; case PH_MILL: if (GENERATE_MILLING == YES) write_outlines(MILL, "mil", MILL_BOARD); break; case PH_TEXT: if (GENERATE_TEXT == YES) write_outlines(TEXT, "txt", MILL_TEXT); break; default: sprintf(g_debug, "Illegal phase %d in main routine", g_phase); Fatal("Sorry...", g_debug); } // Set things up for the next phase. next_phase(); if (SHOW_PROGRESS == YES) { gen_progress_menu(g_phase); } // If this is not the last phase, exit with a command line for // Eagle to process. The command includes running this program again. if (g_phase < PH_LAST_PHASE) { sprintf(g_cmd, "%s" "window fit;\n" "%s" "run '%s' '%s' '%f' '%f' '%d' '%d';\n", g_cmd, (SHOW_PROGRESS) ? "script pcb-gcode-prg.scr;\n" : "", argv[PROGRAM_NAME_ARG], m_file_name, g_width, m_isolate, m_pass_num, g_phase ); exit(g_cmd); } else { g_cmd = "window fit;\n"; if (SHOW_PROGRESS) { if (RESTORE_MENU_FILE != "") { g_cmd = g_cmd + "script " + RESTORE_MENU_FILE + ";\n"; } else { g_cmd = g_cmd + "menu;\n"; } } exit(g_cmd); }