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 NR::Point endButt;
45 NR::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 NR::Point curX;
77 NR::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 NR::Point isD=-nData->start;
106 NR::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 NR::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 NR::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 NR::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 NR::Point curX;
264 NR::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 NR::Point isD=-nData->start;
286 NR::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, NR::Point &lastP, NR::Point &lastT)
347 {
348 outline_callback_data callsData;
350 callsData.orig = this;
351 callsData.dest = dest;
352 int curP = 1;
354 // le moveto
355 NR::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 NR::Point curT(0, 0);
368 bool doFirst = true;
369 NR::Point firstP(0, 0);
370 NR::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 NR::Point nextX;
378 NR::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 ( NR::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 NR::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 NR::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 (NR::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 NR::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 NR::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 NR::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 NR::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 NR::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 NR::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 NR::Point bx=curX;
712 NR::Point cx=curX;
713 NR::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 NR::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 NR::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 NR::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, NR::Point const &curX)
826 {
827 switch(cmd[curD]->getType()) {
828 case descr_lineto:
829 {
830 PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo*>(cmd[curD]);
831 if (NR::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 NR::Point A = nData->start + nData->end + 2*(curX - nData->p);
840 NR::Point B = 3*(nData->p - curX) - 2*nData->start - nData->end;
841 NR::Point C = nData->start;
842 if (NR::LInfty(A) < 0.0001
843 && NR::LInfty(B) < 0.0001
844 && NR::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 ( NR::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 (NR::LInfty(nBData->p - curX) < 0.00001) {
867 return true;
868 }
869 return false;
870 }
871 else if (nBData->nb == 1)
872 {
873 if (NR::LInfty(nBData->p - curX) < 0.00001) {
874 int ip = curD + 1;
875 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(cmd[ip]);
876 if (NR::LInfty(nData->p - curX) < 0.00001) {
877 return true;
878 }
879 }
880 return false;
881 } else if (NR::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 (NR::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, NR::Point const &iS, PathDescrLineTo const &fin,
912 NR::Point &pos, NR::Point &tgt, double &len)
913 {
914 NR::Point const iE = fin.p;
915 NR::Point const seg = iE - iS;
916 double const l = L2(seg);
917 if (l <= 0.000001) {
918 pos = iS;
919 tgt = NR::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 NR::Point &iS, PathDescrArcTo const &fin,
930 NR::Point &pos, NR::Point &tgt, double &len, double &rad)
931 {
932 NR::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 NR::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 NR::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, NR::Point const &iS, PathDescrCubicTo const &fin, bool before,
1075 NR::Point &pos, NR::Point &tgt, double &len, double &rad)
1076 {
1077 const NR::Point E = fin.p;
1078 const NR::Point Sd = fin.start;
1079 const NR::Point Ed = fin.end;
1081 pos = iS;
1082 tgt = NR::Point(0,0);
1083 len = rad = 0;
1085 const NR::Point A = Sd + Ed - 2*E + 2*iS;
1086 const NR::Point B = 0.5*(Ed - Sd);
1087 const NR::Point C = 0.25*(6*E - 6*iS - Sd - Ed);
1088 const NR::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 NR::Point der = (3 * atb * atb)*A + (2 * atb)*B + C;
1092 const NR::Point dder = (6 * atb)*A + 2*B;
1093 const NR::Point ddder = 6 * A;
1095 double l = NR::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, NR::Point const &iS,
1129 PathDescrIntermBezierTo & mid,
1130 PathDescrBezierTo & fin, bool before, NR::Point & pos,
1131 NR::Point & tgt, double &len, double &rad)
1132 {
1133 pos = iS;
1134 tgt = NR::Point(0,0);
1135 len = rad = 0;
1137 const NR::Point A = fin.p + iS - 2*mid.p;
1138 const NR::Point B = 2*mid.p - 2 * iS;
1139 const NR::Point C = iS;
1141 pos = at * at * A + at * B + C;
1142 const NR::Point der = 2 * at * A + B;
1143 const NR::Point dder = 2 * A;
1144 double l = NR::L2(der);
1146 if (l <= 0.0001) {
1147 l = NR::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, NR::Point pos, NR::Point stNor, NR::Point enNor, double width,
1168 JoinType join, double miter)
1169 {
1170 const double angSi = cross (enNor,stNor);
1171 const double angCo = dot (stNor, enNor);
1172 // 1/1000 is very big/ugly, but otherwise it stuffs things up a little...
1173 // 1/1000 est tres grossier, mais sinon ca merde tout azimut
1174 if ((width >= 0 && angSi > -0.001)
1175 || (width < 0 && angSi < 0.001)) {
1176 if (angCo > 0.999) {
1177 // straight ahead
1178 // tout droit
1179 } else if (angCo < -0.999) {
1180 // half turn
1181 // demit-tour
1182 dest->LineTo (pos + width*enNor);
1183 } else {
1184 dest->LineTo (pos);
1185 dest->LineTo (pos + width*enNor);
1186 }
1187 } else {
1188 if (join == join_round) {
1189 // Use the ends of the cubic: approximate the arc at the
1190 // point where .., and support better the rounding of
1191 // coordinates of the end points.
1193 // utiliser des bouts de cubique: approximation de l'arc (au point ou on en est...), et supporte mieux
1194 // l'arrondi des coordonnees des extremites
1195 /* double angle=acos(angCo);
1196 if ( angCo >= 0 ) {
1197 NR::Point stTgt,enTgt;
1198 RotCCWTo(stNor,stTgt);
1199 RotCCWTo(enNor,enTgt);
1200 dest->CubicTo(pos.x+width*enNor.x,pos.y+width*enNor.y,
1201 angle*width*stTgt.x,angle*width*stTgt.y,
1202 angle*width*enTgt.x,angle*width*enTgt.y);
1203 } else {
1204 NR::Point biNor;
1205 NR::Point stTgt,enTgt,biTgt;
1206 biNor.x=stNor.x+enNor.x;
1207 biNor.y=stNor.y+enNor.y;
1208 double biL=sqrt(biNor.x*biNor.x+biNor.y*biNor.y);
1209 biNor.x/=biL;
1210 biNor.y/=biL;
1211 RotCCWTo(stNor,stTgt);
1212 RotCCWTo(enNor,enTgt);
1213 RotCCWTo(biNor,biTgt);
1214 dest->CubicTo(pos.x+width*biNor.x,pos.y+width*biNor.y,
1215 angle*width*stTgt.x,angle*width*stTgt.y,
1216 angle*width*biTgt.x,angle*width*biTgt.y);
1217 dest->CubicTo(pos.x+width*enNor.x,pos.y+width*enNor.y,
1218 angle*width*biTgt.x,angle*width*biTgt.y,
1219 angle*width*enTgt.x,angle*width*enTgt.y);
1220 }*/
1221 if (width > 0) {
1222 dest->ArcTo (pos + width*enNor,
1223 1.0001 * width, 1.0001 * width, 0.0, false, true);
1224 } else {
1225 dest->ArcTo (pos + width*enNor,
1226 -1.0001 * width, -1.0001 * width, 0.0, false,
1227 false);
1228 }
1229 } else if (join == join_pointy) {
1230 NR::Point const biss = unit_vector(NR::rot90( stNor - enNor ));
1231 double c2 = NR::dot (biss, enNor);
1232 double l = width / c2;
1233 if ( fabs(l) > miter) {
1234 dest->LineTo (pos + width*enNor);
1235 } else {
1236 dest->LineTo (pos+l*biss);
1237 dest->LineTo (pos+width*enNor);
1238 }
1239 } else {
1240 dest->LineTo (pos + width*enNor);
1241 }
1242 }
1243 }
1245 // les callbacks
1247 // see http://www.home.unix-ag.org/simon/sketch/pathstroke.py to understand what's happening here
1249 void
1250 Path::RecStdCubicTo (outline_callback_data * data, double tol, double width,
1251 int lev)
1252 {
1253 NR::Point stPos, miPos, enPos;
1254 NR::Point stTgt, enTgt, miTgt, stNor, enNor, miNor;
1255 double stRad, miRad, enRad;
1256 double stTle, miTle, enTle;
1257 // un cubic
1258 {
1259 PathDescrCubicTo temp(NR::Point(data->x2, data->y2),
1260 NR::Point(data->d.c.dx1, data->d.c.dy1),
1261 NR::Point(data->d.c.dx2, data->d.c.dy2));
1263 NR::Point initial_point(data->x1, data->y1);
1264 TangentOnCubAt (0.0, initial_point, temp, false, stPos, stTgt, stTle,
1265 stRad);
1266 TangentOnCubAt (0.5, initial_point, temp, false, miPos, miTgt, miTle,
1267 miRad);
1268 TangentOnCubAt (1.0, initial_point, temp, true, enPos, enTgt, enTle,
1269 enRad);
1270 stNor=stTgt.cw();
1271 miNor=miTgt.cw();
1272 enNor=enTgt.cw();
1273 }
1275 double stGue = 1, miGue = 1, enGue = 1;
1276 // correction of the lengths of the tangent to the offset
1277 // if you don't see why i wrote that, draw a little figure and everything will be clear
1278 if (fabs (stRad) > 0.01)
1279 stGue += width / stRad;
1280 if (fabs (miRad) > 0.01)
1281 miGue += width / miRad;
1282 if (fabs (enRad) > 0.01)
1283 enGue += width / enRad;
1284 stGue *= stTle;
1285 miGue *= miTle;
1286 enGue *= enTle;
1289 if (lev <= 0) {
1290 int n_d = data->dest->CubicTo (enPos + width*enNor,
1291 stGue*stTgt,
1292 enGue*enTgt);
1293 if (n_d >= 0) {
1294 data->dest->descr_cmd[n_d]->associated = data->piece;
1295 data->dest->descr_cmd[n_d]->tSt = data->tSt;
1296 data->dest->descr_cmd[n_d]->tEn = data->tEn;
1297 }
1298 return;
1299 }
1301 NR::Point chk;
1302 const NR::Point req = miPos + width * miNor;
1303 {
1304 PathDescrCubicTo temp(enPos + width * enNor,
1305 stGue * stTgt,
1306 enGue * enTgt);
1307 double chTle, chRad;
1308 NR::Point chTgt;
1309 TangentOnCubAt (0.5, stPos+width*stNor,
1310 temp, false, chk, chTgt, chTle, chRad);
1311 }
1312 const NR::Point diff = req - chk;
1313 const double err = dot(diff,diff);
1314 if (err <= tol ) { // tolerance is given as a quadratic value, no need to use tol*tol here
1315 // printf("%f <= %f %i\n",err,tol,lev);
1316 int n_d = data->dest->CubicTo (enPos + width*enNor,
1317 stGue*stTgt,
1318 enGue*enTgt);
1319 if (n_d >= 0) {
1320 data->dest->descr_cmd[n_d]->associated = data->piece;
1321 data->dest->descr_cmd[n_d]->tSt = data->tSt;
1322 data->dest->descr_cmd[n_d]->tEn = data->tEn;
1323 }
1324 } else {
1325 outline_callback_data desc = *data;
1327 desc.tSt = data->tSt;
1328 desc.tEn = (data->tSt + data->tEn) / 2;
1329 desc.x1 = data->x1;
1330 desc.y1 = data->y1;
1331 desc.x2 = miPos[0];
1332 desc.y2 = miPos[1];
1333 desc.d.c.dx1 = 0.5 * stTle * stTgt[0];
1334 desc.d.c.dy1 = 0.5 * stTle * stTgt[1];
1335 desc.d.c.dx2 = 0.5 * miTle * miTgt[0];
1336 desc.d.c.dy2 = 0.5 * miTle * miTgt[1];
1337 RecStdCubicTo (&desc, tol, width, lev - 1);
1339 desc.tSt = (data->tSt + data->tEn) / 2;
1340 desc.tEn = data->tEn;
1341 desc.x1 = miPos[0];
1342 desc.y1 = miPos[1];
1343 desc.x2 = data->x2;
1344 desc.y2 = data->y2;
1345 desc.d.c.dx1 = 0.5 * miTle * miTgt[0];
1346 desc.d.c.dy1 = 0.5 * miTle * miTgt[1];
1347 desc.d.c.dx2 = 0.5 * enTle * enTgt[0];
1348 desc.d.c.dy2 = 0.5 * enTle * enTgt[1];
1349 RecStdCubicTo (&desc, tol, width, lev - 1);
1350 }
1351 }
1353 void
1354 Path::StdCubicTo (Path::outline_callback_data * data, double tol, double width)
1355 {
1356 // fflush (stdout);
1357 RecStdCubicTo (data, tol, width, 8);
1358 }
1360 void
1361 Path::StdBezierTo (Path::outline_callback_data * data, double tol, double width)
1362 {
1363 PathDescrBezierTo tempb(NR::Point(data->x2, data->y2), 1);
1364 PathDescrIntermBezierTo tempi(NR::Point(data->d.b.mx, data->d.b.my));
1365 NR::Point stPos, enPos, stTgt, enTgt;
1366 double stRad, enRad, stTle, enTle;
1367 NR::Point tmp(data->x1,data->y1);
1368 TangentOnBezAt (0.0, tmp, tempi, tempb, false, stPos, stTgt,
1369 stTle, stRad);
1370 TangentOnBezAt (1.0, tmp, tempi, tempb, true, enPos, enTgt,
1371 enTle, enRad);
1372 data->d.c.dx1 = stTle * stTgt[0];
1373 data->d.c.dy1 = stTle * stTgt[1];
1374 data->d.c.dx2 = enTle * enTgt[0];
1375 data->d.c.dy2 = enTle * enTgt[1];
1376 RecStdCubicTo (data, tol, width, 8);
1377 }
1379 void
1380 Path::RecStdArcTo (outline_callback_data * data, double tol, double width,
1381 int lev)
1382 {
1383 NR::Point stPos, miPos, enPos;
1384 NR::Point stTgt, enTgt, miTgt, stNor, enNor, miNor;
1385 double stRad, miRad, enRad;
1386 double stTle, miTle, enTle;
1387 // un cubic
1388 {
1389 PathDescrArcTo temp(NR::Point(data->x2, data->y2),
1390 data->d.a.rx, data->d.a.ry,
1391 data->d.a.angle, data->d.a.large, data->d.a.clock);
1393 NR::Point tmp(data->x1,data->y1);
1394 TangentOnArcAt (data->d.a.stA, tmp, temp, stPos, stTgt,
1395 stTle, stRad);
1396 TangentOnArcAt ((data->d.a.stA + data->d.a.enA) / 2, tmp,
1397 temp, miPos, miTgt, miTle, miRad);
1398 TangentOnArcAt (data->d.a.enA, tmp, temp, enPos, enTgt,
1399 enTle, enRad);
1400 stNor=stTgt.cw();
1401 miNor=miTgt.cw();
1402 enNor=enTgt.cw();
1403 }
1405 double stGue = 1, miGue = 1, enGue = 1;
1406 if (fabs (stRad) > 0.01)
1407 stGue += width / stRad;
1408 if (fabs (miRad) > 0.01)
1409 miGue += width / miRad;
1410 if (fabs (enRad) > 0.01)
1411 enGue += width / enRad;
1412 stGue *= stTle;
1413 miGue *= miTle;
1414 enGue *= enTle;
1415 double sang, eang;
1416 {
1417 NR::Point tms(data->x1,data->y1),tme(data->x2,data->y2);
1418 ArcAngles (tms,tme, data->d.a.rx,
1419 data->d.a.ry, data->d.a.angle, data->d.a.large, !data->d.a.clock,
1420 sang, eang);
1421 }
1422 double scal = eang - sang;
1423 if (scal < 0)
1424 scal += 2 * M_PI;
1425 if (scal > 2 * M_PI)
1426 scal -= 2 * M_PI;
1427 scal *= data->d.a.enA - data->d.a.stA;
1429 if (lev <= 0)
1430 {
1431 int n_d = data->dest->CubicTo (enPos + width*enNor,
1432 stGue*scal*stTgt,
1433 enGue*scal*enTgt);
1434 if (n_d >= 0) {
1435 data->dest->descr_cmd[n_d]->associated = data->piece;
1436 data->dest->descr_cmd[n_d]->tSt = data->d.a.stA;
1437 data->dest->descr_cmd[n_d]->tEn = data->d.a.enA;
1438 }
1439 return;
1440 }
1442 NR::Point chk;
1443 const NR::Point req = miPos + width*miNor;
1444 {
1445 PathDescrCubicTo temp(enPos + width * enNor, stGue * scal * stTgt, enGue * scal * enTgt);
1446 double chTle, chRad;
1447 NR::Point chTgt;
1448 TangentOnCubAt (0.5, stPos+width*stNor,
1449 temp, false, chk, chTgt, chTle, chRad);
1450 }
1451 const NR::Point diff = req - chk;
1452 const double err = (dot(diff,diff));
1453 if (err <= tol * tol)
1454 {
1455 int n_d = data->dest->CubicTo (enPos + width*enNor,
1456 stGue*scal*stTgt,
1457 enGue*scal*enTgt);
1458 if (n_d >= 0) {
1459 data->dest->descr_cmd[n_d]->associated = data->piece;
1460 data->dest->descr_cmd[n_d]->tSt = data->d.a.stA;
1461 data->dest->descr_cmd[n_d]->tEn = data->d.a.enA;
1462 }
1463 } else {
1464 outline_callback_data desc = *data;
1466 desc.d.a.stA = data->d.a.stA;
1467 desc.d.a.enA = (data->d.a.stA + data->d.a.enA) / 2;
1468 RecStdArcTo (&desc, tol, width, lev - 1);
1470 desc.d.a.stA = (data->d.a.stA + data->d.a.enA) / 2;
1471 desc.d.a.enA = data->d.a.enA;
1472 RecStdArcTo (&desc, tol, width, lev - 1);
1473 }
1474 }
1476 void
1477 Path::StdArcTo (Path::outline_callback_data * data, double tol, double width)
1478 {
1479 data->d.a.stA = 0.0;
1480 data->d.a.enA = 1.0;
1481 RecStdArcTo (data, tol, width, 8);
1482 }
1484 /*
1485 Local Variables:
1486 mode:c++
1487 c-file-style:"stroustrup"
1488 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1489 indent-tabs-mode:nil
1490 fill-column:99
1491 End:
1492 */
1493 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :