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;
/* 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;
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;
#include "stroke.h"
#include "convert.h"
-int main()
+int test_stroke1()
{
gfxline_t l[512], f[256*5];
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();
+}
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)
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;
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);
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;
}
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)
{