fixed bevel joins
authorMatthias Kramm <kramm@quiss.org>
Sat, 6 Jun 2009 12:32:42 +0000 (14:32 +0200)
committerMatthias Kramm <kramm@quiss.org>
Sat, 6 Jun 2009 12:32:42 +0000 (14:32 +0200)
lib/gfxpoly/convert.c
lib/gfxpoly/stroke.c
lib/gfxpoly/stroke.h
lib/gfxpoly/test_stroke.c
lib/gfxtools.c

index 64cef43..9d8ecab 100644 (file)
@@ -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;
index 70a51d0..9735c1b 100644 (file)
@@ -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;t<num;t++) {
-       p[s] = p[t];
-       if(p[t].x != p[t-1].x || p[t].y != p[t-1].y) {
-           s++;
-       } else {
-           num--;
+       if(p[t].x != last.x || p[t].y != last.y) {
+           p[s++] = last = p[t];
        }
     }
+    num = s;
+    
+    char closed = (num>2 && 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 && d<M_PI) turn=LEFT;
@@ -102,61 +105,63 @@ static void draw_single_stroke(gfxpoint_t*p, int num, gfxdrawer_t*draw, double w
                else if(d<=-M_PI) {turn=LEFT;d+=M_PI*2;}
                else {assert(0);}
                if(turn!=LEFT || join==gfx_joinBevel) {
-                   /* TODO: does a bevel join extend beyond the segment (i.e.,
-                      is it like a square cap or like a butt cap? */
+                   // nothing to do. bevel joins are easy
                } else if(join==gfx_joinRound) {
                    draw_arc(draw, p[pos].x, p[pos].y, lastw-M_PI/2, w-M_PI/2, width);
                } else if(join==gfx_joinMiter) {
-                   if(d/2<alimit) {
-                       double r2 = width*(1-sin(d/2)+tan(d/2));
-                       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);
-                   } 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;
index 5f54a6e..4b52d52 100644 (file)
@@ -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
index b05d710..d468cb8 100644 (file)
@@ -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();
+}
index a206e57..0862fd6 100644 (file)
@@ -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)
 {