Code

Merging from trunk
[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 NR::Point StrokeNormalize(const NR::Point value) {
24     double length = L2(value); 
25     if ( length < 0.0000001 ) { 
26         return NR::Point(0, 0);
27     } else { 
28         return value/length; 
29     }
30 }
32 // faster version if length is known
33 NR::Point StrokeNormalize(const NR::Point value, double length) {
34     if ( length < 0.0000001 ) { 
35         return NR::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             NR::Point sbStart = pts[lastM].p;
67             NR::Point sbEnd = pts[lastP - 1].p;
68             if ( NR::LInfty(sbEnd-sbStart) < 0.00001 ) {       // why close lines that shouldn't be closed?
69                 // ah I see, because close is defined here for
70                 // a whole path and should be defined per subpath.
71                 // debut==fin => ferme (on devrait garder un element pour les close(), mais tant pis)
72                 DoStroke(lastM, lastP - lastM, dest, true, width, join, butt, miter, true);
73             } else {
74                 DoStroke(lastM, lastP - lastM, dest, doClose, width, join, butt, miter, true);
75             }
76         } else if (butt == butt_round) {       // special case: zero length round butt is a circle
77             int last[2] = { -1, -1 };
78             NR::Point dir;
79             dir[0] = 1;
80             dir[1] = 0;
81             NR::Point pos = pts[lastM].p;
82             DoButt(dest, width, butt, pos, dir, last[RIGHT], last[LEFT]);
83             int end[2];
84             dir = -dir;
85             DoButt(dest, width, butt, pos, dir, end[LEFT], end[RIGHT]);
86             dest->AddEdge (end[LEFT], last[LEFT]);
87             dest->AddEdge (last[RIGHT], end[RIGHT]);
88         }
89         lastM = lastP;
90     }
91 }
93 void Path::DoStroke(int off, int N, Shape *dest, bool doClose, double width, JoinType join,
94                     ButtType butt, double miter, bool /*justAdd*/)
95 {
96     if (N <= 1) {
97         return;
98     }
100     NR::Point prevP, nextP;
101     int prevI, nextI;
102     int upTo;
104     int curI = 0;
105     NR::Point curP = pts[off].p;
107     if (doClose) {
109         prevI = N - 1;
110         while (prevI > 0) {
111             prevP = pts[off + prevI].p;
112             NR::Point diff = curP - prevP;
113             double dist = dot(diff, diff);
114             if (dist > 0.001) {
115                 break;
116             }
117             prevI--;
118         }
119         if (prevI <= 0) {
120             return;
121         }
122         upTo = prevI;
124     } else {
126         prevP = curP;
127         prevI = curI;
128         upTo = N - 1;
129     }
131     {
132         nextI = 1;
133         while (nextI <= upTo) {
134             nextP = pts[off + nextI].p;
135             NR::Point diff = curP - nextP;
136             double dist = dot(diff, diff);
137             if (dist > 0.0) { // more tolerance for the first distance, to give the cap the right direction
138                 break;
139             }
140             nextI++;
141         }
142         if (nextI > upTo) {
143             if (butt == butt_round) {  // special case: zero length round butt is a circle
144                 int last[2] = { -1, -1 };
145                 NR::Point dir;
146                 dir[0] = 1;
147                 dir[1] = 0;
148                 DoButt(dest, width, butt, curP, dir, last[RIGHT], last[LEFT]);
149                 int end[2];
150                 dir = -dir;
151                 DoButt(dest, width, butt, curP, dir, end[LEFT], end[RIGHT]);
152                 dest->AddEdge (end[LEFT], last[LEFT]);
153                 dest->AddEdge (last[RIGHT], end[RIGHT]);
154             }
155             return;
156         }
157     }
159     int start[2] = { -1, -1 };
160     int last[2] = { -1, -1 };
161     NR::Point prevD = curP - prevP;
162     NR::Point nextD = nextP - curP;
163     double prevLe = NR::L2(prevD);
164     double nextLe = NR::L2(nextD);
165     prevD = StrokeNormalize(prevD, prevLe);
166     nextD = StrokeNormalize(nextD, nextLe);
168     if (doClose) {
169         DoJoin(dest,  width, join, curP, prevD, nextD, miter, prevLe, nextLe, start, last);
170     } else {
171         nextD = -nextD;
172         DoButt(dest,  width, butt, curP, nextD, last[RIGHT], last[LEFT]);
173         nextD = -nextD;
174     }
176     do {
177         prevP = curP;
178         prevI = curI;
179         curP = nextP;
180         curI = nextI;
181         prevD = nextD;
182         prevLe = nextLe;
183         nextI++;
184         while (nextI <= upTo) {
185             nextP = pts[off + nextI].p;
186             NR::Point diff = curP - nextP;
187             double dist = dot(diff, diff);
188             if (dist > 0.001 || (nextI == upTo && dist > 0.0)) { // more tolerance for the last distance too, for the right cap direction
189                 break;
190             }
191             nextI++;
192         }
193         if (nextI > upTo) {
194             break;
195         }
197         nextD = nextP - curP;
198         nextLe = NR::L2(nextD);
199         nextD = StrokeNormalize(nextD, nextLe);
200         int nSt[2] = { -1, -1 };
201         int nEn[2] = { -1, -1 };
202         DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn);
203         dest->AddEdge(nSt[LEFT], last[LEFT]);
204         last[LEFT] = nEn[LEFT];
205         dest->AddEdge(last[RIGHT], nSt[RIGHT]);
206         last[RIGHT] = nEn[RIGHT];
207     } while (nextI <= upTo);
209     if (doClose) {
210         /*              prevP=curP;
211                         prevI=curI;
212                         curP=nextP;
213                         curI=nextI;
214                         prevD=nextD;*/
215         nextP = pts[off].p;
217         nextD = nextP - curP;
218         nextLe = NR::L2(nextD);
219         nextD = StrokeNormalize(nextD, nextLe);
220         int nSt[2] = { -1, -1 };
221         int nEn[2] = { -1, -1 };
222         DoJoin(dest,  width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn);
223         dest->AddEdge (nSt[LEFT], last[LEFT]);
224         last[LEFT] = nEn[LEFT];
225         dest->AddEdge (last[RIGHT], nSt[RIGHT]);
226         last[RIGHT] = nEn[RIGHT];
228         dest->AddEdge (start[LEFT], last[LEFT]);
229         dest->AddEdge (last[RIGHT], start[RIGHT]);
231     } else {
233         int end[2];
234         DoButt (dest,  width, butt, curP, prevD, end[LEFT], end[RIGHT]);
235         dest->AddEdge (end[LEFT], last[LEFT]);
236         dest->AddEdge (last[RIGHT], end[RIGHT]);
237     }
241 void Path::DoButt(Shape *dest, double width, ButtType butt, NR::Point pos, NR::Point dir,
242         int &leftNo, int &rightNo)
244     NR::Point nor;
245     nor = dir.ccw();
247     if (butt == butt_square)
248     {
249         NR::Point x;
250         x = pos + width * dir + width * nor;
251         int bleftNo = dest->AddPoint (x);
252         x = pos + width * dir - width * nor;
253         int brightNo = dest->AddPoint (x);
254         x = pos + width * nor;
255         leftNo = dest->AddPoint (x);
256         x = pos - width * nor;
257         rightNo = dest->AddPoint (x);
258         dest->AddEdge (rightNo, brightNo);
259         dest->AddEdge (brightNo, bleftNo);
260         dest->AddEdge (bleftNo, leftNo);
261     }
262     else if (butt == butt_pointy)
263     {
264         leftNo = dest->AddPoint (pos + width * nor);
265         rightNo = dest->AddPoint (pos - width * nor);
266         int mid = dest->AddPoint (pos + width * dir);
267         dest->AddEdge (rightNo, mid);
268         dest->AddEdge (mid, leftNo);
269     }
270     else if (butt == butt_round)
271     {
272         const NR::Point sx = pos + width * nor;
273         const NR::Point ex = pos - width * nor;
274         leftNo = dest->AddPoint (sx);
275         rightNo = dest->AddPoint (ex);
277         RecRound (dest, rightNo, leftNo, ex, sx, -nor, nor, pos, width);
278     }
279     else
280     {
281         leftNo = dest->AddPoint (pos + width * nor);
282         rightNo = dest->AddPoint (pos - width * nor);
283         dest->AddEdge (rightNo, leftNo);
284     }
288 void Path::DoJoin (Shape *dest, double width, JoinType join, NR::Point pos, NR::Point prev,
289         NR::Point next, double miter, double /*prevL*/, double /*nextL*/,
290         int *stNo, int *enNo)
292     NR::Point pnor = prev.ccw();
293     NR::Point nnor = next.ccw();
294     double angSi = cross(next, prev);
296     /* FIXED: this special case caused bug 1028953 */
297     if (angSi > -0.0001 && angSi < 0.0001) {
298         double angCo = dot (prev, next);
299         if (angCo > 0.9999) {
300             // tout droit
301             stNo[LEFT] = enNo[LEFT] = dest->AddPoint(pos + width * pnor);
302             stNo[RIGHT] = enNo[RIGHT] = dest->AddPoint(pos - width * pnor);
303         } else {
304             // demi-tour
305             const NR::Point sx = pos + width * pnor;
306             const NR::Point ex = pos - width * pnor;
307             stNo[LEFT] = enNo[RIGHT] = dest->AddPoint (sx);
308             stNo[RIGHT] = enNo[LEFT] = dest->AddPoint (ex);
309             if (join == join_round) {
310                 RecRound (dest, enNo[LEFT], stNo[LEFT], ex, sx, -pnor, pnor, pos, width);
311                 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
312             } else {
313                 dest->AddEdge(enNo[LEFT], stNo[LEFT]);
314                 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);        // two times because both are crossing each other
315             }
316         }
317         return;
318     }
320     if (angSi < 0) {
321         int midNo = dest->AddPoint(pos);
322         stNo[LEFT] = dest->AddPoint(pos + width * pnor);
323         enNo[LEFT] = dest->AddPoint(pos + width * nnor);
324         dest->AddEdge(enNo[LEFT], midNo);
325         dest->AddEdge(midNo, stNo[LEFT]);
327         if (join == join_pointy) {
329             stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
330             enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
332             const NR::Point biss = StrokeNormalize(prev - next);
333             double c2 = dot(biss, nnor);
334             double l = width / c2;
335             double emiter = width * c2;
336             if (emiter < miter) {
337                 emiter = miter;
338             }
340             if (fabs(l) < miter) {
341                 int const n = dest->AddPoint(pos - l * biss);
342                 dest->AddEdge(stNo[RIGHT], n);
343                 dest->AddEdge(n, enNo[RIGHT]);
344             } else {
345                 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
346             }
348         } else if (join == join_round) {
349             NR::Point sx = pos - width * pnor;
350             stNo[RIGHT] = dest->AddPoint(sx);
351             NR::Point ex = pos - width * nnor;
352             enNo[RIGHT] = dest->AddPoint(ex);
354             RecRound(dest, stNo[RIGHT], enNo[RIGHT], 
355                     sx, ex, -pnor, -nnor, pos, width);
357         } else {
358             stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
359             enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
360             dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
361         }
363     } else {
365         int midNo = dest->AddPoint(pos);
366         stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
367         enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
368         dest->AddEdge(stNo[RIGHT], midNo);
369         dest->AddEdge(midNo, enNo[RIGHT]);
371         if (join == join_pointy) {
373             stNo[LEFT] = dest->AddPoint(pos + width * pnor);
374             enNo[LEFT] = dest->AddPoint(pos + width * nnor);
376             const NR::Point biss = StrokeNormalize(next - prev);
377             double c2 = dot(biss, nnor);
378             double l = width / c2;
379             double emiter = width * c2;
380             if (emiter < miter) {
381                 emiter = miter;
382             }
383             if ( fabs(l) < miter) {
384                 int const n = dest->AddPoint (pos + l * biss);
385                 dest->AddEdge (enNo[LEFT], n);
386                 dest->AddEdge (n, stNo[LEFT]);
387             }
388             else
389             {
390                 dest->AddEdge (enNo[LEFT], stNo[LEFT]);
391             }
393         } else if (join == join_round) {
395             NR::Point sx = pos + width * pnor;
396             stNo[LEFT] = dest->AddPoint(sx);
397             NR::Point ex = pos + width * nnor;
398             enNo[LEFT] = dest->AddPoint(ex);
400             RecRound(dest, enNo[LEFT], stNo[LEFT], 
401                     ex, sx, nnor, pnor, pos, width);
403         } else {
404             stNo[LEFT] = dest->AddPoint(pos + width * pnor);
405             enNo[LEFT] = dest->AddPoint(pos + width * nnor);
406             dest->AddEdge(enNo[LEFT], stNo[LEFT]);
407         }
408     }
411     void
412 Path::DoLeftJoin (Shape * dest, double width, JoinType join, NR::Point pos,
413         NR::Point prev, NR::Point next, double miter, double /*prevL*/, double /*nextL*/,
414         int &leftStNo, int &leftEnNo,int pathID,int pieceID,double tID)
416     NR::Point pnor=prev.ccw();
417     NR::Point nnor=next.ccw();
418     double angSi = cross (next, prev);
419     if (angSi > -0.0001 && angSi < 0.0001)
420     {
421         double angCo = dot (prev, next);
422         if (angCo > 0.9999)
423         {
424             // tout droit
425             leftEnNo = leftStNo = dest->AddPoint (pos + width * pnor);
426         }
427         else
428         {
429             // demi-tour
430             leftStNo = dest->AddPoint (pos + width * pnor);
431             leftEnNo = dest->AddPoint (pos - width * pnor);
432             int nEdge=dest->AddEdge (leftEnNo, leftStNo);
433             if ( dest->hasBackData() ) {
434                 dest->ebData[nEdge].pathID=pathID;
435                 dest->ebData[nEdge].pieceID=pieceID;
436                 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
437             }
438         }
439         return;
440     }
441     if (angSi < 0)
442     {
443         /*              NR::Point     biss;
444                         biss.x=next.x-prev.x;
445                         biss.y=next.y-prev.y;
446                         double   c2=cross(biss,next);
447                         double   l=width/c2;
448                         double          projn=l*(dot(biss,next));
449                         double          projp=-l*(dot(biss,prev));
450                         if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) {
451                         double   x,y;
452                         x=pos.x+l*biss.x;
453                         y=pos.y+l*biss.y;
454                         leftEnNo=leftStNo=dest->AddPoint(x,y);
455                         } else {*/
456         leftStNo = dest->AddPoint (pos + width * pnor);
457         leftEnNo = dest->AddPoint (pos + width * nnor);
458         int midNo = dest->AddPoint (pos);
459         int nEdge=dest->AddEdge (leftEnNo, midNo);
460         if ( dest->hasBackData() ) {
461             dest->ebData[nEdge].pathID=pathID;
462             dest->ebData[nEdge].pieceID=pieceID;
463             dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
464         }
465         nEdge=dest->AddEdge (midNo, leftStNo);
466         if ( dest->hasBackData() ) {
467             dest->ebData[nEdge].pathID=pathID;
468             dest->ebData[nEdge].pieceID=pieceID;
469             dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
470         }
471         //              }
472     }
473     else
474     {
475         if (join == join_pointy)
476         {
477             leftStNo = dest->AddPoint (pos + width * pnor);
478             leftEnNo = dest->AddPoint (pos + width * nnor);
480             const NR::Point biss = StrokeNormalize (pnor + nnor);
481             double c2 = dot (biss, nnor);
482             double l = width / c2;
483             double emiter = width * c2;
484             if (emiter < miter)
485                 emiter = miter;
486             if (l <= emiter)
487             {
488                 int nleftStNo = dest->AddPoint (pos + l * biss);
489                 int nEdge=dest->AddEdge (leftEnNo, nleftStNo);
490                 if ( dest->hasBackData() ) {
491                     dest->ebData[nEdge].pathID=pathID;
492                     dest->ebData[nEdge].pieceID=pieceID;
493                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
494                 }
495                 nEdge=dest->AddEdge (nleftStNo, leftStNo);
496                 if ( dest->hasBackData() ) {
497                     dest->ebData[nEdge].pathID=pathID;
498                     dest->ebData[nEdge].pieceID=pieceID;
499                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
500                 }
501             }
502             else
503             {
504                 double s2 = cross (biss, nnor);
505                 double dec = (l - emiter) * c2 / s2;
506                 const NR::Point tbiss=biss.ccw();
508                 int nleftStNo = dest->AddPoint (pos + emiter * biss + dec * tbiss);
509                 int nleftEnNo = dest->AddPoint (pos + emiter * biss - dec * tbiss);
510                 int nEdge=dest->AddEdge (nleftEnNo, nleftStNo);
511                 if ( dest->hasBackData() ) {
512                     dest->ebData[nEdge].pathID=pathID;
513                     dest->ebData[nEdge].pieceID=pieceID;
514                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
515                 }
516                 nEdge=dest->AddEdge (leftEnNo, nleftEnNo);
517                 if ( dest->hasBackData() ) {
518                     dest->ebData[nEdge].pathID=pathID;
519                     dest->ebData[nEdge].pieceID=pieceID;
520                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
521                 }
522                 nEdge=dest->AddEdge (nleftStNo, leftStNo);
523                 if ( dest->hasBackData() ) {
524                     dest->ebData[nEdge].pathID=pathID;
525                     dest->ebData[nEdge].pieceID=pieceID;
526                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
527                 }
528             }
529         }
530         else if (join == join_round)
531         {
532             const NR::Point sx = pos + width * pnor;
533             leftStNo = dest->AddPoint (sx);
534             const NR::Point ex = pos + width * nnor;
535             leftEnNo = dest->AddPoint (ex);
537             RecRound(dest, leftEnNo, leftStNo, 
538                     sx, ex, pnor, nnor ,pos, width);
540         }
541         else
542         {
543             leftStNo = dest->AddPoint (pos + width * pnor);
544             leftEnNo = dest->AddPoint (pos + width * nnor);
545             int nEdge=dest->AddEdge (leftEnNo, leftStNo);
546             if ( dest->hasBackData() ) {
547                 dest->ebData[nEdge].pathID=pathID;
548                 dest->ebData[nEdge].pieceID=pieceID;
549                 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
550             }
551         }
552     }
554     void
555 Path::DoRightJoin (Shape * dest, double width, JoinType join, NR::Point pos,
556         NR::Point prev, NR::Point next, double miter, double /*prevL*/,
557         double /*nextL*/, int &rightStNo, int &rightEnNo,int pathID,int pieceID,double tID)
559     const NR::Point pnor=prev.ccw();
560     const NR::Point nnor=next.ccw();
561     double angSi = cross (next,prev);
562     if (angSi > -0.0001 && angSi < 0.0001)
563     {
564         double angCo = dot (prev, next);
565         if (angCo > 0.9999)
566         {
567             // tout droit
568             rightEnNo = rightStNo = dest->AddPoint (pos - width*pnor);
569         }
570         else
571         {
572             // demi-tour
573             rightEnNo = dest->AddPoint (pos + width*pnor);
574             rightStNo = dest->AddPoint (pos - width*pnor);
575             int nEdge=dest->AddEdge (rightStNo, rightEnNo);
576             if ( dest->hasBackData() ) {
577                 dest->ebData[nEdge].pathID=pathID;
578                 dest->ebData[nEdge].pieceID=pieceID;
579                 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
580             }
581         }
582         return;
583     }
584     if (angSi < 0)
585     {
586         if (join == join_pointy)
587         {
588             rightStNo = dest->AddPoint (pos - width*pnor);
589             rightEnNo = dest->AddPoint (pos - width*nnor);
591             const NR::Point biss = StrokeNormalize (pnor + nnor);
592             double c2 = dot (biss, nnor);
593             double l = width / c2;
594             double emiter = width * c2;
595             if (emiter < miter)
596                 emiter = miter;
597             if (l <= emiter)
598             {
599                 int nrightStNo = dest->AddPoint (pos - l * biss);
600                 int nEdge=dest->AddEdge (rightStNo, nrightStNo);
601                 if ( dest->hasBackData() ) {
602                     dest->ebData[nEdge].pathID=pathID;
603                     dest->ebData[nEdge].pieceID=pieceID;
604                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
605                 }
606                 nEdge=dest->AddEdge (nrightStNo, rightEnNo);
607                 if ( dest->hasBackData() ) {
608                     dest->ebData[nEdge].pathID=pathID;
609                     dest->ebData[nEdge].pieceID=pieceID;
610                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
611                 }
612             }
613             else
614             {
615                 double s2 = cross (biss, nnor);
616                 double dec = (l - emiter) * c2 / s2;
617                 const NR::Point tbiss=biss.ccw();
619                 int nrightStNo = dest->AddPoint (pos - emiter*biss - dec*tbiss);
620                 int nrightEnNo = dest->AddPoint (pos - emiter*biss + dec*tbiss);
621                 int nEdge=dest->AddEdge (rightStNo, nrightStNo);
622                 if ( dest->hasBackData() ) {
623                     dest->ebData[nEdge].pathID=pathID;
624                     dest->ebData[nEdge].pieceID=pieceID;
625                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
626                 }
627                 nEdge=dest->AddEdge (nrightStNo, nrightEnNo);
628                 if ( dest->hasBackData() ) {
629                     dest->ebData[nEdge].pathID=pathID;
630                     dest->ebData[nEdge].pieceID=pieceID;
631                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
632                 }
633                 nEdge=dest->AddEdge (nrightEnNo, rightEnNo);
634                 if ( dest->hasBackData() ) {
635                     dest->ebData[nEdge].pathID=pathID;
636                     dest->ebData[nEdge].pieceID=pieceID;
637                     dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
638                 }
639             }
640         }
641         else if (join == join_round)
642         {
643             const NR::Point sx = pos - width * pnor;
644             rightStNo = dest->AddPoint (sx);
645             const NR::Point ex = pos - width * nnor;
646             rightEnNo = dest->AddPoint (ex);
648             RecRound(dest, rightStNo, rightEnNo, 
649                     sx, ex, -pnor, -nnor ,pos, width);
650         }
651         else
652         {
653             rightStNo = dest->AddPoint (pos - width * pnor);
654             rightEnNo = dest->AddPoint (pos - width * nnor);
655             int nEdge=dest->AddEdge (rightStNo, rightEnNo);
656             if ( dest->hasBackData() ) {
657                 dest->ebData[nEdge].pathID=pathID;
658                 dest->ebData[nEdge].pieceID=pieceID;
659                 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
660             }
661         }
662     }
663     else
664     {
665         /*              NR::Point     biss;
666                         biss=next.x-prev.x;
667                         biss.y=next.y-prev.y;
668                         double   c2=cross(next,biss);
669                         double   l=width/c2;
670                         double          projn=l*(dot(biss,next));
671                         double          projp=-l*(dot(biss,prev));
672                         if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) {
673                         double   x,y;
674                         x=pos.x+l*biss.x;
675                         y=pos.y+l*biss.y;
676                         rightEnNo=rightStNo=dest->AddPoint(x,y);
677                         } else {*/
678         rightStNo = dest->AddPoint (pos - width*pnor);
679         rightEnNo = dest->AddPoint (pos - width*nnor);
680         int midNo = dest->AddPoint (pos);
681         int nEdge=dest->AddEdge (rightStNo, midNo);
682         if ( dest->hasBackData() ) {
683             dest->ebData[nEdge].pathID=pathID;                                  
684             dest->ebData[nEdge].pieceID=pieceID;
685             dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
686         }
687         nEdge=dest->AddEdge (midNo, rightEnNo);
688         if ( dest->hasBackData() ) {
689             dest->ebData[nEdge].pathID=pathID;
690             dest->ebData[nEdge].pieceID=pieceID;
691             dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
692         }
693         //              }
694     }
698 // a very ugly way to produce round joins: doing one (or two, depend on the angle of the join) quadratic bezier curves
699 // but since most joins are going to be small, nobody will notice -- but somebody noticed and now the ugly stuff is gone! so:
701 // a very nice way to produce round joins, caps or dots
702 void Path::RecRound(Shape *dest, int sNo, int eNo, // start and end index
703         NR::Point const &iS, NR::Point const &iE, // start and end point
704         NR::Point const &nS, NR::Point const &nE, // start and end normal vector
705         NR::Point &origine, float width) // center and radius of round
707     //NR::Point diff = iS - iE;
708     //double dist = dot(diff, diff);
709     if (width < 0.5 || dot(iS - iE, iS - iE)/width < 2.0) {
710         dest->AddEdge(sNo, eNo);
711         return;
712     }
713     double ang, sia, lod;
714     if (nS == -nE) {
715         ang = M_PI;
716         sia = 1;
717     } else {
718         double coa = dot(nS, nE);
719         sia = cross(nS, nE);
720         ang = acos(coa);
721         if ( coa >= 1 ) {
722             ang = 0;
723         }
724         if ( coa <= -1 ) {
725             ang = M_PI;
726         }
727     }
728     lod = 0.02 + 10 / (10 + width); // limit detail to about 2 degrees (180 * 0.02/Pi degrees)
729     ang /= lod;
731     int nbS = (int) floor(ang);
732     Geom::Rotate omega(((sia > 0) ? -lod : lod));
733     NR::Point cur = iS - origine;
734     //  StrokeNormalize(cur);
735     //  cur*=width;
736     int lastNo = sNo;
737     for (int i = 0; i < nbS; i++) {
738         cur = cur * omega;
739         NR::Point m = origine + cur;
740         int mNo = dest->AddPoint(m);
741         dest->AddEdge(lastNo, mNo);
742         lastNo = mNo;
743     }
744     dest->AddEdge(lastNo, eNo);
747 /*
748    Local Variables:
749 mode:c++
750 c-file-style:"stroustrup"
751 c-file-offsets:((innamespace . 0)(inline-open . 0))
752 indent-tabs-mode:nil
753 fill-column:99
754 End:
755  */
756 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :