HID: Move non-PCB specific drawing calls into a separate API
[geda-pcb/pcjc2.git] / src / hid / gcode / gcode.c
blob44665295d832d4575459434268fc9841df37bad3
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
6 * GCODE export HID
7 * Copyright (c) 2010 Alberto Maccioni
8 * Copyright (c) 2012 Markus Hitter (mah@jump-ing.de)
9 * This code is based on the NELMA export HID, the PNG export HID,
10 * and potrace, a tracing program by Peter Selinger
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * This HID exports a PCB layout into:
30 * one layer mask file (PNG format) per copper layer,
31 * one G-CODE CNC drill file per drill size
32 * one G-CODE CNC file per copper layer.
33 * The latter is used by a CNC milling machine to mill the pcb.
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
40 #include <stdio.h>
41 #include <stdarg.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <assert.h>
46 #include <time.h>
48 #include "global.h"
49 #include "error.h" /* Message() */
50 #include "data.h"
51 #include "change.h" /* UpdateExtents() */
52 #include "misc.h"
53 #include "rats.h"
55 #include "hid.h"
56 #include "../hidint.h"
57 #include <gd.h>
58 #include "hid/common/hidnogui.h"
59 #include "hid/common/draw_helpers.h"
60 #include "bitmap.h"
61 #include "curve.h"
62 #include "potracelib.h"
63 #include "trace.h"
64 #include "decompose.h"
65 #include "pcb-printf.h"
67 #include "hid/common/hidinit.h"
69 #ifdef HAVE_LIBDMALLOC
70 #include <dmalloc.h>
71 #endif
73 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented GCODE function %s.\n", __FUNCTION__); abort()
75 static HID gcode_hid;
76 static HID_DRAW_API gcode_graphics;
78 struct color_struct
80 /* the descriptor used by the gd library */
81 int c;
83 /* so I can figure out what rgb value c refers to */
84 unsigned int r, g, b;
87 struct hid_gc_struct
89 HID *me_pointer;
90 EndCapStyle cap;
91 int width;
92 unsigned char r, g, b;
93 int erase;
94 struct color_struct *color;
95 gdImagePtr brush;
98 static struct color_struct *black = NULL, *white = NULL;
99 static int linewidth = -1;
100 static gdImagePtr lastbrush = (gdImagePtr)((void *) -1);
102 /* gd image and file for PNG export */
103 static gdImagePtr gcode_im = NULL;
104 static FILE *gcode_f = NULL;
106 static int is_mask;
107 static int is_drill;
108 static int is_solder;
111 * Which groups of layers to export into PNG layer masks. 1 means export, 0
112 * means do not export.
114 static int gcode_export_group[MAX_LAYER];
116 /* Group that is currently exported. */
117 static int gcode_cur_group;
119 /* Filename prefix and suffix that will be used when saving files. */
120 static const char *gcode_basename = NULL;
122 /* Horizontal DPI (grid points per inch) */
123 static int gcode_dpi = -1;
125 static double gcode_cutdepth = 0; /* milling depth (inch) */
126 static double gcode_isoplunge = 0; /* isolation milling plunge feedrate */
127 static double gcode_isofeedrate = 0; /* isolation milling feedrate */
128 static char gcode_predrill;
129 static double gcode_drilldepth = 0; /* drilling depth (mm or in) */
130 static double gcode_drillfeedrate = 0; /* drilling feedrate */
131 static double gcode_safeZ = 100; /* safe Z (mm or in) */
132 static int gcode_toolradius = 0; /* iso-mill tool radius (1/100 mil) */
133 static char gcode_drillmill = 0; /* wether to drill with the mill tool */
134 static double gcode_milldepth = 0; /* outline milling depth (mm or in) */
135 static double gcode_milltoolradius = 0; /* outline-mill tool radius (mm or in)*/
136 static double gcode_millplunge = 0; /* outline-milling plunge feedrate */
137 static double gcode_millfeedrate = 0; /* outline-milling feedrate */
138 static char gcode_advanced = 0;
139 static int save_drill = 0;
141 /* structure to represent a single hole */
142 struct drill_hole
144 double x;
145 double y;
148 /* structure to represent all holes of a given size */
149 struct single_size_drills
151 double diameter_inches;
153 int n_holes;
154 int n_holes_allocated;
155 struct drill_hole* holes;
158 /* at the start we have no drills at all */
159 static struct single_size_drills* drills = NULL;
160 static int n_drills = 0;
161 static int n_drills_allocated = 0;
163 HID_Attribute gcode_attribute_list[] = {
164 /* other HIDs expect this to be first. */
165 {"basename", "File name prefix and suffix,\n"
166 "layer names will be inserted before the suffix.",
167 HID_String, 0, 0, {0, 0, 0}, 0, 0},
168 #define HA_basename 0
170 {"measurement-unit", "Measurement unit used in the G-code output.",
171 HID_Unit, 0, 0, {3, 0, 0}, NULL, 0},
172 #define HA_unit 1
174 {"dpi", "Accuracy of the mill path generation in pixels/inch.",
175 HID_Integer, 0, 2000, {600, 0, 0}, 0, 0},
176 #define HA_dpi 2
178 {"safe-Z", "Safe Z for traverse movements of all operations.",
179 HID_Real, -1000, 10000, {0, 0, 2}, 0, 0},
180 #define HA_safeZ 3
182 {"iso-mill-depth", "Isolation milling depth.",
183 HID_Real, -1000, 1000, {0, 0, -0.05}, 0, 0},
184 #define HA_cutdepth 4
186 {"iso-tool-diameter", "Isolation milling tool diameter.",
187 HID_Real, 0, 10000, {0, 0, 0.2}, 0, 0},
188 #define HA_tooldiameter 5
190 {"iso-tool-plunge", "Isolation milling feedrate when plunging into\n"
191 "the material.",
192 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0},
193 #define HA_isoplunge 6
195 {"iso-tool-feedrate", "Isolation milling feedrate.",
196 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
197 #define HA_isofeedrate 7
199 {"predrill", "Wether to pre-drill all drill spots with the isolation milling\n"
200 "tool. Drill depth is iso-mill-depth here. This feature eases\n"
201 "and enhances accuracy of manual drilling.",
202 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
203 #define HA_predrill 8
205 {"drill-depth", "Drilling depth.",
206 HID_Real, -10000, 10000, {0, 0, -2}, 0, 0},
207 #define HA_drilldepth 9
209 {"drill-feedrate", "Drilling feedrate.",
210 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
211 #define HA_drillfeedrate 10
213 {"drill-mill", "Wether to produce drill holes equal or bigger than the\n"
214 "milling tool diameter with the milling tool.\n"
215 "With the milling tool bigger holes can be accurately sized\n"
216 "without changing the tool",
217 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
218 #define HA_drillmill 11
220 {"outline-mill-depth", "Milling depth when milling the outline.\n"
221 "Currently, only the rectangular extents of the\n"
222 "board are milled, no polygonal outlines or holes.",
223 HID_Real, -10000, 10000, {0, 0, -1}, 0, 0},
224 #define HA_milldepth 12
226 {"outline-tool-diameter", "Diameter of the tool used for outline milling.",
227 HID_Real, 0, 10000, {0, 0, 1}, 0, 0},
228 #define HA_milltooldiameter 13
230 {"outline-mill-plunge", "Outline milling feedrate when plunging into\n"
231 "the material",
232 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0},
233 #define HA_millplunge 14
235 {"outline-mill-feedrate", "Outline milling feedrate",
236 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
237 #define HA_millfeedrate 15
239 {"advanced-gcode", "Wether to produce G-code for advanced interpreters,\n"
240 "like using variables or drill cycles. Not all\n"
241 "machine controllers understand this, but it allows\n"
242 "better hand-editing of the resulting files.",
243 HID_Boolean, 0, 0, {-1, 0, 0}, 0, 0},
244 #define HA_advanced 16
247 #define NUM_OPTIONS (sizeof(gcode_attribute_list)/sizeof(gcode_attribute_list[0]))
249 REGISTER_ATTRIBUTES (gcode_attribute_list)
250 static HID_Attr_Val gcode_values[NUM_OPTIONS];
252 /* *** Utility funcions **************************************************** */
254 /* convert from default PCB units to gcode units */
255 static int pcb_to_gcode (int pcb)
257 return round(COORD_TO_INCH(pcb) * gcode_dpi);
260 /* Fits the given layer name into basename, just before the suffix */
261 static void
262 gcode_get_filename (char *filename, const char *layername)
264 char *pt;
265 char suffix[MAXPATHLEN];
267 suffix[0] = '\0';
268 pt = strrchr (gcode_basename, '.');
269 if (pt && pt > strrchr (gcode_basename, '/'))
270 strcpy (suffix, pt);
271 else
272 pt = NULL;
274 strcpy (filename, gcode_basename);
275 if (pt)
276 *(filename + (pt - gcode_basename)) = '\0';
277 strcat (filename, "-");
278 strcat (filename, layername);
279 strcat (filename, suffix);
281 // result is in char *filename
284 /* Sorts drills to produce a short tool path. I start with the hole nearest
285 * (0,0) and for each subsequent one, find the hole nearest to the previous.
286 * This isn't guaranteed to find the shortest path, but should be good enough.
287 * Note that this is O(N^2). We can't use the O(N logN) sort, since our
288 * shortest-distance origin changes with every point */
289 static void
290 sort_drill (struct drill_hole *drill, int n_drill)
292 /* I start out by looking for points closest to (0,0) */
293 struct drill_hole nearest_target = { 0, 0 };
295 /* I sort my list by finding the correct point to fill each slot. I don't need
296 to look at the last one, since it'll be in the right place automatically */
297 for (int j = 0; j < n_drill-1; j++)
299 double dmin = 1e20;
300 int imin = 0;
301 /* look through remaining elements to find the next drill point. This is
302 the one nearest to nearest_target */
303 for (int i = j; i < n_drill; i++)
305 double d =
306 (drill[i].x - nearest_target.x) * (drill[i].x - nearest_target.x) +
307 (drill[i].y - nearest_target.y) * (drill[i].y - nearest_target.y);
308 if (d < dmin)
310 imin = i;
311 dmin = d;
314 /* printf("j=%d imin=%d dmin=%f nearest_target=(%f,%f)\n",j,imin,dmin,
315 nearest_target.x,nearest_target.y); */
316 if (j != imin)
318 struct drill_hole tmp;
319 tmp = drill[j];
320 drill[j] = drill[imin];
321 drill[imin] = tmp;
324 nearest_target = drill[j];
328 /* *** Main export callback ************************************************ */
330 static void
331 gcode_parse_arguments (int *argc, char ***argv)
333 hid_register_attributes (gcode_attribute_list,
334 sizeof (gcode_attribute_list) /
335 sizeof (gcode_attribute_list[0]));
336 hid_parse_command_line (argc, argv);
339 static HID_Attribute *
340 gcode_get_export_options (int *n)
342 static char *last_made_filename = 0;
343 static int last_unit_value = -1;
345 if (gcode_attribute_list[HA_unit].default_val.int_value == last_unit_value)
347 if (Settings.grid_unit)
348 gcode_attribute_list[HA_unit].default_val.int_value = Settings.grid_unit->index;
349 else
350 gcode_attribute_list[HA_unit].default_val.int_value = get_unit_struct ("mil")->index;
351 last_unit_value = gcode_attribute_list[HA_unit].default_val.int_value;
354 if (PCB)
356 derive_default_filename (PCB->Filename,
357 &gcode_attribute_list[HA_basename],
358 ".gcode", &last_made_filename);
360 if (n)
362 *n = NUM_OPTIONS;
364 return gcode_attribute_list;
367 /* Populates gcode_export_group array */
368 static void
369 gcode_choose_groups ()
371 int n, m;
372 LayerType *layer;
374 /* Set entire array to 0 (don't export any layer groups by default */
375 memset (gcode_export_group, 0, sizeof (gcode_export_group));
377 for (n = 0; n < max_copper_layer; n++)
379 layer = &PCB->Data->Layer[n];
381 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
383 /* layer isn't empty */
386 * is this check necessary? It seems that special
387 * layers have negative indexes?
390 if (SL_TYPE (n) == 0)
392 /* layer is a copper layer */
393 m = GetLayerGroupNumberByNumber (n);
395 /* the export layer */
396 gcode_export_group[m] = 1;
402 static void
403 gcode_alloc_colors ()
406 * Allocate white and black -- the first color allocated becomes the
407 * background color
410 white = (struct color_struct *) malloc (sizeof (*white));
411 white->r = white->g = white->b = 255;
412 white->c = gdImageColorAllocate (gcode_im, white->r, white->g, white->b);
414 black = (struct color_struct *) malloc (sizeof (*black));
415 black->r = black->g = black->b = 0;
416 black->c = gdImageColorAllocate (gcode_im, black->r, black->g, black->b);
419 static void
420 gcode_start_png ()
422 gcode_im = gdImageCreate (pcb_to_gcode (PCB->ExtentMaxX - PCB->ExtentMinX),
423 pcb_to_gcode (PCB->ExtentMaxY - PCB->ExtentMinY));
424 gcode_alloc_colors ();
427 static void
428 gcode_finish_png (const char *layername)
430 #ifdef HAVE_GDIMAGEPNG
431 char *pngname, *filename;
432 FILE *file = NULL;
434 pngname = (char *)malloc (MAXPATHLEN);
435 gcode_get_filename (pngname, layername);
436 filename = g_strdup_printf ("%s.png", pngname);
437 free(pngname);
439 file = fopen (filename, "wb");
440 g_free (filename);
442 gdImagePng (gcode_im, file);
443 fclose (file);
444 #else
445 Message ("GCODE: PNG not supported by gd. Can't write layer mask.\n");
446 #endif
447 gdImageDestroy (gcode_im);
449 free (white);
450 free (black);
451 gcode_im = NULL;
454 static void
455 gcode_start_png_export ()
457 BoxType region;
459 region.X1 = PCB->ExtentMinX;
460 region.Y1 = PCB->ExtentMinY;
461 region.X2 = PCB->ExtentMaxX;
462 region.Y2 = PCB->ExtentMaxY;
464 linewidth = -1;
465 lastbrush = (gdImagePtr)((void *) -1);
467 hid_expose_callback (&gcode_hid, &region, 0);
470 static FILE *
471 gcode_start_gcode (const char *layername, bool metric)
473 FILE *file = NULL;
474 char buffer[MAXPATHLEN];
475 time_t t;
477 gcode_get_filename (buffer, layername);
478 file = fopen (buffer, "wb");
479 if ( ! file)
481 perror (buffer);
482 return NULL;
484 fprintf (file, "(Created by G-code exporter)\n");
485 t = time (NULL);
486 snprintf (buffer, sizeof(buffer), "%s", ctime (&t));
487 buffer[strlen (buffer) - 1] = '\0'; // drop the newline
488 fprintf (file, "(%s)\n", buffer);
489 fprintf (file, "(Units: %s)\n", metric ? "mm" : "inch");
490 if (metric)
491 pcb_fprintf (file, "(Board size: %.2mm x %.2mm mm)\n",
492 PCB->ExtentMaxX - PCB->ExtentMinX,
493 PCB->ExtentMaxY - PCB->ExtentMinY);
494 else
495 pcb_fprintf (file, "(Board size: %.2mi x %.2mi inches)\n",
496 PCB->ExtentMaxX - PCB->ExtentMinX,
497 PCB->ExtentMaxY - PCB->ExtentMinY);
499 return file;
502 static void
503 gcode_do_export (HID_Attr_Val * options)
505 int save_ons[MAX_LAYER + 2];
506 int i, idx;
507 const Unit *unit;
508 double scale = 0, d = 0;
509 int r, c, v, p, metric;
510 path_t *plist = NULL;
511 potrace_bitmap_t *bm = NULL;
512 potrace_param_t param_default = {
513 2, /* turnsize */
514 POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */
515 1.0, /* alphamax */
516 1, /* opticurve */
517 0.2, /* opttolerance */
519 NULL, /* callback function */
520 NULL, /* callback data */
521 0.0, 1.0, /* progress range */
522 0.0, /* granularity */
525 char variable_safeZ[20], variable_cutdepth[20];
526 char variable_isoplunge[20], variable_isofeedrate[20];
527 char variable_drilldepth[20], variable_milldepth[20];
528 char variable_millplunge[20], variable_millfeedrate[20];
529 char *old_locale = setlocale (LC_NUMERIC, NULL);
531 if (!options)
533 gcode_get_export_options (0);
534 for (i = 0; i < NUM_OPTIONS; i++)
536 gcode_values[i] = gcode_attribute_list[i].default_val;
538 options = gcode_values;
540 gcode_basename = options[HA_basename].str_value;
541 if (!gcode_basename)
543 gcode_basename = "pcb-out.gcode";
545 gcode_dpi = options[HA_dpi].int_value;
546 if (gcode_dpi < 0)
548 fprintf (stderr, "ERROR: dpi may not be < 0\n");
549 return;
551 unit = &(get_unit_list() [options[HA_unit].int_value]);
552 metric = (unit->family == METRIC);
553 scale = metric ? 1.0 / coord_to_unit (unit, MM_TO_COORD (1.0))
554 : 1.0 / coord_to_unit (unit, INCH_TO_COORD (1.0));
556 gcode_cutdepth = options[HA_cutdepth].real_value * scale;
557 gcode_isoplunge = options[HA_isoplunge].real_value * scale;
558 gcode_isofeedrate = options[HA_isofeedrate].real_value * scale;
559 gcode_predrill = options[HA_predrill].int_value;
560 gcode_drilldepth = options[HA_drilldepth].real_value * scale;
561 gcode_drillfeedrate = options[HA_drillfeedrate].real_value * scale;
562 gcode_safeZ = options[HA_safeZ].real_value * scale;
563 gcode_toolradius = metric
564 ? MM_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale)
565 : INCH_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale);
566 gcode_drillmill = options[HA_drillmill].int_value;
567 gcode_milldepth = options[HA_milldepth].real_value * scale;
568 gcode_milltoolradius = options[HA_milltooldiameter].real_value / 2 * scale;
569 gcode_millplunge = options[HA_millplunge].real_value * scale;
570 gcode_millfeedrate = options[HA_millfeedrate].real_value * scale;
571 gcode_advanced = options[HA_advanced].int_value;
572 gcode_choose_groups ();
573 setlocale (LC_NUMERIC, "C"); /* use . as separator */
574 if (gcode_advanced)
576 /* give each variable distinct names, even if they don't appear
577 together in a file. This allows to join output files */
578 strcpy (variable_safeZ, "#100");
579 strcpy (variable_cutdepth, "#101");
580 strcpy (variable_isoplunge, "#102");
581 strcpy (variable_isofeedrate, "#103");
582 strcpy (variable_drilldepth, "#104");
583 strcpy (variable_milldepth, "#105");
584 strcpy (variable_millplunge, "#106");
585 strcpy (variable_millfeedrate, "#107");
587 else
589 snprintf (variable_safeZ, 20, "%f", gcode_safeZ);
590 snprintf (variable_cutdepth, 20, "%f", gcode_cutdepth);
591 snprintf (variable_isoplunge, 20, "%f", gcode_isoplunge);
592 snprintf (variable_isofeedrate, 20, "%f", gcode_isofeedrate);
593 snprintf (variable_drilldepth, 20, "%f", gcode_drilldepth);
594 snprintf (variable_milldepth, 20, "%f", gcode_milldepth);
595 snprintf (variable_millplunge, 20, "%f", gcode_millplunge);
596 snprintf (variable_millfeedrate, 20, "%f", gcode_millfeedrate);
598 UpdateExtents();
600 for (i = 0; i < MAX_LAYER; i++)
602 if (gcode_export_group[i])
604 gcode_cur_group = i;
606 /* magic */
607 idx = (i >= 0 && i < max_group) ?
608 PCB->LayerGroups.Entries[i][0] : i;
609 is_solder =
610 (GetLayerGroupNumberByNumber (idx) ==
611 GetLayerGroupNumberByNumber (solder_silk_layer)) ? 1 : 0;
612 save_drill = is_solder; /* save drills for one layer only */
613 gcode_start_png ();
614 hid_save_and_show_layer_ons (save_ons);
615 gcode_start_png_export ();
616 hid_restore_layer_ons (save_ons);
618 /* ***************** gcode conversion *************************** */
619 /* potrace uses a different kind of bitmap; for simplicity gcode_im is
620 copied to this format and flipped as needed along the way */
621 bm = bm_new (gdImageSX (gcode_im), gdImageSY (gcode_im));
622 for (r = 0; r < gdImageSX (gcode_im); r++)
624 for (c = 0; c < gdImageSY (gcode_im); c++)
626 if (is_solder)
627 v = /* flip vertically and horizontally */
628 gdImageGetPixel (gcode_im, gdImageSX (gcode_im) - 1 - r,
629 gdImageSY (gcode_im) - 1 - c);
630 else
631 v = /* flip only vertically */
632 gdImageGetPixel (gcode_im, r,
633 gdImageSY (gcode_im) - 1 - c);
634 p = (gcode_im->red[v] || gcode_im->green[v]
635 || gcode_im->blue[v]) ? 0 : 0xFFFFFF;
636 BM_PUT (bm, r, c, p);
639 if (is_solder)
640 { /* flip back layer, used only for PNG output */
641 gdImagePtr temp_im =
642 gdImageCreate (gdImageSX (gcode_im), gdImageSY (gcode_im));
643 gdImageColorAllocate (temp_im, white->r, white->g, white->b);
644 gdImageColorAllocate (temp_im, black->r, black->g, black->b);
645 gdImageCopy (temp_im, gcode_im, 0, 0, 0, 0,
646 gdImageSX (gcode_im), gdImageSY (gcode_im));
647 for (r = 0; r < gdImageSX (gcode_im); r++)
649 for (c = 0; c < gdImageSY (gcode_im); c++)
651 gdImageSetPixel (gcode_im, r, c,
652 gdImageGetPixel (temp_im,
653 gdImageSX (gcode_im) -
654 1 - r, c));
657 gdImageDestroy (temp_im);
659 gcode_finish_png (layer_type_to_file_name (idx, FNS_fixed));
660 plist = NULL;
661 gcode_f = gcode_start_gcode (layer_type_to_file_name (idx, FNS_fixed),
662 metric);
663 if (!gcode_f)
665 bm_free (bm);
666 goto error;
668 fprintf (gcode_f, "(Accuracy %d dpi)\n", gcode_dpi);
669 fprintf (gcode_f, "(Tool diameter: %f %s)\n",
670 options[HA_tooldiameter].real_value * scale,
671 metric ? "mm" : "inch");
672 if (gcode_advanced)
674 fprintf (gcode_f, "%s=%f (safe Z)\n",
675 variable_safeZ, gcode_safeZ);
676 fprintf (gcode_f, "%s=%f (cutting depth)\n",
677 variable_cutdepth, gcode_cutdepth);
678 fprintf (gcode_f, "%s=%f (plunge feedrate)\n",
679 variable_isoplunge, gcode_isoplunge);
680 fprintf (gcode_f, "%s=%f (feedrate)\n",
681 variable_isofeedrate, gcode_isofeedrate);
683 if (gcode_predrill && save_drill)
684 fprintf (gcode_f, "(with predrilling)\n");
685 else
686 fprintf (gcode_f, "(no predrilling)\n");
687 fprintf (gcode_f, "(---------------------------------)\n");
688 if (gcode_advanced)
689 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
690 metric ? 21 : 20);
691 else
692 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
693 metric ? 21 : 20);
694 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
695 /* extract contour points from image */
696 r = bm_to_pathlist (bm, &plist, &param_default);
697 if (r)
699 fprintf (stderr, "ERROR: pathlist function failed\n");
700 goto error;
702 /* generate best polygon and write vertices in g-code format */
703 d = process_path (plist, &param_default, bm, gcode_f,
704 metric ? 25.4 / gcode_dpi : 1.0 / gcode_dpi,
705 variable_cutdepth, variable_safeZ,
706 variable_isoplunge, variable_isofeedrate);
707 if (d < 0)
709 fprintf (stderr, "ERROR: path process function failed\n");
710 goto error;
712 if (gcode_predrill && save_drill)
714 int n_all_drills = 0;
715 struct drill_hole* all_drills = NULL;
716 /* count all drills to be predrilled */
717 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
719 struct single_size_drills* drill_set = &drills[i_drill_sets];
721 /* don't predrill drillmill holes */
722 if (gcode_drillmill) {
723 double radius = metric ?
724 drill_set->diameter_inches * 25.4 / 2:
725 drill_set->diameter_inches / 2;
727 if (gcode_milltoolradius < radius)
728 continue;
731 n_all_drills += drill_set->n_holes;
733 /* for sorting regardless of size, copy all drills to be
734 predrilled into one new structure */
735 all_drills = (struct drill_hole *)
736 malloc (n_all_drills * sizeof (struct drill_hole));
737 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
739 struct single_size_drills* drill_set = &drills[i_drill_sets];
741 /* don't predrill drillmill holes */
742 if (gcode_drillmill) {
743 double radius = metric ?
744 drill_set->diameter_inches * 25.4 / 2:
745 drill_set->diameter_inches / 2;
747 if (gcode_milltoolradius < radius)
748 continue;
751 memcpy(&all_drills[r], drill_set->holes,
752 drill_set->n_holes * sizeof(struct drill_hole));
754 sort_drill(all_drills, n_all_drills);
755 /* write that (almost the same code as writing the drill file) */
756 fprintf (gcode_f, "(predrilling)\n");
757 fprintf (gcode_f, "F%s\n", variable_isoplunge);
759 for (r = 0; r < n_all_drills; r++)
761 double drillX, drillY;
763 if (metric)
765 drillX = all_drills[r].x * 25.4;
766 drillY = all_drills[r].y * 25.4;
768 else
770 drillX = all_drills[r].x;
771 drillY = all_drills[r].y;
773 if (gcode_advanced)
774 fprintf (gcode_f, "G81 X%f Y%f Z%s R%s\n", drillX, drillY,
775 variable_cutdepth, variable_safeZ);
776 else
778 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
779 fprintf (gcode_f, "G1 Z%s\n", variable_cutdepth);
780 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
783 fprintf (gcode_f, "(%d predrills)\n", n_all_drills);
784 free(all_drills);
786 if (metric)
787 fprintf (gcode_f, "(milling distance %.2fmm = %.2fin)\n", d,
788 d * 1 / 25.4);
789 else
790 fprintf (gcode_f, "(milling distance %.2fmm = %.2fin)\n",
791 25.4 * d, d);
792 if (gcode_advanced)
793 fprintf (gcode_f, "M5 M9 M2\n");
794 else
795 fprintf (gcode_f, "M5\nM9\nM2\n");
796 pathlist_free (plist);
797 bm_free (bm);
798 fclose (gcode_f);
799 gcode_f = NULL;
800 if (save_drill)
802 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++)
804 struct single_size_drills* drill = &drills[i_drill_file];
806 /* don't drill drillmill holes */
807 if (gcode_drillmill) {
808 double radius = metric ?
809 drill->diameter_inches * 25.4 / 2:
810 drill->diameter_inches / 2;
812 if (gcode_milltoolradius < radius)
813 continue;
816 d = 0;
817 sort_drill (drill->holes, drill->n_holes);
820 // get the filename with the drill size encoded in it
821 char layername[32];
822 snprintf(layername, sizeof(layername),
823 "%.4f.drill",
824 metric ?
825 drill->diameter_inches * 25.4 :
826 drill->diameter_inches);
827 gcode_f = gcode_start_gcode(layername, metric);
829 if (!gcode_f)
830 goto error;
831 fprintf (gcode_f, "(Drill file: %d drills)\n", drill->n_holes);
832 if (metric)
833 fprintf (gcode_f, "(Drill diameter: %f mm)\n",
834 drill->diameter_inches * 25.4);
835 else
836 fprintf (gcode_f, "(Drill diameter: %f inch)\n",
837 drill->diameter_inches);
838 if (gcode_advanced)
840 fprintf (gcode_f, "%s=%f (safe Z)\n",
841 variable_safeZ, gcode_safeZ);
842 fprintf (gcode_f, "%s=%f (drill depth)\n",
843 variable_drilldepth, gcode_drilldepth);
844 fprintf (gcode_f, "(---------------------------------)\n");
845 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%f\n",
846 metric ? 21 : 20, gcode_drillfeedrate);
848 else
850 fprintf (gcode_f, "(---------------------------------)\n");
851 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\nF%f\n",
852 metric ? 21 : 20, gcode_drillfeedrate);
854 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
855 for (r = 0; r < drill->n_holes; r++)
857 double drillX, drillY;
859 if (metric)
861 drillX = drill->holes[r].x * 25.4;
862 drillY = drill->holes[r].y * 25.4;
864 else
866 drillX = drill->holes[r].x;
867 drillY = drill->holes[r].y;
869 if (gcode_advanced)
870 fprintf (gcode_f, "G81 X%f Y%f Z%s R%s\n", drillX, drillY,
871 variable_drilldepth, variable_safeZ);
872 else
874 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
875 fprintf (gcode_f, "G1 Z%s\n", variable_drilldepth);
876 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
878 if (r > 0)
879 d += Distance(drill->holes[r - 1].x, drill->holes[r - 1].y,
880 drill->holes[r ].x, drill->holes[r ].y);
882 if (gcode_advanced)
883 fprintf (gcode_f, "M5 M9 M2\n");
884 else
885 fprintf (gcode_f, "M5\nM9\nM2\n");
886 fprintf (gcode_f, "(end, total distance %.2fmm = %.2fin)\n",
887 25.4 * d, d);
888 fclose (gcode_f);
891 /* ******************* handle drill-milling **************************** */
892 if (save_drill && gcode_drillmill)
894 int n_drillmill_drills = 0;
895 struct drill_hole* drillmill_drills = NULL;
896 double* drillmill_radiuss = NULL;
897 double mill_radius, inaccuracy;
899 /* count drillmill drills */
900 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
902 struct single_size_drills* drill_set = &drills[i_drill_sets];
903 double radius = metric ?
904 drill_set->diameter_inches * 25.4 / 2:
905 drill_set->diameter_inches / 2;
907 if (gcode_milltoolradius >= radius)
908 n_drillmill_drills += drill_set->n_holes;
910 /* for sorting regardless of size, copy all available drills
911 into one new structure */
912 drillmill_drills = (struct drill_hole *)
913 malloc (n_drillmill_drills * sizeof (struct drill_hole));
914 drillmill_radiuss = (double *)
915 malloc (n_drillmill_drills * sizeof (double));
916 r = 0;
917 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
919 struct single_size_drills* drill_set = &drills[i_drill_sets];
920 double radius = metric ?
921 drill_set->diameter_inches * 25.4 / 2:
922 drill_set->diameter_inches / 2;
924 if (gcode_milltoolradius >= radius)
926 memcpy(&drillmill_drills[r], drill_set->holes,
927 drill_set->n_holes * sizeof(struct drill_hole));
928 drillmill_radiuss[r] = radius;
929 r += drill_set->n_holes;
932 sort_drill(drillmill_drills, n_drillmill_drills);
934 gcode_f = gcode_start_gcode("drillmill", metric);
935 if (!gcode_f)
936 goto error;
937 fprintf (gcode_f, "(Drillmill file)\n");
938 fprintf (gcode_f, "(Tool diameter: %f %s)\n",
939 gcode_milltoolradius * 2, metric ? "mm" : "inch");
940 if (gcode_advanced)
942 fprintf (gcode_f, "%s=%f (safe Z)\n",
943 variable_safeZ, gcode_safeZ);
944 fprintf (gcode_f, "%s=%f (mill depth)\n",
945 variable_milldepth, gcode_milldepth);
946 fprintf (gcode_f, "%s=%f (mill plunge feedrate)\n",
947 variable_millplunge, gcode_millplunge);
948 fprintf (gcode_f, "%s=%f (mill feedrate)\n",
949 variable_millfeedrate, gcode_millfeedrate);
950 fprintf (gcode_f, "(---------------------------------)\n");
951 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
952 metric ? 21 : 20);
954 else
956 fprintf (gcode_f, "(---------------------------------)\n");
957 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
958 metric ? 21 : 20);
960 for (r = 0; r < n_drillmill_drills; r++)
962 double drillX, drillY;
964 if (metric)
966 drillX = drillmill_drills[r].x * 25.4;
967 drillY = drillmill_drills[r].y * 25.4;
969 else
971 drillX = drillmill_drills[r].x;
972 drillY = drillmill_drills[r].y;
974 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
975 fprintf (gcode_f, "G1 Z%s F%s\n",
976 variable_milldepth, variable_millplunge);
978 mill_radius = drillmill_radiuss[r] - gcode_milltoolradius;
979 inaccuracy = (metric ? 25.4 : 1.) / gcode_dpi;
980 if (mill_radius > inaccuracy)
982 int n_sides;
984 /* calculate how many polygon sides we need to stay
985 within our accuracy while avoiding a G02/G03 */
986 n_sides = M_PI / acos (mill_radius /
987 (mill_radius + inaccuracy));
988 if (n_sides < 4)
989 n_sides = 4;
990 fprintf (gcode_f, "F%s\n", variable_millfeedrate);
991 for (i = 0; i <= n_sides; i++)
993 double angle = M_PI * 2 * i / n_sides;
994 fprintf (gcode_f, "G1 X%f Y%f\n",
995 drillX + mill_radius * cos (angle),
996 drillY + mill_radius * sin (angle));
998 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
1000 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1002 if (gcode_advanced)
1003 fprintf (gcode_f, "M5 M9 M2\n");
1004 else
1005 fprintf (gcode_f, "M5\nM9\nM2\n");
1006 fclose (gcode_f);
1008 free(drillmill_radiuss);
1009 free(drillmill_drills);
1011 /* ******************* end of per-layer writing ************************ */
1013 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++)
1014 free(drills[i_drill_file].holes);
1015 free (drills);
1016 drills = NULL;
1017 n_drills = n_drills_allocated = 0;
1022 * General milling. Put this aside from the above code, as paths
1023 * are generated without taking line or curve thickness into account.
1024 * Accordingly, we need an entirely different approach.
1027 * Currently this is a rarther simple implementation, which mills
1028 * the retangular extents of the board and nothing else. This should
1029 * be sufficient for many use cases.
1031 * A better implementation would have to group the lines and polygons
1032 * on the outline layer by outer polygon and inner holes, then offset
1033 * all of them to the right side and mill that.
1035 /* a better implementation might look like this:
1036 LAYER_LOOP (PCB->Data, MAX_LAYER);
1038 if (strcmp (layer->Name, "outline") == 0)
1040 LINE_LOOP (layer);
1042 ... calculate the offset for all lines and polygons of this layer,
1043 mirror it if is_solder, then mill it ...
1045 END_LOOP;
1048 END_LOOP;
1050 for now: */
1051 { /* unconditional */
1052 double lowerX = 0., lowerY = 0., upperX = 0., upperY = 0.;
1053 double mill_distance = 0.;
1055 gcode_f = gcode_start_gcode("outline", metric);
1056 if (!gcode_f)
1057 goto error;
1058 fprintf (gcode_f, "(Outline mill file)\n");
1059 fprintf (gcode_f, "(Tool diameter: %f %s)\n",
1060 gcode_milltoolradius * 2, metric ? "mm" : "inch");
1061 if (gcode_advanced)
1063 fprintf (gcode_f, "%s=%f (safe Z)\n", variable_safeZ, gcode_safeZ);
1064 fprintf (gcode_f, "%s=%f (mill depth)\n",
1065 variable_milldepth, gcode_milldepth);
1066 fprintf (gcode_f, "%s=%f (mill plunge feedrate)\n",
1067 variable_millplunge, gcode_millplunge);
1068 fprintf (gcode_f, "%s=%f (mill feedrate)\n",
1069 variable_millfeedrate, gcode_millfeedrate);
1070 fprintf (gcode_f, "(---------------------------------)\n");
1071 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
1072 metric ? 21 : 20);
1074 else
1076 fprintf (gcode_f, "(---------------------------------)\n");
1077 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
1078 metric ? 21 : 20);
1080 if (metric)
1082 upperX = COORD_TO_MM(PCB->ExtentMaxX - PCB->ExtentMinX);
1083 upperY = COORD_TO_MM(PCB->ExtentMaxY - PCB->ExtentMinY);
1085 else
1087 upperX = COORD_TO_INCH(PCB->ExtentMaxX - PCB->ExtentMinX);
1088 upperY = COORD_TO_INCH(PCB->ExtentMaxY - PCB->ExtentMinY);
1090 lowerX -= gcode_milltoolradius;
1091 lowerY -= gcode_milltoolradius;
1092 upperX += gcode_milltoolradius;
1093 upperY += gcode_milltoolradius;
1095 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1096 /* mill the two edges adjectant to 0,0 first to disconnect the
1097 workpiece from the raw material last */
1098 fprintf (gcode_f, "G0 X%f Y%f\n", upperX, lowerY);
1099 fprintf (gcode_f, "G1 Z%s F%s\n",
1100 variable_milldepth, variable_millplunge);
1101 fprintf (gcode_f, "G1 X%f Y%f F%s\n",
1102 lowerX, lowerY, variable_millfeedrate);
1103 fprintf (gcode_f, "G1 X%f Y%f\n", lowerX, upperY);
1104 fprintf (gcode_f, "G1 X%f Y%f\n", upperX, upperY);
1105 fprintf (gcode_f, "G1 X%f Y%f\n", upperX, lowerY);
1106 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1108 if (gcode_advanced)
1109 fprintf (gcode_f, "M5 M9 M2\n");
1110 else
1111 fprintf (gcode_f, "M5\nM9\nM2\n");
1112 mill_distance = abs(gcode_safeZ - gcode_milldepth);
1113 if (metric)
1114 mill_distance /= 25.4;
1115 fprintf (gcode_f, "(end, total distance G0 %.2f mm = %.2f in)\n",
1116 mill_distance * 25.4, mill_distance);
1117 mill_distance = (upperX - lowerX + upperY - lowerY) * 2;
1118 mill_distance += abs(gcode_safeZ - gcode_milldepth);
1119 if (metric)
1120 mill_distance /= 25.4;
1121 fprintf (gcode_f, "( total distance G1 %.2f mm = %.2f in)\n",
1122 mill_distance * 25.4, mill_distance);
1123 fclose (gcode_f);
1126 error:
1127 setlocale (LC_NUMERIC, old_locale); /* restore locale */
1130 /* *** PNG export (slightly modified code from PNG export HID) ************* */
1132 static int
1133 gcode_set_layer (const char *name, int group, int empty)
1135 int idx = (group >= 0 && group < max_group) ?
1136 PCB->LayerGroups.Entries[group][0] : group;
1138 if (name == 0)
1140 name = PCB->Data->Layer[idx].Name;
1142 if (strcmp (name, "invisible") == 0)
1144 return 0;
1146 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
1147 is_mask = (SL_TYPE (idx) == SL_MASK);
1149 if (is_mask)
1151 /* Don't print masks */
1152 return 0;
1154 if (is_drill)
1157 * Print 'holes', so that we can fill gaps in the copper
1158 * layer
1160 return 1;
1162 if (group == gcode_cur_group)
1164 return 1;
1166 return 0;
1169 static hidGC
1170 gcode_make_gc (void)
1172 hidGC rv = (hidGC) malloc (sizeof (struct hid_gc_struct));
1173 rv->me_pointer = &gcode_hid;
1174 rv->cap = Trace_Cap;
1175 rv->width = 1;
1176 rv->color = (struct color_struct *) malloc (sizeof (*rv->color));
1177 rv->color->r = rv->color->g = rv->color->b = 0;
1178 rv->color->c = 0;
1179 return rv;
1182 static void
1183 gcode_destroy_gc (hidGC gc)
1185 free (gc);
1188 static void
1189 gcode_use_mask (enum mask_mode mode)
1191 /* does nothing */
1194 static void
1195 gcode_set_color (hidGC gc, const char *name)
1197 if (gcode_im == NULL)
1199 return;
1201 if (name == NULL)
1203 name = "#ff0000";
1205 if (!strcmp (name, "drill"))
1207 gc->color = black;
1208 gc->erase = 0;
1209 return;
1211 if (!strcmp (name, "erase"))
1213 /* FIXME -- should be background, not white */
1214 gc->color = white;
1215 gc->erase = 1;
1216 return;
1218 gc->color = black;
1219 gc->erase = 0;
1220 return;
1223 static void
1224 gcode_set_line_cap (hidGC gc, EndCapStyle style)
1226 gc->cap = style;
1229 static void
1230 gcode_set_line_width (hidGC gc, Coord width)
1232 gc->width = width;
1235 static void
1236 gcode_set_draw_xor (hidGC gc, int xor_)
1241 static void
1242 gcode_set_draw_faded (hidGC gc, int faded)
1246 static void
1247 use_gc (hidGC gc)
1249 int need_brush = 0;
1251 if (gc->me_pointer != &gcode_hid)
1253 fprintf (stderr, "Fatal: GC from another HID passed to gcode HID\n");
1254 abort ();
1256 if (linewidth != gc->width)
1258 /* Make sure the scaling doesn't erase lines completely */
1260 if (SCALE (gc->width) == 0 && gc->width > 0)
1261 gdImageSetThickness (im, 1);
1262 else
1264 gdImageSetThickness (gcode_im,
1265 pcb_to_gcode (gc->width + 2 * gcode_toolradius));
1266 linewidth = gc->width;
1267 need_brush = 1;
1269 if (lastbrush != gc->brush || need_brush)
1271 static void *bcache = 0;
1272 hidval bval;
1273 char name[256];
1274 char type;
1275 int r;
1277 switch (gc->cap)
1279 case Round_Cap:
1280 case Trace_Cap:
1281 type = 'C';
1282 r = pcb_to_gcode (gc->width / 2 + gcode_toolradius);
1283 break;
1284 default:
1285 case Square_Cap:
1286 r = pcb_to_gcode (gc->width + gcode_toolradius * 2);
1287 type = 'S';
1288 break;
1290 sprintf (name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g,
1291 gc->color->b, type, r);
1293 if (hid_cache_color (0, name, &bval, &bcache))
1295 gc->brush = (gdImagePtr)bval.ptr;
1297 else
1299 int bg, fg;
1300 if (type == 'C')
1301 gc->brush = gdImageCreate (2 * r + 1, 2 * r + 1);
1302 else
1303 gc->brush = gdImageCreate (r + 1, r + 1);
1304 bg = gdImageColorAllocate (gc->brush, 255, 255, 255);
1305 fg =
1306 gdImageColorAllocate (gc->brush, gc->color->r, gc->color->g,
1307 gc->color->b);
1308 gdImageColorTransparent (gc->brush, bg);
1311 * if we shrunk to a radius/box width of zero, then just use
1312 * a single pixel to draw with.
1314 if (r == 0)
1315 gdImageFilledRectangle (gc->brush, 0, 0, 0, 0, fg);
1316 else
1318 if (type == 'C')
1319 gdImageFilledEllipse (gc->brush, r, r, 2 * r, 2 * r, fg);
1320 else
1321 gdImageFilledRectangle (gc->brush, 0, 0, r, r, fg);
1323 bval.ptr = gc->brush;
1324 hid_cache_color (1, name, &bval, &bcache);
1327 gdImageSetBrush (gcode_im, gc->brush);
1328 lastbrush = gc->brush;
1333 static void
1334 gcode_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1336 use_gc (gc);
1337 gdImageRectangle (gcode_im,
1338 pcb_to_gcode (x1 - PCB->ExtentMinX - gcode_toolradius),
1339 pcb_to_gcode (y1 - PCB->ExtentMinY - gcode_toolradius),
1340 pcb_to_gcode (x2 - PCB->ExtentMinX + gcode_toolradius),
1341 pcb_to_gcode (y2 - PCB->ExtentMinY + gcode_toolradius),
1342 gc->color->c);
1343 /* printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */
1346 static void
1347 gcode_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1349 use_gc (gc);
1350 gdImageSetThickness (gcode_im, 0);
1351 linewidth = 0;
1352 gdImageFilledRectangle (gcode_im,
1353 pcb_to_gcode (x1 - PCB->ExtentMinX - gcode_toolradius),
1354 pcb_to_gcode (y1 - PCB->ExtentMinY - gcode_toolradius),
1355 pcb_to_gcode (x2 - PCB->ExtentMinX + gcode_toolradius),
1356 pcb_to_gcode (y2 - PCB->ExtentMinY + gcode_toolradius),
1357 gc->color->c);
1358 /* printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */
1361 static void
1362 gcode_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1364 if (x1 == x2 && y1 == y2)
1366 Coord w = gc->width / 2;
1367 gcode_fill_rect (gc,
1368 x1 - PCB->ExtentMinX - w, y1 - PCB->ExtentMinX - w,
1369 x1 - PCB->ExtentMinX + w, y1 - PCB->ExtentMinY + w);
1370 return;
1372 use_gc (gc);
1374 gdImageSetThickness (gcode_im, 0);
1375 linewidth = 0;
1376 gdImageLine (gcode_im,
1377 pcb_to_gcode (x1 - PCB->ExtentMinX),
1378 pcb_to_gcode (y1 - PCB->ExtentMinY),
1379 pcb_to_gcode (x2 - PCB->ExtentMinX),
1380 pcb_to_gcode (y2 - PCB->ExtentMinY), gdBrushed);
1383 static void
1384 gcode_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1385 Angle start_angle, Angle delta_angle)
1387 Angle sa, ea;
1390 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
1391 * in pcb, 0 degrees is to the left and +90 degrees is down
1393 start_angle = 180 - start_angle;
1394 delta_angle = -delta_angle;
1395 if (delta_angle > 0)
1397 sa = start_angle;
1398 ea = start_angle + delta_angle;
1400 else
1402 sa = start_angle + delta_angle;
1403 ea = start_angle;
1407 * make sure we start between 0 and 360 otherwise gd does strange
1408 * things
1410 sa = NormalizeAngle (sa);
1411 ea = NormalizeAngle (ea);
1413 #if 0
1414 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
1415 cx, cy, width, height, start_angle, delta_angle, sa, ea);
1416 printf ("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
1417 im, SCALE_X (cx), SCALE_Y (cy),
1418 SCALE (width), SCALE (height), sa, ea, gc->color->c);
1419 #endif
1420 use_gc (gc);
1421 gdImageSetThickness (gcode_im, 0);
1422 linewidth = 0;
1423 gdImageArc (gcode_im,
1424 pcb_to_gcode (cx - PCB->ExtentMinX),
1425 pcb_to_gcode (cy - PCB->ExtentMinY),
1426 pcb_to_gcode (2 * width + gcode_toolradius * 2),
1427 pcb_to_gcode (2 * height + gcode_toolradius * 2), sa, ea,
1428 gdBrushed);
1431 /* given a hole size, return the structure that currently holds the data for
1432 that hole size. If there isn't one, make it */
1433 static int _drill_size_comparator(const void* _size0, const void* _size1)
1435 double size0 = ((const struct single_size_drills*)_size0)->diameter_inches;
1436 double size1 = ((const struct single_size_drills*)_size1)->diameter_inches;
1437 if (size0 == size1)
1438 return 0;
1440 if (size0 < size1)
1441 return -1;
1443 return 1;
1445 static struct single_size_drills*
1446 get_drill(double diameter_inches)
1448 /* see if we already have this size. If so, return that structure */
1449 struct single_size_drills* drill =
1450 bsearch (&diameter_inches,
1451 drills, n_drills, sizeof (drills[0]),
1452 _drill_size_comparator);
1453 if (drill != NULL)
1454 return drill;
1456 /* haven't seen this hole size before, so make a new structure for it */
1457 if (n_drills == n_drills_allocated)
1459 n_drills_allocated += 100;
1460 drills =
1461 (struct single_size_drills *) realloc (drills,
1462 n_drills_allocated *
1463 sizeof (struct single_size_drills));
1466 /* I now add the structure to the list, making sure to keep the list
1467 * sorted. Ideally the bsearch() call above would have given me the location
1468 * to insert this element while keeping things sorted, but it doesn't. For
1469 * simplicity I manually lsearch() to find this location myself */
1471 int i = 0;
1472 for (; i<n_drills; i++)
1473 if (drills[i].diameter_inches >= diameter_inches)
1474 break;
1476 if (n_drills != i)
1477 memmove (&drills[i+1], &drills[i],
1478 (n_drills-i) * sizeof (struct single_size_drills));
1480 drills[i].diameter_inches = diameter_inches;
1481 drills[i].n_holes = 0;
1482 drills[i].n_holes_allocated = 0;
1483 drills[i].holes = NULL;
1484 n_drills++;
1486 return &drills[i];
1490 static void
1491 add_hole (struct single_size_drills* drill,
1492 double cx_inches, double cy_inches)
1494 if (drill->n_holes == drill->n_holes_allocated)
1496 drill->n_holes_allocated += 100;
1497 drill->holes =
1498 (struct drill_hole *) realloc (drill->holes,
1499 drill->n_holes_allocated *
1500 sizeof (struct drill_hole));
1503 drill->holes[ drill->n_holes ].x = cx_inches;
1504 drill->holes[ drill->n_holes ].y = cy_inches;
1505 drill->n_holes++;
1508 static void
1509 gcode_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1511 use_gc (gc);
1513 gdImageSetThickness (gcode_im, 0);
1514 linewidth = 0;
1515 gdImageFilledEllipse (gcode_im,
1516 pcb_to_gcode (cx - PCB->ExtentMinX),
1517 pcb_to_gcode (cy - PCB->ExtentMinY),
1518 pcb_to_gcode (2 * radius + gcode_toolradius * 2),
1519 pcb_to_gcode (2 * radius + gcode_toolradius * 2),
1520 gc->color->c);
1521 if (save_drill && is_drill)
1523 double diameter_inches = COORD_TO_INCH(radius*2);
1525 struct single_size_drills* drill = get_drill (diameter_inches);
1526 add_hole (drill,
1527 /* convert to inch, flip: will drill from bottom side */
1528 COORD_TO_INCH(PCB->ExtentMaxX - cx),
1529 /* PCB reverses y axis */
1530 COORD_TO_INCH(PCB->ExtentMaxY - cy));
1534 static void
1535 gcode_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1537 int i;
1538 gdPoint *points;
1540 points = (gdPoint *) malloc (n_coords * sizeof (gdPoint));
1541 if (points == NULL)
1543 fprintf (stderr, "ERROR: gcode_fill_polygon(): malloc failed\n");
1544 exit (1);
1546 use_gc (gc);
1547 for (i = 0; i < n_coords; i++)
1549 points[i].x = pcb_to_gcode (x[i] - PCB->ExtentMinX);
1550 points[i].y = pcb_to_gcode (y[i] - PCB->ExtentMinY);
1552 gdImageSetThickness (gcode_im, 0);
1553 linewidth = 0;
1554 gdImageFilledPolygon (gcode_im, points, n_coords, gc->color->c);
1555 free (points);
1556 /* printf("FillPoly\n"); */
1559 static void
1560 gcode_calibrate (double xval, double yval)
1562 CRASH;
1565 static void
1566 gcode_set_crosshair (int x, int y, int a)
1570 /* *** Miscellaneous ******************************************************* */
1572 #include "dolists.h"
1574 void
1575 hid_gcode_init ()
1577 memset (&gcode_hid, 0, sizeof (HID));
1578 memset (&gcode_graphics, 0, sizeof (HID_DRAW_API));
1580 common_nogui_init (&gcode_hid);
1581 common_draw_helpers_init (&gcode_hid);
1583 gcode_hid.struct_size = sizeof (HID);
1584 gcode_hid.name = "gcode";
1585 gcode_hid.description = "G-CODE export";
1586 gcode_hid.exporter = 1;
1587 gcode_hid.poly_before = 1;
1589 gcode_hid.get_export_options = gcode_get_export_options;
1590 gcode_hid.do_export = gcode_do_export;
1591 gcode_hid.parse_arguments = gcode_parse_arguments;
1592 gcode_hid.set_layer = gcode_set_layer;
1593 gcode_hid.calibrate = gcode_calibrate;
1594 gcode_hid.set_crosshair = gcode_set_crosshair;
1596 gcode_hid.graphics = &gcode_graphics;
1598 gcode_graphics.make_gc = gcode_make_gc;
1599 gcode_graphics.destroy_gc = gcode_destroy_gc;
1600 gcode_graphics.use_mask = gcode_use_mask;
1601 gcode_graphics.set_color = gcode_set_color;
1602 gcode_graphics.set_line_cap = gcode_set_line_cap;
1603 gcode_graphics.set_line_width = gcode_set_line_width;
1604 gcode_graphics.set_draw_xor = gcode_set_draw_xor;
1605 gcode_graphics.set_draw_faded = gcode_set_draw_faded;
1606 gcode_graphics.draw_line = gcode_draw_line;
1607 gcode_graphics.draw_arc = gcode_draw_arc;
1608 gcode_graphics.draw_rect = gcode_draw_rect;
1609 gcode_graphics.fill_circle = gcode_fill_circle;
1610 gcode_graphics.fill_polygon = gcode_fill_polygon;
1611 gcode_graphics.fill_rect = gcode_fill_rect;
1613 hid_register_hid (&gcode_hid);
1615 #include "gcode_lists.h"