TEMP: Fixup fallout from nogui change
[geda-pcb/pcjc2.git] / src / hid / gerber / gerber.c
blob88393ef9b54250873119905c2dcdcfdae501cf2b
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;
295 static HID_DRAW_CLASS gerber_graphics_class;
297 typedef struct gerber_gc_struct
299 struct hid_gc_struct hid_gc; /* Parent */
301 EndCapStyle cap;
302 int width;
303 int color;
304 int erase;
305 int drill;
306 } *gerberGC;
308 static FILE *f = NULL;
309 static char *filename = NULL;
310 static char *filesuff = NULL;
311 static char *layername = NULL;
312 static int lncount = 0;
314 static int finding_apertures = 0;
315 static int pagecount = 0;
316 static int linewidth = -1;
317 static int lastgroup = -1;
318 static int lastcap = -1;
319 static int print_group[MAX_GROUP];
320 static int print_layer[MAX_LAYER];
321 static int lastX, lastY; /* the last X and Y coordinate */
323 static const char *copy_outline_names[] = {
324 #define COPY_OUTLINE_NONE 0
325 "none",
326 #define COPY_OUTLINE_MASK 1
327 "mask",
328 #define COPY_OUTLINE_SILK 2
329 "silk",
330 #define COPY_OUTLINE_ALL 3
331 "all",
332 NULL
335 static const char *name_style_names[] = {
336 #define NAME_STYLE_FIXED 0
337 "fixed",
338 #define NAME_STYLE_SINGLE 1
339 "single",
340 #define NAME_STYLE_FIRST 2
341 "first",
342 #define NAME_STYLE_EAGLE 3
343 "eagle",
344 #define NAME_STYLE_HACKVANA 4
345 "hackvana",
346 NULL
349 static HID_Attribute gerber_options[] = {
351 /* %start-doc options "90 Gerber Export"
352 @ftable @code
353 @item --gerberfile <string>
354 Gerber output file prefix. Can include a path.
355 @end ftable
356 %end-doc
358 {"gerberfile", "Gerber output file base",
359 HID_String, 0, 0, {0, 0, 0}, 0, 0},
360 #define HA_gerberfile 0
362 /* %start-doc options "90 Gerber Export"
363 @ftable @code
364 @item --all-layers
365 Output contains all layers, even empty ones.
366 @end ftable
367 %end-doc
369 {"all-layers", "Output all layers, even empty ones",
370 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
371 #define HA_all_layers 1
373 /* %start-doc options "90 Gerber Export"
374 @ftable @code
375 @item --verbose
376 Print file names and aperture counts on stdout.
377 @end ftable
378 %end-doc
380 {"verbose", "Print file names and aperture counts on stdout",
381 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
382 #define HA_verbose 2
383 /* %start-doc options "90 Gerber Export"
384 @ftable @code
385 @item --metric
386 generate metric Gerber and drill files
387 @end ftable
388 %end-doc
390 {"metric", "Generate metric Gerber and drill files",
391 HID_Boolean, 0, 0, {0, 0, 0}, 0, 0},
392 #define HA_metric 3
393 {"copy-outline", "Copy outline onto other layers",
394 HID_Enum, 0, 0, {0, 0, 0}, copy_outline_names, 0},
395 #define HA_copy_outline 4
396 {"name-style", "Naming style for individual gerber files",
397 HID_Enum, 0, 0, {0, 0, 0}, name_style_names, 0},
398 #define HA_name_style 5
401 #define NUM_OPTIONS (sizeof(gerber_options)/sizeof(gerber_options[0]))
403 static HID_Attr_Val gerber_values[NUM_OPTIONS];
405 static HID_Attribute *
406 gerber_get_export_options (int *n)
408 static char *last_made_filename = NULL;
409 if (PCB) derive_default_filename(PCB->Filename, &gerber_options[HA_gerberfile], "", &last_made_filename);
411 if (n)
412 *n = NUM_OPTIONS;
413 return gerber_options;
416 static int
417 layer_stack_sort (const void *va, const void *vb)
419 int a_layer = *(int *) va;
420 int b_layer = *(int *) vb;
421 int a_group = GetLayerGroupNumberByNumber (a_layer);
422 int b_group = GetLayerGroupNumberByNumber (b_layer);
424 if (b_group != a_group)
425 return b_group - a_group;
427 return b_layer - a_layer;
430 static void
431 maybe_close_f (FILE *f)
433 if (f)
435 if (was_drill)
436 fprintf (f, "M30\r\n");
437 else
438 fprintf (f, "M02*\r\n");
439 fclose (f);
443 static BoxType region;
445 /* Very similar to layer_type_to_file_name() but appends only a
446 three-character suffix compatible with Eagle's defaults. */
447 static void
448 assign_eagle_file_suffix (char *dest, int idx)
450 int group;
451 int nlayers;
452 char *suff = "out";
454 switch (idx)
456 case SL (SILK, TOP): suff = "plc"; break;
457 case SL (SILK, BOTTOM): suff = "pls"; break;
458 case SL (MASK, TOP): suff = "stc"; break;
459 case SL (MASK, BOTTOM): suff = "sts"; break;
460 case SL (PDRILL, 0): suff = "drd"; break;
461 case SL (UDRILL, 0): suff = "dru"; break;
462 case SL (PASTE, TOP): suff = "crc"; break;
463 case SL (PASTE, BOTTOM): suff = "crs"; break;
464 case SL (INVISIBLE, 0): suff = "inv"; break;
465 case SL (FAB, 0): suff = "fab"; break;
466 case SL (ASSY, TOP): suff = "ast"; break;
467 case SL (ASSY, BOTTOM): suff = "asb"; break;
469 default:
470 group = GetLayerGroupNumberByNumber(idx);
471 nlayers = PCB->LayerGroups.Number[group];
472 if (group == GetLayerGroupNumberBySide(TOP_SIDE)) /* Component */
474 suff = "cmp";
476 else if (group == GetLayerGroupNumberBySide(BOTTOM_SIDE)) /* Solder */
478 suff = "sol";
480 else if (nlayers == 1
481 && (strcmp (PCB->Data->Layer[idx].Name, "route") == 0 ||
482 strcmp (PCB->Data->Layer[idx].Name, "outline") == 0))
484 suff = "oln";
486 else
488 static char buf[20];
489 sprintf (buf, "ly%d", group);
490 suff = buf;
492 break;
495 strcpy (dest, suff);
498 /* Very similar to layer_type_to_file_name() but appends only a
499 three-character suffix compatible with Hackvana's naming requirements */
500 static void
501 assign_hackvana_file_suffix (char *dest, int idx)
503 int group;
504 int nlayers;
505 char *suff = "defau.out";
507 switch (idx)
509 case SL (SILK, TOP): suff = "gto"; break;
510 case SL (SILK, BOTTOM): suff = "gbo"; break;
511 case SL (MASK, TOP): suff = "gts"; break;
512 case SL (MASK, BOTTOM): suff = "gbs"; break;
513 case SL (PDRILL, 0): suff = "drl"; break;
514 case SL (UDRILL, 0):
515 suff = "_NPTH.drl";
516 break;
517 case SL (PASTE, TOP): suff = "gtp"; break;
518 case SL (PASTE, BOTTOM): suff = "gbp"; break;
519 case SL (INVISIBLE, 0): suff = "inv"; break;
520 case SL (FAB, 0): suff = "fab"; break;
521 case SL (ASSY, TOP): suff = "ast"; break;
522 case SL (ASSY, BOTTOM): suff = "asb"; break;
524 default:
525 group = GetLayerGroupNumberByNumber(idx);
526 nlayers = PCB->LayerGroups.Number[group];
527 if (group == GetLayerGroupNumberBySide(TOP_SIDE))
529 suff = "gtl";
531 else if (group == GetLayerGroupNumberBySide(BOTTOM_SIDE))
533 suff = "gbl";
535 else if (nlayers == 1
536 && (strcmp (PCB->Data->Layer[idx].Name, "route") == 0 ||
537 strcmp (PCB->Data->Layer[idx].Name, "outline") == 0))
539 suff = "gm1";
541 else
543 static char buf[20];
544 sprintf (buf, "g%d", group);
545 suff = buf;
547 break;
550 strcpy (dest, suff);
553 static void
554 assign_file_suffix (char *dest, int idx)
556 int fns_style;
557 const char *sext = ".gbr";
559 switch (name_style)
561 default:
562 case NAME_STYLE_FIXED: fns_style = FNS_fixed; break;
563 case NAME_STYLE_SINGLE: fns_style = FNS_single; break;
564 case NAME_STYLE_FIRST: fns_style = FNS_first; break;
565 case NAME_STYLE_EAGLE:
566 assign_eagle_file_suffix (dest, idx);
567 return;
568 case NAME_STYLE_HACKVANA:
569 assign_hackvana_file_suffix (dest, idx);
570 return;
573 switch (idx)
575 case SL (PDRILL, 0):
576 sext = ".cnc";
577 break;
578 case SL (UDRILL, 0):
579 sext = ".cnc";
580 break;
583 strcpy (dest, layer_type_to_file_name (idx, fns_style));
584 strcat (dest, sext);
587 static void
588 gerber_do_export (HID_Attr_Val * options)
590 const char *fnbase;
591 int i;
592 static int saved_layer_stack[MAX_LAYER];
593 int save_ons[MAX_LAYER + 2];
594 FlagType save_thindraw;
596 save_thindraw = PCB->Flags;
597 CLEAR_FLAG(THINDRAWFLAG, PCB);
598 CLEAR_FLAG(THINDRAWPOLYFLAG, PCB);
599 CLEAR_FLAG(CHECKPLANESFLAG, PCB);
601 if (!options)
603 gerber_get_export_options (NULL);
604 for (i = 0; i < NUM_OPTIONS; i++)
605 gerber_values[i] = gerber_options[i].default_val;
606 options = gerber_values;
609 fnbase = options[HA_gerberfile].str_value;
610 if (!fnbase)
611 fnbase = "pcb-out";
613 verbose = options[HA_verbose].int_value;
614 metric = options[HA_metric].int_value;
615 if (metric) {
616 x_convspec = "X%.0mu";
617 y_convspec = "Y%.0mu";
618 } else {
619 x_convspec = "X%.0mc";
620 y_convspec = "Y%.0mc";
622 all_layers = options[HA_all_layers].int_value;
624 copy_outline_mode = options[HA_copy_outline].int_value;
625 name_style = options[HA_name_style].int_value;
627 outline_layer = NULL;
629 for (i = 0; i < max_copper_layer; i++)
631 LayerType *layer = PCB->Data->Layer + i;
632 if (strcmp (layer->Name, "outline") == 0 ||
633 strcmp (layer->Name, "route") == 0)
635 outline_layer = layer;
639 i = strlen (fnbase);
640 filename = (char *)realloc (filename, i + 40);
641 strcpy (filename, fnbase);
642 strcat (filename, ".");
643 filesuff = filename + strlen (filename);
645 if (all_layers)
647 memset (print_group, 1, sizeof (print_group));
648 memset (print_layer, 1, sizeof (print_layer));
650 else
652 memset (print_group, 0, sizeof (print_group));
653 memset (print_layer, 0, sizeof (print_layer));
656 hid_save_and_show_layer_ons (save_ons);
657 for (i = 0; i < max_copper_layer; i++)
659 LayerType *layer = PCB->Data->Layer + i;
660 if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN)
661 print_group[GetLayerGroupNumberByNumber (i)] = 1;
663 print_group[GetLayerGroupNumberBySide (BOTTOM_SIDE)] = 1;
664 print_group[GetLayerGroupNumberBySide (TOP_SIDE)] = 1;
665 for (i = 0; i < max_copper_layer; i++)
666 if (print_group[GetLayerGroupNumberByNumber (i)])
667 print_layer[i] = 1;
669 memcpy (saved_layer_stack, LayerStack, sizeof (LayerStack));
670 qsort (LayerStack, max_copper_layer, sizeof (LayerStack[0]), layer_stack_sort);
671 linewidth = -1;
672 lastcap = -1;
673 lastgroup = -1;
675 region.X1 = 0;
676 region.Y1 = 0;
677 region.X2 = PCB->MaxWidth;
678 region.Y2 = PCB->MaxHeight;
680 pagecount = 1;
681 resetApertures ();
683 lastgroup = -1;
684 layer_list_idx = 0;
685 finding_apertures = 1;
686 hid_expose_callback (&gerber_hid, &region, 0);
688 layer_list_idx = 0;
689 finding_apertures = 0;
690 hid_expose_callback (&gerber_hid, &region, 0);
692 memcpy (LayerStack, saved_layer_stack, sizeof (LayerStack));
694 maybe_close_f (f);
695 f = NULL;
696 hid_restore_layer_ons (save_ons);
697 PCB->Flags = save_thindraw;
700 static void
701 gerber_parse_arguments (int *argc, char ***argv)
703 hid_register_attributes (gerber_options, NUM_OPTIONS);
704 hid_parse_command_line (argc, argv);
707 static int
708 drill_sort (const void *va, const void *vb)
710 PendingDrills *a = (PendingDrills *) va;
711 PendingDrills *b = (PendingDrills *) vb;
712 if (a->diam != b->diam)
713 return a->diam - b->diam;
714 if (a->x != b->x)
715 return a->x - b->x;
716 return a->y - b->y;
719 static int
720 gerber_set_layer (const char *name, int group, int empty)
722 int want_outline;
723 char *cp;
724 int idx = (group >= 0
725 && group <
726 max_group) ? PCB->LayerGroups.Entries[group][0] : group;
728 if (name == NULL)
729 name = PCB->Data->Layer[idx].Name;
731 if (idx >= 0 && idx < max_copper_layer && !print_layer[idx])
732 return 0;
734 if (strcmp (name, "invisible") == 0)
735 return 0;
736 if (SL_TYPE (idx) == SL_ASSY)
737 return 0;
739 flash_drills = 0;
740 if (strcmp (name, "outline") == 0 ||
741 strcmp (name, "route") == 0)
742 flash_drills = 1;
744 if (is_drill && n_pending_drills)
746 int i;
747 /* dump pending drills in sequence */
748 qsort (pending_drills, n_pending_drills, sizeof (pending_drills[0]),
749 drill_sort);
750 for (i = 0; i < n_pending_drills; i++)
752 if (i == 0 || pending_drills[i].diam != pending_drills[i - 1].diam)
754 Aperture *ap = findAperture (curr_aptr_list, pending_drills[i].diam, ROUND);
755 fprintf (f, "T%02d\r\n", ap->dCode);
757 pcb_fprintf (f, metric ? "X%06.0muY%06.0mu\r\n" : "X%06.0mtY%06.0mt\r\n",
758 gerberDrX (PCB, pending_drills[i].x),
759 gerberDrY (PCB, pending_drills[i].y));
761 free (pending_drills);
762 n_pending_drills = max_pending_drills = 0;
763 pending_drills = NULL;
766 is_drill = (SL_TYPE (idx) == SL_PDRILL || SL_TYPE (idx) == SL_UDRILL);
767 is_mask = (SL_TYPE (idx) == SL_MASK);
768 current_mask = HID_MASK_OFF;
769 #if 0
770 printf ("Layer %s group %d drill %d mask %d\n", name, group, is_drill,
771 is_mask);
772 #endif
774 if (group < 0 || group != lastgroup)
776 time_t currenttime;
777 char utcTime[64];
778 #ifdef HAVE_GETPWUID
779 struct passwd *pwentry;
780 #endif
781 ApertureList *aptr_list;
782 Aperture *search;
784 lastgroup = group;
785 lastX = -1;
786 lastY = -1;
787 linewidth = -1;
788 lastcap = -1;
790 aptr_list = setLayerApertureList (layer_list_idx++);
792 if (finding_apertures)
793 goto emit_outline;
795 if (aptr_list->count == 0 && !all_layers)
796 return 0;
798 maybe_close_f (f);
799 f = NULL;
801 pagecount++;
802 assign_file_suffix (filesuff, idx);
803 f = fopen (filename, "wb"); /* Binary needed to force CR-LF */
804 if (f == NULL)
806 Message ( "Error: Could not open %s for writing.\n", filename);
807 return 1;
810 was_drill = is_drill;
812 if (verbose)
814 int c = aptr_list->count;
815 printf ("Gerber: %d aperture%s in %s\n", c,
816 c == 1 ? "" : "s", filename);
819 if (is_drill)
821 /* We omit the ,TZ here because we are not omitting trailing zeros. Our format is
822 always six-digit 0.1 mil or µm resolution (i.e. 001100 = 0.11" or 1.1mm)*/
823 fprintf (f, "M48\r\n");
824 fprintf (f, metric ? "METRIC,000.000\r\n" : "INCH\r\n");
825 for (search = aptr_list->data; search; search = search->next)
826 pcb_fprintf (f, metric ? "T%02dC%.3`mm\r\n" : "T%02dC%.3`mi\r\n", search->dCode, search->width);
827 fprintf (f, "%%\r\n");
828 /* FIXME */
829 return 1;
832 fprintf (f, "G04 start of page %d for group %d idx %d *\r\n",
833 pagecount, group, idx);
835 /* Create a portable timestamp. */
836 currenttime = time (NULL);
838 /* avoid gcc complaints */
839 const char *fmt = "%c UTC";
840 strftime (utcTime, sizeof utcTime, fmt, gmtime (&currenttime));
842 /* Print a cute file header at the beginning of each file. */
843 fprintf (f, "G04 Title: %s, %s *\r\n", UNKNOWN (PCB->Name),
844 UNKNOWN (name));
845 fprintf (f, "G04 Creator: %s " VERSION " *\r\n", Progname);
846 fprintf (f, "G04 CreationDate: %s *\r\n", utcTime);
848 #ifdef HAVE_GETPWUID
849 /* ID the user. */
850 pwentry = getpwuid (getuid ());
851 fprintf (f, "G04 For: %s *\r\n", pwentry->pw_name);
852 #endif
854 fprintf (f, "G04 Format: Gerber/RS-274X *\r\n");
855 pcb_fprintf (f, metric ? "G04 PCB-Dimensions (mm): %.2mm %.2mm *\r\n" :
856 "G04 PCB-Dimensions (mil): %.2ml %.2ml *\r\n",
857 PCB->MaxWidth, PCB->MaxHeight);
858 fprintf (f, "G04 PCB-Coordinate-Origin: lower left *\r\n");
860 /* Signal data in inches. */
861 fprintf (f, metric ? "%%MOMM*%%\r\n" : "%%MOIN*%%\r\n");
863 /* Signal Leading zero suppression, Absolute Data, 2.5 format in inch, 4.3 in mm */
864 fprintf (f, metric ? "%%FSLAX43Y43*%%\r\n" : "%%FSLAX25Y25*%%\r\n");
866 /* build a legal identifier. */
867 if (layername)
868 free (layername);
869 layername = strdup (filesuff);
870 if (strrchr (layername, '.'))
871 * strrchr (layername, '.') = 0;
873 for (cp=layername; *cp; cp++)
875 if (isalnum((int) *cp))
876 *cp = toupper((int) *cp);
877 else
878 *cp = '_';
880 fprintf (f, "%%LN%s*%%\r\n", layername);
881 lncount = 1;
883 for (search = aptr_list->data; search; search = search->next)
884 fprintAperture(f, search);
885 if (aptr_list->count == 0)
886 /* We need to put *something* in the file to make it be parsed
887 as RS-274X instead of RS-274D. */
888 fprintf (f, "%%ADD11C,0.0100*%%\r\n");
891 emit_outline:
892 /* If we're printing a copper layer other than the outline layer,
893 and we want to "print outlines", and we have an outline layer,
894 print the outline layer on this layer also. */
895 want_outline = 0;
896 if (copy_outline_mode == COPY_OUTLINE_MASK
897 && SL_TYPE (idx) == SL_MASK)
898 want_outline = 1;
899 if (copy_outline_mode == COPY_OUTLINE_SILK
900 && SL_TYPE (idx) == SL_SILK)
901 want_outline = 1;
902 if (copy_outline_mode == COPY_OUTLINE_ALL
903 && (SL_TYPE (idx) == SL_SILK
904 || SL_TYPE (idx) == SL_MASK
905 || SL_TYPE (idx) == SL_FAB
906 || SL_TYPE (idx) == SL_ASSY
907 || SL_TYPE (idx) == 0))
908 want_outline = 1;
910 if (want_outline
911 && strcmp (name, "outline")
912 && strcmp (name, "route"))
914 if (outline_layer
915 && outline_layer != PCB->Data->Layer+idx)
916 DrawLayer (outline_layer, &region);
917 else if (!outline_layer)
919 hidGC gc = hid_draw_make_gc (&gerber_graphics);
920 printf("name %s idx %d\n", name, idx);
921 if (SL_TYPE (idx) == SL_SILK)
922 hid_draw_set_line_width (gc, PCB->minSlk);
923 else if (group >= 0)
924 hid_draw_set_line_width (gc, PCB->minWid);
925 else
926 hid_draw_set_line_width (gc, AUTO_OUTLINE_WIDTH);
927 hid_draw_line (gc, 0, 0, PCB->MaxWidth, 0);
928 hid_draw_line (gc, 0, 0, 0, PCB->MaxHeight);
929 hid_draw_line (gc, PCB->MaxWidth, 0, PCB->MaxWidth, PCB->MaxHeight);
930 hid_draw_line (gc, 0, PCB->MaxHeight, PCB->MaxWidth, PCB->MaxHeight);
931 hid_draw_destroy_gc (gc);
935 return 1;
938 static hidGC
939 gerber_make_gc (void)
941 hidGC gc = (hidGC) calloc (1, sizeof (struct gerber_gc_struct));
942 gerberGC gerber_gc = (gerberGC)gc;
944 gc->hid = &gerber_hid;
945 gc->hid_draw = &gerber_graphics;
947 gerber_gc->cap = Trace_Cap;
949 return gc;
952 static void
953 gerber_destroy_gc (hidGC gc)
955 free (gc);
958 static void
959 gerber_use_mask (enum mask_mode mode)
961 current_mask = mode;
964 static void
965 gerber_set_color (hidGC gc, const char *name)
967 gerberGC gerber_gc = (gerberGC)gc;
969 if (strcmp (name, "erase") == 0)
971 gerber_gc->color = 1;
972 gerber_gc->erase = 1;
973 gerber_gc->drill = 0;
975 else if (strcmp (name, "drill") == 0)
977 gerber_gc->color = 1;
978 gerber_gc->erase = 0;
979 gerber_gc->drill = 1;
981 else
983 gerber_gc->color = 0;
984 gerber_gc->erase = 0;
985 gerber_gc->drill = 0;
989 static void
990 gerber_set_line_cap (hidGC gc, EndCapStyle style)
992 gerberGC gerber_gc = (gerberGC)gc;
994 gerber_gc->cap = style;
997 static void
998 gerber_set_line_width (hidGC gc, Coord width)
1000 gerberGC gerber_gc = (gerberGC)gc;
1002 gerber_gc->width = width;
1005 static void
1006 gerber_set_draw_xor (hidGC gc, int xor_)
1011 static void
1012 use_gc (hidGC gc, int radius)
1014 gerberGC gerber_gc = (gerberGC)gc;
1016 if (radius)
1018 radius *= 2;
1019 if (radius != linewidth || lastcap != Round_Cap)
1021 Aperture *aptr = findAperture (curr_aptr_list, radius, ROUND);
1022 if (aptr == NULL)
1023 pcb_fprintf (stderr, "error: aperture for radius %$mS type ROUND is null\n", radius);
1024 else if (f && !is_drill)
1025 fprintf (f, "G54D%d*", aptr->dCode);
1026 linewidth = radius;
1027 lastcap = Round_Cap;
1030 else if (linewidth != gerber_gc->width || lastcap != gerber_gc->cap)
1032 Aperture *aptr;
1033 ApertureShape shape;
1035 linewidth = gerber_gc->width;
1036 lastcap = gerber_gc->cap;
1037 switch (gerber_gc->cap)
1039 case Round_Cap:
1040 case Trace_Cap:
1041 shape = ROUND;
1042 break;
1043 default:
1044 case Square_Cap:
1045 shape = SQUARE;
1046 break;
1048 aptr = findAperture (curr_aptr_list, linewidth, shape);
1049 if (aptr == NULL)
1050 pcb_fprintf (stderr, "error: aperture for width %$mS type %s is null\n",
1051 linewidth, shape == ROUND ? "ROUND" : "SQUARE");
1052 else if (f)
1053 fprintf (f, "G54D%d*", aptr->dCode);
1057 static void
1058 gerber_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1060 gerber_draw_line (gc, x1, y1, x1, y2);
1061 gerber_draw_line (gc, x1, y1, x2, y1);
1062 gerber_draw_line (gc, x1, y2, x2, y2);
1063 gerber_draw_line (gc, x2, y1, x2, y2);
1066 static void
1067 gerber_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1069 gerberGC gerber_gc = (gerberGC)gc;
1070 bool m = false;
1072 if (x1 != x2 && y1 != y2 && gerber_gc->cap == Square_Cap)
1074 Coord x[5], y[5];
1075 double tx, ty, theta;
1077 theta = atan2 (y2-y1, x2-x1);
1079 /* T is a vector half a thickness long, in the direction of
1080 one of the corners. */
1081 tx = gerber_gc->width / 2.0 * cos (theta + M_PI/4) * sqrt(2.0);
1082 ty = gerber_gc->width / 2.0 * sin (theta + M_PI/4) * sqrt(2.0);
1084 x[0] = x1 - tx; y[0] = y1 - ty;
1085 x[1] = x2 + ty; y[1] = y2 - tx;
1086 x[2] = x2 + tx; y[2] = y2 + ty;
1087 x[3] = x1 - ty; y[3] = y1 + tx;
1089 x[4] = x[0]; y[4] = y[0];
1090 gerber_fill_polygon (gc, 5, x, y);
1091 return;
1094 use_gc (gc, 0);
1095 if (!f)
1096 return;
1098 if (x1 != lastX)
1100 m = true;
1101 lastX = x1;
1102 print_xcoord (f, PCB, lastX);
1104 if (y1 != lastY)
1106 m = true;
1107 lastY = y1;
1108 print_ycoord (f, PCB, lastY);
1110 if ((x1 == x2) && (y1 == y2))
1111 fprintf (f, "D03*\r\n");
1112 else
1114 if (m)
1115 fprintf (f, "D02*");
1116 if (x2 != lastX)
1118 lastX = x2;
1119 print_xcoord (f, PCB, lastX);
1121 if (y2 != lastY)
1123 lastY = y2;
1124 print_ycoord (f, PCB, lastY);
1126 fprintf (f, "D01*\r\n");
1131 static void
1132 gerber_draw_arc (hidGC gc, Coord cx, Coord cy, Coord width, Coord height,
1133 Angle start_angle, Angle delta_angle)
1135 gerberGC gerber_gc = (gerberGC)gc;
1136 bool m = false;
1137 double arcStartX, arcStopX, arcStartY, arcStopY;
1139 /* we never draw zero-width lines */
1140 if (gerber_gc->width == 0)
1141 return;
1143 use_gc (gc, 0);
1144 if (!f)
1145 return;
1147 arcStartX = cx - width * cos (TO_RADIANS (start_angle));
1148 arcStartY = cy + height * sin (TO_RADIANS (start_angle));
1150 /* I checked three different gerber viewers, and they all disagreed
1151 on how ellipses should be drawn. The spec just calls G74/G75
1152 "circular interpolation" so there's a chance it just doesn't
1153 support ellipses at all. Thus, we draw them out with line
1154 segments. Note that most arcs in pcb are circles anyway. */
1155 if (width != height)
1157 double step, angle;
1158 Coord max = width > height ? width : height;
1159 Coord minr = max - gerber_gc->width / 10;
1160 int nsteps;
1161 Coord x0, y0, x1, y1;
1163 if (minr >= max)
1164 minr = max - 1;
1165 step = acos((double)minr/(double)max) * 180.0/M_PI;
1166 if (step > 5)
1167 step = 5;
1168 nsteps = abs(delta_angle) / step + 1;
1169 step = (double)delta_angle / nsteps;
1171 x0 = arcStartX;
1172 y0 = arcStartY;
1173 angle = start_angle;
1174 while (nsteps > 0)
1176 nsteps --;
1177 x1 = cx - width * cos (TO_RADIANS (angle+step));
1178 y1 = cy + height * sin (TO_RADIANS (angle+step));
1179 gerber_draw_line (gc, x0, y0, x1, y1);
1180 x0 = x1;
1181 y0 = y1;
1182 angle += step;
1184 return;
1187 arcStopX = cx - width * cos (TO_RADIANS (start_angle + delta_angle));
1188 arcStopY = cy + height * sin (TO_RADIANS (start_angle + delta_angle));
1189 if (arcStartX != lastX)
1191 m = true;
1192 lastX = arcStartX;
1193 print_xcoord (f, PCB, lastX);
1195 if (arcStartY != lastY)
1197 m = true;
1198 lastY = arcStartY;
1199 print_ycoord (f, PCB, lastY);
1201 if (m)
1202 fprintf (f, "D02*");
1203 pcb_fprintf (f,
1204 metric ? "G75*G0%1dX%.0muY%.0muI%.0muJ%.0muD01*G01*\r\n" :
1205 "G75*G0%1dX%.0mcY%.0mcI%.0mcJ%.0mcD01*G01*\r\n",
1206 (delta_angle < 0) ? 2 : 3,
1207 gerberX (PCB, arcStopX), gerberY (PCB, arcStopY),
1208 gerberXOffset (PCB, cx - arcStartX),
1209 gerberYOffset (PCB, cy - arcStartY));
1210 lastX = arcStopX;
1211 lastY = arcStopY;
1214 static void
1215 gerber_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
1217 gerberGC gerber_gc = (gerberGC)gc;
1219 if (radius <= 0)
1220 return;
1221 if (is_drill)
1222 radius = 50 * round (radius / 50.0);
1223 use_gc (gc, radius);
1224 if (!f)
1225 return;
1226 if (is_drill)
1228 if (n_pending_drills >= max_pending_drills)
1230 max_pending_drills += 100;
1231 pending_drills = (PendingDrills *) realloc(pending_drills,
1232 max_pending_drills *
1233 sizeof (pending_drills[0]));
1235 pending_drills[n_pending_drills].x = cx;
1236 pending_drills[n_pending_drills].y = cy;
1237 pending_drills[n_pending_drills].diam = radius * 2;
1238 n_pending_drills++;
1239 return;
1241 else if (gerber_gc->drill && !flash_drills)
1242 return;
1243 if (cx != lastX)
1245 lastX = cx;
1246 print_xcoord (f, PCB, lastX);
1248 if (cy != lastY)
1250 lastY = cy;
1251 print_ycoord (f, PCB, lastY);
1253 fprintf (f, "D03*\r\n");
1256 static void
1257 gerber_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
1259 bool m = false;
1260 int i;
1261 int firstTime = 1;
1262 Coord startX = 0, startY = 0;
1264 if (is_mask && current_mask == HID_MASK_BEFORE)
1265 return;
1267 use_gc (gc, 10 * 100);
1268 if (!f)
1269 return;
1270 fprintf (f, "G36*\r\n");
1271 for (i = 0; i < n_coords; i++)
1273 if (x[i] != lastX)
1275 m = true;
1276 lastX = x[i];
1277 print_xcoord (f, PCB, lastX);
1279 if (y[i] != lastY)
1281 m = true;
1282 lastY = y[i];
1283 print_ycoord (f, PCB, lastY);
1285 if (firstTime)
1287 firstTime = 0;
1288 startX = x[i];
1289 startY = y[i];
1290 if (m)
1291 fprintf (f, "D02*");
1293 else if (m)
1294 fprintf (f, "D01*\r\n");
1295 m = false;
1297 if (startX != lastX)
1299 m = true;
1300 lastX = startX;
1301 print_xcoord (f, PCB, startX);
1303 if (startY != lastY)
1305 m = true;
1306 lastY = startY;
1307 print_ycoord (f, PCB, lastY);
1309 if (m)
1310 fprintf (f, "D01*\r\n");
1311 fprintf (f, "G37*\r\n");
1314 static void
1315 gerber_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
1317 Coord x[5];
1318 Coord y[5];
1319 x[0] = x[4] = x1;
1320 y[0] = y[4] = y1;
1321 x[1] = x1;
1322 y[1] = y2;
1323 x[2] = x2;
1324 y[2] = y2;
1325 x[3] = x2;
1326 y[3] = y1;
1327 gerber_fill_polygon (gc, 5, x, y);
1330 static void
1331 gerber_calibrate (double xval, double yval)
1333 CRASH;
1336 static void
1337 gerber_set_crosshair (int x, int y, int action)
1341 void
1342 hid_gerber_init ()
1344 memset (&gerber_hid, 0, sizeof (gerber_hid));
1345 memset (&gerber_graphics, 0, sizeof (gerber_graphics));
1346 memset (&gerber_graphics_class, 0, sizeof (gerber_graphics_class));
1348 common_nogui_init (&gerber_hid);
1350 gerber_hid.struct_size = sizeof (gerber_hid);
1351 gerber_hid.name = "gerber";
1352 gerber_hid.description = "RS-274X (Gerber) export";
1353 gerber_hid.exporter = 1;
1355 gerber_hid.get_export_options = gerber_get_export_options;
1356 gerber_hid.do_export = gerber_do_export;
1357 gerber_hid.parse_arguments = gerber_parse_arguments;
1358 gerber_hid.calibrate = gerber_calibrate;
1359 gerber_hid.set_crosshair = gerber_set_crosshair;
1361 gerber_hid.graphics = &gerber_graphics;
1363 common_nogui_graphics_class_init (&gerber_graphics_class);
1364 common_draw_helpers_class_init (&gerber_graphics_class);
1366 gerber_graphics_class.set_layer = gerber_set_layer;
1367 gerber_graphics_class.make_gc = gerber_make_gc;
1368 gerber_graphics_class.destroy_gc = gerber_destroy_gc;
1369 gerber_graphics_class.use_mask = gerber_use_mask;
1370 gerber_graphics_class.set_color = gerber_set_color;
1371 gerber_graphics_class.set_line_cap = gerber_set_line_cap;
1372 gerber_graphics_class.set_line_width = gerber_set_line_width;
1373 gerber_graphics_class.set_draw_xor = gerber_set_draw_xor;
1374 gerber_graphics_class.draw_line = gerber_draw_line;
1375 gerber_graphics_class.draw_arc = gerber_draw_arc;
1376 gerber_graphics_class.draw_rect = gerber_draw_rect;
1377 gerber_graphics_class.fill_circle = gerber_fill_circle;
1378 gerber_graphics_class.fill_polygon = gerber_fill_polygon;
1379 gerber_graphics_class.fill_rect = gerber_fill_rect;
1381 gerber_graphics.klass = &gerber_graphics_class;
1382 common_nogui_graphics_init (&gerber_graphics);
1383 common_draw_helpers_init (&gerber_graphics);
1385 hid_register_hid (&gerber_hid);