diff --git a/cameo/Makefile b/cameo/Makefile index 1fff67f..2e0f659 100644 --- a/cameo/Makefile +++ b/cameo/Makefile @@ -15,7 +15,7 @@ PREFIX ?= /usr/local SHELL=/bin/bash MAIN=cameo -OBJS=cameo.o gnuplot.o path.o +OBJS=cameo.o gerber.o gnuplot.o path.o CFLAGS_WARN=-Wall -Wshadow -Wmissing-prototypes \ -Wmissing-declarations -Wno-format-zero-length diff --git a/cameo/cameo.c b/cameo/cameo.c index 8d82073..a7dea36 100644 --- a/cameo/cameo.c +++ b/cameo/cameo.c @@ -17,6 +17,7 @@ #include "path.h" #include "gnuplot.h" +#include "gerber.h" static int dog_bone = 0; @@ -65,9 +66,11 @@ static void process_paths(struct path *paths) static void usage(const char *name) { fprintf(stderr, -"usage: %s [-d] r_mm [in.gnuplot [out.gnuplot]]\n\n" +"usage: %s [-d] r_mm [in.gnuplot [out.gnuplot]]\n" +" %s -g [-d] r_mm [in.gerber [out.gnuplot]]\n\n" " -d put a dog-bone notch in each concave external corner\n" - , name); +" -g input format is KiCad Gerber, not gnuplot\n" + , name, name); exit(1); } @@ -75,15 +78,19 @@ static void usage(const char *name) int main(int argc, char **argv) { char *in = NULL, *out = NULL; + int gerber = 0; double r; struct path *paths; int c; - while ((c = getopt(argc, argv, "d")) != EOF) + while ((c = getopt(argc, argv, "dg")) != EOF) switch (c) { case 'd': dog_bone = 1; break; + case 'g': + gerber = 1; + break; default: usage(*argv); } @@ -102,7 +109,10 @@ int main(int argc, char **argv) usage(*argv); } - paths = gnuplot_read(in, r); + if (gerber) + paths = gerber_read(in, r); + else + paths = gnuplot_read(in, r); process_paths(paths); gnuplot_write(out, paths); diff --git a/cameo/gerber.c b/cameo/gerber.c new file mode 100644 index 0000000..9aca5f5 --- /dev/null +++ b/cameo/gerber.c @@ -0,0 +1,93 @@ +/* + * gerber.c - Gerber file input + * + * Written 2010 by Werner Almesberger + * Copyright 2010 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 "path.h" +#include "gerber.h" + + +/* KiCad Gerber uses 0.1 mil units */ + +#define KU2MM(in) ((in)/10000.0*25.4) + + +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; + int start_x = 0, start_y = 0; + int x, y, d; + + 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 (sscanf(buf, "X%dY%dD%d*\n", &x, &y, &d) != 3) + continue; + x = KU2MM(x); + y = KU2MM(y); + switch (d) { + case 1: + if (!path) { + path = path_new(r_tool_default); + *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); +} diff --git a/cameo/gerber.h b/cameo/gerber.h new file mode 100644 index 0000000..c5b8fea --- /dev/null +++ b/cameo/gerber.h @@ -0,0 +1,22 @@ +/* + * gerber.h - Gerber file input + * + * Written 2010 by Werner Almesberger + * Copyright 2010 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. + */ + +#ifndef GERBER_H +#define GERBER_H + +#include "path.h" + + +struct path *gerber_read(const char *name, double r_tool_default); + +#endif /* !GERBER_H */ + diff --git a/cameo/path.c b/cameo/path.c index 17789a0..bfe7068 100644 --- a/cameo/path.c +++ b/cameo/path.c @@ -147,6 +147,20 @@ struct path *path_reverse(const struct path *path) } +static void path_reverse_inplace(struct path *path) +{ + struct point *points = NULL, *p, *next; + + path->last = path->first; + for (p = path->first; p; p = next) { + next = p->next; + p->next = points; + points = p; + } + path->first = points; +} + + static struct point *offset_point(const struct point *a, const struct point *b, const struct point *c, double off, int left) { @@ -331,3 +345,54 @@ struct path *path_find_leftmost(struct path *path) } return best; } + + +static int points_eq(const struct point *a, const struct point *b) +{ + return a->x == b->x && a->y == b->y && a->z == b->z; +} + + +static int attr_eq(const struct path *a, const struct path *b) +{ + return a->r_tool == b->r_tool && a->outside == b->outside && + a->notch == b->notch; +} + + +struct path *path_connect(struct path *path) +{ + struct path **a, **b; + struct path *tmp; + +again: + for (a = &path; *a; a = &(*a)->next) + for (b = &(*a)->next; *b; b = &(*b)->next) { + if (!attr_eq(*a, *b)) + continue; + if (points_eq((*a)->last, (*b)->last)) + path_reverse_inplace(*b); + if (points_eq((*a)->last, (*b)->first)) { + (*a)->last->next = (*b)->first->next; + (*a)->last = (*b)->last; + free((*b)->first); + tmp = *b; + *b = tmp->next; + free(tmp); + goto again; + } + if (points_eq((*a)->first, (*b)->first)) + path_reverse_inplace(*b); + if (points_eq((*a)->first, (*b)->last)) { + (*b)->last->next = (*a)->first->next; + (*b)->last = (*a)->last; + free((*a)->first); + tmp = *a; + *a = *b; + free(tmp); + *b = (*b)->next; + goto again; + } + } + return path; +} diff --git a/cameo/path.h b/cameo/path.h index 351ecef..23bcafd 100644 --- a/cameo/path.h +++ b/cameo/path.h @@ -37,5 +37,6 @@ int path_tool_is_left(const struct path *path); struct path *path_offset(const struct path *path, int left, int notch); struct path *path_find_leftmost(struct path *path); void path_free(struct path *path); +struct path *path_connect(struct path *path); #endif /* !PATH_H */