/* * gerber.c - Gerber file input * * Written 2010, 2013, 2015 by Werner Almesberger * Copyright 2010, 2013, 2015 Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ /* * Note: this is limited to the Gerber produced by KiCad for the PCB Edge * layer. Furthermore, we ignore the tool diameter for now. * * The relevant details are nicely explained at * http://www.pcbmilling.com/Examples of Gerber and Excellon Data Files.htm */ #include #include #include #include #include "path.h" #include "gerber.h" /* KiCad Gerber uses 0.1 mil units */ #define KU2MM(in) ((in)/10000.0*25.4) /* * @@@ Very crude implementation of G03. Tested with only one example in * KiCad. * * Command definition from: * http://www.rulabinsky.com/cavd/text/chapa.html */ static void arc(struct path *path, const char *buf, double ax, double ay) { int xi, yi, cxi, cyi; double bx, by, cx, cy; double r, a, b, t; double x, y; if (sscanf(buf, "G03X%dY%dI%dJ%dD01*\n", &xi, &yi, &cxi, &cyi) != 4) return; bx = KU2MM(xi); by = KU2MM(yi); cx = KU2MM(cxi); cy = KU2MM(cyi); r = hypot(cx, cy); cx += ax; cy += ay; a = atan2(ay-cy, ax-cx); b = atan2(by-cy, bx-cx); if (a > b) b += 2*M_PI; //fprintf(stderr, "@(%g,%g) (%g,%g)-(%g-%g) %g %g\n", // cx, cy, ax, ay, bx, by, a/M_PI*180, b/M_PI*180); for (t = a; t <= b; t += M_PI/180) { /* @@@ 1 deg increment */ x = cx+r*cos(t); y = cy+r*sin(t); path_add(path, x, y, 0); } path_add(path, bx, by, 0); } struct path *gerber_read(const char *name, double r_tool_default) { FILE *file; int lineno = 0; char buf[1024]; struct path *paths = NULL, **anchor = &paths, *path = NULL; double start_x = 0, start_y = 0; int xi, yi, d; double x, y; file = name ? fopen(name, "r") : stdin; if (!file) { perror(name); exit(1); } while (fgets(buf, sizeof(buf), file)) { lineno++; if (!strncmp(buf, "%FS", 3)) { if (strcmp(buf, "%FSLAX34Y34*%\n")) { fprintf(stderr, "unrecognized format %s\n", buf); exit(1); } continue; } if (!strncmp(buf, "%MO", 3)) { if (strcmp(buf, "%MOIN*%\n")) { fprintf(stderr, "unrecognized mode %s\n", buf); exit(1); } continue; } if (!strncmp(buf, "G03", 3)) { if (path) abort(); path = path_new(r_tool_default, name); *anchor = path; anchor = &path->next; arc(path, buf, start_x, start_y); continue; } if (sscanf(buf, "X%dY%dD%d*\n", &xi, &yi, &d) != 3) continue; x = KU2MM(xi); y = KU2MM(yi); switch (d) { case 1: if (!path) { path = path_new(r_tool_default, name); *anchor = path; anchor = &path->next; path_add(path, start_x, start_y, 0); } path_add(path, x, y, 0); break; case 2: path = NULL; start_x = x; start_y = y; break; default: fprintf(stderr, "don't recognize D%d\n", d); exit(1); } } fclose(file); return path_connect(paths); } static int gerber_do_write(FILE *file, const struct path *paths) { const struct path *path; const struct point *p; fprintf(file, "G04 Generated by cameo*\n" "%%MOMM*%%\n" // dimensions are in mm "%%FSLAX33Y33*%%\n" // no leading zeroes; absolute; 3 digits int, // 3 digits fractional "G01*\n" // linear interpolation "%%ADD10C,0.01*%%\n" // aperture D10, 10 um circle "D10*\n"); // select D10 for (path = paths; path; path = path->next) for (p = path->first; p; p = p->next) fprintf(file, "X%.0fY%.0fD0%u*\n", p->x * 1000.0, p->y * 1000.0, p == path->first ? 2 : 1); fprintf(file, "M02**\n");// end of file if (file == stdout) { if (fflush(file) == EOF) return 0; } else { if (fclose(file) < 0) return 0; } return 1; } void gerber_write(const char *name, const struct path *paths) { FILE *file; file = name ? fopen(name, "w") : stdout; if (!file) { perror(name); exit(1); } if (!gerber_do_write(file, paths)) { perror(name ? name : "(stdout)"); exit(1); } }