beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / luafontloader / fontforge / fontforge / splinestroke.c
blob6d9e7fc86c32ba13df5ae2b7d06d48e8cf57b02a
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:
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
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.
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.
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.
27 #include "pfaedit.h"
28 #include "splinefont.h"
29 #include <math.h>
31 #define PI 3.1415926535897932
33 typedef struct joininfo {
34 SplinePoint *from, *to;
35 real tprev;
36 real tnext;
37 BasePoint inter;
38 } JointPoint;
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;
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;
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;
55 return( atan2(yslope,xslope) );
58 static int PenCorner(double lineangle,StrokeInfo *si) {
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 );
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 */
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;
86 if ( si->factor!=NULL )
87 factor = (si->factor)(si->data,spline,t);
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;
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];
108 return( lineangle );
111 static SplinePoint *makequartercircle(real x, real y, real radius,
112 real xmul, real ymul,SplinePoint *prev) {
113 SplinePoint *here = SplinePointCreate(x,y);
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;
124 here->nonextcp = here->noprevcp = false;
125 if ( prev!=NULL )
126 SplineMake3(prev,here);
127 return( here );
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 );
138 static void SinglePointStroke(SplinePoint *base, StrokeInfo *si, SplinePoint **_plus, SplinePoint **_minus) {
139 SplinePoint *plus, *cur;
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
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;
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;
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;
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);
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);
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;
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);
249 SplinePointCatagorize(to);
250 SplinePointCatagorize(from);
251 *_to = to;
252 return( from );
255 /* Is this the inner intersection or the outer one (the inner one is on both splines) */
256 /* the outer one is beyond both */
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;
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;
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;
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... */
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 */
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 );
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;
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;
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);
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 );
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;
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 );
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 );
345 return( false );
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;
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??? */
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;
379 SplineMake3(mid,to);
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);
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);
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;
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;
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);
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;
441 *_ppt = pptval; *_pmt = pmtval;
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;
455 *_mmt = mmtval; *_mpt = mptval;
458 return( pptval!=-1 || mmtval!=-1 || pmtval!=-1 || mptval==-1 );
461 #define BasePtDistance(pt1, pt2) sqrt(((pt1)->x-(pt2)->x)*((pt1)->x-(pt2)->x) + ((pt1)->y-(pt2)->y)*((pt1)->y-(pt2)->y))
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;
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 );
489 static void MSP(SplinePoint *sp1,SplinePoint **sp2, SplinePoint **sp2alt) {
490 int same2 = *sp2==*sp2alt;
492 *sp2 = MergeSplinePoint(sp1,*sp2);
493 if ( same2 )
494 *sp2alt = *sp2;
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;
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 );
529 return( SplineBisect(s,t));
532 static void SplineFreeBetween(SplinePoint *from,SplinePoint *to,int freefrom,int freeto) {
533 Spline *s;
535 if ( from==to ) {
536 if ( freefrom && freeto )
537 SplinePointFree(from);
538 return;
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);
553 if ( freeto )
554 SplinePointFree(to);
555 else
556 to->prev = NULL;
559 static void SplineFreeForeward(SplinePoint *from) {
560 Spline *s;
562 while ( from!=NULL ) {
563 s = from->next;
564 SplinePointFree(from);
565 if ( s==NULL )
566 return;
567 from = s->to;
568 SplineFree(s);
572 static void SplineFreeBackward(SplinePoint *to) {
573 Spline *s;
575 while ( to!=NULL ) {
576 s = to->prev;
577 SplinePointFree(to);
578 if ( s==NULL )
579 return;
580 to = s->from;
581 SplineFree(s);
585 static SplinePoint *SplineCopyAfter(SplinePoint *from,SplinePoint **end) {
586 SplinePoint *head, *last;
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;
603 *end = last;
604 return( head );
607 static SplinePoint *SplineCopyBefore(SplinePoint *to,SplinePoint **end) {
608 SplinePoint *head, *last;
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;
625 *end = last;
626 return( head );
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];
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]));
643 *ret = NULL;
644 return( NULL );
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;
658 static void StrokeEndComplete(struct strokedspline *cur,StrokeInfo *si,int isstart) {
659 SplinePoint *edgestart, *edgeend, *curat, *edgeat;
660 struct strokedspline *lastp, *lastm;
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);
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);
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);
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);
717 static void StrokedSplineFree(struct strokedspline *head) {
718 struct strokedspline *next, *cur=head;
720 while ( cur!=NULL ) {
721 next = cur->next;
722 chunkfree(cur,sizeof(*cur));
723 cur = next;
724 if ( cur==head )
725 break;
729 static void FreeOrigStuff(struct strokedspline *before) {
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;
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;
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);
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;
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;
815 ret = false;
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;
866 ret = false;
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 );
878 return( ret );
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 */
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
894 before->pangle = pangle = SplineExpand(base->prev,1,0,si,&pplus,&pminus);
895 before->nangle = nangle = SplineExpand(base->next,0,0,si,&nplus,&nminus);
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 */
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);
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);
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;
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
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;
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;
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 );
1027 return( cnt );
1031 static void SplineSetFixCPs(SplineSet *ss) {
1032 SplinePoint *sp;
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;
1042 SPLCatagorizePoints(ss);
1045 static SplinePoint *SPNew(SplinePoint *base,BasePoint *pos,BasePoint *cp,int isnext) {
1046 SplinePoint *sp = SplinePointCreate(pos->x,pos->y);
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);
1062 return( sp );
1065 static void NormalizeT(TPoint *mids,int cnt,double tbase,double tend) {
1066 int i;
1068 for ( i=0; i<cnt; ++i )
1069 mids[i].t = (mids[i].t - tbase)/(tend - tbase);
1072 static void SPFigureCP(SplinePoint *sp,double t,Spline *spline,int isnext) {
1073 Spline temp;
1074 double tn;
1075 Spline1D *s1;
1076 BasePoint off;
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 );
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;
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;
1127 sp->noprevcp = false;
1131 static void SPFigurePlusCP(SplinePoint *sp,double t,Spline *spline,int isnext) {
1132 SplinePoint dummy;
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;
1146 static int Overlaps(TPoint *expanded,TPoint *inner,double rsq) {
1147 double len;
1148 BasePoint dir;
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 );
1160 #define Approx 10
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;
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;
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);
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;
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);
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;
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;
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);
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;
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;
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);
1316 if ( i-j>0 ) {
1317 Overlaps(&mmids[i],&mids[i-j],rsq);
1318 Overlaps(&pmids[i],&mids[i-j],rsq);
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 );
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;
1353 pwascovered = pt1!=-1 || pt2!=-1;
1354 mwascovered = mt1!=-1 || mt2!=-1;
1356 si->radius = radius;
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;
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;
1390 j=i+1;
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);
1400 ApproximateSplineFromPointsSlopes(cur->plusfrom,p_to,pmids+j,i-j,false);
1401 ApproximateSplineFromPointsSlopes(m_from,cur->minusto,mmids+j,i-j,false);
1403 if ( spline->to->next==NULL ) {
1404 /* Done */
1405 break;
1407 if ( first==NULL ) first = spline;
1409 if ( spline==first ) {
1410 head->prev = last;
1411 last->next = head;
1413 free(mmids); free(pmids); free(knots); free(mids);
1414 return( head );
1417 static void SPLCheckValidity(SplineSet *ss) {
1418 SplinePoint *sp, *nsp;
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;
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;
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;
1447 si->gottoobiglocal = false;
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 );
1456 SplineSetAddExtrema(NULL,spl,ae_all,1000/* Not used*/);
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);
1466 head = cur = _SplineSetApprox(spl,si,sc);
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);
1475 FreeOrigStuff(head); /* normally gets freed when we look at the next item on list. But we did that for head first */
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);
1484 lastp = lastm = head;
1485 if ( lastp->plusskip ) lastp = NULL;
1486 if ( lastm->minusskip ) lastm = NULL;
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;
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" );
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");
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;
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 */
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 );
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;
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;
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;
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;
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;
1624 if ( bad ) {
1625 fakefrom = *s->from; fakefrom.next = fakefrom.prev = NULL;
1626 faketo = *s->to; faketo.next = faketo.prev = NULL;
1628 slen = sqrt(slen);
1629 dx /= slen; dy/=slen;
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;
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;
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;
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;
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;
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;
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;
1702 SplineRefigure(s);
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;
1723 SplineRefigure(s);
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;
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;
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;
1759 return( base );
1762 static void SSRemoveColinearPoints(SplineSet *ss) {
1763 SplinePoint *sp, *nsp, *nnsp;
1764 BasePoint dir, ndir;
1765 double len;
1766 int removed;
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;
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;
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;
1817 static void SSesRemoveColinearPoints(SplineSet *ss) {
1818 while ( ss!=NULL ) {
1819 SSRemoveColinearPoints(ss);
1820 ss = ss->next;
1824 SplineSet *SplineSetStroke(SplineSet *spl,StrokeInfo *si,SplineChar *sc) {
1825 SplineSet *ret, *temp, *temp2;
1826 SplineSet *order3 = NULL;
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;
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 );