Convert mask type to enum
[geda-pcb/pcjc2.git] / src / hid / gerber / gerber.c
blob8d73c7e88062dc4aef1c571ea20dff568f760d18
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 "../hidint.h"
31 #include "hid/common/hidnogui.h"
32 #include "hid/common/draw_helpers.h"
33 #include "hid/common/hidinit.h"
35 #ifdef HAVE_LIBDMALLOC
36 #include <dmalloc.h>
37 #endif
39 #define CRASH fprintf(stderr, "HID error: pcb called unimplemented Gerber function %s.\n", __FUNCTION__); abort()
41 /*----------------------------------------------------------------------------*/
42 /* Function prototypes */
43 /*----------------------------------------------------------------------------*/
45 static HID_Attribute * gerber_get_export_options (int *n);
46 static void gerber_do_export (HID_Attr_Val * options);
47 static void gerber_parse_arguments (int *argc, char ***argv);
48 static int gerber_set_layer (const char *name, int group, int empty);
49 static hidGC gerber_make_gc (void);
50 static void gerber_destroy_gc (hidGC gc);
51 static void gerber_use_mask (enum mask_mode mode);
52 static void gerber_set_color (hidGC gc, const char *name);
53 static void gerber_set_line_cap (hidGC gc, EndCapStyle style);
54 static void gerber_set_line_width (hidGC gc, Coord width);
55 static void gerber_set_draw_xor (hidGC gc, int _xor);
56 static void gerber_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
57 static void gerber_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height, Angle start_angle, Angle delta_angle);
58 static void gerber_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
59 static void gerber_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius);
60 static void gerber_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2);
61 static void gerber_calibrate (double xval, double yval);
62 static void gerber_set_crosshair (int x, int y, int action);
63 static void gerber_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y);
65 /*----------------------------------------------------------------------------*/
66 /* Utility routines */
67 /*----------------------------------------------------------------------------*/
69 /* These are for films */
70 #define gerberX(pcb, x) ((Coord) (x))
71 #define gerberY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y)))
72 #define gerberXOffset(pcb, x) ((Coord) (x))
73 #define gerberYOffset(pcb, y) ((Coord) (-(y)))
75 /* These are for drills (printed as mils but are really 1/10th mil) */
76 #define gerberDrX(pcb, x) ((Coord) (x))
77 #define gerberDrY(pcb, y) ((Coord) ((pcb)->MaxHeight - (y)))
79 /*----------------------------------------------------------------------------*/
80 /* Private data structures */
81 /*----------------------------------------------------------------------------*/
83 static int verbose;
84 static int all_layers;
85 static int metric;
86 static char *x_convspec, *y_convspec;
87 static int is_mask, was_drill;
88 static int is_drill;
89 static enum mask_mode current_mask;
90 static int flash_drills;
91 static int copy_outline_mode;
92 static int name_style;
93 static LayerType *outline_layer;
95 #define print_xcoord(file, pcb, val)\
96 pcb_fprintf(file, x_convspec, gerberX(pcb, val))
98 #define print_ycoord(file, pcb, val)\
99 pcb_fprintf(file, y_convspec, gerberY(pcb, val))
101 enum ApertureShape
103 ROUND, /* Shaped like a circle */
104 OCTAGON, /* octagonal shape */
105 SQUARE, /* Shaped like a square */
106 ROUNDCLEAR, /* clearance in negatives */
107 SQUARECLEAR,
108 THERMAL /* negative thermal relief */
110 typedef enum ApertureShape ApertureShape;
112 /* This is added to the global aperture array indexes to get gerber
113 dcode and macro numbers. */
114 #define DCODE_BASE 11
116 typedef struct aperture
118 int dCode; /* The RS-274X D code */
119 Coord width; /* Size in pcb units */
120 ApertureShape shape; /* ROUND/SQUARE etc */
121 struct aperture *next;
123 Aperture;
125 typedef struct
127 Aperture *data;
128 int count;
129 } ApertureList;
131 static ApertureList *layer_aptr_list;
132 static ApertureList *curr_aptr_list;
133 static int layer_list_max;
134 static int layer_list_idx;
136 typedef struct
138 Coord diam;
139 Coord x;
140 Coord y;
141 } PendingDrills;
142 PendingDrills *pending_drills = NULL;
143 int n_pending_drills = 0, max_pending_drills = 0;
145 /*----------------------------------------------------------------------------*/
146 /* Defined Constants */
147 /*----------------------------------------------------------------------------*/
148 #define AUTO_OUTLINE_WIDTH MIL_TO_COORD(8) /* Auto-geneated outline width of 8 mils */
150 /*----------------------------------------------------------------------------*/
151 /* Aperture Routines */
152 /*----------------------------------------------------------------------------*/
154 /* Initialize aperture list */
155 static void
156 initApertureList (ApertureList *list)
158 list->data = NULL;
159 list->count = 0;
162 static void
163 deinitApertureList (ApertureList *list)
165 Aperture *search = list->data;
166 Aperture *next;
167 while (search)
169 next = search->next;
170 free(search);
171 search = next;
173 initApertureList (list);
176 static void resetApertures()
178 int i;
179 for (i = 0; i < layer_list_max; ++i)
180 deinitApertureList (&layer_aptr_list[i]);
181 free (layer_aptr_list);
182 layer_aptr_list = NULL;
183 curr_aptr_list = NULL;
184 layer_list_max = 0;
185 layer_list_idx = 0;
188 /* Create and add a new aperture to the list */
189 static Aperture *
190 addAperture (ApertureList *list, Coord width, ApertureShape shape)
192 static int aperture_count;
194 Aperture *app = (Aperture *) malloc (sizeof *app);
195 if (app == NULL)
196 return NULL;
198 app->width = width;
199 app->shape = shape;
200 app->dCode = DCODE_BASE + aperture_count++;
201 app->next = list->data;
203 list->data = app;
204 ++list->count;
206 return app;
209 /* Fetch an aperture from the list with the specified
210 * width/shape, creating a new one if none exists */
211 static Aperture *
212 findAperture (ApertureList *list, Coord width, ApertureShape shape)
214 Aperture *search;
216 /* we never draw zero-width lines */
217 if (width == 0)
218 return NULL;
220 /* Search for an appropriate aperture. */
221 for (search = list->data; search; search = search->next)
222 if (search->width == width && search->shape == shape)
223 return search;
225 /* Failing that, create a new one */
226 return addAperture (list, width, shape);
229 /* Output aperture data to the file */
230 static void
231 fprintAperture (FILE *f, Aperture *aptr)
233 switch (aptr->shape)
235 case ROUND:
236 pcb_fprintf (f, metric ? "%%ADD%dC,%.3`mm*%%\r\n" : "%%ADD%dC,%.4`mi*%%\r\n", aptr->dCode, aptr->width);
237 break;
238 case SQUARE:
239 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);
240 break;
241 case OCTAGON:
242 pcb_fprintf (f, metric ? "%%AMOCT%d*5,0,8,0,0,%.3`mm,22.5*%%\r\n"
243 "%%ADD%dOCT%d*%%\r\n" : "%%AMOCT%d*5,0,8,0,0,%.3`mm,22.5*%%\r\n"
244 "%%ADD%dOCT%d*%%\r\n", aptr->dCode,
245 (Coord) ((double) aptr->width / COS_22_5_DEGREE), aptr->dCode,
246 aptr->dCode);
247 break;
248 #if 0
249 case THERMAL:
250 fprintf (f, "%%AMTHERM%d*7,0,0,%.4f,%.4f,%.4f,45*%%\r\n"
251 "%%ADD%dTHERM%d*%%\r\n", dCode, gap / 100000.0,
252 width / 100000.0, finger / 100000.0, dCode, dCode);
253 break;
254 case ROUNDCLEAR:
255 fprintf (f, "%%ADD%dC,%.4fX%.4f*%%\r\n",
256 dCode, gap / 100000.0, width / 100000.0);
257 break;
258 case SQUARECLEAR:
259 fprintf (f, "%%ADD%dR,%.4fX%.4fX%.4fX%.4f*%%\r\n",
260 dCode, gap / 100000.0, gap / 100000.0,
261 width / 100000.0, width / 100000.0);
262 break;
263 #else
264 default:
265 break;
266 #endif
270 /* Set the aperture list for the current layer,
271 * expanding the list buffer if needed */
272 static ApertureList *
273 setLayerApertureList (int layer_idx)
275 if (layer_idx >= layer_list_max)
277 int i = layer_list_max;
278 layer_list_max = 2 * (layer_idx + 1);
279 layer_aptr_list = (ApertureList *)
280 realloc (layer_aptr_list, layer_list_max * sizeof (*layer_aptr_list));
281 for (; i < layer_list_max; ++i)
282 initApertureList (&layer_aptr_list[i]);
284 curr_aptr_list = &layer_aptr_list[layer_idx];
285 return curr_aptr_list;
288 /* --------------------------------------------------------------------------- */
290 static HID gerber_hid;
292 typedef struct hid_gc_struct
294 EndCapStyle cap;
295 int width;
296 int color;
297 int erase;
298 int drill;
299 } hid_gc_struct;
301 static FILE *f = NULL;
302 static char *filename = NULL;
303 static char *filesuff = NULL;
304 static char *layername = NULL;
305 static int lncount = 0;
307 static int finding_apertures = 0;
308 static int pagecount = 0;
309 static int linewidth = -1;
310 static int lastgroup = -1;
311 static int lastcap = -1;
312 static int print_group[MAX_LAYER];
313 static int print_layer[MAX_LAYER];
314 static int lastX, lastY; /* the last X and Y coordinate */
316 static const char *copy_outline_names[] = {
317 #define COPY_OUTLINE_NONE 0
318 "none",
319 #define COPY_OUTLINE_MASK 1
320 "mask",
321 #define COPY_OUTLINE_SILK 2
322 "silk",
323 #define COPY_OUTLINE_ALL 3
324 "all",
325 NULL
328 static const char *name_style_names[] = {
329 #define NAME_STYLE_FIXED 0
330 "fixed",
331 #define NAME_STYLE_SINGLE 1
332 "single",
333 #define NAME_STYLE_FIRST 2
334 "first",
335 #define NAME_STYLE_EAGLE 3
336 "eagle",
337 NULL
340 static HID_Attribute gerber_options[] = {
342 /* %start-doc options "90 Gerber Export"
343 @ftable @code
344 @item --gerberfile <string>
345 Gerber output file prefix. Can include a path.
346 @end ftable
347 %end-doc
349 {"gerberfile", "Gerber output file base",
350 HID_String, 0, 0, {0, 0, 0}, 0, 0},
351 #define HA_gerberfile 0
353 /* %start-doc options "90 Gerber Export"
354 @ftable @code
355 @item --all-layers
356 Output contains all layers, even empty ones.
357 @end ftable
358 %end-doc
360 {"all-layers", "Output all layers, even empty ones",
361 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
362 #define HA_all_layers 1
364 /* %start-doc options "90 Gerber Export"
365 @ftable @code
366 @item --verbose
367 Print file names and aperture counts on stdout.
368 @end ftable
369 %end-doc
371 {"verbose", "Print file names and aperture counts on stdout",
372 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
373 #define HA_verbose 2
374 /* %start-doc options "90 Gerber Export"
375 @ftable @code
376 @item --metric
377 generate metric Gerber and drill files
378 @end ftable
379 %end-doc
381 {"metric", "Generate metric Gerber and drill files",
382 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
383 #define HA_metric 3
384 {"copy-outline", "Copy outline onto other layers",
385 HID_Enum, 0, 0, {0, 0, 0}, copy_outline_names, 0},
386 #define HA_copy_outline 4
387 {"name-style", "Naming style for individual gerber files",
388 HID_Enum, 0, 0, {0, 0, 0}, name_style_names, 0},
389 #define HA_name_style 5
392 #define NUM_OPTIONS (sizeof(gerber_options)/sizeof(gerber_options[0]))
394 static HID_Attr_Val gerber_values[NUM_OPTIONS];
396 static HID_Attribute *
397 gerber_get_export_options (int *n)
399 static char *last_made_filename = NULL;
400 if (PCB) derive_default_filename(PCB->Filename, &gerber_options[HA_gerberfile], "", &last_made_filename);
402 if (n)
403 *n = NUM_OPTIONS;
404 return gerber_options;
407 static int
408 group_for_layer (int l)
410 if (l < max_copper_layer + 2 && l >= 0)
411 return GetLayerGroupNumberByNumber (l);
412 /* else something unique */
413 return max_group + 3 + l;
416 static int
417 layer_sort (const void *va, const void *vb)
419 int a = *(int *) va;
420 int b = *(int *) vb;
421 int d = group_for_layer (b) - group_for_layer (a);
422 if (d)
423 return d;
424 return b - a;
427 static void
428 maybe_close_f (FILE *f)
430 if (f)
432 if (was_drill)
433 fprintf (f, "M30\r\n");
434 else
435 fprintf (f, "M02*\r\n");
436 fclose (f);
440 static BoxType region;
442 /* Very similar to layer_type_to_file_name() but appends only a
443 three-character suffix compatible with Eagle's defaults. */
444 static void
445 assign_eagle_file_suffix (char *dest, int idx)
447 int group;
448 int nlayers;
449 char *suff = "out";
451 switch (idx)
453 case SL (SILK, TOP): suff = "plc"; break;
454 case SL (SILK, BOTTOM): suff = "pls"; break;
455 case SL (MASK, TOP): suff = "stc"; break;
456 case SL (MASK, BOTTOM): suff = "sts"; break;
457 case SL (PDRILL, 0): suff = "drd"; break;
458 case SL (UDRILL, 0): suff = "dru"; break;
459 case SL (PASTE, TOP): suff = "crc"; break;
460 case SL (PASTE, BOTTOM): suff = "crs"; break;
461 case SL (INVISIBLE, 0): suff = "inv"; break;
462 case SL (FAB, 0): suff = "fab"; break;
463 case SL (ASSY, TOP): suff = "ast"; break;
464 case SL (ASSY, BOTTOM): suff = "asb"; break;
466 default:
467 group = GetLayerGroupNumberByNumber(idx);
468 nlayers = PCB->LayerGroups.Number[group];
469 if (group == GetLayerGroupNumberByNumber(component_silk_layer))
471 suff = "cmp";
473 else if (group == GetLayerGroupNumberByNumber(solder_silk_layer))
475 suff = "sol";
477 else if (nlayers == 1
478 && (strcmp (PCB->Data->Layer[idx].Name, "route") == 0 ||
479 strcmp (PCB->Data->Layer[idx].Name, "outline") == 0))
481 suff = "oln";
483 else
485 static char buf[20];
486 sprintf (buf, "ly%d", group);
487 suff = buf;
489 break;
492 strcpy (dest, suff);
495 static void
496 assign_file_suffix (char *dest, int idx)
498 int fns_style;
499 const char *sext = ".gbr";
501 switch (name_style)
503 default:
504 case NAME_STYLE_FIXED: fns_style = FNS_fixed; break;
505 case NAME_STYLE_SINGLE: fns_style = FNS_single; break;
506 case NAME_STYLE_FIRST: fns_style = FNS_first; break;
507 case NAME_STYLE_EAGLE:
508 assign_eagle_file_suffix (dest, idx);
509 return;
512 switch (idx)
514 case SL (PDRILL, 0):
515 sext = ".cnc";
516 break;
517 case SL (UDRILL, 0):
518 sext = ".cnc";
519 break;
522 strcpy (dest, layer_type_to_file_name (idx, fns_style));
523 strcat (dest, sext);
526 static void
527 gerber_do_export (HID_Attr_Val * options)
529 const char *fnbase;
530 int i;
531 static int saved_layer_stack[MAX_LAYER];
532 int save_ons[MAX_LAYER + 2];
533 FlagType save_thindraw;
535 save_thindraw = PCB->Flags;
536 CLEAR_FLAG(THINDRAWFLAG, PCB);
537 CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
538 CLEAR_FLAG(CHECKPLANESFLAG, PCB);
540 if (!options)
542 gerber_get_export_options (NULL);
543 for (i = 0; i < NUM_OPTIONS; i++)
544 gerber_values[i] = gerber_options[i].default_val;
545 options = gerber_values;
548 fnbase = options[HA_gerberfile].str_value;
549 if (!fnbase)
550 fnbase = "pcb-out";
552 verbose = options[HA_verbose].int_value;
553 metric = options[HA_metric].int_value;
554 if (metric) {
555 x_convspec = "X%.0mu";
556 y_convspec = "Y%.0mu";
557 } else {
558 x_convspec = "X%.0mc";
559 y_convspec = "Y%.0mc";
561 all_layers = options[HA_all_layers].int_value;
563 copy_outline_mode = options[HA_copy_outline].int_value;
564 name_style = options[HA_name_style].int_value;
566 outline_layer = NULL;
568 for (i = 0; i < max_copper_layer; i++)
570 LayerType *layer = PCB->Data->Layer + i;
571 if (strcmp (layer->Name, "outline") == 0 ||
572 strcmp (layer->Name, "route") == 0)
574 outline_layer = layer;
578 i = strlen (fnbase);
579 filename = (char *)realloc (filename, i + 40);
580 strcpy (filename, fnbase);
581 strcat (filename, ".");
582 filesuff = filename + strlen (filename);
584 if (all_layers)
586 memset (print_group, 1, sizeof (print_group));
587 memset (print_layer, 1, sizeof (print_layer));
589 else
591 memset (print_group, 0, sizeof (print_group));
592 memset (print_layer, 0, sizeof (print_layer));
595 hid_save_and_show_layer_ons (save_ons);
596 for (i = 0; i < max_copper_layer; i++)
598 LayerType *layer = PCB->Data->Layer + i;
599 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
600 print_group[GetLayerGroupNumberByNumber (i)] = 1;
602 print_group[GetLayerGroupNumberByNumber (solder_silk_layer)] = 1;
603 print_group[GetLayerGroupNumberByNumber (component_silk_layer)] = 1;
604 for (i = 0; i < max_copper_layer; i++)
605 if (print_group[GetLayerGroupNumberByNumber (i)])
606 print_layer[i] = 1;
608 memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
609 qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_sort);
610 linewidth = -1;
611 lastcap = -1;
612 lastgroup = -1;
614 region.X1 = 0;
615 region.Y1 = 0;
616 region.X2 = PCB->MaxWidth;
617 region.Y2 = PCB->MaxHeight;
619 pagecount = 1;
620 resetApertures ();
622 lastgroup = -1;
623 layer_list_idx = 0;
624 finding_apertures = 1;
625 hid_expose_callback (&gerber_hid, &region, 0);
627 layer_list_idx = 0;
628 finding_apertures = 0;
629 hid_expose_callback (&gerber_hid, &region, 0);
631 memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
633 maybe_close_f (f);
634 f = NULL;
635 hid_restore_layer_ons (save_ons);
636 PCB->Flags = save_thindraw;
639 static void
640 gerber_parse_arguments (int *argc, char ***argv)
642 hid_register_attributes (gerber_options, NUM_OPTIONS);
643 hid_parse_command_line (argc, argv);
646 static int
647 drill_sort (const void *va, const void *vb)
649 PendingDrills *a = (PendingDrills *) va;
650 PendingDrills *b = (PendingDrills *) vb;
651 if (a->diam != b->diam)
652 return a->diam - b->diam;
653 if (a->x != b->x)
654 return a->x - b->x;
655 return a->y - b->y;
658 static int
659 gerber_set_layer (const char *name, int group, int empty)
661 int want_outline;
662 char *cp;
663 int idx = (group >= 0
664 && group <
665 max_group) ? PCB->LayerGroups.Entries[group][0] : group;
667 if (name == NULL)
668 name = PCB->Data->Layer[idx].Name;
670 if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
671 return 0;
673 if (strcmp (name, "invisible") == 0)
674 return 0;
675 if (SL_TYPE (idx) == SL_ASSY)
676 return 0;
678 flash_drills = 0;
679 if (strcmp (name, "outline") == 0 ||
680 strcmp (name, "route") == 0)
681 flash_drills = 1;
683 if (is_drill && n_pending_drills)
685 int i;
686 /* dump pending drills in sequence */
687 qsort (pending_drills, n_pending_drills, sizeof (pending_drills[0]),
688 drill_sort);
689 for (i = 0; i < n_pending_drills; i++)
691 if (i == 0 || pending_drills[i].diam != pending_drills[i - 1].diam)
693 Aperture *ap = findAperture (curr_aptr_list, pending_drills[i].diam, ROUND);
694 fprintf (f, "T%02d\r\n", ap->dCode);
696 /* Notice the last zeroes are literal zeroes here, a x10 scale factor. *
697 * v v */
698 pcb_fprintf (f, metric ? "X%06.0muY%06.0mu\r\n" : "X%06.0ml0Y%06.0ml0\r\n",
699 gerberDrX (PCB, pending_drills[i].x),
700 gerberDrY (PCB, pending_drills[i].y));
702 free (pending_drills);
703 n_pending_drills = max_pending_drills = 0;
704 pending_drills = NULL;
707 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
708 is_mask = (SL_TYPE (idx) == SL_MASK);
709 current_mask = HID_MASK_OFF;
710 #if 0
711 printf ("Layer %s group %d drill %d mask %d\n", name, group, is_drill,
712 is_mask);
713 #endif
715 if (group < 0 || group != lastgroup)
717 time_t currenttime;
718 char utcTime[64];
719 #ifdef HAVE_GETPWUID
720 struct passwd *pwentry;
721 #endif
722 ApertureList *aptr_list;
723 Aperture *search;
725 lastgroup = group;
726 lastX = -1;
727 lastY = -1;
728 linewidth = -1;
729 lastcap = -1;
731 aptr_list = setLayerApertureList (layer_list_idx++);
733 if (finding_apertures)
734 goto emit_outline;
736 if (aptr_list->count == 0 && !all_layers)
737 return 0;
739 maybe_close_f (f);
740 f = NULL;
742 pagecount++;
743 assign_file_suffix (filesuff, idx);
744 f = fopen (filename, "wb"); /* Binary needed to force CR-LF */
745 if (f == NULL)
747 Message ( "Error: Could not open %s for writing.\n", filename);
748 return 1;
751 was_drill = is_drill;
753 if (verbose)
755 int c = aptr_list->count;
756 printf ("Gerber: %d aperture%s in %s\n", c,
757 c == 1 ? "" : "s", filename);
760 if (is_drill)
762 /* We omit the ,TZ here because we are not omitting trailing zeros. Our format is
763 always six-digit 0.1 mil or µm resolution (i.e. 001100 = 0.11" or 1.1mm)*/
764 fprintf (f, "M48\r\n");
765 fprintf (f, metric ? "METRIC,000.000\r\n" : "INCH\r\n");
766 for (search = aptr_list->data; search; search = search->next)
767 pcb_fprintf (f, metric ? "T%02dC%.3`mm\r\n" : "T%02dC%.3`mi\r\n", search->dCode, search->width);
768 fprintf (f, "%%\r\n");
769 /* FIXME */
770 return 1;
773 fprintf (f, "G04 start of page %d for group %d idx %d *\r\n",
774 pagecount, group, idx);
776 /* Create a portable timestamp. */
777 currenttime = time (NULL);
779 /* avoid gcc complaints */
780 const char *fmt = "%c UTC";
781 strftime (utcTime, sizeof utcTime, fmt, gmtime (&currenttime));
783 /* Print a cute file header at the beginning of each file. */
784 fprintf (f, "G04 Title: %s, %s *\r\n", UNKNOWN (PCB->Name),
785 UNKNOWN (name));
786 fprintf (f, "G04 Creator: %s " VERSION " *\r\n", Progname);
787 fprintf (f, "G04 CreationDate: %s *\r\n", utcTime);
789 #ifdef HAVE_GETPWUID
790 /* ID the user. */
791 pwentry = getpwuid (getuid ());
792 fprintf (f, "G04 For: %s *\r\n", pwentry->pw_name);
793 #endif
795 fprintf (f, "G04 Format: Gerber/RS-274X *\r\n");
796 pcb_fprintf (f, metric ? "G04 PCB-Dimensions (mm): %.2mm %.2mm *\r\n" :
797 "G04 PCB-Dimensions (mil): %.2ml %.2ml *\r\n",
798 PCB->MaxWidth, PCB->MaxHeight);
799 fprintf (f, "G04 PCB-Coordinate-Origin: lower left *\r\n");
801 /* Signal data in inches. */
802 fprintf (f, metric ? "%%MOMM*%%\r\n" : "%%MOIN*%%\r\n");
804 /* Signal Leading zero suppression, Absolute Data, 2.5 format in inch, 4.3 in mm */
805 fprintf (f, metric ? "%%FSLAX43Y43*%%\r\n" : "%%FSLAX25Y25*%%\r\n");
807 /* build a legal identifier. */
808 if (layername)
809 free (layername);
810 layername = strdup (filesuff);
811 if (strrchr (layername, '.'))
812 * strrchr (layername, '.') = 0;
814 for (cp=layername; *cp; cp++)
816 if (isalnum((int) *cp))
817 *cp = toupper((int) *cp);
818 else
819 *cp = '_';
821 fprintf (f, "%%LN%s*%%\r\n", layername);
822 lncount = 1;
824 for (search = aptr_list->data; search; search = search->next)
825 fprintAperture(f, search);
826 if (aptr_list->count == 0)
827 /* We need to put *something* in the file to make it be parsed
828 as RS-274X instead of RS-274D. */
829 fprintf (f, "%%ADD11C,0.0100*%%\r\n");
832 emit_outline:
833 /* If we're printing a copper layer other than the outline layer,
834 and we want to "print outlines", and we have an outline layer,
835 print the outline layer on this layer also. */
836 want_outline = 0;
837 if (copy_outline_mode == COPY_OUTLINE_MASK
838 && SL_TYPE (idx) == SL_MASK)
839 want_outline = 1;
840 if (copy_outline_mode == COPY_OUTLINE_SILK
841 && SL_TYPE (idx) == SL_SILK)
842 want_outline = 1;
843 if (copy_outline_mode == COPY_OUTLINE_ALL
844 && (SL_TYPE (idx) == SL_SILK
845 || SL_TYPE (idx) == SL_MASK
846 || SL_TYPE (idx) == SL_FAB
847 || SL_TYPE (idx) == SL_ASSY
848 || SL_TYPE (idx) == 0))
849 want_outline = 1;
851 if (want_outline
852 && strcmp (name, "outline")
853 && strcmp (name, "route"))
855 if (outline_layer
856 && outline_layer != PCB->Data->Layer+idx)
857 DrawLayer (outline_layer, &region);
858 else if (!outline_layer)
860 hidGC gc = gui->make_gc ();
861 printf("name %s idx %d\n", name, idx);
862 if (SL_TYPE (idx) == SL_SILK)
863 gui->set_line_width (gc, PCB->minSlk);
864 else if (group >= 0)
865 gui->set_line_width (gc, PCB->minWid);
866 else
867 gui->set_line_width (gc, AUTO_OUTLINE_WIDTH);
868 gui->draw_line (gc, 0, 0, PCB->MaxWidth, 0);
869 gui->draw_line (gc, 0, 0, 0, PCB->MaxHeight);
870 gui->draw_line (gc, PCB->MaxWidth, 0, PCB->MaxWidth, PCB->MaxHeight);
871 gui->draw_line (gc, 0, PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight);
872 gui->destroy_gc (gc);
876 return 1;
879 static hidGC
880 gerber_make_gc (void)
882 hidGC rv = (hidGC) calloc (1, sizeof (*rv));
883 rv->cap = Trace_Cap;
884 return rv;
887 static void
888 gerber_destroy_gc (hidGC gc)
890 free (gc);
893 static void
894 gerber_use_mask (enum mask_mode mode)
896 current_mask = mode;
899 static void
900 gerber_set_color (hidGC gc, const char *name)
902 if (strcmp (name, "erase") == 0)
904 gc->color = 1;
905 gc->erase = 1;
906 gc->drill = 0;
908 else if (strcmp (name, "drill") == 0)
910 gc->color = 1;
911 gc->erase = 0;
912 gc->drill = 1;
914 else
916 gc->color = 0;
917 gc->erase = 0;
918 gc->drill = 0;
922 static void
923 gerber_set_line_cap (hidGC gc, EndCapStyle style)
925 gc->cap = style;
928 static void
929 gerber_set_line_width (hidGC gc, Coord width)
931 gc->width = width;
934 static void
935 gerber_set_draw_xor (hidGC gc, int xor_)
940 static void
941 use_gc (hidGC gc, int radius)
943 if (radius)
945 radius *= 2;
946 if (radius != linewidth || lastcap != Round_Cap)
948 Aperture *aptr = findAperture (curr_aptr_list, radius, ROUND);
949 if (aptr == NULL)
950 pcb_fprintf (stderr, "error: aperture for radius %$mS type ROUND is null\n", radius);
951 else if (f && !is_drill)
952 fprintf (f, "G54D%d*", aptr->dCode);
953 linewidth = radius;
954 lastcap = Round_Cap;
957 else if (linewidth != gc->width || lastcap != gc->cap)
959 Aperture *aptr;
960 ApertureShape shape;
962 linewidth = gc->width;
963 lastcap = gc->cap;
964 switch (gc->cap)
966 case Round_Cap:
967 case Trace_Cap:
968 shape = ROUND;
969 break;
970 default:
971 case Square_Cap:
972 shape = SQUARE;
973 break;
975 aptr = findAperture (curr_aptr_list, linewidth, shape);
976 if (aptr == NULL)
977 pcb_fprintf (stderr, "error: aperture for width %$mS type %s is null\n",
978 linewidth, shape == ROUND ? "ROUND" : "SQUARE");
979 else if (f)
980 fprintf (f, "G54D%d*", aptr->dCode);
984 static void
985 gerber_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
987 gerber_draw_line (gc, x1, y1, x1, y2);
988 gerber_draw_line (gc, x1, y1, x2, y1);
989 gerber_draw_line (gc, x1, y2, x2, y2);
990 gerber_draw_line (gc, x2, y1, x2, y2);
993 static void
994 gerber_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
996 bool m = false;
998 if (x1 != x2 && y1 != y2 && gc->cap == Square_Cap)
1000 Coord x[5], y[5];
1001 double tx, ty, theta;
1003 theta = atan2 (y2-y1, x2-x1);
1005 /* T is a vector half a thickness long, in the direction of
1006 one of the corners. */
1007 tx = gc->width / 2.0 * cos (theta + M_PI/4) * sqrt(2.0);
1008 ty = gc->width / 2.0 * sin (theta + M_PI/4) * sqrt(2.0);
1010 x[0] = x1 - tx; y[0] = y1 - ty;
1011 x[1] = x2 + ty; y[1] = y2 - tx;
1012 x[2] = x2 + tx; y[2] = y2 + ty;
1013 x[3] = x1 - ty; y[3] = y1 + tx;
1015 x[4] = x[0]; y[4] = y[0];
1016 gerber_fill_polygon (gc, 5, x, y);
1017 return;
1020 use_gc (gc, 0);
1021 if (!f)
1022 return;
1024 if (x1 != lastX)
1026 m = true;
1027 lastX = x1;
1028 print_xcoord (f, PCB, lastX);
1030 if (y1 != lastY)
1032 m = true;
1033 lastY = y1;
1034 print_ycoord (f, PCB, lastY);
1036 if ((x1 == x2) && (y1 == y2))
1037 fprintf (f, "D03*\r\n");
1038 else
1040 if (m)
1041 fprintf (f, "D02*");
1042 if (x2 != lastX)
1044 lastX = x2;
1045 print_xcoord (f, PCB, lastX);
1047 if (y2 != lastY)
1049 lastY = y2;
1050 print_ycoord (f, PCB, lastY);
1052 fprintf (f, "D01*\r\n");
1057 static void
1058 gerber_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1059 Angle start_angle, Angle delta_angle)
1061 bool m = false;
1062 double arcStartX, arcStopX, arcStartY, arcStopY;
1064 /* we never draw zero-width lines */
1065 if (gc->width == 0)
1066 return;
1068 use_gc (gc, 0);
1069 if (!f)
1070 return;
1072 arcStartX = cx - width * cos (TO_RADIANS (start_angle));
1073 arcStartY = cy + height * sin (TO_RADIANS (start_angle));
1075 /* I checked three different gerber viewers, and they all disagreed
1076 on how ellipses should be drawn. The spec just calls G74/G75
1077 "circular interpolation" so there's a chance it just doesn't
1078 support ellipses at all. Thus, we draw them out with line
1079 segments. Note that most arcs in pcb are circles anyway. */
1080 if (width != height)
1082 double step, angle;
1083 Coord max = width > height ? width : height;
1084 Coord minr = max - gc->width / 10;
1085 int nsteps;
1086 Coord x0, y0, x1, y1;
1088 if (minr >= max)
1089 minr = max - 1;
1090 step = acos((double)minr/(double)max) * 180.0/M_PI;
1091 if (step > 5)
1092 step = 5;
1093 nsteps = abs(delta_angle) / step + 1;
1094 step = (double)delta_angle / nsteps;
1096 x0 = arcStartX;
1097 y0 = arcStartY;
1098 angle = start_angle;
1099 while (nsteps > 0)
1101 nsteps --;
1102 x1 = cx - width * cos (TO_RADIANS (angle+step));
1103 y1 = cy + height * sin (TO_RADIANS (angle+step));
1104 gerber_draw_line (gc, x0, y0, x1, y1);
1105 x0 = x1;
1106 y0 = y1;
1107 angle += step;
1109 return;
1112 arcStopX = cx - width * cos (TO_RADIANS (start_angle + delta_angle));
1113 arcStopY = cy + height * sin (TO_RADIANS (start_angle + delta_angle));
1114 if (arcStartX != lastX)
1116 m = true;
1117 lastX = arcStartX;
1118 print_xcoord (f, PCB, lastX);
1120 if (arcStartY != lastY)
1122 m = true;
1123 lastY = arcStartY;
1124 print_ycoord (f, PCB, lastY);
1126 if (m)
1127 fprintf (f, "D02*");
1128 pcb_fprintf (f,
1129 metric ? "G75*G0%1dX%.0muY%.0muI%.0muJ%.0muD01*G01*\r\n" :
1130 "G75*G0%1dX%.0mcY%.0mcI%.0mcJ%.0mcD01*G01*\r\n",
1131 (delta_angle < 0) ? 2 : 3,
1132 gerberX (PCB, arcStopX), gerberY (PCB, arcStopY),
1133 gerberXOffset (PCB, cx - arcStartX),
1134 gerberYOffset (PCB, cy - arcStartY));
1135 lastX = arcStopX;
1136 lastY = arcStopY;
1139 static void
1140 gerber_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1142 if (radius <= 0)
1143 return;
1144 if (is_drill)
1145 radius = 50 * round (radius / 50.0);
1146 use_gc (gc, radius);
1147 if (!f)
1148 return;
1149 if (is_drill)
1151 if (n_pending_drills >= max_pending_drills)
1153 max_pending_drills += 100;
1154 pending_drills = (PendingDrills *) realloc(pending_drills,
1155 max_pending_drills *
1156 sizeof (pending_drills[0]));
1158 pending_drills[n_pending_drills].x = cx;
1159 pending_drills[n_pending_drills].y = cy;
1160 pending_drills[n_pending_drills].diam = radius * 2;
1161 n_pending_drills++;
1162 return;
1164 else if (gc->drill && !flash_drills)
1165 return;
1166 if (cx != lastX)
1168 lastX = cx;
1169 print_xcoord (f, PCB, lastX);
1171 if (cy != lastY)
1173 lastY = cy;
1174 print_ycoord (f, PCB, lastY);
1176 fprintf (f, "D03*\r\n");
1179 static void
1180 gerber_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1182 bool m = false;
1183 int i;
1184 int firstTime = 1;
1185 Coord startX = 0, startY = 0;
1187 if (is_mask && current_mask == HID_MASK_BEFORE)
1188 return;
1190 use_gc (gc, 10 * 100);
1191 if (!f)
1192 return;
1193 fprintf (f, "G36*\r\n");
1194 for (i = 0; i < n_coords; i++)
1196 if (x[i] != lastX)
1198 m = true;
1199 lastX = x[i];
1200 print_xcoord (f, PCB, lastX);
1202 if (y[i] != lastY)
1204 m = true;
1205 lastY = y[i];
1206 print_ycoord (f, PCB, lastY);
1208 if (firstTime)
1210 firstTime = 0;
1211 startX = x[i];
1212 startY = y[i];
1213 if (m)
1214 fprintf (f, "D02*");
1216 else if (m)
1217 fprintf (f, "D01*\r\n");
1218 m = false;
1220 if (startX != lastX)
1222 m = true;
1223 lastX = startX;
1224 print_xcoord (f, PCB, startX);
1226 if (startY != lastY)
1228 m = true;
1229 lastY = startY;
1230 print_ycoord (f, PCB, lastY);
1232 if (m)
1233 fprintf (f, "D01*\r\n");
1234 fprintf (f, "G37*\r\n");
1237 static void
1238 gerber_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1240 Coord x[5];
1241 Coord y[5];
1242 x[0] = x[4] = x1;
1243 y[0] = y[4] = y1;
1244 x[1] = x1;
1245 y[1] = y2;
1246 x[2] = x2;
1247 y[2] = y2;
1248 x[3] = x2;
1249 y[3] = y1;
1250 gerber_fill_polygon (gc, 5, x, y);
1253 static void
1254 gerber_calibrate (double xval, double yval)
1256 CRASH;
1259 static void
1260 gerber_set_crosshair (int x, int y, int action)
1264 void
1265 hid_gerber_init ()
1267 memset (&gerber_hid, 0, sizeof (gerber_hid));
1269 common_nogui_init (&gerber_hid);
1270 common_draw_helpers_init (&gerber_hid);
1272 gerber_hid.struct_size = sizeof (gerber_hid);
1273 gerber_hid.name = "gerber";
1274 gerber_hid.description = "RS-274X (Gerber) export";
1275 gerber_hid.exporter = 1;
1277 gerber_hid.get_export_options = gerber_get_export_options;
1278 gerber_hid.do_export = gerber_do_export;
1279 gerber_hid.parse_arguments = gerber_parse_arguments;
1280 gerber_hid.set_layer = gerber_set_layer;
1281 gerber_hid.make_gc = gerber_make_gc;
1282 gerber_hid.destroy_gc = gerber_destroy_gc;
1283 gerber_hid.use_mask = gerber_use_mask;
1284 gerber_hid.set_color = gerber_set_color;
1285 gerber_hid.set_line_cap = gerber_set_line_cap;
1286 gerber_hid.set_line_width = gerber_set_line_width;
1287 gerber_hid.set_draw_xor = gerber_set_draw_xor;
1288 gerber_hid.draw_line = gerber_draw_line;
1289 gerber_hid.draw_arc = gerber_draw_arc;
1290 gerber_hid.draw_rect = gerber_draw_rect;
1291 gerber_hid.fill_circle = gerber_fill_circle;
1292 gerber_hid.fill_polygon = gerber_fill_polygon;
1293 gerber_hid.fill_rect = gerber_fill_rect;
1294 gerber_hid.calibrate = gerber_calibrate;
1295 gerber_hid.set_crosshair = gerber_set_crosshair;
1297 hid_register_hid (&gerber_hid);