4 #include "../gfxdevice.h"
5 #include "../gfxtools.h"
10 /* notice: left/right for a coordinate system where y goes up, not down */
11 typedef enum {LEFT=0, RIGHT=1} leftright_t;
13 /* factor that determines into how many line fragments a spline is converted */
14 #define SUBFRACTION (2.4)
17 // s(t) = t*t*x2 + 2*t*(1-t)*cx + (1-t)*(1-t)*x1
19 // s(0.5) = 0.25*x2 + 0.5*cx + 0.25*x1
20 // ds(t)/dt = 2*t*x2 + (2-2t)*cx + (2t-2)*x1
23 static void draw_arc(gfxdrawer_t*draw, double x, double y, double a1, double a2, double r)
28 int steps = ceil(8*d/(M_PI*2)); // we use 8 splines for a full circle
32 double step = (a2-a1)/steps;
33 double lastx = x+cos(a1)*r;
34 double lasty = y+sin(a1)*r;
36 /* we could probably build a table for this- there are only 8
37 possible values for step */
38 double r2 = r*(2-sqrt(0.5+0.5*cos(step)));
40 for(t=1;t<=steps;t++) {
41 double a = a1 + t*step;
46 //double dx = (s*step/2 + lastx);
47 //double dy = (-c*step/2 + lasty);
48 double dx = x + cos(a-step/2)*r2;
49 double dy = y + sin(a-step/2)*r2;
50 //draw->lineTo(draw, xx, yy);
51 draw->splineTo(draw, dx, dy, xx, yy);
57 static void draw_single_stroke(gfxpoint_t*p, int num, gfxdrawer_t*draw, double width, gfx_capType cap, gfx_joinType join, double limit)
63 /* remove duplicate points */
67 if(p[t].x != p[t-1].x || p[t].y != p[t-1].y) {
79 double alimit = atan(limit / width);
81 /* iterate through the points two times: first forward, then backward,
82 adding a stroke outline to the right side and line caps after each
86 for(pass=0;pass<2;pass++) {
88 for(pos=start;pos!=end;pos+=incr) {
89 //printf("%d) %.2f %.2f\n", pos, p[pos].x, p[pos].y);
90 double dx = p[pos+incr].x - p[pos].x;
91 double dy = p[pos+incr].y - p[pos].y;
92 double l = sqrt(dx*dx+dy*dy);
93 double w = atan2(dy,dx);
99 if(d>=0 && d<M_PI) turn=LEFT;
100 else if(d<0 && d>-M_PI) turn=RIGHT;
101 else if(d>=M_PI) {turn=RIGHT;}
102 else if(d<=-M_PI) {turn=LEFT;d+=M_PI*2;}
104 if(turn!=LEFT || join==gfx_joinBevel) {
105 /* TODO: does a bevel join extend beyond the segment (i.e.,
106 is it like a square cap or like a butt cap? */
107 } else if(join==gfx_joinRound) {
108 draw_arc(draw, p[pos].x, p[pos].y, lastw-M_PI/2, w-M_PI/2, width);
109 } else if(join==gfx_joinMiter) {
111 double r2 = width*(1-sin(d/2)+tan(d/2));
112 double addx = cos(lastw-M_PI/2+d/2)*r2;
113 double addy = sin(lastw-M_PI/2+d/2)*r2;
114 draw->lineTo(draw, p[pos].x+addx, p[pos].y+addy);
116 /* convert to bevel join, which always looks the same (is
117 independent of miterLimit TODO: verify this */
122 double addx = cos(w-M_PI/2)*width;
123 double addy = sin(w-M_PI/2)*width;
124 draw->lineTo(draw, p[pos].x+addx, p[pos].y+addy);
125 //printf("-- %.2f %.2f (angle:%d)\n", px1, py1, (int)(180*w/M_PI));
126 double px2 = p[pos+incr].x + addx;
127 double py2 = p[pos+incr].y + addy;
128 //printf("-- %.2f %.2f (angle:%d)\n", px2, py2, (int)(180*w/M_PI));
129 draw->lineTo(draw, p[pos+incr].x+addx, p[pos+incr].y+addy);
133 /* draw stroke ends. We draw duplicates of some points here. The drawer
134 implementation should be smart enough to remove them. */
135 double c = cos(lastw-M_PI/2)*width;
136 double s = sin(lastw-M_PI/2)*width;
137 if(cap == gfx_capButt) {
138 draw->lineTo(draw, p[pos].x+c, p[pos].y+s);
139 draw->lineTo(draw, p[pos].x-c, p[pos].y-s);
140 } else if(cap == gfx_capRound) {
141 draw->lineTo(draw, p[pos].x+c, p[pos].y+s);
142 draw_arc(draw, p[pos].x, p[pos].y, lastw-M_PI/2, lastw+M_PI/2, width);
143 } else if(cap == gfx_capSquare) {
144 double c = cos(lastw-M_PI/2)*width;
145 double s = sin(lastw-M_PI/2)*width;
146 draw->lineTo(draw, p[pos].x+c, p[pos].y+s);
147 draw->lineTo(draw, p[pos].x+c-s, p[pos].y+s+c);
148 draw->lineTo(draw, p[pos].x-c-s, p[pos].y-s+c);
149 draw->lineTo(draw, p[pos].x-c, p[pos].y-s);
154 lastw += M_PI; // for dots
159 static void draw_stroke(gfxline_t*start, gfxdrawer_t*draw, double width, gfx_capType cap, gfx_joinType join, double miterLimit)
163 assert(start->type == gfx_moveTo);
164 gfxline_t*line = start;
165 // measure array size
170 if(line->type == gfx_moveTo) {
171 if(pos>size) size = pos;
173 } else if(line->type == gfx_lineTo) {
175 } else if(line->type == gfx_splineTo) {
176 int parts = (int)(sqrt(fabs(line->x-2*line->sx+lastx) + fabs(line->y-2*line->sy+lasty))*SUBFRACTION);
177 if(!parts) parts = 1;
184 if(pos>size) size = pos;
187 gfxpoint_t* points = malloc(sizeof(gfxpoint_t)*size);
191 if(line->type == gfx_moveTo) {
193 draw_single_stroke(points, pos, draw, width, cap, join, miterLimit);
195 } else if(line->type == gfx_splineTo) {
196 int parts = (int)(sqrt(fabs(line->x-2*line->sx+lastx) + fabs(line->y-2*line->sy+lasty))*SUBFRACTION);
197 if(!parts) parts = 1;
198 double stepsize = 1.0/parts;
200 for(i=0;i<parts;i++) {
201 double t = (double)i*stepsize;
202 points[pos].x = (line->x*t*t + 2*line->sx*t*(1-t) + lastx*(1-t)*(1-t));
203 points[pos].y = (line->y*t*t + 2*line->sy*t*(1-t) + lasty*(1-t)*(1-t));
207 lastx = points[pos].x = line->x;
208 lasty = points[pos].y = line->y;
212 if(pos) draw_single_stroke(points, pos, draw, width, cap, join, miterLimit);
216 static windcontext_t onepolygon = {1};
217 gfxpoly_t* gfxpoly_from_stroke(gfxline_t*line, gfxcoord_t width, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit, double gridsize)
220 gfxdrawer_target_poly(&d, gridsize);
221 draw_stroke(line, &d, width, cap_style, joint_style, miterLimit);
222 gfxpoly_t*poly = (gfxpoly_t*)d.result(&d);
223 assert(gfxpoly_check(poly));
224 gfxpoly_t*poly2 = gfxpoly_process(poly, 0, &windrule_circular, &onepolygon);
225 gfxpoly_destroy(poly);