Use calling wrappers rather than accessing HID_DRAW* vfunc tables directly
[geda-pcb/pcjc2.git] / src / hid / gerber / gerber.c
blob153b2e476e9faa568e8e7f320a00b81a4368ab99
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
5 #include <stdio.h>
6 #include <stdarg.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <string.h>
11 #include <assert.h>
12 #include <ctype.h>
13 #include <math.h>
15 #ifdef HAVE_PWD_H
16 #include <pwd.h>
17 #endif
19 #include <time.h>
21 #include "config.h"
22 #include "global.h"
23 #include "data.h"
24 #include "misc.h"
25 #include "error.h"
26 #include "draw.h"
27 #include "pcb-printf.h"
29 #include "hid.h"
30 #include "hid_draw.h"
31 #include "../hidint.h"
32 #include "hid/common/hidnogui.h"
33 #include "hid/common/draw_helpers.h"
34 #include "hid/common/hidinit.h"
36 #ifdef HAVE_LIBDMALLOC
37 #include <dmalloc.h>
38 #endif
40 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented Gerber function %s.\n", __FUNCTION__); abort()
42 /*----------------------------------------------------------------------------*/
43 /* Function prototypes */
44 /*----------------------------------------------------------------------------*/
46 static HID_Attribute * gerber_get_export_options (int *n);
47 static void gerber_do_export (HID_Attr_Val * options);
48 static void gerber_parse_arguments (int *argc, char ***argv);
49 static int gerber_set_layer (const char *name, int group, int empty);
50 static hidGC gerber_make_gc (void);
51 static void gerber_destroy_gc (hidGC gc);
52 static void gerber_use_mask (enum mask_mode mode);
53 static void gerber_set_color (hidGC gc, const char *name);
54 static void gerber_set_line_cap (hidGC gc, EndCapStyle style);
55 static void gerber_set_line_width (hidGC gc, Coord width);
56 static void gerber_set_draw_xor (hidGC gc, int _xor);
57 static void gerber_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
58 static void gerber_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle);
59 static void gerber_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
60 static void gerber_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius);
61 static void gerber_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
62 static void gerber_calibrate (double xval, double yval);
63 static void gerber_set_crosshair (int x, int y, int action);
64 static void gerber_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y);
66 /*----------------------------------------------------------------------------*/
67 /* Utility routines */
68 /*----------------------------------------------------------------------------*/
70 /* These are for films */
71 #define gerberX(pcb, x) ((Coord) (x))
72 #define gerberY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y)))
73 #define gerberXOffset(pcb, x) ((Coord) (x))
74 #define gerberYOffset(pcb, y) ((Coord) (-(y)))
76 /* These are for drills */
77 #define gerberDrX(pcb, x) ((Coord) (x))
78 #define gerberDrY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y)))
80 /*----------------------------------------------------------------------------*/
81 /* Private data structures */
82 /*----------------------------------------------------------------------------*/
84 static int verbose;
85 static int all_layers;
86 static int metric;
87 static char *x_convspec, *y_convspec;
88 static int is_mask, was_drill;
89 static int is_drill;
90 static enum mask_mode current_mask;
91 static int flash_drills;
92 static int copy_outline_mode;
93 static int name_style;
94 static LayerType *outline_layer;
96 #define print_xcoord(file, pcb, val)\
97 pcb_fprintf(file, x_convspec, gerberX(pcb, val))
99 #define print_ycoord(file, pcb, val)\
100 pcb_fprintf(file, y_convspec, gerberY(pcb, val))
102 enum ApertureShape
104 ROUND, /* Shaped like a circle */
105 OCTAGON, /* octagonal shape */
106 SQUARE, /* Shaped like a square */
107 ROUNDCLEAR, /* clearance in negatives */
108 SQUARECLEAR,
109 THERMAL /* negative thermal relief */
111 typedef enum ApertureShape ApertureShape;
113 /* This is added to the global aperture array indexes to get gerber
114 dcode and macro numbers. */
115 #define DCODE_BASE 11
117 typedef struct aperture
119 int dCode; /* The RS-274X D code */
120 Coord width; /* Size in pcb units */
121 ApertureShape shape; /* ROUND/SQUARE etc */
122 struct aperture *next;
124 Aperture;
126 typedef struct
128 Aperture *data;
129 int count;
130 } ApertureList;
132 static ApertureList *layer_aptr_list;
133 static ApertureList *curr_aptr_list;
134 static int layer_list_max;
135 static int layer_list_idx;
137 typedef struct
139 Coord diam;
140 Coord x;
141 Coord y;
142 } PendingDrills;
143 PendingDrills *pending_drills = NULL;
144 int n_pending_drills = 0, max_pending_drills = 0;
146 /*----------------------------------------------------------------------------*/
147 /* Defined Constants */
148 /*----------------------------------------------------------------------------*/
149 #define AUTO_OUTLINE_WIDTH MIL_TO_COORD(8) /* Auto-geneated outline width of 8 mils */
151 /*----------------------------------------------------------------------------*/
152 /* Aperture Routines */
153 /*----------------------------------------------------------------------------*/
155 /* Initialize aperture list */
156 static void
157 initApertureList (ApertureList *list)
159 list->data = NULL;
160 list->count = 0;
163 static void
164 deinitApertureList (ApertureList *list)
166 Aperture *search = list->data;
167 Aperture *next;
168 while (search)
170 next = search->next;
171 free(search);
172 search = next;
174 initApertureList (list);
177 static int aperture_count;
179 static void resetApertures()
181 int i;
182 for (i = 0; i < layer_list_max; ++i)
183 deinitApertureList (&layer_aptr_list[i]);
184 free (layer_aptr_list);
185 layer_aptr_list = NULL;
186 curr_aptr_list = NULL;
187 layer_list_max = 0;
188 layer_list_idx = 0;
189 aperture_count = 0;
192 /* Create and add a new aperture to the list */
193 static Aperture *
194 addAperture (ApertureList *list, Coord width, ApertureShape shape)
197 Aperture *app = (Aperture *) malloc (sizeof *app);
198 if (app == NULL)
199 return NULL;
201 app->width = width;
202 app->shape = shape;
203 app->dCode = DCODE_BASE + aperture_count++;
204 app->next = list->data;
206 list->data = app;
207 ++list->count;
209 return app;
212 /* Fetch an aperture from the list with the specified
213 * width/shape, creating a new one if none exists */
214 static Aperture *
215 findAperture (ApertureList *list, Coord width, ApertureShape shape)
217 Aperture *search;
219 /* we never draw zero-width lines */
220 if (width == 0)
221 return NULL;
223 /* Search for an appropriate aperture. */
224 for (search = list->data; search; search = search->next)
225 if (search->width == width && search->shape == shape)
226 return search;
228 /* Failing that, create a new one */
229 return addAperture (list, width, shape);
232 /* Output aperture data to the file */
233 static void
234 fprintAperture (FILE *f, Aperture *aptr)
236 switch (aptr->shape)
238 case ROUND:
239 pcb_fprintf (f, metric ? "%%ADD%dC,%.3`mm*%%\r\n" : "%%ADD%dC,%.4`mi*%%\r\n", aptr->dCode, aptr->width);
240 break;
241 case SQUARE:
242 pcb_fprintf (f, metric ? "%%ADD%dR,%.3`mmX%.3`mm*%%\r\n" : "%%ADD%dR,%.4`miX%.4`mi*%%\r\n", aptr->dCode, aptr->width, aptr->width);
243 break;
244 case OCTAGON:
245 pcb_fprintf (f, metric ? "%%AMOCT%d*5,0,8,0,0,%.3`mm,22.5*%%\r\n"
246 "%%ADD%dOCT%d*%%\r\n" : "%%AMOCT%d*5,0,8,0,0,%.3`mm,22.5*%%\r\n"
247 "%%ADD%dOCT%d*%%\r\n", aptr->dCode,
248 (Coord) ((double) aptr->width / COS_22_5_DEGREE), aptr->dCode,
249 aptr->dCode);
250 break;
251 #if 0
252 case THERMAL:
253 fprintf (f, "%%AMTHERM%d*7,0,0,%.4f,%.4f,%.4f,45*%%\r\n"
254 "%%ADD%dTHERM%d*%%\r\n", dCode, gap / 100000.0,
255 width / 100000.0, finger / 100000.0, dCode, dCode);
256 break;
257 case ROUNDCLEAR:
258 fprintf (f, "%%ADD%dC,%.4fX%.4f*%%\r\n",
259 dCode, gap / 100000.0, width / 100000.0);
260 break;
261 case SQUARECLEAR:
262 fprintf (f, "%%ADD%dR,%.4fX%.4fX%.4fX%.4f*%%\r\n",
263 dCode, gap / 100000.0, gap / 100000.0,
264 width / 100000.0, width / 100000.0);
265 break;
266 #else
267 default:
268 break;
269 #endif
273 /* Set the aperture list for the current layer,
274 * expanding the list buffer if needed */
275 static ApertureList *
276 setLayerApertureList (int layer_idx)
278 if (layer_idx >= layer_list_max)
280 int i = layer_list_max;
281 layer_list_max = 2 * (layer_idx + 1);
282 layer_aptr_list = (ApertureList *)
283 realloc (layer_aptr_list, layer_list_max * sizeof (*layer_aptr_list));
284 for (; i < layer_list_max; ++i)
285 initApertureList (&layer_aptr_list[i]);
287 curr_aptr_list = &layer_aptr_list[layer_idx];
288 return curr_aptr_list;
291 /* --------------------------------------------------------------------------- */
293 static HID gerber_hid;
294 static HID_DRAW gerber_graphics;
296 typedef struct gerber_gc_struct
298 struct hid_gc_struct hid_gc; /* Parent */
300 EndCapStyle cap;
301 int width;
302 int color;
303 int erase;
304 int drill;
305 } *gerberGC;
307 static FILE *f = NULL;
308 static char *filename = NULL;
309 static char *filesuff = NULL;
310 static char *layername = NULL;
311 static int lncount = 0;
313 static int finding_apertures = 0;
314 static int pagecount = 0;
315 static int linewidth = -1;
316 static int lastgroup = -1;
317 static int lastcap = -1;
318 static int print_group[MAX_GROUP];
319 static int print_layer[MAX_LAYER];
320 static int lastX, lastY; /* the last X and Y coordinate */
322 static const char *copy_outline_names[] = {
323 #define COPY_OUTLINE_NONE 0
324 "none",
325 #define COPY_OUTLINE_MASK 1
326 "mask",
327 #define COPY_OUTLINE_SILK 2
328 "silk",
329 #define COPY_OUTLINE_ALL 3
330 "all",
331 NULL
334 static const char *name_style_names[] = {
335 #define NAME_STYLE_FIXED 0
336 "fixed",
337 #define NAME_STYLE_SINGLE 1
338 "single",
339 #define NAME_STYLE_FIRST 2
340 "first",
341 #define NAME_STYLE_EAGLE 3
342 "eagle",
343 #define NAME_STYLE_HACKVANA 4
344 "hackvana",
345 NULL
348 static HID_Attribute gerber_options[] = {
350 /* %start-doc options "90 Gerber Export"
351 @ftable @code
352 @item --gerberfile <string>
353 Gerber output file prefix. Can include a path.
354 @end ftable
355 %end-doc
357 {"gerberfile", "Gerber output file base",
358 HID_String, 0, 0, {0, 0, 0}, 0, 0},
359 #define HA_gerberfile 0
361 /* %start-doc options "90 Gerber Export"
362 @ftable @code
363 @item --all-layers
364 Output contains all layers, even empty ones.
365 @end ftable
366 %end-doc
368 {"all-layers", "Output all layers, even empty ones",
369 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
370 #define HA_all_layers 1
372 /* %start-doc options "90 Gerber Export"
373 @ftable @code
374 @item --verbose
375 Print file names and aperture counts on stdout.
376 @end ftable
377 %end-doc
379 {"verbose", "Print file names and aperture counts on stdout",
380 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
381 #define HA_verbose 2
382 /* %start-doc options "90 Gerber Export"
383 @ftable @code
384 @item --metric
385 generate metric Gerber and drill files
386 @end ftable
387 %end-doc
389 {"metric", "Generate metric Gerber and drill files",
390 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
391 #define HA_metric 3
392 {"copy-outline", "Copy outline onto other layers",
393 HID_Enum, 0, 0, {0, 0, 0}, copy_outline_names, 0},
394 #define HA_copy_outline 4
395 {"name-style", "Naming style for individual gerber files",
396 HID_Enum, 0, 0, {0, 0, 0}, name_style_names, 0},
397 #define HA_name_style 5
400 #define NUM_OPTIONS (sizeof(gerber_options)/sizeof(gerber_options[0]))
402 static HID_Attr_Val gerber_values[NUM_OPTIONS];
404 static HID_Attribute *
405 gerber_get_export_options (int *n)
407 static char *last_made_filename = NULL;
408 if (PCB) derive_default_filename(PCB->Filename, &gerber_options[HA_gerberfile], "", &last_made_filename);
410 if (n)
411 *n = NUM_OPTIONS;
412 return gerber_options;
415 static int
416 layer_stack_sort (const void *va, const void *vb)
418 int a_layer = *(int *) va;
419 int b_layer = *(int *) vb;
420 int a_group = GetLayerGroupNumberByNumber (a_layer);
421 int b_group = GetLayerGroupNumberByNumber (b_layer);
423 if (b_group != a_group)
424 return b_group - a_group;
426 return b_layer - a_layer;
429 static void
430 maybe_close_f (FILE *f)
432 if (f)
434 if (was_drill)
435 fprintf (f, "M30\r\n");
436 else
437 fprintf (f, "M02*\r\n");
438 fclose (f);
442 static BoxType region;
444 /* Very similar to layer_type_to_file_name() but appends only a
445 three-character suffix compatible with Eagle's defaults. */
446 static void
447 assign_eagle_file_suffix (char *dest, int idx)
449 int group;
450 int nlayers;
451 char *suff = "out";
453 switch (idx)
455 case SL (SILK, TOP): suff = "plc"; break;
456 case SL (SILK, BOTTOM): suff = "pls"; break;
457 case SL (MASK, TOP): suff = "stc"; break;
458 case SL (MASK, BOTTOM): suff = "sts"; break;
459 case SL (PDRILL, 0): suff = "drd"; break;
460 case SL (UDRILL, 0): suff = "dru"; break;
461 case SL (PASTE, TOP): suff = "crc"; break;
462 case SL (PASTE, BOTTOM): suff = "crs"; break;
463 case SL (INVISIBLE, 0): suff = "inv"; break;
464 case SL (FAB, 0): suff = "fab"; break;
465 case SL (ASSY, TOP): suff = "ast"; break;
466 case SL (ASSY, BOTTOM): suff = "asb"; break;
468 default:
469 group = GetLayerGroupNumberByNumber(idx);
470 nlayers = PCB->LayerGroups.Number[group];
471 if (group == GetLayerGroupNumberBySide(TOP_SIDE)) /* Component */
473 suff = "cmp";
475 else if (group == GetLayerGroupNumberBySide(BOTTOM_SIDE)) /* Solder */
477 suff = "sol";
479 else if (nlayers == 1
480 && (strcmp (PCB->Data->Layer[idx].Name, "route") == 0 ||
481 strcmp (PCB->Data->Layer[idx].Name, "outline") == 0))
483 suff = "oln";
485 else
487 static char buf[20];
488 sprintf (buf, "ly%d", group);
489 suff = buf;
491 break;
494 strcpy (dest, suff);
497 /* Very similar to layer_type_to_file_name() but appends only a
498 three-character suffix compatible with Hackvana's naming requirements */
499 static void
500 assign_hackvana_file_suffix (char *dest, int idx)
502 int group;
503 int nlayers;
504 char *suff = "defau.out";
506 switch (idx)
508 case SL (SILK, TOP): suff = "gto"; break;
509 case SL (SILK, BOTTOM): suff = "gbo"; break;
510 case SL (MASK, TOP): suff = "gts"; break;
511 case SL (MASK, BOTTOM): suff = "gbs"; break;
512 case SL (PDRILL, 0): suff = "drl"; break;
513 case SL (UDRILL, 0):
514 suff = "_NPTH.drl";
515 break;
516 case SL (PASTE, TOP): suff = "gtp"; break;
517 case SL (PASTE, BOTTOM): suff = "gbp"; break;
518 case SL (INVISIBLE, 0): suff = "inv"; break;
519 case SL (FAB, 0): suff = "fab"; break;
520 case SL (ASSY, TOP): suff = "ast"; break;
521 case SL (ASSY, BOTTOM): suff = "asb"; break;
523 default:
524 group = GetLayerGroupNumberByNumber(idx);
525 nlayers = PCB->LayerGroups.Number[group];
526 if (group == GetLayerGroupNumberBySide(TOP_SIDE))
528 suff = "gtl";
530 else if (group == GetLayerGroupNumberBySide(BOTTOM_SIDE))
532 suff = "gbl";
534 else if (nlayers == 1
535 && (strcmp (PCB->Data->Layer[idx].Name, "route") == 0 ||
536 strcmp (PCB->Data->Layer[idx].Name, "outline") == 0))
538 suff = "gm1";
540 else
542 static char buf[20];
543 sprintf (buf, "g%d", group);
544 suff = buf;
546 break;
549 strcpy (dest, suff);
552 static void
553 assign_file_suffix (char *dest, int idx)
555 int fns_style;
556 const char *sext = ".gbr";
558 switch (name_style)
560 default:
561 case NAME_STYLE_FIXED: fns_style = FNS_fixed; break;
562 case NAME_STYLE_SINGLE: fns_style = FNS_single; break;
563 case NAME_STYLE_FIRST: fns_style = FNS_first; break;
564 case NAME_STYLE_EAGLE:
565 assign_eagle_file_suffix (dest, idx);
566 return;
567 case NAME_STYLE_HACKVANA:
568 assign_hackvana_file_suffix (dest, idx);
569 return;
572 switch (idx)
574 case SL (PDRILL, 0):
575 sext = ".cnc";
576 break;
577 case SL (UDRILL, 0):
578 sext = ".cnc";
579 break;
582 strcpy (dest, layer_type_to_file_name (idx, fns_style));
583 strcat (dest, sext);
586 static void
587 gerber_do_export (HID_Attr_Val * options)
589 const char *fnbase;
590 int i;
591 static int saved_layer_stack[MAX_LAYER];
592 int save_ons[MAX_LAYER + 2];
593 FlagType save_thindraw;
595 save_thindraw = PCB->Flags;
596 CLEAR_FLAG(THINDRAWFLAG, PCB);
597 CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
598 CLEAR_FLAG(CHECKPLANESFLAG, PCB);
600 if (!options)
602 gerber_get_export_options (NULL);
603 for (i = 0; i < NUM_OPTIONS; i++)
604 gerber_values[i] = gerber_options[i].default_val;
605 options = gerber_values;
608 fnbase = options[HA_gerberfile].str_value;
609 if (!fnbase)
610 fnbase = "pcb-out";
612 verbose = options[HA_verbose].int_value;
613 metric = options[HA_metric].int_value;
614 if (metric) {
615 x_convspec = "X%.0mu";
616 y_convspec = "Y%.0mu";
617 } else {
618 x_convspec = "X%.0mc";
619 y_convspec = "Y%.0mc";
621 all_layers = options[HA_all_layers].int_value;
623 copy_outline_mode = options[HA_copy_outline].int_value;
624 name_style = options[HA_name_style].int_value;
626 outline_layer = NULL;
628 for (i = 0; i < max_copper_layer; i++)
630 LayerType *layer = PCB->Data->Layer + i;
631 if (strcmp (layer->Name, "outline") == 0 ||
632 strcmp (layer->Name, "route") == 0)
634 outline_layer = layer;
638 i = strlen (fnbase);
639 filename = (char *)realloc (filename, i + 40);
640 strcpy (filename, fnbase);
641 strcat (filename, ".");
642 filesuff = filename + strlen (filename);
644 if (all_layers)
646 memset (print_group, 1, sizeof (print_group));
647 memset (print_layer, 1, sizeof (print_layer));
649 else
651 memset (print_group, 0, sizeof (print_group));
652 memset (print_layer, 0, sizeof (print_layer));
655 hid_save_and_show_layer_ons (save_ons);
656 for (i = 0; i < max_copper_layer; i++)
658 LayerType *layer = PCB->Data->Layer + i;
659 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
660 print_group[GetLayerGroupNumberByNumber (i)] = 1;
662 print_group[GetLayerGroupNumberBySide (BOTTOM_SIDE)] = 1;
663 print_group[GetLayerGroupNumberBySide (TOP_SIDE)] = 1;
664 for (i = 0; i < max_copper_layer; i++)
665 if (print_group[GetLayerGroupNumberByNumber (i)])
666 print_layer[i] = 1;
668 memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
669 qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_stack_sort);
670 linewidth = -1;
671 lastcap = -1;
672 lastgroup = -1;
674 region.X1 = 0;
675 region.Y1 = 0;
676 region.X2 = PCB->MaxWidth;
677 region.Y2 = PCB->MaxHeight;
679 pagecount = 1;
680 resetApertures ();
682 lastgroup = -1;
683 layer_list_idx = 0;
684 finding_apertures = 1;
685 hid_expose_callback (&gerber_hid, &region, 0);
687 layer_list_idx = 0;
688 finding_apertures = 0;
689 hid_expose_callback (&gerber_hid, &region, 0);
691 memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
693 maybe_close_f (f);
694 f = NULL;
695 hid_restore_layer_ons (save_ons);
696 PCB->Flags = save_thindraw;
699 static void
700 gerber_parse_arguments (int *argc, char ***argv)
702 hid_register_attributes (gerber_options, NUM_OPTIONS);
703 hid_parse_command_line (argc, argv);
706 static int
707 drill_sort (const void *va, const void *vb)
709 PendingDrills *a = (PendingDrills *) va;
710 PendingDrills *b = (PendingDrills *) vb;
711 if (a->diam != b->diam)
712 return a->diam - b->diam;
713 if (a->x != b->x)
714 return a->x - b->x;
715 return a->y - b->y;
718 static int
719 gerber_set_layer (const char *name, int group, int empty)
721 int want_outline;
722 char *cp;
723 int idx = (group >= 0
724 && group <
725 max_group) ? PCB->LayerGroups.Entries[group][0] : group;
727 if (name == NULL)
728 name = PCB->Data->Layer[idx].Name;
730 if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
731 return 0;
733 if (strcmp (name, "invisible") == 0)
734 return 0;
735 if (SL_TYPE (idx) == SL_ASSY)
736 return 0;
738 flash_drills = 0;
739 if (strcmp (name, "outline") == 0 ||
740 strcmp (name, "route") == 0)
741 flash_drills = 1;
743 if (is_drill && n_pending_drills)
745 int i;
746 /* dump pending drills in sequence */
747 qsort (pending_drills, n_pending_drills, sizeof (pending_drills[0]),
748 drill_sort);
749 for (i = 0; i < n_pending_drills; i++)
751 if (i == 0 || pending_drills[i].diam != pending_drills[i - 1].diam)
753 Aperture *ap = findAperture (curr_aptr_list, pending_drills[i].diam, ROUND);
754 fprintf (f, "T%02d\r\n", ap->dCode);
756 pcb_fprintf (f, metric ? "X%06.0muY%06.0mu\r\n" : "X%06.0mtY%06.0mt\r\n",
757 gerberDrX (PCB, pending_drills[i].x),
758 gerberDrY (PCB, pending_drills[i].y));
760 free (pending_drills);
761 n_pending_drills = max_pending_drills = 0;
762 pending_drills = NULL;
765 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
766 is_mask = (SL_TYPE (idx) == SL_MASK);
767 current_mask = HID_MASK_OFF;
768 #if 0
769 printf ("Layer %s group %d drill %d mask %d\n", name, group, is_drill,
770 is_mask);
771 #endif
773 if (group < 0 || group != lastgroup)
775 time_t currenttime;
776 char utcTime[64];
777 #ifdef HAVE_GETPWUID
778 struct passwd *pwentry;
779 #endif
780 ApertureList *aptr_list;
781 Aperture *search;
783 lastgroup = group;
784 lastX = -1;
785 lastY = -1;
786 linewidth = -1;
787 lastcap = -1;
789 aptr_list = setLayerApertureList (layer_list_idx++);
791 if (finding_apertures)
792 goto emit_outline;
794 if (aptr_list->count == 0 && !all_layers)
795 return 0;
797 maybe_close_f (f);
798 f = NULL;
800 pagecount++;
801 assign_file_suffix (filesuff, idx);
802 f = fopen (filename, "wb"); /* Binary needed to force CR-LF */
803 if (f == NULL)
805 Message ( "Error: Could not open %s for writing.\n", filename);
806 return 1;
809 was_drill = is_drill;
811 if (verbose)
813 int c = aptr_list->count;
814 printf ("Gerber: %d aperture%s in %s\n", c,
815 c == 1 ? "" : "s", filename);
818 if (is_drill)
820 /* We omit the ,TZ here because we are not omitting trailing zeros. Our format is
821 always six-digit 0.1 mil or µm resolution (i.e. 001100 = 0.11" or 1.1mm)*/
822 fprintf (f, "M48\r\n");
823 fprintf (f, metric ? "METRIC,000.000\r\n" : "INCH\r\n");
824 for (search = aptr_list->data; search; search = search->next)
825 pcb_fprintf (f, metric ? "T%02dC%.3`mm\r\n" : "T%02dC%.3`mi\r\n", search->dCode, search->width);
826 fprintf (f, "%%\r\n");
827 /* FIXME */
828 return 1;
831 fprintf (f, "G04 start of page %d for group %d idx %d *\r\n",
832 pagecount, group, idx);
834 /* Create a portable timestamp. */
835 currenttime = time (NULL);
837 /* avoid gcc complaints */
838 const char *fmt = "%c UTC";
839 strftime (utcTime, sizeof utcTime, fmt, gmtime (&currenttime));
841 /* Print a cute file header at the beginning of each file. */
842 fprintf (f, "G04 Title: %s, %s *\r\n", UNKNOWN (PCB->Name),
843 UNKNOWN (name));
844 fprintf (f, "G04 Creator: %s " VERSION " *\r\n", Progname);
845 fprintf (f, "G04 CreationDate: %s *\r\n", utcTime);
847 #ifdef HAVE_GETPWUID
848 /* ID the user. */
849 pwentry = getpwuid (getuid ());
850 fprintf (f, "G04 For: %s *\r\n", pwentry->pw_name);
851 #endif
853 fprintf (f, "G04 Format: Gerber/RS-274X *\r\n");
854 pcb_fprintf (f, metric ? "G04 PCB-Dimensions (mm): %.2mm %.2mm *\r\n" :
855 "G04 PCB-Dimensions (mil): %.2ml %.2ml *\r\n",
856 PCB->MaxWidth, PCB->MaxHeight);
857 fprintf (f, "G04 PCB-Coordinate-Origin: lower left *\r\n");
859 /* Signal data in inches. */
860 fprintf (f, metric ? "%%MOMM*%%\r\n" : "%%MOIN*%%\r\n");
862 /* Signal Leading zero suppression, Absolute Data, 2.5 format in inch, 4.3 in mm */
863 fprintf (f, metric ? "%%FSLAX43Y43*%%\r\n" : "%%FSLAX25Y25*%%\r\n");
865 /* build a legal identifier. */
866 if (layername)
867 free (layername);
868 layername = strdup (filesuff);
869 if (strrchr (layername, '.'))
870 * strrchr (layername, '.') = 0;
872 for (cp=layername; *cp; cp++)
874 if (isalnum((int) *cp))
875 *cp = toupper((int) *cp);
876 else
877 *cp = '_';
879 fprintf (f, "%%LN%s*%%\r\n", layername);
880 lncount = 1;
882 for (search = aptr_list->data; search; search = search->next)
883 fprintAperture(f, search);
884 if (aptr_list->count == 0)
885 /* We need to put *something* in the file to make it be parsed
886 as RS-274X instead of RS-274D. */
887 fprintf (f, "%%ADD11C,0.0100*%%\r\n");
890 emit_outline:
891 /* If we're printing a copper layer other than the outline layer,
892 and we want to "print outlines", and we have an outline layer,
893 print the outline layer on this layer also. */
894 want_outline = 0;
895 if (copy_outline_mode == COPY_OUTLINE_MASK
896 && SL_TYPE (idx) == SL_MASK)
897 want_outline = 1;
898 if (copy_outline_mode == COPY_OUTLINE_SILK
899 && SL_TYPE (idx) == SL_SILK)
900 want_outline = 1;
901 if (copy_outline_mode == COPY_OUTLINE_ALL
902 && (SL_TYPE (idx) == SL_SILK
903 || SL_TYPE (idx) == SL_MASK
904 || SL_TYPE (idx) == SL_FAB
905 || SL_TYPE (idx) == SL_ASSY
906 || SL_TYPE (idx) == 0))
907 want_outline = 1;
909 if (want_outline
910 && strcmp (name, "outline")
911 && strcmp (name, "route"))
913 if (outline_layer
914 && outline_layer != PCB->Data->Layer+idx)
915 DrawLayer (outline_layer, &region);
916 else if (!outline_layer)
918 hidGC gc = hid_draw_make_gc (&gerber_graphics);
919 printf("name %s idx %d\n", name, idx);
920 if (SL_TYPE (idx) == SL_SILK)
921 hid_draw_set_line_width (gc, PCB->minSlk);
922 else if (group >= 0)
923 hid_draw_set_line_width (gc, PCB->minWid);
924 else
925 hid_draw_set_line_width (gc, AUTO_OUTLINE_WIDTH);
926 hid_draw_line (gc, 0, 0, PCB->MaxWidth, 0);
927 hid_draw_line (gc, 0, 0, 0, PCB->MaxHeight);
928 hid_draw_line (gc, PCB->MaxWidth, 0, PCB->MaxWidth, PCB->MaxHeight);
929 hid_draw_line (gc, 0, PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight);
930 hid_draw_destroy_gc (gc);
934 return 1;
937 static hidGC
938 gerber_make_gc (void)
940 hidGC gc = (hidGC) calloc (1, sizeof (struct gerber_gc_struct));
941 gerberGC gerber_gc = (gerberGC)gc;
943 gc->hid = &gerber_hid;
944 gc->hid_draw = &gerber_graphics;
946 gerber_gc->cap = Trace_Cap;
948 return gc;
951 static void
952 gerber_destroy_gc (hidGC gc)
954 free (gc);
957 static void
958 gerber_use_mask (enum mask_mode mode)
960 current_mask = mode;
963 static void
964 gerber_set_color (hidGC gc, const char *name)
966 gerberGC gerber_gc = (gerberGC)gc;
968 if (strcmp (name, "erase") == 0)
970 gerber_gc->color = 1;
971 gerber_gc->erase = 1;
972 gerber_gc->drill = 0;
974 else if (strcmp (name, "drill") == 0)
976 gerber_gc->color = 1;
977 gerber_gc->erase = 0;
978 gerber_gc->drill = 1;
980 else
982 gerber_gc->color = 0;
983 gerber_gc->erase = 0;
984 gerber_gc->drill = 0;
988 static void
989 gerber_set_line_cap (hidGC gc, EndCapStyle style)
991 gerberGC gerber_gc = (gerberGC)gc;
993 gerber_gc->cap = style;
996 static void
997 gerber_set_line_width (hidGC gc, Coord width)
999 gerberGC gerber_gc = (gerberGC)gc;
1001 gerber_gc->width = width;
1004 static void
1005 gerber_set_draw_xor (hidGC gc, int xor_)
1010 static void
1011 use_gc (hidGC gc, int radius)
1013 gerberGC gerber_gc = (gerberGC)gc;
1015 if (radius)
1017 radius *= 2;
1018 if (radius != linewidth || lastcap != Round_Cap)
1020 Aperture *aptr = findAperture (curr_aptr_list, radius, ROUND);
1021 if (aptr == NULL)
1022 pcb_fprintf (stderr, "error: aperture for radius %$mS type ROUND is null\n", radius);
1023 else if (f && !is_drill)
1024 fprintf (f, "G54D%d*", aptr->dCode);
1025 linewidth = radius;
1026 lastcap = Round_Cap;
1029 else if (linewidth != gerber_gc->width || lastcap != gerber_gc->cap)
1031 Aperture *aptr;
1032 ApertureShape shape;
1034 linewidth = gerber_gc->width;
1035 lastcap = gerber_gc->cap;
1036 switch (gerber_gc->cap)
1038 case Round_Cap:
1039 case Trace_Cap:
1040 shape = ROUND;
1041 break;
1042 default:
1043 case Square_Cap:
1044 shape = SQUARE;
1045 break;
1047 aptr = findAperture (curr_aptr_list, linewidth, shape);
1048 if (aptr == NULL)
1049 pcb_fprintf (stderr, "error: aperture for width %$mS type %s is null\n",
1050 linewidth, shape == ROUND ? "ROUND" : "SQUARE");
1051 else if (f)
1052 fprintf (f, "G54D%d*", aptr->dCode);
1056 static void
1057 gerber_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1059 gerber_draw_line (gc, x1, y1, x1, y2);
1060 gerber_draw_line (gc, x1, y1, x2, y1);
1061 gerber_draw_line (gc, x1, y2, x2, y2);
1062 gerber_draw_line (gc, x2, y1, x2, y2);
1065 static void
1066 gerber_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1068 gerberGC gerber_gc = (gerberGC)gc;
1069 bool m = false;
1071 if (x1 != x2 && y1 != y2 && gerber_gc->cap == Square_Cap)
1073 Coord x[5], y[5];
1074 double tx, ty, theta;
1076 theta = atan2 (y2-y1, x2-x1);
1078 /* T is a vector half a thickness long, in the direction of
1079 one of the corners. */
1080 tx = gerber_gc->width / 2.0 * cos (theta + M_PI/4) * sqrt(2.0);
1081 ty = gerber_gc->width / 2.0 * sin (theta + M_PI/4) * sqrt(2.0);
1083 x[0] = x1 - tx; y[0] = y1 - ty;
1084 x[1] = x2 + ty; y[1] = y2 - tx;
1085 x[2] = x2 + tx; y[2] = y2 + ty;
1086 x[3] = x1 - ty; y[3] = y1 + tx;
1088 x[4] = x[0]; y[4] = y[0];
1089 gerber_fill_polygon (gc, 5, x, y);
1090 return;
1093 use_gc (gc, 0);
1094 if (!f)
1095 return;
1097 if (x1 != lastX)
1099 m = true;
1100 lastX = x1;
1101 print_xcoord (f, PCB, lastX);
1103 if (y1 != lastY)
1105 m = true;
1106 lastY = y1;
1107 print_ycoord (f, PCB, lastY);
1109 if ((x1 == x2) && (y1 == y2))
1110 fprintf (f, "D03*\r\n");
1111 else
1113 if (m)
1114 fprintf (f, "D02*");
1115 if (x2 != lastX)
1117 lastX = x2;
1118 print_xcoord (f, PCB, lastX);
1120 if (y2 != lastY)
1122 lastY = y2;
1123 print_ycoord (f, PCB, lastY);
1125 fprintf (f, "D01*\r\n");
1130 static void
1131 gerber_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1132 Angle start_angle, Angle delta_angle)
1134 gerberGC gerber_gc = (gerberGC)gc;
1135 bool m = false;
1136 double arcStartX, arcStopX, arcStartY, arcStopY;
1138 /* we never draw zero-width lines */
1139 if (gerber_gc->width == 0)
1140 return;
1142 use_gc (gc, 0);
1143 if (!f)
1144 return;
1146 arcStartX = cx - width * cos (TO_RADIANS (start_angle));
1147 arcStartY = cy + height * sin (TO_RADIANS (start_angle));
1149 /* I checked three different gerber viewers, and they all disagreed
1150 on how ellipses should be drawn. The spec just calls G74/G75
1151 "circular interpolation" so there's a chance it just doesn't
1152 support ellipses at all. Thus, we draw them out with line
1153 segments. Note that most arcs in pcb are circles anyway. */
1154 if (width != height)
1156 double step, angle;
1157 Coord max = width > height ? width : height;
1158 Coord minr = max - gerber_gc->width / 10;
1159 int nsteps;
1160 Coord x0, y0, x1, y1;
1162 if (minr >= max)
1163 minr = max - 1;
1164 step = acos((double)minr/(double)max) * 180.0/M_PI;
1165 if (step > 5)
1166 step = 5;
1167 nsteps = abs(delta_angle) / step + 1;
1168 step = (double)delta_angle / nsteps;
1170 x0 = arcStartX;
1171 y0 = arcStartY;
1172 angle = start_angle;
1173 while (nsteps > 0)
1175 nsteps --;
1176 x1 = cx - width * cos (TO_RADIANS (angle+step));
1177 y1 = cy + height * sin (TO_RADIANS (angle+step));
1178 gerber_draw_line (gc, x0, y0, x1, y1);
1179 x0 = x1;
1180 y0 = y1;
1181 angle += step;
1183 return;
1186 arcStopX = cx - width * cos (TO_RADIANS (start_angle + delta_angle));
1187 arcStopY = cy + height * sin (TO_RADIANS (start_angle + delta_angle));
1188 if (arcStartX != lastX)
1190 m = true;
1191 lastX = arcStartX;
1192 print_xcoord (f, PCB, lastX);
1194 if (arcStartY != lastY)
1196 m = true;
1197 lastY = arcStartY;
1198 print_ycoord (f, PCB, lastY);
1200 if (m)
1201 fprintf (f, "D02*");
1202 pcb_fprintf (f,
1203 metric ? "G75*G0%1dX%.0muY%.0muI%.0muJ%.0muD01*G01*\r\n" :
1204 "G75*G0%1dX%.0mcY%.0mcI%.0mcJ%.0mcD01*G01*\r\n",
1205 (delta_angle < 0) ? 2 : 3,
1206 gerberX (PCB, arcStopX), gerberY (PCB, arcStopY),
1207 gerberXOffset (PCB, cx - arcStartX),
1208 gerberYOffset (PCB, cy - arcStartY));
1209 lastX = arcStopX;
1210 lastY = arcStopY;
1213 static void
1214 gerber_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1216 gerberGC gerber_gc = (gerberGC)gc;
1218 if (radius <= 0)
1219 return;
1220 if (is_drill)
1221 radius = 50 * round (radius / 50.0);
1222 use_gc (gc, radius);
1223 if (!f)
1224 return;
1225 if (is_drill)
1227 if (n_pending_drills >= max_pending_drills)
1229 max_pending_drills += 100;
1230 pending_drills = (PendingDrills *) realloc(pending_drills,
1231 max_pending_drills *
1232 sizeof (pending_drills[0]));
1234 pending_drills[n_pending_drills].x = cx;
1235 pending_drills[n_pending_drills].y = cy;
1236 pending_drills[n_pending_drills].diam = radius * 2;
1237 n_pending_drills++;
1238 return;
1240 else if (gerber_gc->drill && !flash_drills)
1241 return;
1242 if (cx != lastX)
1244 lastX = cx;
1245 print_xcoord (f, PCB, lastX);
1247 if (cy != lastY)
1249 lastY = cy;
1250 print_ycoord (f, PCB, lastY);
1252 fprintf (f, "D03*\r\n");
1255 static void
1256 gerber_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1258 bool m = false;
1259 int i;
1260 int firstTime = 1;
1261 Coord startX = 0, startY = 0;
1263 if (is_mask && current_mask == HID_MASK_BEFORE)
1264 return;
1266 use_gc (gc, 10 * 100);
1267 if (!f)
1268 return;
1269 fprintf (f, "G36*\r\n");
1270 for (i = 0; i < n_coords; i++)
1272 if (x[i] != lastX)
1274 m = true;
1275 lastX = x[i];
1276 print_xcoord (f, PCB, lastX);
1278 if (y[i] != lastY)
1280 m = true;
1281 lastY = y[i];
1282 print_ycoord (f, PCB, lastY);
1284 if (firstTime)
1286 firstTime = 0;
1287 startX = x[i];
1288 startY = y[i];
1289 if (m)
1290 fprintf (f, "D02*");
1292 else if (m)
1293 fprintf (f, "D01*\r\n");
1294 m = false;
1296 if (startX != lastX)
1298 m = true;
1299 lastX = startX;
1300 print_xcoord (f, PCB, startX);
1302 if (startY != lastY)
1304 m = true;
1305 lastY = startY;
1306 print_ycoord (f, PCB, lastY);
1308 if (m)
1309 fprintf (f, "D01*\r\n");
1310 fprintf (f, "G37*\r\n");
1313 static void
1314 gerber_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1316 Coord x[5];
1317 Coord y[5];
1318 x[0] = x[4] = x1;
1319 y[0] = y[4] = y1;
1320 x[1] = x1;
1321 y[1] = y2;
1322 x[2] = x2;
1323 y[2] = y2;
1324 x[3] = x2;
1325 y[3] = y1;
1326 gerber_fill_polygon (gc, 5, x, y);
1329 static void
1330 gerber_calibrate (double xval, double yval)
1332 CRASH;
1335 static void
1336 gerber_set_crosshair (int x, int y, int action)
1340 void
1341 hid_gerber_init ()
1343 memset (&gerber_hid, 0, sizeof (gerber_hid));
1344 memset (&gerber_graphics, 0, sizeof (gerber_graphics));
1346 common_nogui_init (&gerber_hid);
1347 common_draw_helpers_init (&gerber_graphics);
1349 gerber_hid.struct_size = sizeof (gerber_hid);
1350 gerber_hid.name = "gerber";
1351 gerber_hid.description = "RS-274X (Gerber) export";
1352 gerber_hid.exporter = 1;
1354 gerber_hid.get_export_options = gerber_get_export_options;
1355 gerber_hid.do_export = gerber_do_export;
1356 gerber_hid.parse_arguments = gerber_parse_arguments;
1357 gerber_hid.set_layer = gerber_set_layer;
1358 gerber_hid.calibrate = gerber_calibrate;
1359 gerber_hid.set_crosshair = gerber_set_crosshair;
1361 gerber_hid.graphics = &gerber_graphics;
1363 gerber_graphics.make_gc = gerber_make_gc;
1364 gerber_graphics.destroy_gc = gerber_destroy_gc;
1365 gerber_graphics.use_mask = gerber_use_mask;
1366 gerber_graphics.set_color = gerber_set_color;
1367 gerber_graphics.set_line_cap = gerber_set_line_cap;
1368 gerber_graphics.set_line_width = gerber_set_line_width;
1369 gerber_graphics.set_draw_xor = gerber_set_draw_xor;
1370 gerber_graphics.draw_line = gerber_draw_line;
1371 gerber_graphics.draw_arc = gerber_draw_arc;
1372 gerber_graphics.draw_rect = gerber_draw_rect;
1373 gerber_graphics.fill_circle = gerber_fill_circle;
1374 gerber_graphics.fill_polygon = gerber_fill_polygon;
1375 gerber_graphics.fill_rect = gerber_fill_rect;
1377 hid_register_hid (&gerber_hid);