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 <libnr/nr-rotate-ops.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 }
238 }
241 void Path::DoButt(Shape *dest, double width, ButtType butt, NR::Point pos, NR::Point dir,
242 int &leftNo, int &rightNo)
243 {
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 }
285 }
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)
291 {
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 }
409 }
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)
415 {
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 }
553 }
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)
558 {
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 }
695 }
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
706 {
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 NR::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);
745 }
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 :