Code

Split SPCanvasItem and SPCanvasGroup to individual .h files. Pruned forward header.
[inkscape.git] / src / livarot / PathOutline.cpp
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;
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)
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);
226 void
227 Path::InsideOutline (Path * dest, double width, JoinType join, ButtType butt,
228                      double miter)
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;
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)
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         }
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)
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         }
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)
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     }
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)
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         }
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)
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;
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)
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;
1166 void
1167 Path::OutlineJoin (Path * dest, Geom::Point pos, Geom::Point stNor, Geom::Point enNor, double width,
1168                    JoinType join, double miter)
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         }
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)
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         }
1363 void
1364 Path::StdCubicTo (Path::outline_callback_data * data, double tol, double width)
1366 //      fflush (stdout);
1367         RecStdCubicTo (data, tol, width, 8);
1370 void
1371 Path::StdBezierTo (Path::outline_callback_data * data, double tol, double width)
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);
1389 void
1390 Path::RecStdArcTo (outline_callback_data * data, double tol, double width,
1391                    int lev)
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         }
1486 void
1487 Path::StdArcTo (Path::outline_callback_data * data, double tol, double width)
1489         data->d.a.stA = 0.0;
1490         data->d.a.enA = 1.0;
1491         RecStdArcTo (data, tol, width, 8);
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 :