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 }
239 }
242 void Path::DoButt(Shape *dest, double width, ButtType butt, Geom::Point pos, Geom::Point dir,
243 int &leftNo, int &rightNo)
244 {
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 }
286 }
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)
292 {
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 }
410 }
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)
416 {
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 }
554 }
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)
559 {
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 }
696 }
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
707 {
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);
746 }
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 :