1 /* Copyright (C) 2000-2008 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include "pfaedit.h"
28 #include "splinefont.h"
29 #include <math.h>
30 
31 #define PI	3.1415926535897932
32 
33 typedef struct joininfo {
34     SplinePoint *from, *to;
35     real tprev;
36     real tnext;
37     BasePoint inter;
38 } JointPoint;
39 
40 
SplineAngle(Spline * spline,real t)41 static real SplineAngle(Spline *spline,real t) {
42     Spline1D *xsp = &spline->splines[0], *ysp = &spline->splines[1];
43     real xslope = (3*xsp->a*t+2*xsp->b)*t + xsp->c;
44     real yslope = (3*ysp->a*t+2*ysp->b)*t + ysp->c;
45 
46     if ( xslope==0 && yslope==0 ) {
47 	real faket = (t>.5) ? t-.01 : t+.01;
48 	xslope = (3*xsp->a*faket+2*xsp->b)*faket + xsp->c;
49 	yslope = (3*ysp->a*faket+2*ysp->b)*faket + ysp->c;
50     }
51     if ( spline->knownlinear || ( xslope==0 && yslope==0 )) {
52 	xslope = spline->to->me.x-spline->from->me.x;
53 	yslope = spline->to->me.y-spline->from->me.y;
54     }
55 return( atan2(yslope,xslope) );
56 }
57 
PenCorner(double lineangle,StrokeInfo * si)58 static int PenCorner(double lineangle,StrokeInfo *si) {
59 
60     if ( ( lineangle>=si->penangle && lineangle<=si->penangle+PI/2 ) ||
61 	 ( lineangle+2*PI>=si->penangle && lineangle+2*PI<=si->penangle+PI/2 ) ||
62 	 ( lineangle-2*PI>=si->penangle && lineangle-2*PI<=si->penangle+PI/2 ) ) {
63 return( 0 );
64     } else if ( ( lineangle>=si->penangle+PI/2 && lineangle<=si->penangle+PI ) ||
65 		( lineangle+2*PI>=si->penangle+PI/2 && lineangle+2*PI<=si->penangle+PI ) ||
66 		( lineangle-2*PI>=si->penangle+PI/2 && lineangle-2*PI<=si->penangle+PI ) ) {
67 return( 1 );
68     } else if ( ( lineangle>=si->penangle+PI && lineangle<=si->penangle+3*PI/2 ) ||
69 		( lineangle+2*PI>=si->penangle+PI && lineangle+2*PI<=si->penangle+3*PI/2 ) ||
70 		( lineangle-2*PI>=si->penangle+PI && lineangle-2*PI<=si->penangle+3*PI/2 ) ) {
71 return( 2 );
72     } else {
73 return( 3 );
74     }
75 }
76 
77 /* the plus point is where we go when we rotate the line's direction by +90degrees */
78 /*  and then move radius in that direction. minus is when we rotate -90 and */
79 /*  then move */	/* counter-clockwise */
SplineExpand(Spline * spline,real t,real toff,StrokeInfo * si,BasePoint * plus,BasePoint * minus)80 static double SplineExpand(Spline *spline,real t,real toff, StrokeInfo *si,
81 	BasePoint *plus, BasePoint *minus) {
82     Spline1D *xsp = &spline->splines[0], *ysp = &spline->splines[1];
83     BasePoint base;
84     double lineangle, c,s, factor = 1.0;
85 
86     if ( si->factor!=NULL )
87 	factor = (si->factor)(si->data,spline,t);
88 
89     base.x = ((xsp->a*t+xsp->b)*t+xsp->c)*t + xsp->d;
90     base.y = ((ysp->a*t+ysp->b)*t+ysp->c)*t + ysp->d;
91 
92     lineangle = SplineAngle(spline,t+toff);
93     if ( si->stroke_type != si_caligraphic ) {
94 	c = si->radius*factor*cos(lineangle+PI/2);
95 	s = si->radius*factor*sin(lineangle+PI/2);
96 	plus->y = base.y+s;
97 	plus->x = base.x+c;
98 	minus->y = base.y-s;
99 	minus->x = base.x-c;
100     } else {
101 	int corner = PenCorner(lineangle,si);
102 	plus->x = base.x + factor*si->xoff[corner];
103 	plus->y = base.y + factor*si->yoff[corner];
104 	corner += 2;
105 	minus->x = base.x + factor*si->xoff[corner];
106 	minus->y = base.y + factor*si->yoff[corner];
107     }
108 return( lineangle );
109 }
110 
makequartercircle(real x,real y,real radius,real xmul,real ymul,SplinePoint * prev)111 static SplinePoint *makequartercircle(real x, real y, real radius,
112 	real xmul, real ymul,SplinePoint *prev) {
113     SplinePoint *here = SplinePointCreate(x,y);
114 
115     if ( xmul==0 ) {
116 	here->nextcp.x = here->prevcp.x = x;
117 	here->nextcp.y = y + .552*ymul*radius;
118 	here->prevcp.y = y - .552*ymul*radius;
119     } else {
120 	here->nextcp.y = here->prevcp.y = y;
121 	here->nextcp.x = x + .552*xmul*radius;
122 	here->prevcp.x = x - .552*xmul*radius;
123     }
124     here->nonextcp = here->noprevcp = false;
125     if ( prev!=NULL )
126 	SplineMake3(prev,here);
127 return( here );
128 }
129 
makeline(SplinePoint * prev,real x,real y)130 static SplinePoint *makeline(SplinePoint *prev, real x, real y) {
131     SplinePoint *here = SplinePointCreate(x,y);
132     here->pointtype = pt_corner;
133     if ( prev!=NULL )
134 	SplineMake3(prev,here);
135 return( here );
136 }
137 
SinglePointStroke(SplinePoint * base,StrokeInfo * si,SplinePoint ** _plus,SplinePoint ** _minus)138 static void SinglePointStroke(SplinePoint *base, StrokeInfo *si, SplinePoint **_plus, SplinePoint **_minus) {
139     SplinePoint *plus, *cur;
140 
141     /* A single point, is kind of dull.
142 	For a caligraphic pen, it's just a copy of the pen
143 	For a linecap of lc_butt it's still a point
144 	For a linecap of lc_round it's a circle
145 	For a linecap of lc_square it should be a square...
146 	    but how does one orient that square? probably a circle is best
147 	    here too
148     */
149     /* We don't have a spline, so don't try guessing factor */
150     if ( si->stroke_type == si_caligraphic ) {
151 	plus = SplinePointCreate(base->me.x+si->xoff[0],base->me.y+si->yoff[0]);
152 	plus->pointtype = pt_corner;
153 	cur = makeline(plus,base->me.x+si->xoff[1],base->me.y+si->yoff[1]);
154 	cur = makeline(cur,base->me.x+si->xoff[2],base->me.y+si->yoff[2]);
155 	cur = makeline(cur,base->me.x+si->xoff[3],base->me.y+si->yoff[3]);
156 	SplineMake3(cur,plus);
157 	*_plus = *_minus = plus;
158     } else if ( si->cap!=lc_butt ) {
159 	plus = makequartercircle(base->me.x-si->radius,base->me.y,si->radius,0,1,NULL);
160 	cur = makequartercircle(base->me.x,base->me.y+si->radius,si->radius,1,0,plus);
161 	cur = makequartercircle(base->me.x+si->radius,base->me.y,si->radius,0,-1,cur);
162 	cur = makequartercircle(base->me.x,base->me.y-si->radius,si->radius,-1,0,cur);
163 	SplineMake3(cur,plus);
164 	*_plus = *_minus = plus;
165     } else {
166 	*_plus = *_minus = cur = chunkalloc(sizeof(SplinePoint));
167 	*cur = *base;
168 	cur->next = cur->prev = NULL;
169 	cur->hintmask = NULL;
170     }
171 }
172 
StrokeEnd(SplinePoint * base,StrokeInfo * si,int isstart,SplinePoint ** _to)173 static SplinePoint *StrokeEnd(SplinePoint *base, StrokeInfo *si, int isstart,
174 	SplinePoint **_to) {
175     BasePoint junk;
176     SplinePoint *mid1, *mid2, *cur, *from, *to;
177     real len;
178     real c,s;
179     real angle;
180     real sign;
181     real factor = si->factor==NULL ? 1.0 :
182 	    base->next!=NULL ? (si->factor)(si->data,base->next,0) :
183 	    base->prev!=NULL ? (si->factor)(si->data,base->prev,1) :
184 	    1.0;
185 
186     from = chunkalloc(sizeof(SplinePoint));
187     to = chunkalloc(sizeof(SplinePoint));
188     from->nonextcp = to->nonextcp = from->noprevcp = to->noprevcp = true;
189     from->pointtype = pt_corner; to->pointtype = pt_corner;
190 
191     if ( isstart )
192 	angle = SplineExpand(base->next,0,0,si,&from->me,&to->me)+ PI;
193     else
194 	angle = SplineExpand(base->prev,1,0,si,&to->me,&from->me);
195 
196     if ( (len = to->me.x-from->me.x)<0 )
197 	len = -len;
198     len += ( to->me.y > from->me.y ) ? (to->me.y - from->me.y) : (from->me.y - to->me.y);
199 
200     if ( si->stroke_type == si_caligraphic ) {
201 	int corner;
202 	corner = PenCorner(angle,si);
203 	cur = makeline(from,base->me.x+factor*si->xoff[corner+1],base->me.y+factor*si->yoff[corner+1]);
204 	SplineMake3(cur,to);
205     } else {
206 	if ( isstart ) {
207 	    SplineIsLinearMake(base->next);
208 	    angle = SplineExpand(base->next,0,0,si,&junk,&junk)+ PI;
209 	    sign = -1;
210 	} else {
211 	    SplineIsLinearMake(base->prev);
212 	    angle = SplineExpand(base->prev,1,0,si,&junk,&junk);
213 	    sign = -1;
214 	}
215 	if ( si->cap==lc_butt ) {
216 	    SplineMake3(from,to);		/* draw a line between */
217 	} else if ( si->cap==lc_square ) {
218 	    mid1 = SplinePointCreate(
219 		    from->me.x+ sign*(from->me.y-base->me.y),
220 		    from->me.y- sign*(from->me.x-base->me.x));
221 	    mid2 = SplinePointCreate(
222 		    to->me.x+ sign*(from->me.y-base->me.y),
223 		    to->me.y- sign*(from->me.x-base->me.x));
224 	    mid1->pointtype = pt_corner; mid2->pointtype = pt_corner;
225 	    SplineMake3(from,mid1);
226 	    SplineMake3(mid1,mid2);
227 	    SplineMake3(mid2,to);
228 	} else if ( si->cap==lc_round ) {
229 	    mid1 = chunkalloc(sizeof(SplinePoint));
230 	    mid1->me.x = base->me.x+ sign*(from->me.y-base->me.y);
231 	    mid1->me.y = base->me.y- sign*(from->me.x-base->me.x);
232 	    mid1->pointtype = pt_curve;
233 	    c = .552*si->radius*factor*cos(angle);
234 	    s = .552*si->radius*factor*sin(angle);
235 	    from->nextcp.x = from->me.x + c;
236 	    from->nextcp.y = from->me.y + s;
237 	    from->nonextcp = false;
238 	    to->prevcp.x = to->me.x +c;
239 	    to->prevcp.y = to->me.y +s;
240 	    to->noprevcp = false;
241 	    mid1->prevcp.x = mid1->me.x - sign*s;
242 	    mid1->prevcp.y = mid1->me.y + sign*c;
243 	    mid1->nextcp.x = mid1->me.x + sign*s;
244 	    mid1->nextcp.y = mid1->me.y - sign*c;
245 	    SplineMake3(from,mid1);
246 	    SplineMake3(mid1,to);
247 	}
248     }
249     SplinePointCatagorize(to);
250     SplinePointCatagorize(from);
251     *_to = to;
252 return( from );
253 }
254 
255 /* Is this the inner intersection or the outer one (the inner one is on both splines) */
256 /*  the outer one is beyond both */
Intersect_Lines(BasePoint * inter,BasePoint * p1,real sx1,real sy1,BasePoint * p2,real sx2,real sy2,real radius)257 static int Intersect_Lines(BasePoint *inter,BasePoint *p1,real sx1, real sy1,
258 	BasePoint *p2, real sx2, real sy2, real radius) {
259     real t1/*,t2*/;
260     real denom;
261 
262     denom = (sx1*sy2-sx2*sy1);
263     if ( denom>-.0001 && denom<.0001 ) {
264 	/* Lines are parallel. Might be coincident, might not */
265 	t1 = 10000;
266     } else {
267 	/* t2 = (sy1*(p2->x-p1->x)-sx1*(p2->y-p1->y))/denom;*/
268 	t1 = (sy2*(p2->x-p1->x)-sx2*(p2->y-p1->y))/denom;
269     }
270     if ( t1>1000 || t1<-1000 ) {
271 	denom = sqrt(sx1*sx1 + sy1*sy1)/radius;
272 	if ( denom==0 ) {
273 	    inter->x = (p1->x+p2->x)/2;
274 	    inter->y = (p1->y+p2->y)/2;
275 	} else {
276 	    inter->x = (p1->x+p2->x)/2 + sx1/denom;
277 	    inter->y = (p1->y+p2->y)/2 + sy1/denom;
278 	}
279 return( -1 );
280     } else {
281 	inter->x = p1->x + t1*sx1;
282 	inter->y = p1->y + t1*sy1;
283 return( t1<=0 );	/* if t1 < 0 then the intersection point is actually */
284 			/*  on both of the spline segments. if it isn't then */
285 			/*  it will be on the continuation of the spline */
286 			/*  but beyond its endpoint... */
287     }
288 }
289 
CircleCpDist(double angle)290 static double CircleCpDist(double angle) {
291     /* To draw an arc of length angle on a unit circle, the control points */
292     /*  should be this far from their base points. Determined empirically, */
293     /*  fit by least squares */
294 
295     if ( angle<0 ) angle = -angle;
296     while ( angle>2*PI ) angle -= 2*PI;
297     if ( angle>PI ) angle = 2*PI-angle;
298 return( ((0.0115445*angle - 0.0111987)*angle + 0.357114)*angle );
299 }
300 
ChordMid(double angle,BasePoint * center,BasePoint * from,double * _cpratio)301 static SplinePoint *ChordMid(double angle,BasePoint *center,BasePoint *from,
302 	double *_cpratio) {
303     BasePoint off, new;
304     double s,c,cpratio;
305     SplinePoint *sp;
306 
307     if ( angle<0 ) angle = -angle;
308     while ( angle>2*PI ) angle -= 2*PI;
309     if ( angle>PI ) angle = 2*PI-angle;
310     angle /= 2;
311 
312     off.x = from->x-center->x;
313     off.y = from->y-center->y;
314     s = sin(angle); c = cos(angle);
315     new.x = c*off.x - s*off.y;
316     new.y = s*off.x + c*off.y;
317     sp = SplinePointCreate(new.x+center->x,new.y+center->y);
318 
319     *_cpratio = cpratio = CircleCpDist(angle);
320     new.x *= cpratio; new.y *= cpratio;		/* new is a vector of length radius pointing perp to the direction of the cps */
321 		/* We need to multiply by cp ratio and rotate 90 degrees */
322     sp->prevcp.x = sp->me.x + new.y;
323     sp->prevcp.y = sp->me.y - new.x;
324     sp->nextcp.x = sp->me.x - new.y;
325     sp->nextcp.y = sp->me.y + new.x;
326     sp->nonextcp = sp->noprevcp = false;
327 return( sp );
328 }
329 
IntersectionTooFar(BasePoint * inter,SplinePoint * from,SplinePoint * to,StrokeInfo * si)330 static int IntersectionTooFar(BasePoint *inter,SplinePoint *from,SplinePoint *to,StrokeInfo *si) {
331     /* Things look really ugly when we try to miter acute angles -- we get */
332     /* huge spikes. So if mitering is going to give bad results, just bevel */
333     double len, xoff, yoff;
334 
335     xoff = inter->x-from->me.x; yoff = inter->y-from->me.y;
336     len = xoff*xoff + yoff*yoff;
337     if ( len > (5*si->radius * 5*si->radius) )
338 return( true );
339 
340     xoff = inter->x-to->me.x; yoff = inter->y-to->me.y;
341     len = xoff*xoff + yoff*yoff;
342     if ( len > (5*si->radius * 5*si->radius) )
343 return( true );
344 
345 return( false );
346 }
347 
MakeJoints(SplinePoint * from,SplinePoint * to,StrokeInfo * si,BasePoint * inter,BasePoint * center,int incr,double pangle,double nangle,real factor)348 static void MakeJoints(SplinePoint *from,SplinePoint *to,StrokeInfo *si,
349 	BasePoint *inter, BasePoint *center,
350 	int incr,double pangle, double nangle, real factor) {
351     SplinePoint *mid;
352     int cstart, cend, i;
353 
354     if ( si->stroke_type == si_caligraphic ) {
355 	cstart = PenCorner(pangle,si);
356 	cend = PenCorner(nangle,si);
357 	if ( cstart==cend ) {
358 	    /* same as a miter join */
359 	    mid = SplinePointCreate(inter->x,inter->y);
360 	    mid->pointtype = pt_corner;
361 	    SplineMake3(from,mid);
362 	    SplineMake3(mid,to);
363 	} else {
364 	    if ( incr<0 ) {
365 		if ((cstart += 2)>=4 ) cstart -= 4;
366 		if ((cend += 2)>=4 ) cend -= 4;
367 		incr = 1;	/* Why??? */
368 	    }
369 	    if ( incr>0 && cstart>cend )
370 		cend += 4;
371 	    else if ( incr<0 && cstart<cend )
372 		cstart += 4;
373 	    i = cstart + incr;		/* First one is from */
374 	    mid = from;
375 	    while ( i!=cend ) {
376 		mid = makeline(mid,center->x+factor*si->xoff[i],center->y+factor*si->yoff[i]);
377 		i += incr;
378 	    }
379 	    SplineMake3(mid,to);
380 	}
381     } else if ( si->join == lj_miter && !IntersectionTooFar(inter,from,to,si)) {
382 	mid = SplinePointCreate(inter->x,inter->y);
383 	mid->pointtype = pt_corner;
384 	SplineMake3(from,mid);
385 	SplineMake3(mid,to);
386 	if ( from->ptindex == to->ptindex )
387 	    mid->ptindex = from->ptindex;
388     } else if ( si->join==lj_bevel ) {
389 	SplineMake3(from,to);
390     } else {
391 	double cplen = CircleCpDist(nangle-pangle);
392 	mid = NULL;
393 	if ( cplen>.6 ) {
394 	    /* If angle of the arc is more than about 90 degrees a cubic */
395 	    /*  spline is noticeably different from a circle's arc */
396 	    /* So add an extra point to help things out */
397 	    mid = ChordMid(nangle-pangle,center,&from->me,&cplen);
398 	}
399 	cplen *= si->radius*factor;
400 	from->pointtype = to->pointtype = pt_curve;
401 	from->nextcp.x = from->me.x-cplen*cos(nangle);
402 	from->nextcp.y = from->me.y-cplen*sin(nangle);
403 	to->prevcp.x = to->me.x+cplen*cos(pangle);
404 	to->prevcp.y = to->me.y+cplen*sin(pangle);
405 	from->nonextcp = false; to->noprevcp = false;
406 	if ( mid==NULL )
407 	    SplineMake3(from,to);
408 	else {
409 	    SplineMake3(from,mid);
410 	    SplineMake3(mid,to);
411 	}
412     }
413 }
414 
OnEdge(BasePoint * plus,BasePoint * minus,Spline * sp,double t,double heret,Spline * hsp,StrokeInfo * si,double * _ppt,double * _pmt,double * _mpt,double * _mmt)415 static int OnEdge(BasePoint *plus,BasePoint *minus,Spline *sp, double t,
416 	double heret, Spline *hsp,
417 	StrokeInfo *si, double *_ppt, double *_pmt, double *_mpt, double *_mmt) {
418     double rsq = si->radius*si->radius;
419     double tt, xdiff, ydiff, loopdiff;
420     double pptval= -1, pmtval= -1, mptval= -1, mmtval = -1;
421     BasePoint here, test;
422 
423     here.x = ((hsp->splines[0].a*heret+hsp->splines[0].b)*heret+hsp->splines[0].c)*heret+hsp->splines[0].d;
424     here.y = ((hsp->splines[1].a*heret+hsp->splines[1].b)*heret+hsp->splines[1].c)*heret+hsp->splines[1].d;
425 
426     if ( (xdiff = sp->to->me.x-sp->from->me.x)<0 ) xdiff = -xdiff;
427     if ( (ydiff = sp->to->me.y-sp->from->me.y)<0 ) ydiff = -ydiff;
428     loopdiff = (xdiff+ydiff==0) ? 2 : 1.0/(4*(xdiff+ydiff)/si->radius);
429 
430     if ( _ppt!=NULL ) {
431 	for ( tt = t+loopdiff; tt<=1 ; tt += loopdiff ) {
432 	    test.x = ((sp->splines[0].a*tt+sp->splines[0].b)*tt+sp->splines[0].c)*tt+sp->splines[0].d;
433 	    test.y = ((sp->splines[1].a*tt+sp->splines[1].b)*tt+sp->splines[1].c)*tt+sp->splines[1].d;
434 	    if ( (test.x-here.x)*(test.x-here.x)+(test.y-here.y)*(test.y-here.y)> 2*rsq )
435 	break;
436 	    if ( (plus->x-test.x)*(plus->x-test.x)+(plus->y-test.y)*(plus->y-test.y)<= rsq )
437 		pptval = tt;
438 	    if ( (minus->x-test.x)*(minus->x-test.x)+(minus->y-test.y)*(minus->y-test.y)<= rsq )
439 		pmtval = tt;
440 	}
441 	*_ppt = pptval; *_pmt = pmtval;
442     }
443 
444     if ( _mmt!=NULL ) {
445 	for ( tt = t-loopdiff; tt>=0 ; tt -= loopdiff ) {
446 	    test.x = ((sp->splines[0].a*tt+sp->splines[0].b)*tt+sp->splines[0].c)*tt+sp->splines[0].d;
447 	    test.y = ((sp->splines[1].a*tt+sp->splines[1].b)*tt+sp->splines[1].c)*tt+sp->splines[1].d;
448 	    if ( (test.x-here.x)*(test.x-here.x)+(test.y-here.y)*(test.y-here.y)> 2*rsq )
449 	break;
450 	    if ( (plus->x-test.x)*(plus->x-test.x)+(plus->y-test.y)*(plus->y-test.y)< rsq )
451 		mptval = tt;
452 	    if ( (minus->x-test.x)*(minus->x-test.x)+(minus->y-test.y)*(minus->y-test.y)< rsq )
453 		mmtval = tt;
454 	}
455 	*_mmt = mmtval; *_mpt = mptval;
456     }
457 
458 return( pptval!=-1 || mmtval!=-1 || pmtval!=-1 || mptval==-1 );
459 }
460 
461 #define BasePtDistance(pt1, pt2)  sqrt(((pt1)->x-(pt2)->x)*((pt1)->x-(pt2)->x) + ((pt1)->y-(pt2)->y)*((pt1)->y-(pt2)->y))
462 
463 
MergeSplinePoint(SplinePoint * sp1,SplinePoint * sp2)464 static SplinePoint *MergeSplinePoint(SplinePoint *sp1,SplinePoint *sp2) {
465     /* sp1 and sp2 should be close together, use their average for the */
466     /*  new position, get rid of one, and add its spline to the other */
467     /* sp1->next==NULL, sp2->prev==NULL */
468     double offx, offy;
469 
470     offx = (sp1->me.x-sp2->me.x)/2;
471     offy = (sp1->me.y-sp2->me.y)/2;
472     sp1->me.x -= offx; sp1->prevcp.x -= offx;
473     sp1->me.y -= offy; sp1->prevcp.y -= offy;
474     sp1->nextcp.x = sp2->nextcp.x + offx;
475     sp1->nextcp.y = sp2->nextcp.y + offy;
476     sp1->nonextcp = sp2->nonextcp;
477     sp1->next = sp2->next;
478     SplinePointFree(sp2);
479     if ( sp1->next!=NULL )
480 	sp1->next->from = sp1;
481     SplinePointCatagorize(sp1);
482     if ( sp1->prev!=NULL )
483 	SplineRefigure(sp1->prev);
484     if ( sp1->next!=NULL )
485 	SplineRefigure(sp1->next);
486 return( sp1 );
487 }
488 
MSP(SplinePoint * sp1,SplinePoint ** sp2,SplinePoint ** sp2alt)489 static void MSP(SplinePoint *sp1,SplinePoint **sp2, SplinePoint **sp2alt) {
490     int same2 = *sp2==*sp2alt;
491 
492     *sp2 = MergeSplinePoint(sp1,*sp2);
493     if ( same2 )
494 	*sp2alt = *sp2;
495 }
496 
SplineMaybeBisect(Spline * s,double t)497 static SplinePoint *SplineMaybeBisect(Spline *s,double t) {
498     /* Things get very confused if I have a splineset with just a single point */
499     SplinePoint *temp, *sp;
500 
501     if ( t<.0001 ) {
502 	temp = chunkalloc(sizeof(SplinePoint));
503 	sp = s->from;
504 	*temp = *sp;
505 	temp->hintmask = NULL;
506 	temp->next->from = temp;
507 	sp->next = NULL;
508 	sp->nextcp = sp->me;
509 	sp->nonextcp = true;
510 	temp->prevcp = temp->me;
511 	temp->noprevcp = true;
512 	SplineMake3(sp,temp);
513 return( temp );
514     } else if ( t>.9999 ) {
515 	temp = chunkalloc(sizeof(SplinePoint));
516 	sp = s->to;
517 	*temp = *sp;
518 	temp->hintmask = NULL;
519 	temp->prev->to = temp;
520 	sp->prev = NULL;
521 	sp->prevcp = sp->me;
522 	sp->noprevcp = true;
523 	temp->nextcp = temp->me;
524 	temp->nonextcp = true;
525 	SplineMake3(temp,sp);
526 return( temp );
527     }
528 
529 return( SplineBisect(s,t));
530 }
531 
SplineFreeBetween(SplinePoint * from,SplinePoint * to,int freefrom,int freeto)532 static void SplineFreeBetween(SplinePoint *from,SplinePoint *to,int freefrom,int freeto) {
533     Spline *s;
534 
535     if ( from==to ) {
536 	if ( freefrom && freeto )
537 	    SplinePointFree(from);
538 return;
539     }
540 
541     while ( from!=to && from!=NULL ) {
542 	s = from->next;
543 	if ( freefrom )
544 	    SplinePointFree(from);
545 	else
546 	    from->next = NULL;
547 	if ( s==NULL )
548 return;
549 	freefrom = true;
550 	from = s->to;
551 	SplineFree(s);
552     }
553     if ( freeto )
554 	SplinePointFree(to);
555     else
556 	to->prev = NULL;
557 }
558 
SplineFreeForeward(SplinePoint * from)559 static void SplineFreeForeward(SplinePoint *from) {
560     Spline *s;
561 
562     while ( from!=NULL ) {
563 	s = from->next;
564 	SplinePointFree(from);
565 	if ( s==NULL )
566 return;
567 	from = s->to;
568 	SplineFree(s);
569     }
570 }
571 
SplineFreeBackward(SplinePoint * to)572 static void SplineFreeBackward(SplinePoint *to) {
573     Spline *s;
574 
575     while ( to!=NULL ) {
576 	s = to->prev;
577 	SplinePointFree(to);
578 	if ( s==NULL )
579 return;
580 	to = s->from;
581 	SplineFree(s);
582     }
583 }
584 
SplineCopyAfter(SplinePoint * from,SplinePoint ** end)585 static SplinePoint *SplineCopyAfter(SplinePoint *from,SplinePoint **end) {
586     SplinePoint *head, *last;
587 
588     last = head = chunkalloc(sizeof(SplinePoint));
589     *head = *from;
590     head->hintmask = NULL;
591     head->prev = NULL;
592     while ( from->next!=NULL ) {
593 	last->next = chunkalloc(sizeof(Spline));
594 	*last->next = *from->next;
595 	last->next->from = last;
596 	last->next->to = chunkalloc(sizeof(SplinePoint));
597 	*last->next->to = *from->next->to;
598 	last->next->to->hintmask = NULL;
599 	last->next->to->prev = last->next;
600 	last = last->next->to;
601 	from = from->next->to;
602     }
603     *end = last;
604 return( head );
605 }
606 
SplineCopyBefore(SplinePoint * to,SplinePoint ** end)607 static SplinePoint *SplineCopyBefore(SplinePoint *to,SplinePoint **end) {
608     SplinePoint *head, *last;
609 
610     last = head = chunkalloc(sizeof(SplinePoint));
611     *head = *to;
612     head->hintmask = NULL;
613     head->next = NULL;
614     while ( to->prev!=NULL ) {
615 	last->prev = chunkalloc(sizeof(Spline));
616 	*last->prev = *to->prev;
617 	last->prev->to = last;
618 	last->prev->from = chunkalloc(sizeof(SplinePoint));
619 	*last->prev->from = *to->prev->from;
620 	last->prev->from->hintmask = NULL;
621 	last->prev->from->next = last->prev;
622 	last = last->prev->from;
623 	to = to->prev->from;
624     }
625     *end = last;
626 return( head );
627 }
628 
Intersect_Splines(SplinePoint * from,SplinePoint * to,SplinePoint ** ret)629 static SplinePoint *Intersect_Splines(SplinePoint *from,SplinePoint *to,
630 	SplinePoint **ret) {
631     Spline *test1, *test2;
632     BasePoint pts[9];
633     extended t1s[9], t2s[9];
634 
635     for ( test1=from->next; test1!=NULL; test1=test1->to->next ) {
636 	for ( test2=to->prev; test2!=NULL; test2=test2->from->prev ) {
637 	    if ( SplinesIntersect(test1,test2,pts,t1s,t2s)>0 ) {
638 		*ret = SplineMaybeBisect(test2,t2s[0]);
639 return( SplineMaybeBisect(test1,t1s[0]));
640 	    }
641 	}
642     }
643     *ret = NULL;
644 return( NULL );
645 }
646 
647 struct strokedspline {
648     Spline *s;
649     SplinePoint *plusfrom, *plusto, *origplusfrom;
650     SplinePoint *minusfrom, *minusto, *origminusto;
651     int8 plusskip, minusskip;		/* If this spline is so small that it is totally within the region stroked by an adjacent spline */
652     int8 pinnerto, minnerto;		/* to and from as defined on original spline s */
653     BasePoint minterto, pinterto;
654     double nangle, pangle;
655     struct strokedspline *next, *prev;
656 };
657 
StrokeEndComplete(struct strokedspline * cur,StrokeInfo * si,int isstart)658 static void StrokeEndComplete(struct strokedspline *cur,StrokeInfo *si,int isstart) {
659     SplinePoint *edgestart, *edgeend, *curat, *edgeat;
660     struct strokedspline *lastp, *lastm;
661 
662     if ( isstart ) {
663 	edgestart = StrokeEnd(cur->s->from,si,true,&edgeend);
664 	for ( lastp=cur; lastp!=NULL && lastp->plusskip ; lastp=lastp->next );
665 	for ( lastm=cur; lastm!=NULL && lastm->minusskip ; lastm=lastm->next );
666 	if ( lastm==cur )
667 	    MSP(edgeend,&cur->minusfrom,&cur->minusto);
668 	else {
669 	    curat = Intersect_Splines(lastm->minusfrom,edgeend,&edgeat);
670 	    if ( curat!=NULL ) {
671 		SplineFreeBetween(lastm->minusfrom,curat,true,false);
672 		SplineFreeBetween(edgeat,edgeend,false,true);
673 	    } else
674 		MSP(edgeend,&lastm->minusfrom,&lastm->minusto);
675 	}
676 	if ( lastp==cur )
677 	    MergeSplinePoint(cur->plusto,edgestart);
678 	else {
679 	    edgeat = Intersect_Splines(edgestart,lastp->plusto,&curat);
680 	    if ( curat!=NULL ) {
681 		SplineFreeBetween(curat,lastp->plusto,false,true);
682 		SplineFreeBetween(edgestart,edgeat,true,false);
683 	    } else
684 		MergeSplinePoint(lastp->plusto,edgestart);
685 	}
686     } else {
687 	edgestart = StrokeEnd(cur->s->to,si,false,&edgeend);
688 	for ( lastp=cur; lastp!=NULL && lastp->plusskip ; lastp=lastp->prev );
689 	for ( lastm=cur; lastm!=NULL && lastm->minusskip ; lastm=lastm->prev );
690 	if ( lastp==cur )
691 	    MSP(edgeend,&cur->plusfrom,&cur->plusto);
692 	else {
693 	    curat = Intersect_Splines(lastp->plusfrom,edgeend,&edgeat);
694 	    if ( curat!=NULL ) {
695 		SplineFreeBetween(lastp->plusfrom,curat,true,false);
696 		lastp->plusfrom = curat;
697 		SplineFreeBetween(edgeat,edgeend,false,true);
698 		lastp->plusfrom = MergeSplinePoint(edgeat,curat);
699 	    } else
700 		MSP(edgeend,&lastp->plusfrom,&lastp->plusto);
701 	}
702 	if ( lastm==cur )
703 	    MergeSplinePoint(cur->minusto,edgestart);
704 	else {
705 	    edgeat = Intersect_Splines(edgestart,lastm->minusto,&curat);
706 	    if ( curat!=NULL ) {
707 		SplineFreeBetween(curat,lastm->minusto,false,true);
708 		lastm->minusto = curat;
709 		SplineFreeBetween(edgestart,edgeat,true,false);
710 		MergeSplinePoint(lastm->minusto,edgeat);
711 	    } else
712 		MergeSplinePoint(lastm->minusto,edgestart);
713 	}
714     }
715 }
716 
StrokedSplineFree(struct strokedspline * head)717 static void StrokedSplineFree(struct strokedspline *head) {
718     struct strokedspline *next, *cur=head;
719 
720     while ( cur!=NULL ) {
721 	next = cur->next;
722 	chunkfree(cur,sizeof(*cur));
723 	cur = next;
724 	if ( cur==head )
725     break;
726     }
727 }
728 
FreeOrigStuff(struct strokedspline * before)729 static void FreeOrigStuff(struct strokedspline *before) {
730 
731     if ( before->origminusto!=NULL )
732 	SplineFreeBackward(before->origminusto);
733     before->origminusto = NULL;
734     if ( before->origplusfrom!=NULL )
735 	SplineFreeForeward(before->origplusfrom);
736     before->origplusfrom = NULL;
737 }
738 
SplineMakeRound(SplinePoint * from,SplinePoint * to,real radius)739 static void SplineMakeRound(SplinePoint *from,SplinePoint *to, real radius) {
740     /* I believe this only gets called when we have a line join where the */
741     /*  contour makes a U-Turn (opposite of being colinear) */
742     BasePoint dir;
743     SplinePoint *center;
744 
745     dir.x = (to->me.y-from->me.y)/2;
746     dir.y = -(to->me.x-from->me.x)/2;
747     center = SplinePointCreate((to->me.x+from->me.x)/2+dir.x,
748 	    (to->me.y+from->me.y)/2+dir.y);
749     from->nextcp.x = from->me.x + .552*dir.x;
750     from->nextcp.y = from->me.y + .552*dir.y;
751     to->prevcp.x = to->me.x + .552*dir.x;
752     to->prevcp.y = to->me.y + .552*dir.y;
753     from->nonextcp = to->noprevcp = false;
754     center->prevcp.x = center->me.x + .552*dir.y;
755     center->nextcp.x = center->me.x - .552*dir.y;
756     center->prevcp.y = center->me.y - .552*dir.x;
757     center->nextcp.y = center->me.y + .552*dir.x;
758     center->nonextcp = center->noprevcp = false;
759     SplineMake3(from,center);
760     SplineMake3(center,to);
761 }
762 
DoIntersect_Splines(struct strokedspline * before,struct strokedspline * after,int doplus,StrokeInfo * si,SplineChar * sc,int force_connect)763 static int DoIntersect_Splines(struct strokedspline *before,
764 	struct strokedspline *after, int doplus,StrokeInfo *si,SplineChar *sc,
765 	int force_connect ) {
766     SplinePoint *beforeat, *afterat;
767     int ret = true;
768     int toobig = false;
769 
770     if ( doplus ) {
771 	beforeat = Intersect_Splines(before->plusfrom,after->plusto,&afterat);
772 	if ( beforeat!=NULL ) {
773 	    after->origplusfrom = after->plusfrom;
774 	    after->plusto = SplineCopyBefore(afterat,&after->plusfrom);
775 	    SplineFreeBetween(before->plusfrom,beforeat,true/*free before->plusfrom*/,false/* keep beforeat */);
776 	    before->plusfrom = beforeat;
777 	} else if ( before->origplusfrom!=NULL &&
778 		(beforeat = Intersect_Splines(before->origplusfrom,after->plusto,&afterat))!=NULL ) {
779 	    toobig = true;
780 	    after->origplusfrom = after->plusfrom;
781 	    after->plusto = SplineCopyBefore(afterat,&after->plusfrom);
782 	    SplineFreeBetween(before->plusfrom,before->plusto,true/*free plusfrom*/,false);
783 	    before->plusfrom = SplinePointCreate(afterat->me.x,afterat->me.y);
784 	    before->plusfrom->nextcp = before->plusfrom->me;
785 	    before->plusfrom->nonextcp = true;
786 	    SplineMake3(before->plusfrom,before->plusto);	/* This line goes backwards */
787 #if 0		/* This introduces lots of bugs, it gets invoked when it */
788 		/*  shouldn't, and I can't figure out how to distinguish */
789 	} else if ( EntirelyWithin(before->plusfrom,after->s,true,si->radius) ) {
790 	    /* the splines at before are all within radius units of the original */
791 	    /*  after spline. This means that they will make no contribution */
792 	    /*  to the outline. */
793 	    if ( before->prev!=NULL && before->prev!=after )
794 		ret = DoIntersect_Splines(before->prev,after,doplus,si,sc);
795 	    before->plusskip = true;
796 	    toobig = ret;
797 	} else if ( EntirelyWithin(after->plusto,before->s,false,si->radius) ) {
798 	    /* the splines at after are entirely within radius units of the original */
799 	    if ( after->next!=NULL && after->next!=before )
800 		ret = DoIntersect_Splines(before,after->next,doplus,si,sc);
801 	    after->plusskip = true;
802 	    toobig = ret;
803 #endif
804 	} else {
805 	    /* No intersection everything can stay as it is */
806 	    if ( force_connect && BasePtDistance(&after->plusto->me,&before->plusfrom->me)>3 ) {
807 		beforeat = SplinePointCreate(after->plusto->me.x,after->plusto->me.y);
808 		if ( si->join==lj_round )
809 		    SplineMakeRound(beforeat,before->plusfrom,si->radius);
810 		else
811 		    SplineMake3(beforeat,before->plusfrom);
812 		before->plusfrom = beforeat;
813 		toobig = true;
814 	    }
815 	    ret = false;
816 	}
817     } else {
818 	afterat = Intersect_Splines(after->minusfrom,before->minusto,&beforeat);
819 	if ( afterat!=NULL ) {
820 	    after->origminusto = after->minusto;
821 	    after->minusfrom = SplineCopyAfter(afterat,&after->minusto);
822 	    SplineFreeBetween(beforeat,before->minusto,false/*keep beforeat*/,true);
823 	    before->minusto = beforeat;
824 	} else if ( before->origminusto!=NULL &&
825 		(afterat = Intersect_Splines(after->minusfrom,before->origminusto,&beforeat))!=NULL ) {
826 	    toobig = true;
827 	    after->origminusto = after->minusto;
828 	    after->minusfrom = SplineCopyAfter(afterat,&after->minusto);
829 	    SplineFreeBetween(before->minusfrom,before->minusto,false/*keep minusfrom*/,true);
830 	    before->minusto = SplinePointCreate(afterat->me.x,afterat->me.y);
831 	    before->minusto->ptindex = afterat->ptindex;
832 	    before->minusfrom->nextcp = before->minusfrom->me;
833 	    before->minusfrom->nonextcp = true;
834 	    SplineMake3(before->minusfrom,before->minusto);	/* This line goes backwards */
835 #if 0		/* This introduces lots of bugs, it gets invoked when it */
836 		/*  shouldn't, and I can't figure out how to distinguish */
837 	} else if ( EntirelyWithin(before->minusto,after->s,false,si->radius) ) {
838 	    /* the splines at before are all within radius units of the original */
839 	    /*  after spline. This means that they will make no contribution */
840 	    /*  to the outline. */
841 	    ret = false;
842 	    if ( before->prev!=NULL && before->prev!=after && before->prev!=after->next )
843 		ret = DoIntersect_Splines(before->prev,after,doplus,si,sc);
844 	    before->minusskip = true;
845 	    toobig = ret;
846 	} else if ( EntirelyWithin(after->minusfrom,before->s,true,si->radius) ) {
847 	    /* the splines at after are entirely within radius units of the original */
848 	    ret = false;
849 	    if ( after->next!=NULL && after->next!=before && before->prev!=after->next )
850 		ret = DoIntersect_Splines(before,after->next,doplus,si,sc);
851 	    after->minusskip = true;
852 	    toobig = ret;
853 #endif
854 	} else {
855 	    /* No intersection everything can stay as it is */
856 	    if ( force_connect && BasePtDistance(&after->minusfrom->me,&before->minusto->me)>3 ) {
857 		beforeat = SplinePointCreate(after->minusfrom->me.x,after->minusfrom->me.y);
858 		beforeat->ptindex = after->minusfrom->ptindex;
859 		if ( si->join==lj_round )
860 		    SplineMakeRound(before->minusto,beforeat,si->radius);
861 		else
862 		    SplineMake3(before->minusto,beforeat);
863 		before->minusto = beforeat;
864 		toobig = true;
865 	    }
866 	    ret = false;
867 	}
868     }
869 
870     if ( toobig ) {
871 	si->gottoobig = si->gottoobiglocal = true;
872 	if ( !si->toobigwarn ) {
873 	    si->toobigwarn = true;
874 	    ff_post_error( _("Bad Stroke"), _("The stroke width is so big that the generated path\nmay intersect itself in %.100s"),
875 		    sc==NULL?"<nameless char>": sc->name );
876 	}
877     }
878 return( ret );
879 }
880 
881 /* Plus joins run from prev to next, minus joins run from next to prev */
882 /* This makes plus joins clockwise and minus joins counter */
StrokeJoint(SplinePoint * base,StrokeInfo * si,struct strokedspline * before,struct strokedspline * after,SplineChar * sc)883 static void StrokeJoint(SplinePoint *base,StrokeInfo *si,
884 	struct strokedspline *before,struct strokedspline *after,
885 	SplineChar *sc) {
886     BasePoint nplus, nminus, pplus,pminus;
887     double nangle, pangle;
888     int pinner, minner;
889 #if 0
890     double pt, mt;
891     double tt, xdiff, ydiff;
892 #endif
893 
894     before->pangle = pangle = SplineExpand(base->prev,1,0,si,&pplus,&pminus);
895     before->nangle = nangle = SplineExpand(base->next,0,0,si,&nplus,&nminus);
896 
897     if ( RealWithin(pangle,nangle,.1) || RealWithin(pangle+2*PI,nangle,.1) ||
898 	    RealWithin(pangle,nangle+2*PI,.1)) {
899 	/* If the two splines are tangent at the base, then everything is */
900 	/*  simple, there is no join, things match up perfectly */
901 	/* Um. No. If there is a sharp bend or a corner nearby then it may */
902 	/*  have the same effect as a corner, in extreme cases the entire */
903 	/*  spline may be eaten up */
904 	/* Actually, that's probably done best in Remove Overlap. If we try */
905 	/*  to do it here, we unlease lots of potentials for bugs in other */
906 	/*  cases */
907 
908 #if 0
909 	if ( (xdiff = base->me.x-base->prev->from->me.x)<0 ) xdiff = -xdiff;
910 	if ( (ydiff = base->me.y-base->prev->from->me.y)<0 ) ydiff = -ydiff;
911 	if ( xdiff+ydiff==0 ) xdiff = 1;
912 	tt = si->radius/(2*(xdiff+ydiff));
913 	if ( tt>.2 ) tt = .2;
914 	OnEdge(&pplus,&pminus,base->next,0,1.0-tt,base->prev,
915 		si,&pt,&mt,NULL,NULL);
916 	if ( pt!=-1 )
917 	    DoIntersect_Splines(before,after,true,si,sc,true);
918 	else {
919 	    if ( (xdiff = base->me.x-base->next->to->me.x)<0 ) xdiff = -xdiff;
920 	    if ( (ydiff = base->me.y-base->next->to->me.y)<0 ) ydiff = -ydiff;
921 	    tt = si->radius/(2*(xdiff+ydiff));
922 	    if ( tt>.2 ) tt = .2;
923 	    OnEdge(&nplus,&nminus,base->prev,1.,tt,base->next,
924 		    si,NULL,NULL,&pt,&mt);
925 	    if ( mt!=-1 )
926 		DoIntersect_Splines(before,after,false,si,sc,true);
927 	}
928 #endif
929 	before->pinnerto = before->minnerto = -1;
930     } else {
931 	pinner = Intersect_Lines(&before->pinterto,&pplus,
932 		 3*base->prev->splines[0].a+2*base->prev->splines[0].b+base->prev->splines[0].c,
933 		 3*base->prev->splines[1].a+2*base->prev->splines[1].b+base->prev->splines[1].c,
934 		&nplus,
935 		 base->next->splines[0].c,
936 		 base->next->splines[1].c,si->radius);
937 	minner = Intersect_Lines(&before->minterto,&pminus,
938 		 3*base->prev->splines[0].a+2*base->prev->splines[0].b+base->prev->splines[0].c,
939 		 3*base->prev->splines[1].a+2*base->prev->splines[1].b+base->prev->splines[1].c,
940 		&nminus,
941 		 base->next->splines[0].c,
942 		 base->next->splines[1].c,si->radius);
943 	if ( pinner==-1 && minner!=-1 )
944 	    pinner = !minner;
945 	before->pinnerto = pinner; before->minnerto = (pinner!=-1?!pinner:-1);
946 	if ( pinner==1 ) {
947 	    DoIntersect_Splines(before,after,true,si,sc,true);
948 	} else if ( pinner==0 ) {
949 	    DoIntersect_Splines(before,after,false,si,sc,true);
950 	} else {	/* splines are parallel, but moving in same dir */
951 	    if ( DoIntersect_Splines(before,after,true,si,sc,false)) {
952 		before->pinnerto = 1;
953 		before->minnerto = 0;
954 	    } else {
955 		if ( DoIntersect_Splines(before,after,false,si,sc,true)) {
956 		    before->pinnerto = 0;
957 		    before->minnerto = 1;
958 		} else
959 		    DoIntersect_Splines(before,after,true,si,sc,true);
960 	    }
961 	}
962     }
963 }
964 
SplineSolveForPen(Spline * s,StrokeInfo * si,double * ts,int * pinners,double tstart,double tend)965 static int SplineSolveForPen(Spline *s,StrokeInfo *si,double *ts,int *pinners,
966 	double tstart,double tend) {
967     /* Find all the places at which the spline has the same slope as one of the */
968     /*  edges of the pen. There can be at most 8 (we get four quadratics) */
969     double a, b, c, sq, t1, t2;
970     int i, cnt=0, j;
971     Spline1D *xsp = &s->splines[0], *ysp = &s->splines[1];
972     BasePoint pp, pm, np, nm, testp, testm;
973 
974     ts[cnt++] = tstart;
975     for ( i=0; i<2; ++i ) {
976 	if ( i==0 ) {
977 	    a = 3*(ysp->a*si->c-xsp->a*si->s);
978 	    b = 2*(ysp->b*si->c-xsp->b*si->s);
979 	    c = ysp->c*si->c-xsp->c*si->s;
980 	} else if ( i==1 ) {
981 	    a = 3*(-ysp->a*si->c-xsp->a*si->s);
982 	    b = 2*(-ysp->b*si->c-xsp->b*si->s);
983 	    c = -ysp->c*si->c-xsp->c*si->s;
984 #if 0	/* These two are just the negatives of the first two and as such have the same roots */
985 	} else if ( i==2 ) {
986 	    a = 3*(-ysp->a*si->c+xsp->a*si->s);
987 	    b = 2*(-ysp->b*si->c+xsp->b*si->s);
988 	    c = -ysp->c*si->c+xsp->c*si->s;
989 	} else {
990 	    a = 3*(ysp->a*si->c+xsp->a*si->s);
991 	    b = 2*(ysp->b*si->c+xsp->b*si->s);
992 	    c = ysp->c*si->c+xsp->c*si->s;
993 #endif
994 	}
995 	sq = b*b-4*a*c;
996 	if ( sq==0 ) {
997 	    t1 = -b/(2*a);
998 	    t2 = -1;
999 	} else if ( sq>0 ) {
1000 	    sq = sqrt(sq);
1001 	    t1 = (-b+sq)/(2*a);
1002 	    t2 = (-b-sq)/(2*a);
1003 	} else
1004 	    t1 = t2 = -1;
1005 	if ( t1>tstart && t1<tend )
1006 	    ts[cnt++] = t1;
1007 	if ( t2>tstart && t2<tend )
1008 	    ts[cnt++] = t2;
1009     }
1010     ts[cnt++] = tend;
1011     if ( cnt<=2 )
1012 return(cnt);
1013     /* Order them */
1014     for ( i=1; i<cnt-1; ++i ) for ( j=i+1; j<cnt; ++j )
1015 	if ( ts[i]>ts[j] ) {
1016 	    double temp = ts[i];
1017 	    ts[i] = ts[j];
1018 	    ts[j] = temp;
1019 	}
1020     /* Figure which side is inner */
1021     for ( i=1; i<cnt-1; ++i ) {
1022 	SplineExpand(s,ts[i],-(ts[i]-ts[i-1])/20.,si,&pp,&pm);
1023 	SplineExpand(s,ts[i],(ts[i+1]-ts[i])/20.,si,&np,&nm);
1024 	SplineExpand(s,ts[i]+(ts[i+1]-ts[i])/20.,0,si,&testp,&testm);
1025 	pinners[i] = ( (testp.x-np.x)*(pp.x-np.x)+(testp.y-np.y)*(pp.y-np.y)> 0 );
1026     }
1027 return( cnt );
1028 }
1029 
1030 
SplineSetFixCPs(SplineSet * ss)1031 static void SplineSetFixCPs(SplineSet *ss) {
1032     SplinePoint *sp;
1033 
1034     for ( sp=ss->first; ; ) {
1035 	SPWeightedAverageCps(sp);
1036 	if ( sp->next==NULL )
1037     break;
1038 	sp = sp->next->to;
1039 	if ( sp==ss->first )
1040     break;
1041     }
1042     SPLCatagorizePoints(ss);
1043 }
1044 
SPNew(SplinePoint * base,BasePoint * pos,BasePoint * cp,int isnext)1045 static SplinePoint *SPNew(SplinePoint *base,BasePoint *pos,BasePoint *cp,int isnext) {
1046     SplinePoint *sp = SplinePointCreate(pos->x,pos->y);
1047 
1048     sp->pointtype = base->pointtype;
1049     /* Embolden wants these three preserved */
1050     sp->ptindex = base->ptindex;
1051     sp->ttfindex = base->ttfindex;
1052     sp->nextcpindex = base->nextcpindex;
1053     if ( isnext ) {
1054 	sp->nextcp.x = pos->x + (cp->x-base->me.x);
1055 	sp->nextcp.y = pos->y + (cp->y-base->me.y);
1056 	sp->nonextcp = (sp->nextcp.x==pos->x) && (sp->nextcp.y==pos->y);
1057     } else {
1058 	sp->prevcp.x = pos->x + (cp->x-base->me.x);
1059 	sp->prevcp.y = pos->y + (cp->y-base->me.y);
1060 	sp->noprevcp = (sp->prevcp.x==pos->x) && (sp->prevcp.y==pos->y);
1061     }
1062 return( sp );
1063 }
1064 
NormalizeT(TPoint * mids,int cnt,double tbase,double tend)1065 static void NormalizeT(TPoint *mids,int cnt,double tbase,double tend) {
1066     int i;
1067 
1068     for ( i=0; i<cnt; ++i )
1069 	mids[i].t = (mids[i].t - tbase)/(tend - tbase);
1070 }
1071 
SPFigureCP(SplinePoint * sp,double t,Spline * spline,int isnext)1072 static void SPFigureCP(SplinePoint *sp,double t,Spline *spline,int isnext) {
1073     Spline temp;
1074     double tn;
1075     Spline1D *s1;
1076     BasePoint off;
1077 
1078     s1 = &spline->splines[0];
1079     off.x = sp->me.x - ( ((s1->a*t+s1->b)*t+s1->c)*t+s1->d );
1080     s1 = &spline->splines[1];
1081     off.y = sp->me.y - ( ((s1->a*t+s1->b)*t+s1->c)*t+s1->d );
1082 
1083     if ( isnext ) {
1084 	double s = (1.0-t);
1085 	/* We want to renormalize the spline so that it runs from [t,1] and */
1086 	/*  then figure what the control point at t should be */
1087 	s1 = &spline->splines[0];
1088 	temp.splines[0].d = s1->d + t*(s1->c + t*(s1->b + t*s1->a));
1089 	temp.splines[0].c = s*(s1->c + t*(2*s1->b + 3*s1->a*t));
1090 	temp.splines[0].b = s*s*(s1->b+3*s1->a*t);
1091 #if 0
1092 	temp.splines[0].a = s*s*s*s1->a;
1093 #endif
1094 	s1 = &spline->splines[1];
1095 	temp.splines[1].d = s1->d + t*(s1->c + t*(s1->b + t*s1->a));
1096 	temp.splines[1].c = s*(s1->c + t*(2*s1->b + 3*s1->a*t));
1097 	temp.splines[1].b = s*s*(s1->b+3*s1->a*t);
1098 #if 0
1099 	temp.splines[1].a = s*s*s*s1->a;
1100 #endif
1101 	if ( spline->order2 ) {
1102 	    sp->nextcp.x = temp.splines[0].d + temp.splines[0].c/2 + off.x;
1103 	    sp->nextcp.y = temp.splines[1].d + temp.splines[1].c/2 + off.y;
1104 	} else {
1105 	    sp->nextcp.x = temp.splines[0].d + temp.splines[0].c/3 + off.x;
1106 	    sp->nextcp.y = temp.splines[1].d + temp.splines[1].c/3 + off.y;
1107 	}
1108 	sp->nonextcp = false;
1109     } else {
1110 	/* We want to renormalize the spline so that it runs from [0,t] and */
1111 	/*  then figure what the control point at t should be */
1112 	temp = *spline;
1113 	temp.splines[0].c *= t;		temp.splines[1].c *= t;
1114 	tn = t*t;
1115 	temp.splines[0].b *= tn;	temp.splines[1].b *= tn;
1116 #if 0
1117 	tn *= t;
1118 	temp.splines[0].a *= tn;	temp.splines[1].a *= tn;
1119 #endif
1120 	if ( spline->order2 ) {
1121 	    sp->prevcp.x = temp.splines[0].d + temp.splines[0].c/2 + off.x;
1122 	    sp->prevcp.y = temp.splines[1].d + temp.splines[1].c/2 + off.y;
1123 	} else {
1124 	    sp->prevcp.x = temp.splines[0].d + (2*temp.splines[0].c+temp.splines[0].b)/3 + off.x;
1125 	    sp->prevcp.y = temp.splines[1].d + (2*temp.splines[1].c+temp.splines[1].b)/3 + off.y;
1126 	}
1127 	sp->noprevcp = false;
1128     }
1129 }
1130 
SPFigurePlusCP(SplinePoint * sp,double t,Spline * spline,int isnext)1131 static void SPFigurePlusCP(SplinePoint *sp,double t,Spline *spline,int isnext) {
1132     SplinePoint dummy;
1133 
1134     /* Plus splines run in the oposite direction */
1135     dummy = *sp;
1136     SPFigureCP(&dummy,t,spline,!isnext);
1137     if ( isnext ) {
1138 	sp->nextcp = dummy.prevcp;
1139 	sp->nonextcp = false;
1140     } else {
1141 	sp->prevcp = dummy.nextcp;
1142 	sp->noprevcp = false;
1143     }
1144 }
1145 
Overlaps(TPoint * expanded,TPoint * inner,double rsq)1146 static int Overlaps(TPoint *expanded,TPoint *inner,double rsq) {
1147     double len;
1148     BasePoint dir;
1149 
1150     dir.x = (expanded->x-inner->x); dir.y = (expanded->y-inner->y);
1151     len = (dir.x*dir.x) + (dir.y*dir.y);
1152     if ( len>=rsq )
1153 return( false );
1154     len = sqrt(rsq/len);
1155     expanded->x = inner->x + len*dir.x;
1156     expanded->y = inner->y + len*dir.y;
1157 return( true );
1158 }
1159 
1160 #define Approx	10
1161 
_SplineSetApprox(SplineSet * spl,StrokeInfo * si,SplineChar * sc)1162 static struct strokedspline *_SplineSetApprox(SplineSet *spl,StrokeInfo *si,SplineChar *sc) {
1163     struct strokedspline *head=NULL, *last=NULL, *cur;
1164     int max=Approx;
1165     TPoint *pmids=galloc(max*sizeof(TPoint)),
1166 	    *mmids=galloc(max*sizeof(TPoint)),
1167 	    *mids=galloc(max*sizeof(TPoint));
1168     uint8 *knots=galloc(max);
1169     BasePoint pto, mto, pfrom, mfrom;
1170     double approx, xdiff, ydiff, loopdiff;
1171     Spline *spline, *first;
1172     int i,j,k;
1173     SplinePoint *p_to, *m_to, *p_from, *m_from;
1174     int cnt, anyknots;
1175     double ts[9];
1176     BasePoint m,p,temp;
1177     double mt1, pt1, mt2, pt2, rsq;
1178     int pinners[10];
1179     int mwascovered, pwascovered;
1180     enum knot_type { kt_knot=1, kt_pgood=2, kt_mgood=4 };
1181     int toobig;
1182 
1183     first = NULL;
1184     for ( spline = spl->first->next; spline!=NULL && spline!=first; spline = spline->to->next ) {
1185 	cur = chunkalloc(sizeof(struct strokedspline));
1186 	if ( last==NULL )
1187 	    head = cur;
1188 	else {
1189 	    last->next = cur;
1190 	    cur->prev = last;
1191 	}
1192 	last = cur;
1193 	cur->s = spline;
1194 	SplineIsLinearMake(spline);
1195 	SplineExpand(spline,0,0,si,&pto,&mfrom);
1196 	SplineExpand(spline,1,0,si,&pfrom,&mto);
1197 	cur->minusfrom = SPNew(spline->from,&mfrom,&spline->from->nextcp,true);
1198 	cur->plusto = SPNew(spline->from,&pto,&spline->from->nextcp,false);
1199 	cur->minusto = SPNew(spline->to,&mto,&spline->to->prevcp,false);
1200 	cur->plusfrom = SPNew(spline->to,&pfrom,&spline->to->prevcp,true);
1201 
1202 	if ( si->stroke_type == si_caligraphic ) {
1203 	    /* At each t where the spline is tangent to one of the pen-angles */
1204 	    /*  we need to figure out which side is inner and which is outer */
1205 	    /*  the outer side gets a copy of the appropriate pen side (with corner points tangent) */
1206 	    /*  the inner side is going to be a single corner point at the */
1207 	    /*  intersection of the splines from the two corners */
1208 	    /* And if (god help us) we've got a point of inflection here then */
1209 	    /*  we get half the pen on each side */
1210 	    /* I ignore the case of a point of inflection, and I don't */
1211 	    /*  find the real intersection point, I just guess that it is */
1212 	    /*  near the mid point of the pen */
1213 	    cnt = SplineSolveForPen(spline,si,ts,pinners+1,0,1);
1214 	    p_to = m_to = NULL;
1215 	    p_from = NULL;		/* Make gcc happy */
1216 	    for ( j=1; j<cnt; ++j ) {
1217 		for ( i=0; i<Approx; ++i ) {
1218 		    real t = ts[j-1] + (i+1)*(ts[j]-ts[j-1])/(Approx+1);
1219 		    mmids[i].t = (i+1)/(double) (Approx+1); pmids[i].t = 1-mmids[i].t;
1220 		    SplineExpand(spline,t,0,si,&p,&m);
1221 		    pmids[i].x = p.x; pmids[i].y = p.y;
1222 		    mmids[i].x = m.x; mmids[i].y = m.y;
1223 		}
1224 		if ( j==1 ) {
1225 		    p_to = cur->plusto; m_from = cur->minusfrom;
1226 		} else if ( pinners[j-1] ) {
1227 		    p_to = p_from;
1228 		    SplineExpand(spline,ts[j-1],(ts[j-1]-ts[j-2])/20.,si,&p,&m);
1229 		    m_from = SplinePointCreate(m.x,m.y);
1230 		    m_from->pointtype = pt_tangent;
1231 		    SplineMake3(m_to,m_from);
1232 		} else {
1233 		    m_from = m_to;
1234 		    SplineExpand(spline,ts[j-1],(ts[j-1]-ts[j-2])/20.,si,&p,&m);
1235 		    p_to = SplinePointCreate(p.x,p.y);
1236 		    p_to->pointtype = pt_tangent;
1237 		    SplineMake3(p_to,p_from);
1238 		}
1239 		if ( j==cnt-1 ) {
1240 		    p_from = cur->plusfrom;
1241 		    m_to = cur->minusto;
1242 		} else if ( pinners[j] ) {
1243 		    SplineExpand(spline,ts[j],(ts[j+1]-ts[j-1])/20.,si,&p,&m);
1244 		    SplineExpand(spline,ts[j],-(ts[j+1]-ts[j-1])/20.,si,&temp,&m);
1245 		    p_from = SplinePointCreate((p.x+temp.x)/2,(p.y+temp.y)/2);
1246 		    p_from->pointtype = pt_corner;
1247 		    m_to = SplinePointCreate(m.x,m.y);
1248 		    m_to->pointtype = pt_tangent;
1249 		} else {
1250 		    SplineExpand(spline,ts[j],(ts[j+1]-ts[j-1])/20.,si,&p,&m);
1251 		    SplineExpand(spline,ts[j],-(ts[j+1]-ts[j-1])/20.,si,&p,&temp);
1252 		    p_from = SplinePointCreate(p.x,p.y);
1253 		    p_from->pointtype = pt_tangent;
1254 		    m_to = SplinePointCreate((m.x+temp.x)/2,(m.y+temp.y)/2);
1255 		    m_to->pointtype = pt_corner;
1256 		}
1257 		ApproximateSplineFromPoints(p_from,p_to,pmids,Approx,false);
1258 		ApproximateSplineFromPoints(m_from,m_to,mmids,Approx,false);
1259 		if ( m_from!=cur->minusfrom && m_from->pointtype!=pt_corner )
1260 		    m_from->pointtype = pt_tangent;
1261 	    }
1262 	} else {
1263 	    /* Figure out where the curve starts to bend sharply, and add */
1264 	    /* New points there. I used to strip out the curve where it */
1265 	    /*  overlapped itself, but I think that's better done by remove */
1266 	    /*  overlap rather than here */
1267 	    if ( (xdiff = spline->to->me.x-spline->from->me.x)<0 ) xdiff = -xdiff;
1268 	    if ( (ydiff = spline->to->me.y-spline->from->me.y)<0 ) ydiff = -ydiff;
1269 	    loopdiff = (xdiff+ydiff==0) ? .1 : 1.0/(4*(xdiff+ydiff)/si->radius);
1270 	    approx = rint(1.0/loopdiff);
1271 	    if ( approx<0 || approx>3000 ) approx=3000;
1272 	    if ( approx>max ) {
1273 		max = approx+10;
1274 		pmids = grealloc(pmids,max*sizeof(TPoint));
1275 		mmids = grealloc(mmids,max*sizeof(TPoint));
1276 		mids = grealloc(mids,max*sizeof(TPoint));
1277 		knots = grealloc(knots,max);
1278 	    }
1279 
1280 	    mwascovered = pwascovered = false;
1281 	    toobig = false;
1282 	    for ( i=0; i<approx; ++i ) {
1283 		real t = (i+1)/(approx+1);
1284 		SplineExpand(spline,t,0,si,&p,&m);
1285 		OnEdge(&p,&m,spline,t,t,spline,si,&pt1,&mt1,&pt2,&mt2);
1286 		knots[i] = 0;
1287 		if ( ((pt1!=-1 || pt2!=-1) && !pwascovered && i!=0) ||
1288 			((mt1!=-1 || mt2!=-1) && !mwascovered && i!=0))
1289 		    knots[i] = kt_knot;
1290 		if ( ((pt1==-1 && pt2==-1) && pwascovered && i!=0 ) ||
1291 			((mt1==-1 && mt2==-1) && mwascovered && i!=0 )) {
1292 		    if ( knots[i-1]&kt_knot )
1293 			knots[i] = kt_knot;
1294 		    else
1295 			knots[i-1] |= kt_knot;
1296 		}
1297 		pwascovered = pt1!=-1 || pt2!=-1;
1298 		mwascovered = mt1!=-1 || mt2!=-1;
1299 		pmids[i].t = 1-(i+1)/(approx+1);
1300 		pmids[i].x = p.x; pmids[i].y = p.y;
1301 		mmids[i].t = (i+1)/(approx+1);
1302 		mmids[i].x = m.x; mmids[i].y = m.y;
1303 		mids[i].x = (m.x+p.x)/2; mids[i].y = (m.y+p.y)/2;
1304 		/*if ( !pwascovered )*/ knots[i] |= kt_pgood;
1305 		/*if ( !mwascovered )*/ knots[i] |= kt_mgood;
1306 		if ( pwascovered || mwascovered )
1307 		    toobig = true;
1308 	    }
1309 	    rsq = si->radius*si->radius;
1310 	    for ( i=0; i<approx; ++i ) {
1311 		for ( j=1; j<approx/2; ++j ) {
1312 		    if ( i+j<approx ) {
1313 			Overlaps(&mmids[i],&mids[i+j],rsq);
1314 			Overlaps(&pmids[i],&mids[i+j],rsq);
1315 		    }
1316 		    if ( i-j>0 ) {
1317 			Overlaps(&mmids[i],&mids[i-j],rsq);
1318 			Overlaps(&pmids[i],&mids[i-j],rsq);
1319 		    }
1320 		}
1321 	    }
1322 	    anyknots = false;
1323 	    for ( i=0; i<approx; ++i ) if ( knots[i]&kt_knot ) { anyknots=true; break; }
1324 	    if ( toobig ) {
1325 		si->gottoobig = si->gottoobiglocal = true;
1326 		if ( !si->toobigwarn ) {
1327 		    si->toobigwarn = true;
1328 		    ff_post_error( _("Bad Stroke"), _("The stroke width is so big that the generated path\nmay intersect itself in %.100s"),
1329 			    sc==NULL?"<nameless char>": sc->name );
1330 		}
1331 	    }
1332 
1333 	    /* Look for any sharp bends, they give us problems which are */
1334 	    /*  eased by creating a new point. */
1335 	    if ( !anyknots ) {
1336 		double radius = si->radius;
1337 		si->radius *= 2;
1338 		mwascovered = pwascovered = false;
1339 		for ( i=0; i<approx; ++i ) {
1340 		    real t = (i+1)/(approx+1);
1341 		    SplineExpand(spline,t,0,si,&p,&m);
1342 		    OnEdge(&p,&m,spline,t,t,spline,si,&pt1,&mt1,&pt2,&mt2);
1343 		    if ( ((pt1!=-1 || pt2!=-1) && !pwascovered && i!=0) ||
1344 			    ((mt1!=-1 || mt2!=-1) && !mwascovered && i!=0))
1345 			knots[i] |= kt_knot;
1346 		    if ( ((pt1==-1 && pt2==-1) && pwascovered && i!=0 ) ||
1347 			    ((mt1==-1 && mt2==-1) && mwascovered && i!=0 )) {
1348 			if ( knots[i-1]&kt_knot )
1349 			    knots[i] |= kt_knot;
1350 			else
1351 			    knots[i-1] |= kt_knot;
1352 		    }
1353 		    pwascovered = pt1!=-1 || pt2!=-1;
1354 		    mwascovered = mt1!=-1 || mt2!=-1;
1355 		}
1356 		si->radius = radius;
1357 	    }
1358 
1359 	    p_to = cur->plusto;
1360 	    m_from = cur->minusfrom;
1361 	    for ( i=0, j=1; i<approx; ++i ) {
1362 		if ( knots[i]&kt_knot ) {
1363 		    for ( k=i+1; k<approx && !(knots[k]&kt_knot); ++k );
1364 		    if ( i>0 && (knots[i-1]&kt_mgood) ) {
1365 			if ( i+1<approx && !(knots[i+1]&kt_mgood) && k<approx )
1366 			    m_to = SplinePointCreate((mmids[i].x+mmids[k].x)/2,(mmids[i].y+mmids[k].y)/2);
1367 			else
1368 			    m_to = SplinePointCreate(mmids[i].x,mmids[i].y);
1369 			m_to->pointtype = pt_corner;
1370 			SPFigureCP(m_from,(j)/(approx+1),spline,true);
1371 			SPFigureCP(m_to,(i+1)/(approx+1),spline,false);
1372 			NormalizeT(mmids+j,i-j,mmids[j-1].t,mmids[i].t);
1373 			ApproximateSplineFromPointsSlopes(m_from,m_to,mmids+j,i-j,false);
1374 			m_from = m_to;
1375 		    }
1376 
1377 		    if ( i>0 && (knots[i-1]&kt_pgood) ) {
1378 			if ( i+1<approx && !(knots[i+1]&kt_pgood) && k<approx )
1379 			    p_from = SplinePointCreate((pmids[i].x+pmids[k].x)/2,(pmids[i].y+pmids[k].y)/2);
1380 			else
1381 			    p_from = SplinePointCreate(pmids[i].x,pmids[i].y);
1382 			p_from->pointtype = pt_corner;
1383 			SPFigurePlusCP(p_to,j/(approx+1),spline,false);
1384 			SPFigurePlusCP(p_from,(i+1)/(approx+1),spline,true);
1385 			NormalizeT(pmids+j,i-j,pmids[i].t,pmids[j-1].t);
1386 			ApproximateSplineFromPointsSlopes(p_from,p_to,pmids+j,i-j,false);
1387 			p_to = p_from;
1388 		    }
1389 
1390 		    j=i+1;
1391 		}
1392 	    }
1393 
1394 	    if ( j!=1 ) {
1395 		NormalizeT(pmids+j,i-j,0.0,pmids[j-1].t);
1396 		NormalizeT(mmids+j,i-j,mmids[j-1].t,1.0);
1397 		SPFigureCP(m_from,(j)/(approx+1),spline,true);
1398 		SPFigurePlusCP(p_to,(j)/(approx+1),spline,false);
1399 	    }
1400 	    ApproximateSplineFromPointsSlopes(cur->plusfrom,p_to,pmids+j,i-j,false);
1401 	    ApproximateSplineFromPointsSlopes(m_from,cur->minusto,mmids+j,i-j,false);
1402 	}
1403 	if ( spline->to->next==NULL ) {
1404 	    /* Done */
1405     break;
1406 	}
1407 	if ( first==NULL ) first = spline;
1408     }
1409     if ( spline==first ) {
1410 	head->prev = last;
1411 	last->next = head;
1412     }
1413     free(mmids); free(pmids); free(knots); free(mids);
1414 return( head );
1415 }
1416 
SPLCheckValidity(SplineSet * ss)1417 static void SPLCheckValidity(SplineSet *ss) {
1418     SplinePoint *sp, *nsp;
1419 
1420     for ( sp=ss->first; ; sp = nsp ) {
1421 	if ( sp->next==NULL )
1422     break;
1423 	nsp = sp->next->to;
1424 	if ( nsp->prev != sp->next || sp->next->from!=sp )
1425 	    IError("Bad SPL");
1426 	if ( nsp==ss->first )
1427     break;
1428     }
1429 
1430     for ( sp=ss->last; ; sp = nsp ) {
1431 	if ( sp->prev==NULL )
1432     break;
1433 	nsp = sp->prev->from;
1434 	if ( nsp->next != sp->prev || sp->prev->to!=sp )
1435 	    IError("Bad SPL");
1436 	if ( nsp==ss->last )
1437     break;
1438     }
1439 }
1440 
_SplineSetStroke(SplineSet * spl,StrokeInfo * si,SplineChar * sc)1441 static SplineSet *_SplineSetStroke(SplineSet *spl,StrokeInfo *si,SplineChar *sc) {
1442     SplineSet *ssplus, *ssminus;
1443     int reversed = false;
1444     struct strokedspline *head, *cur, *first, *lastp, *lastm;
1445     Spline *s1, *s2;
1446 
1447     si->gottoobiglocal = false;
1448 
1449     if ( spl->first->next==NULL || spl->first->next->to==spl->first ) {
1450 	/* Only one point in the SplineSet. */
1451 	ssplus = chunkalloc(sizeof(SplineSet));
1452 	SinglePointStroke(spl->first,si,&ssplus->first,&ssplus->last);
1453 return( ssplus );
1454     }
1455 
1456     SplineSetAddExtrema(NULL,spl,ae_all,1000/* Not used*/);
1457 
1458     if ( spl->first==spl->last && spl->first->next!=NULL ) {
1459 	/* My routine gets screwed up by counter-clockwise triangles */
1460 	if ( !SplinePointListIsClockwise(spl)) {
1461 	    reversed = true;
1462 	    SplineSetReverse(spl);
1463 	}
1464     }
1465 
1466     head = cur = _SplineSetApprox(spl,si,sc);
1467 
1468     first = NULL;
1469     for ( cur=head; cur!=NULL && cur!=first; cur=cur->next ) {
1470 	if ( first==NULL ) first = cur;
1471 	if ( cur->s->to->next!=NULL )
1472 	    StrokeJoint(cur->s->to,si,cur,cur->next,sc);
1473 	FreeOrigStuff(cur);
1474     }
1475     FreeOrigStuff(head);	/* normally gets freed when we look at the next item on list. But we did that for head first */
1476 
1477     /* Finish off intersections, before doing joins */
1478     if ( spl->first->prev==NULL ) {
1479 	StrokeEndComplete(head,si,true);
1480 	for ( cur=head; cur->next!=NULL; cur=cur->next );
1481 	StrokeEndComplete(cur,si,false);
1482     }
1483 
1484     lastp = lastm = head;
1485     if ( lastp->plusskip ) lastp = NULL;
1486     if ( lastm->minusskip ) lastm = NULL;
1487 
1488     first = NULL;
1489     for ( cur=head; cur!=NULL && cur!=first; cur=cur->next ) {
1490 	real factor = si->factor==NULL ? 1.0 : (si->factor)(si->data,cur->s,1.0);
1491 	if ( first==NULL ) first = cur;
1492 
1493 	if ( cur->s->to->next!=NULL ) {
1494 	    if ( !cur->plusskip ) lastp = cur;
1495 	    if ( lastp!=NULL && !cur->next->plusskip ) {
1496 		if ( cur->pinnerto==-1 )
1497 		    MSP(cur->next->plusto,&lastp->plusfrom,&lastp->plusto);
1498 		else if ( cur->pinnerto )
1499 		    MSP(cur->next->plusto,&lastp->plusfrom,&lastp->plusto);
1500 		else if ( cur==lastp )
1501 		    MakeJoints(cur->next->plusto,cur->plusfrom,si,&cur->pinterto,
1502 			    &cur->s->to->me,-1,cur->pangle,cur->nangle,factor);
1503 		else
1504 		    IError("Lastp not cur" );
1505 	    }
1506 	    if ( !cur->minusskip ) lastm = cur;
1507 	    if ( lastm!=NULL && !cur->next->minusskip ) {
1508 		if ( cur->minnerto==-1 )
1509 		    MSP(lastm->minusto,&cur->next->minusfrom,&cur->next->minusto);
1510 		else if ( cur->minnerto )
1511 		    MSP(lastm->minusto,&cur->next->minusfrom,&cur->next->minusto);
1512 		else if ( cur==lastm )
1513 		    MakeJoints(lastm->minusto,cur->next->minusfrom,si,&cur->minterto,
1514 			    &cur->s->to->me,1,PI+cur->nangle,PI+cur->pangle,factor);
1515 		else
1516 		    IError("Lastm not cur");
1517 	    }
1518 	}
1519     }
1520 
1521     for ( cur=head; cur!=NULL && cur->plusskip; ) { cur=cur->next; if ( cur==head ) cur=NULL; }
1522     if ( cur!=NULL ) {
1523 	ssplus = chunkalloc(sizeof(SplineSet));
1524 	ssplus->first = ssplus->last = cur->plusfrom;
1525 	SplineSetFixCPs(ssplus);
1526 	SPLCheckValidity(ssplus);
1527     } else
1528 	/* It is possible to have a contour completely swallowed by the pen */
1529 	ssplus = NULL;
1530     for ( cur=head; cur!=NULL && cur->minusskip; ) { cur=cur->next; if ( cur==head ) cur=NULL; }
1531     if ( spl->first==spl->last && cur!=NULL ) {
1532 	ssminus = chunkalloc(sizeof(SplineSet));
1533 	ssminus->first = ssminus->last = cur->minusfrom;
1534 	SPLCheckValidity(ssminus);
1535 	/*SplineSetFixRidiculous(ssplus); SplineSetFixRidiculous(ssminus);*/
1536 	SplineSetFixCPs(ssminus);
1537 	if ( reversed ) {
1538 	    SplineSet *temp = ssplus;
1539 	    ssplus = ssminus;
1540 	    ssminus = temp;
1541 	}
1542 	SplineSetReverse(ssminus);
1543 	if ( ssplus != NULL )
1544 	    SplineSetReverse(ssplus);
1545 	if ( si->removeinternal && ssplus!=NULL ) {
1546 	    SplinePointListFree(ssminus);
1547 	} else if ( si->removeexternal ) {
1548 	    SplinePointListFree(ssplus);
1549 	    SplineSetReverse(ssminus);
1550 	    ssplus = ssminus;
1551 	} else {
1552 	    if ( ssplus != NULL )
1553 		ssplus->next = ssminus;
1554 	    else
1555 		ssplus = ssminus;
1556 	    /* I used to do a splineset correct dir here on both, but */
1557 	    /*  that doesn't work always if a contour self intersects */
1558 	    /* I think it should always be correct */
1559 	}
1560 	/* I can't always detect an overlap, so let's always do the remove */
1561 		/* Sigh, no. That is still too dangerous */
1562 	if ( si->removeoverlapifneeded && ssplus!=NULL && SplineSetIntersect(ssplus,&s1,&s2))
1563 	    ssplus = SplineSetRemoveOverlap(sc,ssplus,over_remove);
1564 	if ( reversed )		/* restore original, just in case we want it */
1565 	    SplineSetReverse(spl);
1566     } else if ( si->stroke_type==si_std || si->stroke_type==si_elipse )
1567 	SplineSetReverse(ssplus);
1568     StrokedSplineFree(head);
1569 return( ssplus );
1570 }
1571 
SSRemoveUTurns(SplineSet * base,StrokeInfo * si)1572 static SplineSet *SSRemoveUTurns(SplineSet *base, StrokeInfo *si) {
1573     /* All too often in MetaPost output splines have tiny cps which */
1574     /*  make the slope at the end-points irrelevant when looking at */
1575     /*  the curve.  Since we assume the slope at the end-points is */
1576     /*  similar to the slope at t=.01 this confuses us greatly and */
1577     /*  produces nasty results. In this case try to approximate a new */
1578     /*  spline with very different cps. Note: We break continuity! */
1579     /* A special case of this is the following: */
1580     /* My stroking algorithem gets confused by sharp turns. For example */
1581     /*  if we have a spline which is all in a line, but the control points */
1582     /*  are such that it doubles back on itself ( "* +   * +", ie. cps */
1583     /*  outside of the points) then things get very unhappy */
1584     SplineSet *spl= base;
1585     Spline *first, *s, *next, *snew;
1586     double dx,dy, offx,offy, diff, n,l, slen, len, bound;
1587     int linear, bad, i, cnt;
1588     SplinePoint fakefrom, faketo;
1589     TPoint *tps;
1590 
1591     bound = si->radius*si->radius;
1592     first = NULL;
1593   if ( spl->first->next!=NULL && !spl->first->next->order2 )
1594     for ( s = spl->first->next; s!=NULL && s!=first; s=s->to->next ) {
1595 	if ( first==NULL ) first = s;
1596 
1597 	bad = false;
1598 	dx = s->to->me.x-s->from->me.x;
1599 	dy = s->to->me.y-s->from->me.y;
1600 	slen = dx*dx + dy*dy;
1601 
1602 	offx = s->from->nextcp.x-s->from->me.x;
1603 	offy = s->from->nextcp.y-s->from->me.y;
1604 	l= offx*dx + offy*dy;
1605 	if ( l<0 ) {
1606 	    l = -l;
1607 	    if ( (n= offx*dy - offy*dx)<0 ) n = -n;
1608 	    len = offx*offx + offy*offy;
1609 	    if ( (n/l>2*len/si->radius || (n>l/3 && s->from->prev==NULL )) && len<bound && len< slen/4 )
1610 		bad = 1;
1611 	}
1612 
1613 	offx = s->to->me.x-s->to->prevcp.x;
1614 	offy = s->to->me.y-s->to->prevcp.y;
1615 	l= offx*dx + offy*dy;
1616 	if ( l<0 ) {
1617 	    l = -l;
1618 	    if ( (n= offx*dy - offy*dx)<0 ) n = -n;
1619 	    len = offx*offx + offy*offy;
1620 	    if ( (n/l>2*len/si->radius || (n>l/3 && s->to->next==NULL)) && len<bound && len< slen/4 )
1621 		bad |= 2;
1622 	}
1623 
1624 	if ( bad ) {
1625 	    fakefrom = *s->from; fakefrom.next = fakefrom.prev = NULL;
1626 	    faketo   = *s->to;   faketo.next   = faketo.prev   = NULL;
1627 
1628 	    slen = sqrt(slen);
1629 	    dx /= slen; dy/=slen;
1630 
1631 	    if ( bad&1 ) {		/* from->nextcp is nasty */
1632 		offx = s->from->nextcp.x-s->from->me.x;
1633 		offy = s->from->nextcp.y-s->from->me.y;
1634 		len = sqrt(offx*offx + offy*offy);
1635 		offx /= len; offy/=len;
1636 
1637 		n = offx*dy - offy*dx;
1638 		fakefrom.nextcp.x = fakefrom.me.x + slen*dx + 3*len*dy;
1639 		fakefrom.nextcp.y = fakefrom.me.y + slen*dy - 3*len*dx;
1640 	    }
1641 
1642 	    if ( bad&2 ) {		/* from->nextcp is nasty */
1643 		offx = s->to->prevcp.x-s->to->me.x;
1644 		offy = s->to->prevcp.y-s->to->me.y;
1645 		len = sqrt(offx*offx + offy*offy);
1646 		offx /= len; offy/=len;
1647 
1648 		n = offx*dy - offy*dx;
1649 		faketo.prevcp.x = faketo.me.x - slen*dx + 3*len*dy;
1650 		faketo.prevcp.y = faketo.me.y - slen*dy - 3*len*dx;
1651 	    }
1652 
1653 	    if (( cnt = slen/2)<10 ) cnt = 10;
1654 	    tps = galloc(cnt*sizeof(TPoint));
1655 	    for ( i=0; i<cnt; ++i ) {
1656 		double t = ((double) (i+1))/(cnt+1);
1657 		tps[i].t = t;
1658 		tps[i].x = ((s->splines[0].a*t + s->splines[0].b)*t + s->splines[0].c)*t + s->splines[0].d;
1659 		tps[i].y = ((s->splines[1].a*t + s->splines[1].b)*t + s->splines[1].c)*t + s->splines[1].d;
1660 	    }
1661 	    snew = ApproximateSplineFromPointsSlopes(&fakefrom,&faketo,tps,cnt,false);
1662 	    snew->from = s->from;
1663 	    snew->to = s->to;
1664 	    snew->from->next = snew;
1665 	    snew->to->prev = snew;
1666 	    snew->from->nextcp = fakefrom.nextcp;
1667 	    snew->from->nonextcp = fakefrom.nonextcp;
1668 	    if ( bad&1 ) snew->from->pointtype = pt_corner;
1669 	    snew->to->prevcp = faketo.prevcp;
1670 	    snew->to->noprevcp = faketo.noprevcp;
1671 	    if ( bad&2 ) snew->to->pointtype = pt_corner;
1672 	    if ( first==s ) first=snew;
1673 	    SplineFree(s);
1674 	    free(tps);
1675 	    s = snew;
1676 	}
1677     }
1678 
1679     first = NULL;
1680     for ( s = spl->first->next; s!=NULL && s!=first; s=s->to->next ) {
1681 	if ( first==NULL ) first = s;
1682 	dx = s->to->me.x-s->from->me.x;
1683 	dy = s->to->me.y-s->from->me.y;
1684 	offx = s->from->nextcp.x-s->from->me.x;
1685 	offy = s->from->nextcp.y-s->from->me.y;
1686 	if ( offx*dx + offy*dy<0 ) {
1687 	    diff = offx*dy-offy*dx;
1688 	    linear = ( diff<1 && diff>-1 );
1689 	    if ( offx<0 ) offx = -offx;
1690 	    if ( offy<0 ) offy = -offy;
1691 	    if ( offx+offy<1 || linear ) {
1692 		s->from->nextcp = s->from->me;
1693 		s->from->nonextcp = true;
1694 		if ( s->from->pointtype == pt_curve || s->from->pointtype == pt_hvcurve )
1695 		    s->from->pointtype = pt_corner;
1696 		if ( s->order2 ) {
1697 		    s->to->prevcp = s->to->me;
1698 		    s->to->noprevcp = true;
1699 		    if ( s->to->pointtype==pt_curve || s->to->pointtype == pt_hvcurve )
1700 			s->to->pointtype = pt_corner;
1701 		}
1702 		SplineRefigure(s);
1703 	    }
1704 	}
1705 	offx = s->to->me.x-s->to->prevcp.x;
1706 	offy = s->to->me.y-s->to->prevcp.y;
1707 	if ( offx*dx + offy*dy<0 ) {
1708 	    diff = offx*dy-offy*dx;
1709 	    linear = ( diff<1 && diff>-1 );
1710 	    if ( offx<0 ) offx = -offx;
1711 	    if ( offy<0 ) offy = -offy;
1712 	    if ( offx+offy<1 || linear ) {
1713 		s->to->prevcp = s->to->me;
1714 		s->to->noprevcp = true;
1715 		if ( s->to->pointtype==pt_curve || s->to->pointtype == pt_hvcurve )
1716 		    s->to->pointtype = pt_corner;
1717 		if ( s->order2 ) {
1718 		    s->from->nextcp = s->from->me;
1719 		    s->from->nonextcp = true;
1720 		    if ( s->from->pointtype == pt_curve || s->from->pointtype == pt_hvcurve )
1721 			s->from->pointtype = pt_corner;
1722 		}
1723 		SplineRefigure(s);
1724 	    }
1725 	}
1726     }
1727 
1728     /* Zero length splines are bad too */
1729     /* As are splines of length .000003 */
1730     first = NULL;
1731     for ( s = spl->first->next; s!=NULL && s!=first; s=next ) {
1732 	if ( first==NULL ) first = s;
1733 	next = s->to->next;
1734 	if ( s->from->nonextcp && s->to->noprevcp && s!=next &&
1735 		s->from->me.x >= s->to->me.x-.1 && s->from->me.x <= s->to->me.x+.1 &&
1736 		s->from->me.y >= s->to->me.y-.1 && s->from->me.y <= s->to->me.y+.1 ) {
1737 	    s->from->next = next;
1738 	    if ( next!=NULL ) {
1739 		s->from->nextcp = next->from->nextcp;
1740 		s->from->nonextcp = next->from->nonextcp;
1741 		s->from->nextcpdef = next->from->nextcpdef;
1742 		next->from = s->from;
1743 	    }
1744 	    SplinePointCatagorize(s->from);
1745 	    if ( spl->last == s->to ) {
1746 		if ( next==NULL )
1747 		    spl->last = s->from;
1748 		else
1749 		    spl->first = spl->last = s->from;
1750 	    }
1751 	    if ( spl->first==s->to ) spl->first = s->from;
1752 	    if ( spl->last==s->to ) spl->last = s->from;
1753 	    SplinePointFree(s->to);
1754 	    SplineFree(s);
1755 	    if ( first==s ) first = NULL;
1756 	}
1757     }
1758 
1759 return( base );
1760 }
1761 
SSRemoveColinearPoints(SplineSet * ss)1762 static void SSRemoveColinearPoints(SplineSet *ss) {
1763     SplinePoint *sp, *nsp, *nnsp;
1764     BasePoint dir, ndir;
1765     double len;
1766     int removed;
1767 
1768     sp = ss->first;
1769     if ( sp->prev==NULL )
1770 return;
1771     nsp = sp->next->to;
1772     if ( nsp==sp )
1773 return;
1774     dir.x = nsp->me.x - sp->me.x; dir.y = nsp->me.y - sp->me.y;
1775     len = dir.x*dir.x + dir.y*dir.y;
1776     if ( len!=0 ) {
1777 	len = sqrt(len);
1778 	dir.x /= len; dir.y /= len;
1779     }
1780     nnsp = nsp->next->to;
1781     if ( nnsp==sp )
1782 return;
1783     memset(&ndir,0,sizeof(ndir));
1784     forever {
1785 	removed = false;
1786 	if ( nsp->next->islinear ) {
1787 	    ndir.x = nnsp->me.x - nsp->me.x; ndir.y = nnsp->me.y - nsp->me.y;
1788 	    len = ndir.x*ndir.x + ndir.y*ndir.y;
1789 	    if ( len!=0 ) {
1790 		len = sqrt(len);
1791 		ndir.x /= len; ndir.y /= len;
1792 	    }
1793 	}
1794 	if ( sp->next->islinear && nsp->next->islinear ) {
1795 	    double dot =dir.x*ndir.y - dir.y*ndir.x;
1796 	    if ( dot<.001 && dot>-.001 ) {
1797 		sp->next->to = nnsp;
1798 		nnsp->prev = sp->next;
1799 		SplineRefigure(sp->next);
1800 		SplineFree(nsp->next);
1801 		SplinePointFree(nsp);
1802 		if ( ss->first==nsp ) ss->first = sp;
1803 		if ( ss->last ==nsp ) ss->last  = sp;
1804 		removed = true;
1805 	    } else
1806 		sp = nsp;
1807 	} else
1808 	    sp = nsp;
1809 	dir = ndir;
1810 	nsp = nnsp;
1811 	nnsp = nsp->next->to;
1812 	if ( !removed && sp==ss->first )
1813     break;
1814     }
1815 }
1816 
SSesRemoveColinearPoints(SplineSet * ss)1817 static void SSesRemoveColinearPoints(SplineSet *ss) {
1818     while ( ss!=NULL ) {
1819 	SSRemoveColinearPoints(ss);
1820 	ss = ss->next;
1821     }
1822 }
1823 
SplineSetStroke(SplineSet * spl,StrokeInfo * si,SplineChar * sc)1824 SplineSet *SplineSetStroke(SplineSet *spl,StrokeInfo *si,SplineChar *sc) {
1825     SplineSet *ret, *temp, *temp2;
1826     SplineSet *order3 = NULL;
1827 
1828     if ( spl->first->next!=NULL && spl->first->next->order2 )
1829 	order3 = spl = SSPSApprox(spl);
1830     if ( si->radius==0 )
1831 	si->radius=1;
1832     temp2 = SSRemoveUTurns(SplinePointListCopy(spl),si);
1833     if ( si->stroke_type == si_elipse ) {
1834 	real trans[6], factor;
1835 	StrokeInfo si2;
1836 	trans[0] = trans[3] = si->c;
1837 	trans[1] = -si->s;
1838 	trans[2] = si->s;
1839 	trans[4] = trans[5] = 0;
1840 	factor = si->radius/si->minorradius;
1841 	trans[0] *= factor; trans[2] *= factor;
1842 	temp = SplinePointListCopy(temp2);
1843 #if 0
1844 	BisectTurners(temp);
1845 #endif
1846 	temp = SplinePointListTransform(temp,trans,true);
1847 	si2 = *si;
1848 	si2.stroke_type = si_std;
1849 	ret = SplineSetStroke(temp,&si2,sc);
1850 	SplinePointListFree(temp);
1851 	trans[0] = trans[3] = si->c;
1852 	trans[1] = si->s;
1853 	trans[2] = -si->s;
1854 	trans[4] = trans[5] = 0;
1855 	factor = si->minorradius/si->radius;
1856 	trans[0] *= factor; trans[1] *= factor;
1857 	ret = SplinePointListTransform(ret,trans,true);
1858     } else
1859 	ret = _SplineSetStroke(temp2,si,sc);
1860     SplinePointListFree(temp2);
1861     if ( order3!=NULL ) {
1862 	temp = SplineSetsTTFApprox(ret);
1863 	SplinePointListsFree(ret);
1864 	SplinePointListFree(order3);
1865 	ret = temp;
1866     }
1867     /* We tend to get (small) rounding errors */
1868     SplineSetsRound2Int(ret,1024.,false,false);
1869     /* If we use butt line caps or miter joins then we will likely have */
1870     /*  some spurious colinear points. If we do, remove them */
1871     SSesRemoveColinearPoints(ret);
1872 return( ret );
1873 }
1874 
1875