hid/gtk (GL): I think the polygon renderer works in mask mode now
[geda-pcb/pcjc2.git] / src / hid / gcode / gcode.c
blob02bdff820b1b87e12d9c2e806d33aecaace7cbe9
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 #ifdef HAVE_LOCALE_H
49 #include <locale.h>
50 #endif
52 #include "global.h"
53 #include "error.h" /* Message() */
54 #include "data.h"
55 #include "misc.h"
56 #include "rats.h"
58 #include "hid.h"
59 #include "hid_draw.h"
60 #include "../hidint.h"
61 #include <gd.h>
62 #include "hid/common/hidnogui.h"
63 #include "hid/common/draw_helpers.h"
64 #include "bitmap.h"
65 #include "curve.h"
66 #include "potracelib.h"
67 #include "trace.h"
68 #include "decompose.h"
69 #include "pcb-printf.h"
71 #include "hid/common/hidinit.h"
73 #ifdef HAVE_LIBDMALLOC
74 #include <dmalloc.h>
75 #endif
77 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented GCODE function %s.\n", __FUNCTION__); abort()
79 static HID gcode_hid;
80 static HID_DRAW gcode_graphics;
81 static HID_DRAW_CLASS gcode_graphics_class;
83 struct color_struct
85 /* the descriptor used by the gd library */
86 int c;
88 /* so I can figure out what rgb value c refers to */
89 unsigned int r, g, b;
92 typedef struct gcode_gc_struct
94 struct hid_gc_struct hid_gc; /* Parent */
96 EndCapStyle cap;
97 int width;
98 unsigned char r, g, b;
99 int erase;
100 struct color_struct *color;
101 gdImagePtr brush;
102 } *gcodeGC;
104 static struct color_struct *black = NULL, *white = NULL;
105 static int linewidth = -1;
106 static gdImagePtr lastbrush = (gdImagePtr)((void *) -1);
108 /* gd image and file for PNG export */
109 static gdImagePtr gcode_im = NULL;
110 static FILE *gcode_f = NULL;
112 static int is_mask;
113 static int is_drill;
114 static int is_bottom;
117 * Which groups of layers to export into PNG layer masks. 1 means export, 0
118 * means do not export.
120 static int gcode_export_group[MAX_GROUP];
122 /* Group that is currently exported. */
123 static int gcode_cur_group;
125 /* Filename prefix and suffix that will be used when saving files. */
126 static const char *gcode_basename = NULL;
128 /* Horizontal DPI (grid points per inch) */
129 static int gcode_dpi = -1;
131 static double gcode_cutdepth = 0; /* milling depth (inch) */
132 static double gcode_isoplunge = 0; /* isolation milling plunge feedrate */
133 static double gcode_isofeedrate = 0; /* isolation milling feedrate */
134 static char gcode_predrill;
135 static double gcode_drilldepth = 0; /* drilling depth (mm or in) */
136 static double gcode_drillfeedrate = 0; /* drilling feedrate */
137 static double gcode_safeZ = 100; /* safe Z (mm or in) */
138 static int gcode_toolradius = 0; /* iso-mill tool radius (1/100 mil) */
139 static char gcode_drillmill = 0; /* wether to drill with the mill tool */
140 static double gcode_milldepth = 0; /* outline milling depth (mm or in) */
141 static double gcode_milltoolradius = 0; /* outline-mill tool radius (mm or in)*/
142 static double gcode_millplunge = 0; /* outline-milling plunge feedrate */
143 static double gcode_millfeedrate = 0; /* outline-milling feedrate */
144 static char gcode_advanced = 0;
145 static int save_drill = 0;
147 /* structure to represent a single hole */
148 struct drill_hole
150 double x;
151 double y;
154 /* structure to represent all holes of a given size */
155 struct single_size_drills
157 double diameter_inches;
159 int n_holes;
160 int n_holes_allocated;
161 struct drill_hole* holes;
164 /* at the start we have no drills at all */
165 static struct single_size_drills* drills = NULL;
166 static int n_drills = 0;
167 static int n_drills_allocated = 0;
169 HID_Attribute gcode_attribute_list[] = {
170 /* other HIDs expect this to be first. */
171 {"basename", "File name prefix and suffix,\n"
172 "layer names will be inserted before the suffix.",
173 HID_String, 0, 0, {0, 0, 0}, 0, 0},
174 #define HA_basename 0
176 {"measurement-unit", "Measurement unit used in the G-code output.",
177 HID_Unit, 0, 0, {3, 0, 0}, NULL, 0},
178 #define HA_unit 1
180 {"dpi", "Accuracy of the mill path generation in pixels/inch.",
181 HID_Integer, 0, 2000, {600, 0, 0}, 0, 0},
182 #define HA_dpi 2
184 {"safe-Z", "Safe Z for traverse movements of all operations.",
185 HID_Real, -1000, 10000, {0, 0, 2}, 0, 0},
186 #define HA_safeZ 3
188 {"iso-mill-depth", "Isolation milling depth.",
189 HID_Real, -1000, 1000, {0, 0, -0.05}, 0, 0},
190 #define HA_cutdepth 4
192 {"iso-tool-diameter", "Isolation milling tool diameter.",
193 HID_Real, 0, 10000, {0, 0, 0.2}, 0, 0},
194 #define HA_tooldiameter 5
196 {"iso-tool-plunge", "Isolation milling feedrate when plunging into\n"
197 "the material.",
198 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0},
199 #define HA_isoplunge 6
201 {"iso-tool-feedrate", "Isolation milling feedrate.",
202 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
203 #define HA_isofeedrate 7
205 {"predrill", "Wether to pre-drill all drill spots with the isolation milling\n"
206 "tool. Drill depth is iso-mill-depth here. This feature eases\n"
207 "and enhances accuracy of manual drilling.",
208 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
209 #define HA_predrill 8
211 {"drill-depth", "Drilling depth.",
212 HID_Real, -10000, 10000, {0, 0, -2}, 0, 0},
213 #define HA_drilldepth 9
215 {"drill-feedrate", "Drilling feedrate.",
216 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
217 #define HA_drillfeedrate 10
219 {"drill-mill", "Wether to produce drill holes equal or bigger than the\n"
220 "milling tool diameter with the milling tool.\n"
221 "With the milling tool bigger holes can be accurately sized\n"
222 "without changing the tool",
223 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
224 #define HA_drillmill 11
226 {"outline-mill-depth", "Milling depth when milling the outline.\n"
227 "Currently, only the rectangular extents of the\n"
228 "board are milled, no polygonal outlines or holes.",
229 HID_Real, -10000, 10000, {0, 0, -1}, 0, 0},
230 #define HA_milldepth 12
232 {"outline-tool-diameter", "Diameter of the tool used for outline milling.",
233 HID_Real, 0, 10000, {0, 0, 1}, 0, 0},
234 #define HA_milltooldiameter 13
236 {"outline-mill-plunge", "Outline milling feedrate when plunging into\n"
237 "the material",
238 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0},
239 #define HA_millplunge 14
241 {"outline-mill-feedrate", "Outline milling feedrate",
242 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
243 #define HA_millfeedrate 15
245 {"advanced-gcode", "Wether to produce G-code for advanced interpreters,\n"
246 "like using variables or drill cycles. Not all\n"
247 "machine controllers understand this, but it allows\n"
248 "better hand-editing of the resulting files.",
249 HID_Boolean, 0, 0, {-1, 0, 0}, 0, 0},
250 #define HA_advanced 16
253 #define NUM_OPTIONS (sizeof(gcode_attribute_list)/sizeof(gcode_attribute_list[0]))
255 REGISTER_ATTRIBUTES (gcode_attribute_list)
256 static HID_Attr_Val gcode_values[NUM_OPTIONS];
258 /* *** Utility funcions **************************************************** */
260 /* convert from default PCB units to gcode units */
261 static int pcb_to_gcode (int pcb)
263 return round(COORD_TO_INCH(pcb) * gcode_dpi);
266 /* Fits the given layer name into basename, just before the suffix */
267 static void
268 gcode_get_filename (char *filename, const char *layername)
270 char *pt;
271 char suffix[MAXPATHLEN];
273 suffix[0] = '\0';
274 pt = strrchr (gcode_basename, '.');
275 if (pt && pt > strrchr (gcode_basename, '/'))
276 strcpy (suffix, pt);
277 else
278 pt = NULL;
280 strcpy (filename, gcode_basename);
281 if (pt)
282 *(filename + (pt - gcode_basename)) = '\0';
283 strcat (filename, "-");
284 strcat (filename, layername);
285 strcat (filename, suffix);
287 // result is in char *filename
290 /* Sorts drills to produce a short tool path. I start with the hole nearest
291 * (0,0) and for each subsequent one, find the hole nearest to the previous.
292 * This isn't guaranteed to find the shortest path, but should be good enough.
293 * Note that this is O(N^2). We can't use the O(N logN) sort, since our
294 * shortest-distance origin changes with every point */
295 static void
296 sort_drill (struct drill_hole *drill, int n_drill)
298 /* I start out by looking for points closest to (0,0) */
299 struct drill_hole nearest_target = { 0, 0 };
301 /* I sort my list by finding the correct point to fill each slot. I don't need
302 to look at the last one, since it'll be in the right place automatically */
303 for (int j = 0; j < n_drill-1; j++)
305 double dmin = 1e20;
306 int imin = 0;
307 /* look through remaining elements to find the next drill point. This is
308 the one nearest to nearest_target */
309 for (int i = j; i < n_drill; i++)
311 double d =
312 (drill[i].x - nearest_target.x) * (drill[i].x - nearest_target.x) +
313 (drill[i].y - nearest_target.y) * (drill[i].y - nearest_target.y);
314 if (d < dmin)
316 imin = i;
317 dmin = d;
320 /* printf("j=%d imin=%d dmin=%f nearest_target=(%f,%f)\n",j,imin,dmin,
321 nearest_target.x,nearest_target.y); */
322 if (j != imin)
324 struct drill_hole tmp;
325 tmp = drill[j];
326 drill[j] = drill[imin];
327 drill[imin] = tmp;
330 nearest_target = drill[j];
334 /* *** Main export callback ************************************************ */
336 static void
337 gcode_parse_arguments (int *argc, char ***argv)
339 hid_register_attributes (gcode_attribute_list,
340 sizeof (gcode_attribute_list) /
341 sizeof (gcode_attribute_list[0]));
342 hid_parse_command_line (argc, argv);
345 static HID_Attribute *
346 gcode_get_export_options (int *n)
348 static char *last_made_filename = 0;
349 static int last_unit_value = -1;
351 if (gcode_attribute_list[HA_unit].default_val.int_value == last_unit_value)
353 if (Settings.grid_unit)
354 gcode_attribute_list[HA_unit].default_val.int_value = Settings.grid_unit->index;
355 else
356 gcode_attribute_list[HA_unit].default_val.int_value = get_unit_struct ("mil")->index;
357 last_unit_value = gcode_attribute_list[HA_unit].default_val.int_value;
360 if (PCB)
362 derive_default_filename (PCB->Filename,
363 &gcode_attribute_list[HA_basename],
364 ".gcode", &last_made_filename);
366 if (n)
368 *n = NUM_OPTIONS;
370 return gcode_attribute_list;
373 /* Populates gcode_export_group array */
374 static void
375 gcode_choose_groups ()
377 int n, m;
378 LayerType *layer;
380 /* Set entire array to 0 (don't export any layer groups by default */
381 memset (gcode_export_group, 0, sizeof (gcode_export_group));
383 for (n = 0; n < max_copper_layer; n++)
385 layer = &PCB->Data->Layer[n];
387 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
389 /* layer isn't empty */
392 * is this check necessary? It seems that special
393 * layers have negative indexes?
396 if (SL_TYPE (n) == 0)
398 /* layer is a copper layer */
399 m = GetLayerGroupNumberByNumber (n);
401 /* the export layer */
402 gcode_export_group[m] = 1;
408 static void
409 gcode_alloc_colors ()
412 * Allocate white and black -- the first color allocated becomes the
413 * background color
416 white = (struct color_struct *) malloc (sizeof (*white));
417 white->r = white->g = white->b = 255;
418 white->c = gdImageColorAllocate (gcode_im, white->r, white->g, white->b);
420 black = (struct color_struct *) malloc (sizeof (*black));
421 black->r = black->g = black->b = 0;
422 black->c = gdImageColorAllocate (gcode_im, black->r, black->g, black->b);
425 static void
426 gcode_start_png ()
428 gcode_im = gdImageCreate (pcb_to_gcode (PCB->MaxWidth),
429 pcb_to_gcode (PCB->MaxHeight));
430 gcode_alloc_colors ();
433 static void
434 gcode_finish_png (const char *layername)
436 #ifdef HAVE_GDIMAGEPNG
437 char *pngname, *filename;
438 FILE *file = NULL;
440 pngname = (char *)malloc (MAXPATHLEN);
441 gcode_get_filename (pngname, layername);
442 filename = g_strdup_printf ("%s.png", pngname);
443 free(pngname);
445 file = fopen (filename, "wb");
446 g_free (filename);
448 gdImagePng (gcode_im, file);
449 fclose (file);
450 #else
451 Message ("GCODE: PNG not supported by gd. Can't write layer mask.\n");
452 #endif
453 gdImageDestroy (gcode_im);
455 free (white);
456 free (black);
457 gcode_im = NULL;
460 static void
461 gcode_start_png_export ()
463 BoxType region;
465 region.X1 = 0;
466 region.Y1 = 0;
467 region.X2 = PCB->MaxWidth;
468 region.Y2 = PCB->MaxHeight;
470 linewidth = -1;
471 lastbrush = (gdImagePtr)((void *) -1);
473 hid_expose_callback (&gcode_graphics, &region, 0);
476 static FILE *
477 gcode_start_gcode (const char *layername, bool metric)
479 FILE *file = NULL;
480 char buffer[MAXPATHLEN];
481 time_t t;
483 gcode_get_filename (buffer, layername);
484 file = fopen (buffer, "wb");
485 if ( ! file)
487 perror (buffer);
488 return NULL;
490 fprintf (file, "(Created by G-code exporter)\n");
491 t = time (NULL);
492 snprintf (buffer, sizeof(buffer), "%s", ctime (&t));
493 buffer[strlen (buffer) - 1] = '\0'; // drop the newline
494 fprintf (file, "(%s)\n", buffer);
495 fprintf (file, "(Units: %s)\n", metric ? "mm" : "inch");
496 if (metric)
497 pcb_fprintf (file, "(Board size: %.2`mm x %.2`mm mm)\n",
498 PCB->MaxWidth, PCB->MaxHeight);
499 else
500 pcb_fprintf (file, "(Board size: %.2`mi x %.2`mi inches)\n",
501 PCB->MaxWidth, PCB->MaxHeight);
503 return file;
506 static void
507 gcode_do_export (HID_Attr_Val * options)
509 int save_ons[MAX_LAYER + EXTRA_LAYERS];
510 int i, idx;
511 const Unit *unit;
512 double scale = 0, d = 0;
513 int r, c, v, p, metric;
514 path_t *plist = NULL;
515 potrace_bitmap_t *bm = NULL;
516 potrace_param_t param_default = {
517 2, /* turnsize */
518 POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */
519 1.0, /* alphamax */
520 1, /* opticurve */
521 0.2, /* opttolerance */
523 NULL, /* callback function */
524 NULL, /* callback data */
525 0.0, 1.0, /* progress range */
526 0.0, /* granularity */
529 char variable_safeZ[20], variable_cutdepth[20];
530 char variable_isoplunge[20], variable_isofeedrate[20];
531 char variable_drilldepth[20], variable_milldepth[20];
532 char variable_millplunge[20], variable_millfeedrate[20];
534 if (!options)
536 gcode_get_export_options (0);
537 for (i = 0; i < NUM_OPTIONS; i++)
539 gcode_values[i] = gcode_attribute_list[i].default_val;
541 options = gcode_values;
543 gcode_basename = options[HA_basename].str_value;
544 if (!gcode_basename)
546 gcode_basename = "pcb-out.gcode";
548 gcode_dpi = options[HA_dpi].int_value;
549 if (gcode_dpi < 0)
551 fprintf (stderr, "ERROR: dpi may not be < 0\n");
552 return;
554 unit = &(get_unit_list() [options[HA_unit].int_value]);
555 metric = (unit->family == METRIC);
556 scale = metric ? 1.0 / coord_to_unit (unit, MM_TO_COORD (1.0))
557 : 1.0 / coord_to_unit (unit, INCH_TO_COORD (1.0));
559 gcode_cutdepth = options[HA_cutdepth].real_value * scale;
560 gcode_isoplunge = options[HA_isoplunge].real_value * scale;
561 gcode_isofeedrate = options[HA_isofeedrate].real_value * scale;
562 gcode_predrill = options[HA_predrill].int_value;
563 gcode_drilldepth = options[HA_drilldepth].real_value * scale;
564 gcode_drillfeedrate = options[HA_drillfeedrate].real_value * scale;
565 gcode_safeZ = options[HA_safeZ].real_value * scale;
566 gcode_toolradius = metric
567 ? MM_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale)
568 : INCH_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale);
569 gcode_drillmill = options[HA_drillmill].int_value;
570 gcode_milldepth = options[HA_milldepth].real_value * scale;
571 gcode_milltoolradius = options[HA_milltooldiameter].real_value / 2 * scale;
572 gcode_millplunge = options[HA_millplunge].real_value * scale;
573 gcode_millfeedrate = options[HA_millfeedrate].real_value * scale;
574 gcode_advanced = options[HA_advanced].int_value;
575 gcode_choose_groups ();
576 if (gcode_advanced)
578 /* give each variable distinct names, even if they don't appear
579 together in a file. This allows to join output files */
580 strcpy (variable_safeZ, "#100");
581 strcpy (variable_cutdepth, "#101");
582 strcpy (variable_isoplunge, "#102");
583 strcpy (variable_isofeedrate, "#103");
584 strcpy (variable_drilldepth, "#104");
585 strcpy (variable_milldepth, "#105");
586 strcpy (variable_millplunge, "#106");
587 strcpy (variable_millfeedrate, "#107");
589 else
591 snprintf (variable_safeZ, 20, "%f", gcode_safeZ);
592 snprintf (variable_cutdepth, 20, "%f", gcode_cutdepth);
593 snprintf (variable_isoplunge, 20, "%f", gcode_isoplunge);
594 snprintf (variable_isofeedrate, 20, "%f", gcode_isofeedrate);
595 snprintf (variable_drilldepth, 20, "%f", gcode_drilldepth);
596 snprintf (variable_milldepth, 20, "%f", gcode_milldepth);
597 snprintf (variable_millplunge, 20, "%f", gcode_millplunge);
598 snprintf (variable_millfeedrate, 20, "%f", gcode_millfeedrate);
601 for (i = 0; i < MAX_GROUP; 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_bottom =
611 (GetLayerGroupNumberByNumber (idx) ==
612 GetLayerGroupNumberBySide (BOTTOM_SIDE)) ? 1 : 0;
613 save_drill = is_bottom; /* 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_bottom)
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_bottom)
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 return;
669 fprintf (gcode_f, "(Accuracy %d dpi)\n", gcode_dpi);
670 pcb_fprintf (gcode_f, "(Tool diameter: %`f %s)\n",
671 options[HA_tooldiameter].real_value * scale,
672 metric ? "mm" : "inch");
673 if (gcode_advanced)
675 pcb_fprintf (gcode_f, "%s=%`f (safe Z)\n",
676 variable_safeZ, gcode_safeZ);
677 pcb_fprintf (gcode_f, "%s=%`f (cutting depth)\n",
678 variable_cutdepth, gcode_cutdepth);
679 pcb_fprintf (gcode_f, "%s=%`f (plunge feedrate)\n",
680 variable_isoplunge, gcode_isoplunge);
681 pcb_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 return;
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 return;
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 pcb_fprintf (gcode_f, "G81 X%`f Y%`f Z%s R%s\n",
776 drillX, drillY, variable_cutdepth,
777 variable_safeZ);
778 else
780 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n", drillX, drillY);
781 pcb_fprintf (gcode_f, "G1 Z%s\n", variable_cutdepth);
782 pcb_fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
785 fprintf (gcode_f, "(%d predrills)\n", n_all_drills);
786 free(all_drills);
788 if (metric)
789 pcb_fprintf (gcode_f, "(milling distance %`.2fmm = %`.2fin)\n", d,
790 d * 1 / 25.4);
791 else
792 pcb_fprintf (gcode_f, "(milling distance %`.2fmm = %`.2fin)\n",
793 25.4 * d, d);
794 if (gcode_advanced)
795 fprintf (gcode_f, "M5 M9 M2\n");
796 else
797 fprintf (gcode_f, "M5\nM9\nM2\n");
798 pathlist_free (plist);
799 bm_free (bm);
800 fclose (gcode_f);
801 gcode_f = NULL;
802 if (save_drill)
804 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++)
806 struct single_size_drills* drill = &drills[i_drill_file];
808 /* don't drill drillmill holes */
809 if (gcode_drillmill) {
810 double radius = metric ?
811 drill->diameter_inches * 25.4 / 2:
812 drill->diameter_inches / 2;
814 if (gcode_milltoolradius < radius)
815 continue;
818 d = 0;
819 sort_drill (drill->holes, drill->n_holes);
822 // get the filename with the drill size encoded in it
823 char layername[32];
824 pcb_snprintf(layername, sizeof(layername),
825 "%`.4f.drill",
826 metric ?
827 drill->diameter_inches * 25.4 :
828 drill->diameter_inches);
829 gcode_f = gcode_start_gcode(layername, metric);
831 if (!gcode_f)
832 return;
833 fprintf (gcode_f, "(Drill file: %d drills)\n", drill->n_holes);
834 if (metric)
835 pcb_fprintf (gcode_f, "(Drill diameter: %`f mm)\n",
836 drill->diameter_inches * 25.4);
837 else
838 pcb_fprintf (gcode_f, "(Drill diameter: %`f inch)\n",
839 drill->diameter_inches);
840 if (gcode_advanced)
842 pcb_fprintf (gcode_f, "%s=%`f (safe Z)\n",
843 variable_safeZ, gcode_safeZ);
844 pcb_fprintf (gcode_f, "%s=%`f (drill depth)\n",
845 variable_drilldepth, gcode_drilldepth);
846 fprintf (gcode_f, "(---------------------------------)\n");
847 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 ",
848 metric ? 21 : 20);
849 pcb_fprintf (gcode_f, "S3000 M7 F%`f\n",
850 gcode_drillfeedrate);
852 else
854 fprintf (gcode_f, "(---------------------------------)\n");
855 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 ",
856 metric ? 21 : 20);
857 pcb_fprintf (gcode_f, "S3000\nM7\nF%`f\n",
858 gcode_drillfeedrate);
860 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
861 for (r = 0; r < drill->n_holes; r++)
863 double drillX, drillY;
865 if (metric)
867 drillX = drill->holes[r].x * 25.4;
868 drillY = drill->holes[r].y * 25.4;
870 else
872 drillX = drill->holes[r].x;
873 drillY = drill->holes[r].y;
875 if (gcode_advanced)
876 pcb_fprintf (gcode_f, "G81 X%`f Y%`f Z%s R%s\n",
877 drillX, drillY, variable_drilldepth,
878 variable_safeZ);
879 else
881 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n",
882 drillX, drillY);
883 fprintf (gcode_f, "G1 Z%s\n", variable_drilldepth);
884 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
886 if (r > 0)
887 d += Distance(drill->holes[r - 1].x, drill->holes[r - 1].y,
888 drill->holes[r ].x, drill->holes[r ].y);
890 if (gcode_advanced)
891 fprintf (gcode_f, "M5 M9 M2\n");
892 else
893 fprintf (gcode_f, "M5\nM9\nM2\n");
894 pcb_fprintf (gcode_f,
895 "(end, total distance %`.2fmm = %`.2fin)\n", 25.4 * d, d);
896 fclose (gcode_f);
899 /* ******************* handle drill-milling **************************** */
900 if (save_drill && gcode_drillmill)
902 int n_drillmill_drills = 0;
903 struct drill_hole* drillmill_drills = NULL;
904 double* drillmill_radiuss = NULL;
905 double mill_radius, inaccuracy;
907 /* count drillmill drills */
908 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
910 struct single_size_drills* drill_set = &drills[i_drill_sets];
911 double radius = metric ?
912 drill_set->diameter_inches * 25.4 / 2:
913 drill_set->diameter_inches / 2;
915 if (gcode_milltoolradius <= radius)
916 n_drillmill_drills += drill_set->n_holes;
918 if (n_drillmill_drills > 0) {
919 /* for sorting regardless of size, copy all available drills
920 into one new structure */
921 drillmill_drills = (struct drill_hole *)
922 malloc (n_drillmill_drills * sizeof (struct drill_hole));
923 drillmill_radiuss = (double *)
924 malloc (n_drillmill_drills * sizeof (double));
925 r = 0;
926 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
928 struct single_size_drills* drill_set = &drills[i_drill_sets];
929 double radius = metric ?
930 drill_set->diameter_inches * 25.4 / 2:
931 drill_set->diameter_inches / 2;
933 if (gcode_milltoolradius <= radius)
935 memcpy(&drillmill_drills[r], drill_set->holes,
936 drill_set->n_holes * sizeof(struct drill_hole));
937 for (int r2 = r; r2 < r + drill_set->n_holes; r2++)
938 drillmill_radiuss[r2] = radius;
939 r += drill_set->n_holes;
942 sort_drill(drillmill_drills, n_drillmill_drills);
944 gcode_f = gcode_start_gcode("drillmill", metric);
945 if (!gcode_f)
946 return;
947 fprintf (gcode_f, "(Drillmill file)\n");
948 pcb_fprintf (gcode_f, "(Tool diameter: %`f %s)\n",
949 gcode_milltoolradius * 2,
950 metric ? "mm" : "inch");
951 if (gcode_advanced)
953 pcb_fprintf (gcode_f, "%s=%`f (safe Z)\n",
954 variable_safeZ, gcode_safeZ);
955 pcb_fprintf (gcode_f, "%s=%`f (mill depth)\n",
956 variable_milldepth, gcode_milldepth);
957 pcb_fprintf (gcode_f, "%s=%`f (mill plunge feedrate)\n",
958 variable_millplunge, gcode_millplunge);
959 pcb_fprintf (gcode_f, "%s=%`f (mill feedrate)\n",
960 variable_millfeedrate, gcode_millfeedrate);
961 fprintf (gcode_f, "(---------------------------------)\n");
962 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
963 metric ? 21 : 20);
965 else
967 fprintf (gcode_f, "(---------------------------------)\n");
968 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
969 metric ? 21 : 20);
971 for (r = 0; r < n_drillmill_drills; r++)
973 double drillX, drillY;
975 if (metric)
977 drillX = drillmill_drills[r].x * 25.4;
978 drillY = drillmill_drills[r].y * 25.4;
980 else
982 drillX = drillmill_drills[r].x;
983 drillY = drillmill_drills[r].y;
985 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n", drillX, drillY);
986 fprintf (gcode_f, "G1 Z%s F%s\n",
987 variable_milldepth, variable_millplunge);
989 mill_radius = drillmill_radiuss[r] - gcode_milltoolradius;
990 inaccuracy = (metric ? 25.4 : 1.) / gcode_dpi;
991 if (mill_radius > inaccuracy)
993 int n_sides;
995 /* calculate how many polygon sides we need to stay
996 within our accuracy while avoiding a G02/G03 */
997 n_sides = M_PI / acos (mill_radius /
998 (mill_radius + inaccuracy));
999 if (n_sides < 4)
1000 n_sides = 4;
1001 fprintf (gcode_f, "F%s\n", variable_millfeedrate);
1002 for (i = 0; i <= n_sides; i++)
1004 double angle = M_PI * 2 * i / n_sides;
1005 pcb_fprintf (gcode_f, "G1 X%`f Y%`f\n",
1006 drillX + mill_radius * cos (angle),
1007 drillY + mill_radius * sin (angle));
1009 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n",
1010 drillX, drillY);
1012 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1014 if (gcode_advanced)
1015 fprintf (gcode_f, "M5 M9 M2\n");
1016 else
1017 fprintf (gcode_f, "M5\nM9\nM2\n");
1018 fclose (gcode_f);
1020 free(drillmill_radiuss);
1021 free(drillmill_drills);
1024 /* ******************* end of per-layer writing ************************ */
1026 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++)
1027 free(drills[i_drill_file].holes);
1028 free (drills);
1029 drills = NULL;
1030 n_drills = n_drills_allocated = 0;
1035 * General milling. Put this aside from the above code, as paths
1036 * are generated without taking line or curve thickness into account.
1037 * Accordingly, we need an entirely different approach.
1040 * Currently this is a rarther simple implementation, which mills
1041 * the retangular extents of the board and nothing else. This should
1042 * be sufficient for many use cases.
1044 * A better implementation would have to group the lines and polygons
1045 * on the outline layer by outer polygon and inner holes, then offset
1046 * all of them to the right side and mill that.
1048 /* a better implementation might look like this:
1049 LAYER_LOOP (PCB->Data, MAX_LAYER);
1051 if (strcmp (layer->Name, "outline") == 0)
1053 LINE_LOOP (layer);
1055 ... calculate the offset for all lines and polygons of this layer,
1056 mirror it if is_bottom, then mill it ...
1058 END_LOOP;
1061 END_LOOP;
1063 for now: */
1064 { /* unconditional */
1065 double lowerX = 0., lowerY = 0., upperX = 0., upperY = 0.;
1066 double mill_distance = 0.;
1068 gcode_f = gcode_start_gcode("outline", metric);
1069 if (!gcode_f)
1070 return;
1071 fprintf (gcode_f, "(Outline mill file)\n");
1072 pcb_fprintf (gcode_f, "(Tool diameter: %`f %s)\n",
1073 gcode_milltoolradius * 2, metric ? "mm" : "inch");
1074 if (gcode_advanced)
1076 pcb_fprintf (gcode_f, "%s=%`f (safe Z)\n",
1077 variable_safeZ, gcode_safeZ);
1078 pcb_fprintf (gcode_f, "%s=%`f (mill depth)\n",
1079 variable_milldepth, gcode_milldepth);
1080 pcb_fprintf (gcode_f, "%s=%`f (mill plunge feedrate)\n",
1081 variable_millplunge, gcode_millplunge);
1082 pcb_fprintf (gcode_f, "%s=%`f (mill feedrate)\n",
1083 variable_millfeedrate, gcode_millfeedrate);
1084 fprintf (gcode_f, "(---------------------------------)\n");
1085 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
1086 metric ? 21 : 20);
1088 else
1090 fprintf (gcode_f, "(---------------------------------)\n");
1091 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
1092 metric ? 21 : 20);
1094 if (metric)
1096 upperX = COORD_TO_MM(PCB->MaxWidth);
1097 upperY = COORD_TO_MM(PCB->MaxHeight);
1099 else
1101 upperX = COORD_TO_INCH(PCB->MaxWidth);
1102 upperY = COORD_TO_INCH(PCB->MaxHeight);
1104 lowerX -= gcode_milltoolradius;
1105 lowerY -= gcode_milltoolradius;
1106 upperX += gcode_milltoolradius;
1107 upperY += gcode_milltoolradius;
1109 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1110 /* mill the two edges adjectant to 0,0 first to disconnect the
1111 workpiece from the raw material last */
1112 pcb_fprintf (gcode_f, "G0 X%`f Y%`f\n", upperX, lowerY);
1113 pcb_fprintf (gcode_f, "G1 Z%s F%s\n",
1114 variable_milldepth, variable_millplunge);
1115 pcb_fprintf (gcode_f, "G1 X%`f Y%`f F%s\n",
1116 lowerX, lowerY, variable_millfeedrate);
1117 pcb_fprintf (gcode_f, "G1 X%`f Y%`f\n", lowerX, upperY);
1118 pcb_fprintf (gcode_f, "G1 X%`f Y%`f\n", upperX, upperY);
1119 pcb_fprintf (gcode_f, "G1 X%`f Y%`f\n", upperX, lowerY);
1120 pcb_fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1122 if (gcode_advanced)
1123 fprintf (gcode_f, "M5 M9 M2\n");
1124 else
1125 fprintf (gcode_f, "M5\nM9\nM2\n");
1126 mill_distance = abs(gcode_safeZ - gcode_milldepth);
1127 if (metric)
1128 mill_distance /= 25.4;
1129 pcb_fprintf (gcode_f, "(end, total distance G0 %`.2f mm = %`.2f in)\n",
1130 mill_distance * 25.4, mill_distance);
1131 mill_distance = (upperX - lowerX + upperY - lowerY) * 2;
1132 mill_distance += abs(gcode_safeZ - gcode_milldepth);
1133 if (metric)
1134 mill_distance /= 25.4;
1135 pcb_fprintf (gcode_f, "( total distance G1 %`.2f mm = %`.2f in)\n",
1136 mill_distance * 25.4, mill_distance);
1137 fclose (gcode_f);
1141 /* *** PNG export (slightly modified code from PNG export HID) ************* */
1143 static int
1144 gcode_set_layer (const char *name, int group, int empty)
1146 int idx = (group >= 0 && group < max_group) ?
1147 PCB->LayerGroups.Entries[group][0] : group;
1149 if (name == 0)
1151 name = PCB->Data->Layer[idx].Name;
1153 if (strcmp (name, "invisible") == 0)
1155 return 0;
1157 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
1158 is_mask = (SL_TYPE (idx) == SL_MASK);
1160 if (is_mask)
1162 /* Don't print masks */
1163 return 0;
1165 if (is_drill)
1168 * Print 'holes', so that we can fill gaps in the copper
1169 * layer
1171 return 1;
1173 if (group == gcode_cur_group)
1175 return 1;
1177 return 0;
1180 static hidGC
1181 gcode_make_gc (void)
1183 hidGC gc = (hidGC) calloc (1, sizeof (struct gcode_gc_struct));
1184 gcodeGC gcode_gc = (gcodeGC)gc;
1186 gc->hid = &gcode_hid;
1187 gc->hid_draw = &gcode_graphics;
1189 gcode_gc->cap = Trace_Cap;
1190 gcode_gc->width = 1;
1191 gcode_gc->color = (struct color_struct *) malloc (sizeof (*gcode_gc->color));
1192 gcode_gc->color->r = gcode_gc->color->g = gcode_gc->color->b = 0;
1193 gcode_gc->color->c = 0;
1195 return gc;
1198 static void
1199 gcode_destroy_gc (hidGC gc)
1201 free (gc);
1204 static void
1205 gcode_use_mask (enum mask_mode mode)
1207 /* does nothing */
1210 static void
1211 gcode_set_color (hidGC gc, const char *name)
1213 gcodeGC gcode_gc = (gcodeGC)gc;
1215 if (gcode_im == NULL)
1217 return;
1219 if (name == NULL)
1221 name = "#ff0000";
1223 if (!strcmp (name, "drill"))
1225 gcode_gc->color = black;
1226 gcode_gc->erase = 0;
1227 return;
1229 if (!strcmp (name, "erase"))
1231 /* FIXME -- should be background, not white */
1232 gcode_gc->color = white;
1233 gcode_gc->erase = 1;
1234 return;
1236 gcode_gc->color = black;
1237 gcode_gc->erase = 0;
1238 return;
1241 static void
1242 gcode_set_line_cap (hidGC gc, EndCapStyle style)
1244 gcodeGC gcode_gc = (gcodeGC)gc;
1246 gcode_gc->cap = style;
1249 static void
1250 gcode_set_line_width (hidGC gc, Coord width)
1252 gcodeGC gcode_gc = (gcodeGC)gc;
1254 gcode_gc->width = width;
1257 static void
1258 gcode_set_draw_xor (hidGC gc, int xor_)
1263 static void
1264 gcode_set_draw_faded (hidGC gc, int faded)
1268 static void
1269 use_gc (hidGC gc)
1271 gcodeGC gcode_gc = (gcodeGC)gc;
1273 int need_brush = 0;
1275 if (gc->hid != &gcode_hid)
1277 fprintf (stderr, "Fatal: GC from another HID passed to gcode HID\n");
1278 abort ();
1280 if (linewidth != gcode_gc->width)
1282 /* Make sure the scaling doesn't erase lines completely */
1284 if (SCALE (gcode_gc->width) == 0 && gcode_gc->width > 0)
1285 gdImageSetThickness (im, 1);
1286 else
1288 gdImageSetThickness (gcode_im,
1289 pcb_to_gcode (gcode_gc->width + 2 * gcode_toolradius));
1290 linewidth = gcode_gc->width;
1291 need_brush = 1;
1293 if (lastbrush != gcode_gc->brush || need_brush)
1295 static void *bcache = 0;
1296 hidval bval;
1297 const size_t name_len = 256;
1298 char name[name_len];
1299 char type;
1300 int r;
1302 switch (gcode_gc->cap)
1304 case Round_Cap:
1305 case Trace_Cap:
1306 type = 'C';
1307 r = pcb_to_gcode (gcode_gc->width / 2 + gcode_toolradius);
1308 break;
1309 default:
1310 case Square_Cap:
1311 r = pcb_to_gcode (gcode_gc->width + gcode_toolradius * 2);
1312 type = 'S';
1313 break;
1315 snprintf (name, name_len, "#%.2x%.2x%.2x_%c_%d", gcode_gc->color->r,
1316 gcode_gc->color->g, gcode_gc->color->b, type, r);
1318 if (hid_cache_color (0, name, &bval, &bcache))
1320 gcode_gc->brush = (gdImagePtr)bval.ptr;
1322 else
1324 int bg, fg;
1325 if (type == 'C')
1326 gcode_gc->brush = gdImageCreate (2 * r + 1, 2 * r + 1);
1327 else
1328 gcode_gc->brush = gdImageCreate (r + 1, r + 1);
1329 bg = gdImageColorAllocate (gcode_gc->brush, 255, 255, 255);
1330 fg =
1331 gdImageColorAllocate (gcode_gc->brush, gcode_gc->color->r, gcode_gc->color->g,
1332 gcode_gc->color->b);
1333 gdImageColorTransparent (gcode_gc->brush, bg);
1336 * if we shrunk to a radius/box width of zero, then just use
1337 * a single pixel to draw with.
1339 if (r == 0)
1340 gdImageFilledRectangle (gcode_gc->brush, 0, 0, 0, 0, fg);
1341 else
1343 if (type == 'C')
1344 gdImageFilledEllipse (gcode_gc->brush, r, r, 2 * r, 2 * r, fg);
1345 else
1346 gdImageFilledRectangle (gcode_gc->brush, 0, 0, r, r, fg);
1348 bval.ptr = gcode_gc->brush;
1349 hid_cache_color (1, name, &bval, &bcache);
1352 gdImageSetBrush (gcode_im, gcode_gc->brush);
1353 lastbrush = gcode_gc->brush;
1358 static void
1359 gcode_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1361 gcodeGC gcode_gc = (gcodeGC)gc;
1363 use_gc (gc);
1364 gdImageRectangle (gcode_im,
1365 pcb_to_gcode (x1 - gcode_toolradius),
1366 pcb_to_gcode (y1 - gcode_toolradius),
1367 pcb_to_gcode (x2 + gcode_toolradius),
1368 pcb_to_gcode (y2 + gcode_toolradius),
1369 gcode_gc->color->c);
1370 /* printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */
1373 static void
1374 gcode_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1376 gcodeGC gcode_gc = (gcodeGC)gc;
1378 use_gc (gc);
1379 gdImageSetThickness (gcode_im, 0);
1380 linewidth = 0;
1381 gdImageFilledRectangle (gcode_im,
1382 pcb_to_gcode (x1 - gcode_toolradius),
1383 pcb_to_gcode (y1 - gcode_toolradius),
1384 pcb_to_gcode (x2 + gcode_toolradius),
1385 pcb_to_gcode (y2 + gcode_toolradius),
1386 gcode_gc->color->c);
1387 /* printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */
1390 static void
1391 gcode_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1393 gcodeGC gcode_gc = (gcodeGC)gc;
1395 if (x1 == x2 && y1 == y2)
1397 Coord w = gcode_gc->width / 2;
1398 gcode_fill_rect (gc,
1399 x1 - w, y1 - w,
1400 x1 + w, y1 + w);
1401 return;
1403 use_gc (gc);
1405 gdImageSetThickness (gcode_im, 0);
1406 linewidth = 0;
1407 gdImageLine (gcode_im,
1408 pcb_to_gcode (x1),
1409 pcb_to_gcode (y1),
1410 pcb_to_gcode (x2),
1411 pcb_to_gcode (y2),
1412 gdBrushed);
1415 static void
1416 gcode_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1417 Angle start_angle, Angle delta_angle)
1419 Angle sa, ea;
1422 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
1423 * in pcb, 0 degrees is to the left and +90 degrees is down
1425 start_angle = 180 - start_angle;
1426 delta_angle = -delta_angle;
1427 if (delta_angle > 0)
1429 sa = start_angle;
1430 ea = start_angle + delta_angle;
1432 else
1434 sa = start_angle + delta_angle;
1435 ea = start_angle;
1439 * make sure we start between 0 and 360 otherwise gd does strange
1440 * things
1442 sa = NormalizeAngle (sa);
1443 ea = NormalizeAngle (ea);
1445 #if 0
1446 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
1447 cx, cy, width, height, start_angle, delta_angle, sa, ea);
1448 printf ("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
1449 im, SCALE_X (cx), SCALE_Y (cy),
1450 SCALE (width), SCALE (height), sa, ea, gcode_gc->color->c);
1451 #endif
1452 use_gc (gc);
1453 gdImageSetThickness (gcode_im, 0);
1454 linewidth = 0;
1455 gdImageArc (gcode_im,
1456 pcb_to_gcode (cx),
1457 pcb_to_gcode (cy),
1458 pcb_to_gcode (2 * width + gcode_toolradius * 2),
1459 pcb_to_gcode (2 * height + gcode_toolradius * 2), sa, ea,
1460 gdBrushed);
1463 /* given a hole size, return the structure that currently holds the data for
1464 that hole size. If there isn't one, make it */
1465 static int _drill_size_comparator(const void* _size0, const void* _size1)
1467 double size0 = ((const struct single_size_drills*)_size0)->diameter_inches;
1468 double size1 = ((const struct single_size_drills*)_size1)->diameter_inches;
1469 if (size0 == size1)
1470 return 0;
1472 if (size0 < size1)
1473 return -1;
1475 return 1;
1477 static struct single_size_drills*
1478 get_drill(double diameter_inches)
1480 /* see if we already have this size. If so, return that structure */
1481 struct single_size_drills* drill =
1482 bsearch (&diameter_inches,
1483 drills, n_drills, sizeof (drills[0]),
1484 _drill_size_comparator);
1485 if (drill != NULL)
1486 return drill;
1488 /* haven't seen this hole size before, so make a new structure for it */
1489 if (n_drills == n_drills_allocated)
1491 n_drills_allocated += 100;
1492 drills =
1493 (struct single_size_drills *) realloc (drills,
1494 n_drills_allocated *
1495 sizeof (struct single_size_drills));
1498 /* I now add the structure to the list, making sure to keep the list
1499 * sorted. Ideally the bsearch() call above would have given me the location
1500 * to insert this element while keeping things sorted, but it doesn't. For
1501 * simplicity I manually lsearch() to find this location myself */
1503 int i = 0;
1504 for (; i<n_drills; i++)
1505 if (drills[i].diameter_inches >= diameter_inches)
1506 break;
1508 if (n_drills != i)
1509 memmove (&drills[i+1], &drills[i],
1510 (n_drills-i) * sizeof (struct single_size_drills));
1512 drills[i].diameter_inches = diameter_inches;
1513 drills[i].n_holes = 0;
1514 drills[i].n_holes_allocated = 0;
1515 drills[i].holes = NULL;
1516 n_drills++;
1518 return &drills[i];
1522 static void
1523 add_hole (struct single_size_drills* drill,
1524 double cx_inches, double cy_inches)
1526 if (drill->n_holes == drill->n_holes_allocated)
1528 drill->n_holes_allocated += 100;
1529 drill->holes =
1530 (struct drill_hole *) realloc (drill->holes,
1531 drill->n_holes_allocated *
1532 sizeof (struct drill_hole));
1535 drill->holes[ drill->n_holes ].x = cx_inches;
1536 drill->holes[ drill->n_holes ].y = cy_inches;
1537 drill->n_holes++;
1540 static void
1541 gcode_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1543 gcodeGC gcode_gc = (gcodeGC)gc;
1545 use_gc (gc);
1547 gdImageSetThickness (gcode_im, 0);
1548 linewidth = 0;
1549 gdImageFilledEllipse (gcode_im,
1550 pcb_to_gcode (cx),
1551 pcb_to_gcode (cy),
1552 pcb_to_gcode (2 * radius + gcode_toolradius * 2),
1553 pcb_to_gcode (2 * radius + gcode_toolradius * 2),
1554 gcode_gc->color->c);
1555 if (save_drill && is_drill)
1557 double diameter_inches = COORD_TO_INCH(radius*2);
1559 struct single_size_drills* drill = get_drill (diameter_inches);
1560 add_hole (drill,
1561 /* convert to inch, flip: will drill from bottom side */
1562 COORD_TO_INCH(PCB->MaxWidth - cx),
1563 /* PCB reverses y axis */
1564 COORD_TO_INCH(PCB->MaxHeight - cy));
1568 static void
1569 gcode_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1571 gcodeGC gcode_gc = (gcodeGC)gc;
1572 int i;
1573 gdPoint *points;
1575 points = (gdPoint *) malloc (n_coords * sizeof (gdPoint));
1576 if (points == NULL)
1578 fprintf (stderr, "ERROR: gcode_fill_polygon(): malloc failed\n");
1579 exit (1);
1581 use_gc (gc);
1582 for (i = 0; i < n_coords; i++)
1584 points[i].x = pcb_to_gcode (x[i]);
1585 points[i].y = pcb_to_gcode (y[i]);
1587 gdImageSetThickness (gcode_im, 0);
1588 linewidth = 0;
1589 gdImageFilledPolygon (gcode_im, points, n_coords, gcode_gc->color->c);
1590 free (points);
1591 /* printf("FillPoly\n"); */
1594 static void
1595 gcode_calibrate (double xval, double yval)
1597 CRASH;
1600 static void
1601 gcode_set_crosshair (int x, int y, int a)
1605 /* *** Miscellaneous ******************************************************* */
1607 #include "dolists.h"
1609 void
1610 hid_gcode_init ()
1612 memset (&gcode_hid, 0, sizeof (HID));
1613 memset (&gcode_graphics, 0, sizeof (HID_DRAW));
1614 memset (&gcode_graphics_class, 0, sizeof (HID_DRAW_CLASS));
1616 common_nogui_init (&gcode_hid);
1618 gcode_hid.struct_size = sizeof (HID);
1619 gcode_hid.name = "gcode";
1620 gcode_hid.description = "G-CODE export";
1621 gcode_hid.exporter = 1;
1623 gcode_hid.get_export_options = gcode_get_export_options;
1624 gcode_hid.do_export = gcode_do_export;
1625 gcode_hid.parse_arguments = gcode_parse_arguments;
1626 gcode_hid.calibrate = gcode_calibrate;
1627 gcode_hid.set_crosshair = gcode_set_crosshair;
1629 common_nogui_graphics_class_init (&gcode_graphics_class);
1630 common_draw_helpers_class_init (&gcode_graphics_class);
1632 gcode_graphics_class.set_layer = gcode_set_layer;
1633 gcode_graphics_class.make_gc = gcode_make_gc;
1634 gcode_graphics_class.destroy_gc = gcode_destroy_gc;
1635 gcode_graphics_class.use_mask = gcode_use_mask;
1636 gcode_graphics_class.set_color = gcode_set_color;
1637 gcode_graphics_class.set_line_cap = gcode_set_line_cap;
1638 gcode_graphics_class.set_line_width = gcode_set_line_width;
1639 gcode_graphics_class.set_draw_xor = gcode_set_draw_xor;
1640 gcode_graphics_class.set_draw_faded = gcode_set_draw_faded;
1641 gcode_graphics_class.draw_line = gcode_draw_line;
1642 gcode_graphics_class.draw_arc = gcode_draw_arc;
1643 gcode_graphics_class.draw_rect = gcode_draw_rect;
1644 gcode_graphics_class.fill_circle = gcode_fill_circle;
1645 gcode_graphics_class.fill_polygon = gcode_fill_polygon;
1646 gcode_graphics_class.fill_rect = gcode_fill_rect;
1648 gcode_graphics.klass = &gcode_graphics_class;
1649 gcode_graphics.poly_before = true;
1650 common_nogui_graphics_init (&gcode_graphics);
1651 common_draw_helpers_init (&gcode_graphics);
1653 hid_register_hid (&gcode_hid);
1655 #include "gcode_lists.h"