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 if ( pts[lastP - 1].closed ) {
67 DoStroke(lastM, lastP - lastM, dest, true, width, join, butt, miter, true);
68 } else {
69 DoStroke(lastM, lastP - lastM, dest, doClose, width, join, butt, miter, true);
70 }
71 } else if (butt == butt_round) { // special case: zero length round butt is a circle
72 int last[2] = { -1, -1 };
73 Geom::Point dir;
74 dir[0] = 1;
75 dir[1] = 0;
76 Geom::Point pos = pts[lastM].p;
77 DoButt(dest, width, butt, pos, dir, last[RIGHT], last[LEFT]);
78 int end[2];
79 dir = -dir;
80 DoButt(dest, width, butt, pos, dir, end[LEFT], end[RIGHT]);
81 dest->AddEdge (end[LEFT], last[LEFT]);
82 dest->AddEdge (last[RIGHT], end[RIGHT]);
83 }
84 lastM = lastP;
85 }
86 }
88 void Path::DoStroke(int off, int N, Shape *dest, bool doClose, double width, JoinType join,
89 ButtType butt, double miter, bool /*justAdd*/)
90 {
91 if (N <= 1) {
92 return;
93 }
95 Geom::Point prevP, nextP;
96 int prevI, nextI;
97 int upTo;
99 int curI = 0;
100 Geom::Point curP = pts[off].p;
102 if (doClose) {
104 prevI = N - 1;
105 while (prevI > 0) {
106 prevP = pts[off + prevI].p;
107 Geom::Point diff = curP - prevP;
108 double dist = dot(diff, diff);
109 if (dist > 0.001) {
110 break;
111 }
112 prevI--;
113 }
114 if (prevI <= 0) {
115 return;
116 }
117 upTo = prevI;
119 } else {
121 prevP = curP;
122 prevI = curI;
123 upTo = N - 1;
124 }
126 {
127 nextI = 1;
128 while (nextI <= upTo) {
129 nextP = pts[off + nextI].p;
130 Geom::Point diff = curP - nextP;
131 double dist = dot(diff, diff);
132 if (dist > 0.0) { // more tolerance for the first distance, to give the cap the right direction
133 break;
134 }
135 nextI++;
136 }
137 if (nextI > upTo) {
138 if (butt == butt_round) { // special case: zero length round butt is a circle
139 int last[2] = { -1, -1 };
140 Geom::Point dir;
141 dir[0] = 1;
142 dir[1] = 0;
143 DoButt(dest, width, butt, curP, dir, last[RIGHT], last[LEFT]);
144 int end[2];
145 dir = -dir;
146 DoButt(dest, width, butt, curP, dir, end[LEFT], end[RIGHT]);
147 dest->AddEdge (end[LEFT], last[LEFT]);
148 dest->AddEdge (last[RIGHT], end[RIGHT]);
149 }
150 return;
151 }
152 }
154 int start[2] = { -1, -1 };
155 int last[2] = { -1, -1 };
156 Geom::Point prevD = curP - prevP;
157 Geom::Point nextD = nextP - curP;
158 double prevLe = Geom::L2(prevD);
159 double nextLe = Geom::L2(nextD);
160 prevD = StrokeNormalize(prevD, prevLe);
161 nextD = StrokeNormalize(nextD, nextLe);
163 if (doClose) {
164 DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, start, last);
165 } else {
166 nextD = -nextD;
167 DoButt(dest, width, butt, curP, nextD, last[RIGHT], last[LEFT]);
168 nextD = -nextD;
169 }
171 do {
172 prevP = curP;
173 prevI = curI;
174 curP = nextP;
175 curI = nextI;
176 prevD = nextD;
177 prevLe = nextLe;
178 nextI++;
179 while (nextI <= upTo) {
180 nextP = pts[off + nextI].p;
181 Geom::Point diff = curP - nextP;
182 double dist = dot(diff, diff);
183 if (dist > 0.001 || (nextI == upTo && dist > 0.0)) { // more tolerance for the last distance too, for the right cap direction
184 break;
185 }
186 nextI++;
187 }
188 if (nextI > upTo) {
189 break;
190 }
192 nextD = nextP - curP;
193 nextLe = Geom::L2(nextD);
194 nextD = StrokeNormalize(nextD, nextLe);
195 int nSt[2] = { -1, -1 };
196 int nEn[2] = { -1, -1 };
197 DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn);
198 dest->AddEdge(nSt[LEFT], last[LEFT]);
199 last[LEFT] = nEn[LEFT];
200 dest->AddEdge(last[RIGHT], nSt[RIGHT]);
201 last[RIGHT] = nEn[RIGHT];
202 } while (nextI <= upTo);
204 if (doClose) {
205 /* prevP=curP;
206 prevI=curI;
207 curP=nextP;
208 curI=nextI;
209 prevD=nextD;*/
210 nextP = pts[off].p;
212 nextD = nextP - curP;
213 nextLe = Geom::L2(nextD);
214 nextD = StrokeNormalize(nextD, nextLe);
215 int nSt[2] = { -1, -1 };
216 int nEn[2] = { -1, -1 };
217 DoJoin(dest, width, join, curP, prevD, nextD, miter, prevLe, nextLe, nSt, nEn);
218 dest->AddEdge (nSt[LEFT], last[LEFT]);
219 last[LEFT] = nEn[LEFT];
220 dest->AddEdge (last[RIGHT], nSt[RIGHT]);
221 last[RIGHT] = nEn[RIGHT];
223 dest->AddEdge (start[LEFT], last[LEFT]);
224 dest->AddEdge (last[RIGHT], start[RIGHT]);
226 } else {
228 int end[2];
229 DoButt (dest, width, butt, curP, prevD, end[LEFT], end[RIGHT]);
230 dest->AddEdge (end[LEFT], last[LEFT]);
231 dest->AddEdge (last[RIGHT], end[RIGHT]);
232 }
233 }
236 void Path::DoButt(Shape *dest, double width, ButtType butt, Geom::Point pos, Geom::Point dir,
237 int &leftNo, int &rightNo)
238 {
239 Geom::Point nor;
240 nor = dir.ccw();
242 if (butt == butt_square)
243 {
244 Geom::Point x;
245 x = pos + width * dir + width * nor;
246 int bleftNo = dest->AddPoint (x);
247 x = pos + width * dir - width * nor;
248 int brightNo = dest->AddPoint (x);
249 x = pos + width * nor;
250 leftNo = dest->AddPoint (x);
251 x = pos - width * nor;
252 rightNo = dest->AddPoint (x);
253 dest->AddEdge (rightNo, brightNo);
254 dest->AddEdge (brightNo, bleftNo);
255 dest->AddEdge (bleftNo, leftNo);
256 }
257 else if (butt == butt_pointy)
258 {
259 leftNo = dest->AddPoint (pos + width * nor);
260 rightNo = dest->AddPoint (pos - width * nor);
261 int mid = dest->AddPoint (pos + width * dir);
262 dest->AddEdge (rightNo, mid);
263 dest->AddEdge (mid, leftNo);
264 }
265 else if (butt == butt_round)
266 {
267 const Geom::Point sx = pos + width * nor;
268 const Geom::Point ex = pos - width * nor;
269 leftNo = dest->AddPoint (sx);
270 rightNo = dest->AddPoint (ex);
272 RecRound (dest, rightNo, leftNo, ex, sx, -nor, nor, pos, width);
273 }
274 else
275 {
276 leftNo = dest->AddPoint (pos + width * nor);
277 rightNo = dest->AddPoint (pos - width * nor);
278 dest->AddEdge (rightNo, leftNo);
279 }
280 }
283 void Path::DoJoin (Shape *dest, double width, JoinType join, Geom::Point pos, Geom::Point prev,
284 Geom::Point next, double miter, double /*prevL*/, double /*nextL*/,
285 int *stNo, int *enNo)
286 {
287 Geom::Point pnor = prev.ccw();
288 Geom::Point nnor = next.ccw();
289 double angSi = cross(next, prev);
291 /* FIXED: this special case caused bug 1028953 */
292 if (angSi > -0.0001 && angSi < 0.0001) {
293 double angCo = dot (prev, next);
294 if (angCo > 0.9999) {
295 // tout droit
296 stNo[LEFT] = enNo[LEFT] = dest->AddPoint(pos + width * pnor);
297 stNo[RIGHT] = enNo[RIGHT] = dest->AddPoint(pos - width * pnor);
298 } else {
299 // demi-tour
300 const Geom::Point sx = pos + width * pnor;
301 const Geom::Point ex = pos - width * pnor;
302 stNo[LEFT] = enNo[RIGHT] = dest->AddPoint (sx);
303 stNo[RIGHT] = enNo[LEFT] = dest->AddPoint (ex);
304 if (join == join_round) {
305 RecRound (dest, enNo[LEFT], stNo[LEFT], ex, sx, -pnor, pnor, pos, width);
306 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
307 } else {
308 dest->AddEdge(enNo[LEFT], stNo[LEFT]);
309 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]); // two times because both are crossing each other
310 }
311 }
312 return;
313 }
315 if (angSi < 0) {
316 int midNo = dest->AddPoint(pos);
317 stNo[LEFT] = dest->AddPoint(pos + width * pnor);
318 enNo[LEFT] = dest->AddPoint(pos + width * nnor);
319 dest->AddEdge(enNo[LEFT], midNo);
320 dest->AddEdge(midNo, stNo[LEFT]);
322 if (join == join_pointy) {
324 stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
325 enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
327 const Geom::Point biss = StrokeNormalize(prev - next);
328 double c2 = dot(biss, nnor);
329 double l = width / c2;
330 double emiter = width * c2;
331 if (emiter < miter) {
332 emiter = miter;
333 }
335 if (fabs(l) < miter) {
336 int const n = dest->AddPoint(pos - l * biss);
337 dest->AddEdge(stNo[RIGHT], n);
338 dest->AddEdge(n, enNo[RIGHT]);
339 } else {
340 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
341 }
343 } else if (join == join_round) {
344 Geom::Point sx = pos - width * pnor;
345 stNo[RIGHT] = dest->AddPoint(sx);
346 Geom::Point ex = pos - width * nnor;
347 enNo[RIGHT] = dest->AddPoint(ex);
349 RecRound(dest, stNo[RIGHT], enNo[RIGHT],
350 sx, ex, -pnor, -nnor, pos, width);
352 } else {
353 stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
354 enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
355 dest->AddEdge(stNo[RIGHT], enNo[RIGHT]);
356 }
358 } else {
360 int midNo = dest->AddPoint(pos);
361 stNo[RIGHT] = dest->AddPoint(pos - width * pnor);
362 enNo[RIGHT] = dest->AddPoint(pos - width * nnor);
363 dest->AddEdge(stNo[RIGHT], midNo);
364 dest->AddEdge(midNo, enNo[RIGHT]);
366 if (join == join_pointy) {
368 stNo[LEFT] = dest->AddPoint(pos + width * pnor);
369 enNo[LEFT] = dest->AddPoint(pos + width * nnor);
371 const Geom::Point biss = StrokeNormalize(next - prev);
372 double c2 = dot(biss, nnor);
373 double l = width / c2;
374 double emiter = width * c2;
375 if (emiter < miter) {
376 emiter = miter;
377 }
378 if ( fabs(l) < miter) {
379 int const n = dest->AddPoint (pos + l * biss);
380 dest->AddEdge (enNo[LEFT], n);
381 dest->AddEdge (n, stNo[LEFT]);
382 }
383 else
384 {
385 dest->AddEdge (enNo[LEFT], stNo[LEFT]);
386 }
388 } else if (join == join_round) {
390 Geom::Point sx = pos + width * pnor;
391 stNo[LEFT] = dest->AddPoint(sx);
392 Geom::Point ex = pos + width * nnor;
393 enNo[LEFT] = dest->AddPoint(ex);
395 RecRound(dest, enNo[LEFT], stNo[LEFT],
396 ex, sx, nnor, pnor, pos, width);
398 } else {
399 stNo[LEFT] = dest->AddPoint(pos + width * pnor);
400 enNo[LEFT] = dest->AddPoint(pos + width * nnor);
401 dest->AddEdge(enNo[LEFT], stNo[LEFT]);
402 }
403 }
404 }
406 void
407 Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
408 Geom::Point prev, Geom::Point next, double miter, double /*prevL*/, double /*nextL*/,
409 int &leftStNo, int &leftEnNo,int pathID,int pieceID,double tID)
410 {
411 Geom::Point pnor=prev.ccw();
412 Geom::Point nnor=next.ccw();
413 double angSi = cross (next, prev);
414 if (angSi > -0.0001 && angSi < 0.0001)
415 {
416 double angCo = dot (prev, next);
417 if (angCo > 0.9999)
418 {
419 // tout droit
420 leftEnNo = leftStNo = dest->AddPoint (pos + width * pnor);
421 }
422 else
423 {
424 // demi-tour
425 leftStNo = dest->AddPoint (pos + width * pnor);
426 leftEnNo = dest->AddPoint (pos - width * pnor);
427 int nEdge=dest->AddEdge (leftEnNo, leftStNo);
428 if ( dest->hasBackData() ) {
429 dest->ebData[nEdge].pathID=pathID;
430 dest->ebData[nEdge].pieceID=pieceID;
431 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
432 }
433 }
434 return;
435 }
436 if (angSi < 0)
437 {
438 /* Geom::Point biss;
439 biss.x=next.x-prev.x;
440 biss.y=next.y-prev.y;
441 double c2=cross(biss,next);
442 double l=width/c2;
443 double projn=l*(dot(biss,next));
444 double projp=-l*(dot(biss,prev));
445 if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) {
446 double x,y;
447 x=pos.x+l*biss.x;
448 y=pos.y+l*biss.y;
449 leftEnNo=leftStNo=dest->AddPoint(x,y);
450 } else {*/
451 leftStNo = dest->AddPoint (pos + width * pnor);
452 leftEnNo = dest->AddPoint (pos + width * nnor);
453 int midNo = dest->AddPoint (pos);
454 int nEdge=dest->AddEdge (leftEnNo, midNo);
455 if ( dest->hasBackData() ) {
456 dest->ebData[nEdge].pathID=pathID;
457 dest->ebData[nEdge].pieceID=pieceID;
458 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
459 }
460 nEdge=dest->AddEdge (midNo, leftStNo);
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 // }
467 }
468 else
469 {
470 if (join == join_pointy)
471 {
472 leftStNo = dest->AddPoint (pos + width * pnor);
473 leftEnNo = dest->AddPoint (pos + width * nnor);
475 const Geom::Point biss = StrokeNormalize (pnor + nnor);
476 double c2 = dot (biss, nnor);
477 double l = width / c2;
478 double emiter = width * c2;
479 if (emiter < miter)
480 emiter = miter;
481 if (l <= emiter)
482 {
483 int nleftStNo = dest->AddPoint (pos + l * biss);
484 int nEdge=dest->AddEdge (leftEnNo, nleftStNo);
485 if ( dest->hasBackData() ) {
486 dest->ebData[nEdge].pathID=pathID;
487 dest->ebData[nEdge].pieceID=pieceID;
488 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
489 }
490 nEdge=dest->AddEdge (nleftStNo, leftStNo);
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 }
497 else
498 {
499 double s2 = cross (biss, nnor);
500 double dec = (l - emiter) * c2 / s2;
501 const Geom::Point tbiss=biss.ccw();
503 int nleftStNo = dest->AddPoint (pos + emiter * biss + dec * tbiss);
504 int nleftEnNo = dest->AddPoint (pos + emiter * biss - dec * tbiss);
505 int nEdge=dest->AddEdge (nleftEnNo, nleftStNo);
506 if ( dest->hasBackData() ) {
507 dest->ebData[nEdge].pathID=pathID;
508 dest->ebData[nEdge].pieceID=pieceID;
509 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
510 }
511 nEdge=dest->AddEdge (leftEnNo, nleftEnNo);
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 (nleftStNo, leftStNo);
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 }
524 }
525 else if (join == join_round)
526 {
527 const Geom::Point sx = pos + width * pnor;
528 leftStNo = dest->AddPoint (sx);
529 const Geom::Point ex = pos + width * nnor;
530 leftEnNo = dest->AddPoint (ex);
532 RecRound(dest, leftEnNo, leftStNo,
533 sx, ex, pnor, nnor ,pos, width);
535 }
536 else
537 {
538 leftStNo = dest->AddPoint (pos + width * pnor);
539 leftEnNo = dest->AddPoint (pos + width * nnor);
540 int nEdge=dest->AddEdge (leftEnNo, leftStNo);
541 if ( dest->hasBackData() ) {
542 dest->ebData[nEdge].pathID=pathID;
543 dest->ebData[nEdge].pieceID=pieceID;
544 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
545 }
546 }
547 }
548 }
549 void
550 Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
551 Geom::Point prev, Geom::Point next, double miter, double /*prevL*/,
552 double /*nextL*/, int &rightStNo, int &rightEnNo,int pathID,int pieceID,double tID)
553 {
554 const Geom::Point pnor=prev.ccw();
555 const Geom::Point nnor=next.ccw();
556 double angSi = cross (next,prev);
557 if (angSi > -0.0001 && angSi < 0.0001)
558 {
559 double angCo = dot (prev, next);
560 if (angCo > 0.9999)
561 {
562 // tout droit
563 rightEnNo = rightStNo = dest->AddPoint (pos - width*pnor);
564 }
565 else
566 {
567 // demi-tour
568 rightEnNo = dest->AddPoint (pos + width*pnor);
569 rightStNo = dest->AddPoint (pos - width*pnor);
570 int nEdge=dest->AddEdge (rightStNo, rightEnNo);
571 if ( dest->hasBackData() ) {
572 dest->ebData[nEdge].pathID=pathID;
573 dest->ebData[nEdge].pieceID=pieceID;
574 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
575 }
576 }
577 return;
578 }
579 if (angSi < 0)
580 {
581 if (join == join_pointy)
582 {
583 rightStNo = dest->AddPoint (pos - width*pnor);
584 rightEnNo = dest->AddPoint (pos - width*nnor);
586 const Geom::Point biss = StrokeNormalize (pnor + nnor);
587 double c2 = dot (biss, nnor);
588 double l = width / c2;
589 double emiter = width * c2;
590 if (emiter < miter)
591 emiter = miter;
592 if (l <= emiter)
593 {
594 int nrightStNo = dest->AddPoint (pos - l * biss);
595 int nEdge=dest->AddEdge (rightStNo, nrightStNo);
596 if ( dest->hasBackData() ) {
597 dest->ebData[nEdge].pathID=pathID;
598 dest->ebData[nEdge].pieceID=pieceID;
599 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
600 }
601 nEdge=dest->AddEdge (nrightStNo, rightEnNo);
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 }
608 else
609 {
610 double s2 = cross (biss, nnor);
611 double dec = (l - emiter) * c2 / s2;
612 const Geom::Point tbiss=biss.ccw();
614 int nrightStNo = dest->AddPoint (pos - emiter*biss - dec*tbiss);
615 int nrightEnNo = dest->AddPoint (pos - emiter*biss + dec*tbiss);
616 int nEdge=dest->AddEdge (rightStNo, nrightStNo);
617 if ( dest->hasBackData() ) {
618 dest->ebData[nEdge].pathID=pathID;
619 dest->ebData[nEdge].pieceID=pieceID;
620 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
621 }
622 nEdge=dest->AddEdge (nrightStNo, nrightEnNo);
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 (nrightEnNo, rightEnNo);
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 }
635 }
636 else if (join == join_round)
637 {
638 const Geom::Point sx = pos - width * pnor;
639 rightStNo = dest->AddPoint (sx);
640 const Geom::Point ex = pos - width * nnor;
641 rightEnNo = dest->AddPoint (ex);
643 RecRound(dest, rightStNo, rightEnNo,
644 sx, ex, -pnor, -nnor ,pos, width);
645 }
646 else
647 {
648 rightStNo = dest->AddPoint (pos - width * pnor);
649 rightEnNo = dest->AddPoint (pos - width * nnor);
650 int nEdge=dest->AddEdge (rightStNo, rightEnNo);
651 if ( dest->hasBackData() ) {
652 dest->ebData[nEdge].pathID=pathID;
653 dest->ebData[nEdge].pieceID=pieceID;
654 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
655 }
656 }
657 }
658 else
659 {
660 /* Geom::Point biss;
661 biss=next.x-prev.x;
662 biss.y=next.y-prev.y;
663 double c2=cross(next,biss);
664 double l=width/c2;
665 double projn=l*(dot(biss,next));
666 double projp=-l*(dot(biss,prev));
667 if ( projp <= 0.5*prevL && projn <= 0.5*nextL ) {
668 double x,y;
669 x=pos.x+l*biss.x;
670 y=pos.y+l*biss.y;
671 rightEnNo=rightStNo=dest->AddPoint(x,y);
672 } else {*/
673 rightStNo = dest->AddPoint (pos - width*pnor);
674 rightEnNo = dest->AddPoint (pos - width*nnor);
675 int midNo = dest->AddPoint (pos);
676 int nEdge=dest->AddEdge (rightStNo, midNo);
677 if ( dest->hasBackData() ) {
678 dest->ebData[nEdge].pathID=pathID;
679 dest->ebData[nEdge].pieceID=pieceID;
680 dest->ebData[nEdge].tSt=dest->ebData[nEdge].tEn=tID;
681 }
682 nEdge=dest->AddEdge (midNo, rightEnNo);
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 // }
689 }
690 }
693 // a very ugly way to produce round joins: doing one (or two, depend on the angle of the join) quadratic bezier curves
694 // but since most joins are going to be small, nobody will notice -- but somebody noticed and now the ugly stuff is gone! so:
696 // a very nice way to produce round joins, caps or dots
697 void Path::RecRound(Shape *dest, int sNo, int eNo, // start and end index
698 Geom::Point const &iS, Geom::Point const &iE, // start and end point
699 Geom::Point const &nS, Geom::Point const &nE, // start and end normal vector
700 Geom::Point &origine, float width) // center and radius of round
701 {
702 //Geom::Point diff = iS - iE;
703 //double dist = dot(diff, diff);
704 if (width < 0.5 || dot(iS - iE, iS - iE)/width < 2.0) {
705 dest->AddEdge(sNo, eNo);
706 return;
707 }
708 double ang, sia, lod;
709 if (nS == -nE) {
710 ang = M_PI;
711 sia = 1;
712 } else {
713 double coa = dot(nS, nE);
714 sia = cross(nS, nE);
715 ang = acos(coa);
716 if ( coa >= 1 ) {
717 ang = 0;
718 }
719 if ( coa <= -1 ) {
720 ang = M_PI;
721 }
722 }
723 lod = 0.02 + 10 / (10 + width); // limit detail to about 2 degrees (180 * 0.02/Pi degrees)
724 ang /= lod;
726 int nbS = (int) floor(ang);
727 Geom::Rotate omega(((sia > 0) ? -lod : lod));
728 Geom::Point cur = iS - origine;
729 // StrokeNormalize(cur);
730 // cur*=width;
731 int lastNo = sNo;
732 for (int i = 0; i < nbS; i++) {
733 cur = cur * omega;
734 Geom::Point m = origine + cur;
735 int mNo = dest->AddPoint(m);
736 dest->AddEdge(lastNo, mNo);
737 lastNo = mNo;
738 }
739 dest->AddEdge(lastNo, eNo);
740 }
742 /*
743 Local Variables:
744 mode:c++
745 c-file-style:"stroustrup"
746 c-file-offsets:((innamespace . 0)(inline-open . 0))
747 indent-tabs-mode:nil
748 fill-column:99
749 End:
750 */
751 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :