diff --git a/cameo/Makefile b/cameo/Makefile index 549e649..790ae0e 100644 --- a/cameo/Makefile +++ b/cameo/Makefile @@ -1,8 +1,8 @@ # # Makefile - Makefile of cameo # -# Written 2010 by Werner Almesberger -# Copyright 2010 by Werner Almesberger +# Written 2010, 2012 by Werner Almesberger +# Copyright 2010, 2012 by 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 @@ -15,7 +15,7 @@ PREFIX ?= /usr/local SHELL=/bin/bash MAIN=cameo -OBJS=cameo.o excellon.o gerber.o gnuplot.o ops.o path.o shape.o \ +OBJS=cameo.o excellon.o area.o gerber.o gnuplot.o ops.o path.o shape.o \ lex.yy.o y.tab.o CFLAGS_WARN=-Wall -Wshadow -Wmissing-prototypes \ diff --git a/cameo/README b/cameo/README index eea93a5..42ce688 100644 --- a/cameo/README +++ b/cameo/README @@ -206,7 +206,7 @@ Tool path optimization: Try to reduce the movements made between paths by reordering the paths. Note that this disturbs the order generated by "offset" and should thus -not be used on paths that to be executed in a specific sequence. +not be used on paths that are to be executed in a specific sequence. Statistics: diff --git a/cameo/area.c b/cameo/area.c new file mode 100644 index 0000000..01057c9 --- /dev/null +++ b/cameo/area.c @@ -0,0 +1,308 @@ +/* + * area.c - Area fill + * + * Written 2012 by Werner Almesberger + * Copyright 2012 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. + */ + + +#include +#include +#include +#include + +#include "util.h" +#include "path.h" +#include "area.h" + + +#define EPSILON 0.0001 + + +static int bbox(const struct path *path, + double *xa, double *ya, double *xb, double *yb) +{ + const struct point *p = path->first; + + if (!p) + return 0; + *xa = *xb = p->x; + *ya = *yb = p->y; + while (p) { + if (p->x < *xa) + *xa = p->x; + if (p->x > *xb) + *xb = p->x; + if (p->y < *ya) + *ya = p->y; + if (p->y > *yb) + *yb = p->y; + p = p->next; + } + return 1; +} + + +/* + * @@@ this is a bit too simple. E.g., it would report A as being inside B + * in this case: + * + * +---+ + * +---+ | | + * | A | | | + * +---+ | | + * | B | + * +--------+ | + * | | + * +------------+ + */ + +static int is_inside(const struct path *a, const struct path *b) +{ + double xa, ya, xb, yb; + const struct point *p; + + if (!bbox(b, &xa, &ya, &xb, &yb)) + return 0; + for (p = a->first; p; p = p->next) + if (p->x < xa || p->x > xb || + p->y < ya || p->y > yb) + return 0; + return 1; +} + + +/* + * Solve + * + * ax+na*bx = cx+nb*dx + * ay+na*by = cy+nb*dy + * + * which is + * + * na*bx + nb*-dx = cx-ax + * na*by + nb*-dy = cy-ay + * + * which we the solve with Cramer's rule: + * http://en.wikipedia.org/wiki/Cramer's_rule + */ + +static int intersect(double ax, double ay, double bx, double by, + double cx, double cy, double dx, double dy, double *na, double *nb) +{ + double det; + + det = dx*by-bx*dy; + if (fabs(det) < EPSILON) + return 0; + + *na = (dx*(cy-ay)-dy*(cx-ax))/det; + *nb = (bx*(cy-ay)-by*(cx-ax))/det; + return 1; +} + + +/* + * Solve + * + * (ax+n*bx-cx)^2+(ay+n*by-cy)^2 = r^2 for n + * + * http://en.wikipedia.org/wiki/Quadratic_equation + */ + +static int touch(double ax, double ay, double bx, double by, + double cx, double cy, double r, double *n) +{ + double dx = cx-ax; + double dy = cy-ay; + double a = bx*bx+by*by; + double b = -2*bx*dx-2*by*dy; + double c = dx*dx+dy*dy-r*r; + double d, n0, n1; + + d = b*b-4*a*c; + if (d < 0) + return 0; + d = sqrt(d); + n0 = (-b-d)/2/a; + n1 = (-b+d)/2/a; + + if (n0 > 0) { + *n = n0; + return 1; + } + if (n1 > 0) { + *n = n1; + return 1; + } + return 0; +} + + +static int hit_segment(double fx, double fy, double tx, double ty, + const struct point *a, const struct point *b, double r, double *n) +{ + double dx, dy, d; + double px, py; + double na, nb; + +printf(" seg (%g,%g)+(%g,%g) -> (%g,%g)-(%g,%g)\n", + fx, fy, tx, ty, a->x, a->y, b->x, b->y); + tx -= fx; + ty -= fy; + + dx = b->x-a->x; + dy = b->y-a->y; + d = hypot(dx, dy); + + px = a->x-dy/d*r; + py = a->y+dx/d*r; + + if (!intersect(fx, fy, tx, ty, px, py, dx, dy, &na, &nb)) + return 0; +printf("\tna %g (%g) nb %g (%g)\n", na, fx+tx*na, nb, fx+tx*nb); + if (nb <= 0) { + if (!touch(fx, fy, tx, ty, a->x, a->y, r, &na)) + return 0; + } + if (nb >= 1) { + if (!touch(fx, fy, tx, ty, b->x, b->y, r, &na)) + return 0; + } + if (na <= 0 || na >= 1) + return 0; + *n = na; + return 1; +} + + +static int hit_path(double fx, double fy, double tx, double ty, + const struct path *path, int inside, double r, double *x) +{ + const struct point *p; + int left; + double nx, tmp; + int found = 0; + + left = path_tool_is_left(path); + if (inside) + left = !left; + for (p = path->first; p != path->last; p = p->next) { + if (hit_segment(fx, fy, tx, ty, + left ? p : p->next, left ? p->next : p, r, &tmp)) { + if (!found || nx > tmp) + nx = tmp; + found = 1; + } + } + if (found) + *x = fx+nx*(tx-fx); + return found; +} + + +static const struct path **subordinates(const struct path *paths, + const struct path *path) +{ + const struct path **sub, **w, **a, **b;; + const struct path *p; + int n = 0; + + for (p = paths; p; p = p->next) + n++; + sub = alloc_size(sizeof(struct path *)*n); + w = sub; + for (p = paths; p; p = p->next) + if (p != path && is_inside(p, path) && !is_inside(path, p)) + *w++ = p; + *w = NULL; + for (a = sub; a != w; a++) + for (b = sub; b != w; b++) + if (a != b && is_inside(*a, *b)) { + *a = *w--; + *w = NULL; + a--; + break; + } + return sub; +} + + +static void do_line(const struct path *path, const struct path **sub, + double xa, double xb, double y, double r_tool, struct path **res) +{ + const struct path *last = path; + const struct path **s; + struct path *new; + double x, next; + +printf(" y=%g\n", y); + if (!hit_path(xa-3*r_tool, y, xb, y, last, 1, r_tool, &x)) + return; + while (1) { +printf(" x=%g\n", x); + next = xb; + last = NULL; + if (hit_path(x, y, xb, y, path, 1, r_tool, &next)) + last = path; + for (s = sub; *s; s++) + if (hit_path(x, y, next, y, *s, 0, r_tool, &next)) + last = *s; + new = path_new(r_tool, ""); + path_add(new, x, y, path->first->z); + path_add(new, next, y, path->first->z); + new->next = *res; + *res = new; + if (!last) + return; + if (!hit_path(next+EPSILON, y, xb, y, last, last == path, + r_tool, &x)) + return; + } +} + + +static void fill_path(const struct path *paths, const struct path *path, + double r_tool, double overlap, struct path **res) +{ + const struct path **sub, **s; + const struct path **sub2, **s2; + double xa, ya, xb, yb; + int n, i; + + if (!bbox(path, &xa, &ya, &xb, &yb)) + return; + sub = subordinates(paths, path); + xa += r_tool; + ya += r_tool; + xb -= r_tool; + yb -= r_tool; + n = ceil((yb-ya)/(2*r_tool-overlap)); +printf("x[%g:%g] y[%g:%g] n=%d\n", xa, xb, ya, yb, n); + for (i = 0; i <= n; i++) + do_line(path, sub, xa, xb, ya+(yb-ya)*((double) i/n), r_tool, + res); + for (s = sub; *s; s++) { + sub2 = subordinates(paths, *s); + for (s2 = sub2; *s2; s2++) + fill_path(paths, *s2, r_tool, overlap, res); + free(sub2); + } + free(sub); +} + + +struct path *area(const struct path *path, double r_tool, double overlap) +{ + struct path *res = NULL; + + if (!path) + return NULL; + fill_path(path, path_find_leftmost(path), r_tool, overlap, &res); + return res; +} diff --git a/cameo/area.h b/cameo/area.h new file mode 100644 index 0000000..5f1c907 --- /dev/null +++ b/cameo/area.h @@ -0,0 +1,23 @@ +/* + * area.h - Area fill + * + * Written 2012 by Werner Almesberger + * Copyright 2012 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 AREA_H +#define AREA_H + + +#include "path.h" + + +struct path *area(const struct path *path, double r_tool, double overlap); + +#endif /* !AREA_H */ diff --git a/cameo/lang.l b/cameo/lang.l index f8b42c3..361588c 100644 --- a/cameo/lang.l +++ b/cameo/lang.l @@ -2,8 +2,8 @@ /* * lang.l - Toolpath adaptation language * - * Written 2010-2011 by Werner Almesberger - * Copyright 2010-2011 by Werner Almesberger + * Written 2010-2012 by Werner Almesberger + * Copyright 2010-2012 by 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 @@ -44,6 +44,7 @@ NUM -?[0-9]+\.?[0-9]* clear return TOK_CLEAR; drill return TOK_DRILL; empty return TOK_EMPTY; +area return TOK_AREA; mill return TOK_MILL; offset return TOK_OFFSET; optimize return TOK_OPTIMIZE; diff --git a/cameo/lang.y b/cameo/lang.y index 2806b6c..5757934 100644 --- a/cameo/lang.y +++ b/cameo/lang.y @@ -2,8 +2,8 @@ /* * lang.y - Toolpath adaptation language * - * Written 2010-2011 by Werner Almesberger - * Copyright 2010-2011 by Werner Almesberger + * Written 2010-2012 by Werner Almesberger + * Copyright 2010-2012 by 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 @@ -18,6 +18,7 @@ #include "path.h" #include "ops.h" +#include "area.h" #include "gnuplot.h" #include "gerber.h" #include "excellon.h" @@ -186,7 +187,7 @@ static struct path **classify(struct path **anchor, struct path *path) }; -%token TOK_ALIGN TOK_ARRAY TOK_CLEAR TOK_DRILL TOK_EMPTY +%token TOK_ALIGN TOK_ARRAY TOK_CLEAR TOK_DRILL TOK_EMPTY TOK_AREA %token TOK_MILL TOK_OFFSET TOK_OPTIMIZE TOK_REMAINDER TOK_RESET %token TOK_ROTATE TOK_STATS TOK_TRANSLATE TOK_Z %token TOK_APPEND TOK_GERBER TOK_GNUPLOT TOK_EXCELLON TOK_WRITE @@ -327,6 +328,14 @@ command: walk = classify(walk, try_drill(*walk, $2, $4)); } + | TOK_AREA dimen opt_comma dimen + { + struct path *tmp; + + tmp = area(paths, $2/2, $4); + clear_paths(); + paths = tmp; + } | TOK_MILL opt_any dimen dimen { struct path **walk;