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