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