From d4c96f129d67f4713cfe8d636e831939b5ed93fc Mon Sep 17 00:00:00 2001 From: Werner Almesberger Date: Thu, 28 Jul 2011 22:31:25 -0300 Subject: [PATCH] ptrude/: path extrusion, work in progress --- ptrude/Makefile | 13 ++ ptrude/README | 24 ++++ ptrude/arc.fig | 68 +++++++++ ptrude/corner.fig | 68 +++++++++ ptrude/path.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++ ptrude/path.h | 28 ++++ ptrude/ptrude.c | 15 ++ ptrude/try | 14 ++ 8 files changed, 574 insertions(+) create mode 100644 ptrude/Makefile create mode 100644 ptrude/README create mode 100644 ptrude/arc.fig create mode 100644 ptrude/corner.fig create mode 100644 ptrude/path.c create mode 100644 ptrude/path.h create mode 100644 ptrude/ptrude.c create mode 100644 ptrude/try diff --git a/ptrude/Makefile b/ptrude/Makefile new file mode 100644 index 0000000..8b92aae --- /dev/null +++ b/ptrude/Makefile @@ -0,0 +1,13 @@ +CFLAGS = -Wall -g +LDFLAGS = -lm +OBJS = ptrude.o path.o + +.PHONY: clean try + +ptrude: $(OBJS) + +try: ptrude + ./ptrude 2D transform. + Affects only the next point. +x-coord y-coord + line endpoint + +#r=, #delta=, and #tag= modify the next point, e.g., + +0 0 +#r=2 +#tag=foo +1 0 +1 1 + +would set the bend radius at the corner (and the final point) to 2 mm +and the tag of the corner point to "foo". diff --git a/ptrude/arc.fig b/ptrude/arc.fig new file mode 100644 index 0000000..1fb3b37 --- /dev/null +++ b/ptrude/arc.fig @@ -0,0 +1,68 @@ +#FIG 3.2 Produced by xfig version 3.2.5b +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 3822.761 2922.761 3825 7425 7193 5908 8325 2925 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 3821.168 2921.168 3825 7290 4883 7159 8190 2925 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 3822.431 2923.366 3825 7560 4969 7416 8459 2948 +5 1 0 1 0 0 50 -1 -1 4.000 0 1 0 0 8324.358 7425.641 8775 7425 8706 7186 8325 6975 +5 1 0 1 0 0 50 -1 -1 4.000 0 0 0 0 3823.445 2923.445 5400 2925 5284 3517 3825 4500 +1 3 0 0 0 0 50 -1 20 0.000 1 0.0000 4725 7065 45 45 4725 7065 4770 7065 +1 3 0 0 0 0 50 -1 20 0.000 1 0.0000 7065 5625 45 45 7065 5625 7110 5625 +1 3 0 0 0 0 50 -1 20 0.000 1 0.0000 4950 7020 45 45 4950 7020 4995 7020 +1 3 0 0 0 0 50 -1 20 0.000 1 0.0000 7245 5400 45 45 7245 5400 7290 5400 +2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 900 7425 3825 7425 +2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 8325 2925 8325 1125 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 1 2 + 0 0 1.00 120.00 120.00 + 0 0 1.00 120.00 120.00 + 3870 2790 8235 2790 +2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 + 3825 7425 6390 6795 8235 4320 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3825 2925 8235 4320 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3825 2925 7290 5580 +2 1 1 2 0 0 50 -1 -1 6.000 0 0 -1 0 0 2 + 8235 4320 8280 3690 +2 1 0 1 0 0 50 -1 -1 4.000 0 0 -1 0 0 2 + 8325 2925 8325 7650 +2 1 0 1 0 0 50 -1 -1 4.000 0 0 -1 0 0 2 + 3825 7425 9000 7425 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9000 2925 3330 2925 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3825 2925 6390 6795 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3825 2475 3825 8100 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3825 2925 4860 7155 +4 1 0 50 -1 18 18 0.0000 4 150 105 6075 2655 r\001 +4 1 0 50 -1 18 18 0.0000 4 210 180 3690 7380 d\001 +4 1 0 50 -1 18 18 0.0000 4 210 180 3690 7695 d\001 +4 1 0 50 -1 18 18 0.0000 4 150 105 3645 5490 r\001 +4 1 0 50 -1 18 18 0.0000 4 210 375 4365 6390 r-d\001 +4 1 0 50 -1 18 18 0.0000 4 210 450 5580 6300 r+d\001 +4 1 0 50 -1 18 18 0.0000 4 210 375 6210 5175 r-d\001 +4 1 0 50 -1 18 18 0.0000 4 210 450 6840 4230 r+d\001 +4 1 0 50 -1 18 18 0.0000 4 150 165 4545 7695 u\001 +4 1 0 50 -1 18 18 0.0000 4 150 135 5625 7425 v\001 +4 1 0 50 -1 18 18 0.0000 4 150 135 7290 6390 v\001 +4 1 0 50 -1 18 18 0.0000 4 150 135 8055 5220 v\001 +4 0 0 50 -1 18 18 0.0000 4 150 855 8640 3690 ...+v+u\001 +4 1 0 50 -1 18 18 0.0000 4 195 240 8505 7380 2t\001 +4 0 0 50 -1 18 18 0.0000 4 270 2085 9405 7200 (r-d)^2+u^2=r^2\001 +4 0 0 50 -1 18 18 0.0000 4 270 2610 9405 7650 (r-d)^2+v^2=(r+d)^2\001 +4 0 0 50 -1 18 18 0.0000 4 270 1530 9405 8100 2t = 2p+n*q\001 +4 0 0 50 -1 18 18 0.0000 4 210 915 4410 3105 ...+q+p\001 +4 1 0 50 -1 18 18 0.0000 4 210 180 4950 3555 q\001 +4 1 0 50 -1 18 18 0.0000 4 210 180 4635 3870 q\001 +4 1 0 50 -1 18 18 0.0000 4 210 180 4320 4050 q\001 +4 1 0 50 -1 18 18 0.0000 4 210 180 3960 4185 p\001 diff --git a/ptrude/corner.fig b/ptrude/corner.fig new file mode 100644 index 0000000..d782b63 --- /dev/null +++ b/ptrude/corner.fig @@ -0,0 +1,68 @@ +#FIG 3.2 Produced by xfig version 3.2.5b +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +5 1 0 2 0 7 48 -1 -1 0.000 0 1 0 0 4494.036 1337.375 4500 9000 6203 8807 9909 6759 +5 1 0 1 0 7 48 -1 -1 0.000 0 1 0 0 4499.660 1353.986 5220 3105 5552 2928 5818 2713 +5 1 0 1 0 7 52 -1 -1 0.000 0 1 0 0 7651.482 8999.573 9000 9000 8926 8559 8604 8045 +5 1 0 1 0 7 52 -1 -1 0.000 0 1 0 0 3374.893 7199.935 3915 7200 3838 6922 2993 6818 +5 1 0 2 0 7 48 -1 -1 0.000 0 1 0 0 1798.655 6524.275 1800 7200 2162 7094 2277 6047 +1 3 0 0 0 7 46 -1 0 0.000 1 0.0000 2250 6255 45 45 2250 6255 2295 6255 +1 3 0 0 0 7 46 -1 0 0.000 1 0.0000 1935 7065 45 45 1935 7065 1980 7065 +1 3 0 0 0 7 46 -1 0 0.000 1 0.0000 4680 8820 45 45 4680 8820 4725 8820 +1 3 0 0 0 7 46 -1 0 0.000 1 0.0000 9630 6750 45 45 9630 6750 9675 6750 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9900 6750 4050 900 +2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 + 1575 9000 7650 9000 13050 3600 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7650 9000 4185 585 +2 1 0 1 0 7 52 -1 -1 0.000 0 0 -1 0 0 2 + 7650 9000 11025 9000 +2 1 0 2 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 + 675 7200 3375 7200 1125 4950 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 1800 7200 1800 5850 +2 1 0 1 0 7 52 -1 -1 0.000 0 0 -1 0 0 2 + 3375 7200 4050 7200 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2250 6075 1350 6975 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 3375 7200 1620 6435 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 1 2 + 0 0 1.00 120.00 120.00 + 0 0 1.00 120.00 120.00 + 4500 9135 7650 9135 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 1 2 + 0 0 1.00 90.00 90.00 + 0 0 1.00 90.00 90.00 + 1800 7335 3375 7335 +2 1 0 0 0 7 46 -1 0 0.000 0 0 -1 1 1 2 + 0 0 1.00 90.00 90.00 + 0 0 1.00 90.00 90.00 + 4500 1350 4500 9000 +2 1 0 0 0 7 46 -1 0 0.000 0 0 -1 1 1 2 + 0 0 1.00 90.00 90.00 + 0 0 1.00 90.00 90.00 + 7425 8415 7650 9000 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4500 9000 4500 675 +4 1 0 50 -1 18 18 0.0000 4 210 180 7650 8685 d\001 +4 0 0 50 -1 18 18 0.0000 4 150 105 4320 5580 r\001 +4 0 0 50 -1 18 18 0.0000 4 150 105 6030 5760 r\001 +4 0 0 50 -1 18 18 0.0000 4 195 90 5130 2475 t\001 +4 0 0 50 -1 18 18 0.0000 4 270 1830 7425 1125 cos t = r/(r+d)\001 +4 1 0 50 -1 18 18 0.0000 4 150 165 5985 9405 s\001 +4 0 0 50 -1 18 18 0.0000 4 210 1305 7425 1575 s = r*tan t\001 +4 0 0 50 -1 18 18 0.0000 4 195 240 8325 8775 2t\001 +4 0 0 48 -1 18 18 0.0000 4 195 240 3330 7020 2t\001 +4 1 0 48 -1 18 18 0.0000 4 195 90 2115 6525 t\001 +4 1 0 48 -1 18 18 0.0000 4 150 105 1665 7020 r\001 +4 1 0 48 -1 18 18 0.0000 4 210 180 2520 7110 d\001 +4 1 0 50 -1 18 18 0.0000 4 150 165 2610 7605 s\001 +4 0 0 50 -1 18 18 0.0000 4 270 2310 7425 2025 r^2+s^2 = (r+d)^2\001 diff --git a/ptrude/path.c b/ptrude/path.c new file mode 100644 index 0000000..150abd1 --- /dev/null +++ b/ptrude/path.c @@ -0,0 +1,344 @@ +#include +#include +#include +#include + +#include "path.h" + + +#define alloc_type(t) ((t *) malloc(sizeof(t))) +#define stralloc(s) strdup(s) + + +static double deg(double rad) +{ + return rad/M_PI*180.0; +} + + +static struct path *alloc_path(void) +{ + struct path *path; + + path = alloc_type(struct path); + path->vertices = NULL; + path->last = &path->vertices; + return path; +} + + +static struct vertex *alloc_vertex(void) +{ + struct vertex *v; + + v = alloc_type(struct vertex); + v->r = 0; + v->d = 0; + v->tag = NULL; + v->next = NULL; + return v; +} + + +static void free_vertex(struct vertex *v) +{ + free(v); +} + + +void free_path(struct path *path) +{ + struct vertex *v, *next; + + for (v = path->vertices; v; v = next) { + next = v->next; + free_vertex(v); + } + free(path); +} + + +static struct vertex *clone_vertex(const struct vertex *v) +{ + struct vertex *new; + + new = alloc_type(struct vertex); + *new = *v; + new->next = NULL; + return new; +} + + +static void append_vertex(struct path *path, struct vertex *v) +{ + *path->last = v; + path->last = &v->next; +} + + +static const struct vertex *add_vertex(struct path *path, double x, double y, + double r, double d, const char *tag) +{ + struct vertex *v; + + v = alloc_vertex(); + v->x = x; + v->y = y; + v->r = r; + v->d = d; + v->tag = tag; + append_vertex(path, v); + return v; +} + + +static const struct vertex *corner(struct path *path, const struct vertex *a, + const struct vertex *b, const struct vertex *c, double r, double d) +{ + double ax = b->x-a->x; + double ay = b->y-a->y; + double bx = c->x-b->x; + double by = c->y-b->y; + double aa = hypot(ax, ay); + double bb = hypot(bx, by); + double dp = ax*bx+ay*by; /* a * b = a*b*cos 2t */ + double cp = ax*by-ay*bx; /* |a x b| = a*b*sin 2t */ + double dd; /* "d" of the given vectors */ + double tt, s; + double t2, p, q, ang; + double u, v; + double f, x, y; + int n, i; + + /* + * http://en.wikipedia.org/wiki/Dot_product + * dp = a*b*cos 2t + * + * http://en.wikipedia.org/wiki/Cross_product + * cp = a*b*sin 2t + * + * http://en.wikipedia.org/wiki/Tangent_half-angle_formula + * tan t = sin 2t/(1+cos 2t) + */ + tt = cp/(aa*bb+dp); + + /* + * From s = r*tan t + */ + s = fabs(r*tt); + + /* + * From r^2+s^2 = (r+d)^2 + */ + dd = hypot(r, s)-r; + + fprintf(stderr, "a = (%g, %g)-(%g, %g) = (%g, %g); |a| = %g\n", + b->x, b->y, a->x, a->y, ax, ay, aa); + fprintf(stderr, "b = (%g, %g)-(%g, %g) = (%g, %g); |b| = %g\n", + c->x, c->y, b->x, b->y, bx, by, bb); + fprintf(stderr, "sin 2t = %g, cos 2t = %g, tan t = %g\n", + cp/aa/bb, dp/aa/bb, tt); + fprintf(stderr, "r = %g, d = %g, s = %g, dd = %g\n", r, d, s, dd); + + /* + * We only know how to make a rounded corner if two vectors are + * involved. They therefore have to be long enough to accommodate the + * entire arc, from beginning to end. Furthermore, we split the + * available length in half, one for the inbound arc, the other for the + * outbound arc. + */ + if (aa/2 < s) { + fprintf(stderr, "first vector is too short (%g/2 < %g)\n", + aa, s); + exit(1); + } + if (bb/2 < s) { + fprintf(stderr, "second vector is too short (%g/2 < %g)\n", + bb, s); + exit(1); + } + + /* + * If the corner is already smooth enough, we just keep what we have. + */ + if (dd <= d) { + append_vertex(path, clone_vertex(b)); + return b; + } + + /* Step 1: determine the total angle (2*t) */ + + t2 = acos(dp/aa/bb); + + /* + * Step 2: determine the maximum angle of the first and last segment. + * + * We use + * r*cos p = r-d + * cos p = 1-d/r + */ + + p = acos(1-d/r); + + /* + * Step 3: determine the maximum angle of intermediate segments (if + * there are any). + * + * We use + * (r+d)*cos q = r-d + * cos q = r-q/(r+d) + */ + + q = acos((r-d)/(r+d)); + + fprintf(stderr, "t2 = %g, p(max) = %g, q(max) = %g\n", + deg(t2), deg(p), deg(q)); + + /* + * Step 4: emit the starting point of the arc + */ + + f = s/aa; + x = b->x-f*ax; + y = b->y-f*ay; + add_vertex(path, x, y, b->r, b->d, b->tag); + + /* + * Step 5: determine if we need intermediate points. If yes, how many, + * and then proceed to add them. + */ + + if (t2 > 2*p) { + n = (int) ceil((t2-2*(p+q))/(2*q)); + + /* + * @@@ We should evenly distribute the slack, but that seems + * difficult. For now, we just center the polygon. + */ + q = (t2/2-p)/(n+1); + + double dir = copysign(1, cp); +#if 0 + if (cp < 0) { +// t2 = -t2; + q = -q; + p = -p; + } +#endif + + if (n) + ang = p+q; + else + ang = t2/2; + u = tan(p)*(r-d); + v = tan(q)*(r-d); + f = (u+v)/aa; + for (i = 0; i <= n; i++) { + x += f*ax*cos(ang-q)-dir*f*ay*sin(ang-q); + y += dir*f*ax*sin(ang-q)+f*ay*cos(ang-q); + fprintf(stderr, " %d/%d: %g %g @ %g\n", i, n, + x, y, deg(ang)); + add_vertex(path, x, y, 0, 0, NULL); + ang += 2*q; + f = (2*v)/aa; + } + } + + /* + * Step 6: emit the finishing point of the arc + */ + + f = s/bb; + return add_vertex(path, b->x+f*bx, b->y+f*by, 0, 0, NULL); +} + + +struct path *round_path(const struct path *path, double r, double d) +{ + struct path *new; + const struct vertex *prev, *v; + + new = alloc_path(); + prev = path->vertices; + if (!prev) + return new; + append_vertex(new, clone_vertex(prev)); + if (!prev->next) + return new; + + if (prev->r) + r = prev->r; + if (prev->d) + d = prev->d; + + for (v = prev->next; v->next; v = v->next) { + if (v->r) + r = v->r; + if (v->d) + d = v->d; + prev = corner(new, prev, v, v->next, r, d); + } + append_vertex(new, clone_vertex(v)); + return new; +} + + +struct path *load_path(FILE *file) +{ + struct path *path; + char buf[1100]; /* plenty :) */ + char buf2[sizeof(buf)]; + char *s; + float x, y, tmp; + float r = 0, d = 0; + const char *tag = NULL; + + path = alloc_path(); + while (fgets(buf, sizeof(buf),file)) { + s = strchr(buf, '\n'); + if (s) + *s = 0; + if (sscanf(buf, "#r=%f", &tmp) == 1) { + r = tmp; + continue; + } + if (sscanf(buf, "#delta=%f", &tmp) == 1) { + d = tmp; + continue; + } + if (sscanf(buf, "#tag=%s", buf2) == 1) { + tag = stralloc(buf2); + continue; + } + if (*buf == '#') + continue; + if (sscanf(buf, "%f %f", &x, &y) != 2) { + fprintf(stderr, "can't parse \"%s\"\n", buf); + exit(1); + } + + add_vertex(path, x, y, r, d, tag); + + r = 0; + d = 0; + tag = NULL; + } + + return path; +} + + +void save_path(FILE *file, const struct path *path) +{ + const struct vertex *v; + + for (v = path->vertices; v; v = v->next) { + if (v->r) + fprintf(file, "#r=%f\n", v->r); + if (v->d) + fprintf(file, "#delta=%f\n", v->d); + if (v->tag) + fprintf(file, "#delta=%f\n", v->d); + fprintf(file, "%f %f\n", v->x, v->y); + } +} diff --git a/ptrude/path.h b/ptrude/path.h new file mode 100644 index 0000000..8215298 --- /dev/null +++ b/ptrude/path.h @@ -0,0 +1,28 @@ +#ifndef PATH_H +#define PATH_H + +#include + + +struct vertex { + double x, y; + double r; /* minimum bend radius; 0 = use previous value */ + double d; /* max. distance of corner from ideal arc; 0 = prev */ + const char *tag; + struct vertex *next; +}; + +struct path { + struct vertex *vertices; + struct vertex **last; +}; + + +void free_path(struct path *path); + +struct path *round_path(const struct path *path, double r, double d); + +struct path *load_path(FILE *file); +void save_path(FILE *file, const struct path *path); + +#endif /* !PATH_H */ diff --git a/ptrude/ptrude.c b/ptrude/ptrude.c new file mode 100644 index 0000000..6df85ff --- /dev/null +++ b/ptrude/ptrude.c @@ -0,0 +1,15 @@ +#include + +#include "path.h" + + +int main(void) +{ + const struct path *path; + + path = load_path(stdin); + path = round_path(path, 1, 0.1); + save_path(stdout, path); + + return 0; +} diff --git a/ptrude/try b/ptrude/try new file mode 100644 index 0000000..422f5a5 --- /dev/null +++ b/ptrude/try @@ -0,0 +1,14 @@ +0 0 +#r=10 +#delta=0.01 +100 0 +100 100 +200 100 +100 200 +0 200 +100 100 +0 100 +0 50 +100 50 +150 0 +100 0