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()
76 static HID_DRAW_API gcode_graphics
;
80 /* the descriptor used by the gd library */
83 /* so I can figure out what rgb value c refers to */
92 unsigned char r
, g
, b
;
94 struct color_struct
*color
;
98 static struct color_struct
*black
= NULL
, *white
= NULL
;
99 static int linewidth
= -1;
100 static gdImagePtr lastbrush
= (gdImagePtr
)((void *) -1);
102 /* gd image and file for PNG export */
103 static gdImagePtr gcode_im
= NULL
;
104 static FILE *gcode_f
= NULL
;
108 static int is_solder
;
111 * Which groups of layers to export into PNG layer masks. 1 means export, 0
112 * means do not export.
114 static int gcode_export_group
[MAX_LAYER
];
116 /* Group that is currently exported. */
117 static int gcode_cur_group
;
119 /* Filename prefix and suffix that will be used when saving files. */
120 static const char *gcode_basename
= NULL
;
122 /* Horizontal DPI (grid points per inch) */
123 static int gcode_dpi
= -1;
125 static double gcode_cutdepth
= 0; /* milling depth (inch) */
126 static double gcode_isoplunge
= 0; /* isolation milling plunge feedrate */
127 static double gcode_isofeedrate
= 0; /* isolation milling feedrate */
128 static char gcode_predrill
;
129 static double gcode_drilldepth
= 0; /* drilling depth (mm or in) */
130 static double gcode_drillfeedrate
= 0; /* drilling feedrate */
131 static double gcode_safeZ
= 100; /* safe Z (mm or in) */
132 static int gcode_toolradius
= 0; /* iso-mill tool radius (1/100 mil) */
133 static char gcode_drillmill
= 0; /* wether to drill with the mill tool */
134 static double gcode_milldepth
= 0; /* outline milling depth (mm or in) */
135 static double gcode_milltoolradius
= 0; /* outline-mill tool radius (mm or in)*/
136 static double gcode_millplunge
= 0; /* outline-milling plunge feedrate */
137 static double gcode_millfeedrate
= 0; /* outline-milling feedrate */
138 static char gcode_advanced
= 0;
139 static int save_drill
= 0;
141 /* structure to represent a single hole */
148 /* structure to represent all holes of a given size */
149 struct single_size_drills
151 double diameter_inches
;
154 int n_holes_allocated
;
155 struct drill_hole
* holes
;
158 /* at the start we have no drills at all */
159 static struct single_size_drills
* drills
= NULL
;
160 static int n_drills
= 0;
161 static int n_drills_allocated
= 0;
163 HID_Attribute gcode_attribute_list
[] = {
164 /* other HIDs expect this to be first. */
165 {"basename", "File name prefix and suffix,\n"
166 "layer names will be inserted before the suffix.",
167 HID_String
, 0, 0, {0, 0, 0}, 0, 0},
168 #define HA_basename 0
170 {"measurement-unit", "Measurement unit used in the G-code output.",
171 HID_Unit
, 0, 0, {3, 0, 0}, NULL
, 0},
174 {"dpi", "Accuracy of the mill path generation in pixels/inch.",
175 HID_Integer
, 0, 2000, {600, 0, 0}, 0, 0},
178 {"safe-Z", "Safe Z for traverse movements of all operations.",
179 HID_Real
, -1000, 10000, {0, 0, 2}, 0, 0},
182 {"iso-mill-depth", "Isolation milling depth.",
183 HID_Real
, -1000, 1000, {0, 0, -0.05}, 0, 0},
184 #define HA_cutdepth 4
186 {"iso-tool-diameter", "Isolation milling tool diameter.",
187 HID_Real
, 0, 10000, {0, 0, 0.2}, 0, 0},
188 #define HA_tooldiameter 5
190 {"iso-tool-plunge", "Isolation milling feedrate when plunging into\n"
192 HID_Real
, 0.1, 10000, {0, 0, 25.}, 0, 0},
193 #define HA_isoplunge 6
195 {"iso-tool-feedrate", "Isolation milling feedrate.",
196 HID_Real
, 0.1, 10000, {0, 0, 50.}, 0, 0},
197 #define HA_isofeedrate 7
199 {"predrill", "Wether to pre-drill all drill spots with the isolation milling\n"
200 "tool. Drill depth is iso-mill-depth here. This feature eases\n"
201 "and enhances accuracy of manual drilling.",
202 HID_Boolean
, 0, 0, {1, 0, 0}, 0, 0},
203 #define HA_predrill 8
205 {"drill-depth", "Drilling depth.",
206 HID_Real
, -10000, 10000, {0, 0, -2}, 0, 0},
207 #define HA_drilldepth 9
209 {"drill-feedrate", "Drilling feedrate.",
210 HID_Real
, 0.1, 10000, {0, 0, 50.}, 0, 0},
211 #define HA_drillfeedrate 10
213 {"drill-mill", "Wether to produce drill holes equal or bigger than the\n"
214 "milling tool diameter with the milling tool.\n"
215 "With the milling tool bigger holes can be accurately sized\n"
216 "without changing the tool",
217 HID_Boolean
, 0, 0, {1, 0, 0}, 0, 0},
218 #define HA_drillmill 11
220 {"outline-mill-depth", "Milling depth when milling the outline.\n"
221 "Currently, only the rectangular extents of the\n"
222 "board are milled, no polygonal outlines or holes.",
223 HID_Real
, -10000, 10000, {0, 0, -1}, 0, 0},
224 #define HA_milldepth 12
226 {"outline-tool-diameter", "Diameter of the tool used for outline milling.",
227 HID_Real
, 0, 10000, {0, 0, 1}, 0, 0},
228 #define HA_milltooldiameter 13
230 {"outline-mill-plunge", "Outline milling feedrate when plunging into\n"
232 HID_Real
, 0.1, 10000, {0, 0, 25.}, 0, 0},
233 #define HA_millplunge 14
235 {"outline-mill-feedrate", "Outline milling feedrate",
236 HID_Real
, 0.1, 10000, {0, 0, 50.}, 0, 0},
237 #define HA_millfeedrate 15
239 {"advanced-gcode", "Wether to produce G-code for advanced interpreters,\n"
240 "like using variables or drill cycles. Not all\n"
241 "machine controllers understand this, but it allows\n"
242 "better hand-editing of the resulting files.",
243 HID_Boolean
, 0, 0, {-1, 0, 0}, 0, 0},
244 #define HA_advanced 16
247 #define NUM_OPTIONS (sizeof(gcode_attribute_list)/sizeof(gcode_attribute_list[0]))
249 REGISTER_ATTRIBUTES (gcode_attribute_list
)
250 static HID_Attr_Val gcode_values
[NUM_OPTIONS
];
252 /* *** Utility funcions **************************************************** */
254 /* convert from default PCB units to gcode units */
255 static int pcb_to_gcode (int pcb
)
257 return round(COORD_TO_INCH(pcb
) * gcode_dpi
);
260 /* Fits the given layer name into basename, just before the suffix */
262 gcode_get_filename (char *filename
, const char *layername
)
265 char suffix
[MAXPATHLEN
];
268 pt
= strrchr (gcode_basename
, '.');
269 if (pt
&& pt
> strrchr (gcode_basename
, '/'))
274 strcpy (filename
, gcode_basename
);
276 *(filename
+ (pt
- gcode_basename
)) = '\0';
277 strcat (filename
, "-");
278 strcat (filename
, layername
);
279 strcat (filename
, suffix
);
281 // result is in char *filename
284 /* Sorts drills to produce a short tool path. I start with the hole nearest
285 * (0,0) and for each subsequent one, find the hole nearest to the previous.
286 * This isn't guaranteed to find the shortest path, but should be good enough.
287 * Note that this is O(N^2). We can't use the O(N logN) sort, since our
288 * shortest-distance origin changes with every point */
290 sort_drill (struct drill_hole
*drill
, int n_drill
)
292 /* I start out by looking for points closest to (0,0) */
293 struct drill_hole nearest_target
= { 0, 0 };
295 /* I sort my list by finding the correct point to fill each slot. I don't need
296 to look at the last one, since it'll be in the right place automatically */
297 for (int j
= 0; j
< n_drill
-1; j
++)
301 /* look through remaining elements to find the next drill point. This is
302 the one nearest to nearest_target */
303 for (int i
= j
; i
< n_drill
; i
++)
306 (drill
[i
].x
- nearest_target
.x
) * (drill
[i
].x
- nearest_target
.x
) +
307 (drill
[i
].y
- nearest_target
.y
) * (drill
[i
].y
- nearest_target
.y
);
314 /* printf("j=%d imin=%d dmin=%f nearest_target=(%f,%f)\n",j,imin,dmin,
315 nearest_target.x,nearest_target.y); */
318 struct drill_hole tmp
;
320 drill
[j
] = drill
[imin
];
324 nearest_target
= drill
[j
];
328 /* *** Main export callback ************************************************ */
331 gcode_parse_arguments (int *argc
, char ***argv
)
333 hid_register_attributes (gcode_attribute_list
,
334 sizeof (gcode_attribute_list
) /
335 sizeof (gcode_attribute_list
[0]));
336 hid_parse_command_line (argc
, argv
);
339 static HID_Attribute
*
340 gcode_get_export_options (int *n
)
342 static char *last_made_filename
= 0;
343 static int last_unit_value
= -1;
345 if (gcode_attribute_list
[HA_unit
].default_val
.int_value
== last_unit_value
)
347 if (Settings
.grid_unit
)
348 gcode_attribute_list
[HA_unit
].default_val
.int_value
= Settings
.grid_unit
->index
;
350 gcode_attribute_list
[HA_unit
].default_val
.int_value
= get_unit_struct ("mil")->index
;
351 last_unit_value
= gcode_attribute_list
[HA_unit
].default_val
.int_value
;
356 derive_default_filename (PCB
->Filename
,
357 &gcode_attribute_list
[HA_basename
],
358 ".gcode", &last_made_filename
);
364 return gcode_attribute_list
;
367 /* Populates gcode_export_group array */
369 gcode_choose_groups ()
374 /* Set entire array to 0 (don't export any layer groups by default */
375 memset (gcode_export_group
, 0, sizeof (gcode_export_group
));
377 for (n
= 0; n
< max_copper_layer
; n
++)
379 layer
= &PCB
->Data
->Layer
[n
];
381 if (layer
->LineN
|| layer
->TextN
|| layer
->ArcN
|| layer
->PolygonN
)
383 /* layer isn't empty */
386 * is this check necessary? It seems that special
387 * layers have negative indexes?
390 if (SL_TYPE (n
) == 0)
392 /* layer is a copper layer */
393 m
= GetLayerGroupNumberByNumber (n
);
395 /* the export layer */
396 gcode_export_group
[m
] = 1;
403 gcode_alloc_colors ()
406 * Allocate white and black -- the first color allocated becomes the
410 white
= (struct color_struct
*) malloc (sizeof (*white
));
411 white
->r
= white
->g
= white
->b
= 255;
412 white
->c
= gdImageColorAllocate (gcode_im
, white
->r
, white
->g
, white
->b
);
414 black
= (struct color_struct
*) malloc (sizeof (*black
));
415 black
->r
= black
->g
= black
->b
= 0;
416 black
->c
= gdImageColorAllocate (gcode_im
, black
->r
, black
->g
, black
->b
);
422 gcode_im
= gdImageCreate (pcb_to_gcode (PCB
->ExtentMaxX
- PCB
->ExtentMinX
),
423 pcb_to_gcode (PCB
->ExtentMaxY
- PCB
->ExtentMinY
));
424 gcode_alloc_colors ();
428 gcode_finish_png (const char *layername
)
430 #ifdef HAVE_GDIMAGEPNG
431 char *pngname
, *filename
;
434 pngname
= (char *)malloc (MAXPATHLEN
);
435 gcode_get_filename (pngname
, layername
);
436 filename
= g_strdup_printf ("%s.png", pngname
);
439 file
= fopen (filename
, "wb");
442 gdImagePng (gcode_im
, file
);
445 Message ("GCODE: PNG not supported by gd. Can't write layer mask.\n");
447 gdImageDestroy (gcode_im
);
455 gcode_start_png_export ()
459 region
.X1
= PCB
->ExtentMinX
;
460 region
.Y1
= PCB
->ExtentMinY
;
461 region
.X2
= PCB
->ExtentMaxX
;
462 region
.Y2
= PCB
->ExtentMaxY
;
465 lastbrush
= (gdImagePtr
)((void *) -1);
467 hid_expose_callback (&gcode_hid
, ®ion
, 0);
471 gcode_start_gcode (const char *layername
, bool metric
)
474 char buffer
[MAXPATHLEN
];
477 gcode_get_filename (buffer
, layername
);
478 file
= fopen (buffer
, "wb");
484 fprintf (file
, "(Created by G-code exporter)\n");
486 snprintf (buffer
, sizeof(buffer
), "%s", ctime (&t
));
487 buffer
[strlen (buffer
) - 1] = '\0'; // drop the newline
488 fprintf (file
, "(%s)\n", buffer
);
489 fprintf (file
, "(Units: %s)\n", metric
? "mm" : "inch");
491 pcb_fprintf (file
, "(Board size: %.2mm x %.2mm mm)\n",
492 PCB
->ExtentMaxX
- PCB
->ExtentMinX
,
493 PCB
->ExtentMaxY
- PCB
->ExtentMinY
);
495 pcb_fprintf (file
, "(Board size: %.2mi x %.2mi inches)\n",
496 PCB
->ExtentMaxX
- PCB
->ExtentMinX
,
497 PCB
->ExtentMaxY
- PCB
->ExtentMinY
);
503 gcode_do_export (HID_Attr_Val
* options
)
505 int save_ons
[MAX_LAYER
+ 2];
508 double scale
= 0, d
= 0;
509 int r
, c
, v
, p
, metric
;
510 path_t
*plist
= NULL
;
511 potrace_bitmap_t
*bm
= NULL
;
512 potrace_param_t param_default
= {
514 POTRACE_TURNPOLICY_MINORITY
, /* turnpolicy */
517 0.2, /* opttolerance */
519 NULL
, /* callback function */
520 NULL
, /* callback data */
521 0.0, 1.0, /* progress range */
522 0.0, /* granularity */
525 char variable_safeZ
[20], variable_cutdepth
[20];
526 char variable_isoplunge
[20], variable_isofeedrate
[20];
527 char variable_drilldepth
[20], variable_milldepth
[20];
528 char variable_millplunge
[20], variable_millfeedrate
[20];
529 char *old_locale
= setlocale (LC_NUMERIC
, NULL
);
533 gcode_get_export_options (0);
534 for (i
= 0; i
< NUM_OPTIONS
; i
++)
536 gcode_values
[i
] = gcode_attribute_list
[i
].default_val
;
538 options
= gcode_values
;
540 gcode_basename
= options
[HA_basename
].str_value
;
543 gcode_basename
= "pcb-out.gcode";
545 gcode_dpi
= options
[HA_dpi
].int_value
;
548 fprintf (stderr
, "ERROR: dpi may not be < 0\n");
551 unit
= &(get_unit_list() [options
[HA_unit
].int_value
]);
552 metric
= (unit
->family
== METRIC
);
553 scale
= metric
? 1.0 / coord_to_unit (unit
, MM_TO_COORD (1.0))
554 : 1.0 / coord_to_unit (unit
, INCH_TO_COORD (1.0));
556 gcode_cutdepth
= options
[HA_cutdepth
].real_value
* scale
;
557 gcode_isoplunge
= options
[HA_isoplunge
].real_value
* scale
;
558 gcode_isofeedrate
= options
[HA_isofeedrate
].real_value
* scale
;
559 gcode_predrill
= options
[HA_predrill
].int_value
;
560 gcode_drilldepth
= options
[HA_drilldepth
].real_value
* scale
;
561 gcode_drillfeedrate
= options
[HA_drillfeedrate
].real_value
* scale
;
562 gcode_safeZ
= options
[HA_safeZ
].real_value
* scale
;
563 gcode_toolradius
= metric
564 ? MM_TO_COORD(options
[HA_tooldiameter
].real_value
/ 2 * scale
)
565 : INCH_TO_COORD(options
[HA_tooldiameter
].real_value
/ 2 * scale
);
566 gcode_drillmill
= options
[HA_drillmill
].int_value
;
567 gcode_milldepth
= options
[HA_milldepth
].real_value
* scale
;
568 gcode_milltoolradius
= options
[HA_milltooldiameter
].real_value
/ 2 * scale
;
569 gcode_millplunge
= options
[HA_millplunge
].real_value
* scale
;
570 gcode_millfeedrate
= options
[HA_millfeedrate
].real_value
* scale
;
571 gcode_advanced
= options
[HA_advanced
].int_value
;
572 gcode_choose_groups ();
573 setlocale (LC_NUMERIC
, "C"); /* use . as separator */
576 /* give each variable distinct names, even if they don't appear
577 together in a file. This allows to join output files */
578 strcpy (variable_safeZ
, "#100");
579 strcpy (variable_cutdepth
, "#101");
580 strcpy (variable_isoplunge
, "#102");
581 strcpy (variable_isofeedrate
, "#103");
582 strcpy (variable_drilldepth
, "#104");
583 strcpy (variable_milldepth
, "#105");
584 strcpy (variable_millplunge
, "#106");
585 strcpy (variable_millfeedrate
, "#107");
589 snprintf (variable_safeZ
, 20, "%f", gcode_safeZ
);
590 snprintf (variable_cutdepth
, 20, "%f", gcode_cutdepth
);
591 snprintf (variable_isoplunge
, 20, "%f", gcode_isoplunge
);
592 snprintf (variable_isofeedrate
, 20, "%f", gcode_isofeedrate
);
593 snprintf (variable_drilldepth
, 20, "%f", gcode_drilldepth
);
594 snprintf (variable_milldepth
, 20, "%f", gcode_milldepth
);
595 snprintf (variable_millplunge
, 20, "%f", gcode_millplunge
);
596 snprintf (variable_millfeedrate
, 20, "%f", gcode_millfeedrate
);
600 for (i
= 0; i
< MAX_LAYER
; i
++)
602 if (gcode_export_group
[i
])
607 idx
= (i
>= 0 && i
< max_group
) ?
608 PCB
->LayerGroups
.Entries
[i
][0] : i
;
610 (GetLayerGroupNumberByNumber (idx
) ==
611 GetLayerGroupNumberByNumber (solder_silk_layer
)) ? 1 : 0;
612 save_drill
= is_solder
; /* save drills for one layer only */
614 hid_save_and_show_layer_ons (save_ons
);
615 gcode_start_png_export ();
616 hid_restore_layer_ons (save_ons
);
618 /* ***************** gcode conversion *************************** */
619 /* potrace uses a different kind of bitmap; for simplicity gcode_im is
620 copied to this format and flipped as needed along the way */
621 bm
= bm_new (gdImageSX (gcode_im
), gdImageSY (gcode_im
));
622 for (r
= 0; r
< gdImageSX (gcode_im
); r
++)
624 for (c
= 0; c
< gdImageSY (gcode_im
); c
++)
627 v
= /* flip vertically and horizontally */
628 gdImageGetPixel (gcode_im
, gdImageSX (gcode_im
) - 1 - r
,
629 gdImageSY (gcode_im
) - 1 - c
);
631 v
= /* flip only vertically */
632 gdImageGetPixel (gcode_im
, r
,
633 gdImageSY (gcode_im
) - 1 - c
);
634 p
= (gcode_im
->red
[v
] || gcode_im
->green
[v
]
635 || gcode_im
->blue
[v
]) ? 0 : 0xFFFFFF;
636 BM_PUT (bm
, r
, c
, p
);
640 { /* flip back layer, used only for PNG output */
642 gdImageCreate (gdImageSX (gcode_im
), gdImageSY (gcode_im
));
643 gdImageColorAllocate (temp_im
, white
->r
, white
->g
, white
->b
);
644 gdImageColorAllocate (temp_im
, black
->r
, black
->g
, black
->b
);
645 gdImageCopy (temp_im
, gcode_im
, 0, 0, 0, 0,
646 gdImageSX (gcode_im
), gdImageSY (gcode_im
));
647 for (r
= 0; r
< gdImageSX (gcode_im
); r
++)
649 for (c
= 0; c
< gdImageSY (gcode_im
); c
++)
651 gdImageSetPixel (gcode_im
, r
, c
,
652 gdImageGetPixel (temp_im
,
653 gdImageSX (gcode_im
) -
657 gdImageDestroy (temp_im
);
659 gcode_finish_png (layer_type_to_file_name (idx
, FNS_fixed
));
661 gcode_f
= gcode_start_gcode (layer_type_to_file_name (idx
, FNS_fixed
),
668 fprintf (gcode_f
, "(Accuracy %d dpi)\n", gcode_dpi
);
669 fprintf (gcode_f
, "(Tool diameter: %f %s)\n",
670 options
[HA_tooldiameter
].real_value
* scale
,
671 metric
? "mm" : "inch");
674 fprintf (gcode_f
, "%s=%f (safe Z)\n",
675 variable_safeZ
, gcode_safeZ
);
676 fprintf (gcode_f
, "%s=%f (cutting depth)\n",
677 variable_cutdepth
, gcode_cutdepth
);
678 fprintf (gcode_f
, "%s=%f (plunge feedrate)\n",
679 variable_isoplunge
, gcode_isoplunge
);
680 fprintf (gcode_f
, "%s=%f (feedrate)\n",
681 variable_isofeedrate
, gcode_isofeedrate
);
683 if (gcode_predrill
&& save_drill
)
684 fprintf (gcode_f
, "(with predrilling)\n");
686 fprintf (gcode_f
, "(no predrilling)\n");
687 fprintf (gcode_f
, "(---------------------------------)\n");
689 fprintf (gcode_f
, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
692 fprintf (gcode_f
, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
694 fprintf (gcode_f
, "G0 Z%s\n", variable_safeZ
);
695 /* extract contour points from image */
696 r
= bm_to_pathlist (bm
, &plist
, ¶m_default
);
699 fprintf (stderr
, "ERROR: pathlist function failed\n");
702 /* generate best polygon and write vertices in g-code format */
703 d
= process_path (plist
, ¶m_default
, bm
, gcode_f
,
704 metric
? 25.4 / gcode_dpi
: 1.0 / gcode_dpi
,
705 variable_cutdepth
, variable_safeZ
,
706 variable_isoplunge
, variable_isofeedrate
);
709 fprintf (stderr
, "ERROR: path process function failed\n");
712 if (gcode_predrill
&& save_drill
)
714 int n_all_drills
= 0;
715 struct drill_hole
* all_drills
= NULL
;
716 /* count all drills to be predrilled */
717 for (int i_drill_sets
= 0; i_drill_sets
< n_drills
; i_drill_sets
++)
719 struct single_size_drills
* drill_set
= &drills
[i_drill_sets
];
721 /* don't predrill drillmill holes */
722 if (gcode_drillmill
) {
723 double radius
= metric
?
724 drill_set
->diameter_inches
* 25.4 / 2:
725 drill_set
->diameter_inches
/ 2;
727 if (gcode_milltoolradius
< radius
)
731 n_all_drills
+= drill_set
->n_holes
;
733 /* for sorting regardless of size, copy all drills to be
734 predrilled into one new structure */
735 all_drills
= (struct drill_hole
*)
736 malloc (n_all_drills
* sizeof (struct drill_hole
));
737 for (int i_drill_sets
= 0; i_drill_sets
< n_drills
; i_drill_sets
++)
739 struct single_size_drills
* drill_set
= &drills
[i_drill_sets
];
741 /* don't predrill drillmill holes */
742 if (gcode_drillmill
) {
743 double radius
= metric
?
744 drill_set
->diameter_inches
* 25.4 / 2:
745 drill_set
->diameter_inches
/ 2;
747 if (gcode_milltoolradius
< radius
)
751 memcpy(&all_drills
[r
], drill_set
->holes
,
752 drill_set
->n_holes
* sizeof(struct drill_hole
));
754 sort_drill(all_drills
, n_all_drills
);
755 /* write that (almost the same code as writing the drill file) */
756 fprintf (gcode_f
, "(predrilling)\n");
757 fprintf (gcode_f
, "F%s\n", variable_isoplunge
);
759 for (r
= 0; r
< n_all_drills
; r
++)
761 double drillX
, drillY
;
765 drillX
= all_drills
[r
].x
* 25.4;
766 drillY
= all_drills
[r
].y
* 25.4;
770 drillX
= all_drills
[r
].x
;
771 drillY
= all_drills
[r
].y
;
774 fprintf (gcode_f
, "G81 X%f Y%f Z%s R%s\n", drillX
, drillY
,
775 variable_cutdepth
, variable_safeZ
);
778 fprintf (gcode_f
, "G0 X%f Y%f\n", drillX
, drillY
);
779 fprintf (gcode_f
, "G1 Z%s\n", variable_cutdepth
);
780 fprintf (gcode_f
, "G0 Z%s\n", variable_safeZ
);
783 fprintf (gcode_f
, "(%d predrills)\n", n_all_drills
);
787 fprintf (gcode_f
, "(milling distance %.2fmm = %.2fin)\n", d
,
790 fprintf (gcode_f
, "(milling distance %.2fmm = %.2fin)\n",
793 fprintf (gcode_f
, "M5 M9 M2\n");
795 fprintf (gcode_f
, "M5\nM9\nM2\n");
796 pathlist_free (plist
);
802 for (int i_drill_file
=0; i_drill_file
< n_drills
; i_drill_file
++)
804 struct single_size_drills
* drill
= &drills
[i_drill_file
];
806 /* don't drill drillmill holes */
807 if (gcode_drillmill
) {
808 double radius
= metric
?
809 drill
->diameter_inches
* 25.4 / 2:
810 drill
->diameter_inches
/ 2;
812 if (gcode_milltoolradius
< radius
)
817 sort_drill (drill
->holes
, drill
->n_holes
);
820 // get the filename with the drill size encoded in it
822 snprintf(layername
, sizeof(layername
),
825 drill
->diameter_inches
* 25.4 :
826 drill
->diameter_inches
);
827 gcode_f
= gcode_start_gcode(layername
, metric
);
831 fprintf (gcode_f
, "(Drill file: %d drills)\n", drill
->n_holes
);
833 fprintf (gcode_f
, "(Drill diameter: %f mm)\n",
834 drill
->diameter_inches
* 25.4);
836 fprintf (gcode_f
, "(Drill diameter: %f inch)\n",
837 drill
->diameter_inches
);
840 fprintf (gcode_f
, "%s=%f (safe Z)\n",
841 variable_safeZ
, gcode_safeZ
);
842 fprintf (gcode_f
, "%s=%f (drill depth)\n",
843 variable_drilldepth
, gcode_drilldepth
);
844 fprintf (gcode_f
, "(---------------------------------)\n");
845 fprintf (gcode_f
, "G17 G%d G90 G64 P0.003 M3 S3000 M7 F%f\n",
846 metric
? 21 : 20, gcode_drillfeedrate
);
850 fprintf (gcode_f
, "(---------------------------------)\n");
851 fprintf (gcode_f
, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\nF%f\n",
852 metric
? 21 : 20, gcode_drillfeedrate
);
854 fprintf (gcode_f
, "G0 Z%s\n", variable_safeZ
);
855 for (r
= 0; r
< drill
->n_holes
; r
++)
857 double drillX
, drillY
;
861 drillX
= drill
->holes
[r
].x
* 25.4;
862 drillY
= drill
->holes
[r
].y
* 25.4;
866 drillX
= drill
->holes
[r
].x
;
867 drillY
= drill
->holes
[r
].y
;
870 fprintf (gcode_f
, "G81 X%f Y%f Z%s R%s\n", drillX
, drillY
,
871 variable_drilldepth
, variable_safeZ
);
874 fprintf (gcode_f
, "G0 X%f Y%f\n", drillX
, drillY
);
875 fprintf (gcode_f
, "G1 Z%s\n", variable_drilldepth
);
876 fprintf (gcode_f
, "G0 Z%s\n", variable_safeZ
);
879 d
+= Distance(drill
->holes
[r
- 1].x
, drill
->holes
[r
- 1].y
,
880 drill
->holes
[r
].x
, drill
->holes
[r
].y
);
883 fprintf (gcode_f
, "M5 M9 M2\n");
885 fprintf (gcode_f
, "M5\nM9\nM2\n");
886 fprintf (gcode_f
, "(end, total distance %.2fmm = %.2fin)\n",
891 /* ******************* handle drill-milling **************************** */
892 if (save_drill
&& gcode_drillmill
)
894 int n_drillmill_drills
= 0;
895 struct drill_hole
* drillmill_drills
= NULL
;
896 double* drillmill_radiuss
= NULL
;
897 double mill_radius
, inaccuracy
;
899 /* count drillmill drills */
900 for (int i_drill_sets
= 0; i_drill_sets
< n_drills
; i_drill_sets
++)
902 struct single_size_drills
* drill_set
= &drills
[i_drill_sets
];
903 double radius
= metric
?
904 drill_set
->diameter_inches
* 25.4 / 2:
905 drill_set
->diameter_inches
/ 2;
907 if (gcode_milltoolradius
>= radius
)
908 n_drillmill_drills
+= drill_set
->n_holes
;
910 /* for sorting regardless of size, copy all available drills
911 into one new structure */
912 drillmill_drills
= (struct drill_hole
*)
913 malloc (n_drillmill_drills
* sizeof (struct drill_hole
));
914 drillmill_radiuss
= (double *)
915 malloc (n_drillmill_drills
* sizeof (double));
917 for (int i_drill_sets
= 0; i_drill_sets
< n_drills
; i_drill_sets
++)
919 struct single_size_drills
* drill_set
= &drills
[i_drill_sets
];
920 double radius
= metric
?
921 drill_set
->diameter_inches
* 25.4 / 2:
922 drill_set
->diameter_inches
/ 2;
924 if (gcode_milltoolradius
>= radius
)
926 memcpy(&drillmill_drills
[r
], drill_set
->holes
,
927 drill_set
->n_holes
* sizeof(struct drill_hole
));
928 drillmill_radiuss
[r
] = radius
;
929 r
+= drill_set
->n_holes
;
932 sort_drill(drillmill_drills
, n_drillmill_drills
);
934 gcode_f
= gcode_start_gcode("drillmill", metric
);
937 fprintf (gcode_f
, "(Drillmill file)\n");
938 fprintf (gcode_f
, "(Tool diameter: %f %s)\n",
939 gcode_milltoolradius
* 2, metric
? "mm" : "inch");
942 fprintf (gcode_f
, "%s=%f (safe Z)\n",
943 variable_safeZ
, gcode_safeZ
);
944 fprintf (gcode_f
, "%s=%f (mill depth)\n",
945 variable_milldepth
, gcode_milldepth
);
946 fprintf (gcode_f
, "%s=%f (mill plunge feedrate)\n",
947 variable_millplunge
, gcode_millplunge
);
948 fprintf (gcode_f
, "%s=%f (mill feedrate)\n",
949 variable_millfeedrate
, gcode_millfeedrate
);
950 fprintf (gcode_f
, "(---------------------------------)\n");
951 fprintf (gcode_f
, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
956 fprintf (gcode_f
, "(---------------------------------)\n");
957 fprintf (gcode_f
, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
960 for (r
= 0; r
< n_drillmill_drills
; r
++)
962 double drillX
, drillY
;
966 drillX
= drillmill_drills
[r
].x
* 25.4;
967 drillY
= drillmill_drills
[r
].y
* 25.4;
971 drillX
= drillmill_drills
[r
].x
;
972 drillY
= drillmill_drills
[r
].y
;
974 fprintf (gcode_f
, "G0 X%f Y%f\n", drillX
, drillY
);
975 fprintf (gcode_f
, "G1 Z%s F%s\n",
976 variable_milldepth
, variable_millplunge
);
978 mill_radius
= drillmill_radiuss
[r
] - gcode_milltoolradius
;
979 inaccuracy
= (metric
? 25.4 : 1.) / gcode_dpi
;
980 if (mill_radius
> inaccuracy
)
984 /* calculate how many polygon sides we need to stay
985 within our accuracy while avoiding a G02/G03 */
986 n_sides
= M_PI
/ acos (mill_radius
/
987 (mill_radius
+ inaccuracy
));
990 fprintf (gcode_f
, "F%s\n", variable_millfeedrate
);
991 for (i
= 0; i
<= n_sides
; i
++)
993 double angle
= M_PI
* 2 * i
/ n_sides
;
994 fprintf (gcode_f
, "G1 X%f Y%f\n",
995 drillX
+ mill_radius
* cos (angle
),
996 drillY
+ mill_radius
* sin (angle
));
998 fprintf (gcode_f
, "G0 X%f Y%f\n", drillX
, drillY
);
1000 fprintf (gcode_f
, "G0 Z%s\n", variable_safeZ
);
1003 fprintf (gcode_f
, "M5 M9 M2\n");
1005 fprintf (gcode_f
, "M5\nM9\nM2\n");
1008 free(drillmill_radiuss
);
1009 free(drillmill_drills
);
1011 /* ******************* end of per-layer writing ************************ */
1013 for (int i_drill_file
=0; i_drill_file
< n_drills
; i_drill_file
++)
1014 free(drills
[i_drill_file
].holes
);
1017 n_drills
= n_drills_allocated
= 0;
1022 * General milling. Put this aside from the above code, as paths
1023 * are generated without taking line or curve thickness into account.
1024 * Accordingly, we need an entirely different approach.
1027 * Currently this is a rarther simple implementation, which mills
1028 * the retangular extents of the board and nothing else. This should
1029 * be sufficient for many use cases.
1031 * A better implementation would have to group the lines and polygons
1032 * on the outline layer by outer polygon and inner holes, then offset
1033 * all of them to the right side and mill that.
1035 /* a better implementation might look like this:
1036 LAYER_LOOP (PCB->Data, MAX_LAYER);
1038 if (strcmp (layer->Name, "outline") == 0)
1042 ... calculate the offset for all lines and polygons of this layer,
1043 mirror it if is_solder, then mill it ...
1051 { /* unconditional */
1052 double lowerX
= 0., lowerY
= 0., upperX
= 0., upperY
= 0.;
1053 double mill_distance
= 0.;
1055 gcode_f
= gcode_start_gcode("outline", metric
);
1058 fprintf (gcode_f
, "(Outline mill file)\n");
1059 fprintf (gcode_f
, "(Tool diameter: %f %s)\n",
1060 gcode_milltoolradius
* 2, metric
? "mm" : "inch");
1063 fprintf (gcode_f
, "%s=%f (safe Z)\n", variable_safeZ
, gcode_safeZ
);
1064 fprintf (gcode_f
, "%s=%f (mill depth)\n",
1065 variable_milldepth
, gcode_milldepth
);
1066 fprintf (gcode_f
, "%s=%f (mill plunge feedrate)\n",
1067 variable_millplunge
, gcode_millplunge
);
1068 fprintf (gcode_f
, "%s=%f (mill feedrate)\n",
1069 variable_millfeedrate
, gcode_millfeedrate
);
1070 fprintf (gcode_f
, "(---------------------------------)\n");
1071 fprintf (gcode_f
, "G17 G%d G90 G64 P0.003 M3 S3000 M7\n",
1076 fprintf (gcode_f
, "(---------------------------------)\n");
1077 fprintf (gcode_f
, "G17\nG%d\nG90\nG64 P0.003\nM3 S3000\nM7\n",
1082 upperX
= COORD_TO_MM(PCB
->ExtentMaxX
- PCB
->ExtentMinX
);
1083 upperY
= COORD_TO_MM(PCB
->ExtentMaxY
- PCB
->ExtentMinY
);
1087 upperX
= COORD_TO_INCH(PCB
->ExtentMaxX
- PCB
->ExtentMinX
);
1088 upperY
= COORD_TO_INCH(PCB
->ExtentMaxY
- PCB
->ExtentMinY
);
1090 lowerX
-= gcode_milltoolradius
;
1091 lowerY
-= gcode_milltoolradius
;
1092 upperX
+= gcode_milltoolradius
;
1093 upperY
+= gcode_milltoolradius
;
1095 fprintf (gcode_f
, "G0 Z%s\n", variable_safeZ
);
1096 /* mill the two edges adjectant to 0,0 first to disconnect the
1097 workpiece from the raw material last */
1098 fprintf (gcode_f
, "G0 X%f Y%f\n", upperX
, lowerY
);
1099 fprintf (gcode_f
, "G1 Z%s F%s\n",
1100 variable_milldepth
, variable_millplunge
);
1101 fprintf (gcode_f
, "G1 X%f Y%f F%s\n",
1102 lowerX
, lowerY
, variable_millfeedrate
);
1103 fprintf (gcode_f
, "G1 X%f Y%f\n", lowerX
, upperY
);
1104 fprintf (gcode_f
, "G1 X%f Y%f\n", upperX
, upperY
);
1105 fprintf (gcode_f
, "G1 X%f Y%f\n", upperX
, lowerY
);
1106 fprintf (gcode_f
, "G0 Z%s\n", variable_safeZ
);
1109 fprintf (gcode_f
, "M5 M9 M2\n");
1111 fprintf (gcode_f
, "M5\nM9\nM2\n");
1112 mill_distance
= abs(gcode_safeZ
- gcode_milldepth
);
1114 mill_distance
/= 25.4;
1115 fprintf (gcode_f
, "(end, total distance G0 %.2f mm = %.2f in)\n",
1116 mill_distance
* 25.4, mill_distance
);
1117 mill_distance
= (upperX
- lowerX
+ upperY
- lowerY
) * 2;
1118 mill_distance
+= abs(gcode_safeZ
- gcode_milldepth
);
1120 mill_distance
/= 25.4;
1121 fprintf (gcode_f
, "( total distance G1 %.2f mm = %.2f in)\n",
1122 mill_distance
* 25.4, mill_distance
);
1127 setlocale (LC_NUMERIC
, old_locale
); /* restore locale */
1130 /* *** PNG export (slightly modified code from PNG export HID) ************* */
1133 gcode_set_layer (const char *name
, int group
, int empty
)
1135 int idx
= (group
>= 0 && group
< max_group
) ?
1136 PCB
->LayerGroups
.Entries
[group
][0] : group
;
1140 name
= PCB
->Data
->Layer
[idx
].Name
;
1142 if (strcmp (name
, "invisible") == 0)
1146 is_drill
= (SL_TYPE (idx
) == SL_PDRILL
|| SL_TYPE (idx
) == SL_UDRILL
);
1147 is_mask
= (SL_TYPE (idx
) == SL_MASK
);
1151 /* Don't print masks */
1157 * Print 'holes', so that we can fill gaps in the copper
1162 if (group
== gcode_cur_group
)
1170 gcode_make_gc (void)
1172 hidGC rv
= (hidGC
) malloc (sizeof (struct hid_gc_struct
));
1173 rv
->me_pointer
= &gcode_hid
;
1174 rv
->cap
= Trace_Cap
;
1176 rv
->color
= (struct color_struct
*) malloc (sizeof (*rv
->color
));
1177 rv
->color
->r
= rv
->color
->g
= rv
->color
->b
= 0;
1183 gcode_destroy_gc (hidGC gc
)
1189 gcode_use_mask (enum mask_mode mode
)
1195 gcode_set_color (hidGC gc
, const char *name
)
1197 if (gcode_im
== NULL
)
1205 if (!strcmp (name
, "drill"))
1211 if (!strcmp (name
, "erase"))
1213 /* FIXME -- should be background, not white */
1224 gcode_set_line_cap (hidGC gc
, EndCapStyle style
)
1230 gcode_set_line_width (hidGC gc
, Coord width
)
1236 gcode_set_draw_xor (hidGC gc
, int xor_
)
1242 gcode_set_draw_faded (hidGC gc
, int faded
)
1251 if (gc
->me_pointer
!= &gcode_hid
)
1253 fprintf (stderr
, "Fatal: GC from another HID passed to gcode HID\n");
1256 if (linewidth
!= gc
->width
)
1258 /* Make sure the scaling doesn't erase lines completely */
1260 if (SCALE (gc->width) == 0 && gc->width > 0)
1261 gdImageSetThickness (im, 1);
1264 gdImageSetThickness (gcode_im
,
1265 pcb_to_gcode (gc
->width
+ 2 * gcode_toolradius
));
1266 linewidth
= gc
->width
;
1269 if (lastbrush
!= gc
->brush
|| need_brush
)
1271 static void *bcache
= 0;
1282 r
= pcb_to_gcode (gc
->width
/ 2 + gcode_toolradius
);
1286 r
= pcb_to_gcode (gc
->width
+ gcode_toolradius
* 2);
1290 sprintf (name
, "#%.2x%.2x%.2x_%c_%d", gc
->color
->r
, gc
->color
->g
,
1291 gc
->color
->b
, type
, r
);
1293 if (hid_cache_color (0, name
, &bval
, &bcache
))
1295 gc
->brush
= (gdImagePtr
)bval
.ptr
;
1301 gc
->brush
= gdImageCreate (2 * r
+ 1, 2 * r
+ 1);
1303 gc
->brush
= gdImageCreate (r
+ 1, r
+ 1);
1304 bg
= gdImageColorAllocate (gc
->brush
, 255, 255, 255);
1306 gdImageColorAllocate (gc
->brush
, gc
->color
->r
, gc
->color
->g
,
1308 gdImageColorTransparent (gc
->brush
, bg
);
1311 * if we shrunk to a radius/box width of zero, then just use
1312 * a single pixel to draw with.
1315 gdImageFilledRectangle (gc
->brush
, 0, 0, 0, 0, fg
);
1319 gdImageFilledEllipse (gc
->brush
, r
, r
, 2 * r
, 2 * r
, fg
);
1321 gdImageFilledRectangle (gc
->brush
, 0, 0, r
, r
, fg
);
1323 bval
.ptr
= gc
->brush
;
1324 hid_cache_color (1, name
, &bval
, &bcache
);
1327 gdImageSetBrush (gcode_im
, gc
->brush
);
1328 lastbrush
= gc
->brush
;
1334 gcode_draw_rect (hidGC gc
, Coord x1
, Coord y1
, Coord x2
, Coord y2
)
1337 gdImageRectangle (gcode_im
,
1338 pcb_to_gcode (x1
- PCB
->ExtentMinX
- gcode_toolradius
),
1339 pcb_to_gcode (y1
- PCB
->ExtentMinY
- gcode_toolradius
),
1340 pcb_to_gcode (x2
- PCB
->ExtentMinX
+ gcode_toolradius
),
1341 pcb_to_gcode (y2
- PCB
->ExtentMinY
+ gcode_toolradius
),
1343 /* printf("Rect %d %d %d %d\n",x1,y1,x2,y2); */
1347 gcode_fill_rect (hidGC gc
, Coord x1
, Coord y1
, Coord x2
, Coord y2
)
1350 gdImageSetThickness (gcode_im
, 0);
1352 gdImageFilledRectangle (gcode_im
,
1353 pcb_to_gcode (x1
- PCB
->ExtentMinX
- gcode_toolradius
),
1354 pcb_to_gcode (y1
- PCB
->ExtentMinY
- gcode_toolradius
),
1355 pcb_to_gcode (x2
- PCB
->ExtentMinX
+ gcode_toolradius
),
1356 pcb_to_gcode (y2
- PCB
->ExtentMinY
+ gcode_toolradius
),
1358 /* printf("FillRect %d %d %d %d\n",x1,y1,x2,y2); */
1362 gcode_draw_line (hidGC gc
, Coord x1
, Coord y1
, Coord x2
, Coord y2
)
1364 if (x1
== x2
&& y1
== y2
)
1366 Coord w
= gc
->width
/ 2;
1367 gcode_fill_rect (gc
,
1368 x1
- PCB
->ExtentMinX
- w
, y1
- PCB
->ExtentMinX
- w
,
1369 x1
- PCB
->ExtentMinX
+ w
, y1
- PCB
->ExtentMinY
+ w
);
1374 gdImageSetThickness (gcode_im
, 0);
1376 gdImageLine (gcode_im
,
1377 pcb_to_gcode (x1
- PCB
->ExtentMinX
),
1378 pcb_to_gcode (y1
- PCB
->ExtentMinY
),
1379 pcb_to_gcode (x2
- PCB
->ExtentMinX
),
1380 pcb_to_gcode (y2
- PCB
->ExtentMinY
), gdBrushed
);
1384 gcode_draw_arc (hidGC gc
, Coord cx
, Coord cy
, Coord width
, Coord height
,
1385 Angle start_angle
, Angle delta_angle
)
1390 * in gdImageArc, 0 degrees is to the right and +90 degrees is down
1391 * in pcb, 0 degrees is to the left and +90 degrees is down
1393 start_angle
= 180 - start_angle
;
1394 delta_angle
= -delta_angle
;
1395 if (delta_angle
> 0)
1398 ea
= start_angle
+ delta_angle
;
1402 sa
= start_angle
+ delta_angle
;
1407 * make sure we start between 0 and 360 otherwise gd does strange
1410 sa
= NormalizeAngle (sa
);
1411 ea
= NormalizeAngle (ea
);
1414 printf ("draw_arc %d,%d %dx%d %d..%d %d..%d\n",
1415 cx
, cy
, width
, height
, start_angle
, delta_angle
, sa
, ea
);
1416 printf ("gdImageArc (%p, %d, %d, %d, %d, %d, %d, %d)\n",
1417 im
, SCALE_X (cx
), SCALE_Y (cy
),
1418 SCALE (width
), SCALE (height
), sa
, ea
, gc
->color
->c
);
1421 gdImageSetThickness (gcode_im
, 0);
1423 gdImageArc (gcode_im
,
1424 pcb_to_gcode (cx
- PCB
->ExtentMinX
),
1425 pcb_to_gcode (cy
- PCB
->ExtentMinY
),
1426 pcb_to_gcode (2 * width
+ gcode_toolradius
* 2),
1427 pcb_to_gcode (2 * height
+ gcode_toolradius
* 2), sa
, ea
,
1431 /* given a hole size, return the structure that currently holds the data for
1432 that hole size. If there isn't one, make it */
1433 static int _drill_size_comparator(const void* _size0
, const void* _size1
)
1435 double size0
= ((const struct single_size_drills
*)_size0
)->diameter_inches
;
1436 double size1
= ((const struct single_size_drills
*)_size1
)->diameter_inches
;
1445 static struct single_size_drills
*
1446 get_drill(double diameter_inches
)
1448 /* see if we already have this size. If so, return that structure */
1449 struct single_size_drills
* drill
=
1450 bsearch (&diameter_inches
,
1451 drills
, n_drills
, sizeof (drills
[0]),
1452 _drill_size_comparator
);
1456 /* haven't seen this hole size before, so make a new structure for it */
1457 if (n_drills
== n_drills_allocated
)
1459 n_drills_allocated
+= 100;
1461 (struct single_size_drills
*) realloc (drills
,
1462 n_drills_allocated
*
1463 sizeof (struct single_size_drills
));
1466 /* I now add the structure to the list, making sure to keep the list
1467 * sorted. Ideally the bsearch() call above would have given me the location
1468 * to insert this element while keeping things sorted, but it doesn't. For
1469 * simplicity I manually lsearch() to find this location myself */
1472 for (; i
<n_drills
; i
++)
1473 if (drills
[i
].diameter_inches
>= diameter_inches
)
1477 memmove (&drills
[i
+1], &drills
[i
],
1478 (n_drills
-i
) * sizeof (struct single_size_drills
));
1480 drills
[i
].diameter_inches
= diameter_inches
;
1481 drills
[i
].n_holes
= 0;
1482 drills
[i
].n_holes_allocated
= 0;
1483 drills
[i
].holes
= NULL
;
1491 add_hole (struct single_size_drills
* drill
,
1492 double cx_inches
, double cy_inches
)
1494 if (drill
->n_holes
== drill
->n_holes_allocated
)
1496 drill
->n_holes_allocated
+= 100;
1498 (struct drill_hole
*) realloc (drill
->holes
,
1499 drill
->n_holes_allocated
*
1500 sizeof (struct drill_hole
));
1503 drill
->holes
[ drill
->n_holes
].x
= cx_inches
;
1504 drill
->holes
[ drill
->n_holes
].y
= cy_inches
;
1509 gcode_fill_circle (hidGC gc
, Coord cx
, Coord cy
, Coord radius
)
1513 gdImageSetThickness (gcode_im
, 0);
1515 gdImageFilledEllipse (gcode_im
,
1516 pcb_to_gcode (cx
- PCB
->ExtentMinX
),
1517 pcb_to_gcode (cy
- PCB
->ExtentMinY
),
1518 pcb_to_gcode (2 * radius
+ gcode_toolradius
* 2),
1519 pcb_to_gcode (2 * radius
+ gcode_toolradius
* 2),
1521 if (save_drill
&& is_drill
)
1523 double diameter_inches
= COORD_TO_INCH(radius
*2);
1525 struct single_size_drills
* drill
= get_drill (diameter_inches
);
1527 /* convert to inch, flip: will drill from bottom side */
1528 COORD_TO_INCH(PCB
->ExtentMaxX
- cx
),
1529 /* PCB reverses y axis */
1530 COORD_TO_INCH(PCB
->ExtentMaxY
- cy
));
1535 gcode_fill_polygon (hidGC gc
, int n_coords
, Coord
*x
, Coord
*y
)
1540 points
= (gdPoint
*) malloc (n_coords
* sizeof (gdPoint
));
1543 fprintf (stderr
, "ERROR: gcode_fill_polygon(): malloc failed\n");
1547 for (i
= 0; i
< n_coords
; i
++)
1549 points
[i
].x
= pcb_to_gcode (x
[i
] - PCB
->ExtentMinX
);
1550 points
[i
].y
= pcb_to_gcode (y
[i
] - PCB
->ExtentMinY
);
1552 gdImageSetThickness (gcode_im
, 0);
1554 gdImageFilledPolygon (gcode_im
, points
, n_coords
, gc
->color
->c
);
1556 /* printf("FillPoly\n"); */
1560 gcode_calibrate (double xval
, double yval
)
1566 gcode_set_crosshair (int x
, int y
, int a
)
1570 /* *** Miscellaneous ******************************************************* */
1572 #include "dolists.h"
1577 memset (&gcode_hid
, 0, sizeof (HID
));
1578 memset (&gcode_graphics
, 0, sizeof (HID_DRAW_API
));
1580 common_nogui_init (&gcode_hid
);
1581 common_draw_helpers_init (&gcode_hid
);
1583 gcode_hid
.struct_size
= sizeof (HID
);
1584 gcode_hid
.name
= "gcode";
1585 gcode_hid
.description
= "G-CODE export";
1586 gcode_hid
.exporter
= 1;
1587 gcode_hid
.poly_before
= 1;
1589 gcode_hid
.get_export_options
= gcode_get_export_options
;
1590 gcode_hid
.do_export
= gcode_do_export
;
1591 gcode_hid
.parse_arguments
= gcode_parse_arguments
;
1592 gcode_hid
.set_layer
= gcode_set_layer
;
1593 gcode_hid
.calibrate
= gcode_calibrate
;
1594 gcode_hid
.set_crosshair
= gcode_set_crosshair
;
1596 gcode_hid
.graphics
= &gcode_graphics
;
1598 gcode_graphics
.make_gc
= gcode_make_gc
;
1599 gcode_graphics
.destroy_gc
= gcode_destroy_gc
;
1600 gcode_graphics
.use_mask
= gcode_use_mask
;
1601 gcode_graphics
.set_color
= gcode_set_color
;
1602 gcode_graphics
.set_line_cap
= gcode_set_line_cap
;
1603 gcode_graphics
.set_line_width
= gcode_set_line_width
;
1604 gcode_graphics
.set_draw_xor
= gcode_set_draw_xor
;
1605 gcode_graphics
.set_draw_faded
= gcode_set_draw_faded
;
1606 gcode_graphics
.draw_line
= gcode_draw_line
;
1607 gcode_graphics
.draw_arc
= gcode_draw_arc
;
1608 gcode_graphics
.draw_rect
= gcode_draw_rect
;
1609 gcode_graphics
.fill_circle
= gcode_fill_circle
;
1610 gcode_graphics
.fill_polygon
= gcode_fill_polygon
;
1611 gcode_graphics
.fill_rect
= gcode_fill_rect
;
1613 hid_register_hid (&gcode_hid
);
1615 #include "gcode_lists.h"