pcb 4.1.1
An interactive printed circuit board layout editor.

gcode.c

Go to the documentation of this file.
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, &region, 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, &param_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, &param_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