Code

Unit test cleanup.
[inkscape.git] / src / livarot / PathStroke.cpp
1 /*
2  *  PathStroke.cpp
3  *  nlivarot
4  *
5  *  Created by fred on Tue Jun 17 2003.
6  *
7  */
9 #include "Path.h"
10 #include "Shape.h"
11 #include <2geom/transforms.h>
13 /*
14  * stroking polylines into a Shape instance
15  * grunt work.
16  * if the goal is to raster the stroke, polyline stroke->polygon->uncrossed polygon->raster is grossly
17  * inefficient (but reuse the intersector, so that's what a lazy programmer like me does). the correct way would be
18  * to set up a supersampled buffer, raster each polyline stroke's part (one part per segment in the polyline, plus 
19  * each join) because these are all convex polygons, then transform in alpha values
20  */
22 // until i find something better
23 Geom::Point StrokeNormalize(const Geom::Point value) {
24     double length = L2(value); 
25     if ( length < 0.0000001 ) { 
26         return Geom::Point(0, 0);
27     } else { 
28         return value/length; 
29     }
30 }
32 // faster version if length is known
33 Geom::Point StrokeNormalize(const Geom::Point value, double length) {
34     if ( length < 0.0000001 ) { 
35         return Geom::Point(0, 0);
36     } else { 
37         return value/length; 
38     }
39 }
41 void Path::Stroke(Shape *dest, bool doClose, double width, JoinType join,
42         ButtType butt, double miter, bool justAdd)
43 {
44     if (dest == NULL) {
45         return;
46     }
48     if (justAdd == false) {
49         dest->Reset(3 * pts.size(), 3 * pts.size());
50     }
52     dest->MakeBackData(false);
54     int lastM = 0;
55     while (lastM < int(pts.size())) {
57         int lastP = lastM + 1;
58         while (lastP < int(pts.size()) // select one subpath
59                 && (pts[lastP].isMoveTo == polyline_lineto
60                     || pts[lastP].isMoveTo == polyline_forced))
61         {
62             lastP++;
63         }
65         if ( lastP > lastM+1 ) {
66             Geom::Point sbStart = pts[lastM].p;
67             Geom::Point sbEnd = pts[lastP - 1].p;
68             // if ( pts[lastP - 1].closed ) { // this is correct, but this bugs text rendering (doesn't close text stroke)...
69             if ( Geom::LInfty(sbEnd-sbStart) < 0.00001 ) {       // why close lines that shouldn't be closed?
70                 // ah I see, because close is defined here for
71                 // a whole path and should be defined per subpath.
72                 // debut==fin => ferme (on devrait garder un element pour les close(), mais tant pis)
73                 DoStroke(lastM, lastP - lastM, dest, true, width, join, butt, miter, true);
74             } else {
75                 DoStroke(lastM, lastP - lastM, dest, doClose, width, join, butt, miter, true);
76             }
77         } else if (butt == butt_round) {       // special case: zero length round butt is a circle
78             int last[2] = { -1, -1 };
79             Geom::Point dir;
80             dir[0] = 1;
81             dir[1] = 0;
82             Geom::Point pos = pts[lastM].p;
83             DoButt(dest, width, butt, pos, dir, last[RIGHT], last[LEFT]);
84             int end[2];
85             dir = -dir;
86             DoButt(dest, width, butt, pos, dir, end[LEFT], end[RIGHT]);
87             dest->AddEdge (end[LEFT], last[LEFT]);
88             dest->AddEdge (last[RIGHT], end[RIGHT]);
89         }
90         lastM = lastP;
91     }
92 }
94 void Path::DoStroke(int off, int N, Shape *dest, bool doClose, double width, JoinType join,
95                     ButtType butt, double miter, bool /*justAdd*/)
96 {
97     if (N <= 1) {
98         return;
99     }
101     Geom::Point prevP, nextP;
102     int prevI, nextI;
103     int upTo;
105     int curI = 0;
106     Geom::Point curP = pts[off].p;
108     if (doClose) {
110         prevI = N - 1;
111         while (prevI > 0) {
112             prevP = pts[off + prevI].p;
113             Geom::Point diff = curP - prevP;
114             double dist = dot(diff, diff);
115             if (dist > 0.001) {
116                 break;
117             }
118             prevI--;
119         }
120         if (prevI <= 0) {
121             return;
122         }
123         upTo = prevI;
125     } else {
127         prevP = curP;
128         prevI = curI;
129         upTo = N - 1;
130     }
132     {
133         nextI = 1;
134         while (nextI <= upTo) {
135             nextP = pts[off + nextI].p;
136             Geom::Point diff = curP - nextP;
137             double dist = dot(diff, diff);
138             if (dist > 0.0) { // more tolerance for the first distance, to give the cap the right direction
139                 break;
140             }
141             nextI++;
142         }
143         if (nextI > upTo) {
144             if (butt == butt_round) {  // special case: zero length round butt is a circle
145                 int last[2] = { -1, -1 };
146                 Geom::Point dir;
147                 dir[0] = 1;
148                 dir[1] = 0;
149                 DoButt(dest, width, butt, curP, dir, last[RIGHT], last[LEFT]);
150                 int end[2];
151                 dir = -dir;
152                 DoButt(dest, width, butt, curP, dir, end[LEFT], end[RIGHT]);
153                 dest->AddEdge (end[LEFT], last[LEFT]);
154                 dest->AddEdge (last[RIGHT], end[RIGHT]);
155             }
156             return;
157         }
158     }
160     int start[2] = { -1, -1 };
161     int last[2] = { -1, -1 };
162     Geom::Point prevD = curP - prevP;
163     Geom::Point nextD = nextP - curP;
164     double prevLe = Geom::L2(prevD);
165     double nextLe = Geom::L2(nextD);
166     prevD = StrokeNormalize(prevD, prevLe);
167     nextD = StrokeNormalize(nextD, nextLe);
169     if (doClose) {
170         DoJoin(dest,  width, join, curP, prevD, nextD, miter, prevLe, nextLe, start, last);
171     } else {
172         nextD = -nextD;
173         DoButt(dest,  width, butt, curP, nextD, last[RIGHT], last[LEFT]);
174         nextD = -nextD;
175     }
177     do {
178         prevP = curP;
179         prevI = curI;
180         curP = nextP;
181         curI = nextI;
182         prevD = nextD;
183         prevLe = nextLe;
184         nextI++;
185         while (nextI <= upTo) {
186             nextP = pts[off + nextI].p;
187             Geom::Point diff = curP - nextP;
188             double dist = dot(diff, diff);
189             if (dist > 0.001 || (nextI == upTo && dist > 0.0)) { // more tolerance for the last distance too, for the right cap direction
190                 break;
191             }
192             nextI++;
193         }
194         if (nextI > upTo) {
195             break;
196         }
198         nextD = nextP - curP;
199         nextLe = Geom::L2(nextD);
200         nextD = StrokeNormalize(nextD, nextLe);
201         int nSt[2] = { -1, -1 };
202         int nEn[2] = { -1, -1 };
203         DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn);
204         dest->AddEdge(nSt[LEFT], last[LEFT]);
205         last[LEFT] = nEn[LEFT];
206         dest->AddEdge(last[RIGHT], nSt[RIGHT]);
207         last[RIGHT] = nEn[RIGHT];
208     } while (nextI <= upTo);
210     if (doClose) {
211         /*              prevP=curP;
212                         prevI=curI;
213                         curP=nextP;
214                         curI=nextI;
215                         prevD=nextD;*/
216         nextP = pts[off].p;
218         nextD = nextP - curP;
219         nextLe = Geom::L2(nextD);
220         nextD = StrokeNormalize(nextD, nextLe);
221         int nSt[2] = { -1, -1 };
222         int nEn[2] = { -1, -1 };
223         DoJoin(dest,  width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn);
224         dest->AddEdge (nSt[LEFT], last[LEFT]);
225         last[LEFT] = nEn[LEFT];
226         dest->AddEdge (last[RIGHT], nSt[RIGHT]);
227         last[RIGHT] = nEn[RIGHT];
229         dest->AddEdge (start[LEFT], last[LEFT]);
230         dest->AddEdge (last[RIGHT], start[RIGHT]);
232     } else {
234         int end[2];
235         DoButt (dest,  width, butt, curP, prevD, end[LEFT], end[RIGHT]);
236         dest->AddEdge (end[LEFT], last[LEFT]);
237         dest->AddEdge (last[RIGHT], end[RIGHT]);
238     }
242 void Path::DoButt(Shape *dest, double width, ButtType butt, Geom::Point pos, Geom::Point dir,
243         int &leftNo, int &rightNo)
245     Geom::Point nor;
246     nor = dir.ccw();
248     if (butt == butt_square)
249     {
250         Geom::Point x;
251         x = pos + width * dir + width * nor;
252         int bleftNo = dest->AddPoint (x);
253         x = pos + width * dir - width * nor;
254         int brightNo = dest->AddPoint (x);
255         x = pos + width * nor;
256         leftNo = dest->AddPoint (x);
257         x = pos - width * nor;
258         rightNo = dest->AddPoint (x);
259         dest->AddEdge (rightNo, brightNo);
260         dest->AddEdge (brightNo, bleftNo);
261         dest->AddEdge (bleftNo, leftNo);
262     }
263     else if (butt == butt_pointy)
264     {
265         leftNo = dest->AddPoint (pos + width * nor);
266         rightNo = dest->AddPoint (pos - width * nor);
267         int mid = dest->AddPoint (pos + width * dir);
268         dest->AddEdge (rightNo, mid);
269         dest->AddEdge (mid, leftNo);
270     }
271     else if (butt == butt_round)
272     {
273         const Geom::Point sx = pos + width * nor;
274         const Geom::Point ex = pos - width * nor;
275         leftNo = dest->AddPoint (sx);
276         rightNo = dest->AddPoint (ex);
278         RecRound (dest, rightNo, leftNo, ex, sx, -nor, nor, pos, width);
279     }
280     else
281     {
282         leftNo = dest->AddPoint (pos + width * nor);
283         rightNo = dest->AddPoint (pos - width * nor);
284         dest->AddEdge (rightNo, leftNo);
285     }
289 void Path::DoJoin (Shape *dest, double width, JoinType join, Geom::Point pos, Geom::Point prev,
290         Geom::Point next, double miter, double /*prevL*/, double /*nextL*/,
291         int *stNo, int *enNo)
293     Geom::Point pnor = prev.ccw();
294     Geom::Point nnor = next.ccw();
295     double angSi = cross(next, prev);
297     /* FIXED: this special case caused bug 1028953 */
298     if (angSi > -0.0001 && angSi < 0.0001) {
299         double angCo = dot (prev, next);
300         if (angCo > 0.9999) {
301             // tout droit
302             stNo[LEFT] = enNo[LEFT] = dest->AddPoint(pos + width * pnor);
303             stNo[RIGHT] = enNo[RIGHT] = dest->AddPoint(pos - width * pnor);
304         } else {
305             // demi-tour
306             const Geom::Point sx = pos + width * pnor;
307             const Geom::Point ex = pos - width * pnor;
308             stNo[LEFT] = enNo[RIGHT] = dest->AddPoint (sx);
309             stNo[RIGHT] = enNo[LEFT] = dest->AddPoint (ex);
310             if (join == join_round) {
311                 RecRound (dest, enNo[LEFT], stNo[LEFT], ex, sx, -pnor, pnor, pos, width);
312                 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
313             } else {
314                 dest->AddEdge(enNo[LEFT], stNo[LEFT]);
315                 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);        // two times because both are crossing each other
316             }
317         }
318         return;
319     }
321     if (angSi < 0) {
322         int midNo = dest->AddPoint(pos);
323         stNo[LEFT] = dest->AddPoint(pos + width * pnor);
324         enNo[LEFT] = dest->AddPoint(pos + width * nnor);
325         dest->AddEdge(enNo[LEFT], midNo);
326         dest->AddEdge(midNo, stNo[LEFT]);
328         if (join == join_pointy) {
330             stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
331             enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
333             const Geom::Point biss = StrokeNormalize(prev - next);
334             double c2 = dot(biss, nnor);
335             double l = width / c2;
336             double emiter = width * c2;
337             if (emiter < miter) {
338                 emiter = miter;
339             }
341             if (fabs(l) < miter) {
342                 int const n = dest->AddPoint(pos - l * biss);
343                 dest->AddEdge(stNo[RIGHT], n);
344                 dest->AddEdge(n, enNo[RIGHT]);
345             } else {
346                 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
347             }
349         } else if (join == join_round) {
350             Geom::Point sx = pos - width * pnor;
351             stNo[RIGHT] = dest->AddPoint(sx);
352             Geom::Point ex = pos - width * nnor;
353             enNo[RIGHT] = dest->AddPoint(ex);
355             RecRound(dest, stNo[RIGHT], enNo[RIGHT], 
356                     sx, ex, -pnor, -nnor, pos, width);
358         } else {
359             stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
360             enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
361             dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
362         }
364     } else {
366         int midNo = dest->AddPoint(pos);
367         stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
368         enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
369         dest->AddEdge(stNo[RIGHT], midNo);
370         dest->AddEdge(midNo, enNo[RIGHT]);
372         if (join == join_pointy) {
374             stNo[LEFT] = dest->AddPoint(pos + width * pnor);
375             enNo[LEFT] = dest->AddPoint(pos + width * nnor);
377             const Geom::Point biss = StrokeNormalize(next - prev);
378             double c2 = dot(biss, nnor);
379             double l = width / c2;
380             double emiter = width * c2;
381             if (emiter < miter) {
382                 emiter = miter;
383             }
384             if ( fabs(l) < miter) {
385                 int const n = dest->AddPoint (pos + l * biss);
386                 dest->AddEdge (enNo[LEFT], n);
387                 dest->AddEdge (n, stNo[LEFT]);
388             }
389             else
390             {
391                 dest->AddEdge (enNo[LEFT], stNo[LEFT]);
392             }
394         } else if (join == join_round) {
396             Geom::Point sx = pos + width * pnor;
397             stNo[LEFT] = dest->AddPoint(sx);
398             Geom::Point ex = pos + width * nnor;
399             enNo[LEFT] = dest->AddPoint(ex);
401             RecRound(dest, enNo[LEFT], stNo[LEFT], 
402                     ex, sx, nnor, pnor, pos, width);
404         } else {
405             stNo[LEFT] = dest->AddPoint(pos + width * pnor);
406             enNo[LEFT] = dest->AddPoint(pos + width * nnor);
407             dest->AddEdge(enNo[LEFT], stNo[LEFT]);
408         }
409     }
412     void
413 Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
414         Geom::Point prev, Geom::Point next, double miter, double /*prevL*/, double /*nextL*/,
415         int &leftStNo, int &leftEnNo,int pathID,int pieceID,double tID)
417     Geom::Point pnor=prev.ccw();
418     Geom::Point nnor=next.ccw();
419     double angSi = cross (next, prev);
420     if (angSi > -0.0001 && angSi < 0.0001)
421     {
422         double angCo = dot (prev, next);
423         if (angCo > 0.9999)
424         {
425             // tout droit
426             leftEnNo = leftStNo = dest->AddPoint (pos + width * pnor);
427         }
428         else
429         {
430             // demi-tour
431             leftStNo = dest->AddPoint (pos + width * pnor);
432             leftEnNo = dest->AddPoint (pos - width * pnor);
433             int nEdge=dest->AddEdge (leftEnNo, leftStNo);
434             if ( dest->hasBackData() ) {
435                 dest->ebData[nEdge].pathID=pathID;
436                 dest->ebData[nEdge].pieceID=pieceID;
437                 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
438             }
439         }
440         return;
441     }
442     if (angSi < 0)
443     {
444         /*              Geom::Point     biss;
445                         biss.x=next.x-prev.x;
446                         biss.y=next.y-prev.y;
447                         double   c2=cross(biss,next);
448                         double   l=width/c2;
449                         double          projn=l*(dot(biss,next));
450                         double          projp=-l*(dot(biss,prev));
451                         if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) {
452                         double   x,y;
453                         x=pos.x+l*biss.x;
454                         y=pos.y+l*biss.y;
455                         leftEnNo=leftStNo=dest->AddPoint(x,y);
456                         } else {*/
457         leftStNo = dest->AddPoint (pos + width * pnor);
458         leftEnNo = dest->AddPoint (pos + width * nnor);
459         int midNo = dest->AddPoint (pos);
460         int nEdge=dest->AddEdge (leftEnNo, midNo);
461         if ( dest->hasBackData() ) {
462             dest->ebData[nEdge].pathID=pathID;
463             dest->ebData[nEdge].pieceID=pieceID;
464             dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
465         }
466         nEdge=dest->AddEdge (midNo, leftStNo);
467         if ( dest->hasBackData() ) {
468             dest->ebData[nEdge].pathID=pathID;
469             dest->ebData[nEdge].pieceID=pieceID;
470             dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
471         }
472         //              }
473     }
474     else
475     {
476         if (join == join_pointy)
477         {
478             leftStNo = dest->AddPoint (pos + width * pnor);
479             leftEnNo = dest->AddPoint (pos + width * nnor);
481             const Geom::Point biss = StrokeNormalize (pnor + nnor);
482             double c2 = dot (biss, nnor);
483             double l = width / c2;
484             double emiter = width * c2;
485             if (emiter < miter)
486                 emiter = miter;
487             if (l <= emiter)
488             {
489                 int nleftStNo = dest->AddPoint (pos + l * biss);
490                 int nEdge=dest->AddEdge (leftEnNo, nleftStNo);
491                 if ( dest->hasBackData() ) {
492                     dest->ebData[nEdge].pathID=pathID;
493                     dest->ebData[nEdge].pieceID=pieceID;
494                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
495                 }
496                 nEdge=dest->AddEdge (nleftStNo, leftStNo);
497                 if ( dest->hasBackData() ) {
498                     dest->ebData[nEdge].pathID=pathID;
499                     dest->ebData[nEdge].pieceID=pieceID;
500                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
501                 }
502             }
503             else
504             {
505                 double s2 = cross (biss, nnor);
506                 double dec = (l - emiter) * c2 / s2;
507                 const Geom::Point tbiss=biss.ccw();
509                 int nleftStNo = dest->AddPoint (pos + emiter * biss + dec * tbiss);
510                 int nleftEnNo = dest->AddPoint (pos + emiter * biss - dec * tbiss);
511                 int nEdge=dest->AddEdge (nleftEnNo, nleftStNo);
512                 if ( dest->hasBackData() ) {
513                     dest->ebData[nEdge].pathID=pathID;
514                     dest->ebData[nEdge].pieceID=pieceID;
515                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
516                 }
517                 nEdge=dest->AddEdge (leftEnNo, nleftEnNo);
518                 if ( dest->hasBackData() ) {
519                     dest->ebData[nEdge].pathID=pathID;
520                     dest->ebData[nEdge].pieceID=pieceID;
521                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
522                 }
523                 nEdge=dest->AddEdge (nleftStNo, leftStNo);
524                 if ( dest->hasBackData() ) {
525                     dest->ebData[nEdge].pathID=pathID;
526                     dest->ebData[nEdge].pieceID=pieceID;
527                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
528                 }
529             }
530         }
531         else if (join == join_round)
532         {
533             const Geom::Point sx = pos + width * pnor;
534             leftStNo = dest->AddPoint (sx);
535             const Geom::Point ex = pos + width * nnor;
536             leftEnNo = dest->AddPoint (ex);
538             RecRound(dest, leftEnNo, leftStNo, 
539                     sx, ex, pnor, nnor ,pos, width);
541         }
542         else
543         {
544             leftStNo = dest->AddPoint (pos + width * pnor);
545             leftEnNo = dest->AddPoint (pos + width * nnor);
546             int nEdge=dest->AddEdge (leftEnNo, leftStNo);
547             if ( dest->hasBackData() ) {
548                 dest->ebData[nEdge].pathID=pathID;
549                 dest->ebData[nEdge].pieceID=pieceID;
550                 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
551             }
552         }
553     }
555     void
556 Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
557         Geom::Point prev, Geom::Point next, double miter, double /*prevL*/,
558         double /*nextL*/, int &rightStNo, int &rightEnNo,int pathID,int pieceID,double tID)
560     const Geom::Point pnor=prev.ccw();
561     const Geom::Point nnor=next.ccw();
562     double angSi = cross (next,prev);
563     if (angSi > -0.0001 && angSi < 0.0001)
564     {
565         double angCo = dot (prev, next);
566         if (angCo > 0.9999)
567         {
568             // tout droit
569             rightEnNo = rightStNo = dest->AddPoint (pos - width*pnor);
570         }
571         else
572         {
573             // demi-tour
574             rightEnNo = dest->AddPoint (pos + width*pnor);
575             rightStNo = dest->AddPoint (pos - width*pnor);
576             int nEdge=dest->AddEdge (rightStNo, rightEnNo);
577             if ( dest->hasBackData() ) {
578                 dest->ebData[nEdge].pathID=pathID;
579                 dest->ebData[nEdge].pieceID=pieceID;
580                 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
581             }
582         }
583         return;
584     }
585     if (angSi < 0)
586     {
587         if (join == join_pointy)
588         {
589             rightStNo = dest->AddPoint (pos - width*pnor);
590             rightEnNo = dest->AddPoint (pos - width*nnor);
592             const Geom::Point biss = StrokeNormalize (pnor + nnor);
593             double c2 = dot (biss, nnor);
594             double l = width / c2;
595             double emiter = width * c2;
596             if (emiter < miter)
597                 emiter = miter;
598             if (l <= emiter)
599             {
600                 int nrightStNo = dest->AddPoint (pos - l * biss);
601                 int nEdge=dest->AddEdge (rightStNo, nrightStNo);
602                 if ( dest->hasBackData() ) {
603                     dest->ebData[nEdge].pathID=pathID;
604                     dest->ebData[nEdge].pieceID=pieceID;
605                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
606                 }
607                 nEdge=dest->AddEdge (nrightStNo, rightEnNo);
608                 if ( dest->hasBackData() ) {
609                     dest->ebData[nEdge].pathID=pathID;
610                     dest->ebData[nEdge].pieceID=pieceID;
611                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
612                 }
613             }
614             else
615             {
616                 double s2 = cross (biss, nnor);
617                 double dec = (l - emiter) * c2 / s2;
618                 const Geom::Point tbiss=biss.ccw();
620                 int nrightStNo = dest->AddPoint (pos - emiter*biss - dec*tbiss);
621                 int nrightEnNo = dest->AddPoint (pos - emiter*biss + dec*tbiss);
622                 int nEdge=dest->AddEdge (rightStNo, nrightStNo);
623                 if ( dest->hasBackData() ) {
624                     dest->ebData[nEdge].pathID=pathID;
625                     dest->ebData[nEdge].pieceID=pieceID;
626                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
627                 }
628                 nEdge=dest->AddEdge (nrightStNo, nrightEnNo);
629                 if ( dest->hasBackData() ) {
630                     dest->ebData[nEdge].pathID=pathID;
631                     dest->ebData[nEdge].pieceID=pieceID;
632                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
633                 }
634                 nEdge=dest->AddEdge (nrightEnNo, rightEnNo);
635                 if ( dest->hasBackData() ) {
636                     dest->ebData[nEdge].pathID=pathID;
637                     dest->ebData[nEdge].pieceID=pieceID;
638                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
639                 }
640             }
641         }
642         else if (join == join_round)
643         {
644             const Geom::Point sx = pos - width * pnor;
645             rightStNo = dest->AddPoint (sx);
646             const Geom::Point ex = pos - width * nnor;
647             rightEnNo = dest->AddPoint (ex);
649             RecRound(dest, rightStNo, rightEnNo, 
650                     sx, ex, -pnor, -nnor ,pos, width);
651         }
652         else
653         {
654             rightStNo = dest->AddPoint (pos - width * pnor);
655             rightEnNo = dest->AddPoint (pos - width * nnor);
656             int nEdge=dest->AddEdge (rightStNo, rightEnNo);
657             if ( dest->hasBackData() ) {
658                 dest->ebData[nEdge].pathID=pathID;
659                 dest->ebData[nEdge].pieceID=pieceID;
660                 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
661             }
662         }
663     }
664     else
665     {
666         /*              Geom::Point     biss;
667                         biss=next.x-prev.x;
668                         biss.y=next.y-prev.y;
669                         double   c2=cross(next,biss);
670                         double   l=width/c2;
671                         double          projn=l*(dot(biss,next));
672                         double          projp=-l*(dot(biss,prev));
673                         if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) {
674                         double   x,y;
675                         x=pos.x+l*biss.x;
676                         y=pos.y+l*biss.y;
677                         rightEnNo=rightStNo=dest->AddPoint(x,y);
678                         } else {*/
679         rightStNo = dest->AddPoint (pos - width*pnor);
680         rightEnNo = dest->AddPoint (pos - width*nnor);
681         int midNo = dest->AddPoint (pos);
682         int nEdge=dest->AddEdge (rightStNo, midNo);
683         if ( dest->hasBackData() ) {
684             dest->ebData[nEdge].pathID=pathID;                                  
685             dest->ebData[nEdge].pieceID=pieceID;
686             dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
687         }
688         nEdge=dest->AddEdge (midNo, rightEnNo);
689         if ( dest->hasBackData() ) {
690             dest->ebData[nEdge].pathID=pathID;
691             dest->ebData[nEdge].pieceID=pieceID;
692             dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
693         }
694         //              }
695     }
699 // a very ugly way to produce round joins: doing one (or two, depend on the angle of the join) quadratic bezier curves
700 // but since most joins are going to be small, nobody will notice -- but somebody noticed and now the ugly stuff is gone! so:
702 // a very nice way to produce round joins, caps or dots
703 void Path::RecRound(Shape *dest, int sNo, int eNo, // start and end index
704         Geom::Point const &iS, Geom::Point const &iE, // start and end point
705         Geom::Point const &nS, Geom::Point const &nE, // start and end normal vector
706         Geom::Point &origine, float width) // center and radius of round
708     //Geom::Point diff = iS - iE;
709     //double dist = dot(diff, diff);
710     if (width < 0.5 || dot(iS - iE, iS - iE)/width < 2.0) {
711         dest->AddEdge(sNo, eNo);
712         return;
713     }
714     double ang, sia, lod;
715     if (nS == -nE) {
716         ang = M_PI;
717         sia = 1;
718     } else {
719         double coa = dot(nS, nE);
720         sia = cross(nS, nE);
721         ang = acos(coa);
722         if ( coa >= 1 ) {
723             ang = 0;
724         }
725         if ( coa <= -1 ) {
726             ang = M_PI;
727         }
728     }
729     lod = 0.02 + 10 / (10 + width); // limit detail to about 2 degrees (180 * 0.02/Pi degrees)
730     ang /= lod;
732     int nbS = (int) floor(ang);
733     Geom::Rotate omega(((sia > 0) ? -lod : lod));
734     Geom::Point cur = iS - origine;
735     //  StrokeNormalize(cur);
736     //  cur*=width;
737     int lastNo = sNo;
738     for (int i = 0; i < nbS; i++) {
739         cur = cur * omega;
740         Geom::Point m = origine + cur;
741         int mNo = dest->AddPoint(m);
742         dest->AddEdge(lastNo, mNo);
743         lastNo = mNo;
744     }
745     dest->AddEdge(lastNo, eNo);
748 /*
749    Local Variables:
750 mode:c++
751 c-file-style:"stroustrup"
752 c-file-offsets:((innamespace . 0)(inline-open . 0))
753 indent-tabs-mode:nil
754 fill-column:99
755 End:
756  */
757 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :