1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** @file
3 * TODO: insert short description here
4 *//*
5 * Authors: see git history
6 *
7 * Copyright (C) 2018 Authors
8 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
9 */
10
11 #include "livarot/Path.h"
12 #include "livarot/path-description.h"
13
14 /*
15 * the "outliner"
16 * takes a sequence of path commands and produces a set of commands that approximates the offset
17 * result is stored in dest (that paremeter is handed to all the subfunctions)
18 * not that the result is in general not mathematically correct; you can end up with unwanted holes in your
19 * beautiful offset. a better way is to do path->polyline->polygon->offset of polygon->polyline(=contours of the polygon)->path
20 * but computing offsets of the path is faster...
21 */
22
23 // outline of a path.
24 // computed by making 2 offsets, one of the "left" side of the path, one of the right side, and then glueing the two
25 // the left side has to be reversed to make a contour
Outline(Path * dest,double width,JoinType join,ButtType butt,double miter)26 void Path::Outline(Path *dest, double width, JoinType join, ButtType butt, double miter)
27 {
28 if ( descr_flags & descr_adding_bezier ) {
29 CancelBezier();
30 }
31 if ( descr_flags & descr_doing_subpath ) {
32 CloseSubpath();
33 }
34 if ( descr_cmd.size() <= 1 ) {
35 return;
36 }
37 if ( dest == nullptr ) {
38 return;
39 }
40
41 dest->Reset();
42 dest->SetBackData(false);
43
44 outline_callbacks calls;
45 Geom::Point endButt;
46 Geom::Point endPos;
47 calls.cubicto = StdCubicTo;
48 calls.bezierto = StdBezierTo;
49 calls.arcto = StdArcTo;
50
51 Path *rev = new Path;
52
53 // we repeat the offset contour creation for each subpath
54 int curP = 0;
55 do {
56 int lastM = curP;
57 do {
58 curP++;
59 if (curP >= int(descr_cmd.size())) {
60 break;
61 }
62 int typ = descr_cmd[curP]->getType();
63 if (typ == descr_moveto) {
64 break;
65 }
66 } while (curP < int(descr_cmd.size()));
67
68 if (curP >= int(descr_cmd.size())) {
69 curP = descr_cmd.size();
70 }
71
72 if (curP > lastM + 1) {
73 // we have isolated a subpath, now we make a reversed version of it
74 // we do so by taking the subpath in the reverse and constructing a path as appropriate
75 // the construct is stored in "rev"
76 int curD = curP - 1;
77 Geom::Point curX;
78 Geom::Point nextX;
79 int firstTyp = descr_cmd[curD]->getType();
80 bool const needClose = (firstTyp == descr_close);
81 while (curD > lastM && descr_cmd[curD]->getType() == descr_close) {
82 curD--;
83 }
84
85 int realP = curD + 1;
86 if (curD > lastM) {
87 curX = PrevPoint(curD);
88 rev->Reset ();
89 rev->MoveTo(curX);
90 while (curD > lastM) {
91 int const typ = descr_cmd[curD]->getType();
92 if (typ == descr_moveto) {
93 // rev->Close();
94 curD--;
95 } else if (typ == descr_forced) {
96 // rev->Close();
97 curD--;
98 } else if (typ == descr_lineto) {
99 nextX = PrevPoint (curD - 1);
100 rev->LineTo (nextX);
101 curX = nextX;
102 curD--;
103 } else if (typ == descr_cubicto) {
104 PathDescrCubicTo* nData = dynamic_cast<PathDescrCubicTo*>(descr_cmd[curD]);
105 nextX = PrevPoint (curD - 1);
106 Geom::Point isD=-nData->start;
107 Geom::Point ieD=-nData->end;
108 rev->CubicTo (nextX, ieD,isD);
109 curX = nextX;
110 curD--;
111 } else if (typ == descr_arcto) {
112 PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(descr_cmd[curD]);
113 nextX = PrevPoint (curD - 1);
114 rev->ArcTo (nextX, nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
115 curX = nextX;
116 curD--;
117 } else if (typ == descr_bezierto) {
118 nextX = PrevPoint (curD - 1);
119 rev->LineTo (nextX);
120 curX = nextX;
121 curD--;
122 } else if (typ == descr_interm_bezier) {
123 int nD = curD - 1;
124 while (nD > lastM && descr_cmd[nD]->getType() != descr_bezierto) nD--;
125 if ((descr_cmd[nD]->getType()) != descr_bezierto) {
126 // pas trouve le debut!?
127 // Not find the start?!
128 nextX = PrevPoint (nD);
129 rev->LineTo (nextX);
130 curX = nextX;
131 } else {
132 nextX = PrevPoint (nD - 1);
133 rev->BezierTo (nextX);
134 for (int i = curD; i > nD; i--) {
135 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[i]);
136 rev->IntermBezierTo (nData->p);
137 }
138 rev->EndBezierTo ();
139 curX = nextX;
140 }
141 curD = nD - 1;
142 } else {
143 curD--;
144 }
145 }
146
147 // offset the paths and glue everything
148 // actual offseting is done in SubContractOutline()
149 if (needClose) {
150 rev->Close ();
151 rev->SubContractOutline (0, rev->descr_cmd.size(),
152 dest, calls, 0.0025 * width * width, width,
153 join, butt, miter, true, false, endPos, endButt);
154 SubContractOutline (lastM, realP + 1 - lastM,
155 dest, calls, 0.0025 * width * width,
156 width, join, butt, miter, true, false, endPos, endButt);
157 } else {
158 rev->SubContractOutline (0, rev->descr_cmd.size(),
159 dest, calls, 0.0025 * width * width, width,
160 join, butt, miter, false, false, endPos, endButt);
161 Geom::Point endNor=endButt.ccw();
162 if (butt == butt_round) {
163 dest->ArcTo (endPos+width*endButt, width, width, 0.0, false, true);
164 dest->ArcTo (endPos+width*endNor, width, width, 0.0, false, true);
165 } else if (butt == butt_square) {
166 dest->LineTo (endPos-width*endNor+width*endButt);
167 dest->LineTo (endPos+width*endNor+width*endButt);
168 dest->LineTo (endPos+width*endNor);
169 } else if (butt == butt_pointy) {
170 dest->LineTo (endPos+width*endButt);
171 dest->LineTo (endPos+width*endNor);
172 } else {
173 dest->LineTo (endPos+width*endNor);
174 }
175 SubContractOutline (lastM, realP - lastM,
176 dest, calls, 0.0025 * width * width, width, join, butt,
177 miter, false, true, endPos, endButt);
178
179 endNor=endButt.ccw();
180 if (butt == butt_round) {
181 dest->ArcTo (endPos+width*endButt, width, width, 0.0, false, true);
182 dest->ArcTo (endPos+width*endNor, width, width, 0.0, false, true);
183 } else if (butt == butt_square) {
184 dest->LineTo (endPos-width*endNor+width*endButt);
185 dest->LineTo (endPos+width*endNor+width*endButt);
186 dest->LineTo (endPos+width*endNor);
187 } else if (butt == butt_pointy) {
188 dest->LineTo (endPos+width*endButt);
189 dest->LineTo (endPos+width*endNor);
190 } else {
191 dest->LineTo (endPos+width*endNor);
192 }
193 dest->Close ();
194 }
195 } // if (curD > lastM)
196 } // if (curP > lastM + 1)
197
198 } while (curP < int(descr_cmd.size()));
199
200 delete rev;
201 }
202
203 // versions for outlining closed path: they only make one side of the offset contour
204 void
OutsideOutline(Path * dest,double width,JoinType join,ButtType butt,double miter)205 Path::OutsideOutline (Path * dest, double width, JoinType join, ButtType butt,
206 double miter)
207 {
208 if (descr_flags & descr_adding_bezier) {
209 CancelBezier();
210 }
211 if (descr_flags & descr_doing_subpath) {
212 CloseSubpath();
213 }
214 if (int(descr_cmd.size()) <= 1) return;
215 if (dest == nullptr) return;
216 dest->Reset ();
217 dest->SetBackData (false);
218
219 outline_callbacks calls;
220 Geom::Point endButt, endPos;
221 calls.cubicto = StdCubicTo;
222 calls.bezierto = StdBezierTo;
223 calls.arcto = StdArcTo;
224 SubContractOutline (0, descr_cmd.size(),
225 dest, calls, 0.0025 * width * width, width, join, butt,
226 miter, true, false, endPos, endButt);
227 }
228
229 void
InsideOutline(Path * dest,double width,JoinType join,ButtType butt,double miter)230 Path::InsideOutline (Path * dest, double width, JoinType join, ButtType butt,
231 double miter)
232 {
233 if ( descr_flags & descr_adding_bezier ) {
234 CancelBezier();
235 }
236 if ( descr_flags & descr_doing_subpath ) {
237 CloseSubpath();
238 }
239 if (int(descr_cmd.size()) <= 1) return;
240 if (dest == nullptr) return;
241 dest->Reset ();
242 dest->SetBackData (false);
243
244 outline_callbacks calls;
245 Geom::Point endButt, endPos;
246 calls.cubicto = StdCubicTo;
247 calls.bezierto = StdBezierTo;
248 calls.arcto = StdArcTo;
249
250 Path *rev = new Path;
251
252 int curP = 0;
253 do {
254 int lastM = curP;
255 do {
256 curP++;
257 if (curP >= int(descr_cmd.size())) break;
258 int typ = descr_cmd[curP]->getType();
259 if (typ == descr_moveto) break;
260 } while (curP < int(descr_cmd.size()));
261 if (curP >= int(descr_cmd.size())) curP = descr_cmd.size();
262 if (curP > lastM + 1) {
263 // Otherwise there's only one point. (tr: or "only a point")
264 // [sinon il n'y a qu'un point]
265 int curD = curP - 1;
266 Geom::Point curX;
267 Geom::Point nextX;
268 while (curD > lastM && (descr_cmd[curD]->getType()) == descr_close) curD--;
269 if (curD > lastM) {
270 curX = PrevPoint (curD);
271 rev->Reset ();
272 rev->MoveTo (curX);
273 while (curD > lastM) {
274 int typ = descr_cmd[curD]->getType();
275 if (typ == descr_moveto) {
276 rev->Close ();
277 curD--;
278 } else if (typ == descr_forced) {
279 curD--;
280 } else if (typ == descr_lineto) {
281 nextX = PrevPoint (curD - 1);
282 rev->LineTo (nextX);
283 curX = nextX;
284 curD--;
285 } else if (typ == descr_cubicto) {
286 PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo*>(descr_cmd[curD]);
287 nextX = PrevPoint (curD - 1);
288 Geom::Point isD=-nData->start;
289 Geom::Point ieD=-nData->end;
290 rev->CubicTo (nextX, ieD,isD);
291 curX = nextX;
292 curD--;
293 } else if (typ == descr_arcto) {
294 PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(descr_cmd[curD]);
295 nextX = PrevPoint (curD - 1);
296 rev->ArcTo (nextX, nData->rx,nData->ry,nData->angle,nData->large,nData->clockwise);
297 curX = nextX;
298 curD--;
299 } else if (typ == descr_bezierto) {
300 nextX = PrevPoint (curD - 1);
301 rev->LineTo (nextX);
302 curX = nextX;
303 curD--;
304 } else if (typ == descr_interm_bezier) {
305 int nD = curD - 1;
306 while (nD > lastM && (descr_cmd[nD]->getType()) != descr_bezierto) nD--;
307 if (descr_cmd[nD]->getType() != descr_bezierto) {
308 // pas trouve le debut!?
309 nextX = PrevPoint (nD);
310 rev->LineTo (nextX);
311 curX = nextX;
312 } else {
313 nextX = PrevPoint (nD - 1);
314 rev->BezierTo (nextX);
315 for (int i = curD; i > nD; i--) {
316 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[i]);
317 rev->IntermBezierTo (nData->p);
318 }
319 rev->EndBezierTo ();
320 curX = nextX;
321 }
322 curD = nD - 1;
323 } else {
324 curD--;
325 }
326 }
327 rev->Close ();
328 rev->SubContractOutline (0, rev->descr_cmd.size(),
329 dest, calls, 0.0025 * width * width,
330 width, join, butt, miter, true, false,
331 endPos, endButt);
332 }
333 }
334 } while (curP < int(descr_cmd.size()));
335
336 delete rev;
337 }
338
339
340 // the offset
341 // take each command and offset it.
342 // the bezier spline is split in a sequence of bezier curves, and these are transformed in cubic bezier (which is
343 // not hard since they are quadratic bezier)
344 // joins are put where needed
SubContractOutline(int off,int num_pd,Path * dest,outline_callbacks & calls,double tolerance,double width,JoinType join,ButtType,double miter,bool closeIfNeeded,bool skipMoveto,Geom::Point & lastP,Geom::Point & lastT)345 void Path::SubContractOutline(int off, int num_pd,
346 Path *dest, outline_callbacks & calls,
347 double tolerance, double width, JoinType join,
348 ButtType /*butt*/, double miter, bool closeIfNeeded,
349 bool skipMoveto, Geom::Point &lastP, Geom::Point &lastT)
350 {
351 outline_callback_data callsData;
352
353 callsData.orig = this;
354 callsData.dest = dest;
355 int curP = 1;
356
357 // le moveto
358 Geom::Point curX;
359 {
360 int firstTyp = descr_cmd[off]->getType();
361 if ( firstTyp != descr_moveto ) {
362 curX[0] = curX[1] = 0;
363 curP = 0;
364 } else {
365 PathDescrMoveTo* nData = dynamic_cast<PathDescrMoveTo*>(descr_cmd[off]);
366 curX = nData->p;
367 }
368 }
369 Geom::Point curT(0, 0);
370
371 bool doFirst = true;
372 Geom::Point firstP(0, 0);
373 Geom::Point firstT(0, 0);
374
375 // et le reste, 1 par 1
376 while (curP < num_pd)
377 {
378 int curD = off + curP;
379 int nType = descr_cmd[curD]->getType();
380 Geom::Point nextX;
381 Geom::Point stPos, enPos, stTgt, enTgt, stNor, enNor;
382 double stRad, enRad, stTle, enTle;
383 if (nType == descr_forced) {
384 curP++;
385 } else if (nType == descr_moveto) {
386 PathDescrMoveTo* nData = dynamic_cast<PathDescrMoveTo*>(descr_cmd[curD]);
387 nextX = nData->p;
388 // et on avance
389 if (doFirst) {
390 } else {
391 if (closeIfNeeded) {
392 if ( Geom::LInfty (curX- firstP) < 0.0001 ) {
393 OutlineJoin (dest, firstP, curT, firstT, width, join,
394 miter, nType);
395 dest->Close ();
396 } else {
397 PathDescrLineTo temp(firstP);
398
399 TangentOnSegAt (0.0, curX, temp, stPos, stTgt,
400 stTle);
401 TangentOnSegAt (1.0, curX, temp, enPos, enTgt,
402 enTle);
403 stNor=stTgt.cw();
404 enNor=enTgt.cw();
405
406 // jointure
407 {
408 Geom::Point pos;
409 pos = curX;
410 OutlineJoin (dest, pos, curT, stNor, width, join,
411 miter, nType);
412 }
413 dest->LineTo (enPos+width*enNor);
414
415 // jointure
416 {
417 Geom::Point pos;
418 pos = firstP;
419 OutlineJoin (dest, enPos, enNor, firstT, width, join,
420 miter, nType);
421 dest->Close ();
422 }
423 }
424 }
425 }
426 firstP = nextX;
427 curP++;
428 }
429 else if (nType == descr_close)
430 {
431 if (! doFirst)
432 {
433 if (Geom::LInfty (curX - firstP) < 0.0001)
434 {
435 OutlineJoin (dest, firstP, curT, firstT, width, join,
436 miter, nType);
437 dest->Close ();
438 }
439 else
440 {
441 PathDescrLineTo temp(firstP);
442 nextX = firstP;
443
444 TangentOnSegAt (0.0, curX, temp, stPos, stTgt, stTle);
445 TangentOnSegAt (1.0, curX, temp, enPos, enTgt, enTle);
446 stNor=stTgt.cw();
447 enNor=enTgt.cw();
448
449 // jointure
450 {
451 OutlineJoin (dest, stPos, curT, stNor, width, join,
452 miter, nType);
453 }
454
455 dest->LineTo (enPos+width*enNor);
456
457 // jointure
458 {
459 OutlineJoin (dest, enPos, enNor, firstT, width, join,
460 miter, nType);
461 dest->Close ();
462 }
463 }
464 }
465 doFirst = true;
466 curP++;
467 }
468 else if (nType == descr_lineto)
469 {
470 PathDescrLineTo* nData = dynamic_cast<PathDescrLineTo*>(descr_cmd[curD]);
471 nextX = nData->p;
472 // et on avance
473 TangentOnSegAt (0.0, curX, *nData, stPos, stTgt, stTle);
474 TangentOnSegAt (1.0, curX, *nData, enPos, enTgt, enTle);
475 // test de nullité du segment
476 if (IsNulCurve (descr_cmd, curD, curX))
477 {
478 if (descr_cmd.size() == 2) { // single point, see LP Bug 1006666
479 stTgt = dest->descr_cmd.size() ? Geom::Point(1, 0) : Geom::Point(-1, 0); // reverse direction
480 enTgt = stTgt;
481 } else {
482 curP++;
483 continue;
484 }
485 }
486 stNor=stTgt.cw();
487 enNor=enTgt.cw();
488
489 lastP = enPos;
490 lastT = enTgt;
491
492 if (doFirst)
493 {
494 doFirst = false;
495 firstP = stPos;
496 firstT = stNor;
497 if (skipMoveto)
498 {
499 skipMoveto = false;
500 }
501 else
502 dest->MoveTo (curX+width*stNor);
503 }
504 else
505 {
506 // jointure
507 Geom::Point pos;
508 pos = curX;
509 OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
510 }
511
512 int n_d = dest->LineTo (nextX+width*enNor);
513 if (n_d >= 0)
514 {
515 dest->descr_cmd[n_d]->associated = curP;
516 dest->descr_cmd[n_d]->tSt = 0.0;
517 dest->descr_cmd[n_d]->tEn = 1.0;
518 }
519 curP++;
520 }
521 else if (nType == descr_cubicto)
522 {
523 PathDescrCubicTo* nData = dynamic_cast<PathDescrCubicTo*>(descr_cmd[curD]);
524 nextX = nData->p;
525 // test de nullite du segment
526 if (IsNulCurve (descr_cmd, curD, curX))
527 {
528 curP++;
529 continue;
530 }
531 // et on avance
532 TangentOnCubAt (0.0, curX, *nData, false, stPos, stTgt,
533 stTle, stRad);
534 TangentOnCubAt (1.0, curX, *nData, true, enPos, enTgt,
535 enTle, enRad);
536 stNor=stTgt.cw();
537 enNor=enTgt.cw();
538
539 lastP = enPos;
540 lastT = enTgt;
541
542 if (doFirst)
543 {
544 doFirst = false;
545 firstP = stPos;
546 firstT = stNor;
547 if (skipMoveto)
548 {
549 skipMoveto = false;
550 }
551 else
552 dest->MoveTo (curX+width*stNor);
553 }
554 else
555 {
556 // jointure
557 Geom::Point pos;
558 pos = curX;
559 OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
560 }
561
562 callsData.piece = curP;
563 callsData.tSt = 0.0;
564 callsData.tEn = 1.0;
565 callsData.x1 = curX[0];
566 callsData.y1 = curX[1];
567 callsData.x2 = nextX[0];
568 callsData.y2 = nextX[1];
569 callsData.d.c.dx1 = nData->start[0];
570 callsData.d.c.dy1 = nData->start[1];
571 callsData.d.c.dx2 = nData->end[0];
572 callsData.d.c.dy2 = nData->end[1];
573 (calls.cubicto) (&callsData, tolerance, width);
574
575 curP++;
576 }
577 else if (nType == descr_arcto)
578 {
579 PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(descr_cmd[curD]);
580 nextX = nData->p;
581 // test de nullité du segment
582 if (IsNulCurve (descr_cmd, curD, curX))
583 {
584 curP++;
585 continue;
586 }
587 // et on avance
588 TangentOnArcAt (0.0, curX, *nData, stPos, stTgt, stTle,
589 stRad);
590 TangentOnArcAt (1.0, curX, *nData, enPos, enTgt, enTle,
591 enRad);
592 stNor=stTgt.cw();
593 enNor=enTgt.cw();
594
595 lastP = enPos;
596 lastT = enTgt; // tjs definie
597
598 if (doFirst)
599 {
600 doFirst = false;
601 firstP = stPos;
602 firstT = stNor;
603 if (skipMoveto)
604 {
605 skipMoveto = false;
606 }
607 else
608 dest->MoveTo (curX+width*stNor);
609 }
610 else
611 {
612 // jointure
613 Geom::Point pos;
614 pos = curX;
615 OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
616 }
617
618 callsData.piece = curP;
619 callsData.tSt = 0.0;
620 callsData.tEn = 1.0;
621 callsData.x1 = curX[0];
622 callsData.y1 = curX[1];
623 callsData.x2 = nextX[0];
624 callsData.y2 = nextX[1];
625 callsData.d.a.rx = nData->rx;
626 callsData.d.a.ry = nData->ry;
627 callsData.d.a.angle = nData->angle;
628 callsData.d.a.clock = nData->clockwise;
629 callsData.d.a.large = nData->large;
630 (calls.arcto) (&callsData, tolerance, width);
631
632 curP++;
633 }
634 else if (nType == descr_bezierto)
635 {
636 PathDescrBezierTo* nBData = dynamic_cast<PathDescrBezierTo*>(descr_cmd[curD]);
637 int nbInterm = nBData->nb;
638 nextX = nBData->p;
639
640 if (IsNulCurve (descr_cmd, curD, curX)) {
641 curP += nbInterm + 1;
642 continue;
643 }
644
645 curP++;
646
647 curD = off + curP;
648 int ip = curD;
649 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[ip]);
650
651 if (nbInterm <= 0) {
652 // et on avance
653 PathDescrLineTo temp(nextX);
654 TangentOnSegAt (0.0, curX, temp, stPos, stTgt, stTle);
655 TangentOnSegAt (1.0, curX, temp, enPos, enTgt, enTle);
656 stNor=stTgt.cw();
657 enNor=enTgt.cw();
658
659 lastP = enPos;
660 lastT = enTgt;
661
662 if (doFirst) {
663 doFirst = false;
664 firstP = stPos;
665 firstT = stNor;
666 if (skipMoveto) {
667 skipMoveto = false;
668 } else dest->MoveTo (curX+width*stNor);
669 } else {
670 // jointure
671 Geom::Point pos;
672 pos = curX;
673 if (stTle > 0) OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
674 }
675 int n_d = dest->LineTo (nextX+width*enNor);
676 if (n_d >= 0) {
677 dest->descr_cmd[n_d]->associated = curP - 1;
678 dest->descr_cmd[n_d]->tSt = 0.0;
679 dest->descr_cmd[n_d]->tEn = 1.0;
680 }
681 } else if (nbInterm == 1) {
682 Geom::Point midX;
683 midX = nData->p;
684 // et on avance
685 TangentOnBezAt (0.0, curX, *nData, *nBData, false, stPos, stTgt, stTle, stRad);
686 TangentOnBezAt (1.0, curX, *nData, *nBData, true, enPos, enTgt, enTle, enRad);
687 stNor=stTgt.cw();
688 enNor=enTgt.cw();
689
690 lastP = enPos;
691 lastT = enTgt;
692
693 if (doFirst) {
694 doFirst = false;
695 firstP = stPos;
696 firstT = stNor;
697 if (skipMoveto) {
698 skipMoveto = false;
699 } else dest->MoveTo (curX+width*stNor);
700 } else {
701 // jointure
702 Geom::Point pos;
703 pos = curX;
704 OutlineJoin (dest, pos, curT, stNor, width, join, miter, nType);
705 }
706
707 callsData.piece = curP;
708 callsData.tSt = 0.0;
709 callsData.tEn = 1.0;
710 callsData.x1 = curX[0];
711 callsData.y1 = curX[1];
712 callsData.x2 = nextX[0];
713 callsData.y2 = nextX[1];
714 callsData.d.b.mx = midX[0];
715 callsData.d.b.my = midX[1];
716 (calls.bezierto) (&callsData, tolerance, width);
717
718 } else if (nbInterm > 1) {
719 Geom::Point bx=curX;
720 Geom::Point cx=curX;
721 Geom::Point dx=nData->p;
722
723 TangentOnBezAt (0.0, curX, *nData, *nBData, false, stPos, stTgt, stTle, stRad);
724 stNor=stTgt.cw();
725
726 ip++;
727 nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[ip]);
728 // et on avance
729 if (stTle > 0) {
730 if (doFirst) {
731 doFirst = false;
732 firstP = stPos;
733 firstT = stNor;
734 if (skipMoveto) {
735 skipMoveto = false;
736 } else dest->MoveTo (curX+width*stNor);
737 } else {
738 // jointure
739 Geom::Point pos=curX;
740 OutlineJoin (dest, pos, stTgt, stNor, width, join, miter, nType);
741 // dest->LineTo(curX+width*stNor.x,curY+width*stNor.y);
742 }
743 }
744
745 cx = 2 * bx - dx;
746
747 for (int k = 0; k < nbInterm - 1; k++) {
748 bx = cx;
749 cx = dx;
750
751 dx = nData->p;
752 ip++;
753 nData = dynamic_cast<PathDescrIntermBezierTo*>(descr_cmd[ip]);
754 Geom::Point stx = (bx + cx) / 2;
755 // double stw=(bw+cw)/2;
756
757 PathDescrBezierTo tempb((cx + dx) / 2, 1);
758 PathDescrIntermBezierTo tempi(cx);
759 TangentOnBezAt (1.0, stx, tempi, tempb, true, enPos, enTgt, enTle, enRad);
760 enNor=enTgt.cw();
761
762 lastP = enPos;
763 lastT = enTgt;
764
765 callsData.piece = curP + k;
766 callsData.tSt = 0.0;
767 callsData.tEn = 1.0;
768 callsData.x1 = stx[0];
769 callsData.y1 = stx[1];
770 callsData.x2 = (cx[0] + dx[0]) / 2;
771 callsData.y2 = (cx[1] + dx[1]) / 2;
772 callsData.d.b.mx = cx[0];
773 callsData.d.b.my = cx[1];
774 (calls.bezierto) (&callsData, tolerance, width);
775 }
776 {
777 bx = cx;
778 cx = dx;
779
780 dx = nextX;
781 dx = 2 * dx - cx;
782
783 Geom::Point stx = (bx + cx) / 2;
784 // double stw=(bw+cw)/2;
785
786 PathDescrBezierTo tempb((cx + dx) / 2, 1);
787 PathDescrIntermBezierTo tempi(cx);
788 TangentOnBezAt (1.0, stx, tempi, tempb, true, enPos,
789 enTgt, enTle, enRad);
790 enNor=enTgt.cw();
791
792 lastP = enPos;
793 lastT = enTgt;
794
795 callsData.piece = curP + nbInterm - 1;
796 callsData.tSt = 0.0;
797 callsData.tEn = 1.0;
798 callsData.x1 = stx[0];
799 callsData.y1 = stx[1];
800 callsData.x2 = (cx[0] + dx[0]) / 2;
801 callsData.y2 = (cx[1] + dx[1]) / 2;
802 callsData.d.b.mx = cx[0];
803 callsData.d.b.my = cx[1];
804 (calls.bezierto) (&callsData, tolerance, width);
805
806 }
807 }
808
809 // et on avance
810 curP += nbInterm;
811 }
812 curX = nextX;
813 curT = enNor; // sera tjs bien definie
814 }
815 if (closeIfNeeded)
816 {
817 if (! doFirst)
818 {
819 }
820 }
821
822 }
823
824 /*
825 *
826 * utilitaires pour l'outline
827 *
828 */
829
830 // like the name says: check whether the path command is actually more than a dumb point.
831 bool
IsNulCurve(std::vector<PathDescr * > const & cmd,int curD,Geom::Point const & curX)832 Path::IsNulCurve (std::vector<PathDescr*> const &cmd, int curD, Geom::Point const &curX)
833 {
834 switch(cmd[curD]->getType()) {
835 case descr_lineto:
836 {
837 PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo*>(cmd[curD]);
838 if (Geom::LInfty(nData->p - curX) < 0.00001) {
839 return true;
840 }
841 return false;
842 }
843 case descr_cubicto:
844 {
845 PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo*>(cmd[curD]);
846 Geom::Point A = nData->start + nData->end + 2*(curX - nData->p);
847 Geom::Point B = 3*(nData->p - curX) - 2*nData->start - nData->end;
848 Geom::Point C = nData->start;
849 if (Geom::LInfty(A) < 0.0001
850 && Geom::LInfty(B) < 0.0001
851 && Geom::LInfty (C) < 0.0001) {
852 return true;
853 }
854 return false;
855 }
856 case descr_arcto:
857 {
858 PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo*>(cmd[curD]);
859 if ( Geom::LInfty(nData->p - curX) < 0.00001) {
860 if ((! nData->large)
861 || (fabs (nData->rx) < 0.00001
862 || fabs (nData->ry) < 0.00001)) {
863 return true;
864 }
865 }
866 return false;
867 }
868 case descr_bezierto:
869 {
870 PathDescrBezierTo* nBData = dynamic_cast<PathDescrBezierTo*>(cmd[curD]);
871 if (nBData->nb <= 0)
872 {
873 if (Geom::LInfty(nBData->p - curX) < 0.00001) {
874 return true;
875 }
876 return false;
877 }
878 else if (nBData->nb == 1)
879 {
880 if (Geom::LInfty(nBData->p - curX) < 0.00001) {
881 int ip = curD + 1;
882 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(cmd[ip]);
883 if (Geom::LInfty(nData->p - curX) < 0.00001) {
884 return true;
885 }
886 }
887 return false;
888 } else if (Geom::LInfty(nBData->p - curX) < 0.00001) {
889 for (int i = 1; i <= nBData->nb; i++) {
890 int ip = curD + i;
891 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(cmd[ip]);
892 if (Geom::LInfty(nData->p - curX) > 0.00001) {
893 return false;
894 }
895 }
896 return true;
897 }
898 }
899 default:
900 return true;
901 }
902 }
903
904 // tangents and curvarture computing, for the different path command types.
905 // the need for tangent is obvious: it gives the normal, along which we offset points
906 // curvature is used to do strength correction on the length of the tangents to the offset (see
907 // cubic offset)
908
909 /**
910 * \param at Distance along a tangent (0 <= at <= 1).
911 * \param iS Start point.
912 * \param fin LineTo description containing end point.
913 * \param pos Filled in with the position of `at' on the segment.
914 * \param tgt Filled in with the normalised tangent vector.
915 * \param len Filled in with the length of the segment.
916 */
917
TangentOnSegAt(double at,Geom::Point const & iS,PathDescrLineTo const & fin,Geom::Point & pos,Geom::Point & tgt,double & len)918 void Path::TangentOnSegAt(double at, Geom::Point const &iS, PathDescrLineTo const &fin,
919 Geom::Point &pos, Geom::Point &tgt, double &len)
920 {
921 Geom::Point const iE = fin.p;
922 Geom::Point const seg = iE - iS;
923 double const l = L2(seg);
924 if (l <= 0.000001) {
925 pos = iS;
926 tgt = Geom::Point(0, 0);
927 len = 0;
928 } else {
929 tgt = seg / l;
930 pos = (1 - at) * iS + at * iE; // in other words, pos = iS + at * seg
931 len = l;
932 }
933 }
934
935 // barf
TangentOnArcAt(double at,const Geom::Point & iS,PathDescrArcTo const & fin,Geom::Point & pos,Geom::Point & tgt,double & len,double & rad)936 void Path::TangentOnArcAt(double at, const Geom::Point &iS, PathDescrArcTo const &fin,
937 Geom::Point &pos, Geom::Point &tgt, double &len, double &rad)
938 {
939 Geom::Point const iE = fin.p;
940 double const rx = fin.rx;
941 double const ry = fin.ry;
942 double const angle = fin.angle*M_PI/180.0;
943 bool const large = fin.large;
944 bool const wise = fin.clockwise;
945
946 pos = iS;
947 tgt[0] = tgt[1] = 0;
948 if (rx <= 0.0001 || ry <= 0.0001)
949 return;
950
951 double const sex = iE[0] - iS[0], sey = iE[1] - iS[1];
952 double const ca = cos (angle), sa = sin (angle);
953 double csex = ca * sex + sa * sey;
954 double csey = -sa * sex + ca * sey;
955 csex /= rx;
956 csey /= ry;
957 double l = csex * csex + csey * csey;
958 double const d = sqrt(std::max(1 - l / 4, 0.0));
959 double csdx = csey;
960 double csdy = -csex;
961 l = sqrt(l);
962 csdx /= l;
963 csdy /= l;
964 csdx *= d;
965 csdy *= d;
966
967 double sang;
968 double eang;
969 double rax = -csdx - csex / 2;
970 double ray = -csdy - csey / 2;
971 if (rax < -1)
972 {
973 sang = M_PI;
974 }
975 else if (rax > 1)
976 {
977 sang = 0;
978 }
979 else
980 {
981 sang = acos (rax);
982 if (ray < 0)
983 sang = 2 * M_PI - sang;
984 }
985 rax = -csdx + csex / 2;
986 ray = -csdy + csey / 2;
987 if (rax < -1)
988 {
989 eang = M_PI;
990 }
991 else if (rax > 1)
992 {
993 eang = 0;
994 }
995 else
996 {
997 eang = acos (rax);
998 if (ray < 0)
999 eang = 2 * M_PI - eang;
1000 }
1001
1002 csdx *= rx;
1003 csdy *= ry;
1004 double drx = ca * csdx - sa * csdy;
1005 double dry = sa * csdx + ca * csdy;
1006
1007 if (wise)
1008 {
1009 if (large)
1010 {
1011 drx = -drx;
1012 dry = -dry;
1013 double swap = eang;
1014 eang = sang;
1015 sang = swap;
1016 eang += M_PI;
1017 sang += M_PI;
1018 if (eang >= 2 * M_PI)
1019 eang -= 2 * M_PI;
1020 if (sang >= 2 * M_PI)
1021 sang -= 2 * M_PI;
1022 }
1023 }
1024 else
1025 {
1026 if (! large)
1027 {
1028 drx = -drx;
1029 dry = -dry;
1030 double swap = eang;
1031 eang = sang;
1032 sang = swap;
1033 eang += M_PI;
1034 sang += M_PI;
1035 if (eang >= 2 * M_PI)
1036 eang -= 2 * M_PI;
1037 if (sang >= 2 * M_PI)
1038 sang -= 2 * M_PI;
1039 }
1040 }
1041 drx += (iS[0] + iE[0]) / 2;
1042 dry += (iS[1] + iE[1]) / 2;
1043
1044 if (wise) {
1045 if (sang < eang)
1046 sang += 2 * M_PI;
1047 double b = sang * (1 - at) + eang * at;
1048 double cb = cos (b), sb = sin (b);
1049 pos[0] = drx + ca * rx * cb - sa * ry * sb;
1050 pos[1] = dry + sa * rx * cb + ca * ry * sb;
1051 tgt[0] = ca * rx * sb + sa * ry * cb;
1052 tgt[1] = sa * rx * sb - ca * ry * cb;
1053 Geom::Point dtgt;
1054 dtgt[0] = -ca * rx * cb + sa * ry * sb;
1055 dtgt[1] = -sa * rx * cb - ca * ry * sb;
1056 len = L2(tgt);
1057 rad = -len * dot(tgt, tgt) / (tgt[0] * dtgt[1] - tgt[1] * dtgt[0]);
1058 tgt /= len;
1059 }
1060 else
1061 {
1062 if (sang > eang)
1063 sang -= 2 * M_PI;
1064 double b = sang * (1 - at) + eang * at;
1065 double cb = cos (b), sb = sin (b);
1066 pos[0] = drx + ca * rx * cb - sa * ry * sb;
1067 pos[1] = dry + sa * rx * cb + ca * ry * sb;
1068 tgt[0] = ca * rx * sb + sa * ry * cb;
1069 tgt[1] = sa * rx * sb - ca * ry * cb;
1070 Geom::Point dtgt;
1071 dtgt[0] = -ca * rx * cb + sa * ry * sb;
1072 dtgt[1] = -sa * rx * cb - ca * ry * sb;
1073 len = L2(tgt);
1074 rad = len * dot(tgt, tgt) / (tgt[0] * dtgt[1] - tgt[1] * dtgt[0]);
1075 tgt /= len;
1076 }
1077 }
1078 void
TangentOnCubAt(double at,Geom::Point const & iS,PathDescrCubicTo const & fin,bool before,Geom::Point & pos,Geom::Point & tgt,double & len,double & rad)1079 Path::TangentOnCubAt (double at, Geom::Point const &iS, PathDescrCubicTo const &fin, bool before,
1080 Geom::Point &pos, Geom::Point &tgt, double &len, double &rad)
1081 {
1082 const Geom::Point E = fin.p;
1083 const Geom::Point Sd = fin.start;
1084 const Geom::Point Ed = fin.end;
1085
1086 pos = iS;
1087 tgt = Geom::Point(0,0);
1088 len = rad = 0;
1089
1090 const Geom::Point A = Sd + Ed - 2*E + 2*iS;
1091 const Geom::Point B = 0.5*(Ed - Sd);
1092 const Geom::Point C = 0.25*(6*E - 6*iS - Sd - Ed);
1093 const Geom::Point D = 0.125*(4*iS + 4*E - Ed + Sd);
1094 const double atb = at - 0.5;
1095 pos = (atb * atb * atb)*A + (atb * atb)*B + atb*C + D;
1096 const Geom::Point der = (3 * atb * atb)*A + (2 * atb)*B + C;
1097 const Geom::Point dder = (6 * atb)*A + 2*B;
1098 const Geom::Point ddder = 6 * A;
1099
1100 double l = Geom::L2 (der);
1101 // lots of nasty cases. inversion points are sadly too common...
1102 if (l <= 0.0001) {
1103 len = 0;
1104 l = L2(dder);
1105 if (l <= 0.0001) {
1106 l = L2(ddder);
1107 if (l <= 0.0001) {
1108 // pas de segment....
1109 return;
1110 }
1111 rad = 100000000;
1112 tgt = ddder / l;
1113 if (before) {
1114 tgt = -tgt;
1115 }
1116 return;
1117 }
1118 rad = -l * (dot(dder,dder)) / (cross(dder, ddder));
1119 tgt = dder / l;
1120 if (before) {
1121 tgt = -tgt;
1122 }
1123 return;
1124 }
1125 len = l;
1126
1127 rad = -l * (dot(der,der)) / (cross(der, dder));
1128
1129 tgt = der / l;
1130 }
1131
1132 void
TangentOnBezAt(double at,Geom::Point const & iS,PathDescrIntermBezierTo & mid,PathDescrBezierTo & fin,bool before,Geom::Point & pos,Geom::Point & tgt,double & len,double & rad)1133 Path::TangentOnBezAt (double at, Geom::Point const &iS,
1134 PathDescrIntermBezierTo & mid,
1135 PathDescrBezierTo & fin, bool before, Geom::Point & pos,
1136 Geom::Point & tgt, double &len, double &rad)
1137 {
1138 pos = iS;
1139 tgt = Geom::Point(0,0);
1140 len = rad = 0;
1141
1142 const Geom::Point A = fin.p + iS - 2*mid.p;
1143 const Geom::Point B = 2*mid.p - 2 * iS;
1144 const Geom::Point C = iS;
1145
1146 pos = at * at * A + at * B + C;
1147 const Geom::Point der = 2 * at * A + B;
1148 const Geom::Point dder = 2 * A;
1149 double l = Geom::L2(der);
1150
1151 if (l <= 0.0001) {
1152 l = Geom::L2(dder);
1153 if (l <= 0.0001) {
1154 // pas de segment....
1155 // Not a segment.
1156 return;
1157 }
1158 rad = 100000000; // Why this number?
1159 tgt = dder / l;
1160 if (before) {
1161 tgt = -tgt;
1162 }
1163 return;
1164 }
1165 len = l;
1166 rad = -l * (dot(der,der)) / (cross(der, dder));
1167
1168 tgt = der / l;
1169 }
1170
1171 void
OutlineJoin(Path * dest,Geom::Point pos,Geom::Point stNor,Geom::Point enNor,double width,JoinType join,double miter,int nType)1172 Path::OutlineJoin (Path * dest, Geom::Point pos, Geom::Point stNor, Geom::Point enNor, double width,
1173 JoinType join, double miter, int nType)
1174 {
1175 /*
1176 Arbitrarily decide if we're on the inside or outside of a half turn.
1177 A turn of 180 degrees (line path leaves the node in the same direction as it arrived)
1178 is symmetric and has no real inside and outside. However when outlining we shall handle
1179 one path as inside and the reverse path as outside. Handling both as inside joins (as
1180 was done previously) will cut off round joins. Handling both as outside joins could
1181 ideally work because both should fall together, but it seems that this causes many
1182 extra nodes (due to rounding errors). Solution: for the 'half turn'-case toggle
1183 inside/outside each time the same node is processed 2 consecutive times.
1184 */
1185 static bool TurnInside = true;
1186 static Geom::Point PrevPos(0, 0);
1187 TurnInside ^= PrevPos == pos;
1188 PrevPos = pos;
1189
1190 const double angSi = cross (stNor, enNor);
1191 const double angCo = dot (stNor, enNor);
1192
1193 if ((fabs(angSi) < .0000001) && angCo > 0) { // The join is straight -> nothing to do.
1194 } else {
1195 if ((angSi > 0 && width >= 0)
1196 || (angSi < 0 && width < 0)) { // This is an inside join -> join is independent of chosen JoinType.
1197 if ((dest->descr_cmd[dest->descr_cmd.size() - 1]->getType() == descr_lineto) && (nType == descr_lineto)) {
1198 Geom::Point const biss = unit_vector(Geom::rot90( stNor - enNor ));
1199 double c2 = Geom::dot (biss, enNor);
1200 if (fabs(c2) > M_SQRT1_2) { // apply only to obtuse angles
1201 double l = width / c2;
1202 PathDescrLineTo* nLine = dynamic_cast<PathDescrLineTo*>(dest->descr_cmd[dest->descr_cmd.size() - 1]);
1203 nLine->p = pos + l*biss; // relocate to bisector
1204 } else {
1205 dest->LineTo (pos + width*enNor);
1206 }
1207 } else {
1208 // dest->LineTo (pos); // redundant
1209 dest->LineTo (pos + width*enNor);
1210 }
1211 } else if (angSi == 0 && TurnInside) { // Half turn (180 degrees) ... inside (see above).
1212 dest->LineTo (pos + width*enNor);
1213 } else { // This is an outside join -> chosen JoinType should be applied.
1214 if (join == join_round) {
1215 // Use the ends of the cubic: approximate the arc at the
1216 // point where .., and support better the rounding of
1217 // coordinates of the end points.
1218
1219 // utiliser des bouts de cubique: approximation de l'arc (au point ou on en est...), et supporte mieux
1220 // l'arrondi des coordonnees des extremites
1221 /* double angle=acos(angCo);
1222 if ( angCo >= 0 ) {
1223 Geom::Point stTgt,enTgt;
1224 RotCCWTo(stNor,stTgt);
1225 RotCCWTo(enNor,enTgt);
1226 dest->CubicTo(pos.x+width*enNor.x,pos.y+width*enNor.y,
1227 angle*width*stTgt.x,angle*width*stTgt.y,
1228 angle*width*enTgt.x,angle*width*enTgt.y);
1229 } else {
1230 Geom::Point biNor;
1231 Geom::Point stTgt,enTgt,biTgt;
1232 biNor.x=stNor.x+enNor.x;
1233 biNor.y=stNor.y+enNor.y;
1234 double biL=sqrt(biNor.x*biNor.x+biNor.y*biNor.y);
1235 biNor.x/=biL;
1236 biNor.y/=biL;
1237 RotCCWTo(stNor,stTgt);
1238 RotCCWTo(enNor,enTgt);
1239 RotCCWTo(biNor,biTgt);
1240 dest->CubicTo(pos.x+width*biNor.x,pos.y+width*biNor.y,
1241 angle*width*stTgt.x,angle*width*stTgt.y,
1242 angle*width*biTgt.x,angle*width*biTgt.y);
1243 dest->CubicTo(pos.x+width*enNor.x,pos.y+width*enNor.y,
1244 angle*width*biTgt.x,angle*width*biTgt.y,
1245 angle*width*enTgt.x,angle*width*enTgt.y);
1246 }*/
1247 if (width > 0) {
1248 dest->ArcTo (pos + width*enNor,
1249 1.0001 * width, 1.0001 * width, 0.0, false, true);
1250 } else {
1251 dest->ArcTo (pos + width*enNor,
1252 -1.0001 * width, -1.0001 * width, 0.0, false,
1253 false);
1254 }
1255 } else if (join == join_pointy) {
1256 Geom::Point const biss = unit_vector(Geom::rot90( stNor - enNor ));
1257 double c2 = Geom::dot (biss, enNor);
1258 double l = width / c2;
1259 if ( fabs(l) > miter) {
1260 dest->LineTo (pos + width*enNor);
1261 } else {
1262 if (dest->descr_cmd[dest->descr_cmd.size() - 1]->getType() == descr_lineto) {
1263 PathDescrLineTo* nLine = dynamic_cast<PathDescrLineTo*>(dest->descr_cmd[dest->descr_cmd.size() - 1]);
1264 nLine->p = pos+l*biss; // relocate to bisector
1265 } else {
1266 dest->LineTo (pos+l*biss);
1267 }
1268 if (nType != descr_lineto)
1269 dest->LineTo (pos+width*enNor);
1270 }
1271 } else { // Bevel join
1272 dest->LineTo (pos + width*enNor);
1273 }
1274 }
1275 }
1276 }
1277
1278 // les callbacks
1279
1280 // see http://www.home.unix-ag.org/simon/sketch/pathstroke.py to understand what's happening here
1281
1282 void
RecStdCubicTo(outline_callback_data * data,double tol,double width,int lev)1283 Path::RecStdCubicTo (outline_callback_data * data, double tol, double width,
1284 int lev)
1285 {
1286 Geom::Point stPos, miPos, enPos;
1287 Geom::Point stTgt, enTgt, miTgt, stNor, enNor, miNor;
1288 double stRad, miRad, enRad;
1289 double stTle, miTle, enTle;
1290 // un cubic
1291 {
1292 PathDescrCubicTo temp(Geom::Point(data->x2, data->y2),
1293 Geom::Point(data->d.c.dx1, data->d.c.dy1),
1294 Geom::Point(data->d.c.dx2, data->d.c.dy2));
1295
1296 Geom::Point initial_point(data->x1, data->y1);
1297 TangentOnCubAt (0.0, initial_point, temp, false, stPos, stTgt, stTle,
1298 stRad);
1299 TangentOnCubAt (0.5, initial_point, temp, false, miPos, miTgt, miTle,
1300 miRad);
1301 TangentOnCubAt (1.0, initial_point, temp, true, enPos, enTgt, enTle,
1302 enRad);
1303 stNor=stTgt.cw();
1304 miNor=miTgt.cw();
1305 enNor=enTgt.cw();
1306 }
1307
1308 double stGue = 1, miGue = 1, enGue = 1;
1309 // correction of the lengths of the tangent to the offset
1310 // if you don't see why i wrote that, draw a little figure and everything will be clear
1311 if (fabs (stRad) > 0.01)
1312 stGue += width / stRad;
1313 if (fabs (miRad) > 0.01)
1314 miGue += width / miRad;
1315 if (fabs (enRad) > 0.01)
1316 enGue += width / enRad;
1317 stGue *= stTle;
1318 miGue *= miTle;
1319 enGue *= enTle;
1320
1321
1322 if (lev <= 0) {
1323 int n_d = data->dest->CubicTo (enPos + width*enNor,
1324 stGue*stTgt,
1325 enGue*enTgt);
1326 if (n_d >= 0) {
1327 data->dest->descr_cmd[n_d]->associated = data->piece;
1328 data->dest->descr_cmd[n_d]->tSt = data->tSt;
1329 data->dest->descr_cmd[n_d]->tEn = data->tEn;
1330 }
1331 return;
1332 }
1333
1334 Geom::Point chk;
1335 const Geom::Point req = miPos + width * miNor;
1336 {
1337 PathDescrCubicTo temp(enPos + width * enNor,
1338 stGue * stTgt,
1339 enGue * enTgt);
1340 double chTle, chRad;
1341 Geom::Point chTgt;
1342 TangentOnCubAt (0.5, stPos+width*stNor,
1343 temp, false, chk, chTgt, chTle, chRad);
1344 }
1345 const Geom::Point diff = req - chk;
1346 const double err = dot(diff,diff);
1347 if (err <= tol ) { // tolerance is given as a quadratic value, no need to use tol*tol here
1348 // printf("%f <= %f %i\n",err,tol,lev);
1349 int n_d = data->dest->CubicTo (enPos + width*enNor,
1350 stGue*stTgt,
1351 enGue*enTgt);
1352 if (n_d >= 0) {
1353 data->dest->descr_cmd[n_d]->associated = data->piece;
1354 data->dest->descr_cmd[n_d]->tSt = data->tSt;
1355 data->dest->descr_cmd[n_d]->tEn = data->tEn;
1356 }
1357 } else {
1358 outline_callback_data desc = *data;
1359
1360 desc.tSt = data->tSt;
1361 desc.tEn = (data->tSt + data->tEn) / 2;
1362 desc.x1 = data->x1;
1363 desc.y1 = data->y1;
1364 desc.x2 = miPos[0];
1365 desc.y2 = miPos[1];
1366 desc.d.c.dx1 = 0.5 * stTle * stTgt[0];
1367 desc.d.c.dy1 = 0.5 * stTle * stTgt[1];
1368 desc.d.c.dx2 = 0.5 * miTle * miTgt[0];
1369 desc.d.c.dy2 = 0.5 * miTle * miTgt[1];
1370 RecStdCubicTo (&desc, tol, width, lev - 1);
1371
1372 desc.tSt = (data->tSt + data->tEn) / 2;
1373 desc.tEn = data->tEn;
1374 desc.x1 = miPos[0];
1375 desc.y1 = miPos[1];
1376 desc.x2 = data->x2;
1377 desc.y2 = data->y2;
1378 desc.d.c.dx1 = 0.5 * miTle * miTgt[0];
1379 desc.d.c.dy1 = 0.5 * miTle * miTgt[1];
1380 desc.d.c.dx2 = 0.5 * enTle * enTgt[0];
1381 desc.d.c.dy2 = 0.5 * enTle * enTgt[1];
1382 RecStdCubicTo (&desc, tol, width, lev - 1);
1383 }
1384 }
1385
1386 void
StdCubicTo(Path::outline_callback_data * data,double tol,double width)1387 Path::StdCubicTo (Path::outline_callback_data * data, double tol, double width)
1388 {
1389 // fflush (stdout);
1390 RecStdCubicTo (data, tol, width, 8);
1391 }
1392
1393 void
StdBezierTo(Path::outline_callback_data * data,double tol,double width)1394 Path::StdBezierTo (Path::outline_callback_data * data, double tol, double width)
1395 {
1396 PathDescrBezierTo tempb(Geom::Point(data->x2, data->y2), 1);
1397 PathDescrIntermBezierTo tempi(Geom::Point(data->d.b.mx, data->d.b.my));
1398 Geom::Point stPos, enPos, stTgt, enTgt;
1399 double stRad, enRad, stTle, enTle;
1400 Geom::Point tmp(data->x1,data->y1);
1401 TangentOnBezAt (0.0, tmp, tempi, tempb, false, stPos, stTgt,
1402 stTle, stRad);
1403 TangentOnBezAt (1.0, tmp, tempi, tempb, true, enPos, enTgt,
1404 enTle, enRad);
1405 data->d.c.dx1 = stTle * stTgt[0];
1406 data->d.c.dy1 = stTle * stTgt[1];
1407 data->d.c.dx2 = enTle * enTgt[0];
1408 data->d.c.dy2 = enTle * enTgt[1];
1409 RecStdCubicTo (data, tol, width, 8);
1410 }
1411
1412 void
RecStdArcTo(outline_callback_data * data,double tol,double width,int lev)1413 Path::RecStdArcTo (outline_callback_data * data, double tol, double width,
1414 int lev)
1415 {
1416 Geom::Point stPos, miPos, enPos;
1417 Geom::Point stTgt, enTgt, miTgt, stNor, enNor, miNor;
1418 double stRad, miRad, enRad;
1419 double stTle, miTle, enTle;
1420 // un cubic
1421 {
1422 PathDescrArcTo temp(Geom::Point(data->x2, data->y2),
1423 data->d.a.rx, data->d.a.ry,
1424 data->d.a.angle, data->d.a.large, data->d.a.clock);
1425
1426 Geom::Point tmp(data->x1,data->y1);
1427 TangentOnArcAt (data->d.a.stA, tmp, temp, stPos, stTgt,
1428 stTle, stRad);
1429 TangentOnArcAt ((data->d.a.stA + data->d.a.enA) / 2, tmp,
1430 temp, miPos, miTgt, miTle, miRad);
1431 TangentOnArcAt (data->d.a.enA, tmp, temp, enPos, enTgt,
1432 enTle, enRad);
1433 stNor=stTgt.cw();
1434 miNor=miTgt.cw();
1435 enNor=enTgt.cw();
1436 }
1437
1438 double stGue = 1, miGue = 1, enGue = 1;
1439 if (fabs (stRad) > 0.01)
1440 stGue += width / stRad;
1441 if (fabs (miRad) > 0.01)
1442 miGue += width / miRad;
1443 if (fabs (enRad) > 0.01)
1444 enGue += width / enRad;
1445 stGue *= stTle;
1446 miGue *= miTle;
1447 enGue *= enTle;
1448 double sang, eang;
1449 {
1450 Geom::Point tms(data->x1,data->y1),tme(data->x2,data->y2);
1451 ArcAngles (tms,tme, data->d.a.rx,
1452 data->d.a.ry, data->d.a.angle*M_PI/180.0, data->d.a.large, !data->d.a.clock,
1453 sang, eang);
1454 }
1455 double scal = eang - sang;
1456 if (scal < 0)
1457 scal += 2 * M_PI;
1458 if (scal > 2 * M_PI)
1459 scal -= 2 * M_PI;
1460 scal *= data->d.a.enA - data->d.a.stA;
1461
1462 if (lev <= 0)
1463 {
1464 int n_d = data->dest->CubicTo (enPos + width*enNor,
1465 stGue*scal*stTgt,
1466 enGue*scal*enTgt);
1467 if (n_d >= 0) {
1468 data->dest->descr_cmd[n_d]->associated = data->piece;
1469 data->dest->descr_cmd[n_d]->tSt = data->d.a.stA;
1470 data->dest->descr_cmd[n_d]->tEn = data->d.a.enA;
1471 }
1472 return;
1473 }
1474
1475 Geom::Point chk;
1476 const Geom::Point req = miPos + width*miNor;
1477 {
1478 PathDescrCubicTo temp(enPos + width * enNor, stGue * scal * stTgt, enGue * scal * enTgt);
1479 double chTle, chRad;
1480 Geom::Point chTgt;
1481 TangentOnCubAt (0.5, stPos+width*stNor,
1482 temp, false, chk, chTgt, chTle, chRad);
1483 }
1484 const Geom::Point diff = req - chk;
1485 const double err = (dot(diff,diff));
1486 if (err <= tol) // tolerance is given as a quadratic value, no need to use tol*tol here
1487 {
1488 int n_d = data->dest->CubicTo (enPos + width*enNor,
1489 stGue*scal*stTgt,
1490 enGue*scal*enTgt);
1491 if (n_d >= 0) {
1492 data->dest->descr_cmd[n_d]->associated = data->piece;
1493 data->dest->descr_cmd[n_d]->tSt = data->d.a.stA;
1494 data->dest->descr_cmd[n_d]->tEn = data->d.a.enA;
1495 }
1496 } else {
1497 outline_callback_data desc = *data;
1498
1499 desc.d.a.stA = data->d.a.stA;
1500 desc.d.a.enA = (data->d.a.stA + data->d.a.enA) / 2;
1501 RecStdArcTo (&desc, tol, width, lev - 1);
1502
1503 desc.d.a.stA = (data->d.a.stA + data->d.a.enA) / 2;
1504 desc.d.a.enA = data->d.a.enA;
1505 RecStdArcTo (&desc, tol, width, lev - 1);
1506 }
1507 }
1508
1509 void
StdArcTo(Path::outline_callback_data * data,double tol,double width)1510 Path::StdArcTo (Path::outline_callback_data * data, double tol, double width)
1511 {
1512 data->d.a.stA = 0.0;
1513 data->d.a.enA = 1.0;
1514 RecStdArcTo (data, tol, width, 8);
1515 }
1516
1517 /*
1518 Local Variables:
1519 mode:c++
1520 c-file-style:"stroustrup"
1521 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1522 indent-tabs-mode:nil
1523 fill-column:99
1524 End:
1525 */
1526 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
1527