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