HID: Move non-PCB specific drawing calls into a separate API
[geda-pcb/pcjc2.git] / src / hid / gerber / gerber.c
blobb884c51d3110e2f6ca38110ca919452c1d04a96f
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;
291 static HID_DRAW_API gerber_graphics;
293 typedef struct hid_gc_struct
295 EndCapStyle cap;
296 int width;
297 int color;
298 int erase;
299 int drill;
300 } hid_gc_struct;
302 static FILE *f = NULL;
303 static char *filename = NULL;
304 static char *filesuff = NULL;
305 static char *layername = NULL;
306 static int lncount = 0;
308 static int finding_apertures = 0;
309 static int pagecount = 0;
310 static int linewidth = -1;
311 static int lastgroup = -1;
312 static int lastcap = -1;
313 static int print_group[MAX_LAYER];
314 static int print_layer[MAX_LAYER];
315 static int lastX, lastY; /* the last X and Y coordinate */
317 static const char *copy_outline_names[] = {
318 #define COPY_OUTLINE_NONE 0
319 "none",
320 #define COPY_OUTLINE_MASK 1
321 "mask",
322 #define COPY_OUTLINE_SILK 2
323 "silk",
324 #define COPY_OUTLINE_ALL 3
325 "all",
326 NULL
329 static const char *name_style_names[] = {
330 #define NAME_STYLE_FIXED 0
331 "fixed",
332 #define NAME_STYLE_SINGLE 1
333 "single",
334 #define NAME_STYLE_FIRST 2
335 "first",
336 #define NAME_STYLE_EAGLE 3
337 "eagle",
338 NULL
341 static HID_Attribute gerber_options[] = {
343 /* %start-doc options "90 Gerber Export"
344 @ftable @code
345 @item --gerberfile <string>
346 Gerber output file prefix. Can include a path.
347 @end ftable
348 %end-doc
350 {"gerberfile", "Gerber output file base",
351 HID_String, 0, 0, {0, 0, 0}, 0, 0},
352 #define HA_gerberfile 0
354 /* %start-doc options "90 Gerber Export"
355 @ftable @code
356 @item --all-layers
357 Output contains all layers, even empty ones.
358 @end ftable
359 %end-doc
361 {"all-layers", "Output all layers, even empty ones",
362 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
363 #define HA_all_layers 1
365 /* %start-doc options "90 Gerber Export"
366 @ftable @code
367 @item --verbose
368 Print file names and aperture counts on stdout.
369 @end ftable
370 %end-doc
372 {"verbose", "Print file names and aperture counts on stdout",
373 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
374 #define HA_verbose 2
375 /* %start-doc options "90 Gerber Export"
376 @ftable @code
377 @item --metric
378 generate metric Gerber and drill files
379 @end ftable
380 %end-doc
382 {"metric", "Generate metric Gerber and drill files",
383 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
384 #define HA_metric 3
385 {"copy-outline", "Copy outline onto other layers",
386 HID_Enum, 0, 0, {0, 0, 0}, copy_outline_names, 0},
387 #define HA_copy_outline 4
388 {"name-style", "Naming style for individual gerber files",
389 HID_Enum, 0, 0, {0, 0, 0}, name_style_names, 0},
390 #define HA_name_style 5
393 #define NUM_OPTIONS (sizeof(gerber_options)/sizeof(gerber_options[0]))
395 static HID_Attr_Val gerber_values[NUM_OPTIONS];
397 static HID_Attribute *
398 gerber_get_export_options (int *n)
400 static char *last_made_filename = NULL;
401 if (PCB) derive_default_filename(PCB->Filename, &gerber_options[HA_gerberfile], "", &last_made_filename);
403 if (n)
404 *n = NUM_OPTIONS;
405 return gerber_options;
408 static int
409 group_for_layer (int l)
411 if (l < max_copper_layer + 2 && l >= 0)
412 return GetLayerGroupNumberByNumber (l);
413 /* else something unique */
414 return max_group + 3 + l;
417 static int
418 layer_sort (const void *va, const void *vb)
420 int a = *(int *) va;
421 int b = *(int *) vb;
422 int d = group_for_layer (b) - group_for_layer (a);
423 if (d)
424 return d;
425 return b - a;
428 static void
429 maybe_close_f (FILE *f)
431 if (f)
433 if (was_drill)
434 fprintf (f, "M30\r\n");
435 else
436 fprintf (f, "M02*\r\n");
437 fclose (f);
441 static BoxType region;
443 /* Very similar to layer_type_to_file_name() but appends only a
444 three-character suffix compatible with Eagle's defaults. */
445 static void
446 assign_eagle_file_suffix (char *dest, int idx)
448 int group;
449 int nlayers;
450 char *suff = "out";
452 switch (idx)
454 case SL (SILK, TOP): suff = "plc"; break;
455 case SL (SILK, BOTTOM): suff = "pls"; break;
456 case SL (MASK, TOP): suff = "stc"; break;
457 case SL (MASK, BOTTOM): suff = "sts"; break;
458 case SL (PDRILL, 0): suff = "drd"; break;
459 case SL (UDRILL, 0): suff = "dru"; break;
460 case SL (PASTE, TOP): suff = "crc"; break;
461 case SL (PASTE, BOTTOM): suff = "crs"; break;
462 case SL (INVISIBLE, 0): suff = "inv"; break;
463 case SL (FAB, 0): suff = "fab"; break;
464 case SL (ASSY, TOP): suff = "ast"; break;
465 case SL (ASSY, BOTTOM): suff = "asb"; break;
467 default:
468 group = GetLayerGroupNumberByNumber(idx);
469 nlayers = PCB->LayerGroups.Number[group];
470 if (group == GetLayerGroupNumberByNumber(component_silk_layer))
472 suff = "cmp";
474 else if (group == GetLayerGroupNumberByNumber(solder_silk_layer))
476 suff = "sol";
478 else if (nlayers == 1
479 && (strcmp (PCB->Data->Layer[idx].Name, "route") == 0 ||
480 strcmp (PCB->Data->Layer[idx].Name, "outline") == 0))
482 suff = "oln";
484 else
486 static char buf[20];
487 sprintf (buf, "ly%d", group);
488 suff = buf;
490 break;
493 strcpy (dest, suff);
496 static void
497 assign_file_suffix (char *dest, int idx)
499 int fns_style;
500 const char *sext = ".gbr";
502 switch (name_style)
504 default:
505 case NAME_STYLE_FIXED: fns_style = FNS_fixed; break;
506 case NAME_STYLE_SINGLE: fns_style = FNS_single; break;
507 case NAME_STYLE_FIRST: fns_style = FNS_first; break;
508 case NAME_STYLE_EAGLE:
509 assign_eagle_file_suffix (dest, idx);
510 return;
513 switch (idx)
515 case SL (PDRILL, 0):
516 sext = ".cnc";
517 break;
518 case SL (UDRILL, 0):
519 sext = ".cnc";
520 break;
523 strcpy (dest, layer_type_to_file_name (idx, fns_style));
524 strcat (dest, sext);
527 static void
528 gerber_do_export (HID_Attr_Val * options)
530 const char *fnbase;
531 int i;
532 static int saved_layer_stack[MAX_LAYER];
533 int save_ons[MAX_LAYER + 2];
534 FlagType save_thindraw;
536 save_thindraw = PCB->Flags;
537 CLEAR_FLAG(THINDRAWFLAG, PCB);
538 CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
539 CLEAR_FLAG(CHECKPLANESFLAG, PCB);
541 if (!options)
543 gerber_get_export_options (NULL);
544 for (i = 0; i < NUM_OPTIONS; i++)
545 gerber_values[i] = gerber_options[i].default_val;
546 options = gerber_values;
549 fnbase = options[HA_gerberfile].str_value;
550 if (!fnbase)
551 fnbase = "pcb-out";
553 verbose = options[HA_verbose].int_value;
554 metric = options[HA_metric].int_value;
555 if (metric) {
556 x_convspec = "X%.0mu";
557 y_convspec = "Y%.0mu";
558 } else {
559 x_convspec = "X%.0mc";
560 y_convspec = "Y%.0mc";
562 all_layers = options[HA_all_layers].int_value;
564 copy_outline_mode = options[HA_copy_outline].int_value;
565 name_style = options[HA_name_style].int_value;
567 outline_layer = NULL;
569 for (i = 0; i < max_copper_layer; i++)
571 LayerType *layer = PCB->Data->Layer + i;
572 if (strcmp (layer->Name, "outline") == 0 ||
573 strcmp (layer->Name, "route") == 0)
575 outline_layer = layer;
579 i = strlen (fnbase);
580 filename = (char *)realloc (filename, i + 40);
581 strcpy (filename, fnbase);
582 strcat (filename, ".");
583 filesuff = filename + strlen (filename);
585 if (all_layers)
587 memset (print_group, 1, sizeof (print_group));
588 memset (print_layer, 1, sizeof (print_layer));
590 else
592 memset (print_group, 0, sizeof (print_group));
593 memset (print_layer, 0, sizeof (print_layer));
596 hid_save_and_show_layer_ons (save_ons);
597 for (i = 0; i < max_copper_layer; i++)
599 LayerType *layer = PCB->Data->Layer + i;
600 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
601 print_group[GetLayerGroupNumberByNumber (i)] = 1;
603 print_group[GetLayerGroupNumberByNumber (solder_silk_layer)] = 1;
604 print_group[GetLayerGroupNumberByNumber (component_silk_layer)] = 1;
605 for (i = 0; i < max_copper_layer; i++)
606 if (print_group[GetLayerGroupNumberByNumber (i)])
607 print_layer[i] = 1;
609 memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
610 qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_sort);
611 linewidth = -1;
612 lastcap = -1;
613 lastgroup = -1;
615 region.X1 = 0;
616 region.Y1 = 0;
617 region.X2 = PCB->MaxWidth;
618 region.Y2 = PCB->MaxHeight;
620 pagecount = 1;
621 resetApertures ();
623 lastgroup = -1;
624 layer_list_idx = 0;
625 finding_apertures = 1;
626 hid_expose_callback (&gerber_hid, &region, 0);
628 layer_list_idx = 0;
629 finding_apertures = 0;
630 hid_expose_callback (&gerber_hid, &region, 0);
632 memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
634 maybe_close_f (f);
635 f = NULL;
636 hid_restore_layer_ons (save_ons);
637 PCB->Flags = save_thindraw;
640 static void
641 gerber_parse_arguments (int *argc, char ***argv)
643 hid_register_attributes (gerber_options, NUM_OPTIONS);
644 hid_parse_command_line (argc, argv);
647 static int
648 drill_sort (const void *va, const void *vb)
650 PendingDrills *a = (PendingDrills *) va;
651 PendingDrills *b = (PendingDrills *) vb;
652 if (a->diam != b->diam)
653 return a->diam - b->diam;
654 if (a->x != b->x)
655 return a->x - b->x;
656 return a->y - b->y;
659 static int
660 gerber_set_layer (const char *name, int group, int empty)
662 int want_outline;
663 char *cp;
664 int idx = (group >= 0
665 && group <
666 max_group) ? PCB->LayerGroups.Entries[group][0] : group;
668 if (name == NULL)
669 name = PCB->Data->Layer[idx].Name;
671 if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
672 return 0;
674 if (strcmp (name, "invisible") == 0)
675 return 0;
676 if (SL_TYPE (idx) == SL_ASSY)
677 return 0;
679 flash_drills = 0;
680 if (strcmp (name, "outline") == 0 ||
681 strcmp (name, "route") == 0)
682 flash_drills = 1;
684 if (is_drill && n_pending_drills)
686 int i;
687 /* dump pending drills in sequence */
688 qsort (pending_drills, n_pending_drills, sizeof (pending_drills[0]),
689 drill_sort);
690 for (i = 0; i < n_pending_drills; i++)
692 if (i == 0 || pending_drills[i].diam != pending_drills[i - 1].diam)
694 Aperture *ap = findAperture (curr_aptr_list, pending_drills[i].diam, ROUND);
695 fprintf (f, "T%02d\r\n", ap->dCode);
697 /* Notice the last zeroes are literal zeroes here, a x10 scale factor. *
698 * v v */
699 pcb_fprintf (f, metric ? "X%06.0muY%06.0mu\r\n" : "X%06.0ml0Y%06.0ml0\r\n",
700 gerberDrX (PCB, pending_drills[i].x),
701 gerberDrY (PCB, pending_drills[i].y));
703 free (pending_drills);
704 n_pending_drills = max_pending_drills = 0;
705 pending_drills = NULL;
708 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
709 is_mask = (SL_TYPE (idx) == SL_MASK);
710 current_mask = HID_MASK_OFF;
711 #if 0
712 printf ("Layer %s group %d drill %d mask %d\n", name, group, is_drill,
713 is_mask);
714 #endif
716 if (group < 0 || group != lastgroup)
718 time_t currenttime;
719 char utcTime[64];
720 #ifdef HAVE_GETPWUID
721 struct passwd *pwentry;
722 #endif
723 ApertureList *aptr_list;
724 Aperture *search;
726 lastgroup = group;
727 lastX = -1;
728 lastY = -1;
729 linewidth = -1;
730 lastcap = -1;
732 aptr_list = setLayerApertureList (layer_list_idx++);
734 if (finding_apertures)
735 goto emit_outline;
737 if (aptr_list->count == 0 && !all_layers)
738 return 0;
740 maybe_close_f (f);
741 f = NULL;
743 pagecount++;
744 assign_file_suffix (filesuff, idx);
745 f = fopen (filename, "wb"); /* Binary needed to force CR-LF */
746 if (f == NULL)
748 Message ( "Error: Could not open %s for writing.\n", filename);
749 return 1;
752 was_drill = is_drill;
754 if (verbose)
756 int c = aptr_list->count;
757 printf ("Gerber: %d aperture%s in %s\n", c,
758 c == 1 ? "" : "s", filename);
761 if (is_drill)
763 /* We omit the ,TZ here because we are not omitting trailing zeros. Our format is
764 always six-digit 0.1 mil or µm resolution (i.e. 001100 = 0.11" or 1.1mm)*/
765 fprintf (f, "M48\r\n");
766 fprintf (f, metric ? "METRIC,000.000\r\n" : "INCH\r\n");
767 for (search = aptr_list->data; search; search = search->next)
768 pcb_fprintf (f, metric ? "T%02dC%.3`mm\r\n" : "T%02dC%.3`mi\r\n", search->dCode, search->width);
769 fprintf (f, "%%\r\n");
770 /* FIXME */
771 return 1;
774 fprintf (f, "G04 start of page %d for group %d idx %d *\r\n",
775 pagecount, group, idx);
777 /* Create a portable timestamp. */
778 currenttime = time (NULL);
780 /* avoid gcc complaints */
781 const char *fmt = "%c UTC";
782 strftime (utcTime, sizeof utcTime, fmt, gmtime (&currenttime));
784 /* Print a cute file header at the beginning of each file. */
785 fprintf (f, "G04 Title: %s, %s *\r\n", UNKNOWN (PCB->Name),
786 UNKNOWN (name));
787 fprintf (f, "G04 Creator: %s " VERSION " *\r\n", Progname);
788 fprintf (f, "G04 CreationDate: %s *\r\n", utcTime);
790 #ifdef HAVE_GETPWUID
791 /* ID the user. */
792 pwentry = getpwuid (getuid ());
793 fprintf (f, "G04 For: %s *\r\n", pwentry->pw_name);
794 #endif
796 fprintf (f, "G04 Format: Gerber/RS-274X *\r\n");
797 pcb_fprintf (f, metric ? "G04 PCB-Dimensions (mm): %.2mm %.2mm *\r\n" :
798 "G04 PCB-Dimensions (mil): %.2ml %.2ml *\r\n",
799 PCB->MaxWidth, PCB->MaxHeight);
800 fprintf (f, "G04 PCB-Coordinate-Origin: lower left *\r\n");
802 /* Signal data in inches. */
803 fprintf (f, metric ? "%%MOMM*%%\r\n" : "%%MOIN*%%\r\n");
805 /* Signal Leading zero suppression, Absolute Data, 2.5 format in inch, 4.3 in mm */
806 fprintf (f, metric ? "%%FSLAX43Y43*%%\r\n" : "%%FSLAX25Y25*%%\r\n");
808 /* build a legal identifier. */
809 if (layername)
810 free (layername);
811 layername = strdup (filesuff);
812 if (strrchr (layername, '.'))
813 * strrchr (layername, '.') = 0;
815 for (cp=layername; *cp; cp++)
817 if (isalnum((int) *cp))
818 *cp = toupper((int) *cp);
819 else
820 *cp = '_';
822 fprintf (f, "%%LN%s*%%\r\n", layername);
823 lncount = 1;
825 for (search = aptr_list->data; search; search = search->next)
826 fprintAperture(f, search);
827 if (aptr_list->count == 0)
828 /* We need to put *something* in the file to make it be parsed
829 as RS-274X instead of RS-274D. */
830 fprintf (f, "%%ADD11C,0.0100*%%\r\n");
833 emit_outline:
834 /* If we're printing a copper layer other than the outline layer,
835 and we want to "print outlines", and we have an outline layer,
836 print the outline layer on this layer also. */
837 want_outline = 0;
838 if (copy_outline_mode == COPY_OUTLINE_MASK
839 && SL_TYPE (idx) == SL_MASK)
840 want_outline = 1;
841 if (copy_outline_mode == COPY_OUTLINE_SILK
842 && SL_TYPE (idx) == SL_SILK)
843 want_outline = 1;
844 if (copy_outline_mode == COPY_OUTLINE_ALL
845 && (SL_TYPE (idx) == SL_SILK
846 || SL_TYPE (idx) == SL_MASK
847 || SL_TYPE (idx) == SL_FAB
848 || SL_TYPE (idx) == SL_ASSY
849 || SL_TYPE (idx) == 0))
850 want_outline = 1;
852 if (want_outline
853 && strcmp (name, "outline")
854 && strcmp (name, "route"))
856 if (outline_layer
857 && outline_layer != PCB->Data->Layer+idx)
858 DrawLayer (outline_layer, &region);
859 else if (!outline_layer)
861 hidGC gc = gui->graphics->make_gc ();
862 printf("name %s idx %d\n", name, idx);
863 if (SL_TYPE (idx) == SL_SILK)
864 gui->graphics->set_line_width (gc, PCB->minSlk);
865 else if (group >= 0)
866 gui->graphics->set_line_width (gc, PCB->minWid);
867 else
868 gui->graphics->set_line_width (gc, AUTO_OUTLINE_WIDTH);
869 gui->graphics->draw_line (gc, 0, 0, PCB->MaxWidth, 0);
870 gui->graphics->draw_line (gc, 0, 0, 0, PCB->MaxHeight);
871 gui->graphics->draw_line (gc, PCB->MaxWidth, 0, PCB->MaxWidth, PCB->MaxHeight);
872 gui->graphics->draw_line (gc, 0, PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight);
873 gui->graphics->destroy_gc (gc);
877 return 1;
880 static hidGC
881 gerber_make_gc (void)
883 hidGC rv = (hidGC) calloc (1, sizeof (*rv));
884 rv->cap = Trace_Cap;
885 return rv;
888 static void
889 gerber_destroy_gc (hidGC gc)
891 free (gc);
894 static void
895 gerber_use_mask (enum mask_mode mode)
897 current_mask = mode;
900 static void
901 gerber_set_color (hidGC gc, const char *name)
903 if (strcmp (name, "erase") == 0)
905 gc->color = 1;
906 gc->erase = 1;
907 gc->drill = 0;
909 else if (strcmp (name, "drill") == 0)
911 gc->color = 1;
912 gc->erase = 0;
913 gc->drill = 1;
915 else
917 gc->color = 0;
918 gc->erase = 0;
919 gc->drill = 0;
923 static void
924 gerber_set_line_cap (hidGC gc, EndCapStyle style)
926 gc->cap = style;
929 static void
930 gerber_set_line_width (hidGC gc, Coord width)
932 gc->width = width;
935 static void
936 gerber_set_draw_xor (hidGC gc, int xor_)
941 static void
942 use_gc (hidGC gc, int radius)
944 if (radius)
946 radius *= 2;
947 if (radius != linewidth || lastcap != Round_Cap)
949 Aperture *aptr = findAperture (curr_aptr_list, radius, ROUND);
950 if (aptr == NULL)
951 pcb_fprintf (stderr, "error: aperture for radius %$mS type ROUND is null\n", radius);
952 else if (f && !is_drill)
953 fprintf (f, "G54D%d*", aptr->dCode);
954 linewidth = radius;
955 lastcap = Round_Cap;
958 else if (linewidth != gc->width || lastcap != gc->cap)
960 Aperture *aptr;
961 ApertureShape shape;
963 linewidth = gc->width;
964 lastcap = gc->cap;
965 switch (gc->cap)
967 case Round_Cap:
968 case Trace_Cap:
969 shape = ROUND;
970 break;
971 default:
972 case Square_Cap:
973 shape = SQUARE;
974 break;
976 aptr = findAperture (curr_aptr_list, linewidth, shape);
977 if (aptr == NULL)
978 pcb_fprintf (stderr, "error: aperture for width %$mS type %s is null\n",
979 linewidth, shape == ROUND ? "ROUND" : "SQUARE");
980 else if (f)
981 fprintf (f, "G54D%d*", aptr->dCode);
985 static void
986 gerber_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
988 gerber_draw_line (gc, x1, y1, x1, y2);
989 gerber_draw_line (gc, x1, y1, x2, y1);
990 gerber_draw_line (gc, x1, y2, x2, y2);
991 gerber_draw_line (gc, x2, y1, x2, y2);
994 static void
995 gerber_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
997 bool m = false;
999 if (x1 != x2 && y1 != y2 && gc->cap == Square_Cap)
1001 Coord x[5], y[5];
1002 double tx, ty, theta;
1004 theta = atan2 (y2-y1, x2-x1);
1006 /* T is a vector half a thickness long, in the direction of
1007 one of the corners. */
1008 tx = gc->width / 2.0 * cos (theta + M_PI/4) * sqrt(2.0);
1009 ty = gc->width / 2.0 * sin (theta + M_PI/4) * sqrt(2.0);
1011 x[0] = x1 - tx; y[0] = y1 - ty;
1012 x[1] = x2 + ty; y[1] = y2 - tx;
1013 x[2] = x2 + tx; y[2] = y2 + ty;
1014 x[3] = x1 - ty; y[3] = y1 + tx;
1016 x[4] = x[0]; y[4] = y[0];
1017 gerber_fill_polygon (gc, 5, x, y);
1018 return;
1021 use_gc (gc, 0);
1022 if (!f)
1023 return;
1025 if (x1 != lastX)
1027 m = true;
1028 lastX = x1;
1029 print_xcoord (f, PCB, lastX);
1031 if (y1 != lastY)
1033 m = true;
1034 lastY = y1;
1035 print_ycoord (f, PCB, lastY);
1037 if ((x1 == x2) && (y1 == y2))
1038 fprintf (f, "D03*\r\n");
1039 else
1041 if (m)
1042 fprintf (f, "D02*");
1043 if (x2 != lastX)
1045 lastX = x2;
1046 print_xcoord (f, PCB, lastX);
1048 if (y2 != lastY)
1050 lastY = y2;
1051 print_ycoord (f, PCB, lastY);
1053 fprintf (f, "D01*\r\n");
1058 static void
1059 gerber_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1060 Angle start_angle, Angle delta_angle)
1062 bool m = false;
1063 double arcStartX, arcStopX, arcStartY, arcStopY;
1065 /* we never draw zero-width lines */
1066 if (gc->width == 0)
1067 return;
1069 use_gc (gc, 0);
1070 if (!f)
1071 return;
1073 arcStartX = cx - width * cos (TO_RADIANS (start_angle));
1074 arcStartY = cy + height * sin (TO_RADIANS (start_angle));
1076 /* I checked three different gerber viewers, and they all disagreed
1077 on how ellipses should be drawn. The spec just calls G74/G75
1078 "circular interpolation" so there's a chance it just doesn't
1079 support ellipses at all. Thus, we draw them out with line
1080 segments. Note that most arcs in pcb are circles anyway. */
1081 if (width != height)
1083 double step, angle;
1084 Coord max = width > height ? width : height;
1085 Coord minr = max - gc->width / 10;
1086 int nsteps;
1087 Coord x0, y0, x1, y1;
1089 if (minr >= max)
1090 minr = max - 1;
1091 step = acos((double)minr/(double)max) * 180.0/M_PI;
1092 if (step > 5)
1093 step = 5;
1094 nsteps = abs(delta_angle) / step + 1;
1095 step = (double)delta_angle / nsteps;
1097 x0 = arcStartX;
1098 y0 = arcStartY;
1099 angle = start_angle;
1100 while (nsteps > 0)
1102 nsteps --;
1103 x1 = cx - width * cos (TO_RADIANS (angle+step));
1104 y1 = cy + height * sin (TO_RADIANS (angle+step));
1105 gerber_draw_line (gc, x0, y0, x1, y1);
1106 x0 = x1;
1107 y0 = y1;
1108 angle += step;
1110 return;
1113 arcStopX = cx - width * cos (TO_RADIANS (start_angle + delta_angle));
1114 arcStopY = cy + height * sin (TO_RADIANS (start_angle + delta_angle));
1115 if (arcStartX != lastX)
1117 m = true;
1118 lastX = arcStartX;
1119 print_xcoord (f, PCB, lastX);
1121 if (arcStartY != lastY)
1123 m = true;
1124 lastY = arcStartY;
1125 print_ycoord (f, PCB, lastY);
1127 if (m)
1128 fprintf (f, "D02*");
1129 pcb_fprintf (f,
1130 metric ? "G75*G0%1dX%.0muY%.0muI%.0muJ%.0muD01*G01*\r\n" :
1131 "G75*G0%1dX%.0mcY%.0mcI%.0mcJ%.0mcD01*G01*\r\n",
1132 (delta_angle < 0) ? 2 : 3,
1133 gerberX (PCB, arcStopX), gerberY (PCB, arcStopY),
1134 gerberXOffset (PCB, cx - arcStartX),
1135 gerberYOffset (PCB, cy - arcStartY));
1136 lastX = arcStopX;
1137 lastY = arcStopY;
1140 static void
1141 gerber_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1143 if (radius <= 0)
1144 return;
1145 if (is_drill)
1146 radius = 50 * round (radius / 50.0);
1147 use_gc (gc, radius);
1148 if (!f)
1149 return;
1150 if (is_drill)
1152 if (n_pending_drills >= max_pending_drills)
1154 max_pending_drills += 100;
1155 pending_drills = (PendingDrills *) realloc(pending_drills,
1156 max_pending_drills *
1157 sizeof (pending_drills[0]));
1159 pending_drills[n_pending_drills].x = cx;
1160 pending_drills[n_pending_drills].y = cy;
1161 pending_drills[n_pending_drills].diam = radius * 2;
1162 n_pending_drills++;
1163 return;
1165 else if (gc->drill && !flash_drills)
1166 return;
1167 if (cx != lastX)
1169 lastX = cx;
1170 print_xcoord (f, PCB, lastX);
1172 if (cy != lastY)
1174 lastY = cy;
1175 print_ycoord (f, PCB, lastY);
1177 fprintf (f, "D03*\r\n");
1180 static void
1181 gerber_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1183 bool m = false;
1184 int i;
1185 int firstTime = 1;
1186 Coord startX = 0, startY = 0;
1188 if (is_mask && current_mask == HID_MASK_BEFORE)
1189 return;
1191 use_gc (gc, 10 * 100);
1192 if (!f)
1193 return;
1194 fprintf (f, "G36*\r\n");
1195 for (i = 0; i < n_coords; i++)
1197 if (x[i] != lastX)
1199 m = true;
1200 lastX = x[i];
1201 print_xcoord (f, PCB, lastX);
1203 if (y[i] != lastY)
1205 m = true;
1206 lastY = y[i];
1207 print_ycoord (f, PCB, lastY);
1209 if (firstTime)
1211 firstTime = 0;
1212 startX = x[i];
1213 startY = y[i];
1214 if (m)
1215 fprintf (f, "D02*");
1217 else if (m)
1218 fprintf (f, "D01*\r\n");
1219 m = false;
1221 if (startX != lastX)
1223 m = true;
1224 lastX = startX;
1225 print_xcoord (f, PCB, startX);
1227 if (startY != lastY)
1229 m = true;
1230 lastY = startY;
1231 print_ycoord (f, PCB, lastY);
1233 if (m)
1234 fprintf (f, "D01*\r\n");
1235 fprintf (f, "G37*\r\n");
1238 static void
1239 gerber_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1241 Coord x[5];
1242 Coord y[5];
1243 x[0] = x[4] = x1;
1244 y[0] = y[4] = y1;
1245 x[1] = x1;
1246 y[1] = y2;
1247 x[2] = x2;
1248 y[2] = y2;
1249 x[3] = x2;
1250 y[3] = y1;
1251 gerber_fill_polygon (gc, 5, x, y);
1254 static void
1255 gerber_calibrate (double xval, double yval)
1257 CRASH;
1260 static void
1261 gerber_set_crosshair (int x, int y, int action)
1265 void
1266 hid_gerber_init ()
1268 memset (&gerber_hid, 0, sizeof (gerber_hid));
1269 memset (&gerber_graphics, 0, sizeof (gerber_graphics));
1271 common_nogui_init (&gerber_hid);
1272 common_draw_helpers_init (&gerber_hid);
1274 gerber_hid.struct_size = sizeof (gerber_hid);
1275 gerber_hid.name = "gerber";
1276 gerber_hid.description = "RS-274X (Gerber) export";
1277 gerber_hid.exporter = 1;
1279 gerber_hid.get_export_options = gerber_get_export_options;
1280 gerber_hid.do_export = gerber_do_export;
1281 gerber_hid.parse_arguments = gerber_parse_arguments;
1282 gerber_hid.set_layer = gerber_set_layer;
1283 gerber_hid.calibrate = gerber_calibrate;
1284 gerber_hid.set_crosshair = gerber_set_crosshair;
1286 gerber_hid.graphics = &gerber_graphics;
1288 gerber_graphics.make_gc = gerber_make_gc;
1289 gerber_graphics.destroy_gc = gerber_destroy_gc;
1290 gerber_graphics.use_mask = gerber_use_mask;
1291 gerber_graphics.set_color = gerber_set_color;
1292 gerber_graphics.set_line_cap = gerber_set_line_cap;
1293 gerber_graphics.set_line_width = gerber_set_line_width;
1294 gerber_graphics.set_draw_xor = gerber_set_draw_xor;
1295 gerber_graphics.draw_line = gerber_draw_line;
1296 gerber_graphics.draw_arc = gerber_draw_arc;
1297 gerber_graphics.draw_rect = gerber_draw_rect;
1298 gerber_graphics.fill_circle = gerber_fill_circle;
1299 gerber_graphics.fill_polygon = gerber_fill_polygon;
1300 gerber_graphics.fill_rect = gerber_fill_rect;
1302 hid_register_hid (&gerber_hid);