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