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