pcb 4.1.1
An interactive printed circuit board layout editor.
|
00001 00041 #ifdef HAVE_CONFIG_H 00042 #include "config.h" 00043 #endif 00044 00045 #include <stdio.h> 00046 #include <stdarg.h> 00047 #include <stdlib.h> 00048 #include <string.h> 00049 #include <assert.h> 00050 00051 #include <time.h> 00052 00053 #ifdef HAVE_LOCALE_H 00054 #include <locale.h> 00055 #endif 00056 00057 #include "global.h" 00058 #include "error.h" /* Message() */ 00059 #include "data.h" 00060 #include "misc.h" 00061 #include "rats.h" 00062 00063 #include "hid.h" 00064 #include "hid_draw.h" 00065 #include "../hidint.h" 00066 #include <gd.h> 00067 #include "hid/common/hidnogui.h" 00068 #include "hid/common/draw_helpers.h" 00069 #include "bitmap.h" 00070 #include "curve.h" 00071 #include "potracelib.h" 00072 #include "trace.h" 00073 #include "decompose.h" 00074 #include "pcb-printf.h" 00075 00076 #include "hid/common/hidinit.h" 00077 00078 #ifdef HAVE_LIBDMALLOC 00079 #include <dmalloc.h> 00080 #endif 00081 00082 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented GCODE function %s.\n", __FUNCTION__); abort() 00083 00084 static HID gcode_hid; 00085 static HID_DRAW gcode_graphics; 00086 00087 struct color_struct 00088 { 00089 int c; 00091 /* so I can figure out what rgb value c refers to */ 00092 unsigned int r, g, b; 00093 }; 00094 00095 struct hid_gc_struct 00096 { 00097 HID *me_pointer; 00098 EndCapStyle cap; 00099 int width; 00100 unsigned char r, g, b; 00101 int erase; 00102 struct color_struct *color; 00103 gdImagePtr brush; 00104 }; 00105 00106 static struct color_struct *black = NULL, *white = NULL; 00107 static int linewidth = -1; 00108 static gdImagePtr lastbrush = (gdImagePtr)((void *) -1); 00109 00110 static gdImagePtr gcode_im = NULL; 00111 static FILE *gcode_f = NULL; 00112 00113 static int is_mask; 00114 static int is_drill; 00115 static int is_bottom; 00116 00117 static int gcode_export_group[MAX_GROUP]; 00121 static int gcode_cur_group; 00123 static const char *gcode_basename = NULL; 00126 static int gcode_dpi = -1; 00128 static double gcode_cutdepth = 0; 00129 static double gcode_isoplunge = 0; 00130 static double gcode_isofeedrate = 0; 00131 static char gcode_predrill; 00132 static double gcode_drilldepth = 0; 00133 static double gcode_drillfeedrate = 0; 00134 static double gcode_safeZ = 100; 00135 static int gcode_toolradius = 0; 00136 static char gcode_drillmill = 0; 00137 static double gcode_milldepth = 0; 00138 static double gcode_milltoolradius = 0; 00139 static double gcode_millplunge = 0; 00140 static double gcode_millfeedrate = 0; 00141 static char gcode_advanced = 0; 00142 static int save_drill = 0; 00143 00147 struct drill_hole 00148 { 00149 double x; 00150 double y; 00151 }; 00152 00156 struct single_size_drills 00157 { 00158 double diameter_inches; 00159 00160 int n_holes; 00161 int n_holes_allocated; 00162 struct drill_hole* holes; 00163 }; 00164 00165 static struct single_size_drills* drills = NULL; 00167 static int n_drills = 0; 00168 static int n_drills_allocated = 0; 00169 00170 HID_Attribute gcode_attribute_list[] = { 00171 /* other HIDs expect this to be first. */ 00172 00173 /* %start-doc options "85 G-code Options" 00174 @ftable @code 00175 @item --basename <string> 00176 File name prefix and suffix, layer names will be inserted before the 00177 suffix. 00178 Parameter @code{<string>} can include a path. 00179 @end ftable 00180 %end-doc 00181 */ 00182 {"basename", "File name prefix and suffix,\n" 00183 "layer names will be inserted before the suffix.", 00184 HID_String, 0, 0, {0, 0, 0}, 0, 0}, 00185 #define HA_basename 0 00186 00187 /* %start-doc options "85 G-code Options" 00188 @ftable @code 00189 @item --measurement-unit <unit> 00190 Measurement unit used in the G-code output. 00191 Defaults to mil. 00192 Parameter @code{<unit>} can be @samp{km}, @samp{m}, @samp{cm}, @samp{mm}, 00193 @samp{um}, @samp{nm}, @samp{px}, @samp{in}, @samp{mil}, @samp{dmil}, 00194 @samp{cmil}, or @samp{inch}. 00195 @end ftable 00196 %end-doc 00197 */ 00198 {"measurement-unit", "Measurement unit used in the G-code output.", 00199 HID_Unit, 0, 0, {3, 0, 0}, NULL, 0}, 00200 #define HA_unit 1 00201 00202 /* %start-doc options "85 G-code Options" 00203 @ftable @code 00204 @item --dpi <int> 00205 Accuracy of the mill path generation in pixels/inch. 00206 @end ftable 00207 %end-doc 00208 */ 00209 {"dpi", "Accuracy of the mill path generation in pixels/inch.", 00210 HID_Integer, 0, 2000, {600, 0, 0}, 0, 0}, 00211 #define HA_dpi 2 00212 00213 /* %start-doc options "85 G-code Options" 00214 @ftable @code 00215 @item --safe-Z <real> 00216 Safe Z for traverse movements of all operations. 00217 @end ftable 00218 %end-doc 00219 */ 00220 {"safe-Z", "Safe Z for traverse movements of all operations.", 00221 HID_Real, -1000, 10000, {0, 0, 2}, 0, 0}, 00222 #define HA_safeZ 3 00223 00224 /* %start-doc options "85 G-code Options" 00225 @ftable @code 00226 @item --iso-mill-depth <real> 00227 Isolation milling depth. 00228 @end ftable 00229 %end-doc 00230 */ 00231 {"iso-mill-depth", "Isolation milling depth.", 00232 HID_Real, -1000, 1000, {0, 0, -0.05}, 0, 0}, 00233 #define HA_cutdepth 4 00234 00235 /* %start-doc options "85 G-code Options" 00236 @ftable @code 00237 @item --iso-tool-diameter <real> 00238 Isolation milling tool diameter. 00239 @end ftable 00240 %end-doc 00241 */ 00242 {"iso-tool-diameter", "Isolation milling tool diameter.", 00243 HID_Real, 0, 10000, {0, 0, 0.2}, 0, 0}, 00244 #define HA_tooldiameter 5 00245 00246 /* %start-doc options "85 G-code Options" 00247 @ftable @code 00248 @item --iso-tool-plunge <real> 00249 Isolation milling feedrate when plunging into the material. 00250 @end ftable 00251 %end-doc 00252 */ 00253 {"iso-tool-plunge", "Isolation milling feedrate when plunging into\n" 00254 "the material.", 00255 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0}, 00256 #define HA_isoplunge 6 00257 00258 /* %start-doc options "85 G-code Options" 00259 @ftable @code 00260 @item --iso-tool-feedrate <real> 00261 Isolation milling feedrate. 00262 @end ftable 00263 %end-doc 00264 */ 00265 {"iso-tool-feedrate", "Isolation milling feedrate.", 00266 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0}, 00267 #define HA_isofeedrate 7 00268 00269 /* %start-doc options "85 G-code Options" 00270 @ftable @code 00271 @item --predrill 00272 Whether to pre-drill all drill spots with the isolation milling tool. 00273 Drill depth is iso-mill-depth here. 00274 This feature eases and enhances accuracy of manual drilling. 00275 @end ftable 00276 %end-doc 00277 */ 00278 {"predrill", "Wether to pre-drill all drill spots with the isolation milling\n" 00279 "tool. Drill depth is iso-mill-depth here. This feature eases\n" 00280 "and enhances accuracy of manual drilling.", 00281 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0}, 00282 #define HA_predrill 8 00283 00284 /* %start-doc options "85 G-code Options" 00285 @ftable @code 00286 @item --drill-depth <real> 00287 Drilling depth. 00288 @end ftable 00289 %end-doc 00290 */ 00291 {"drill-depth", "Drilling depth.", 00292 HID_Real, -10000, 10000, {0, 0, -2}, 0, 0}, 00293 #define HA_drilldepth 9 00294 00295 /* %start-doc options "85 G-code Options" 00296 @ftable @code 00297 @item --drill-feedrate <real> 00298 Drilling feedrate. 00299 @end ftable 00300 %end-doc 00301 */ 00302 {"drill-feedrate", "Drilling feedrate.", 00303 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0}, 00304 #define HA_drillfeedrate 10 00305 00306 /* %start-doc options "85 G-code Options" 00307 @ftable @code 00308 @item --drill-mill 00309 Whether to produce drill holes equal or bigger than the milling tool 00310 diameter with the milling tool. 00311 With the milling tool bigger holes can be accurately sized without 00312 changing the tool. 00313 @end ftable 00314 %end-doc 00315 */ 00316 {"drill-mill", "Wether to produce drill holes equal or bigger than the\n" 00317 "milling tool diameter with the milling tool.\n" 00318 "With the milling tool bigger holes can be accurately sized\n" 00319 "without changing the tool.", 00320 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0}, 00321 #define HA_drillmill 11 00322 00323 /* %start-doc options "85 G-code Options" 00324 @ftable @code 00325 @item --outline-mill-depth <real> 00326 Milling depth when milling the outline. 00327 Currently, only the rectangular extents of the board are milled, no 00328 polygonal outlines or holes. 00329 @end ftable 00330 %end-doc 00331 */ 00332 {"outline-mill-depth", "Milling depth when milling the outline.\n" 00333 "Currently, only the rectangular extents of the\n" 00334 "board are milled, no polygonal outlines or holes.", 00335 HID_Real, -10000, 10000, {0, 0, -1}, 0, 0}, 00336 #define HA_milldepth 12 00337 00338 /* %start-doc options "85 G-code Options" 00339 @ftable @code 00340 @item --outline-tool-diameter <real> 00341 Diameter of the tool used for outline milling. 00342 @end ftable 00343 %end-doc 00344 */ 00345 {"outline-tool-diameter", "Diameter of the tool used for outline milling.", 00346 HID_Real, 0, 10000, {0, 0, 1}, 0, 0}, 00347 #define HA_milltooldiameter 13 00348 00349 /* %start-doc options "85 G-code Options" 00350 @ftable @code 00351 @item --outline-mill-plunge <real> 00352 Outline milling feedrate when plunging into the material. 00353 @end ftable 00354 %end-doc 00355 */ 00356 {"outline-mill-plunge", "Outline milling feedrate when plunging into\n" 00357 "the material.", 00358 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0}, 00359 #define HA_millplunge 14 00360 00361 /* %start-doc options "85 G-code Options" 00362 @ftable @code 00363 @item --outline-mill-feedrate <real> 00364 Outline milling feedrate. 00365 @end ftable 00366 %end-doc 00367 */ 00368 {"outline-mill-feedrate", "Outline milling feedrate.", 00369 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0}, 00370 #define HA_millfeedrate 15 00371 00372 /* %start-doc options "85 G-code Options" 00373 @ftable @code 00374 @item --advanced-gcode 00375 Whether to produce G-code for advanced interpreters, like using variables 00376 or drill cycles. 00377 Not all machine controllers understand this, but it allows better 00378 hand-editing of the resulting files. 00379 @end ftable 00380 %end-doc 00381 */ 00382 {"advanced-gcode", "Wether to produce G-code for advanced interpreters,\n" 00383 "like using variables or drill cycles. Not all\n" 00384 "machine controllers understand this, but it allows\n" 00385 "better hand-editing of the resulting files.", 00386 HID_Boolean, 0, 0, {-1, 0, 0}, 0, 0}, 00387 #define HA_advanced 16 00388 }; 00389 00390 #define NUM_OPTIONS (sizeof(gcode_attribute_list)/sizeof(gcode_attribute_list[0])) 00391 00392 REGISTER_ATTRIBUTES (gcode_attribute_list) 00393 static HID_Attr_Val gcode_values[NUM_OPTIONS]; 00394 00395 /* *** Utility funcions **************************************************** */ 00396 00400 static int pcb_to_gcode (int pcb) 00401 { 00402 return round(COORD_TO_INCH(pcb) * gcode_dpi); 00403 } 00404 00409 static void 00410 gcode_get_filename (char *filename, const char *layername) 00411 { 00412 char *pt; 00413 char suffix[MAXPATHLEN]; 00414 00415 suffix[0] = '\0'; 00416 pt = strrchr (gcode_basename, '.'); 00417 if (pt && pt > strrchr (gcode_basename, '/')) 00418 strcpy (suffix, pt); 00419 else 00420 pt = NULL; 00421 00422 strcpy (filename, gcode_basename); 00423 if (pt) 00424 *(filename + (pt - gcode_basename)) = '\0'; 00425 strcat (filename, "-"); 00426 strcat (filename, layername); 00427 strcat (filename, suffix); 00428 00429 // result is in char *filename 00430 } 00431 00445 static void 00446 sort_drill (struct drill_hole *drill, int n_drill) 00447 { 00448 /* I start out by looking for points closest to (0,0) */ 00449 struct drill_hole nearest_target = { 0, 0 }; 00450 00451 /* I sort my list by finding the correct point to fill each slot. I don't need 00452 to look at the last one, since it'll be in the right place automatically */ 00453 for (int j = 0; j < n_drill-1; j++) 00454 { 00455 double dmin = 1e20; 00456 int imin = 0; 00457 /* look through remaining elements to find the next drill point. This is 00458 the one nearest to nearest_target */ 00459 for (int i = j; i < n_drill; i++) 00460 { 00461 double d = 00462 (drill[i].x - nearest_target.x) * (drill[i].x - nearest_target.x) + 00463 (drill[i].y - nearest_target.y) * (drill[i].y - nearest_target.y); 00464 if (d < dmin) 00465 { 00466 imin = i; 00467 dmin = d; 00468 } 00469 } 00470 /* printf("j=%d imin=%d dmin=%f nearest_target=(%f,%f)\n",j,imin,dmin, 00471 nearest_target.x,nearest_target.y); */ 00472 if (j != imin) 00473 { 00474 struct drill_hole tmp; 00475 tmp = drill[j]; 00476 drill[j] = drill[imin]; 00477 drill[imin] = tmp; 00478 } 00479 00480 nearest_target = drill[j]; 00481 } 00482 } 00483 00484 /* *** Main export callback ************************************************ */ 00485 00486 static void 00487 gcode_parse_arguments (int *argc, char ***argv) 00488 { 00489 hid_register_attributes (gcode_attribute_list, 00490 sizeof (gcode_attribute_list) / 00491 sizeof (gcode_attribute_list[0])); 00492 hid_parse_command_line (argc, argv); 00493 } 00494 00495 static HID_Attribute * 00496 gcode_get_export_options (int *n) 00497 { 00498 static char *last_made_filename = 0; 00499 static int last_unit_value = -1; 00500 00501 if (gcode_attribute_list[HA_unit].default_val.int_value == last_unit_value) 00502 { 00503 if (Settings.grid_unit) 00504 gcode_attribute_list[HA_unit].default_val.int_value = Settings.grid_unit->index; 00505 else 00506 gcode_attribute_list[HA_unit].default_val.int_value = get_unit_struct ("mil")->index; 00507 last_unit_value = gcode_attribute_list[HA_unit].default_val.int_value; 00508 } 00509 00510 if (PCB) 00511 { 00512 derive_default_filename (PCB->Filename, 00513 &gcode_attribute_list[HA_basename], 00514 ".gcode", &last_made_filename); 00515 } 00516 if (n) 00517 { 00518 *n = NUM_OPTIONS; 00519 } 00520 return gcode_attribute_list; 00521 } 00522 00526 static void 00527 gcode_choose_groups () 00528 { 00529 int n, m; 00530 LayerType *layer; 00531 00532 /* Set entire array to 0 (don't export any layer groups by default */ 00533 memset (gcode_export_group, 0, sizeof (gcode_export_group)); 00534 00535 for (n = 0; n < max_copper_layer; n++) 00536 { 00537 layer = &PCB->Data->Layer[n]; 00538 00539 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN) 00540 { 00541 /* layer isn't empty */ 00542 00543 /* 00544 * is this check necessary? It seems that special 00545 * layers have negative indexes? 00546 */ 00547 00548 if (SL_TYPE (n) == 0) 00549 { 00550 /* layer is a copper layer */ 00551 m = GetLayerGroupNumberByNumber (n); 00552 00553 /* the export layer */ 00554 gcode_export_group[m] = 1; 00555 } 00556 } 00557 } 00558 } 00559 00565 static void 00566 gcode_alloc_colors () 00567 { 00568 white = (struct color_struct *) malloc (sizeof (*white)); 00569 white->r = white->g = white->b = 255; 00570 white->c = gdImageColorAllocate (gcode_im, white->r, white->g, white->b); 00571 00572 black = (struct color_struct *) malloc (sizeof (*black)); 00573 black->r = black->g = black->b = 0; 00574 black->c = gdImageColorAllocate (gcode_im, black->r, black->g, black->b); 00575 } 00576 00577 static void 00578 gcode_start_png () 00579 { 00580 gcode_im = gdImageCreate (pcb_to_gcode (PCB->MaxWidth), 00581 pcb_to_gcode (PCB->MaxHeight)); 00582 gcode_alloc_colors (); 00583 } 00584 00585 static void 00586 gcode_finish_png (const char *layername) 00587 { 00588 #ifdef HAVE_GDIMAGEPNG 00589 char *pngname, *filename; 00590 FILE *file = NULL; 00591 00592 pngname = (char *)malloc (MAXPATHLEN); 00593 gcode_get_filename (pngname, layername); 00594 filename = g_strdup_printf ("%s.png", pngname); 00595 free(pngname); 00596 00597 file = fopen (filename, "wb"); 00598 g_free (filename); 00599 00600 gdImagePng (gcode_im, file); 00601 fclose (file); 00602 #else 00603 Message ("GCODE: PNG not supported by gd. Can't write layer mask.\n"); 00604 #endif 00605 gdImageDestroy (gcode_im); 00606 00607 free (white); 00608 free (black); 00609 gcode_im = NULL; 00610 } 00611 00612 static void 00613 gcode_start_png_export () 00614 { 00615 BoxType region; 00616 00617 region.X1 = 0; 00618 region.Y1 = 0; 00619 region.X2 = PCB->MaxWidth; 00620 region.Y2 = PCB->MaxHeight; 00621 00622 linewidth = -1; 00623 lastbrush = (gdImagePtr)((void *) -1); 00624 00625 hid_expose_callback (&gcode_hid, ®ion, 0); 00626 } 00627 00628 static FILE * 00629 gcode_start_gcode (const char *layername, bool metric) 00630 { 00631 FILE *file = NULL; 00632 char buffer[MAXPATHLEN]; 00633 time_t t; 00634 00635 gcode_get_filename (buffer, layername); 00636 file = fopen (buffer, "wb"); 00637 if ( ! file) 00638 { 00639 perror (buffer); 00640 Message ("Can't open file %s\n", buffer); 00641 return NULL; 00642 } 00643 fprintf (file, "(Created by G-code exporter)\n"); 00644 t = time (NULL); 00645 snprintf (buffer, sizeof(buffer), "%s", ctime (&t)); 00646 buffer[strlen (buffer) - 1] = '\0'; // drop the newline 00647 fprintf (file, "(%s)\n", buffer); 00648 fprintf (file, "(Units: %s)\n", metric ? "mm" : "inch"); 00649 if (metric) 00650 pcb_fprintf (file, "(Board size: %.2`mm x %.2`mm mm)\n", 00651 PCB->MaxWidth, PCB->MaxHeight); 00652 else 00653 pcb_fprintf (file, "(Board size: %.2`mi x %.2`mi inches)\n", 00654 PCB->MaxWidth, PCB->MaxHeight); 00655 00656 return file; 00657 } 00658 00659 static void 00660 gcode_do_export (HID_Attr_Val * options) 00661 { 00662 int save_ons[MAX_ALL_LAYER]; 00663 int i, idx; 00664 const Unit *unit; 00665 double scale = 0, d = 0; 00666 int r, c, v, p, metric; 00667 path_t *plist = NULL; 00668 potrace_bitmap_t *bm = NULL; 00669 potrace_param_t param_default = { 00670 2, /* turnsize */ 00671 POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */ 00672 1.0, /* alphamax */ 00673 1, /* opticurve */ 00674 0.2, /* opttolerance */ 00675 { 00676 NULL, /* callback function */ 00677 NULL, /* callback data */ 00678 0.0, 1.0, /* progress range */ 00679 0.0, /* granularity */ 00680 }, 00681 }; 00682 char variable_safeZ[20], variable_cutdepth[20]; 00683 char variable_isoplunge[20], variable_isofeedrate[20]; 00684 char variable_drilldepth[20], variable_milldepth[20]; 00685 char variable_millplunge[20], variable_millfeedrate[20]; 00686 00687 if (!options) 00688 { 00689 gcode_get_export_options (0); 00690 for (i = 0; i < NUM_OPTIONS; i++) 00691 { 00692 gcode_values[i] = gcode_attribute_list[i].default_val; 00693 } 00694 options = gcode_values; 00695 } 00696 gcode_basename = options[HA_basename].str_value; 00697 if (!gcode_basename) 00698 { 00699 gcode_basename = "pcb-out.gcode"; 00700 } 00701 gcode_dpi = options[HA_dpi].int_value; 00702 if (gcode_dpi < 0) 00703 { 00704 fprintf (stderr, "ERROR: dpi may not be < 0\n"); 00705 return; 00706 } 00707 unit = &(get_unit_list() [options[HA_unit].int_value]); 00708 metric = (unit->family == METRIC); 00709 scale = metric ? 1.0 / coord_to_unit (unit, MM_TO_COORD (1.0)) 00710 : 1.0 / coord_to_unit (unit, INCH_TO_COORD (1.0)); 00711 00712 gcode_cutdepth = options[HA_cutdepth].real_value * scale; 00713 gcode_isoplunge = options[HA_isoplunge].real_value * scale; 00714 gcode_isofeedrate = options[HA_isofeedrate].real_value * scale; 00715 gcode_predrill = options[HA_predrill].int_value; 00716 gcode_drilldepth = options[HA_drilldepth].real_value * scale; 00717 gcode_drillfeedrate = options[HA_drillfeedrate].real_value * scale; 00718 gcode_safeZ = options[HA_safeZ].real_value * scale; 00719 gcode_toolradius = metric 00720 ? MM_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale) 00721 : INCH_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale); 00722 gcode_drillmill = options[HA_drillmill].int_value; 00723 gcode_milldepth = options[HA_milldepth].real_value * scale; 00724 gcode_milltoolradius = options[HA_milltooldiameter].real_value / 2 * scale; 00725 gcode_millplunge = options[HA_millplunge].real_value * scale; 00726 gcode_millfeedrate = options[HA_millfeedrate].real_value * scale; 00727 gcode_advanced = options[HA_advanced].int_value; 00728 gcode_choose_groups (); 00729 if (gcode_advanced) 00730 { 00731 /* give each variable distinct names, even if they don't appear 00732 together in a file. This allows to join output files */ 00733 strcpy (variable_safeZ, "#100"); 00734 strcpy (variable_cutdepth, "#101"); 00735 strcpy (variable_isoplunge, "#102"); 00736 strcpy (variable_isofeedrate, "#103"); 00737 strcpy (variable_drilldepth, "#104"); 00738 strcpy (variable_milldepth, "#105"); 00739 strcpy (variable_millplunge, "#106"); 00740 strcpy (variable_millfeedrate, "#107"); 00741 } 00742 else 00743 { 00744 snprintf (variable_safeZ, 20, "%f", gcode_safeZ); 00745 snprintf (variable_cutdepth, 20, "%f", gcode_cutdepth); 00746 snprintf (variable_isoplunge, 20, "%f", gcode_isoplunge); 00747 snprintf (variable_isofeedrate, 20, "%f", gcode_isofeedrate); 00748 snprintf (variable_drilldepth, 20, "%f", gcode_drilldepth); 00749 snprintf (variable_milldepth, 20, "%f", gcode_milldepth); 00750 snprintf (variable_millplunge, 20, "%f", gcode_millplunge); 00751 snprintf (variable_millfeedrate, 20, "%f", gcode_millfeedrate); 00752 } 00753 00754 for (i = 0; i < MAX_GROUP; i++) 00755 { 00756 if (gcode_export_group[i]) 00757 { 00758 gcode_cur_group = i; 00759 00760 /* magic */ 00761 idx = (i >= 0 && i < max_group) ? 00762 PCB->LayerGroups.Entries[i][0] : i; 00763 is_bottom = 00764 (GetLayerGroupNumberByNumber (idx) == 00765 GetLayerGroupNumberBySide (BOTTOM_SIDE)) ? 1 : 0; 00766 save_drill = is_bottom; /* save drills for one layer only */ 00767 gcode_start_png (); 00768 hid_save_and_show_layer_ons (save_ons); 00769 gcode_start_png_export (); 00770 hid_restore_layer_ons (save_ons); 00771 00772 /* ***************** gcode conversion *************************** */ 00773 /* potrace uses a different kind of bitmap; for simplicity gcode_im is 00774 copied to this format and flipped as needed along the way */ 00775 bm = bm_new (gdImageSX (gcode_im), gdImageSY (gcode_im)); 00776 for (r = 0; r < gdImageSX (gcode_im); r++) 00777 { 00778 for (c = 0; c < gdImageSY (gcode_im); c++) 00779 { 00780 if (is_bottom) 00781 v = /* flip vertically and horizontally */ 00782 gdImageGetPixel (gcode_im, gdImageSX (gcode_im) - 1 - r, 00783 gdImageSY (gcode_im) - 1 - c); 00784 else 00785 v = /* flip only vertically */ 00786 gdImageGetPixel (gcode_im, r, 00787 gdImageSY (gcode_im) - 1 - c); 00788 p = (gcode_im->red[v] || gcode_im->green[v] 00789 || gcode_im->blue[v]) ? 0 : 0xFFFFFF; 00790 BM_PUT (bm, r, c, p); 00791 } 00792 } 00793 if (is_bottom) 00794 { /* flip back layer, used only for PNG output */ 00795 gdImagePtr temp_im = 00796 gdImageCreate (gdImageSX (gcode_im), gdImageSY (gcode_im)); 00797 gdImageColorAllocate (temp_im, white->r, white->g, white->b); 00798 gdImageColorAllocate (temp_im, black->r, black->g, black->b); 00799 gdImageCopy (temp_im, gcode_im, 0, 0, 0, 0, 00800 gdImageSX (gcode_im), gdImageSY (gcode_im)); 00801 for (r = 0; r < gdImageSX (gcode_im); r++) 00802 { 00803 for (c = 0; c < gdImageSY (gcode_im); c++) 00804 { 00805 gdImageSetPixel (gcode_im, r, c, 00806 gdImageGetPixel (temp_im, 00807 gdImageSX (gcode_im) - 00808 1 - r, c)); 00809 } 00810 } 00811 gdImageDestroy (temp_im); 00812 } 00813 gcode_finish_png (layer_type_to_file_name (idx, FNS_fixed)); 00814 plist = NULL; 00815 gcode_f = gcode_start_gcode (layer_type_to_file_name (idx, FNS_fixed), 00816 metric); 00817 if (!gcode_f) 00818 { 00819 bm_free (bm); 00820 return; 00821 } 00822 fprintf (gcode_f, "(Accuracy %d dpi)\n", gcode_dpi); 00823 pcb_fprintf (gcode_f, "(Tool diameter: %`f %s)\n", 00824 options[HA_tooldiameter].real_value * scale, 00825 metric ? "mm" : "inch"); 00826 if (gcode_advanced) 00827 { 00828 pcb_fprintf (gcode_f, "%s=%`f (safe Z)\n", 00829 variable_safeZ, gcode_safeZ); 00830 pcb_fprintf (gcode_f, "%s=%`f (cutting depth)\n", 00831 variable_cutdepth, gcode_cutdepth); 00832 pcb_fprintf (gcode_f, "%s=%`f (plunge feedrate)\n", 00833 variable_isoplunge, gcode_isoplunge); 00834 pcb_fprintf (gcode_f, "%s=%`f (feedrate)\n", 00835 variable_isofeedrate, gcode_isofeedrate); 00836 } 00837 if (gcode_predrill && save_drill) 00838 fprintf (gcode_f, "(with predrilling)\n"); 00839 else 00840 fprintf (gcode_f, "(no predrilling)\n"); 00841 fprintf (gcode_f, "(---------------------------------)\n"); 00842 if (gcode_advanced) 00843 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n", 00844 metric ? 21 : 20); 00845 else 00846 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n", 00847 metric ? 21 : 20); 00848 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ); 00849 /* extract contour points from image */ 00850 r = bm_to_pathlist (bm, &plist, ¶m_default); 00851 if (r) 00852 { 00853 fprintf (stderr, "ERROR: pathlist function failed\n"); 00854 return; 00855 } 00856 /* generate best polygon and write vertices in g-code format */ 00857 d = process_path (plist, ¶m_default, bm, gcode_f, 00858 metric ? 25.4 / gcode_dpi : 1.0 / gcode_dpi, 00859 variable_cutdepth, variable_safeZ, 00860 variable_isoplunge, variable_isofeedrate); 00861 if (d < 0) 00862 { 00863 fprintf (stderr, "ERROR: path process function failed\n"); 00864 return; 00865 } 00866 if (gcode_predrill && save_drill) 00867 { 00868 int n_all_drills = 0; 00869 struct drill_hole* all_drills = NULL; 00870 /* count all drills to be predrilled */ 00871 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++) 00872 { 00873 struct single_size_drills* drill_set = &drills[i_drill_sets]; 00874 00875 /* don't predrill drillmill holes */ 00876 if (gcode_drillmill) { 00877 double radius = metric ? 00878 drill_set->diameter_inches * 25.4 / 2: 00879 drill_set->diameter_inches / 2; 00880 00881 if (gcode_milltoolradius < radius) 00882 continue; 00883 } 00884 00885 n_all_drills += drill_set->n_holes; 00886 } 00887 /* for sorting regardless of size, copy all drills to be 00888 predrilled into one new structure */ 00889 all_drills = (struct drill_hole *) 00890 malloc (n_all_drills * sizeof (struct drill_hole)); 00891 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++) 00892 { 00893 struct single_size_drills* drill_set = &drills[i_drill_sets]; 00894 00895 /* don't predrill drillmill holes */ 00896 if (gcode_drillmill) { 00897 double radius = metric ? 00898 drill_set->diameter_inches * 25.4 / 2: 00899 drill_set->diameter_inches / 2; 00900 00901 if (gcode_milltoolradius < radius) 00902 continue; 00903 } 00904 00905 memcpy(&all_drills[r], drill_set->holes, 00906 drill_set->n_holes * sizeof(struct drill_hole)); 00907 } 00908 sort_drill(all_drills, n_all_drills); 00909 /* write that (almost the same code as writing the drill file) */ 00910 fprintf (gcode_f, "(predrilling)\n"); 00911 fprintf (gcode_f, "F%s\n", variable_isoplunge); 00912 00913 for (r = 0; r < n_all_drills; r++) 00914 { 00915 double drillX, drillY; 00916 00917 if (metric) 00918 { 00919 drillX = all_drills[r].x * 25.4; 00920 drillY = all_drills[r].y * 25.4; 00921 } 00922 else 00923 { 00924 drillX = all_drills[r].x; 00925 drillY = all_drills[r].y; 00926 } 00927 if (gcode_advanced) 00928 pcb_fprintf (gcode_f, "G81 X%`f Y%`f Z%s R%s\n", 00929 drillX, drillY, variable_cutdepth, 00930 variable_safeZ); 00931 else 00932 { 00933 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n", drillX, drillY); 00934 pcb_fprintf (gcode_f, "G1 Z%s\n", variable_cutdepth); 00935 pcb_fprintf (gcode_f, "G0 Z%s\n", variable_safeZ); 00936 } 00937 } 00938 fprintf (gcode_f, "(%d predrills)\n", n_all_drills); 00939 free(all_drills); 00940 } 00941 if (metric) 00942 pcb_fprintf (gcode_f, "(milling distance %`.2fmm = %`.2fin)\n", d, 00943 d * 1 / 25.4); 00944 else 00945 pcb_fprintf (gcode_f, "(milling distance %`.2fmm = %`.2fin)\n", 00946 25.4 * d, d); 00947 if (gcode_advanced) 00948 fprintf (gcode_f, "M5 M9 M2\n"); 00949 else 00950 fprintf (gcode_f, "M5\nM9\nM2\n"); 00951 pathlist_free (plist); 00952 bm_free (bm); 00953 fclose (gcode_f); 00954 gcode_f = NULL; 00955 if (save_drill) 00956 { 00957 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++) 00958 { 00959 struct single_size_drills* drill = &drills[i_drill_file]; 00960 00961 /* don't drill drillmill holes */ 00962 if (gcode_drillmill) { 00963 double radius = metric ? 00964 drill->diameter_inches * 25.4 / 2: 00965 drill->diameter_inches / 2; 00966 00967 if (gcode_milltoolradius < radius) 00968 continue; 00969 } 00970 00971 d = 0; 00972 sort_drill (drill->holes, drill->n_holes); 00973 00974 { 00975 // get the filename with the drill size encoded in it 00976 char layername[32]; 00977 pcb_snprintf(layername, sizeof(layername), 00978 "%`.4f.drill", 00979 metric ? 00980 drill->diameter_inches * 25.4 : 00981 drill->diameter_inches); 00982 gcode_f = gcode_start_gcode(layername, metric); 00983 } 00984 if (!gcode_f) 00985 return; 00986 fprintf (gcode_f, "(Drill file: %d drills)\n", drill->n_holes); 00987 if (metric) 00988 pcb_fprintf (gcode_f, "(Drill diameter: %`f mm)\n", 00989 drill->diameter_inches * 25.4); 00990 else 00991 pcb_fprintf (gcode_f, "(Drill diameter: %`f inch)\n", 00992 drill->diameter_inches); 00993 if (gcode_advanced) 00994 { 00995 pcb_fprintf (gcode_f, "%s=%`f (safe Z)\n", 00996 variable_safeZ, gcode_safeZ); 00997 pcb_fprintf (gcode_f, "%s=%`f (drill depth)\n", 00998 variable_drilldepth, gcode_drilldepth); 00999 fprintf (gcode_f, "(---------------------------------)\n"); 01000 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 ", 01001 metric ? 21 : 20); 01002 pcb_fprintf (gcode_f, "S3000 M7 F%`f\n", 01003 gcode_drillfeedrate); 01004 } 01005 else 01006 { 01007 fprintf (gcode_f, "(---------------------------------)\n"); 01008 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 ", 01009 metric ? 21 : 20); 01010 pcb_fprintf (gcode_f, "S3000\nM7\nF%`f\n", 01011 gcode_drillfeedrate); 01012 } 01013 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ); 01014 for (r = 0; r < drill->n_holes; r++) 01015 { 01016 double drillX, drillY; 01017 01018 if (metric) 01019 { 01020 drillX = drill->holes[r].x * 25.4; 01021 drillY = drill->holes[r].y * 25.4; 01022 } 01023 else 01024 { 01025 drillX = drill->holes[r].x; 01026 drillY = drill->holes[r].y; 01027 } 01028 if (gcode_advanced) 01029 pcb_fprintf (gcode_f, "G81 X%`f Y%`f Z%s R%s\n", 01030 drillX, drillY, variable_drilldepth, 01031 variable_safeZ); 01032 else 01033 { 01034 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n", 01035 drillX, drillY); 01036 fprintf (gcode_f, "G1 Z%s\n", variable_drilldepth); 01037 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ); 01038 } 01039 if (r > 0) 01040 d += Distance(drill->holes[r - 1].x, drill->holes[r - 1].y, 01041 drill->holes[r ].x, drill->holes[r ].y); 01042 } 01043 if (gcode_advanced) 01044 fprintf (gcode_f, "M5 M9 M2\n"); 01045 else 01046 fprintf (gcode_f, "M5\nM9\nM2\n"); 01047 pcb_fprintf (gcode_f, 01048 "(end, total distance %`.2fmm = %`.2fin)\n", 25.4 * d, d); 01049 fclose (gcode_f); 01050 } 01051 01052 /* ******************* handle drill-milling **************************** */ 01053 if (save_drill && gcode_drillmill) 01054 { 01055 int n_drillmill_drills = 0; 01056 struct drill_hole* drillmill_drills = NULL; 01057 double* drillmill_radiuss = NULL; 01058 double mill_radius, inaccuracy; 01059 01060 /* count drillmill drills */ 01061 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++) 01062 { 01063 struct single_size_drills* drill_set = &drills[i_drill_sets]; 01064 double radius = metric ? 01065 drill_set->diameter_inches * 25.4 / 2: 01066 drill_set->diameter_inches / 2; 01067 01068 if (gcode_milltoolradius <= radius) 01069 n_drillmill_drills += drill_set->n_holes; 01070 } 01071 if (n_drillmill_drills > 0) { 01072 /* for sorting regardless of size, copy all available drills 01073 into one new structure */ 01074 drillmill_drills = (struct drill_hole *) 01075 malloc (n_drillmill_drills * sizeof (struct drill_hole)); 01076 drillmill_radiuss = (double *) 01077 malloc (n_drillmill_drills * sizeof (double)); 01078 r = 0; 01079 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++) 01080 { 01081 struct single_size_drills* drill_set = &drills[i_drill_sets]; 01082 double radius = metric ? 01083 drill_set->diameter_inches * 25.4 / 2: 01084 drill_set->diameter_inches / 2; 01085 01086 if (gcode_milltoolradius <= radius) 01087 { 01088 memcpy(&drillmill_drills[r], drill_set->holes, 01089 drill_set->n_holes * sizeof(struct drill_hole)); 01090 for (int r2 = r; r2 < r + drill_set->n_holes; r2++) 01091 drillmill_radiuss[r2] = radius; 01092 r += drill_set->n_holes; 01093 } 01094 } 01095 sort_drill(drillmill_drills, n_drillmill_drills); 01096 01097 gcode_f = gcode_start_gcode("drillmill", metric); 01098 if (!gcode_f) 01099 return; 01100 fprintf (gcode_f, "(Drillmill file)\n"); 01101 pcb_fprintf (gcode_f, "(Tool diameter: %`f %s)\n", 01102 gcode_milltoolradius * 2, 01103 metric ? "mm" : "inch"); 01104 if (gcode_advanced) 01105 { 01106 pcb_fprintf (gcode_f, "%s=%`f (safe Z)\n", 01107 variable_safeZ, gcode_safeZ); 01108 pcb_fprintf (gcode_f, "%s=%`f (mill depth)\n", 01109 variable_milldepth, gcode_milldepth); 01110 pcb_fprintf (gcode_f, "%s=%`f (mill plunge feedrate)\n", 01111 variable_millplunge, gcode_millplunge); 01112 pcb_fprintf (gcode_f, "%s=%`f (mill feedrate)\n", 01113 variable_millfeedrate, gcode_millfeedrate); 01114 fprintf (gcode_f, "(---------------------------------)\n"); 01115 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n", 01116 metric ? 21 : 20); 01117 } 01118 else 01119 { 01120 fprintf (gcode_f, "(---------------------------------)\n"); 01121 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n", 01122 metric ? 21 : 20); 01123 } 01124 for (r = 0; r < n_drillmill_drills; r++) 01125 { 01126 double drillX, drillY; 01127 01128 if (metric) 01129 { 01130 drillX = drillmill_drills[r].x * 25.4; 01131 drillY = drillmill_drills[r].y * 25.4; 01132 } 01133 else 01134 { 01135 drillX = drillmill_drills[r].x; 01136 drillY = drillmill_drills[r].y; 01137 } 01138 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n", drillX, drillY); 01139 fprintf (gcode_f, "G1 Z%s F%s\n", 01140 variable_milldepth, variable_millplunge); 01141 01142 mill_radius = drillmill_radiuss[r] - gcode_milltoolradius; 01143 inaccuracy = (metric ? 25.4 : 1.) / gcode_dpi; 01144 if (mill_radius > inaccuracy) 01145 { 01146 int n_sides; 01147 01148 /* calculate how many polygon sides we need to stay 01149 within our accuracy while avoiding a G02/G03 */ 01150 n_sides = M_PI / acos (mill_radius / 01151 (mill_radius + inaccuracy)); 01152 if (n_sides < 4) 01153 n_sides = 4; 01154 fprintf (gcode_f, "F%s\n", variable_millfeedrate); 01155 for (i = 0; i <= n_sides; i++) 01156 { 01157 double angle = M_PI * 2 * i / n_sides; 01158 pcb_fprintf (gcode_f, "G1 X%`f Y%`f\n", 01159 drillX + mill_radius * cos (angle), 01160 drillY + mill_radius * sin (angle)); 01161 } 01162 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n", 01163 drillX, drillY); 01164 } 01165 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ); 01166 } 01167 if (gcode_advanced) 01168 fprintf (gcode_f, "M5 M9 M2\n"); 01169 else 01170 fprintf (gcode_f, "M5\nM9\nM2\n"); 01171 fclose (gcode_f); 01172 01173 free(drillmill_radiuss); 01174 free(drillmill_drills); 01175 } 01176 } 01177 /* ******************* end of per-layer writing ************************ */ 01178 01179 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++) 01180 free(drills[i_drill_file].holes); 01181 free (drills); 01182 drills = NULL; 01183 n_drills = n_drills_allocated = 0; 01184 } 01185 } 01186 } 01187 /* 01188 * General milling. Put this aside from the above code, as paths 01189 * are generated without taking line or curve thickness into account. 01190 * Accordingly, we need an entirely different approach. 01191 */ 01192 /* 01193 * Currently this is a rather simple implementation, which mills 01194 * the rectangular extents of the board and nothing else. This should 01195 * be sufficient for many use cases. 01196 * 01197 * A better implementation would have to group the lines and polygons 01198 * on the outline layer by outer polygon and inner holes, then offset 01199 * all of them to the right side and mill that. 01200 */ 01201 /* a better implementation might look like this: 01202 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE); 01203 { 01204 LINE_LOOP (layer); 01205 { 01206 ... calculate the offset for all lines and polygons of this layer, 01207 mirror it if is_bottom, then mill it ... 01208 } 01209 END_LOOP; 01210 } 01211 END_LOOP; 01212 01213 for now: */ 01214 { /* unconditional */ 01215 double lowerX = 0., lowerY = 0., upperX = 0., upperY = 0.; 01216 double mill_distance = 0.; 01217 01218 gcode_f = gcode_start_gcode("outline", metric); 01219 if (!gcode_f) 01220 return; 01221 fprintf (gcode_f, "(Outline mill file)\n"); 01222 pcb_fprintf (gcode_f, "(Tool diameter: %`f %s)\n", 01223 gcode_milltoolradius * 2, metric ? "mm" : "inch"); 01224 if (gcode_advanced) 01225 { 01226 pcb_fprintf (gcode_f, "%s=%`f (safe Z)\n", 01227 variable_safeZ, gcode_safeZ); 01228 pcb_fprintf (gcode_f, "%s=%`f (mill depth)\n", 01229 variable_milldepth, gcode_milldepth); 01230 pcb_fprintf (gcode_f, "%s=%`f (mill plunge feedrate)\n", 01231 variable_millplunge, gcode_millplunge); 01232 pcb_fprintf (gcode_f, "%s=%`f (mill feedrate)\n", 01233 variable_millfeedrate, gcode_millfeedrate); 01234 fprintf (gcode_f, "(---------------------------------)\n"); 01235 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n", 01236 metric ? 21 : 20); 01237 } 01238 else 01239 { 01240 fprintf (gcode_f, "(---------------------------------)\n"); 01241 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n", 01242 metric ? 21 : 20); 01243 } 01244 if (metric) 01245 { 01246 upperX = COORD_TO_MM(PCB->MaxWidth); 01247 upperY = COORD_TO_MM(PCB->MaxHeight); 01248 } 01249 else 01250 { 01251 upperX = COORD_TO_INCH(PCB->MaxWidth); 01252 upperY = COORD_TO_INCH(PCB->MaxHeight); 01253 } 01254 lowerX -= gcode_milltoolradius; 01255 lowerY -= gcode_milltoolradius; 01256 upperX += gcode_milltoolradius; 01257 upperY += gcode_milltoolradius; 01258 01259 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ); 01260 /* mill the two edges adjectant to 0,0 first to disconnect the 01261 workpiece from the raw material last */ 01262 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n", upperX, lowerY); 01263 pcb_fprintf (gcode_f, "G1 Z%s F%s\n", 01264 variable_milldepth, variable_millplunge); 01265 pcb_fprintf (gcode_f, "G1 X%`f Y%`f F%s\n", 01266 lowerX, lowerY, variable_millfeedrate); 01267 pcb_fprintf (gcode_f, "G1 X%`f Y%`f\n", lowerX, upperY); 01268 pcb_fprintf (gcode_f, "G1 X%`f Y%`f\n", upperX, upperY); 01269 pcb_fprintf (gcode_f, "G1 X%`f Y%`f\n", upperX, lowerY); 01270 pcb_fprintf (gcode_f, "G0 Z%s\n", variable_safeZ); 01271 01272 if (gcode_advanced) 01273 fprintf (gcode_f, "M5 M9 M2\n"); 01274 else 01275 fprintf (gcode_f, "M5\nM9\nM2\n"); 01276 mill_distance = abs(gcode_safeZ - gcode_milldepth); 01277 if (metric) 01278 mill_distance /= 25.4; 01279 pcb_fprintf (gcode_f, "(end, total distance G0 %`.2f mm = %`.2f in)\n", 01280 mill_distance * 25.4, mill_distance); 01281 mill_distance = (upperX - lowerX + upperY - lowerY) * 2; 01282 mill_distance += abs(gcode_safeZ - gcode_milldepth); 01283 if (metric) 01284 mill_distance /= 25.4; 01285 pcb_fprintf (gcode_f, "( total distance G1 %`.2f mm = %`.2f in)\n", 01286 mill_distance * 25.4, mill_distance); 01287 fclose (gcode_f); 01288 } 01289 } 01290 01291 /* *** PNG export (slightly modified code from PNG export HID) ************* */ 01292 01293 static int 01294 gcode_set_layer (const char *name, int group, int empty) 01295 { 01296 int idx = (group >= 0 && group < max_group) ? 01297 PCB->LayerGroups.Entries[group][0] : group; 01298 01299 if (name == 0) 01300 { 01301 name = PCB->Data->Layer[idx].Name; 01302 } 01303 if (strcmp (name, "invisible") == 0) 01304 { 01305 return 0; 01306 } 01307 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL); 01308 is_mask = (SL_TYPE (idx) == SL_MASK); 01309 01310 if (is_mask) 01311 { 01312 /* Don't print masks */ 01313 return 0; 01314 } 01315 if (is_drill) 01316 { 01317 /* 01318 * Print 'holes', so that we can fill gaps in the copper 01319 * layer 01320 */ 01321 return 1; 01322 } 01323 if (group == gcode_cur_group) 01324 { 01325 return 1; 01326 } 01327 return 0; 01328 } 01329 01330 static hidGC 01331 gcode_make_gc (void) 01332 { 01333 hidGC rv = (hidGC) malloc (sizeof (struct hid_gc_struct)); 01334 rv->me_pointer = &gcode_hid; 01335 rv->cap = Trace_Cap; 01336 rv->width = 1; 01337 rv->color = (struct color_struct *) malloc (sizeof (*rv->color)); 01338 rv->color->r = rv->color->g = rv->color->b = 0; 01339 rv->color->c = 0; 01340 return rv; 01341 } 01342 01343 static void 01344 gcode_destroy_gc (hidGC gc) 01345 { 01346 free (gc); 01347 } 01348 01349 static void 01350 gcode_use_mask (enum mask_mode mode) 01351 { 01352 /* does nothing */ 01353 } 01354 01355 static void 01356 gcode_set_color (hidGC gc, const char *name) 01357 { 01358 if (gcode_im == NULL) 01359 { 01360 return; 01361 } 01362 if (name == NULL) 01363 { 01364 name = "#ff0000"; 01365 } 01366 if (!strcmp (name, "drill")) 01367 { 01368 gc->color = black; 01369 gc->erase = 0; 01370 return; 01371 } 01372 if (!strcmp (name, "erase")) 01373 { 01374 /* FIXME -- should be background, not white */ 01375 gc->color = white; 01376 gc->erase = 1; 01377 return; 01378 } 01379 gc->color = black; 01380 gc->erase = 0; 01381 return; 01382 } 01383 01384 static void 01385 gcode_set_line_cap (hidGC gc, EndCapStyle style) 01386 { 01387 gc->cap = style; 01388 } 01389 01390 static void 01391 gcode_set_line_width (hidGC gc, Coord width) 01392 { 01393 gc->width = width; 01394 } 01395 01396 static void 01397 gcode_set_draw_xor (hidGC gc, int xor_) 01398 { 01399 ; 01400 } 01401 01402 static void 01403 gcode_set_draw_faded (hidGC gc, int faded) 01404 { 01405 } 01406 01407 static void 01408 use_gc (hidGC gc) 01409 { 01410 int need_brush = 0; 01411 01412 if (gc->me_pointer != &gcode_hid) 01413 { 01414 fprintf (stderr, "Fatal: GC from another HID passed to gcode HID\n"); 01415 abort (); 01416 } 01417 if (linewidth != gc->width) 01418 { 01419 /* Make sure the scaling doesn't erase lines completely */ 01420 /* 01421 if (SCALE (gc->width) == 0 && gc->width > 0) 01422 gdImageSetThickness (im, 1); 01423 else 01424 */ 01425 gdImageSetThickness (gcode_im, 01426 pcb_to_gcode (gc->width + 2 * gcode_toolradius)); 01427 linewidth = gc->width; 01428 need_brush = 1; 01429 } 01430 if (lastbrush != gc->brush || need_brush) 01431 { 01432 static void *bcache = 0; 01433 hidval bval; 01434 const size_t name_len = 256; 01435 char name[name_len]; 01436 char type; 01437 int r; 01438 01439 switch (gc->cap) 01440 { 01441 case Round_Cap: 01442 case Trace_Cap: 01443 type = 'C'; 01444 r = pcb_to_gcode (gc->width / 2 + gcode_toolradius); 01445 break; 01446 default: 01447 case Square_Cap: 01448 r = pcb_to_gcode (gc->width + gcode_toolradius * 2); 01449 type = 'S'; 01450 break; 01451 } 01452 snprintf (name, name_len, "#%.2x%.2x%.2x_%c_%d", gc->color->r, 01453 gc->color->g, gc->color->b, type, r); 01454 01455 if (hid_cache_color (0, name, &bval, &bcache)) 01456 { 01457 gc->brush = (gdImagePtr)bval.ptr; 01458 } 01459 else 01460 { 01461 int bg, fg; 01462 if (type == 'C') 01463 gc->brush = gdImageCreate (2 * r + 1, 2 * r + 1); 01464 else 01465 gc->brush = gdImageCreate (r + 1, r + 1); 01466 bg = gdImageColorAllocate (gc->brush, 255, 255, 255); 01467 fg = 01468 gdImageColorAllocate (gc->brush, gc->color->r, gc->color->g, 01469 gc->color->b); 01470 gdImageColorTransparent (gc->brush, bg); 01471 01472 /* 01473 * if we shrunk to a radius/box width of zero, then just use 01474 * a single pixel to draw with. 01475 */ 01476 if (r == 0) 01477 gdImageFilledRectangle (gc->brush, 0, 0, 0, 0, fg); 01478 else 01479 { 01480 if (type == 'C') 01481 gdImageFilledEllipse (gc->brush, r, r, 2 * r, 2 * r, fg); 01482 else 01483 gdImageFilledRectangle (gc->brush, 0, 0, r, r, fg); 01484 } 01485 bval.ptr = gc->brush; 01486 hid_cache_color (1, name, &bval, &bcache); 01487 } 01488 01489 gdImageSetBrush (gcode_im, gc->brush); 01490 lastbrush = gc->brush; 01491 01492 } 01493 } 01494 01495 static void 01496 gcode_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) 01497 { 01498 use_gc (gc); 01499 gdImageRectangle (gcode_im, 01500 pcb_to_gcode (x1 - gcode_toolradius), 01501 pcb_to_gcode (y1 - gcode_toolradius), 01502 pcb_to_gcode (x2 + gcode_toolradius), 01503 pcb_to_gcode (y2 + gcode_toolradius), 01504 gc->color->c); 01505 /* printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */ 01506 } 01507 01508 static void 01509 gcode_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) 01510 { 01511 use_gc (gc); 01512 gdImageSetThickness (gcode_im, 0); 01513 linewidth = 0; 01514 gdImageFilledRectangle (gcode_im, 01515 pcb_to_gcode (x1 - gcode_toolradius), 01516 pcb_to_gcode (y1 - gcode_toolradius), 01517 pcb_to_gcode (x2 + gcode_toolradius), 01518 pcb_to_gcode (y2 + gcode_toolradius), 01519 gc->color->c); 01520 /* printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */ 01521 } 01522 01523 static void 01524 gcode_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2) 01525 { 01526 if (x1 == x2 && y1 == y2) 01527 { 01528 Coord w = gc->width / 2; 01529 gcode_fill_rect (gc, 01530 x1 - w, y1 - w, 01531 x1 + w, y1 + w); 01532 return; 01533 } 01534 use_gc (gc); 01535 01536 gdImageSetThickness (gcode_im, 0); 01537 linewidth = 0; 01538 gdImageLine (gcode_im, 01539 pcb_to_gcode (x1), 01540 pcb_to_gcode (y1), 01541 pcb_to_gcode (x2), 01542 pcb_to_gcode (y2), 01543 gdBrushed); 01544 } 01545 01546 static void 01547 gcode_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height, 01548 Angle start_angle, Angle delta_angle) 01549 { 01550 Angle sa, ea; 01551 01552 /* 01553 * in gdImageArc, 0 degrees is to the right and +90 degrees is down 01554 * in pcb, 0 degrees is to the left and +90 degrees is down 01555 */ 01556 start_angle = 180 - start_angle; 01557 delta_angle = -delta_angle; 01558 if (delta_angle > 0) 01559 { 01560 sa = start_angle; 01561 ea = start_angle + delta_angle; 01562 } 01563 else 01564 { 01565 sa = start_angle + delta_angle; 01566 ea = start_angle; 01567 } 01568 01569 /* 01570 * make sure we start between 0 and 360 otherwise gd does strange 01571 * things 01572 */ 01573 sa = NormalizeAngle (sa); 01574 ea = NormalizeAngle (ea); 01575 01576 #if 0 01577 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n", 01578 cx, cy, width, height, start_angle, delta_angle, sa, ea); 01579 printf ("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n", 01580 im, SCALE_X (cx), SCALE_Y (cy), 01581 SCALE (width), SCALE (height), sa, ea, gc->color->c); 01582 #endif 01583 use_gc (gc); 01584 gdImageSetThickness (gcode_im, 0); 01585 linewidth = 0; 01586 gdImageArc (gcode_im, 01587 pcb_to_gcode (cx), 01588 pcb_to_gcode (cy), 01589 pcb_to_gcode (2 * width + gcode_toolradius * 2), 01590 pcb_to_gcode (2 * height + gcode_toolradius * 2), sa, ea, 01591 gdBrushed); 01592 } 01593 01600 static int _drill_size_comparator(const void* _size0, const void* _size1) 01601 { 01602 double size0 = ((const struct single_size_drills*)_size0)->diameter_inches; 01603 double size1 = ((const struct single_size_drills*)_size1)->diameter_inches; 01604 if (size0 == size1) 01605 return 0; 01606 01607 if (size0 < size1) 01608 return -1; 01609 01610 return 1; 01611 } 01612 01613 static struct single_size_drills* 01614 get_drill(double diameter_inches) 01615 { 01616 /* see if we already have this size. If so, return that structure */ 01617 struct single_size_drills* drill = 01618 bsearch (&diameter_inches, 01619 drills, n_drills, sizeof (drills[0]), 01620 _drill_size_comparator); 01621 if (drill != NULL) 01622 return drill; 01623 01624 /* haven't seen this hole size before, so make a new structure for it */ 01625 if (n_drills == n_drills_allocated) 01626 { 01627 n_drills_allocated += 100; 01628 drills = 01629 (struct single_size_drills *) realloc (drills, 01630 n_drills_allocated * 01631 sizeof (struct single_size_drills)); 01632 } 01633 01634 /* I now add the structure to the list, making sure to keep the list 01635 * sorted. Ideally the bsearch() call above would have given me the location 01636 * to insert this element while keeping things sorted, but it doesn't. For 01637 * simplicity I manually lsearch() to find this location myself */ 01638 { 01639 int i = 0; 01640 for (; i<n_drills; i++) 01641 if (drills[i].diameter_inches >= diameter_inches) 01642 break; 01643 01644 if (n_drills != i) 01645 memmove (&drills[i+1], &drills[i], 01646 (n_drills-i) * sizeof (struct single_size_drills)); 01647 01648 drills[i].diameter_inches = diameter_inches; 01649 drills[i].n_holes = 0; 01650 drills[i].n_holes_allocated = 0; 01651 drills[i].holes = NULL; 01652 n_drills++; 01653 01654 return &drills[i]; 01655 } 01656 } 01657 01658 static void 01659 add_hole (struct single_size_drills* drill, 01660 double cx_inches, double cy_inches) 01661 { 01662 if (drill->n_holes == drill->n_holes_allocated) 01663 { 01664 drill->n_holes_allocated += 100; 01665 drill->holes = 01666 (struct drill_hole *) realloc (drill->holes, 01667 drill->n_holes_allocated * 01668 sizeof (struct drill_hole)); 01669 } 01670 01671 drill->holes[ drill->n_holes ].x = cx_inches; 01672 drill->holes[ drill->n_holes ].y = cy_inches; 01673 drill->n_holes++; 01674 } 01675 01676 static void 01677 gcode_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius) 01678 { 01679 use_gc (gc); 01680 01681 gdImageSetThickness (gcode_im, 0); 01682 linewidth = 0; 01683 gdImageFilledEllipse (gcode_im, 01684 pcb_to_gcode (cx), 01685 pcb_to_gcode (cy), 01686 pcb_to_gcode (2 * radius + gcode_toolradius * 2), 01687 pcb_to_gcode (2 * radius + gcode_toolradius * 2), 01688 gc->color->c); 01689 if (save_drill && is_drill) 01690 { 01691 double diameter_inches = COORD_TO_INCH(radius*2); 01692 01693 struct single_size_drills* drill = get_drill (diameter_inches); 01694 add_hole (drill, 01695 /* convert to inch, flip: will drill from bottom side */ 01696 COORD_TO_INCH(PCB->MaxWidth - cx), 01697 /* PCB reverses y axis */ 01698 COORD_TO_INCH(PCB->MaxHeight - cy)); 01699 } 01700 } 01701 01702 static void 01703 gcode_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y) 01704 { 01705 int i; 01706 gdPoint *points; 01707 01708 points = (gdPoint *) malloc (n_coords * sizeof (gdPoint)); 01709 if (points == NULL) 01710 { 01711 fprintf (stderr, "ERROR: gcode_fill_polygon(): malloc failed\n"); 01712 exit (1); 01713 } 01714 use_gc (gc); 01715 for (i = 0; i < n_coords; i++) 01716 { 01717 points[i].x = pcb_to_gcode (x[i]); 01718 points[i].y = pcb_to_gcode (y[i]); 01719 } 01720 linewidth = pcb_to_gcode (2 * gcode_toolradius); 01721 gdImageSetThickness (gcode_im, linewidth); 01722 gdImageFilledPolygon (gcode_im, points, n_coords, gc->color->c); 01723 free (points); 01724 /* printf("FillPoly\n"); */ 01725 } 01726 01727 static void 01728 gcode_calibrate (double xval, double yval) 01729 { 01730 CRASH; 01731 } 01732 01733 static void 01734 gcode_set_crosshair (int x, int y, int a) 01735 { 01736 } 01737 01738 /* *** Miscellaneous ******************************************************* */ 01739 01740 #include "dolists.h" 01741 01742 void 01743 hid_gcode_init () 01744 { 01745 memset (&gcode_hid, 0, sizeof (HID)); 01746 memset (&gcode_graphics, 0, sizeof (HID_DRAW)); 01747 01748 common_nogui_init (&gcode_hid); 01749 common_draw_helpers_init (&gcode_graphics); 01750 01751 gcode_hid.struct_size = sizeof (HID); 01752 gcode_hid.name = "gcode"; 01753 gcode_hid.description = "G-CODE export"; 01754 gcode_hid.exporter = 1; 01755 gcode_hid.poly_before = 1; 01756 01757 gcode_hid.get_export_options = gcode_get_export_options; 01758 gcode_hid.do_export = gcode_do_export; 01759 gcode_hid.parse_arguments = gcode_parse_arguments; 01760 gcode_hid.set_layer = gcode_set_layer; 01761 gcode_hid.calibrate = gcode_calibrate; 01762 gcode_hid.set_crosshair = gcode_set_crosshair; 01763 01764 gcode_hid.graphics = &gcode_graphics; 01765 01766 gcode_graphics.make_gc = gcode_make_gc; 01767 gcode_graphics.destroy_gc = gcode_destroy_gc; 01768 gcode_graphics.use_mask = gcode_use_mask; 01769 gcode_graphics.set_color = gcode_set_color; 01770 gcode_graphics.set_line_cap = gcode_set_line_cap; 01771 gcode_graphics.set_line_width = gcode_set_line_width; 01772 gcode_graphics.set_draw_xor = gcode_set_draw_xor; 01773 gcode_graphics.set_draw_faded = gcode_set_draw_faded; 01774 gcode_graphics.draw_line = gcode_draw_line; 01775 gcode_graphics.draw_arc = gcode_draw_arc; 01776 gcode_graphics.draw_rect = gcode_draw_rect; 01777 gcode_graphics.fill_circle = gcode_fill_circle; 01778 gcode_graphics.fill_polygon = gcode_fill_polygon; 01779 gcode_graphics.fill_rect = gcode_fill_rect; 01780 01781 hid_register_hid (&gcode_hid); 01782 01783 #include "gcode_lists.h" 01784 } 01785