From be6d3cbb485c5f7e9dd6fe22bf54245c79b10b71 Mon Sep 17 00:00:00 2001 From: Matthias Kramm Date: Sat, 6 Jun 2009 14:32:42 +0200 Subject: [PATCH] fixed bevel joins --- lib/gfxpoly/convert.c | 3 ++ lib/gfxpoly/stroke.c | 95 ++++++++++++++++++++++++--------------------- lib/gfxpoly/stroke.h | 1 + lib/gfxpoly/test_stroke.c | 70 ++++++++++++++++++++++++++++++++- lib/gfxtools.c | 20 +++++----- 5 files changed, 133 insertions(+), 56 deletions(-) diff --git a/lib/gfxpoly/convert.c b/lib/gfxpoly/convert.c index 64cef43..9d8ecab 100644 --- a/lib/gfxpoly/convert.c +++ b/lib/gfxpoly/convert.c @@ -446,6 +446,9 @@ gfxline_t*gfxline_from_gfxpoly(gfxpoly_t*poly) pos = stroke->num_points-1; incr = -1; } + /* TODO: keeping the up/down order intact increases the polygon size + considerably. If this is going to be an even/odd polygon, + we could ignore it and save some space */ if(last.x != stroke->points[pos].x || last.y != stroke->points[pos].y) { l[count].x = stroke->points[pos].x * poly->gridsize; l[count].y = stroke->points[pos].y * poly->gridsize; diff --git a/lib/gfxpoly/stroke.c b/lib/gfxpoly/stroke.c index 70a51d0..9735c1b 100644 --- a/lib/gfxpoly/stroke.c +++ b/lib/gfxpoly/stroke.c @@ -37,17 +37,15 @@ static void draw_arc(gfxdrawer_t*draw, double x, double y, double a1, double a2, possible values for step */ double r2 = r*(2-sqrt(0.5+0.5*cos(step))); + draw->lineTo(draw, x+cos(a1)*r,y+sin(a1)*r); for(t=1;t<=steps;t++) { double a = a1 + t*step; double c = cos(a)*r; double s = sin(a)*r; double xx = c + x; double yy = s + y; - //double dx = (s*step/2 + lastx); - //double dy = (-c*step/2 + lasty); double dx = x + cos(a-step/2)*r2; double dy = y + sin(a-step/2)*r2; - //draw->lineTo(draw, xx, yy); draw->splineTo(draw, dx, dy, xx, yy); lastx = xx; lasty = yy; @@ -62,38 +60,43 @@ static void draw_single_stroke(gfxpoint_t*p, int num, gfxdrawer_t*draw, double w /* remove duplicate points */ int s=1,t; + gfxpoint_t last = p[0]; for(t=1;t2 && p[0].x == p[num-1].x && p[0].y == p[num-1].y); int start = 0; int end = num-1; int incr = 1; int pos = 0; - double alimit = atan(limit / width); - + double lastw=0; /* iterate through the points two times: first forward, then backward, adding a stroke outline to the right side and line caps after each pass */ int pass; - double lastw=0; for(pass=0;pass<2;pass++) { + if(closed) { + double dx = p[end].x - p[end-incr].x; + double dy = p[end].y - p[end-incr].y; + lastw = atan2(dy,dx); + if(lastw<0) lastw+=M_PI*2; + } + int pos; for(pos=start;pos!=end;pos+=incr) { //printf("%d) %.2f %.2f\n", pos, p[pos].x, p[pos].y); double dx = p[pos+incr].x - p[pos].x; double dy = p[pos+incr].y - p[pos].y; - double l = sqrt(dx*dx+dy*dy); double w = atan2(dy,dx); if(w<0) w+=M_PI*2; - if(pos!=start) { + if(closed || pos!=start) { double d = w-lastw; leftright_t turn; if(d>=0 && dlineTo(draw, p[pos].x+addx, p[pos].y+addy); - } else { - /* convert to bevel join, which always looks the same (is - independent of miterLimit TODO: verify this */ - } + double xw = M_PI/2 - d/2; + if(xw>0) { + double r2 = 1.0 / sin(M_PI/2-d/2); + if(r2 < limit) { + r2 *= width; + double addx = cos(lastw-M_PI/2+d/2)*r2; + double addy = sin(lastw-M_PI/2+d/2)*r2; + draw->lineTo(draw, p[pos].x+addx, p[pos].y+addy); + } + } } } double addx = cos(w-M_PI/2)*width; double addy = sin(w-M_PI/2)*width; draw->lineTo(draw, p[pos].x+addx, p[pos].y+addy); - //printf("-- %.2f %.2f (angle:%d)\n", px1, py1, (int)(180*w/M_PI)); double px2 = p[pos+incr].x + addx; double py2 = p[pos+incr].y + addy; - //printf("-- %.2f %.2f (angle:%d)\n", px2, py2, (int)(180*w/M_PI)); draw->lineTo(draw, p[pos+incr].x+addx, p[pos+incr].y+addy); - lastw = w; } - /* draw stroke ends. We draw duplicates of some points here. The drawer - implementation should be smart enough to remove them. */ - double c = cos(lastw-M_PI/2)*width; - double s = sin(lastw-M_PI/2)*width; - if(cap == gfx_capButt) { - draw->lineTo(draw, p[pos].x+c, p[pos].y+s); - draw->lineTo(draw, p[pos].x-c, p[pos].y-s); - } else if(cap == gfx_capRound) { - draw->lineTo(draw, p[pos].x+c, p[pos].y+s); - draw_arc(draw, p[pos].x, p[pos].y, lastw-M_PI/2, lastw+M_PI/2, width); - } else if(cap == gfx_capSquare) { + + if(closed) { + draw->close(draw); + } else { + /* draw stroke ends. We draw duplicates of some points here. The drawer + implementation should be smart enough to remove them. */ double c = cos(lastw-M_PI/2)*width; double s = sin(lastw-M_PI/2)*width; - draw->lineTo(draw, p[pos].x+c, p[pos].y+s); - draw->lineTo(draw, p[pos].x+c-s, p[pos].y+s+c); - draw->lineTo(draw, p[pos].x-c-s, p[pos].y-s+c); - draw->lineTo(draw, p[pos].x-c, p[pos].y-s); + if(cap == gfx_capButt) { + draw->lineTo(draw, p[pos].x+c, p[pos].y+s); + draw->lineTo(draw, p[pos].x-c, p[pos].y-s); + } else if(cap == gfx_capRound) { + draw_arc(draw, p[pos].x, p[pos].y, lastw-M_PI/2, lastw+M_PI/2, width); + } else if(cap == gfx_capSquare) { + double c = cos(lastw-M_PI/2)*width; + double s = sin(lastw-M_PI/2)*width; + draw->lineTo(draw, p[pos].x+c, p[pos].y+s); + draw->lineTo(draw, p[pos].x+c-s, p[pos].y+s+c); + draw->lineTo(draw, p[pos].x-c-s, p[pos].y-s+c); + draw->lineTo(draw, p[pos].x-c, p[pos].y-s); + } + lastw += M_PI; // for dots } start=num-1; end=0; incr=-1; - lastw += M_PI; // for dots } - draw->close(draw); + if(!closed) + draw->close(draw); } -static void draw_stroke(gfxline_t*start, gfxdrawer_t*draw, double width, gfx_capType cap, gfx_joinType join, double miterLimit) +void draw_stroke(gfxline_t*start, gfxdrawer_t*draw, double width, gfx_capType cap, gfx_joinType join, double miterLimit) { if(!start) return; diff --git a/lib/gfxpoly/stroke.h b/lib/gfxpoly/stroke.h index 5f54a6e..4b52d52 100644 --- a/lib/gfxpoly/stroke.h +++ b/lib/gfxpoly/stroke.h @@ -3,4 +3,5 @@ #include "../gfxdevice.h" #include "poly.h" gfxpoly_t* gfxpoly_from_stroke(gfxline_t*line, gfxcoord_t width, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit, double gridsize); +void draw_stroke(gfxline_t*start, gfxdrawer_t*draw, double width, gfx_capType cap, gfx_joinType join, double miterLimit); #endif diff --git a/lib/gfxpoly/test_stroke.c b/lib/gfxpoly/test_stroke.c index b05d710..d468cb8 100644 --- a/lib/gfxpoly/test_stroke.c +++ b/lib/gfxpoly/test_stroke.c @@ -3,7 +3,7 @@ #include "stroke.h" #include "convert.h" -int main() +int test_stroke1() { gfxline_t l[512], f[256*5]; @@ -130,3 +130,71 @@ int main() result->save(result, "test.swf"); result->destroy(result); } + +int test_stroke2() +{ + gfxline_t l[4]; + l[0].type = gfx_moveTo; + l[0].x = 100;l[0].sx=2; + l[0].y = 100;l[0].sy=2; + l[0].next = &l[1]; + l[1].type = gfx_lineTo; + l[1].x = 100;l[1].sx=2; + l[1].y = 200;l[1].sy=-2; + l[1].next = &l[2]; + l[2].type = gfx_lineTo; + l[2].x = 250;l[2].sx=4; + l[2].y = 200;l[2].sy=0; + l[2].next = &l[3]; + l[3].type = gfx_lineTo; + l[3].x = 200;l[3].sx=0; + l[3].y = 150;l[3].sy=4; + l[3].next = 0; + + + gfxdevice_t dev; + gfxdevice_swf_init(&dev); + dev.setparameter(&dev, "framerate", "25.0"); + int t; + for(t=0;t<300;t++) { + dev.startpage(&dev, 700,700); + gfxline_t*g = l; + while(g) { + g->x += g->sx; + g->y += g->sy; + if(g->x<200) {g->x=400-g->x;g->sx=-g->sx;} + if(g->y<200) {g->y=400-g->y;g->sy=-g->sy;} + if(g->x>500) {g->x=1000-g->x;g->sx=-g->sx;} + if(g->y>500) {g->y=1000-g->y;g->sy=-g->sy;} + g = g->next; + } + //l[3].x = l[0].x; + //l[3].y = l[0].y; + + gfxdrawer_t d; + gfxdrawer_target_gfxline(&d); + double width = t/3.0; + if(width>50) width=100-width; + width = 40; + + draw_stroke(l, &d, width, gfx_capSquare, gfx_joinMiter, 500); + gfxline_t*line = (gfxline_t*)d.result(&d); + //gfxline_dump(line, stdout, ""); + + gfxcolor_t black = {255,0,0,0}; + gfxcolor_t cyan = {255,0,128,128}; + dev.stroke(&dev, l, 2, &black, gfx_capRound, gfx_joinRound, 0); + dev.stroke(&dev, line, 2, &cyan, gfx_capRound, gfx_joinRound, 0); + gfxline_free(line); + dev.endpage(&dev); + } + + gfxresult_t* result = dev.finish(&dev); + result->save(result, "test.swf"); + result->destroy(result); +} + +int main() +{ + test_stroke2(); +} diff --git a/lib/gfxtools.c b/lib/gfxtools.c index a206e57..0862fd6 100644 --- a/lib/gfxtools.c +++ b/lib/gfxtools.c @@ -34,6 +34,7 @@ typedef struct _linedraw_internal gfxline_t*start; gfxline_t*next; gfxcoord_t x0,y0; + char has_moveto; } linedraw_internal_t; static void linedraw_moveTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y) @@ -41,12 +42,7 @@ static void linedraw_moveTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y) linedraw_internal_t*i = (linedraw_internal_t*)d->internal; gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)); l->type = gfx_moveTo; - if((int)((d->x * 5120) == (int)(x * 5120)) && - (int)((d->y * 5120) == (int)(y * 5120))) { - /* never mind- we're already there */ - return; - - } + i->has_moveto = 1; i->x0 = x; i->y0 = y; l->sx = l->sy = 0; @@ -64,7 +60,7 @@ static void linedraw_lineTo(gfxdrawer_t*d, gfxcoord_t x, gfxcoord_t y) linedraw_internal_t*i = (linedraw_internal_t*)d->internal; gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)); - if(!i->start) { + if(!i->has_moveto) { /* starts with a line, not with a moveto. As this is the first entry in the list, this is probably *meant* to be a moveto */ linedraw_moveTo(d, x, y); @@ -87,8 +83,7 @@ static void linedraw_splineTo(gfxdrawer_t*d, gfxcoord_t sx, gfxcoord_t sy, gfxco linedraw_internal_t*i = (linedraw_internal_t*)d->internal; gfxline_t*l = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)); - if(!i->start) { - fprintf(stderr, "Error: drawing startpoint is a spline\n"); + if(!i->has_moveto) { linedraw_moveTo(d, x, y); return; } @@ -105,10 +100,15 @@ static void linedraw_splineTo(gfxdrawer_t*d, gfxcoord_t sx, gfxcoord_t sy, gfxco if(!i->start) i->start = l; } -static void* linedraw_close(gfxdrawer_t*d) +static void linedraw_close(gfxdrawer_t*d) { linedraw_internal_t*i = (linedraw_internal_t*)d->internal; + if(!i->has_moveto) + return; linedraw_lineTo(d, i->x0, i->y0); + i->has_moveto = 0; + i->x0 = 0; + i->y0 = 0; } static void* linedraw_result(gfxdrawer_t*d) { -- 1.7.10.4