Root/
| 1 | /* |
| 2 | * gerber.c - Gerber file input |
| 3 | * |
| 4 | * Written 2010, 2013, 2015, 2017 by Werner Almesberger |
| 5 | * Copyright 2010, 2013, 2015, 2017 Werner Almesberger |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | */ |
| 12 | |
| 13 | /* |
| 14 | * Note: this is limited to the Gerber produced by KiCad for the PCB Edge |
| 15 | * layer. Furthermore, we ignore the tool diameter for now. |
| 16 | * |
| 17 | * The relevant details are nicely explained at |
| 18 | * http://www.pcbmilling.com/Examples of Gerber and Excellon Data Files.htm |
| 19 | */ |
| 20 | |
| 21 | #include <stdlib.h> |
| 22 | #include <stdio.h> |
| 23 | #include <string.h> |
| 24 | #include <math.h> |
| 25 | |
| 26 | #include "path.h" |
| 27 | #include "gerber.h" |
| 28 | |
| 29 | |
| 30 | static double scale; /* KiCad Gerber units */ |
| 31 | |
| 32 | #define KU2MM(in) ((in)/scale) |
| 33 | |
| 34 | |
| 35 | /* |
| 36 | * @@@ Very crude implementation of G03. Tested with only one example in |
| 37 | * KiCad. |
| 38 | * |
| 39 | * Command definition from: |
| 40 | * http://www.rulabinsky.com/cavd/text/chapa.html |
| 41 | */ |
| 42 | |
| 43 | static void arc(struct path *path, const char *buf, double ax, double ay) |
| 44 | { |
| 45 | int xi, yi, cxi, cyi; |
| 46 | double bx, by, cx, cy; |
| 47 | double r, a, b, t; |
| 48 | double x, y; |
| 49 | |
| 50 | if (sscanf(buf, "G03X%dY%dI%dJ%dD01*\n", &xi, &yi, &cxi, &cyi) != 4) |
| 51 | return; |
| 52 | bx = KU2MM(xi); |
| 53 | by = KU2MM(yi); |
| 54 | cx = KU2MM(cxi); |
| 55 | cy = KU2MM(cyi); |
| 56 | r = hypot(cx, cy); |
| 57 | cx += ax; |
| 58 | cy += ay; |
| 59 | a = atan2(ay-cy, ax-cx); |
| 60 | b = atan2(by-cy, bx-cx); |
| 61 | if (a > b) |
| 62 | b += 2*M_PI; |
| 63 | //fprintf(stderr, "@(%g,%g) (%g,%g)-(%g-%g) %g %g\n", |
| 64 | // cx, cy, ax, ay, bx, by, a/M_PI*180, b/M_PI*180); |
| 65 | for (t = a; t <= b; t += M_PI/180) { /* @@@ 1 deg increment */ |
| 66 | x = cx+r*cos(t); |
| 67 | y = cy+r*sin(t); |
| 68 | path_add(path, x, y, 0); |
| 69 | } |
| 70 | path_add(path, bx, by, 0); |
| 71 | } |
| 72 | |
| 73 | |
| 74 | struct path *gerber_read(const char *name, double r_tool_default) |
| 75 | { |
| 76 | FILE *file; |
| 77 | int lineno = 0; |
| 78 | char buf[1024]; |
| 79 | struct path *paths = NULL, **anchor = &paths, *path = NULL; |
| 80 | double start_x = 0, start_y = 0; |
| 81 | int xi, yi, d; |
| 82 | double x, y; |
| 83 | |
| 84 | file = name ? fopen(name, "r") : stdin; |
| 85 | if (!file) { |
| 86 | perror(name); |
| 87 | exit(1); |
| 88 | } |
| 89 | |
| 90 | while (fgets(buf, sizeof(buf), file)) { |
| 91 | lineno++; |
| 92 | if (!strncmp(buf, "%FS", 3)) { |
| 93 | if (!strcmp(buf, "%FSLAX34Y34*%\n")) { |
| 94 | scale = 10 * 1000; |
| 95 | } else if (!strcmp(buf, "%FSLAX46Y46*%\n")) { |
| 96 | scale = 1000 * 1000; |
| 97 | } else { |
| 98 | fprintf(stderr, |
| 99 | "unrecognized format %s\n", buf); |
| 100 | exit(1); |
| 101 | } |
| 102 | continue; |
| 103 | } |
| 104 | /* @@@ we assume that %MO follows %FS */ |
| 105 | if (!strncmp(buf, "%MO", 3)) { |
| 106 | if (!strcmp(buf, "%MOIN*%\n")) { |
| 107 | scale /= 25.4; |
| 108 | } else if (strcmp(buf, "%MOMM*%\n")) { |
| 109 | fprintf(stderr, |
| 110 | "unrecognized mode %s\n", buf); |
| 111 | exit(1); |
| 112 | } |
| 113 | continue; |
| 114 | } |
| 115 | if (!strncmp(buf, "G03", 3)) { |
| 116 | if (path) |
| 117 | abort(); |
| 118 | path = path_new(r_tool_default, name); |
| 119 | *anchor = path; |
| 120 | anchor = &path->next; |
| 121 | arc(path, buf, start_x, start_y); |
| 122 | continue; |
| 123 | } |
| 124 | if (sscanf(buf, "X%dY%dD%d*\n", &xi, &yi, &d) != 3) |
| 125 | continue; |
| 126 | x = KU2MM(xi); |
| 127 | y = KU2MM(yi); |
| 128 | switch (d) { |
| 129 | case 1: |
| 130 | if (!path) { |
| 131 | path = path_new(r_tool_default, name); |
| 132 | *anchor = path; |
| 133 | anchor = &path->next; |
| 134 | path_add(path, start_x, start_y, 0); |
| 135 | } |
| 136 | path_add(path, x, y, 0); |
| 137 | break; |
| 138 | case 2: |
| 139 | path = NULL; |
| 140 | start_x = x; |
| 141 | start_y = y; |
| 142 | break; |
| 143 | default: |
| 144 | fprintf(stderr, "don't recognize D%d\n", d); |
| 145 | exit(1); |
| 146 | } |
| 147 | } |
| 148 | fclose(file); |
| 149 | return path_connect(paths); |
| 150 | } |
| 151 | |
| 152 | |
| 153 | static int gerber_do_write(FILE *file, const struct path *paths) |
| 154 | { |
| 155 | const struct path *path; |
| 156 | const struct point *p; |
| 157 | |
| 158 | fprintf(file, |
| 159 | "G04 Generated by cameo*\n" |
| 160 | "%%MOMM*%%\n" // dimensions are in mm |
| 161 | "%%FSLAX33Y33*%%\n" // no leading zeroes; absolute; 3 digits int, |
| 162 | // 3 digits fractional |
| 163 | "G01*\n" // linear interpolation |
| 164 | "%%ADD10C,0.01*%%\n" // aperture D10, 10 um circle |
| 165 | "D10*\n"); // select D10 |
| 166 | |
| 167 | for (path = paths; path; path = path->next) |
| 168 | for (p = path->first; p; p = p->next) |
| 169 | fprintf(file, "X%.0fY%.0fD0%u*\n", |
| 170 | p->x * 1000.0, p->y * 1000.0, |
| 171 | p == path->first ? 2 : 1); |
| 172 | |
| 173 | fprintf(file, "M02**\n");// end of file |
| 174 | |
| 175 | if (file == stdout) { |
| 176 | if (fflush(file) == EOF) |
| 177 | return 0; |
| 178 | } else { |
| 179 | if (fclose(file) < 0) |
| 180 | return 0; |
| 181 | } |
| 182 | return 1; |
| 183 | } |
| 184 | |
| 185 | |
| 186 | void gerber_write(const char *name, const struct path *paths) |
| 187 | { |
| 188 | FILE *file; |
| 189 | |
| 190 | file = name ? fopen(name, "w") : stdout; |
| 191 | if (!file) { |
| 192 | perror(name); |
| 193 | exit(1); |
| 194 | } |
| 195 | if (!gerber_do_write(file, paths)) { |
| 196 | perror(name ? name : "(stdout)"); |
| 197 | exit(1); |
| 198 | } |
| 199 | } |
| 200 |
Branches:
master
