hid/gcode: Make gcode_hid structure static
[geda-pcb/pcjc2.git] / src / hid / gcode / gcode.c
blob9bd6b5952b7ab10c0454472f4a8782754cf36727
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
6 * GCODE export HID
7 * Copyright (c) 2010 Alberto Maccioni
8 * Copyright (c) 2012 Markus Hitter (mah@jump-ing.de)
9 * This code is based on the NELMA export HID, the PNG export HID,
10 * and potrace, a tracing program by Peter Selinger
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 * This HID exports a PCB layout into:
30 * one layer mask file (PNG format) per copper layer,
31 * one G-CODE CNC drill file per drill size
32 * one G-CODE CNC file per copper layer.
33 * The latter is used by a CNC milling machine to mill the pcb.
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
40 #include <stdio.h>
41 #include <stdarg.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <assert.h>
46 #include <time.h>
48 #include "global.h"
49 #include "error.h" /* Message() */
50 #include "data.h"
51 #include "change.h" /* UpdateExtents() */
52 #include "misc.h"
53 #include "rats.h"
55 #include "hid.h"
56 #include "../hidint.h"
57 #include <gd.h>
58 #include "hid/common/hidnogui.h"
59 #include "hid/common/draw_helpers.h"
60 #include "bitmap.h"
61 #include "curve.h"
62 #include "potracelib.h"
63 #include "trace.h"
64 #include "decompose.h"
65 #include "pcb-printf.h"
67 #include "hid/common/hidinit.h"
69 #ifdef HAVE_LIBDMALLOC
70 #include <dmalloc.h>
71 #endif
73 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented GCODE function %s.\n", __FUNCTION__); abort()
75 static HID gcode_hid;
77 struct color_struct
79 /* the descriptor used by the gd library */
80 int c;
82 /* so I can figure out what rgb value c refers to */
83 unsigned int r, g, b;
86 struct hid_gc_struct
88 HID *me_pointer;
89 EndCapStyle cap;
90 int width;
91 unsigned char r, g, b;
92 int erase;
93 struct color_struct *color;
94 gdImagePtr brush;
97 static struct color_struct *black = NULL, *white = NULL;
98 static int linewidth = -1;
99 static gdImagePtr lastbrush = (gdImagePtr)((void *) -1);
101 /* gd image and file for PNG export */
102 static gdImagePtr gcode_im = NULL;
103 static FILE *gcode_f = NULL;
105 static int is_mask;
106 static int is_drill;
107 static int is_solder;
110 * Which groups of layers to export into PNG layer masks. 1 means export, 0
111 * means do not export.
113 static int gcode_export_group[MAX_LAYER];
115 /* Group that is currently exported. */
116 static int gcode_cur_group;
118 /* Filename prefix and suffix that will be used when saving files. */
119 static const char *gcode_basename = NULL;
121 /* Horizontal DPI (grid points per inch) */
122 static int gcode_dpi = -1;
124 static double gcode_cutdepth = 0; /* milling depth (inch) */
125 static double gcode_isoplunge = 0; /* isolation milling plunge feedrate */
126 static double gcode_isofeedrate = 0; /* isolation milling feedrate */
127 static char gcode_predrill;
128 static double gcode_drilldepth = 0; /* drilling depth (mm or in) */
129 static double gcode_drillfeedrate = 0; /* drilling feedrate */
130 static double gcode_safeZ = 100; /* safe Z (mm or in) */
131 static int gcode_toolradius = 0; /* iso-mill tool radius (1/100 mil) */
132 static char gcode_drillmill = 0; /* wether to drill with the mill tool */
133 static double gcode_milldepth = 0; /* outline milling depth (mm or in) */
134 static double gcode_milltoolradius = 0; /* outline-mill tool radius (mm or in)*/
135 static double gcode_millplunge = 0; /* outline-milling plunge feedrate */
136 static double gcode_millfeedrate = 0; /* outline-milling feedrate */
137 static char gcode_advanced = 0;
138 static int save_drill = 0;
140 /* structure to represent a single hole */
141 struct drill_hole
143 double x;
144 double y;
147 /* structure to represent all holes of a given size */
148 struct single_size_drills
150 double diameter_inches;
152 int n_holes;
153 int n_holes_allocated;
154 struct drill_hole* holes;
157 /* at the start we have no drills at all */
158 static struct single_size_drills* drills = NULL;
159 static int n_drills = 0;
160 static int n_drills_allocated = 0;
162 HID_Attribute gcode_attribute_list[] = {
163 /* other HIDs expect this to be first. */
164 {"basename", "File name prefix and suffix,\n"
165 "layer names will be inserted before the suffix.",
166 HID_String, 0, 0, {0, 0, 0}, 0, 0},
167 #define HA_basename 0
169 {"measurement-unit", "Measurement unit used in the G-code output.",
170 HID_Unit, 0, 0, {3, 0, 0}, NULL, 0},
171 #define HA_unit 1
173 {"dpi", "Accuracy of the mill path generation in pixels/inch.",
174 HID_Integer, 0, 2000, {600, 0, 0}, 0, 0},
175 #define HA_dpi 2
177 {"safe-Z", "Safe Z for traverse movements of all operations.",
178 HID_Real, -1000, 10000, {0, 0, 2}, 0, 0},
179 #define HA_safeZ 3
181 {"iso-mill-depth", "Isolation milling depth.",
182 HID_Real, -1000, 1000, {0, 0, -0.05}, 0, 0},
183 #define HA_cutdepth 4
185 {"iso-tool-diameter", "Isolation milling tool diameter.",
186 HID_Real, 0, 10000, {0, 0, 0.2}, 0, 0},
187 #define HA_tooldiameter 5
189 {"iso-tool-plunge", "Isolation milling feedrate when plunging into\n"
190 "the material.",
191 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0},
192 #define HA_isoplunge 6
194 {"iso-tool-feedrate", "Isolation milling feedrate.",
195 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
196 #define HA_isofeedrate 7
198 {"predrill", "Wether to pre-drill all drill spots with the isolation milling\n"
199 "tool. Drill depth is iso-mill-depth here. This feature eases\n"
200 "and enhances accuracy of manual drilling.",
201 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
202 #define HA_predrill 8
204 {"drill-depth", "Drilling depth.",
205 HID_Real, -10000, 10000, {0, 0, -2}, 0, 0},
206 #define HA_drilldepth 9
208 {"drill-feedrate", "Drilling feedrate.",
209 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
210 #define HA_drillfeedrate 10
212 {"drill-mill", "Wether to produce drill holes equal or bigger than the\n"
213 "milling tool diameter with the milling tool.\n"
214 "With the milling tool bigger holes can be accurately sized\n"
215 "without changing the tool",
216 HID_Boolean, 0, 0, {1, 0, 0}, 0, 0},
217 #define HA_drillmill 11
219 {"outline-mill-depth", "Milling depth when milling the outline.\n"
220 "Currently, only the rectangular extents of the\n"
221 "board are milled, no polygonal outlines or holes.",
222 HID_Real, -10000, 10000, {0, 0, -1}, 0, 0},
223 #define HA_milldepth 12
225 {"outline-tool-diameter", "Diameter of the tool used for outline milling.",
226 HID_Real, 0, 10000, {0, 0, 1}, 0, 0},
227 #define HA_milltooldiameter 13
229 {"outline-mill-plunge", "Outline milling feedrate when plunging into\n"
230 "the material",
231 HID_Real, 0.1, 10000, {0, 0, 25.}, 0, 0},
232 #define HA_millplunge 14
234 {"outline-mill-feedrate", "Outline milling feedrate",
235 HID_Real, 0.1, 10000, {0, 0, 50.}, 0, 0},
236 #define HA_millfeedrate 15
238 {"advanced-gcode", "Wether to produce G-code for advanced interpreters,\n"
239 "like using variables or drill cycles. Not all\n"
240 "machine controllers understand this, but it allows\n"
241 "better hand-editing of the resulting files.",
242 HID_Boolean, 0, 0, {-1, 0, 0}, 0, 0},
243 #define HA_advanced 16
246 #define NUM_OPTIONS (sizeof(gcode_attribute_list)/sizeof(gcode_attribute_list[0]))
248 REGISTER_ATTRIBUTES (gcode_attribute_list)
249 static HID_Attr_Val gcode_values[NUM_OPTIONS];
251 /* *** Utility funcions **************************************************** */
253 /* convert from default PCB units to gcode units */
254 static int pcb_to_gcode (int pcb)
256 return round(COORD_TO_INCH(pcb) * gcode_dpi);
259 /* Fits the given layer name into basename, just before the suffix */
260 static void
261 gcode_get_filename (char *filename, const char *layername)
263 char *pt;
264 char suffix[MAXPATHLEN];
266 suffix[0] = '\0';
267 pt = strrchr (gcode_basename, '.');
268 if (pt && pt > strrchr (gcode_basename, '/'))
269 strcpy (suffix, pt);
270 else
271 pt = NULL;
273 strcpy (filename, gcode_basename);
274 if (pt)
275 *(filename + (pt - gcode_basename)) = '\0';
276 strcat (filename, "-");
277 strcat (filename, layername);
278 strcat (filename, suffix);
280 // result is in char *filename
283 /* Sorts drills to produce a short tool path. I start with the hole nearest
284 * (0,0) and for each subsequent one, find the hole nearest to the previous.
285 * This isn't guaranteed to find the shortest path, but should be good enough.
286 * Note that this is O(N^2). We can't use the O(N logN) sort, since our
287 * shortest-distance origin changes with every point */
288 static void
289 sort_drill (struct drill_hole *drill, int n_drill)
291 /* I start out by looking for points closest to (0,0) */
292 struct drill_hole nearest_target = { 0, 0 };
294 /* I sort my list by finding the correct point to fill each slot. I don't need
295 to look at the last one, since it'll be in the right place automatically */
296 for (int j = 0; j < n_drill-1; j++)
298 double dmin = 1e20;
299 int imin = 0;
300 /* look through remaining elements to find the next drill point. This is
301 the one nearest to nearest_target */
302 for (int i = j; i < n_drill; i++)
304 double d =
305 (drill[i].x - nearest_target.x) * (drill[i].x - nearest_target.x) +
306 (drill[i].y - nearest_target.y) * (drill[i].y - nearest_target.y);
307 if (d < dmin)
309 imin = i;
310 dmin = d;
313 /* printf("j=%d imin=%d dmin=%f nearest_target=(%f,%f)\n",j,imin,dmin,
314 nearest_target.x,nearest_target.y); */
315 if (j != imin)
317 struct drill_hole tmp;
318 tmp = drill[j];
319 drill[j] = drill[imin];
320 drill[imin] = tmp;
323 nearest_target = drill[j];
327 /* *** Main export callback ************************************************ */
329 static void
330 gcode_parse_arguments (int *argc, char ***argv)
332 hid_register_attributes (gcode_attribute_list,
333 sizeof (gcode_attribute_list) /
334 sizeof (gcode_attribute_list[0]));
335 hid_parse_command_line (argc, argv);
338 static HID_Attribute *
339 gcode_get_export_options (int *n)
341 static char *last_made_filename = 0;
342 static int last_unit_value = -1;
344 if (gcode_attribute_list[HA_unit].default_val.int_value == last_unit_value)
346 if (Settings.grid_unit)
347 gcode_attribute_list[HA_unit].default_val.int_value = Settings.grid_unit->index;
348 else
349 gcode_attribute_list[HA_unit].default_val.int_value = get_unit_struct ("mil")->index;
350 last_unit_value = gcode_attribute_list[HA_unit].default_val.int_value;
353 if (PCB)
355 derive_default_filename (PCB->Filename,
356 &gcode_attribute_list[HA_basename],
357 ".gcode", &last_made_filename);
359 if (n)
361 *n = NUM_OPTIONS;
363 return gcode_attribute_list;
366 /* Populates gcode_export_group array */
367 static void
368 gcode_choose_groups ()
370 int n, m;
371 LayerType *layer;
373 /* Set entire array to 0 (don't export any layer groups by default */
374 memset (gcode_export_group, 0, sizeof (gcode_export_group));
376 for (n = 0; n < max_copper_layer; n++)
378 layer = &PCB->Data->Layer[n];
380 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
382 /* layer isn't empty */
385 * is this check necessary? It seems that special
386 * layers have negative indexes?
389 if (SL_TYPE (n) == 0)
391 /* layer is a copper layer */
392 m = GetLayerGroupNumberByNumber (n);
394 /* the export layer */
395 gcode_export_group[m] = 1;
401 static void
402 gcode_alloc_colors ()
405 * Allocate white and black -- the first color allocated becomes the
406 * background color
409 white = (struct color_struct *) malloc (sizeof (*white));
410 white->r = white->g = white->b = 255;
411 white->c = gdImageColorAllocate (gcode_im, white->r, white->g, white->b);
413 black = (struct color_struct *) malloc (sizeof (*black));
414 black->r = black->g = black->b = 0;
415 black->c = gdImageColorAllocate (gcode_im, black->r, black->g, black->b);
418 static void
419 gcode_start_png ()
421 gcode_im = gdImageCreate (pcb_to_gcode (PCB->ExtentMaxX - PCB->ExtentMinX),
422 pcb_to_gcode (PCB->ExtentMaxY - PCB->ExtentMinY));
423 gcode_alloc_colors ();
426 static void
427 gcode_finish_png (const char *layername)
429 #ifdef HAVE_GDIMAGEPNG
430 char *pngname, *filename;
431 FILE *file = NULL;
433 pngname = (char *)malloc (MAXPATHLEN);
434 gcode_get_filename (pngname, layername);
435 filename = g_strdup_printf ("%s.png", pngname);
436 free(pngname);
438 file = fopen (filename, "wb");
439 g_free (filename);
441 gdImagePng (gcode_im, file);
442 fclose (file);
443 #else
444 Message ("GCODE: PNG not supported by gd. Can't write layer mask.\n");
445 #endif
446 gdImageDestroy (gcode_im);
448 free (white);
449 free (black);
450 gcode_im = NULL;
453 static void
454 gcode_start_png_export ()
456 BoxType region;
458 region.X1 = PCB->ExtentMinX;
459 region.Y1 = PCB->ExtentMinY;
460 region.X2 = PCB->ExtentMaxX;
461 region.Y2 = PCB->ExtentMaxY;
463 linewidth = -1;
464 lastbrush = (gdImagePtr)((void *) -1);
466 hid_expose_callback (&gcode_hid, &region, 0);
469 static FILE *
470 gcode_start_gcode (const char *layername, bool metric)
472 FILE *file = NULL;
473 char buffer[MAXPATHLEN];
474 time_t t;
476 gcode_get_filename (buffer, layername);
477 file = fopen (buffer, "wb");
478 if ( ! file)
480 perror (buffer);
481 return NULL;
483 fprintf (file, "(Created by G-code exporter)\n");
484 t = time (NULL);
485 snprintf (buffer, sizeof(buffer), "%s", ctime (&t));
486 buffer[strlen (buffer) - 1] = '\0'; // drop the newline
487 fprintf (file, "(%s)\n", buffer);
488 fprintf (file, "(Units: %s)\n", metric ? "mm" : "inch");
489 if (metric)
490 pcb_fprintf (file, "(Board size: %.2mm x %.2mm mm)\n",
491 PCB->ExtentMaxX - PCB->ExtentMinX,
492 PCB->ExtentMaxY - PCB->ExtentMinY);
493 else
494 pcb_fprintf (file, "(Board size: %.2mi x %.2mi inches)\n",
495 PCB->ExtentMaxX - PCB->ExtentMinX,
496 PCB->ExtentMaxY - PCB->ExtentMinY);
498 return file;
501 static void
502 gcode_do_export (HID_Attr_Val * options)
504 int save_ons[MAX_LAYER + 2];
505 int i, idx;
506 const Unit *unit;
507 double scale = 0, d = 0;
508 int r, c, v, p, metric;
509 path_t *plist = NULL;
510 potrace_bitmap_t *bm = NULL;
511 potrace_param_t param_default = {
512 2, /* turnsize */
513 POTRACE_TURNPOLICY_MINORITY, /* turnpolicy */
514 1.0, /* alphamax */
515 1, /* opticurve */
516 0.2, /* opttolerance */
518 NULL, /* callback function */
519 NULL, /* callback data */
520 0.0, 1.0, /* progress range */
521 0.0, /* granularity */
524 char variable_safeZ[20], variable_cutdepth[20];
525 char variable_isoplunge[20], variable_isofeedrate[20];
526 char variable_drilldepth[20], variable_milldepth[20];
527 char variable_millplunge[20], variable_millfeedrate[20];
528 char *old_locale = setlocale (LC_NUMERIC, NULL);
530 if (!options)
532 gcode_get_export_options (0);
533 for (i = 0; i < NUM_OPTIONS; i++)
535 gcode_values[i] = gcode_attribute_list[i].default_val;
537 options = gcode_values;
539 gcode_basename = options[HA_basename].str_value;
540 if (!gcode_basename)
542 gcode_basename = "pcb-out.gcode";
544 gcode_dpi = options[HA_dpi].int_value;
545 if (gcode_dpi < 0)
547 fprintf (stderr, "ERROR: dpi may not be < 0\n");
548 return;
550 unit = &(get_unit_list() [options[HA_unit].int_value]);
551 metric = (unit->family == METRIC);
552 scale = metric ? 1.0 / coord_to_unit (unit, MM_TO_COORD (1.0))
553 : 1.0 / coord_to_unit (unit, INCH_TO_COORD (1.0));
555 gcode_cutdepth = options[HA_cutdepth].real_value * scale;
556 gcode_isoplunge = options[HA_isoplunge].real_value * scale;
557 gcode_isofeedrate = options[HA_isofeedrate].real_value * scale;
558 gcode_predrill = options[HA_predrill].int_value;
559 gcode_drilldepth = options[HA_drilldepth].real_value * scale;
560 gcode_drillfeedrate = options[HA_drillfeedrate].real_value * scale;
561 gcode_safeZ = options[HA_safeZ].real_value * scale;
562 gcode_toolradius = metric
563 ? MM_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale)
564 : INCH_TO_COORD(options[HA_tooldiameter].real_value / 2 * scale);
565 gcode_drillmill = options[HA_drillmill].int_value;
566 gcode_milldepth = options[HA_milldepth].real_value * scale;
567 gcode_milltoolradius = options[HA_milltooldiameter].real_value / 2 * scale;
568 gcode_millplunge = options[HA_millplunge].real_value * scale;
569 gcode_millfeedrate = options[HA_millfeedrate].real_value * scale;
570 gcode_advanced = options[HA_advanced].int_value;
571 gcode_choose_groups ();
572 setlocale (LC_NUMERIC, "C"); /* use . as separator */
573 if (gcode_advanced)
575 /* give each variable distinct names, even if they don't appear
576 together in a file. This allows to join output files */
577 strcpy (variable_safeZ, "#100");
578 strcpy (variable_cutdepth, "#101");
579 strcpy (variable_isoplunge, "#102");
580 strcpy (variable_isofeedrate, "#103");
581 strcpy (variable_drilldepth, "#104");
582 strcpy (variable_milldepth, "#105");
583 strcpy (variable_millplunge, "#106");
584 strcpy (variable_millfeedrate, "#107");
586 else
588 snprintf (variable_safeZ, 20, "%f", gcode_safeZ);
589 snprintf (variable_cutdepth, 20, "%f", gcode_cutdepth);
590 snprintf (variable_isoplunge, 20, "%f", gcode_isoplunge);
591 snprintf (variable_isofeedrate, 20, "%f", gcode_isofeedrate);
592 snprintf (variable_drilldepth, 20, "%f", gcode_drilldepth);
593 snprintf (variable_milldepth, 20, "%f", gcode_milldepth);
594 snprintf (variable_millplunge, 20, "%f", gcode_millplunge);
595 snprintf (variable_millfeedrate, 20, "%f", gcode_millfeedrate);
597 UpdateExtents();
599 for (i = 0; i < MAX_LAYER; i++)
601 if (gcode_export_group[i])
603 gcode_cur_group = i;
605 /* magic */
606 idx = (i >= 0 && i < max_group) ?
607 PCB->LayerGroups.Entries[i][0] : i;
608 is_solder =
609 (GetLayerGroupNumberByNumber (idx) ==
610 GetLayerGroupNumberByNumber (solder_silk_layer)) ? 1 : 0;
611 save_drill = is_solder; /* save drills for one layer only */
612 gcode_start_png ();
613 hid_save_and_show_layer_ons (save_ons);
614 gcode_start_png_export ();
615 hid_restore_layer_ons (save_ons);
617 /* ***************** gcode conversion *************************** */
618 /* potrace uses a different kind of bitmap; for simplicity gcode_im is
619 copied to this format and flipped as needed along the way */
620 bm = bm_new (gdImageSX (gcode_im), gdImageSY (gcode_im));
621 for (r = 0; r < gdImageSX (gcode_im); r++)
623 for (c = 0; c < gdImageSY (gcode_im); c++)
625 if (is_solder)
626 v = /* flip vertically and horizontally */
627 gdImageGetPixel (gcode_im, gdImageSX (gcode_im) - 1 - r,
628 gdImageSY (gcode_im) - 1 - c);
629 else
630 v = /* flip only vertically */
631 gdImageGetPixel (gcode_im, r,
632 gdImageSY (gcode_im) - 1 - c);
633 p = (gcode_im->red[v] || gcode_im->green[v]
634 || gcode_im->blue[v]) ? 0 : 0xFFFFFF;
635 BM_PUT (bm, r, c, p);
638 if (is_solder)
639 { /* flip back layer, used only for PNG output */
640 gdImagePtr temp_im =
641 gdImageCreate (gdImageSX (gcode_im), gdImageSY (gcode_im));
642 gdImageColorAllocate (temp_im, white->r, white->g, white->b);
643 gdImageColorAllocate (temp_im, black->r, black->g, black->b);
644 gdImageCopy (temp_im, gcode_im, 0, 0, 0, 0,
645 gdImageSX (gcode_im), gdImageSY (gcode_im));
646 for (r = 0; r < gdImageSX (gcode_im); r++)
648 for (c = 0; c < gdImageSY (gcode_im); c++)
650 gdImageSetPixel (gcode_im, r, c,
651 gdImageGetPixel (temp_im,
652 gdImageSX (gcode_im) -
653 1 - r, c));
656 gdImageDestroy (temp_im);
658 gcode_finish_png (layer_type_to_file_name (idx, FNS_fixed));
659 plist = NULL;
660 gcode_f = gcode_start_gcode (layer_type_to_file_name (idx, FNS_fixed),
661 metric);
662 if (!gcode_f)
664 bm_free (bm);
665 goto error;
667 fprintf (gcode_f, "(Accuracy %d dpi)\n", gcode_dpi);
668 fprintf (gcode_f, "(Tool diameter: %f %s)\n",
669 options[HA_tooldiameter].real_value * scale,
670 metric ? "mm" : "inch");
671 if (gcode_advanced)
673 fprintf (gcode_f, "%s=%f (safe Z)\n",
674 variable_safeZ, gcode_safeZ);
675 fprintf (gcode_f, "%s=%f (cutting depth)\n",
676 variable_cutdepth, gcode_cutdepth);
677 fprintf (gcode_f, "%s=%f (plunge feedrate)\n",
678 variable_isoplunge, gcode_isoplunge);
679 fprintf (gcode_f, "%s=%f (feedrate)\n",
680 variable_isofeedrate, gcode_isofeedrate);
682 if (gcode_predrill && save_drill)
683 fprintf (gcode_f, "(with predrilling)\n");
684 else
685 fprintf (gcode_f, "(no predrilling)\n");
686 fprintf (gcode_f, "(---------------------------------)\n");
687 if (gcode_advanced)
688 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
689 metric ? 21 : 20);
690 else
691 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
692 metric ? 21 : 20);
693 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
694 /* extract contour points from image */
695 r = bm_to_pathlist (bm, &plist, &param_default);
696 if (r)
698 fprintf (stderr, "ERROR: pathlist function failed\n");
699 goto error;
701 /* generate best polygon and write vertices in g-code format */
702 d = process_path (plist, &param_default, bm, gcode_f,
703 metric ? 25.4 / gcode_dpi : 1.0 / gcode_dpi,
704 variable_cutdepth, variable_safeZ,
705 variable_isoplunge, variable_isofeedrate);
706 if (d < 0)
708 fprintf (stderr, "ERROR: path process function failed\n");
709 goto error;
711 if (gcode_predrill && save_drill)
713 int n_all_drills = 0;
714 struct drill_hole* all_drills = NULL;
715 /* count all drills to be predrilled */
716 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
718 struct single_size_drills* drill_set = &drills[i_drill_sets];
720 /* don't predrill drillmill holes */
721 if (gcode_drillmill) {
722 double radius = metric ?
723 drill_set->diameter_inches * 25.4 / 2:
724 drill_set->diameter_inches / 2;
726 if (gcode_milltoolradius < radius)
727 continue;
730 n_all_drills += drill_set->n_holes;
732 /* for sorting regardless of size, copy all drills to be
733 predrilled into one new structure */
734 all_drills = (struct drill_hole *)
735 malloc (n_all_drills * sizeof (struct drill_hole));
736 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
738 struct single_size_drills* drill_set = &drills[i_drill_sets];
740 /* don't predrill drillmill holes */
741 if (gcode_drillmill) {
742 double radius = metric ?
743 drill_set->diameter_inches * 25.4 / 2:
744 drill_set->diameter_inches / 2;
746 if (gcode_milltoolradius < radius)
747 continue;
750 memcpy(&all_drills[r], drill_set->holes,
751 drill_set->n_holes * sizeof(struct drill_hole));
753 sort_drill(all_drills, n_all_drills);
754 /* write that (almost the same code as writing the drill file) */
755 fprintf (gcode_f, "(predrilling)\n");
756 fprintf (gcode_f, "F%s\n", variable_isoplunge);
758 for (r = 0; r < n_all_drills; r++)
760 double drillX, drillY;
762 if (metric)
764 drillX = all_drills[r].x * 25.4;
765 drillY = all_drills[r].y * 25.4;
767 else
769 drillX = all_drills[r].x;
770 drillY = all_drills[r].y;
772 if (gcode_advanced)
773 fprintf (gcode_f, "G81 X%f Y%f Z%s R%s\n", drillX, drillY,
774 variable_cutdepth, variable_safeZ);
775 else
777 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
778 fprintf (gcode_f, "G1 Z%s\n", variable_cutdepth);
779 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
782 fprintf (gcode_f, "(%d predrills)\n", n_all_drills);
783 free(all_drills);
785 if (metric)
786 fprintf (gcode_f, "(milling distance %.2fmm = %.2fin)\n", d,
787 d * 1 / 25.4);
788 else
789 fprintf (gcode_f, "(milling distance %.2fmm = %.2fin)\n",
790 25.4 * d, d);
791 if (gcode_advanced)
792 fprintf (gcode_f, "M5 M9 M2\n");
793 else
794 fprintf (gcode_f, "M5\nM9\nM2\n");
795 pathlist_free (plist);
796 bm_free (bm);
797 fclose (gcode_f);
798 gcode_f = NULL;
799 if (save_drill)
801 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++)
803 struct single_size_drills* drill = &drills[i_drill_file];
805 /* don't drill drillmill holes */
806 if (gcode_drillmill) {
807 double radius = metric ?
808 drill->diameter_inches * 25.4 / 2:
809 drill->diameter_inches / 2;
811 if (gcode_milltoolradius < radius)
812 continue;
815 d = 0;
816 sort_drill (drill->holes, drill->n_holes);
819 // get the filename with the drill size encoded in it
820 char layername[32];
821 snprintf(layername, sizeof(layername),
822 "%.4f.drill",
823 metric ?
824 drill->diameter_inches * 25.4 :
825 drill->diameter_inches);
826 gcode_f = gcode_start_gcode(layername, metric);
828 if (!gcode_f)
829 goto error;
830 fprintf (gcode_f, "(Drill file: %d drills)\n", drill->n_holes);
831 if (metric)
832 fprintf (gcode_f, "(Drill diameter: %f mm)\n",
833 drill->diameter_inches * 25.4);
834 else
835 fprintf (gcode_f, "(Drill diameter: %f inch)\n",
836 drill->diameter_inches);
837 if (gcode_advanced)
839 fprintf (gcode_f, "%s=%f (safe Z)\n",
840 variable_safeZ, gcode_safeZ);
841 fprintf (gcode_f, "%s=%f (drill depth)\n",
842 variable_drilldepth, gcode_drilldepth);
843 fprintf (gcode_f, "(---------------------------------)\n");
844 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%f\n",
845 metric ? 21 : 20, gcode_drillfeedrate);
847 else
849 fprintf (gcode_f, "(---------------------------------)\n");
850 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\nF%f\n",
851 metric ? 21 : 20, gcode_drillfeedrate);
853 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
854 for (r = 0; r < drill->n_holes; r++)
856 double drillX, drillY;
858 if (metric)
860 drillX = drill->holes[r].x * 25.4;
861 drillY = drill->holes[r].y * 25.4;
863 else
865 drillX = drill->holes[r].x;
866 drillY = drill->holes[r].y;
868 if (gcode_advanced)
869 fprintf (gcode_f, "G81 X%f Y%f Z%s R%s\n", drillX, drillY,
870 variable_drilldepth, variable_safeZ);
871 else
873 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
874 fprintf (gcode_f, "G1 Z%s\n", variable_drilldepth);
875 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
877 if (r > 0)
878 d += Distance(drill->holes[r - 1].x, drill->holes[r - 1].y,
879 drill->holes[r ].x, drill->holes[r ].y);
881 if (gcode_advanced)
882 fprintf (gcode_f, "M5 M9 M2\n");
883 else
884 fprintf (gcode_f, "M5\nM9\nM2\n");
885 fprintf (gcode_f, "(end, total distance %.2fmm = %.2fin)\n",
886 25.4 * d, d);
887 fclose (gcode_f);
890 /* ******************* handle drill-milling **************************** */
891 if (save_drill && gcode_drillmill)
893 int n_drillmill_drills = 0;
894 struct drill_hole* drillmill_drills = NULL;
895 double* drillmill_radiuss = NULL;
896 double mill_radius, inaccuracy;
898 /* count drillmill drills */
899 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
901 struct single_size_drills* drill_set = &drills[i_drill_sets];
902 double radius = metric ?
903 drill_set->diameter_inches * 25.4 / 2:
904 drill_set->diameter_inches / 2;
906 if (gcode_milltoolradius >= radius)
907 n_drillmill_drills += drill_set->n_holes;
909 /* for sorting regardless of size, copy all available drills
910 into one new structure */
911 drillmill_drills = (struct drill_hole *)
912 malloc (n_drillmill_drills * sizeof (struct drill_hole));
913 drillmill_radiuss = (double *)
914 malloc (n_drillmill_drills * sizeof (double));
915 r = 0;
916 for (int i_drill_sets = 0; i_drill_sets < n_drills; i_drill_sets++)
918 struct single_size_drills* drill_set = &drills[i_drill_sets];
919 double radius = metric ?
920 drill_set->diameter_inches * 25.4 / 2:
921 drill_set->diameter_inches / 2;
923 if (gcode_milltoolradius >= radius)
925 memcpy(&drillmill_drills[r], drill_set->holes,
926 drill_set->n_holes * sizeof(struct drill_hole));
927 drillmill_radiuss[r] = radius;
928 r += drill_set->n_holes;
931 sort_drill(drillmill_drills, n_drillmill_drills);
933 gcode_f = gcode_start_gcode("drillmill", metric);
934 if (!gcode_f)
935 goto error;
936 fprintf (gcode_f, "(Drillmill file)\n");
937 fprintf (gcode_f, "(Tool diameter: %f %s)\n",
938 gcode_milltoolradius * 2, metric ? "mm" : "inch");
939 if (gcode_advanced)
941 fprintf (gcode_f, "%s=%f (safe Z)\n",
942 variable_safeZ, gcode_safeZ);
943 fprintf (gcode_f, "%s=%f (mill depth)\n",
944 variable_milldepth, gcode_milldepth);
945 fprintf (gcode_f, "%s=%f (mill plunge feedrate)\n",
946 variable_millplunge, gcode_millplunge);
947 fprintf (gcode_f, "%s=%f (mill feedrate)\n",
948 variable_millfeedrate, gcode_millfeedrate);
949 fprintf (gcode_f, "(---------------------------------)\n");
950 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
951 metric ? 21 : 20);
953 else
955 fprintf (gcode_f, "(---------------------------------)\n");
956 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
957 metric ? 21 : 20);
959 for (r = 0; r < n_drillmill_drills; r++)
961 double drillX, drillY;
963 if (metric)
965 drillX = drillmill_drills[r].x * 25.4;
966 drillY = drillmill_drills[r].y * 25.4;
968 else
970 drillX = drillmill_drills[r].x;
971 drillY = drillmill_drills[r].y;
973 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
974 fprintf (gcode_f, "G1 Z%s F%s\n",
975 variable_milldepth, variable_millplunge);
977 mill_radius = drillmill_radiuss[r] - gcode_milltoolradius;
978 inaccuracy = (metric ? 25.4 : 1.) / gcode_dpi;
979 if (mill_radius > inaccuracy)
981 int n_sides;
983 /* calculate how many polygon sides we need to stay
984 within our accuracy while avoiding a G02/G03 */
985 n_sides = M_PI / acos (mill_radius /
986 (mill_radius + inaccuracy));
987 if (n_sides < 4)
988 n_sides = 4;
989 fprintf (gcode_f, "F%s\n", variable_millfeedrate);
990 for (i = 0; i <= n_sides; i++)
992 double angle = M_PI * 2 * i / n_sides;
993 fprintf (gcode_f, "G1 X%f Y%f\n",
994 drillX + mill_radius * cos (angle),
995 drillY + mill_radius * sin (angle));
997 fprintf (gcode_f, "G0 X%f Y%f\n", drillX, drillY);
999 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1001 if (gcode_advanced)
1002 fprintf (gcode_f, "M5 M9 M2\n");
1003 else
1004 fprintf (gcode_f, "M5\nM9\nM2\n");
1005 fclose (gcode_f);
1007 free(drillmill_radiuss);
1008 free(drillmill_drills);
1010 /* ******************* end of per-layer writing ************************ */
1012 for (int i_drill_file=0; i_drill_file < n_drills; i_drill_file++)
1013 free(drills[i_drill_file].holes);
1014 free (drills);
1015 drills = NULL;
1016 n_drills = n_drills_allocated = 0;
1021 * General milling. Put this aside from the above code, as paths
1022 * are generated without taking line or curve thickness into account.
1023 * Accordingly, we need an entirely different approach.
1026 * Currently this is a rarther simple implementation, which mills
1027 * the retangular extents of the board and nothing else. This should
1028 * be sufficient for many use cases.
1030 * A better implementation would have to group the lines and polygons
1031 * on the outline layer by outer polygon and inner holes, then offset
1032 * all of them to the right side and mill that.
1034 /* a better implementation might look like this:
1035 LAYER_LOOP (PCB->Data, MAX_LAYER);
1037 if (strcmp (layer->Name, "outline") == 0)
1039 LINE_LOOP (layer);
1041 ... calculate the offset for all lines and polygons of this layer,
1042 mirror it if is_solder, then mill it ...
1044 END_LOOP;
1047 END_LOOP;
1049 for now: */
1050 { /* unconditional */
1051 double lowerX = 0., lowerY = 0., upperX = 0., upperY = 0.;
1052 double mill_distance = 0.;
1054 gcode_f = gcode_start_gcode("outline", metric);
1055 if (!gcode_f)
1056 goto error;
1057 fprintf (gcode_f, "(Outline mill file)\n");
1058 fprintf (gcode_f, "(Tool diameter: %f %s)\n",
1059 gcode_milltoolradius * 2, metric ? "mm" : "inch");
1060 if (gcode_advanced)
1062 fprintf (gcode_f, "%s=%f (safe Z)\n", variable_safeZ, gcode_safeZ);
1063 fprintf (gcode_f, "%s=%f (mill depth)\n",
1064 variable_milldepth, gcode_milldepth);
1065 fprintf (gcode_f, "%s=%f (mill plunge feedrate)\n",
1066 variable_millplunge, gcode_millplunge);
1067 fprintf (gcode_f, "%s=%f (mill feedrate)\n",
1068 variable_millfeedrate, gcode_millfeedrate);
1069 fprintf (gcode_f, "(---------------------------------)\n");
1070 fprintf (gcode_f, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
1071 metric ? 21 : 20);
1073 else
1075 fprintf (gcode_f, "(---------------------------------)\n");
1076 fprintf (gcode_f, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
1077 metric ? 21 : 20);
1079 if (metric)
1081 upperX = COORD_TO_MM(PCB->ExtentMaxX - PCB->ExtentMinX);
1082 upperY = COORD_TO_MM(PCB->ExtentMaxY - PCB->ExtentMinY);
1084 else
1086 upperX = COORD_TO_INCH(PCB->ExtentMaxX - PCB->ExtentMinX);
1087 upperY = COORD_TO_INCH(PCB->ExtentMaxY - PCB->ExtentMinY);
1089 lowerX -= gcode_milltoolradius;
1090 lowerY -= gcode_milltoolradius;
1091 upperX += gcode_milltoolradius;
1092 upperY += gcode_milltoolradius;
1094 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1095 /* mill the two edges adjectant to 0,0 first to disconnect the
1096 workpiece from the raw material last */
1097 fprintf (gcode_f, "G0 X%f Y%f\n", upperX, lowerY);
1098 fprintf (gcode_f, "G1 Z%s F%s\n",
1099 variable_milldepth, variable_millplunge);
1100 fprintf (gcode_f, "G1 X%f Y%f F%s\n",
1101 lowerX, lowerY, variable_millfeedrate);
1102 fprintf (gcode_f, "G1 X%f Y%f\n", lowerX, upperY);
1103 fprintf (gcode_f, "G1 X%f Y%f\n", upperX, upperY);
1104 fprintf (gcode_f, "G1 X%f Y%f\n", upperX, lowerY);
1105 fprintf (gcode_f, "G0 Z%s\n", variable_safeZ);
1107 if (gcode_advanced)
1108 fprintf (gcode_f, "M5 M9 M2\n");
1109 else
1110 fprintf (gcode_f, "M5\nM9\nM2\n");
1111 mill_distance = abs(gcode_safeZ - gcode_milldepth);
1112 if (metric)
1113 mill_distance /= 25.4;
1114 fprintf (gcode_f, "(end, total distance G0 %.2f mm = %.2f in)\n",
1115 mill_distance * 25.4, mill_distance);
1116 mill_distance = (upperX - lowerX + upperY - lowerY) * 2;
1117 mill_distance += abs(gcode_safeZ - gcode_milldepth);
1118 if (metric)
1119 mill_distance /= 25.4;
1120 fprintf (gcode_f, "( total distance G1 %.2f mm = %.2f in)\n",
1121 mill_distance * 25.4, mill_distance);
1122 fclose (gcode_f);
1125 error:
1126 setlocale (LC_NUMERIC, old_locale); /* restore locale */
1129 /* *** PNG export (slightly modified code from PNG export HID) ************* */
1131 static int
1132 gcode_set_layer (const char *name, int group, int empty)
1134 int idx = (group >= 0 && group < max_group) ?
1135 PCB->LayerGroups.Entries[group][0] : group;
1137 if (name == 0)
1139 name = PCB->Data->Layer[idx].Name;
1141 if (strcmp (name, "invisible") == 0)
1143 return 0;
1145 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
1146 is_mask = (SL_TYPE (idx) == SL_MASK);
1148 if (is_mask)
1150 /* Don't print masks */
1151 return 0;
1153 if (is_drill)
1156 * Print 'holes', so that we can fill gaps in the copper
1157 * layer
1159 return 1;
1161 if (group == gcode_cur_group)
1163 return 1;
1165 return 0;
1168 static hidGC
1169 gcode_make_gc (void)
1171 hidGC rv = (hidGC) malloc (sizeof (struct hid_gc_struct));
1172 rv->me_pointer = &gcode_hid;
1173 rv->cap = Trace_Cap;
1174 rv->width = 1;
1175 rv->color = (struct color_struct *) malloc (sizeof (*rv->color));
1176 rv->color->r = rv->color->g = rv->color->b = 0;
1177 rv->color->c = 0;
1178 return rv;
1181 static void
1182 gcode_destroy_gc (hidGC gc)
1184 free (gc);
1187 static void
1188 gcode_use_mask (enum mask_mode mode)
1190 /* does nothing */
1193 static void
1194 gcode_set_color (hidGC gc, const char *name)
1196 if (gcode_im == NULL)
1198 return;
1200 if (name == NULL)
1202 name = "#ff0000";
1204 if (!strcmp (name, "drill"))
1206 gc->color = black;
1207 gc->erase = 0;
1208 return;
1210 if (!strcmp (name, "erase"))
1212 /* FIXME -- should be background, not white */
1213 gc->color = white;
1214 gc->erase = 1;
1215 return;
1217 gc->color = black;
1218 gc->erase = 0;
1219 return;
1222 static void
1223 gcode_set_line_cap (hidGC gc, EndCapStyle style)
1225 gc->cap = style;
1228 static void
1229 gcode_set_line_width (hidGC gc, Coord width)
1231 gc->width = width;
1234 static void
1235 gcode_set_draw_xor (hidGC gc, int xor_)
1240 static void
1241 gcode_set_draw_faded (hidGC gc, int faded)
1245 static void
1246 use_gc (hidGC gc)
1248 int need_brush = 0;
1250 if (gc->me_pointer != &gcode_hid)
1252 fprintf (stderr, "Fatal: GC from another HID passed to gcode HID\n");
1253 abort ();
1255 if (linewidth != gc->width)
1257 /* Make sure the scaling doesn't erase lines completely */
1259 if (SCALE (gc->width) == 0 && gc->width > 0)
1260 gdImageSetThickness (im, 1);
1261 else
1263 gdImageSetThickness (gcode_im,
1264 pcb_to_gcode (gc->width + 2 * gcode_toolradius));
1265 linewidth = gc->width;
1266 need_brush = 1;
1268 if (lastbrush != gc->brush || need_brush)
1270 static void *bcache = 0;
1271 hidval bval;
1272 char name[256];
1273 char type;
1274 int r;
1276 switch (gc->cap)
1278 case Round_Cap:
1279 case Trace_Cap:
1280 type = 'C';
1281 r = pcb_to_gcode (gc->width / 2 + gcode_toolradius);
1282 break;
1283 default:
1284 case Square_Cap:
1285 r = pcb_to_gcode (gc->width + gcode_toolradius * 2);
1286 type = 'S';
1287 break;
1289 sprintf (name, "#%.2x%.2x%.2x_%c_%d", gc->color->r, gc->color->g,
1290 gc->color->b, type, r);
1292 if (hid_cache_color (0, name, &bval, &bcache))
1294 gc->brush = (gdImagePtr)bval.ptr;
1296 else
1298 int bg, fg;
1299 if (type == 'C')
1300 gc->brush = gdImageCreate (2 * r + 1, 2 * r + 1);
1301 else
1302 gc->brush = gdImageCreate (r + 1, r + 1);
1303 bg = gdImageColorAllocate (gc->brush, 255, 255, 255);
1304 fg =
1305 gdImageColorAllocate (gc->brush, gc->color->r, gc->color->g,
1306 gc->color->b);
1307 gdImageColorTransparent (gc->brush, bg);
1310 * if we shrunk to a radius/box width of zero, then just use
1311 * a single pixel to draw with.
1313 if (r == 0)
1314 gdImageFilledRectangle (gc->brush, 0, 0, 0, 0, fg);
1315 else
1317 if (type == 'C')
1318 gdImageFilledEllipse (gc->brush, r, r, 2 * r, 2 * r, fg);
1319 else
1320 gdImageFilledRectangle (gc->brush, 0, 0, r, r, fg);
1322 bval.ptr = gc->brush;
1323 hid_cache_color (1, name, &bval, &bcache);
1326 gdImageSetBrush (gcode_im, gc->brush);
1327 lastbrush = gc->brush;
1332 static void
1333 gcode_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1335 use_gc (gc);
1336 gdImageRectangle (gcode_im,
1337 pcb_to_gcode (x1 - PCB->ExtentMinX - gcode_toolradius),
1338 pcb_to_gcode (y1 - PCB->ExtentMinY - gcode_toolradius),
1339 pcb_to_gcode (x2 - PCB->ExtentMinX + gcode_toolradius),
1340 pcb_to_gcode (y2 - PCB->ExtentMinY + gcode_toolradius),
1341 gc->color->c);
1342 /* printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */
1345 static void
1346 gcode_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1348 use_gc (gc);
1349 gdImageSetThickness (gcode_im, 0);
1350 linewidth = 0;
1351 gdImageFilledRectangle (gcode_im,
1352 pcb_to_gcode (x1 - PCB->ExtentMinX - gcode_toolradius),
1353 pcb_to_gcode (y1 - PCB->ExtentMinY - gcode_toolradius),
1354 pcb_to_gcode (x2 - PCB->ExtentMinX + gcode_toolradius),
1355 pcb_to_gcode (y2 - PCB->ExtentMinY + gcode_toolradius),
1356 gc->color->c);
1357 /* printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */
1360 static void
1361 gcode_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1363 if (x1 == x2 && y1 == y2)
1365 Coord w = gc->width / 2;
1366 gcode_fill_rect (gc,
1367 x1 - PCB->ExtentMinX - w, y1 - PCB->ExtentMinX - w,
1368 x1 - PCB->ExtentMinX + w, y1 - PCB->ExtentMinY + w);
1369 return;
1371 use_gc (gc);
1373 gdImageSetThickness (gcode_im, 0);
1374 linewidth = 0;
1375 gdImageLine (gcode_im,
1376 pcb_to_gcode (x1 - PCB->ExtentMinX),
1377 pcb_to_gcode (y1 - PCB->ExtentMinY),
1378 pcb_to_gcode (x2 - PCB->ExtentMinX),
1379 pcb_to_gcode (y2 - PCB->ExtentMinY), gdBrushed);
1382 static void
1383 gcode_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1384 Angle start_angle, Angle delta_angle)
1386 Angle sa, ea;
1389 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
1390 * in pcb, 0 degrees is to the left and +90 degrees is down
1392 start_angle = 180 - start_angle;
1393 delta_angle = -delta_angle;
1394 if (delta_angle > 0)
1396 sa = start_angle;
1397 ea = start_angle + delta_angle;
1399 else
1401 sa = start_angle + delta_angle;
1402 ea = start_angle;
1406 * make sure we start between 0 and 360 otherwise gd does strange
1407 * things
1409 sa = NormalizeAngle (sa);
1410 ea = NormalizeAngle (ea);
1412 #if 0
1413 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
1414 cx, cy, width, height, start_angle, delta_angle, sa, ea);
1415 printf ("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
1416 im, SCALE_X (cx), SCALE_Y (cy),
1417 SCALE (width), SCALE (height), sa, ea, gc->color->c);
1418 #endif
1419 use_gc (gc);
1420 gdImageSetThickness (gcode_im, 0);
1421 linewidth = 0;
1422 gdImageArc (gcode_im,
1423 pcb_to_gcode (cx - PCB->ExtentMinX),
1424 pcb_to_gcode (cy - PCB->ExtentMinY),
1425 pcb_to_gcode (2 * width + gcode_toolradius * 2),
1426 pcb_to_gcode (2 * height + gcode_toolradius * 2), sa, ea,
1427 gdBrushed);
1430 /* given a hole size, return the structure that currently holds the data for
1431 that hole size. If there isn't one, make it */
1432 static int _drill_size_comparator(const void* _size0, const void* _size1)
1434 double size0 = ((const struct single_size_drills*)_size0)->diameter_inches;
1435 double size1 = ((const struct single_size_drills*)_size1)->diameter_inches;
1436 if (size0 == size1)
1437 return 0;
1439 if (size0 < size1)
1440 return -1;
1442 return 1;
1444 static struct single_size_drills*
1445 get_drill(double diameter_inches)
1447 /* see if we already have this size. If so, return that structure */
1448 struct single_size_drills* drill =
1449 bsearch (&diameter_inches,
1450 drills, n_drills, sizeof (drills[0]),
1451 _drill_size_comparator);
1452 if (drill != NULL)
1453 return drill;
1455 /* haven't seen this hole size before, so make a new structure for it */
1456 if (n_drills == n_drills_allocated)
1458 n_drills_allocated += 100;
1459 drills =
1460 (struct single_size_drills *) realloc (drills,
1461 n_drills_allocated *
1462 sizeof (struct single_size_drills));
1465 /* I now add the structure to the list, making sure to keep the list
1466 * sorted. Ideally the bsearch() call above would have given me the location
1467 * to insert this element while keeping things sorted, but it doesn't. For
1468 * simplicity I manually lsearch() to find this location myself */
1470 int i = 0;
1471 for (; i<n_drills; i++)
1472 if (drills[i].diameter_inches >= diameter_inches)
1473 break;
1475 if (n_drills != i)
1476 memmove (&drills[i+1], &drills[i],
1477 (n_drills-i) * sizeof (struct single_size_drills));
1479 drills[i].diameter_inches = diameter_inches;
1480 drills[i].n_holes = 0;
1481 drills[i].n_holes_allocated = 0;
1482 drills[i].holes = NULL;
1483 n_drills++;
1485 return &drills[i];
1489 static void
1490 add_hole (struct single_size_drills* drill,
1491 double cx_inches, double cy_inches)
1493 if (drill->n_holes == drill->n_holes_allocated)
1495 drill->n_holes_allocated += 100;
1496 drill->holes =
1497 (struct drill_hole *) realloc (drill->holes,
1498 drill->n_holes_allocated *
1499 sizeof (struct drill_hole));
1502 drill->holes[ drill->n_holes ].x = cx_inches;
1503 drill->holes[ drill->n_holes ].y = cy_inches;
1504 drill->n_holes++;
1507 static void
1508 gcode_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1510 use_gc (gc);
1512 gdImageSetThickness (gcode_im, 0);
1513 linewidth = 0;
1514 gdImageFilledEllipse (gcode_im,
1515 pcb_to_gcode (cx - PCB->ExtentMinX),
1516 pcb_to_gcode (cy - PCB->ExtentMinY),
1517 pcb_to_gcode (2 * radius + gcode_toolradius * 2),
1518 pcb_to_gcode (2 * radius + gcode_toolradius * 2),
1519 gc->color->c);
1520 if (save_drill && is_drill)
1522 double diameter_inches = COORD_TO_INCH(radius*2);
1524 struct single_size_drills* drill = get_drill (diameter_inches);
1525 add_hole (drill,
1526 /* convert to inch, flip: will drill from bottom side */
1527 COORD_TO_INCH(PCB->ExtentMaxX - cx),
1528 /* PCB reverses y axis */
1529 COORD_TO_INCH(PCB->ExtentMaxY - cy));
1533 static void
1534 gcode_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1536 int i;
1537 gdPoint *points;
1539 points = (gdPoint *) malloc (n_coords * sizeof (gdPoint));
1540 if (points == NULL)
1542 fprintf (stderr, "ERROR: gcode_fill_polygon(): malloc failed\n");
1543 exit (1);
1545 use_gc (gc);
1546 for (i = 0; i < n_coords; i++)
1548 points[i].x = pcb_to_gcode (x[i] - PCB->ExtentMinX);
1549 points[i].y = pcb_to_gcode (y[i] - PCB->ExtentMinY);
1551 gdImageSetThickness (gcode_im, 0);
1552 linewidth = 0;
1553 gdImageFilledPolygon (gcode_im, points, n_coords, gc->color->c);
1554 free (points);
1555 /* printf("FillPoly\n"); */
1558 static void
1559 gcode_calibrate (double xval, double yval)
1561 CRASH;
1564 static void
1565 gcode_set_crosshair (int x, int y, int a)
1569 /* *** Miscellaneous ******************************************************* */
1571 #include "dolists.h"
1573 void
1574 hid_gcode_init ()
1576 memset (&gcode_hid, 0, sizeof (HID));
1578 common_nogui_init (&gcode_hid);
1579 common_draw_helpers_init (&gcode_hid);
1581 gcode_hid.struct_size = sizeof (HID);
1582 gcode_hid.name = "gcode";
1583 gcode_hid.description = "G-CODE export";
1584 gcode_hid.exporter = 1;
1585 gcode_hid.poly_before = 1;
1587 gcode_hid.get_export_options = gcode_get_export_options;
1588 gcode_hid.do_export = gcode_do_export;
1589 gcode_hid.parse_arguments = gcode_parse_arguments;
1590 gcode_hid.set_layer = gcode_set_layer;
1591 gcode_hid.make_gc = gcode_make_gc;
1592 gcode_hid.destroy_gc = gcode_destroy_gc;
1593 gcode_hid.use_mask = gcode_use_mask;
1594 gcode_hid.set_color = gcode_set_color;
1595 gcode_hid.set_line_cap = gcode_set_line_cap;
1596 gcode_hid.set_line_width = gcode_set_line_width;
1597 gcode_hid.set_draw_xor = gcode_set_draw_xor;
1598 gcode_hid.set_draw_faded = gcode_set_draw_faded;
1599 gcode_hid.draw_line = gcode_draw_line;
1600 gcode_hid.draw_arc = gcode_draw_arc;
1601 gcode_hid.draw_rect = gcode_draw_rect;
1602 gcode_hid.fill_circle = gcode_fill_circle;
1603 gcode_hid.fill_polygon = gcode_fill_polygon;
1604 gcode_hid.fill_rect = gcode_fill_rect;
1605 gcode_hid.calibrate = gcode_calibrate;
1606 gcode_hid.set_crosshair = gcode_set_crosshair;
1608 hid_register_hid (&gcode_hid);
1610 #include "gcode_lists.h"