Move HID drawing API prototypes into a separate header file
[geda-pcb/pcjc2.git] / src / hid / gcode / gcode.c
blob26530a5596d8bccdf263c2ecd6a5f81fa5a44a44
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 "hid_draw.h"
57 #include "../hidint.h"
58 #include <gd.h>
59 #include "hid/common/hidnogui.h"
60 #include "hid/common/draw_helpers.h"
61 #include "bitmap.h"
62 #include "curve.h"
63 #include "potracelib.h"
64 #include "trace.h"
65 #include "decompose.h"
66 #include "pcb-printf.h"
68 #include "hid/common/hidinit.h"
70 #ifdef HAVE_LIBDMALLOC
71 #include <dmalloc.h>
72 #endif
74 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented GCODE function %s.\n", __FUNCTION__); abort()
76 static HID gcode_hid;
77 static HID_DRAW gcode_graphics;
79 struct color_struct
81 /* the descriptor used by the gd library */
82 int c;
84 /* so I can figure out what rgb value c refers to */
85 unsigned int r, g, b;
88 struct hid_gc_struct
90 HID *me_pointer;
91 EndCapStyle cap;
92 int width;
93 unsigned char r, g, b;
94 int erase;
95 struct color_struct *color;
96 gdImagePtr brush;
99 static struct color_struct *black = NULL, *white = NULL;
100 static int linewidth = -1;
101 static gdImagePtr lastbrush = (gdImagePtr)((void *) -1);
103 /* gd image and file for PNG export */
104 static gdImagePtr gcode_im = NULL;
105 static FILE *gcode_f = NULL;
107 static int is_mask;
108 static int is_drill;
109 static int is_solder;
112 * Which groups of layers to export into PNG layer masks. 1 means export, 0
113 * means do not export.
115 static int gcode_export_group[MAX_LAYER];
117 /* Group that is currently exported. */
118 static int gcode_cur_group;
120 /* Filename prefix and suffix that will be used when saving files. */
121 static const char *gcode_basename = NULL;
123 /* Horizontal DPI (grid points per inch) */
124 static int gcode_dpi = -1;
126 static double gcode_cutdepth = 0; /* milling depth (inch) */
127 static double gcode_isoplunge = 0; /* isolation milling plunge feedrate */
128 static double gcode_isofeedrate = 0; /* isolation milling feedrate */
129 static char gcode_predrill;
130 static double gcode_drilldepth = 0; /* drilling depth (mm or in) */
131 static double gcode_drillfeedrate = 0; /* drilling feedrate */
132 static double gcode_safeZ = 100; /* safe Z (mm or in) */
133 static int gcode_toolradius = 0; /* iso-mill tool radius (1/100 mil) */
134 static char gcode_drillmill = 0; /* wether to drill with the mill tool */
135 static double gcode_milldepth = 0; /* outline milling depth (mm or in) */
136 static double gcode_milltoolradius = 0; /* outline-mill tool radius (mm or in)*/
137 static double gcode_millplunge = 0; /* outline-milling plunge feedrate */
138 static double gcode_millfeedrate = 0; /* outline-milling feedrate */
139 static char gcode_advanced = 0;
140 static int save_drill = 0;
142 /* structure to represent a single hole */
143 struct drill_hole
145 double x;
146 double y;
149 /* structure to represent all holes of a given size */
150 struct single_size_drills
152 double diameter_inches;
154 int n_holes;
155 int n_holes_allocated;
156 struct drill_hole* holes;
159 /* at the start we have no drills at all */
160 static struct single_size_drills* drills = NULL;
161 static int n_drills = 0;
162 static int n_drills_allocated = 0;
164 HID_Attribute gcode_attribute_list[] = {
165 /* other HIDs expect this to be first. */
166 {"basename", "File name prefix and suffix,\n"
167 "layer names will be inserted before the suffix.",
168 HID_String, 0, 0, {0, 0, 0}, 0, 0},
169 #define HA_basename 0
171 {"measurement-unit", "Measurement unit used in the G-code output.",
172 HID_Unit, 0, 0, {3, 0, 0}, NULL, 0},
173 #define HA_unit 1
175 {"dpi", "Accuracy of the mill path generation in pixels/inch.",
176 HID_Integer, 0, 2000, {600, 0, 0}, 0, 0},
177 #define HA_dpi 2
179 {"safe-Z", "Safe Z for traverse movements of all operations.",
180 HID_Real, -1000, 10000, {0, 0, 2}, 0, 0},
181 #define HA_safeZ 3
183 {"iso-mill-depth", "Isolation milling depth.",
184 HID_Real, -1000, 1000, {0, 0, -0.05}, 0, 0},
185 #define HA_cutdepth 4
187 {"iso-tool-diameter", "Isolation milling tool diameter.",
188 HID_Real, 0, 10000, {0, 0, 0.2}, 0, 0},
189 #define HA_tooldiameter 5
191 {"iso-tool-plunge", "Isolation milling feedrate when plunging into\n"
192 "the material.",
193 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0},
194 #define HA_isoplunge 6
196 {"iso-tool-feedrate", "Isolation milling feedrate.",
197 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
198 #define HA_isofeedrate 7
200 {"predrill", "Wether to pre-drill all drill spots with the isolation milling\n"
201 "tool. Drill depth is iso-mill-depth here. This feature eases\n"
202 "and enhances accuracy of manual drilling.",
203 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
204 #define HA_predrill 8
206 {"drill-depth", "Drilling depth.",
207 HID_Real, -10000, 10000, {0, 0, -2}, 0, 0},
208 #define HA_drilldepth 9
210 {"drill-feedrate", "Drilling feedrate.",
211 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
212 #define HA_drillfeedrate 10
214 {"drill-mill", "Wether to produce drill holes equal or bigger than the\n"
215 "milling tool diameter with the milling tool.\n"
216 "With the milling tool bigger holes can be accurately sized\n"
217 "without changing the tool",
218 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
219 #define HA_drillmill 11
221 {"outline-mill-depth", "Milling depth when milling the outline.\n"
222 "Currently, only the rectangular extents of the\n"
223 "board are milled, no polygonal outlines or holes.",
224 HID_Real, -10000, 10000, {0, 0, -1}, 0, 0},
225 #define HA_milldepth 12
227 {"outline-tool-diameter", "Diameter of the tool used for outline milling.",
228 HID_Real, 0, 10000, {0, 0, 1}, 0, 0},
229 #define HA_milltooldiameter 13
231 {"outline-mill-plunge", "Outline milling feedrate when plunging into\n"
232 "the material",
233 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0},
234 #define HA_millplunge 14
236 {"outline-mill-feedrate", "Outline milling feedrate",
237 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
238 #define HA_millfeedrate 15
240 {"advanced-gcode", "Wether to produce G-code for advanced interpreters,\n"
241 "like using variables or drill cycles. Not all\n"
242 "machine controllers understand this, but it allows\n"
243 "better hand-editing of the resulting files.",
244 HID_Boolean, 0, 0, {-1, 0, 0}, 0, 0},
245 #define HA_advanced 16
248 #define NUM_OPTIONS (sizeof(gcode_attribute_list)/sizeof(gcode_attribute_list[0]))
250 REGISTER_ATTRIBUTES (gcode_attribute_list)
251 static HID_Attr_Val gcode_values[NUM_OPTIONS];
253 /* *** Utility funcions **************************************************** */
255 /* convert from default PCB units to gcode units */
256 static int pcb_to_gcode (int pcb)
258 return round(COORD_TO_INCH(pcb) * gcode_dpi);
261 /* Fits the given layer name into basename, just before the suffix */
262 static void
263 gcode_get_filename (char *filename, const char *layername)
265 char *pt;
266 char suffix[MAXPATHLEN];
268 suffix[0] = '\0';
269 pt = strrchr (gcode_basename, '.');
270 if (pt && pt > strrchr (gcode_basename, '/'))
271 strcpy (suffix, pt);
272 else
273 pt = NULL;
275 strcpy (filename, gcode_basename);
276 if (pt)
277 *(filename + (pt - gcode_basename)) = '\0';
278 strcat (filename, "-");
279 strcat (filename, layername);
280 strcat (filename, suffix);
282 // result is in char *filename
285 /* Sorts drills to produce a short tool path. I start with the hole nearest
286 * (0,0) and for each subsequent one, find the hole nearest to the previous.
287 * This isn't guaranteed to find the shortest path, but should be good enough.
288 * Note that this is O(N^2). We can't use the O(N logN) sort, since our
289 * shortest-distance origin changes with every point */
290 static void
291 sort_drill (struct drill_hole *drill, int n_drill)
293 /* I start out by looking for points closest to (0,0) */
294 struct drill_hole nearest_target = { 0, 0 };
296 /* I sort my list by finding the correct point to fill each slot. I don't need
297 to look at the last one, since it'll be in the right place automatically */
298 for (int j = 0; j < n_drill-1; j++)
300 double dmin = 1e20;
301 int imin = 0;
302 /* look through remaining elements to find the next drill point. This is
303 the one nearest to nearest_target */
304 for (int i = j; i < n_drill; i++)
306 double d =
307 (drill[i].x - nearest_target.x) * (drill[i].x - nearest_target.x) +
308 (drill[i].y - nearest_target.y) * (drill[i].y - nearest_target.y);
309 if (d < dmin)
311 imin = i;
312 dmin = d;
315 /* printf("j=%d imin=%d dmin=%f nearest_target=(%f,%f)\n",j,imin,dmin,
316 nearest_target.x,nearest_target.y); */
317 if (j != imin)
319 struct drill_hole tmp;
320 tmp = drill[j];
321 drill[j] = drill[imin];
322 drill[imin] = tmp;
325 nearest_target = drill[j];
329 /* *** Main export callback ************************************************ */
331 static void
332 gcode_parse_arguments (int *argc, char ***argv)
334 hid_register_attributes (gcode_attribute_list,
335 sizeof (gcode_attribute_list) /
336 sizeof (gcode_attribute_list[0]));
337 hid_parse_command_line (argc, argv);
340 static HID_Attribute *
341 gcode_get_export_options (int *n)
343 static char *last_made_filename = 0;
344 static int last_unit_value = -1;
346 if (gcode_attribute_list[HA_unit].default_val.int_value == last_unit_value)
348 if (Settings.grid_unit)
349 gcode_attribute_list[HA_unit].default_val.int_value = Settings.grid_unit->index;
350 else
351 gcode_attribute_list[HA_unit].default_val.int_value = get_unit_struct ("mil")->index;
352 last_unit_value = gcode_attribute_list[HA_unit].default_val.int_value;
355 if (PCB)
357 derive_default_filename (PCB->Filename,
358 &gcode_attribute_list[HA_basename],
359 ".gcode", &last_made_filename);
361 if (n)
363 *n = NUM_OPTIONS;
365 return gcode_attribute_list;
368 /* Populates gcode_export_group array */
369 static void
370 gcode_choose_groups ()
372 int n, m;
373 LayerType *layer;
375 /* Set entire array to 0 (don't export any layer groups by default */
376 memset (gcode_export_group, 0, sizeof (gcode_export_group));
378 for (n = 0; n < max_copper_layer; n++)
380 layer = &PCB->Data->Layer[n];
382 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
384 /* layer isn't empty */
387 * is this check necessary? It seems that special
388 * layers have negative indexes?
391 if (SL_TYPE (n) == 0)
393 /* layer is a copper layer */
394 m = GetLayerGroupNumberByNumber (n);
396 /* the export layer */
397 gcode_export_group[m] = 1;
403 static void
404 gcode_alloc_colors ()
407 * Allocate white and black -- the first color allocated becomes the
408 * background color
411 white = (struct color_struct *) malloc (sizeof (*white));
412 white->r = white->g = white->b = 255;
413 white->c = gdImageColorAllocate (gcode_im, white->r, white->g, white->b);
415 black = (struct color_struct *) malloc (sizeof (*black));
416 black->r = black->g = black->b = 0;
417 black->c = gdImageColorAllocate (gcode_im, black->r, black->g, black->b);
420 static void
421 gcode_start_png ()
423 gcode_im = gdImageCreate (pcb_to_gcode (PCB->ExtentMaxX - PCB->ExtentMinX),
424 pcb_to_gcode (PCB->ExtentMaxY - PCB->ExtentMinY));
425 gcode_alloc_colors ();
428 static void
429 gcode_finish_png (const char *layername)
431 #ifdef HAVE_GDIMAGEPNG
432 char *pngname, *filename;
433 FILE *file = NULL;
435 pngname = (char *)malloc (MAXPATHLEN);
436 gcode_get_filename (pngname, layername);
437 filename = g_strdup_printf ("%s.png", pngname);
438 free(pngname);
440 file = fopen (filename, "wb");
441 g_free (filename);
443 gdImagePng (gcode_im, file);
444 fclose (file);
445 #else
446 Message ("GCODE: PNG not supported by gd. Can't write layer mask.\n");
447 #endif
448 gdImageDestroy (gcode_im);
450 free (white);
451 free (black);
452 gcode_im = NULL;
455 static void
456 gcode_start_png_export ()
458 BoxType region;
460 region.X1 = PCB->ExtentMinX;
461 region.Y1 = PCB->ExtentMinY;
462 region.X2 = PCB->ExtentMaxX;
463 region.Y2 = PCB->ExtentMaxY;
465 linewidth = -1;
466 lastbrush = (gdImagePtr)((void *) -1);
468 hid_expose_callback (&gcode_hid, &region, 0);
471 static FILE *
472 gcode_start_gcode (const char *layername, bool metric)
474 FILE *file = NULL;
475 char buffer[MAXPATHLEN];
476 time_t t;
478 gcode_get_filename (buffer, layername);
479 file = fopen (buffer, "wb");
480 if ( ! file)
482 perror (buffer);
483 return NULL;
485 fprintf (file, "(Created by G-code exporter)\n");
486 t = time (NULL);
487 snprintf (buffer, sizeof(buffer), "%s", ctime (&t));
488 buffer[strlen (buffer) - 1] = '\0'; // drop the newline
489 fprintf (file, "(%s)\n", buffer);
490 fprintf (file, "(Units: %s)\n", metric ? "mm" : "inch");
491 if (metric)
492 pcb_fprintf (file, "(Board size: %.2mm x %.2mm mm)\n",
493 PCB->ExtentMaxX - PCB->ExtentMinX,
494 PCB->ExtentMaxY - PCB->ExtentMinY);
495 else
496 pcb_fprintf (file, "(Board size: %.2mi x %.2mi inches)\n",
497 PCB->ExtentMaxX - PCB->ExtentMinX,
498 PCB->ExtentMaxY - PCB->ExtentMinY);
500 return file;
503 static void
504 gcode_do_export (HID_Attr_Val * options)
506 int save_ons[MAX_LAYER + 2];
507 int i, idx;
508 const Unit *unit;
509 double scale = 0, d = 0;
510 int r, c, v, p, metric;
511 path_t *plist = NULL;
512 potrace_bitmap_t *bm = NULL;
513 potrace_param_t param_default = {
514 2, /* turnsize */
515 POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */
516 1.0, /* alphamax */
517 1, /* opticurve */
518 0.2, /* opttolerance */
520 NULL, /* callback function */
521 NULL, /* callback data */
522 0.0, 1.0, /* progress range */
523 0.0, /* granularity */
526 char variable_safeZ[20], variable_cutdepth[20];
527 char variable_isoplunge[20], variable_isofeedrate[20];
528 char variable_drilldepth[20], variable_milldepth[20];
529 char variable_millplunge[20], variable_millfeedrate[20];
530 char *old_locale = setlocale (LC_NUMERIC, NULL);
532 if (!options)
534 gcode_get_export_options (0);
535 for (i = 0; i < NUM_OPTIONS; i++)
537 gcode_values[i] = gcode_attribute_list[i].default_val;
539 options = gcode_values;
541 gcode_basename = options[HA_basename].str_value;
542 if (!gcode_basename)
544 gcode_basename = "pcb-out.gcode";
546 gcode_dpi = options[HA_dpi].int_value;
547 if (gcode_dpi < 0)
549 fprintf (stderr, "ERROR: dpi may not be < 0\n");
550 return;
552 unit = &(get_unit_list() [options[HA_unit].int_value]);
553 metric = (unit->family == METRIC);
554 scale = metric ? 1.0 / coord_to_unit (unit, MM_TO_COORD (1.0))
555 : 1.0 / coord_to_unit (unit, INCH_TO_COORD (1.0));
557 gcode_cutdepth = options[HA_cutdepth].real_value * scale;
558 gcode_isoplunge = options[HA_isoplunge].real_value * scale;
559 gcode_isofeedrate = options[HA_isofeedrate].real_value * scale;
560 gcode_predrill = options[HA_predrill].int_value;
561 gcode_drilldepth = options[HA_drilldepth].real_value * scale;
562 gcode_drillfeedrate = options[HA_drillfeedrate].real_value * scale;
563 gcode_safeZ = options[HA_safeZ].real_value * scale;
564 gcode_toolradius = metric
565 ? MM_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale)
566 : INCH_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale);
567 gcode_drillmill = options[HA_drillmill].int_value;
568 gcode_milldepth = options[HA_milldepth].real_value * scale;
569 gcode_milltoolradius = options[HA_milltooldiameter].real_value / 2 * scale;
570 gcode_millplunge = options[HA_millplunge].real_value * scale;
571 gcode_millfeedrate = options[HA_millfeedrate].real_value * scale;
572 gcode_advanced = options[HA_advanced].int_value;
573 gcode_choose_groups ();
574 setlocale (LC_NUMERIC, "C"); /* use . as separator */
575 if (gcode_advanced)
577 /* give each variable distinct names, even if they don't appear
578 together in a file. This allows to join output files */
579 strcpy (variable_safeZ, "#100");
580 strcpy (variable_cutdepth, "#101");
581 strcpy (variable_isoplunge, "#102");
582 strcpy (variable_isofeedrate, "#103");
583 strcpy (variable_drilldepth, "#104");
584 strcpy (variable_milldepth, "#105");
585 strcpy (variable_millplunge, "#106");
586 strcpy (variable_millfeedrate, "#107");
588 else
590 snprintf (variable_safeZ, 20, "%f", gcode_safeZ);
591 snprintf (variable_cutdepth, 20, "%f", gcode_cutdepth);
592 snprintf (variable_isoplunge, 20, "%f", gcode_isoplunge);
593 snprintf (variable_isofeedrate, 20, "%f", gcode_isofeedrate);
594 snprintf (variable_drilldepth, 20, "%f", gcode_drilldepth);
595 snprintf (variable_milldepth, 20, "%f", gcode_milldepth);
596 snprintf (variable_millplunge, 20, "%f", gcode_millplunge);
597 snprintf (variable_millfeedrate, 20, "%f", gcode_millfeedrate);
599 UpdateExtents();
601 for (i = 0; i < MAX_LAYER; i++)
603 if (gcode_export_group[i])
605 gcode_cur_group = i;
607 /* magic */
608 idx = (i >= 0 && i < max_group) ?
609 PCB->LayerGroups.Entries[i][0] : i;
610 is_solder =
611 (GetLayerGroupNumberByNumber (idx) ==
612 GetLayerGroupNumberByNumber (solder_silk_layer)) ? 1 : 0;
613 save_drill = is_solder; /* save drills for one layer only */
614 gcode_start_png ();
615 hid_save_and_show_layer_ons (save_ons);
616 gcode_start_png_export ();
617 hid_restore_layer_ons (save_ons);
619 /* ***************** gcode conversion *************************** */
620 /* potrace uses a different kind of bitmap; for simplicity gcode_im is
621 copied to this format and flipped as needed along the way */
622 bm = bm_new (gdImageSX (gcode_im), gdImageSY (gcode_im));
623 for (r = 0; r < gdImageSX (gcode_im); r++)
625 for (c = 0; c < gdImageSY (gcode_im); c++)
627 if (is_solder)
628 v = /* flip vertically and horizontally */
629 gdImageGetPixel (gcode_im, gdImageSX (gcode_im) - 1 - r,
630 gdImageSY (gcode_im) - 1 - c);
631 else
632 v = /* flip only vertically */
633 gdImageGetPixel (gcode_im, r,
634 gdImageSY (gcode_im) - 1 - c);
635 p = (gcode_im->red[v] || gcode_im->green[v]
636 || gcode_im->blue[v]) ? 0 : 0xFFFFFF;
637 BM_PUT (bm, r, c, p);
640 if (is_solder)
641 { /* flip back layer, used only for PNG output */
642 gdImagePtr temp_im =
643 gdImageCreate (gdImageSX (gcode_im), gdImageSY (gcode_im));
644 gdImageColorAllocate (temp_im, white->r, white->g, white->b);
645 gdImageColorAllocate (temp_im, black->r, black->g, black->b);
646 gdImageCopy (temp_im, gcode_im, 0, 0, 0, 0,
647 gdImageSX (gcode_im), gdImageSY (gcode_im));
648 for (r = 0; r < gdImageSX (gcode_im); r++)
650 for (c = 0; c < gdImageSY (gcode_im); c++)
652 gdImageSetPixel (gcode_im, r, c,
653 gdImageGetPixel (temp_im,
654 gdImageSX (gcode_im) -
655 1 - r, c));
658 gdImageDestroy (temp_im);
660 gcode_finish_png (layer_type_to_file_name (idx, FNS_fixed));
661 plist = NULL;
662 gcode_f = gcode_start_gcode (layer_type_to_file_name (idx, FNS_fixed),
663 metric);
664 if (!gcode_f)
666 bm_free (bm);
667 goto error;
669 fprintf (gcode_f, "(Accuracy %d dpi)\n", gcode_dpi);
670 fprintf (gcode_f, "(Tool diameter: %f %s)\n",
671 options[HA_tooldiameter].real_value * scale,
672 metric ? "mm" : "inch");
673 if (gcode_advanced)
675 fprintf (gcode_f, "%s=%f (safe Z)\n",
676 variable_safeZ, gcode_safeZ);
677 fprintf (gcode_f, "%s=%f (cutting depth)\n",
678 variable_cutdepth, gcode_cutdepth);
679 fprintf (gcode_f, "%s=%f (plunge feedrate)\n",
680 variable_isoplunge, gcode_isoplunge);
681 fprintf (gcode_f, "%s=%f (feedrate)\n",
682 variable_isofeedrate, gcode_isofeedrate);
684 if (gcode_predrill && save_drill)
685 fprintf (gcode_f, "(with predrilling)\n");
686 else
687 fprintf (gcode_f, "(no predrilling)\n");
688 fprintf (gcode_f, "(---------------------------------)\n");
689 if (gcode_advanced)
690 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
691 metric ? 21 : 20);
692 else
693 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
694 metric ? 21 : 20);
695 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
696 /* extract contour points from image */
697 r = bm_to_pathlist (bm, &plist, &param_default);
698 if (r)
700 fprintf (stderr, "ERROR: pathlist function failed\n");
701 goto error;
703 /* generate best polygon and write vertices in g-code format */
704 d = process_path (plist, &param_default, bm, gcode_f,
705 metric ? 25.4 / gcode_dpi : 1.0 / gcode_dpi,
706 variable_cutdepth, variable_safeZ,
707 variable_isoplunge, variable_isofeedrate);
708 if (d < 0)
710 fprintf (stderr, "ERROR: path process function failed\n");
711 goto error;
713 if (gcode_predrill && save_drill)
715 int n_all_drills = 0;
716 struct drill_hole* all_drills = NULL;
717 /* count all drills to be predrilled */
718 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
720 struct single_size_drills* drill_set = &drills[i_drill_sets];
722 /* don't predrill drillmill holes */
723 if (gcode_drillmill) {
724 double radius = metric ?
725 drill_set->diameter_inches * 25.4 / 2:
726 drill_set->diameter_inches / 2;
728 if (gcode_milltoolradius < radius)
729 continue;
732 n_all_drills += drill_set->n_holes;
734 /* for sorting regardless of size, copy all drills to be
735 predrilled into one new structure */
736 all_drills = (struct drill_hole *)
737 malloc (n_all_drills * sizeof (struct drill_hole));
738 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
740 struct single_size_drills* drill_set = &drills[i_drill_sets];
742 /* don't predrill drillmill holes */
743 if (gcode_drillmill) {
744 double radius = metric ?
745 drill_set->diameter_inches * 25.4 / 2:
746 drill_set->diameter_inches / 2;
748 if (gcode_milltoolradius < radius)
749 continue;
752 memcpy(&all_drills[r], drill_set->holes,
753 drill_set->n_holes * sizeof(struct drill_hole));
755 sort_drill(all_drills, n_all_drills);
756 /* write that (almost the same code as writing the drill file) */
757 fprintf (gcode_f, "(predrilling)\n");
758 fprintf (gcode_f, "F%s\n", variable_isoplunge);
760 for (r = 0; r < n_all_drills; r++)
762 double drillX, drillY;
764 if (metric)
766 drillX = all_drills[r].x * 25.4;
767 drillY = all_drills[r].y * 25.4;
769 else
771 drillX = all_drills[r].x;
772 drillY = all_drills[r].y;
774 if (gcode_advanced)
775 fprintf (gcode_f, "G81 X%f Y%f Z%s R%s\n", drillX, drillY,
776 variable_cutdepth, variable_safeZ);
777 else
779 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
780 fprintf (gcode_f, "G1 Z%s\n", variable_cutdepth);
781 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
784 fprintf (gcode_f, "(%d predrills)\n", n_all_drills);
785 free(all_drills);
787 if (metric)
788 fprintf (gcode_f, "(milling distance %.2fmm = %.2fin)\n", d,
789 d * 1 / 25.4);
790 else
791 fprintf (gcode_f, "(milling distance %.2fmm = %.2fin)\n",
792 25.4 * d, d);
793 if (gcode_advanced)
794 fprintf (gcode_f, "M5 M9 M2\n");
795 else
796 fprintf (gcode_f, "M5\nM9\nM2\n");
797 pathlist_free (plist);
798 bm_free (bm);
799 fclose (gcode_f);
800 gcode_f = NULL;
801 if (save_drill)
803 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++)
805 struct single_size_drills* drill = &drills[i_drill_file];
807 /* don't drill drillmill holes */
808 if (gcode_drillmill) {
809 double radius = metric ?
810 drill->diameter_inches * 25.4 / 2:
811 drill->diameter_inches / 2;
813 if (gcode_milltoolradius < radius)
814 continue;
817 d = 0;
818 sort_drill (drill->holes, drill->n_holes);
821 // get the filename with the drill size encoded in it
822 char layername[32];
823 snprintf(layername, sizeof(layername),
824 "%.4f.drill",
825 metric ?
826 drill->diameter_inches * 25.4 :
827 drill->diameter_inches);
828 gcode_f = gcode_start_gcode(layername, metric);
830 if (!gcode_f)
831 goto error;
832 fprintf (gcode_f, "(Drill file: %d drills)\n", drill->n_holes);
833 if (metric)
834 fprintf (gcode_f, "(Drill diameter: %f mm)\n",
835 drill->diameter_inches * 25.4);
836 else
837 fprintf (gcode_f, "(Drill diameter: %f inch)\n",
838 drill->diameter_inches);
839 if (gcode_advanced)
841 fprintf (gcode_f, "%s=%f (safe Z)\n",
842 variable_safeZ, gcode_safeZ);
843 fprintf (gcode_f, "%s=%f (drill depth)\n",
844 variable_drilldepth, gcode_drilldepth);
845 fprintf (gcode_f, "(---------------------------------)\n");
846 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%f\n",
847 metric ? 21 : 20, gcode_drillfeedrate);
849 else
851 fprintf (gcode_f, "(---------------------------------)\n");
852 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\nF%f\n",
853 metric ? 21 : 20, gcode_drillfeedrate);
855 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
856 for (r = 0; r < drill->n_holes; r++)
858 double drillX, drillY;
860 if (metric)
862 drillX = drill->holes[r].x * 25.4;
863 drillY = drill->holes[r].y * 25.4;
865 else
867 drillX = drill->holes[r].x;
868 drillY = drill->holes[r].y;
870 if (gcode_advanced)
871 fprintf (gcode_f, "G81 X%f Y%f Z%s R%s\n", drillX, drillY,
872 variable_drilldepth, variable_safeZ);
873 else
875 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
876 fprintf (gcode_f, "G1 Z%s\n", variable_drilldepth);
877 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
879 if (r > 0)
880 d += Distance(drill->holes[r - 1].x, drill->holes[r - 1].y,
881 drill->holes[r ].x, drill->holes[r ].y);
883 if (gcode_advanced)
884 fprintf (gcode_f, "M5 M9 M2\n");
885 else
886 fprintf (gcode_f, "M5\nM9\nM2\n");
887 fprintf (gcode_f, "(end, total distance %.2fmm = %.2fin)\n",
888 25.4 * d, d);
889 fclose (gcode_f);
892 /* ******************* handle drill-milling **************************** */
893 if (save_drill && gcode_drillmill)
895 int n_drillmill_drills = 0;
896 struct drill_hole* drillmill_drills = NULL;
897 double* drillmill_radiuss = NULL;
898 double mill_radius, inaccuracy;
900 /* count drillmill drills */
901 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
903 struct single_size_drills* drill_set = &drills[i_drill_sets];
904 double radius = metric ?
905 drill_set->diameter_inches * 25.4 / 2:
906 drill_set->diameter_inches / 2;
908 if (gcode_milltoolradius >= radius)
909 n_drillmill_drills += drill_set->n_holes;
911 /* for sorting regardless of size, copy all available drills
912 into one new structure */
913 drillmill_drills = (struct drill_hole *)
914 malloc (n_drillmill_drills * sizeof (struct drill_hole));
915 drillmill_radiuss = (double *)
916 malloc (n_drillmill_drills * sizeof (double));
917 r = 0;
918 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
920 struct single_size_drills* drill_set = &drills[i_drill_sets];
921 double radius = metric ?
922 drill_set->diameter_inches * 25.4 / 2:
923 drill_set->diameter_inches / 2;
925 if (gcode_milltoolradius >= radius)
927 memcpy(&drillmill_drills[r], drill_set->holes,
928 drill_set->n_holes * sizeof(struct drill_hole));
929 drillmill_radiuss[r] = radius;
930 r += drill_set->n_holes;
933 sort_drill(drillmill_drills, n_drillmill_drills);
935 gcode_f = gcode_start_gcode("drillmill", metric);
936 if (!gcode_f)
937 goto error;
938 fprintf (gcode_f, "(Drillmill file)\n");
939 fprintf (gcode_f, "(Tool diameter: %f %s)\n",
940 gcode_milltoolradius * 2, metric ? "mm" : "inch");
941 if (gcode_advanced)
943 fprintf (gcode_f, "%s=%f (safe Z)\n",
944 variable_safeZ, gcode_safeZ);
945 fprintf (gcode_f, "%s=%f (mill depth)\n",
946 variable_milldepth, gcode_milldepth);
947 fprintf (gcode_f, "%s=%f (mill plunge feedrate)\n",
948 variable_millplunge, gcode_millplunge);
949 fprintf (gcode_f, "%s=%f (mill feedrate)\n",
950 variable_millfeedrate, gcode_millfeedrate);
951 fprintf (gcode_f, "(---------------------------------)\n");
952 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
953 metric ? 21 : 20);
955 else
957 fprintf (gcode_f, "(---------------------------------)\n");
958 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
959 metric ? 21 : 20);
961 for (r = 0; r < n_drillmill_drills; r++)
963 double drillX, drillY;
965 if (metric)
967 drillX = drillmill_drills[r].x * 25.4;
968 drillY = drillmill_drills[r].y * 25.4;
970 else
972 drillX = drillmill_drills[r].x;
973 drillY = drillmill_drills[r].y;
975 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
976 fprintf (gcode_f, "G1 Z%s F%s\n",
977 variable_milldepth, variable_millplunge);
979 mill_radius = drillmill_radiuss[r] - gcode_milltoolradius;
980 inaccuracy = (metric ? 25.4 : 1.) / gcode_dpi;
981 if (mill_radius > inaccuracy)
983 int n_sides;
985 /* calculate how many polygon sides we need to stay
986 within our accuracy while avoiding a G02/G03 */
987 n_sides = M_PI / acos (mill_radius /
988 (mill_radius + inaccuracy));
989 if (n_sides < 4)
990 n_sides = 4;
991 fprintf (gcode_f, "F%s\n", variable_millfeedrate);
992 for (i = 0; i <= n_sides; i++)
994 double angle = M_PI * 2 * i / n_sides;
995 fprintf (gcode_f, "G1 X%f Y%f\n",
996 drillX + mill_radius * cos (angle),
997 drillY + mill_radius * sin (angle));
999 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
1001 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1003 if (gcode_advanced)
1004 fprintf (gcode_f, "M5 M9 M2\n");
1005 else
1006 fprintf (gcode_f, "M5\nM9\nM2\n");
1007 fclose (gcode_f);
1009 free(drillmill_radiuss);
1010 free(drillmill_drills);
1012 /* ******************* end of per-layer writing ************************ */
1014 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++)
1015 free(drills[i_drill_file].holes);
1016 free (drills);
1017 drills = NULL;
1018 n_drills = n_drills_allocated = 0;
1023 * General milling. Put this aside from the above code, as paths
1024 * are generated without taking line or curve thickness into account.
1025 * Accordingly, we need an entirely different approach.
1028 * Currently this is a rarther simple implementation, which mills
1029 * the retangular extents of the board and nothing else. This should
1030 * be sufficient for many use cases.
1032 * A better implementation would have to group the lines and polygons
1033 * on the outline layer by outer polygon and inner holes, then offset
1034 * all of them to the right side and mill that.
1036 /* a better implementation might look like this:
1037 LAYER_LOOP (PCB->Data, MAX_LAYER);
1039 if (strcmp (layer->Name, "outline") == 0)
1041 LINE_LOOP (layer);
1043 ... calculate the offset for all lines and polygons of this layer,
1044 mirror it if is_solder, then mill it ...
1046 END_LOOP;
1049 END_LOOP;
1051 for now: */
1052 { /* unconditional */
1053 double lowerX = 0., lowerY = 0., upperX = 0., upperY = 0.;
1054 double mill_distance = 0.;
1056 gcode_f = gcode_start_gcode("outline", metric);
1057 if (!gcode_f)
1058 goto error;
1059 fprintf (gcode_f, "(Outline mill file)\n");
1060 fprintf (gcode_f, "(Tool diameter: %f %s)\n",
1061 gcode_milltoolradius * 2, metric ? "mm" : "inch");
1062 if (gcode_advanced)
1064 fprintf (gcode_f, "%s=%f (safe Z)\n", variable_safeZ, gcode_safeZ);
1065 fprintf (gcode_f, "%s=%f (mill depth)\n",
1066 variable_milldepth, gcode_milldepth);
1067 fprintf (gcode_f, "%s=%f (mill plunge feedrate)\n",
1068 variable_millplunge, gcode_millplunge);
1069 fprintf (gcode_f, "%s=%f (mill feedrate)\n",
1070 variable_millfeedrate, gcode_millfeedrate);
1071 fprintf (gcode_f, "(---------------------------------)\n");
1072 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
1073 metric ? 21 : 20);
1075 else
1077 fprintf (gcode_f, "(---------------------------------)\n");
1078 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
1079 metric ? 21 : 20);
1081 if (metric)
1083 upperX = COORD_TO_MM(PCB->ExtentMaxX - PCB->ExtentMinX);
1084 upperY = COORD_TO_MM(PCB->ExtentMaxY - PCB->ExtentMinY);
1086 else
1088 upperX = COORD_TO_INCH(PCB->ExtentMaxX - PCB->ExtentMinX);
1089 upperY = COORD_TO_INCH(PCB->ExtentMaxY - PCB->ExtentMinY);
1091 lowerX -= gcode_milltoolradius;
1092 lowerY -= gcode_milltoolradius;
1093 upperX += gcode_milltoolradius;
1094 upperY += gcode_milltoolradius;
1096 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1097 /* mill the two edges adjectant to 0,0 first to disconnect the
1098 workpiece from the raw material last */
1099 fprintf (gcode_f, "G0 X%f Y%f\n", upperX, lowerY);
1100 fprintf (gcode_f, "G1 Z%s F%s\n",
1101 variable_milldepth, variable_millplunge);
1102 fprintf (gcode_f, "G1 X%f Y%f F%s\n",
1103 lowerX, lowerY, variable_millfeedrate);
1104 fprintf (gcode_f, "G1 X%f Y%f\n", lowerX, upperY);
1105 fprintf (gcode_f, "G1 X%f Y%f\n", upperX, upperY);
1106 fprintf (gcode_f, "G1 X%f Y%f\n", upperX, lowerY);
1107 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1109 if (gcode_advanced)
1110 fprintf (gcode_f, "M5 M9 M2\n");
1111 else
1112 fprintf (gcode_f, "M5\nM9\nM2\n");
1113 mill_distance = abs(gcode_safeZ - gcode_milldepth);
1114 if (metric)
1115 mill_distance /= 25.4;
1116 fprintf (gcode_f, "(end, total distance G0 %.2f mm = %.2f in)\n",
1117 mill_distance * 25.4, mill_distance);
1118 mill_distance = (upperX - lowerX + upperY - lowerY) * 2;
1119 mill_distance += abs(gcode_safeZ - gcode_milldepth);
1120 if (metric)
1121 mill_distance /= 25.4;
1122 fprintf (gcode_f, "( total distance G1 %.2f mm = %.2f in)\n",
1123 mill_distance * 25.4, mill_distance);
1124 fclose (gcode_f);
1127 error:
1128 setlocale (LC_NUMERIC, old_locale); /* restore locale */
1131 /* *** PNG export (slightly modified code from PNG export HID) ************* */
1133 static int
1134 gcode_set_layer (const char *name, int group, int empty)
1136 int idx = (group >= 0 && group < max_group) ?
1137 PCB->LayerGroups.Entries[group][0] : group;
1139 if (name == 0)
1141 name = PCB->Data->Layer[idx].Name;
1143 if (strcmp (name, "invisible") == 0)
1145 return 0;
1147 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
1148 is_mask = (SL_TYPE (idx) == SL_MASK);
1150 if (is_mask)
1152 /* Don't print masks */
1153 return 0;
1155 if (is_drill)
1158 * Print 'holes', so that we can fill gaps in the copper
1159 * layer
1161 return 1;
1163 if (group == gcode_cur_group)
1165 return 1;
1167 return 0;
1170 static hidGC
1171 gcode_make_gc (void)
1173 hidGC rv = (hidGC) malloc (sizeof (struct hid_gc_struct));
1174 rv->me_pointer = &gcode_hid;
1175 rv->cap = Trace_Cap;
1176 rv->width = 1;
1177 rv->color = (struct color_struct *) malloc (sizeof (*rv->color));
1178 rv->color->r = rv->color->g = rv->color->b = 0;
1179 rv->color->c = 0;
1180 return rv;
1183 static void
1184 gcode_destroy_gc (hidGC gc)
1186 free (gc);
1189 static void
1190 gcode_use_mask (enum mask_mode mode)
1192 /* does nothing */
1195 static void
1196 gcode_set_color (hidGC gc, const char *name)
1198 if (gcode_im == NULL)
1200 return;
1202 if (name == NULL)
1204 name = "#ff0000";
1206 if (!strcmp (name, "drill"))
1208 gc->color = black;
1209 gc->erase = 0;
1210 return;
1212 if (!strcmp (name, "erase"))
1214 /* FIXME -- should be background, not white */
1215 gc->color = white;
1216 gc->erase = 1;
1217 return;
1219 gc->color = black;
1220 gc->erase = 0;
1221 return;
1224 static void
1225 gcode_set_line_cap (hidGC gc, EndCapStyle style)
1227 gc->cap = style;
1230 static void
1231 gcode_set_line_width (hidGC gc, Coord width)
1233 gc->width = width;
1236 static void
1237 gcode_set_draw_xor (hidGC gc, int xor_)
1242 static void
1243 gcode_set_draw_faded (hidGC gc, int faded)
1247 static void
1248 use_gc (hidGC gc)
1250 int need_brush = 0;
1252 if (gc->me_pointer != &gcode_hid)
1254 fprintf (stderr, "Fatal: GC from another HID passed to gcode HID\n");
1255 abort ();
1257 if (linewidth != gc->width)
1259 /* Make sure the scaling doesn't erase lines completely */
1261 if (SCALE (gc->width) == 0 && gc->width > 0)
1262 gdImageSetThickness (im, 1);
1263 else
1265 gdImageSetThickness (gcode_im,
1266 pcb_to_gcode (gc->width + 2 * gcode_toolradius));
1267 linewidth = gc->width;
1268 need_brush = 1;
1270 if (lastbrush != gc->brush || need_brush)
1272 static void *bcache = 0;
1273 hidval bval;
1274 char name[256];
1275 char type;
1276 int r;
1278 switch (gc->cap)
1280 case Round_Cap:
1281 case Trace_Cap:
1282 type = 'C';
1283 r = pcb_to_gcode (gc->width / 2 + gcode_toolradius);
1284 break;
1285 default:
1286 case Square_Cap:
1287 r = pcb_to_gcode (gc->width + gcode_toolradius * 2);
1288 type = 'S';
1289 break;
1291 sprintf (name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g,
1292 gc->color->b, type, r);
1294 if (hid_cache_color (0, name, &bval, &bcache))
1296 gc->brush = (gdImagePtr)bval.ptr;
1298 else
1300 int bg, fg;
1301 if (type == 'C')
1302 gc->brush = gdImageCreate (2 * r + 1, 2 * r + 1);
1303 else
1304 gc->brush = gdImageCreate (r + 1, r + 1);
1305 bg = gdImageColorAllocate (gc->brush, 255, 255, 255);
1306 fg =
1307 gdImageColorAllocate (gc->brush, gc->color->r, gc->color->g,
1308 gc->color->b);
1309 gdImageColorTransparent (gc->brush, bg);
1312 * if we shrunk to a radius/box width of zero, then just use
1313 * a single pixel to draw with.
1315 if (r == 0)
1316 gdImageFilledRectangle (gc->brush, 0, 0, 0, 0, fg);
1317 else
1319 if (type == 'C')
1320 gdImageFilledEllipse (gc->brush, r, r, 2 * r, 2 * r, fg);
1321 else
1322 gdImageFilledRectangle (gc->brush, 0, 0, r, r, fg);
1324 bval.ptr = gc->brush;
1325 hid_cache_color (1, name, &bval, &bcache);
1328 gdImageSetBrush (gcode_im, gc->brush);
1329 lastbrush = gc->brush;
1334 static void
1335 gcode_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1337 use_gc (gc);
1338 gdImageRectangle (gcode_im,
1339 pcb_to_gcode (x1 - PCB->ExtentMinX - gcode_toolradius),
1340 pcb_to_gcode (y1 - PCB->ExtentMinY - gcode_toolradius),
1341 pcb_to_gcode (x2 - PCB->ExtentMinX + gcode_toolradius),
1342 pcb_to_gcode (y2 - PCB->ExtentMinY + gcode_toolradius),
1343 gc->color->c);
1344 /* printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */
1347 static void
1348 gcode_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1350 use_gc (gc);
1351 gdImageSetThickness (gcode_im, 0);
1352 linewidth = 0;
1353 gdImageFilledRectangle (gcode_im,
1354 pcb_to_gcode (x1 - PCB->ExtentMinX - gcode_toolradius),
1355 pcb_to_gcode (y1 - PCB->ExtentMinY - gcode_toolradius),
1356 pcb_to_gcode (x2 - PCB->ExtentMinX + gcode_toolradius),
1357 pcb_to_gcode (y2 - PCB->ExtentMinY + gcode_toolradius),
1358 gc->color->c);
1359 /* printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */
1362 static void
1363 gcode_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1365 if (x1 == x2 && y1 == y2)
1367 Coord w = gc->width / 2;
1368 gcode_fill_rect (gc,
1369 x1 - PCB->ExtentMinX - w, y1 - PCB->ExtentMinX - w,
1370 x1 - PCB->ExtentMinX + w, y1 - PCB->ExtentMinY + w);
1371 return;
1373 use_gc (gc);
1375 gdImageSetThickness (gcode_im, 0);
1376 linewidth = 0;
1377 gdImageLine (gcode_im,
1378 pcb_to_gcode (x1 - PCB->ExtentMinX),
1379 pcb_to_gcode (y1 - PCB->ExtentMinY),
1380 pcb_to_gcode (x2 - PCB->ExtentMinX),
1381 pcb_to_gcode (y2 - PCB->ExtentMinY), gdBrushed);
1384 static void
1385 gcode_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1386 Angle start_angle, Angle delta_angle)
1388 Angle sa, ea;
1391 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
1392 * in pcb, 0 degrees is to the left and +90 degrees is down
1394 start_angle = 180 - start_angle;
1395 delta_angle = -delta_angle;
1396 if (delta_angle > 0)
1398 sa = start_angle;
1399 ea = start_angle + delta_angle;
1401 else
1403 sa = start_angle + delta_angle;
1404 ea = start_angle;
1408 * make sure we start between 0 and 360 otherwise gd does strange
1409 * things
1411 sa = NormalizeAngle (sa);
1412 ea = NormalizeAngle (ea);
1414 #if 0
1415 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
1416 cx, cy, width, height, start_angle, delta_angle, sa, ea);
1417 printf ("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
1418 im, SCALE_X (cx), SCALE_Y (cy),
1419 SCALE (width), SCALE (height), sa, ea, gc->color->c);
1420 #endif
1421 use_gc (gc);
1422 gdImageSetThickness (gcode_im, 0);
1423 linewidth = 0;
1424 gdImageArc (gcode_im,
1425 pcb_to_gcode (cx - PCB->ExtentMinX),
1426 pcb_to_gcode (cy - PCB->ExtentMinY),
1427 pcb_to_gcode (2 * width + gcode_toolradius * 2),
1428 pcb_to_gcode (2 * height + gcode_toolradius * 2), sa, ea,
1429 gdBrushed);
1432 /* given a hole size, return the structure that currently holds the data for
1433 that hole size. If there isn't one, make it */
1434 static int _drill_size_comparator(const void* _size0, const void* _size1)
1436 double size0 = ((const struct single_size_drills*)_size0)->diameter_inches;
1437 double size1 = ((const struct single_size_drills*)_size1)->diameter_inches;
1438 if (size0 == size1)
1439 return 0;
1441 if (size0 < size1)
1442 return -1;
1444 return 1;
1446 static struct single_size_drills*
1447 get_drill(double diameter_inches)
1449 /* see if we already have this size. If so, return that structure */
1450 struct single_size_drills* drill =
1451 bsearch (&diameter_inches,
1452 drills, n_drills, sizeof (drills[0]),
1453 _drill_size_comparator);
1454 if (drill != NULL)
1455 return drill;
1457 /* haven't seen this hole size before, so make a new structure for it */
1458 if (n_drills == n_drills_allocated)
1460 n_drills_allocated += 100;
1461 drills =
1462 (struct single_size_drills *) realloc (drills,
1463 n_drills_allocated *
1464 sizeof (struct single_size_drills));
1467 /* I now add the structure to the list, making sure to keep the list
1468 * sorted. Ideally the bsearch() call above would have given me the location
1469 * to insert this element while keeping things sorted, but it doesn't. For
1470 * simplicity I manually lsearch() to find this location myself */
1472 int i = 0;
1473 for (; i<n_drills; i++)
1474 if (drills[i].diameter_inches >= diameter_inches)
1475 break;
1477 if (n_drills != i)
1478 memmove (&drills[i+1], &drills[i],
1479 (n_drills-i) * sizeof (struct single_size_drills));
1481 drills[i].diameter_inches = diameter_inches;
1482 drills[i].n_holes = 0;
1483 drills[i].n_holes_allocated = 0;
1484 drills[i].holes = NULL;
1485 n_drills++;
1487 return &drills[i];
1491 static void
1492 add_hole (struct single_size_drills* drill,
1493 double cx_inches, double cy_inches)
1495 if (drill->n_holes == drill->n_holes_allocated)
1497 drill->n_holes_allocated += 100;
1498 drill->holes =
1499 (struct drill_hole *) realloc (drill->holes,
1500 drill->n_holes_allocated *
1501 sizeof (struct drill_hole));
1504 drill->holes[ drill->n_holes ].x = cx_inches;
1505 drill->holes[ drill->n_holes ].y = cy_inches;
1506 drill->n_holes++;
1509 static void
1510 gcode_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1512 use_gc (gc);
1514 gdImageSetThickness (gcode_im, 0);
1515 linewidth = 0;
1516 gdImageFilledEllipse (gcode_im,
1517 pcb_to_gcode (cx - PCB->ExtentMinX),
1518 pcb_to_gcode (cy - PCB->ExtentMinY),
1519 pcb_to_gcode (2 * radius + gcode_toolradius * 2),
1520 pcb_to_gcode (2 * radius + gcode_toolradius * 2),
1521 gc->color->c);
1522 if (save_drill && is_drill)
1524 double diameter_inches = COORD_TO_INCH(radius*2);
1526 struct single_size_drills* drill = get_drill (diameter_inches);
1527 add_hole (drill,
1528 /* convert to inch, flip: will drill from bottom side */
1529 COORD_TO_INCH(PCB->ExtentMaxX - cx),
1530 /* PCB reverses y axis */
1531 COORD_TO_INCH(PCB->ExtentMaxY - cy));
1535 static void
1536 gcode_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1538 int i;
1539 gdPoint *points;
1541 points = (gdPoint *) malloc (n_coords * sizeof (gdPoint));
1542 if (points == NULL)
1544 fprintf (stderr, "ERROR: gcode_fill_polygon(): malloc failed\n");
1545 exit (1);
1547 use_gc (gc);
1548 for (i = 0; i < n_coords; i++)
1550 points[i].x = pcb_to_gcode (x[i] - PCB->ExtentMinX);
1551 points[i].y = pcb_to_gcode (y[i] - PCB->ExtentMinY);
1553 gdImageSetThickness (gcode_im, 0);
1554 linewidth = 0;
1555 gdImageFilledPolygon (gcode_im, points, n_coords, gc->color->c);
1556 free (points);
1557 /* printf("FillPoly\n"); */
1560 static void
1561 gcode_calibrate (double xval, double yval)
1563 CRASH;
1566 static void
1567 gcode_set_crosshair (int x, int y, int a)
1571 /* *** Miscellaneous ******************************************************* */
1573 #include "dolists.h"
1575 void
1576 hid_gcode_init ()
1578 memset (&gcode_hid, 0, sizeof (HID));
1579 memset (&gcode_graphics, 0, sizeof (HID_DRAW));
1581 common_nogui_init (&gcode_hid);
1582 common_draw_helpers_init (&gcode_graphics);
1584 gcode_hid.struct_size = sizeof (HID);
1585 gcode_hid.name = "gcode";
1586 gcode_hid.description = "G-CODE export";
1587 gcode_hid.exporter = 1;
1588 gcode_hid.poly_before = 1;
1590 gcode_hid.get_export_options = gcode_get_export_options;
1591 gcode_hid.do_export = gcode_do_export;
1592 gcode_hid.parse_arguments = gcode_parse_arguments;
1593 gcode_hid.set_layer = gcode_set_layer;
1594 gcode_hid.calibrate = gcode_calibrate;
1595 gcode_hid.set_crosshair = gcode_set_crosshair;
1597 gcode_hid.graphics = &gcode_graphics;
1599 gcode_graphics.make_gc = gcode_make_gc;
1600 gcode_graphics.destroy_gc = gcode_destroy_gc;
1601 gcode_graphics.use_mask = gcode_use_mask;
1602 gcode_graphics.set_color = gcode_set_color;
1603 gcode_graphics.set_line_cap = gcode_set_line_cap;
1604 gcode_graphics.set_line_width = gcode_set_line_width;
1605 gcode_graphics.set_draw_xor = gcode_set_draw_xor;
1606 gcode_graphics.set_draw_faded = gcode_set_draw_faded;
1607 gcode_graphics.draw_line = gcode_draw_line;
1608 gcode_graphics.draw_arc = gcode_draw_arc;
1609 gcode_graphics.draw_rect = gcode_draw_rect;
1610 gcode_graphics.fill_circle = gcode_fill_circle;
1611 gcode_graphics.fill_polygon = gcode_fill_polygon;
1612 gcode_graphics.fill_rect = gcode_fill_rect;
1614 hid_register_hid (&gcode_hid);
1616 #include "gcode_lists.h"