%{ /* * lang.y - Toolpath adaptation language * * Written 2010-2013, 2015 by Werner Almesberger * Copyright 2010-2013, 2015 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include "path.h" #include "ops.h" #include "area.h" #include "gnuplot.h" #include "gerber.h" #include "excellon.h" #include "y.tab.h" static double xo = 0, yo = 0, zo = 0; /* origin */ static double rot = 0; static struct path *paths = NULL; static struct path *remain = NULL; #define MIL2MM(mil) ((mil)/1000*25.4) static void add_paths(struct path *new) { struct path **anchor = &paths; while (*anchor) anchor = &(*anchor)->next; *anchor = new; } static void translate(struct path *list, double x, double y, double z) { struct path *path; struct point *p; for (path = list; path; path = path->next) for (p = path->first; p; p = p->next) { p->x += x; p->y += y; p->z += z; } } static void rotate(struct path *list, double angle) { double m[2][2], tmp; struct point *p; angle = angle/180.0*M_PI; m[0][0] = cos(angle); m[0][1] = -sin(angle); m[1][0] = -m[0][1]; m[1][1] = m[0][0]; while (list) { for (p = list->first; p; p = p->next) { tmp = p->x*m[0][0]+p->y*m[0][1]; p->y = p->x*m[1][0]+p->y*m[1][1]; p->x = tmp; } list = list->next; } } static void flip(struct path *list, enum axis axis, double center) { const struct path *path; struct point *p; for (path = list; path; path = path->next) for (p = path->first; p; p = p->next) { if (axis == axis_x) p->x = 2*center-p->x; else p->y = 2*center-p->y; } } static void flip_center(struct path *list, enum axis axis) { double min = 0, max = 0; double coord; const struct path *path; const struct point *p; int first = 1; for (path = list; path; path = path->next) for (p = path->first; p; p = p->next) { coord = axis == axis_x ? p->x : p->y; if (first || coord < min) min = coord; if (first || coord > max) max = coord; first = 0; } flip(list, axis, (min+max)/2); } static double ref_pick_1(int ref, double a, double b) { switch (ref) { case 0: return a; case 1: return (a+b)/2; case 2: return b; default: abort(); } } static void ref_pick_xy(int ref, double xa, double ya, double xb, double yb, double *x, double *y) { *x = ref_pick_1((ref-1) % 3, xa, xb); *y = ref_pick_1((ref-1)/3, ya, yb); } static void bbox(const struct path *path, double *xa, double *ya, double *xb, double *yb) { const struct point *p; int first = 1; *xa = *ya = *xb = *yb = 0; while (path) { for (p = path->first; p; p = p->next) { if (first || p->x < *xa) *xa = p->x; if (first || p->x > *xb) *xb = p->x; if (first || p->y < *ya) *ya = p->y; if (first || p->y > *yb) *yb = p->y; first = 0; } path = path->next; } } static void align(int ref, double x, double y) { double xa = 0, ya = 0, xb = 0, yb = 0; double xr, yr, xd, yd; int first = 1; bbox(paths, &xa, &ya, &xb, &yb); ref_pick_xy(ref, xa, ya, xb, yb, &xr, &yr); xd = x-xr; yd = y-yr; translate(paths, xd, yd, 0); xo += xd; yo += yd; } static void clear_paths(void) { struct path *next; while (paths) { next = paths->next; path_free(paths); paths = next; } } static struct path **classify(struct path **anchor, struct path *path) { struct path **walk, *next; if (!path) return &(*anchor)->next; for (walk = &paths; *walk; walk = &(*walk)->next); *walk = path; next = (*anchor)->next; path_free(*anchor); *anchor = next; return anchor; } %} %union { double num; char *str; int flag; enum { OO_DOG = 1 << 0, OO_INSIDE = 1 << 1, } oopt; enum axis { axis_x, axis_y } axis; }; %token TOK_ALIGN TOK_AREA TOK_ARRAY TOK_CLEAR TOK_DRILL TOK_EMPTY %token TOK_FLIP TOK_KEEP TOK_MILL TOK_OFFSET TOK_OPTIMIZE %token TOK_OUTSIDE TOK_PURGE TOK_REMAINDER %token TOK_REMOVE TOK_RESET %token TOK_REVERSE TOK_ROTATE TOK_STATS TOK_STL TOK_TRANSLATE %token TOK_X TOK_Y TOK_Z %token TOK_APPEND TOK_GERBER TOK_GNUPLOT TOK_EXCELLON TOK_WRITE %token TOK_WRITE_GERBER %token TOK_DOG TOK_INSIDE TOK_ANY %token NUM_EXP_MIL NUM_EXP_MM NUM_IMP_MIL NUM_IMP_MM REF %token STRING %type opt_filename %type dimen number opt_dimen x_size y_size %type opt_any %type offset_options offset_option %type axis %% all: | command all ; command: TOK_ALIGN REF dimen dimen { align((int) $2, $3, $4); } | TOK_ALIGN REF dimen dimen dimen dimen { int ref = $2; double x, y; if ($3 > $5) yyerror("left edge > right edge"); if ($4 > $6) yyerror("bottom > top"); ref_pick_xy(ref, $3, $4, $5, $6, &x, &y); align(ref, x, y); } | TOK_ARRAY x_size y_size number number { double x = $2*$4; double y = $3*$5; translate(paths, x, y, 0); xo += x; yo += y; } | TOK_CLEAR { clear_paths(); } | TOK_RESET { xo = yo = rot = 0; } | TOK_OFFSET offset_options { struct path *new; new = tool_comp_paths(paths, !!($2 & OO_DOG), !!($2 & OO_INSIDE)); clear_paths(); paths = new; } | TOK_OPTIMIZE { paths = optimize_paths(paths); } | TOK_OUTSIDE { struct path *a; struct path *b; for (a = paths; a; a = a->next) { for (b = paths; b; b = b->next) if (a != b && path_is_inside(a, b)) break; if (!b) a->outside = 1; } } | TOK_REVERSE { struct path *tmp; tmp = reverse_paths(paths); clear_paths(); paths = tmp; } | TOK_ROTATE number { rotate(paths, $2); rot += $2; } | TOK_FLIP axis { flip_center(paths, $2); } | TOK_FLIP axis dimen { flip(paths, $2, $3); } | TOK_STATS { path_stats(paths); } | TOK_TRANSLATE dimen dimen { translate(paths, $2, $3, 0); xo += $2; yo += $3; } | TOK_Z dimen { zo += $2; } | TOK_Z dimen dimen { zo += $3-$2; } | TOK_GERBER dimen opt_filename { struct path *new; new = gerber_read($3, $2/2); rotate(new, rot); translate(new, xo, yo, 0); add_paths(new); } | TOK_GNUPLOT dimen opt_filename { struct path *new; new = gnuplot_read($3, $2/2); rotate(new, rot); translate(new, xo, yo, 0); add_paths(new); } | TOK_EXCELLON opt_filename { struct path *new; new = excellon_read($2); rotate(new, rot); translate(new, xo, yo, 0); add_paths(new); } | TOK_WRITE opt_filename { translate(paths, 0, 0, zo); gnuplot_write($2, paths); translate(paths, 0, 0, -zo); } | TOK_WRITE_GERBER opt_filename { gerber_write($2, paths); } | TOK_APPEND opt_filename { translate(paths, 0, 0, zo); gnuplot_append($2, paths); translate(paths, 0, 0, -zo); } | TOK_DRILL dimen opt_comma dimen { struct path **walk; remain = paths; paths = NULL; walk = &remain; while (*walk) walk = classify(walk, try_drill(*walk, $2, $4)); } | TOK_AREA dimen { struct path *tmp; tmp = area(paths, $2); clear_paths(); paths = tmp; } | TOK_STL opt_filename { stl($2, paths); } | TOK_MILL opt_any dimen dimen { struct path **walk; remain = paths; paths = NULL; walk = &remain; while (*walk) walk = classify(walk, try_mill(*walk, $3, $4, $2)); } | TOK_REMAINDER { clear_paths(); paths = remain; remain = NULL; } | TOK_EMPTY { if (paths) yyerror("path list is not empty"); } | TOK_KEEP dimen dimen dimen dimen { struct path *tmp; tmp = select_paths(paths, $2, $3, $4, $5, 1); clear_paths(); paths = tmp; } | TOK_REMOVE dimen dimen dimen dimen { struct path *tmp; tmp = select_paths(paths, $2, $3, $4, $5, 0); clear_paths(); paths = tmp; } | TOK_PURGE opt_dimen { struct path *tmp; tmp = purge_paths(paths, $2); clear_paths(); paths = tmp; } ; opt_filename: { $$ = NULL; } | STRING { $$ = $1; } ; dimen: NUM_EXP_MM { $$ = $1; } | NUM_IMP_MM { $$ = $1; } | NUM_EXP_MIL { $$ = MIL2MM($1); } | NUM_IMP_MIL { $$ = MIL2MM($1); } ; opt_dimen: { $$ = 0; } | dimen { $$ = $1; } ; x_size: dimen { $$ = $1; } | '+' dimen { double xa, ya, xb, yb; bbox(paths, &xa, &ya, &xb, &yb); $$ = xb-xa+$2; } ; y_size: dimen { $$ = $1; } | '+' dimen { double xa, ya, xb, yb; bbox(paths, &xa, &ya, &xb, &yb); $$ = yb-ya+$2; } ; number: NUM_IMP_MIL { $$ = $1; } | NUM_IMP_MM { $$ = $1; } ; offset_options: { $$ = 0; } | offset_option offset_options { $$ = $1 | $2; } ; offset_option: TOK_DOG { $$ = OO_DOG; } | TOK_INSIDE { $$ = OO_INSIDE; } ; axis: TOK_X { $$ = axis_x; } | TOK_Y { $$ = axis_y; } ; opt_comma: | ',' ; opt_any: { $$ = 0; } | TOK_ANY { $$ = 1; } ;