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