4 * PCB, interactive printed circuit board design
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.
49 #include "error.h" /* Message() */
51 #include "change.h" /* UpdateExtents() */
56 #include "../hidint.h"
58 #include "hid/common/hidnogui.h"
59 #include "hid/common/draw_helpers.h"
62 #include "potracelib.h"
64 #include "decompose.h"
65 #include "pcb-printf.h"
67 #include "hid/common/hidinit.h"
69 #ifdef HAVE_LIBDMALLOC
73 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented GCODE function %s.\n", __FUNCTION__); abort()
79 /* the descriptor used by the gd library */
82 /* so I can figure out what rgb value c refers to */
91 unsigned char r
, g
, b
;
93 struct color_struct
*color
;
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
;
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 */
147 /* structure to represent all holes of a given size */
148 struct single_size_drills
150 double diameter_inches
;
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},
173 {"dpi", "Accuracy of the mill path generation in pixels/inch.",
174 HID_Integer
, 0, 2000, {600, 0, 0}, 0, 0},
177 {"safe-Z", "Safe Z for traverse movements of all operations.",
178 HID_Real
, -1000, 10000, {0, 0, 2}, 0, 0},
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"
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"
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 */
261 gcode_get_filename (char *filename
, const char *layername
)
264 char suffix
[MAXPATHLEN
];
267 pt
= strrchr (gcode_basename
, '.');
268 if (pt
&& pt
> strrchr (gcode_basename
, '/'))
273 strcpy (filename
, gcode_basename
);
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 */
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
++)
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
++)
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
);
313 /* printf("j=%d imin=%d dmin=%f nearest_target=(%f,%f)\n",j,imin,dmin,
314 nearest_target.x,nearest_target.y); */
317 struct drill_hole tmp
;
319 drill
[j
] = drill
[imin
];
323 nearest_target
= drill
[j
];
327 /* *** Main export callback ************************************************ */
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
;
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
;
355 derive_default_filename (PCB
->Filename
,
356 &gcode_attribute_list
[HA_basename
],
357 ".gcode", &last_made_filename
);
363 return gcode_attribute_list
;
366 /* Populates gcode_export_group array */
368 gcode_choose_groups ()
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;
402 gcode_alloc_colors ()
405 * Allocate white and black -- the first color allocated becomes the
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
);
421 gcode_im
= gdImageCreate (pcb_to_gcode (PCB
->ExtentMaxX
- PCB
->ExtentMinX
),
422 pcb_to_gcode (PCB
->ExtentMaxY
- PCB
->ExtentMinY
));
423 gcode_alloc_colors ();
427 gcode_finish_png (const char *layername
)
429 #ifdef HAVE_GDIMAGEPNG
430 char *pngname
, *filename
;
433 pngname
= (char *)malloc (MAXPATHLEN
);
434 gcode_get_filename (pngname
, layername
);
435 filename
= g_strdup_printf ("%s.png", pngname
);
438 file
= fopen (filename
, "wb");
441 gdImagePng (gcode_im
, file
);
444 Message ("GCODE: PNG not supported by gd. Can't write layer mask.\n");
446 gdImageDestroy (gcode_im
);
454 gcode_start_png_export ()
458 region
.X1
= PCB
->ExtentMinX
;
459 region
.Y1
= PCB
->ExtentMinY
;
460 region
.X2
= PCB
->ExtentMaxX
;
461 region
.Y2
= PCB
->ExtentMaxY
;
464 lastbrush
= (gdImagePtr
)((void *) -1);
466 hid_expose_callback (&gcode_hid
, ®ion
, 0);
470 gcode_start_gcode (const char *layername
, bool metric
)
473 char buffer
[MAXPATHLEN
];
476 gcode_get_filename (buffer
, layername
);
477 file
= fopen (buffer
, "wb");
483 fprintf (file
, "(Created by G-code exporter)\n");
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");
490 pcb_fprintf (file
, "(Board size: %.2mm x %.2mm mm)\n",
491 PCB
->ExtentMaxX
- PCB
->ExtentMinX
,
492 PCB
->ExtentMaxY
- PCB
->ExtentMinY
);
494 pcb_fprintf (file
, "(Board size: %.2mi x %.2mi inches)\n",
495 PCB
->ExtentMaxX
- PCB
->ExtentMinX
,
496 PCB
->ExtentMaxY
- PCB
->ExtentMinY
);
502 gcode_do_export (HID_Attr_Val
* options
)
504 int save_ons
[MAX_LAYER
+ 2];
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
= {
513 POTRACE_TURNPOLICY_MINORITY
, /* turnpolicy */
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
);
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
;
542 gcode_basename
= "pcb-out.gcode";
544 gcode_dpi
= options
[HA_dpi
].int_value
;
547 fprintf (stderr
, "ERROR: dpi may not be < 0\n");
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 */
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");
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
);
599 for (i
= 0; i
< MAX_LAYER
; i
++)
601 if (gcode_export_group
[i
])
606 idx
= (i
>= 0 && i
< max_group
) ?
607 PCB
->LayerGroups
.Entries
[i
][0] : i
;
609 (GetLayerGroupNumberByNumber (idx
) ==
610 GetLayerGroupNumberByNumber (solder_silk_layer
)) ? 1 : 0;
611 save_drill
= is_solder
; /* save drills for one layer only */
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
++)
626 v
= /* flip vertically and horizontally */
627 gdImageGetPixel (gcode_im
, gdImageSX (gcode_im
) - 1 - r
,
628 gdImageSY (gcode_im
) - 1 - c
);
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
);
639 { /* flip back layer, used only for PNG output */
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
) -
656 gdImageDestroy (temp_im
);
658 gcode_finish_png (layer_type_to_file_name (idx
, FNS_fixed
));
660 gcode_f
= gcode_start_gcode (layer_type_to_file_name (idx
, FNS_fixed
),
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");
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");
685 fprintf (gcode_f
, "(no predrilling)\n");
686 fprintf (gcode_f
, "(---------------------------------)\n");
688 fprintf (gcode_f
, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
691 fprintf (gcode_f
, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
693 fprintf (gcode_f
, "G0 Z%s\n", variable_safeZ
);
694 /* extract contour points from image */
695 r
= bm_to_pathlist (bm
, &plist
, ¶m_default
);
698 fprintf (stderr
, "ERROR: pathlist function failed\n");
701 /* generate best polygon and write vertices in g-code format */
702 d
= process_path (plist
, ¶m_default
, bm
, gcode_f
,
703 metric
? 25.4 / gcode_dpi
: 1.0 / gcode_dpi
,
704 variable_cutdepth
, variable_safeZ
,
705 variable_isoplunge
, variable_isofeedrate
);
708 fprintf (stderr
, "ERROR: path process function failed\n");
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
)
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
)
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
;
764 drillX
= all_drills
[r
].x
* 25.4;
765 drillY
= all_drills
[r
].y
* 25.4;
769 drillX
= all_drills
[r
].x
;
770 drillY
= all_drills
[r
].y
;
773 fprintf (gcode_f
, "G81 X%f Y%f Z%s R%s\n", drillX
, drillY
,
774 variable_cutdepth
, variable_safeZ
);
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
);
786 fprintf (gcode_f
, "(milling distance %.2fmm = %.2fin)\n", d
,
789 fprintf (gcode_f
, "(milling distance %.2fmm = %.2fin)\n",
792 fprintf (gcode_f
, "M5 M9 M2\n");
794 fprintf (gcode_f
, "M5\nM9\nM2\n");
795 pathlist_free (plist
);
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
)
816 sort_drill (drill
->holes
, drill
->n_holes
);
819 // get the filename with the drill size encoded in it
821 snprintf(layername
, sizeof(layername
),
824 drill
->diameter_inches
* 25.4 :
825 drill
->diameter_inches
);
826 gcode_f
= gcode_start_gcode(layername
, metric
);
830 fprintf (gcode_f
, "(Drill file: %d drills)\n", drill
->n_holes
);
832 fprintf (gcode_f
, "(Drill diameter: %f mm)\n",
833 drill
->diameter_inches
* 25.4);
835 fprintf (gcode_f
, "(Drill diameter: %f inch)\n",
836 drill
->diameter_inches
);
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
);
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
;
860 drillX
= drill
->holes
[r
].x
* 25.4;
861 drillY
= drill
->holes
[r
].y
* 25.4;
865 drillX
= drill
->holes
[r
].x
;
866 drillY
= drill
->holes
[r
].y
;
869 fprintf (gcode_f
, "G81 X%f Y%f Z%s R%s\n", drillX
, drillY
,
870 variable_drilldepth
, variable_safeZ
);
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
);
878 d
+= Distance(drill
->holes
[r
- 1].x
, drill
->holes
[r
- 1].y
,
879 drill
->holes
[r
].x
, drill
->holes
[r
].y
);
882 fprintf (gcode_f
, "M5 M9 M2\n");
884 fprintf (gcode_f
, "M5\nM9\nM2\n");
885 fprintf (gcode_f
, "(end, total distance %.2fmm = %.2fin)\n",
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));
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
);
936 fprintf (gcode_f
, "(Drillmill file)\n");
937 fprintf (gcode_f
, "(Tool diameter: %f %s)\n",
938 gcode_milltoolradius
* 2, metric
? "mm" : "inch");
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",
955 fprintf (gcode_f
, "(---------------------------------)\n");
956 fprintf (gcode_f
, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
959 for (r
= 0; r
< n_drillmill_drills
; r
++)
961 double drillX
, drillY
;
965 drillX
= drillmill_drills
[r
].x
* 25.4;
966 drillY
= drillmill_drills
[r
].y
* 25.4;
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
)
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
));
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
);
1002 fprintf (gcode_f
, "M5 M9 M2\n");
1004 fprintf (gcode_f
, "M5\nM9\nM2\n");
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
);
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)
1041 ... calculate the offset for all lines and polygons of this layer,
1042 mirror it if is_solder, then mill it ...
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
);
1057 fprintf (gcode_f
, "(Outline mill file)\n");
1058 fprintf (gcode_f
, "(Tool diameter: %f %s)\n",
1059 gcode_milltoolradius
* 2, metric
? "mm" : "inch");
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",
1075 fprintf (gcode_f
, "(---------------------------------)\n");
1076 fprintf (gcode_f
, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
1081 upperX
= COORD_TO_MM(PCB
->ExtentMaxX
- PCB
->ExtentMinX
);
1082 upperY
= COORD_TO_MM(PCB
->ExtentMaxY
- PCB
->ExtentMinY
);
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
);
1108 fprintf (gcode_f
, "M5 M9 M2\n");
1110 fprintf (gcode_f
, "M5\nM9\nM2\n");
1111 mill_distance
= abs(gcode_safeZ
- gcode_milldepth
);
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
);
1119 mill_distance
/= 25.4;
1120 fprintf (gcode_f
, "( total distance G1 %.2f mm = %.2f in)\n",
1121 mill_distance
* 25.4, mill_distance
);
1126 setlocale (LC_NUMERIC
, old_locale
); /* restore locale */
1129 /* *** PNG export (slightly modified code from PNG export HID) ************* */
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
;
1139 name
= PCB
->Data
->Layer
[idx
].Name
;
1141 if (strcmp (name
, "invisible") == 0)
1145 is_drill
= (SL_TYPE (idx
) == SL_PDRILL
|| SL_TYPE (idx
) == SL_UDRILL
);
1146 is_mask
= (SL_TYPE (idx
) == SL_MASK
);
1150 /* Don't print masks */
1156 * Print 'holes', so that we can fill gaps in the copper
1161 if (group
== gcode_cur_group
)
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
;
1175 rv
->color
= (struct color_struct
*) malloc (sizeof (*rv
->color
));
1176 rv
->color
->r
= rv
->color
->g
= rv
->color
->b
= 0;
1182 gcode_destroy_gc (hidGC gc
)
1188 gcode_use_mask (enum mask_mode mode
)
1194 gcode_set_color (hidGC gc
, const char *name
)
1196 if (gcode_im
== NULL
)
1204 if (!strcmp (name
, "drill"))
1210 if (!strcmp (name
, "erase"))
1212 /* FIXME -- should be background, not white */
1223 gcode_set_line_cap (hidGC gc
, EndCapStyle style
)
1229 gcode_set_line_width (hidGC gc
, Coord width
)
1235 gcode_set_draw_xor (hidGC gc
, int xor_
)
1241 gcode_set_draw_faded (hidGC gc
, int faded
)
1250 if (gc
->me_pointer
!= &gcode_hid
)
1252 fprintf (stderr
, "Fatal: GC from another HID passed to gcode HID\n");
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);
1263 gdImageSetThickness (gcode_im
,
1264 pcb_to_gcode (gc
->width
+ 2 * gcode_toolradius
));
1265 linewidth
= gc
->width
;
1268 if (lastbrush
!= gc
->brush
|| need_brush
)
1270 static void *bcache
= 0;
1281 r
= pcb_to_gcode (gc
->width
/ 2 + gcode_toolradius
);
1285 r
= pcb_to_gcode (gc
->width
+ gcode_toolradius
* 2);
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
;
1300 gc
->brush
= gdImageCreate (2 * r
+ 1, 2 * r
+ 1);
1302 gc
->brush
= gdImageCreate (r
+ 1, r
+ 1);
1303 bg
= gdImageColorAllocate (gc
->brush
, 255, 255, 255);
1305 gdImageColorAllocate (gc
->brush
, gc
->color
->r
, gc
->color
->g
,
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.
1314 gdImageFilledRectangle (gc
->brush
, 0, 0, 0, 0, fg
);
1318 gdImageFilledEllipse (gc
->brush
, r
, r
, 2 * r
, 2 * r
, fg
);
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
;
1333 gcode_draw_rect (hidGC gc
, Coord x1
, Coord y1
, Coord x2
, Coord y2
)
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
),
1342 /* printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */
1346 gcode_fill_rect (hidGC gc
, Coord x1
, Coord y1
, Coord x2
, Coord y2
)
1349 gdImageSetThickness (gcode_im
, 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
),
1357 /* printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */
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
);
1373 gdImageSetThickness (gcode_im
, 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
);
1383 gcode_draw_arc (hidGC gc
, Coord cx
, Coord cy
, Coord width
, Coord height
,
1384 Angle start_angle
, Angle delta_angle
)
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)
1397 ea
= start_angle
+ delta_angle
;
1401 sa
= start_angle
+ delta_angle
;
1406 * make sure we start between 0 and 360 otherwise gd does strange
1409 sa
= NormalizeAngle (sa
);
1410 ea
= NormalizeAngle (ea
);
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
);
1420 gdImageSetThickness (gcode_im
, 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
,
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
;
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
);
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;
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 */
1471 for (; i
<n_drills
; i
++)
1472 if (drills
[i
].diameter_inches
>= diameter_inches
)
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
;
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;
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
;
1508 gcode_fill_circle (hidGC gc
, Coord cx
, Coord cy
, Coord radius
)
1512 gdImageSetThickness (gcode_im
, 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),
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
);
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
));
1534 gcode_fill_polygon (hidGC gc
, int n_coords
, Coord
*x
, Coord
*y
)
1539 points
= (gdPoint
*) malloc (n_coords
* sizeof (gdPoint
));
1542 fprintf (stderr
, "ERROR: gcode_fill_polygon(): malloc failed\n");
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);
1553 gdImageFilledPolygon (gcode_im
, points
, n_coords
, gc
->color
->c
);
1555 /* printf("FillPoly\n"); */
1559 gcode_calibrate (double xval
, double yval
)
1565 gcode_set_crosshair (int x
, int y
, int a
)
1569 /* *** Miscellaneous ******************************************************* */
1571 #include "dolists.h"
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"