1 /* Copyright (C) 2000-2008 by George Williams */
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.
28 #include "splinefont.h"
31 #define PI 3.1415926535897932
33 typedef struct joininfo
{
34 SplinePoint
*from
, *to
;
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 ) ) {
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
) ) {
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 ) ) {
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];
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);
101 int corner
= PenCorner(lineangle
,si
);
102 plus
->x
= base
.x
+ factor
*si
->xoff
[corner
];
103 plus
->y
= base
.y
+ factor
*si
->yoff
[corner
];
105 minus
->x
= base
.x
+ factor
*si
->xoff
[corner
];
106 minus
->y
= base
.y
+ factor
*si
->yoff
[corner
];
111 static SplinePoint
*makequartercircle(real x
, real y
, real radius
,
112 real xmul
, real ymul
,SplinePoint
*prev
) {
113 SplinePoint
*here
= SplinePointCreate(x
,y
);
116 here
->nextcp
.x
= here
->prevcp
.x
= x
;
117 here
->nextcp
.y
= y
+ .552*ymul
*radius
;
118 here
->prevcp
.y
= y
- .552*ymul
*radius
;
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;
126 SplineMake3(prev
,here
);
130 static SplinePoint
*makeline(SplinePoint
*prev
, real x
, real y
) {
131 SplinePoint
*here
= SplinePointCreate(x
,y
);
132 here
->pointtype
= pt_corner
;
134 SplineMake3(prev
,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
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
;
166 *_plus
= *_minus
= cur
= chunkalloc(sizeof(SplinePoint
));
168 cur
->next
= cur
->prev
= NULL
;
169 cur
->hintmask
= NULL
;
173 static SplinePoint
*StrokeEnd(SplinePoint
*base
, StrokeInfo
*si
, int isstart
,
176 SplinePoint
*mid1
, *mid2
, *cur
, *from
, *to
;
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) :
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
;
192 angle
= SplineExpand(base
->next
,0,0,si
,&from
->me
,&to
->me
)+ PI
;
194 angle
= SplineExpand(base
->prev
,1,0,si
,&to
->me
,&from
->me
);
196 if ( (len
= to
->me
.x
-from
->me
.x
)<0 )
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
) {
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]);
207 SplineIsLinearMake(base
->next
);
208 angle
= SplineExpand(base
->next
,0,0,si
,&junk
,&junk
)+ PI
;
211 SplineIsLinearMake(base
->prev
);
212 angle
= SplineExpand(base
->prev
,1,0,si
,&junk
,&junk
);
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
);
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
) {
262 denom
= (sx1
*sy2
-sx2
*sy1
);
263 if ( denom
>-.0001 && denom
<.0001 ) {
264 /* Lines are parallel. Might be coincident, might not */
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
;
273 inter
->x
= (p1
->x
+p2
->x
)/2;
274 inter
->y
= (p1
->y
+p2
->y
)/2;
276 inter
->x
= (p1
->x
+p2
->x
)/2 + sx1
/denom
;
277 inter
->y
= (p1
->y
+p2
->y
)/2 + sy1
/denom
;
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
,
307 if ( angle
<0 ) angle
= -angle
;
308 while ( angle
>2*PI
) angle
-= 2*PI
;
309 if ( angle
>PI
) angle
= 2*PI
-angle
;
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;
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
) )
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
) )
348 static void MakeJoints(SplinePoint
*from
,SplinePoint
*to
,StrokeInfo
*si
,
349 BasePoint
*inter
, BasePoint
*center
,
350 int incr
,double pangle
, double nangle
, real factor
) {
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
);
365 if ((cstart
+= 2)>=4 ) cstart
-= 4;
366 if ((cend
+= 2)>=4 ) cend
-= 4;
367 incr
= 1; /* Why??? */
369 if ( incr
>0 && cstart
>cend
)
371 else if ( incr
<0 && cstart
<cend
)
373 i
= cstart
+ incr
; /* First one is from */
376 mid
= makeline(mid
,center
->x
+factor
*si
->xoff
[i
],center
->y
+factor
*si
->yoff
[i
]);
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
);
386 if ( from
->ptindex
== to
->ptindex
)
387 mid
->ptindex
= from
->ptindex
;
388 } else if ( si
->join
==lj_bevel
) {
389 SplineMake3(from
,to
);
391 double cplen
= CircleCpDist(nangle
-pangle
);
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;
407 SplineMake3(from
,to
);
409 SplineMake3(from
,mid
);
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
);
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
)
436 if ( (plus
->x
-test
.x
)*(plus
->x
-test
.x
)+(plus
->y
-test
.y
)*(plus
->y
-test
.y
)<= rsq
)
438 if ( (minus
->x
-test
.x
)*(minus
->x
-test
.x
)+(minus
->y
-test
.y
)*(minus
->y
-test
.y
)<= rsq
)
441 *_ppt
= pptval
; *_pmt
= pmtval
;
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
)
450 if ( (plus
->x
-test
.x
)*(plus
->x
-test
.x
)+(plus
->y
-test
.y
)*(plus
->y
-test
.y
)< rsq
)
452 if ( (minus
->x
-test
.x
)*(minus
->x
-test
.x
)+(minus
->y
-test
.y
)*(minus
->y
-test
.y
)< rsq
)
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 */
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
);
489 static void MSP(SplinePoint
*sp1
,SplinePoint
**sp2
, SplinePoint
**sp2alt
) {
490 int same2
= *sp2
==*sp2alt
;
492 *sp2
= MergeSplinePoint(sp1
,*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
;
502 temp
= chunkalloc(sizeof(SplinePoint
));
505 temp
->hintmask
= NULL
;
506 temp
->next
->from
= temp
;
510 temp
->prevcp
= temp
->me
;
511 temp
->noprevcp
= true;
512 SplineMake3(sp
,temp
);
514 } else if ( t
>.9999 ) {
515 temp
= chunkalloc(sizeof(SplinePoint
));
518 temp
->hintmask
= NULL
;
519 temp
->prev
->to
= temp
;
523 temp
->nextcp
= temp
->me
;
524 temp
->nonextcp
= true;
525 SplineMake3(temp
,sp
);
529 return( SplineBisect(s
,t
));
532 static void SplineFreeBetween(SplinePoint
*from
,SplinePoint
*to
,int freefrom
,int freeto
) {
536 if ( freefrom
&& freeto
)
537 SplinePointFree(from
);
541 while ( from
!=to
&& from
!=NULL
) {
544 SplinePointFree(from
);
559 static void SplineFreeForeward(SplinePoint
*from
) {
562 while ( from
!=NULL
) {
564 SplinePointFree(from
);
572 static void SplineFreeBackward(SplinePoint
*to
) {
585 static SplinePoint
*SplineCopyAfter(SplinePoint
*from
,SplinePoint
**end
) {
586 SplinePoint
*head
, *last
;
588 last
= head
= chunkalloc(sizeof(SplinePoint
));
590 head
->hintmask
= 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
;
607 static SplinePoint
*SplineCopyBefore(SplinePoint
*to
,SplinePoint
**end
) {
608 SplinePoint
*head
, *last
;
610 last
= head
= chunkalloc(sizeof(SplinePoint
));
612 head
->hintmask
= 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
;
629 static SplinePoint
*Intersect_Splines(SplinePoint
*from
,SplinePoint
*to
,
631 Spline
*test1
, *test2
;
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]));
647 struct strokedspline
{
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
;
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
);
667 MSP(edgeend
,&cur
->minusfrom
,&cur
->minusto
);
669 curat
= Intersect_Splines(lastm
->minusfrom
,edgeend
,&edgeat
);
671 SplineFreeBetween(lastm
->minusfrom
,curat
,true,false);
672 SplineFreeBetween(edgeat
,edgeend
,false,true);
674 MSP(edgeend
,&lastm
->minusfrom
,&lastm
->minusto
);
677 MergeSplinePoint(cur
->plusto
,edgestart
);
679 edgeat
= Intersect_Splines(edgestart
,lastp
->plusto
,&curat
);
681 SplineFreeBetween(curat
,lastp
->plusto
,false,true);
682 SplineFreeBetween(edgestart
,edgeat
,true,false);
684 MergeSplinePoint(lastp
->plusto
,edgestart
);
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
);
691 MSP(edgeend
,&cur
->plusfrom
,&cur
->plusto
);
693 curat
= Intersect_Splines(lastp
->plusfrom
,edgeend
,&edgeat
);
695 SplineFreeBetween(lastp
->plusfrom
,curat
,true,false);
696 lastp
->plusfrom
= curat
;
697 SplineFreeBetween(edgeat
,edgeend
,false,true);
698 lastp
->plusfrom
= MergeSplinePoint(edgeat
,curat
);
700 MSP(edgeend
,&lastp
->plusfrom
,&lastp
->plusto
);
703 MergeSplinePoint(cur
->minusto
,edgestart
);
705 edgeat
= Intersect_Splines(edgestart
,lastm
->minusto
,&curat
);
707 SplineFreeBetween(curat
,lastm
->minusto
,false,true);
708 lastm
->minusto
= curat
;
709 SplineFreeBetween(edgestart
,edgeat
,true,false);
710 MergeSplinePoint(lastm
->minusto
,edgeat
);
712 MergeSplinePoint(lastm
->minusto
,edgestart
);
717 static void StrokedSplineFree(struct strokedspline
*head
) {
718 struct strokedspline
*next
, *cur
=head
;
720 while ( cur
!=NULL
) {
722 chunkfree(cur
,sizeof(*cur
));
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) */
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
;
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
) {
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;
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;
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
);
811 SplineMake3(beforeat
,before
->plusfrom
);
812 before
->plusfrom
= beforeat
;
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
) {
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. */
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;
846 } else if ( EntirelyWithin(after
->minusfrom
,before
->s
,true,si
->radius
) ) {
847 /* the splines at after are entirely within radius units of the original */
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;
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
);
862 SplineMake3(before
->minusto
,beforeat
);
863 before
->minusto
= beforeat
;
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
);
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
,
886 BasePoint nplus
, nminus
, pplus
,pminus
;
887 double nangle
, pangle
;
891 double tt
, xdiff
, ydiff
;
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 */
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
);
917 DoIntersect_Splines(before
,after
,true,si
,sc
,true);
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
);
926 DoIntersect_Splines(before
,after
,false,si
,sc
,true);
929 before
->pinnerto
= before
->minnerto
= -1;
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
,
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
,
941 base
->next
->splines
[0].c
,
942 base
->next
->splines
[1].c
,si
->radius
);
943 if ( pinner
==-1 && minner
!=-1 )
945 before
->pinnerto
= pinner
; before
->minnerto
= (pinner
!=-1?!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;
955 if ( DoIntersect_Splines(before
,after
,false,si
,sc
,true)) {
956 before
->pinnerto
= 0;
957 before
->minnerto
= 1;
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
;
971 Spline1D
*xsp
= &s
->splines
[0], *ysp
= &s
->splines
[1];
972 BasePoint pp
, pm
, np
, nm
, testp
, testm
;
975 for ( i
=0; i
<2; ++i
) {
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
;
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 */
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
;
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
;
1005 if ( t1
>tstart
&& t1
<tend
)
1007 if ( t2
>tstart
&& t2
<tend
)
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
];
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 );
1031 static void SplineSetFixCPs(SplineSet
*ss
) {
1034 for ( sp
=ss
->first
; ; ) {
1035 SPWeightedAverageCps(sp
);
1036 if ( sp
->next
==NULL
)
1039 if ( sp
==ss
->first
)
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
;
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
);
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
);
1065 static void NormalizeT(TPoint
*mids
,int cnt
,double tbase
,double tend
) {
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
) {
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
);
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
);
1092 temp
.splines
[0].a
= s
*s
*s
*s1
->a
;
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
);
1099 temp
.splines
[1].a
= s
*s
*s
*s1
->a
;
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
;
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;
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 */
1113 temp
.splines
[0].c
*= t
; temp
.splines
[1].c
*= t
;
1115 temp
.splines
[0].b
*= tn
; temp
.splines
[1].b
*= tn
;
1118 temp
.splines
[0].a
*= tn
; temp
.splines
[1].a
*= tn
;
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
;
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
) {
1134 /* Plus splines run in the oposite direction */
1136 SPFigureCP(&dummy
,t
,spline
,!isnext
);
1138 sp
->nextcp
= dummy
.prevcp
;
1139 sp
->nonextcp
= false;
1141 sp
->prevcp
= dummy
.nextcp
;
1142 sp
->noprevcp
= false;
1146 static int Overlaps(TPoint
*expanded
,TPoint
*inner
,double rsq
) {
1150 dir
.x
= (expanded
->x
-inner
->x
); dir
.y
= (expanded
->y
-inner
->y
);
1151 len
= (dir
.x
*dir
.x
) + (dir
.y
*dir
.y
);
1154 len
= sqrt(rsq
/len
);
1155 expanded
->x
= inner
->x
+ len
*dir
.x
;
1156 expanded
->y
= inner
->y
+ len
*dir
.y
;
1162 static struct strokedspline
*_SplineSetApprox(SplineSet
*spl
,StrokeInfo
*si
,SplineChar
*sc
) {
1163 struct strokedspline
*head
=NULL
, *last
=NULL
, *cur
;
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
;
1173 SplinePoint
*p_to
, *m_to
, *p_from
, *m_from
;
1177 double mt1
, pt1
, mt2
, pt2
, rsq
;
1179 int mwascovered
, pwascovered
;
1180 enum knot_type
{ kt_knot
=1, kt_pgood
=2, kt_mgood
=4 };
1184 for ( spline
= spl
->first
->next
; spline
!=NULL
&& spline
!=first
; spline
= spline
->to
->next
) {
1185 cur
= chunkalloc(sizeof(struct strokedspline
));
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);
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
;
1225 p_to
= cur
->plusto
; m_from
= cur
->minusfrom
;
1226 } else if ( pinners
[j
-1] ) {
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
);
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
);
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
;
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
;
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;
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;
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
);
1287 if ( ((pt1
!=-1 || pt2
!=-1) && !pwascovered
&& i
!=0) ||
1288 ((mt1
!=-1 || mt2
!=-1) && !mwascovered
&& i
!=0))
1290 if ( ((pt1
==-1 && pt2
==-1) && pwascovered
&& i
!=0 ) ||
1291 ((mt1
==-1 && mt2
==-1) && mwascovered
&& i
!=0 )) {
1292 if ( knots
[i
-1]&kt_knot
)
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
)
1309 rsq
= si
->radius
*si
->radius
;
1310 for ( i
=0; i
<approx
; ++i
) {
1311 for ( j
=1; j
<approx
/2; ++j
) {
1313 Overlaps(&mmids
[i
],&mids
[i
+j
],rsq
);
1314 Overlaps(&pmids
[i
],&mids
[i
+j
],rsq
);
1317 Overlaps(&mmids
[i
],&mids
[i
-j
],rsq
);
1318 Overlaps(&pmids
[i
],&mids
[i
-j
],rsq
);
1323 for ( i
=0; i
<approx
; ++i
) if ( knots
[i
]&kt_knot
) { anyknots
=true; break; }
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. */
1336 double radius
= si
->radius
;
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
;
1351 knots
[i
-1] |= kt_knot
;
1353 pwascovered
= pt1
!=-1 || pt2
!=-1;
1354 mwascovered
= mt1
!=-1 || mt2
!=-1;
1356 si
->radius
= radius
;
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);
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);
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);
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);
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
) {
1407 if ( first
==NULL
) first
= spline
;
1409 if ( spline
==first
) {
1413 free(mmids
); free(pmids
); free(knots
); free(mids
);
1417 static void SPLCheckValidity(SplineSet
*ss
) {
1418 SplinePoint
*sp
, *nsp
;
1420 for ( sp
=ss
->first
; ; sp
= nsp
) {
1421 if ( sp
->next
==NULL
)
1424 if ( nsp
->prev
!= sp
->next
|| sp
->next
->from
!=sp
)
1426 if ( nsp
==ss
->first
)
1430 for ( sp
=ss
->last
; ; sp
= nsp
) {
1431 if ( sp
->prev
==NULL
)
1433 nsp
= sp
->prev
->from
;
1434 if ( nsp
->next
!= sp
->prev
|| sp
->prev
->to
!=sp
)
1436 if ( nsp
==ss
->last
)
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
;
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
);
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
)) {
1462 SplineSetReverse(spl
);
1466 head
= cur
= _SplineSetApprox(spl
,si
,sc
);
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
);
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
;
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
);
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
);
1516 IError("Lastm not cur");
1521 for ( cur
=head
; cur
!=NULL
&& cur
->plusskip
; ) { cur
=cur
->next
; if ( cur
==head
) cur
=NULL
; }
1523 ssplus
= chunkalloc(sizeof(SplineSet
));
1524 ssplus
->first
= ssplus
->last
= cur
->plusfrom
;
1525 SplineSetFixCPs(ssplus
);
1526 SPLCheckValidity(ssplus
);
1528 /* It is possible to have a contour completely swallowed by the pen */
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
);
1538 SplineSet
*temp
= ssplus
;
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
);
1552 if ( ssplus
!= NULL
)
1553 ssplus
->next
= 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
);
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
;
1591 bound
= si
->radius
*si
->radius
;
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
;
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
;
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 )
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
;
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 )
1625 fakefrom
= *s
->from
; fakefrom
.next
= fakefrom
.prev
= NULL
;
1626 faketo
= *s
->to
; faketo
.next
= faketo
.prev
= NULL
;
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);
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
;
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
;
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
;
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
;
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
;
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
;
1728 /* Zero length splines are bad too */
1729 /* As are splines of length .000003 */
1731 for ( s
= spl
->first
->next
; s
!=NULL
&& s
!=first
; s
=next
) {
1732 if ( first
==NULL
) first
= s
;
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
;
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
) {
1747 spl
->last
= s
->from
;
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
);
1755 if ( first
==s
) first
= NULL
;
1762 static void SSRemoveColinearPoints(SplineSet
*ss
) {
1763 SplinePoint
*sp
, *nsp
, *nnsp
;
1764 BasePoint dir
, ndir
;
1769 if ( sp
->prev
==NULL
)
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
;
1778 dir
.x
/= len
; dir
.y
/= len
;
1780 nnsp
= nsp
->next
->to
;
1783 memset(&ndir
,0,sizeof(ndir
));
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
;
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
;
1811 nnsp
= nsp
->next
->to
;
1812 if ( !removed
&& sp
==ss
->first
)
1817 static void SSesRemoveColinearPoints(SplineSet
*ss
) {
1818 while ( ss
!=NULL
) {
1819 SSRemoveColinearPoints(ss
);
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 )
1832 temp2
= SSRemoveUTurns(SplinePointListCopy(spl
),si
);
1833 if ( si
->stroke_type
== si_elipse
) {
1834 real trans
[6], factor
;
1836 trans
[0] = trans
[3] = si
->c
;
1839 trans
[4] = trans
[5] = 0;
1840 factor
= si
->radius
/si
->minorradius
;
1841 trans
[0] *= factor
; trans
[2] *= factor
;
1842 temp
= SplinePointListCopy(temp2
);
1844 BisectTurners(temp
);
1846 temp
= SplinePointListTransform(temp
,trans
,true);
1848 si2
.stroke_type
= si_std
;
1849 ret
= SplineSetStroke(temp
,&si2
,sc
);
1850 SplinePointListFree(temp
);
1851 trans
[0] = trans
[3] = si
->c
;
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);
1859 ret
= _SplineSetStroke(temp2
,si
,sc
);
1860 SplinePointListFree(temp2
);
1861 if ( order3
!=NULL
) {
1862 temp
= SplineSetsTTFApprox(ret
);
1863 SplinePointListsFree(ret
);
1864 SplinePointListFree(order3
);
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
);