diff --git a/ptrude/path.c b/ptrude/path.c index bdd2e78..de9b72c 100644 --- a/ptrude/path.c +++ b/ptrude/path.c @@ -49,6 +49,7 @@ static struct vertex *alloc_vertex(void) v->d = 0; v->tag = NULL; v->next = NULL; + v->len = 0; return v; } @@ -89,7 +90,7 @@ static void append_vertex(struct path *path, struct vertex *v) } -static const struct vertex *add_vertex(struct path *path, double x, double y, +static struct vertex *add_vertex(struct path *path, double x, double y, double r, double d, const char *tag) { struct vertex *v; @@ -105,14 +106,47 @@ static const struct vertex *add_vertex(struct path *path, double x, double y, } +double path_set_length(struct path *path) +{ + struct vertex *v; + double sum = 0; + + if (!path->vertices) + return 0; + for (v = path->vertices; v->next; v = v->next) { + v->len = hypot(v->x-v->next->x, v->y-v->next->y); + sum += v->len; + } + v->len = 0; + return sum; +} + + +static void adjust_length(struct vertex *from, struct vertex *to, double len) +{ + struct vertex *v; + double sum, f; + + sum = 0; + for (v = from; v != to; v = v->next) { + v->len = hypot(v->x-v->next->x, v->y-v->next->y); + sum += v->len; + } + + f = len/sum; + for (v = from; v != to; v = v->next) + v->len *= f; +} + + /* - * "corner" replaces a corner with a ploygon if the corner is too sharp to - * be within distance "d" of the bend radius. This may change the point from + * "corner" replaces a corner with a ploygon if the corner is too sharp to be + * within distance "d" of the bend radius. This may change the point from * where we resume drawing (originally the corner point, "b"). "corner" * therefore returns the new end of the arc. */ -static const struct vertex *corner(struct path *path, const struct vertex *a, +static struct vertex *corner(struct path *path, struct vertex *a, const struct vertex *b, const struct vertex *c, double r, double d) { /* points to vectors */ @@ -148,6 +182,10 @@ static const struct vertex *corner(struct path *path, const struct vertex *a, double x, y; /* current position; for iteration */ int i; /* segment; for iteration */ + struct vertex *v0; /* first vertex of arc */ + struct vertex *v1; /* last vertex of arc */ + + /* * http://en.wikipedia.org/wiki/Dot_product * dp = a*b*cos 2t @@ -203,8 +241,9 @@ static const struct vertex *corner(struct path *path, const struct vertex *a, * If the corner is already smooth enough, we just keep what we have. */ if (dd <= d) { - append_vertex(path, clone_vertex(b)); - return b; + v1 = clone_vertex(b); + append_vertex(path, v1); + return v1; } /* Step 1: determine the total angle (2*t) */ @@ -243,7 +282,8 @@ static const struct vertex *corner(struct path *path, const struct vertex *a, f = s/aa; x = b->x-f*ax; y = b->y-f*ay; - add_vertex(path, x, y, b->r, b->d, b->tag); + v0 = add_vertex(path, x, y, b->r, b->d, b->tag); + a->len = a->len-s; /* * Step 5: determine if we need intermediate points. If yes, how many, @@ -298,21 +338,34 @@ static const struct vertex *corner(struct path *path, const struct vertex *a, */ f = s/bb; - return add_vertex(path, b->x+f*bx, b->y+f*by, 0, 0, NULL); + v1 = add_vertex(path, b->x+f*bx, b->y+f*by, 0, 0, NULL); + v1->len = b->len-s; + + /* + * Step 7: adjust the nominal length of the segments + */ + + adjust_length(v0, v1, 2*s); + + + return v1; } struct path *round_path(const struct path *path, double r, double d) { struct path *new; - const struct vertex *prev, *v; + struct vertex *prev; + const struct vertex *v; new = alloc_path(); - prev = path->vertices; - if (!prev) + if (!path->vertices) return new; - append_vertex(new, clone_vertex(prev)); - if (!prev->next) + + prev = clone_vertex(path->vertices); + append_vertex(new, prev); + + if (!path->vertices->next) return new; if (prev->r) @@ -320,7 +373,7 @@ struct path *round_path(const struct path *path, double r, double d) if (prev->d) d = prev->d; - for (v = prev->next; v->next; v = v->next) { + for (v = path->vertices->next; v->next; v = v->next) { if (v->r) r = v->r; if (v->d) @@ -332,6 +385,65 @@ struct path *round_path(const struct path *path, double r, double d) } +static void move_vertex(struct path *path, const struct vertex *v, + double nx, double ny, double d) +{ + struct vertex *new; + + new = clone_vertex(v); + new->x += nx*d; + new->y += ny*d; + append_vertex(path, new); +} + + +struct path *stretch_path(const struct path *path, double d) +{ + struct path *new; /* new path */ + const struct vertex *v; /* current vertex (for iteration) */ + const struct vertex *a, *b, *c; /* previous, current, next vertex */ + double nx, ny; /* 2D normals */ + double f; /* factor for normalization */ + double tx, ty; /* temporary 2D normals */ + + new = alloc_path(); + + a = path->vertices; + b = a->next; + nx = b->y-a->y; + ny = a->x-b->x; + f = hypot(nx, ny); + move_vertex(new, a, nx/f, ny/f, d); + + for (v = path->vertices->next; v->next; v = v->next) { + b = v; + c = v->next; + + tx = b->y-a->y; + ty = a->x-b->x; + f = hypot(tx, ty); + nx = tx/f; + ny = ty/f; + + tx = c->y-b->y; + ty = b->x-c->x; + f = hypot(tx, ty); + nx += tx/f; + ny += ty/f; + move_vertex(new, b, nx/2, ny/2, d); + + a = v; + } + + nx = v->y-a->y; + ny = a->x-v->x; + f = hypot(nx, ny); + move_vertex(new, v, nx/f, ny/f, d); + + return new; +} + + struct path *load_path(FILE *file) { struct path *path; diff --git a/ptrude/path.h b/ptrude/path.h index 5410c27..2797417 100644 --- a/ptrude/path.h +++ b/ptrude/path.h @@ -22,6 +22,14 @@ struct vertex { double d; /* max. distance of corner from ideal arc; 0 = prev */ const char *tag; struct vertex *next; + + /* + * "len" is set by path_set_len to the distance to the next vertex, or + * 0 if this is the last vertex in the path. round_path adjusts "len" + * such that it corresponds to the length of the same part of the + * original path. + */ + double len; }; struct path { @@ -32,7 +40,9 @@ struct path { void free_path(struct path *path); +double path_set_length(struct path *path); struct path *round_path(const struct path *path, double r, double d); +struct path *stretch_path(const struct path *path, double d); struct path *load_path(FILE *file); void save_path(FILE *file, const struct path *path);