#include #include #include #include #include #include #include #include #include #include "config.h" #include "global.h" #include "data.h" #include "misc.h" #include "error.h" #include "hid.h" #include "../hidint.h" #define CRASH fprintf(stderr, "HID error: pcb called unimplemented Gerber function %s.\n", __FUNCTION__); abort() static HID gerber_hid; static void gerber_fill_polygon (hidGC gc, int n_coords, int *x, int *y); /*----------------------------------------------------------------------------*/ /* Utility routines */ /*----------------------------------------------------------------------------*/ #define gerberX(pcb, x) ((long) ((x)/10)) #define gerberY(pcb, y) ((long) (((pcb)->MaxHeight - (y)))/10) #define gerberXOffset(pcb, x) ((long) ((x) /10)) #define gerberYOffset(pcb, y) ((long) (-(y) /10)) /*----------------------------------------------------------------------------*/ /* Private data structures */ /*----------------------------------------------------------------------------*/ static int is_mask, was_drill; static int is_drill; static int current_mask; enum ApertureShape { ROUND, /* Shaped like a circle */ OCTAGON, /* octagonal shape */ SQUARE, /* Shaped like a square */ ROUNDCLEAR, /* clearance in negatives */ SQUARECLEAR, THERMAL /* negative thermal relief */ }; typedef enum ApertureShape ApertureShape; typedef struct Aperture { int dCode; /* The RS-274X D code */ int apertureSize; /* Size in mils */ ApertureShape apertureShape; /* ROUND/SQUARE etc */ } Aperture; typedef struct { DynamicStringType appList; int lastdCode; int lastTherm; int nextAperture; /* Number of apertures */ Aperture aperture[GBX_MAXAPERTURECOUNT]; } Apertures; static Apertures *layerapps = 0; static Apertures *curapp; static int n_layerapps = 0; static int c_layerapps = 0; typedef struct { int diam; int x; int y; } PendingDrills; PendingDrills *pending_drills=0; int n_pending_drills=0, max_pending_drills=0; /*----------------------------------------------------------------------------*/ /* Aperture Routines */ /*----------------------------------------------------------------------------*/ static int findApertureCode (int width, ApertureShape shape) { int i; Aperture *ap; char appMacro[256]; /* we never draw zero-width lines */ if (width == 0) return (0); /* Search for an appropriate aperture. */ for (i = 0; i < curapp->nextAperture; i++) { ap = &(curapp->aperture[i]); if (ap->apertureSize == width && ap->apertureShape == shape) return (ap->dCode); } appMacro[0] = '\0'; /* Not found, create a new aperture and add it to the list */ if (curapp->nextAperture < GBX_MAXAPERTURECOUNT) { i = curapp->nextAperture++; ap = &(curapp->aperture[i]); ap->dCode = curapp->lastdCode++; ap->apertureSize = width; ap->apertureShape = shape; switch (shape) { case ROUND: sprintf (appMacro, "%%ADD%dC,%.4f*%%\015\012", ap->dCode, width / 100000.0); break; case SQUARE: sprintf (appMacro, "%%ADD%dR,%.4fX%.4f*%%\015\012", ap->dCode, width / 100000.0, width / 100000.0); break; case OCTAGON: sprintf (appMacro, "%%AMOCT%d*5,0,8,0,0,%.4f,22.5*%%\015\012" "%%ADD%dOCT%d*%%\015\012", curapp->lastTherm, width / (100000.0 * COS_22_5_DEGREE), ap->dCode, curapp->lastTherm); curapp->lastTherm++; break; #if 0 case THERMAL: sprintf (appMacro, "%%AMTHERM%d*7,0,0,%.4f,%.4f,%.4f,45*%%\015\012" "%%ADD%dTHERM%d*%%\015\012", lastTherm, gap / 100000.0, width / 100000.0, finger / 100000.0, ap->dCode, lastTherm); lastTherm++; break; case ROUNDCLEAR: sprintf (appMacro, "%%ADD%dC,%.4fX%.4f*%%\015\012", ap->dCode, gap / 100000.0, width / 100000.0); break; case SQUARECLEAR: sprintf (appMacro, "%%ADD%dR,%.4fX%.4fX%.4fX%.4f*%%\015\012", ap->dCode, gap / 100000.0, gap / 100000.0, width / 100000.0, width / 100000.0); break; #endif } DSAddString (&(curapp->appList), appMacro); return (ap->dCode); } else { Message (_("Error, too many apertures needed for Gerber file.\n")); return (10); } } static void initApertures () { layerapps = 0; n_layerapps = 0; } static void SetAppLayer(int l) { if (l >= n_layerapps) { int prev = n_layerapps; n_layerapps = l + 1; layerapps = MyRealloc(layerapps, n_layerapps * sizeof(Apertures), "SetAppLayer"); curapp = layerapps + prev; while (curapp < layerapps + n_layerapps) { curapp->appList.Data = NULL; curapp->appList.MaxLength = 0; curapp->lastdCode = 11; curapp->lastTherm = 1; curapp->nextAperture = 0; curapp ++; } } curapp = layerapps + l; } /* --------------------------------------------------------------------------- */ typedef struct hid_gc_struct { EndCapStyle cap; int width; int color; int erase; int drill; } hid_gc_struct; static FILE *f = 0; static char *filename = 0; static char *filesuff; static int finding_apertures = 0; static int pagecount = 0; static int linewidth = -1; static int lastgroup = -1; static int lastcap = -1; static int lastcolor = -1; static int print_group[MAX_LAYER]; static int print_layer[MAX_LAYER]; static int lastX, lastY; /* the last X and Y coordinate */ static HID_Attribute gerber_options[] = { {"gerberfile", "Gerber output file base", HID_String, 0, 0, {0, 0, 0}, 0, 0}, #define HA_gerberfile 0 }; #define NUM_OPTIONS (sizeof(gerber_options)/sizeof(gerber_options[0])) static HID_Attr_Val gerber_values[NUM_OPTIONS]; static HID_Attribute * gerber_get_export_options (int *n) { char *buf = 0; if (PCB && PCB->Filename) { buf = (char *)malloc(strlen(PCB->Filename) + 4); if (buf) { strcpy(buf, PCB->Filename); if (strcmp (buf + strlen(buf) - 4, ".pcb") == 0) buf[strlen(buf)-4] = 0; if (gerber_options[HA_gerberfile].default_val.str_value) free (gerber_options[HA_gerberfile].default_val.str_value); gerber_options[HA_gerberfile].default_val.str_value = buf; } } if (n) *n = NUM_OPTIONS; return gerber_options; } static int group_for_layer (int l) { if (l < MAX_LAYER+2 && l >= 0) return GetLayerGroupNumberByNumber(l); /* else something unique */ return MAX_LAYER + 3 + l; } static int layer_sort(const void *va, const void *vb) { int a = *(int *)va; int b = *(int *)vb; int d = group_for_layer(b) - group_for_layer(a); if (d) return d; return b - a; } static void maybe_close_f() { if (f) { if (was_drill) fprintf (f, "M30\015\012"); else fprintf (f, "M02*\015\012"); fclose(f); } f = 0; } static void gerber_do_export (HID_Attr_Val *options) { char *fnbase; int i; static int saved_layer_stack[MAX_LAYER]; BoxType region; int save_ons[MAX_LAYER+2]; if (!options) { gerber_get_export_options (0); for (i=0; iData->Layer+i; if (layer->LineN || layer->TextN || layer->ArcN || layer->PolygonN) print_group[GetLayerGroupNumberByNumber(i)] = 1; } print_group[GetLayerGroupNumberByNumber(MAX_LAYER)] = 1; print_group[GetLayerGroupNumberByNumber(MAX_LAYER+1)] = 1; for (i=0; iMaxWidth; region.Y2 = PCB->MaxHeight; pagecount = 1; initApertures (); f = 0; lastgroup = -1; c_layerapps = 0; finding_apertures = 1; hid_expose_callback(&gerber_hid, ®ion, 0); c_layerapps = 0; finding_apertures = 0; hid_expose_callback(&gerber_hid, ®ion, 0); memcpy (LayerStack, saved_layer_stack, sizeof(LayerStack)); maybe_close_f(); hid_restore_layer_ons (save_ons); } extern void hid_parse_command_line (int *argc, char ***argv); static void gerber_parse_arguments (int *argc, char ***argv) { hid_register_attributes (gerber_options, sizeof(gerber_options)/sizeof(gerber_options[0])); hid_parse_command_line (argc, argv); } static int drill_sort (const void *va, const void *vb) { PendingDrills *a = (PendingDrills *)va; PendingDrills *b = (PendingDrills *)vb; if (a->diam != b->diam) return a->diam - b->diam; if (a->x != b->x) return a->x - a->x; return b->y - b->y; } static int gerber_set_layer (const char *name, int group) { int idx = (group >= 0 && group < MAX_LAYER) ? PCB->LayerGroups.Entries[group][0] : group; if (name == 0) name = PCB->Data->Layer[idx].Name; if (idx >= 0 && idx < MAX_LAYER && !print_layer[idx]) return 0; if (strcmp (name, "invisible") == 0) return 0; if (SL_TYPE(idx) == SL_ASSY) return 0; if (is_drill && n_pending_drills) { int i; /* dump pending drills in sequence */ qsort(pending_drills, n_pending_drills, sizeof(PendingDrills), drill_sort); for (i=0; inextAperture) return 0; maybe_close_f(); pagecount++; switch (idx) { case SL(SILK,TOP): spat = "topsilk"; break; case SL(SILK,BOTTOM): spat = "bottomsilk"; break; case SL(MASK,TOP): spat = "topmask"; break; case SL(MASK,BOTTOM): spat = "bottommask"; break; case SL(PASTE,TOP): spat = "toppaste"; break; case SL(PASTE,BOTTOM): spat = "bottompaste"; break; case SL(DRILL,0): spat = "drill"; break; case SL(FAB,0): spat = "fab"; break; case SL(ASSY,TOP): spat = "topassembly"; break; case SL(ASSY,BOTTOM): spat = "bottomassembly"; break; default: spat = "copper%d"; break; } sprintf(filesuff, spat, group); strcat(filesuff, ".gbr"); f = fopen(filename, "w"); was_drill = is_drill; printf("Gerber: %d aperture%s in %s\n", curapp->nextAperture, curapp->nextAperture == 1 ? "" : "s", filename); if (is_drill) { for (i=0; inextAperture; i++) fprintf (f, "T%02dC%.3f\015\012", curapp->aperture[i].dCode, curapp->aperture[i].apertureSize / 100000.0); /* FIXME */ return 1; } fprintf(f, "G04 start of page %d for group %d idx %d\015\012", pagecount, group, idx); /* Create a portable timestamp. */ currenttime = time (NULL); strftime (utcTime, sizeof utcTime, "%c UTC", gmtime (¤ttime)); /* ID the user. */ pwentry = getpwuid (getuid ()); /* Print a cute file header at the beginning of each file. */ fprintf (f, "G04 Title: %s, %s *\015\012", UNKNOWN (PCB->Name), UNKNOWN (name)); fprintf (f, "G04 Creator: %s " VERSION " *\015\012", Progname); fprintf (f, "G04 CreationDate: %s *\015\012", utcTime); fprintf (f, "G04 For: %s *\015\012", pwentry->pw_name); fprintf (f, "G04 Format: Gerber/RS-274X *\015\012"); fprintf (f, "G04 PCB-Dimensions: %d %d *\015\012", PCB->MaxWidth, PCB->MaxHeight); fprintf (f, "G04 PCB-Coordinate-Origin: lower left *\015\012"); /* Signal data in inches. */ fprintf (f, "%%MOIN*%%\015\012"); /* Signal Leading zero suppression, Absolute Data, 2.3 format */ fprintf (f, "%%FSLAX24Y24*%%\015\012"); fprintf (f, "%s", curapp->appList.Data); lastX = -1; lastY = -1; lastcolor = 0; linewidth = -1; lastcap = -1; } return 1; } static hidGC gerber_make_gc (void) { hidGC rv = (hidGC) calloc (1, sizeof(hid_gc_struct)); rv->cap = Trace_Cap; return rv; } static void gerber_destroy_gc (hidGC gc) { free(gc); } static void gerber_use_mask (int use_it) { current_mask = use_it; } static void gerber_set_color (hidGC gc, const char *name) { if (strcmp (name, "erase") == 0) { gc->color = 1; gc->erase = 1; gc->drill = 0; } else if (strcmp (name, "drill") == 0) { gc->color = 1; gc->erase = 0; gc->drill = 1; } else { gc->color = 0; gc->erase = 0; gc->drill = 0; } } static void gerber_set_line_cap (hidGC gc, EndCapStyle style) { gc->cap = style; } static void gerber_set_line_width (hidGC gc, int width) { gc->width = width; } static void gerber_set_draw_xor (hidGC gc, int xor) { ; } static void gerber_set_draw_faded (hidGC gc, int faded) { ; } static void gerber_set_line_cap_angle (hidGC gc, int x1, int y1, int x2, int y2) { CRASH; } static void use_gc(hidGC gc, int radius) { int c; if (radius) { radius *= 2; if (radius != linewidth || lastcap != Round_Cap) { c = findApertureCode(radius, ROUND); if (c <= 0) { fprintf(stderr, "error: aperture for radius %d type ROUND is %d\n", radius, c); } if (f && !is_drill) fprintf(f, "G54D%d*", c); linewidth = radius; lastcap = Round_Cap; } } else if (linewidth != gc->width || lastcap != gc->cap) { int ap; linewidth = gc->width; lastcap = gc->cap; switch (gc->cap) { case Round_Cap: case Trace_Cap: c = ROUND; break; default: case Square_Cap: c = SQUARE; break; } ap = findApertureCode(linewidth, c); if (ap <= 0) { fprintf(stderr, "error: aperture for width %d type %s is %d\n", linewidth, c == ROUND ? "ROUND" : "SQUARE", ap); } if (f) fprintf(f, "G54D%d*", ap); } if (lastcolor != gc->color) { c = gc->color; if (is_drill) return; if (is_mask) c = (gc->erase ? 0 : 1); lastcolor = gc->color; if (f) { if (c) fprintf(f, "%%LPC*%%\015\012"); else fprintf(f, "%%LPD*%%\015\012"); } } } static void gerber_draw_rect (hidGC gc, int x1, int y1, int x2, int y2) { int x[5]; int y[5]; x[0] = x[4] = x1; y[0] = y[4] = y1; x[1] = x1; y[1] = y2; x[2] = x2; y[2] = y2; x[3] = x2; y[3] = y1; gerber_fill_polygon(gc, 5, x, y); } static void gerber_draw_line (hidGC gc, int x1, int y1, int x2, int y2) { Boolean m = False; use_gc(gc, 0); if (!f) return; if (x1 != lastX) { m = True; lastX = x1; fprintf (f, "X%ld", gerberX (PCB, lastX)); } if (y1 != lastY) { m = True; lastY = y1; fprintf (f, "Y%ld", gerberY (PCB, lastY)); } if ((x1 == x2) && (y1 == y2)) fprintf (f, "D03*\015\012"); else { if (m) fprintf (f, "D02*"); if (x2 != lastX) { lastX = x2; fprintf (f, "X%ld", gerberX (PCB, lastX)); } if (y2 != lastY) { lastY = y2; fprintf (f, "Y%ld", gerberY (PCB, lastY)); } fprintf (f, "D01*\015\012"); } } static void gerber_draw_arc (hidGC gc, int cx, int cy, int width, int height, int start_angle, int delta_angle) { Boolean m = False; int size; float arcStartX, arcStopX, arcStartY, arcStopY; if (gc->width == 1) return; use_gc(gc, 0); if (!f) return; arcStartX = cx - width * cos (TO_RADIANS (start_angle)); arcStartY = cy + height * sin (TO_RADIANS (start_angle)); arcStopX = cx - width * cos (TO_RADIANS (start_angle + delta_angle)); arcStopY = cy + height * sin (TO_RADIANS (start_angle + delta_angle)); if (arcStartX != lastX) { m = True; lastX = arcStartX; fprintf (f, "X%ld", gerberX (PCB, lastX)); } if (arcStartY != lastY) { m = True; lastY = arcStartY; fprintf (f, "Y%ld", gerberY (PCB, lastY)); } if (m) fprintf (f, "D02*"); fprintf (f, "G75*G0%1dX%ldY%ldI%ldJ%ldD01*G01*\015\012", (delta_angle < 0) ? 2 : 3, gerberX (PCB, arcStopX), gerberY (PCB, arcStopY), gerberXOffset (PCB, cx - arcStartX), gerberYOffset (PCB, cy - arcStartY)); lastX = arcStopX; lastY = arcStopY; } static void gerber_fill_circle (hidGC gc, int cx, int cy, int radius) { use_gc(gc, radius); if (!f) return; if (is_drill) { if (n_pending_drills >= max_pending_drills) { max_pending_drills += 100; pending_drills = (PendingDrills *) realloc (pending_drills, max_pending_drills * sizeof(PendingDrills)); } pending_drills[n_pending_drills].x = cx; pending_drills[n_pending_drills].y = cy; pending_drills[n_pending_drills].diam = radius * 2; n_pending_drills ++; return; } else if (gc->drill) return; if (cx != lastX) { lastX = cx; fprintf (f, "X%ld", gerberX (PCB, lastX)); } if (cy != lastY) { lastY = cy; fprintf (f, "Y%ld", gerberY (PCB, lastY)); } fprintf (f, "D03*\015\012"); } static void gerber_fill_polygon (hidGC gc, int n_coords, int *x, int *y) { Boolean m = False; int i; int firstTime = 1; LocationType startX = 0, startY = 0; if (is_mask && current_mask == HID_MASK_BEFORE) return; use_gc(gc, 10*100); if (!f) return; fprintf (f, "G36*\015\012"); for (i=0; i