1 /*
2 * ShapeMisc.cpp
3 * nlivarot
4 *
5 * Created by fred on Sun Jul 20 2003.
6 *
7 */
9 #include "livarot/Shape.h"
10 #include <libnr/nr-point-fns.h>
11 #include "livarot/Path.h"
12 #include "livarot/path-description.h"
13 #include <glib.h>
15 /*
16 * polygon offset and polyline to path reassembling (when using back data)
17 */
19 // until i find something better
20 #define MiscNormalize(v) {\
21 double _l=sqrt(dot(v,v)); \
22 if ( _l < 0.0000001 ) { \
23 v[0]=v[1]=0; \
24 } else { \
25 v/=_l; \
26 }\
27 }
29 // extracting the contour of an uncrossed polygon: a mere depth first search
30 // more precisely that's extracting an eulerian path from a graph, but here we want to split
31 // the polygon into contours and avoid holes. so we take a "next counter-clockwise edge first" approach
32 // (make a checkboard and extract its contours to see the difference)
33 void
34 Shape::ConvertToForme (Path * dest)
35 {
36 if (numberOfPoints() <= 1 || numberOfEdges() <= 1)
37 return;
39 // prepare
40 dest->Reset ();
42 MakePointData (true);
43 MakeEdgeData (true);
44 MakeSweepDestData (true);
46 for (int i = 0; i < numberOfPoints(); i++)
47 {
48 pData[i].rx[0] = Round (getPoint(i).x[0]);
49 pData[i].rx[1] = Round (getPoint(i).x[1]);
50 }
51 for (int i = 0; i < numberOfEdges(); i++)
52 {
53 eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
54 }
56 // sort edge clockwise, with the closest after midnight being first in the doubly-linked list
57 // that's vital to the algorithm...
58 SortEdges ();
60 // depth-first search implies: we make a stack of edges traversed.
61 // precParc: previous in the stack
62 // suivParc: next in the stack
63 for (int i = 0; i < numberOfEdges(); i++)
64 {
65 swdData[i].misc = 0;
66 swdData[i].precParc = swdData[i].suivParc = -1;
67 }
69 int searchInd = 0;
71 int lastPtUsed = 0;
72 do
73 {
74 // first get a starting point, and a starting edge
75 // -> take the upper left point, and take its first edge
76 // points traversed have swdData[].misc != 0, so it's easy
77 int startBord = -1;
78 {
79 int fi = 0;
80 for (fi = lastPtUsed; fi < numberOfPoints(); fi++)
81 {
82 if (getPoint(fi).incidentEdge[FIRST] >= 0 && swdData[getPoint(fi).incidentEdge[FIRST]].misc == 0)
83 break;
84 }
85 lastPtUsed = fi + 1;
86 if (fi < numberOfPoints())
87 {
88 int bestB = getPoint(fi).incidentEdge[FIRST];
89 while (bestB >= 0 && getEdge(bestB).st != fi)
90 bestB = NextAt (fi, bestB);
91 if (bestB >= 0)
92 {
93 startBord = bestB;
94 dest->MoveTo (getPoint(getEdge(startBord).en).x);
95 }
96 }
97 }
98 // and walk the graph, doing contours when needed
99 if (startBord >= 0)
100 {
101 // parcours en profondeur pour mettre les leF et riF a leurs valeurs
102 swdData[startBord].misc = (void *) 1;
103 // printf("part de %d\n",startBord);
104 int curBord = startBord;
105 bool back = false;
106 swdData[curBord].precParc = -1;
107 swdData[curBord].suivParc = -1;
108 do
109 {
110 int cPt = getEdge(curBord).en;
111 int nb = curBord;
112 // printf("de curBord= %d au point %i -> ",curBord,cPt);
113 // get next edge
114 do
115 {
116 int nnb = CycleNextAt (cPt, nb);
117 if (nnb == nb)
118 {
119 // cul-de-sac
120 nb = -1;
121 break;
122 }
123 nb = nnb;
124 if (nb < 0 || nb == curBord)
125 break;
126 }
127 while (swdData[nb].misc != 0 || getEdge(nb).st != cPt);
129 if (nb < 0 || nb == curBord)
130 {
131 // no next edge: end of this contour, we get back
132 if (back == false)
133 dest->Close ();
134 back = true;
135 // retour en arriere
136 curBord = swdData[curBord].precParc;
137 // printf("retour vers %d\n",curBord);
138 if (curBord < 0)
139 break;
140 }
141 else
142 {
143 // new edge, maybe for a new contour
144 if (back)
145 {
146 // we were backtracking, so if we have a new edge, that means we're creating a new contour
147 dest->MoveTo (getPoint(cPt).x);
148 back = false;
149 }
150 swdData[nb].misc = (void *) 1;
151 swdData[nb].ind = searchInd++;
152 swdData[nb].precParc = curBord;
153 swdData[curBord].suivParc = nb;
154 curBord = nb;
155 // printf("suite %d\n",curBord);
156 {
157 // add that edge
158 dest->LineTo (getPoint(getEdge(nb).en).x);
159 }
160 }
161 }
162 while (1 /*swdData[curBord].precParc >= 0 */ );
163 // fin du cas non-oriente
164 }
165 }
166 while (lastPtUsed < numberOfPoints());
168 MakePointData (false);
169 MakeEdgeData (false);
170 MakeSweepDestData (false);
171 }
173 // same as before, but each time we have a contour, try to reassemble the segments on it to make chunks of
174 // the original(s) path(s)
175 // originals are in the orig array, whose size is nbP
176 void
177 Shape::ConvertToForme (Path * dest, int nbP, Path * *orig, bool splitWhenForced)
178 {
179 if (numberOfPoints() <= 1 || numberOfEdges() <= 1)
180 return;
181 // if (Eulerian (true) == false)
182 // return;
184 if (_has_back_data == false)
185 {
186 ConvertToForme (dest);
187 return;
188 }
190 dest->Reset ();
192 MakePointData (true);
193 MakeEdgeData (true);
194 MakeSweepDestData (true);
196 for (int i = 0; i < numberOfPoints(); i++)
197 {
198 pData[i].rx[0] = Round (getPoint(i).x[0]);
199 pData[i].rx[1] = Round (getPoint(i).x[1]);
200 }
201 for (int i = 0; i < numberOfEdges(); i++)
202 {
203 eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
204 }
206 SortEdges ();
208 for (int i = 0; i < numberOfEdges(); i++)
209 {
210 swdData[i].misc = 0;
211 swdData[i].precParc = swdData[i].suivParc = -1;
212 }
214 int searchInd = 0;
216 int lastPtUsed = 0;
217 do
218 {
219 int startBord = -1;
220 {
221 int fi = 0;
222 for (fi = lastPtUsed; fi < numberOfPoints(); fi++)
223 {
224 if (getPoint(fi).incidentEdge[FIRST] >= 0 && swdData[getPoint(fi).incidentEdge[FIRST]].misc == 0)
225 break;
226 }
227 lastPtUsed = fi + 1;
228 if (fi < numberOfPoints())
229 {
230 int bestB = getPoint(fi).incidentEdge[FIRST];
231 while (bestB >= 0 && getEdge(bestB).st != fi)
232 bestB = NextAt (fi, bestB);
233 if (bestB >= 0)
234 {
235 startBord = bestB;
236 }
237 }
238 }
239 if (startBord >= 0)
240 {
241 // parcours en profondeur pour mettre les leF et riF a leurs valeurs
242 swdData[startBord].misc = (void *) 1;
243 //printf("part de %d\n",startBord);
244 int curBord = startBord;
245 bool back = false;
246 swdData[curBord].precParc = -1;
247 swdData[curBord].suivParc = -1;
248 int curStartPt=getEdge(curBord).st;
249 do
250 {
251 int cPt = getEdge(curBord).en;
252 int nb = curBord;
253 //printf("de curBord= %d au point %i -> ",curBord,cPt);
254 do
255 {
256 int nnb = CycleNextAt (cPt, nb);
257 if (nnb == nb)
258 {
259 // cul-de-sac
260 nb = -1;
261 break;
262 }
263 nb = nnb;
264 if (nb < 0 || nb == curBord)
265 break;
266 }
267 while (swdData[nb].misc != 0 || getEdge(nb).st != cPt);
269 if (nb < 0 || nb == curBord)
270 {
271 if (back == false)
272 {
273 if (curBord == startBord || curBord < 0)
274 {
275 // probleme -> on vire le moveto
276 // dest->descr_nb--;
277 }
278 else
279 {
280 swdData[curBord].suivParc = -1;
281 AddContour (dest, nbP, orig, startBord, curBord,splitWhenForced);
282 }
283 // dest->Close();
284 }
285 back = true;
286 // retour en arriere
287 curBord = swdData[curBord].precParc;
288 //printf("retour vers %d\n",curBord);
289 if (curBord < 0)
290 break;
291 }
292 else
293 {
294 if (back)
295 {
296 back = false;
297 startBord = nb;
298 curStartPt=getEdge(nb).st;
299 } else {
300 if ( getEdge(curBord).en == curStartPt ) {
301 //printf("contour %i ",curStartPt);
302 swdData[curBord].suivParc = -1;
303 AddContour (dest, nbP, orig, startBord, curBord,splitWhenForced);
304 startBord=nb;
305 }
306 }
307 swdData[nb].misc = (void *) 1;
308 swdData[nb].ind = searchInd++;
309 swdData[nb].precParc = curBord;
310 swdData[curBord].suivParc = nb;
311 curBord = nb;
312 //printf("suite %d\n",curBord);
313 }
314 }
315 while (1 /*swdData[curBord].precParc >= 0 */ );
316 // fin du cas non-oriente
317 }
318 }
319 while (lastPtUsed < numberOfPoints());
321 MakePointData (false);
322 MakeEdgeData (false);
323 MakeSweepDestData (false);
324 }
325 void
326 Shape::ConvertToFormeNested (Path * dest, int nbP, Path * *orig, int wildPath,int &nbNest,int *&nesting,int *&contStart,bool splitWhenForced)
327 {
328 nesting=NULL;
329 contStart=NULL;
330 nbNest=0;
332 if (numberOfPoints() <= 1 || numberOfEdges() <= 1)
333 return;
334 // if (Eulerian (true) == false)
335 // return;
337 if (_has_back_data == false)
338 {
339 ConvertToForme (dest);
340 return;
341 }
343 dest->Reset ();
345 // MakePointData (true);
346 MakeEdgeData (true);
347 MakeSweepDestData (true);
349 for (int i = 0; i < numberOfPoints(); i++)
350 {
351 pData[i].rx[0] = Round (getPoint(i).x[0]);
352 pData[i].rx[1] = Round (getPoint(i).x[1]);
353 }
354 for (int i = 0; i < numberOfEdges(); i++)
355 {
356 eData[i].rdx = pData[getEdge(i).en].rx - pData[getEdge(i).st].rx;
357 }
359 SortEdges ();
361 for (int i = 0; i < numberOfEdges(); i++)
362 {
363 swdData[i].misc = 0;
364 swdData[i].precParc = swdData[i].suivParc = -1;
365 }
367 int searchInd = 0;
369 int lastPtUsed = 0;
370 do
371 {
372 int dadContour=-1;
373 int startBord = -1;
374 {
375 int fi = 0;
376 for (fi = lastPtUsed; fi < numberOfPoints(); fi++)
377 {
378 if (getPoint(fi).incidentEdge[FIRST] >= 0 && swdData[getPoint(fi).incidentEdge[FIRST]].misc == 0)
379 break;
380 }
381 {
382 int askTo = pData[fi].askForWindingB;
383 if (askTo < 0 || askTo >= numberOfEdges() ) {
384 dadContour=-1;
385 } else {
386 dadContour = GPOINTER_TO_INT(swdData[askTo].misc);
387 dadContour-=1; // pour compenser le decalage
388 }
389 }
390 lastPtUsed = fi + 1;
391 if (fi < numberOfPoints())
392 {
393 int bestB = getPoint(fi).incidentEdge[FIRST];
394 while (bestB >= 0 && getEdge(bestB).st != fi)
395 bestB = NextAt (fi, bestB);
396 if (bestB >= 0)
397 {
398 startBord = bestB;
399 }
400 }
401 }
402 if (startBord >= 0)
403 {
404 // parcours en profondeur pour mettre les leF et riF a leurs valeurs
405 swdData[startBord].misc = (void *) (1+nbNest);
406 //printf("part de %d\n",startBord);
407 int curBord = startBord;
408 bool back = false;
409 swdData[curBord].precParc = -1;
410 swdData[curBord].suivParc = -1;
411 int curStartPt=getEdge(curBord).st;
412 do
413 {
414 int cPt = getEdge(curBord).en;
415 int nb = curBord;
416 //printf("de curBord= %d au point %i -> ",curBord,cPt);
417 do
418 {
419 int nnb = CycleNextAt (cPt, nb);
420 if (nnb == nb)
421 {
422 // cul-de-sac
423 nb = -1;
424 break;
425 }
426 nb = nnb;
427 if (nb < 0 || nb == curBord)
428 break;
429 }
430 while (swdData[nb].misc != 0 || getEdge(nb).st != cPt);
432 if (nb < 0 || nb == curBord)
433 {
434 if (back == false)
435 {
436 if (curBord == startBord || curBord < 0)
437 {
438 // probleme -> on vire le moveto
439 // dest->descr_nb--;
440 }
441 else
442 {
443 bool escapePath=false;
444 int tb=curBord;
445 while ( tb >= 0 && tb < numberOfEdges() ) {
446 if ( ebData[tb].pathID == wildPath ) {
447 escapePath=true;
448 break;
449 }
450 tb=swdData[tb].precParc;
451 }
452 nesting=(int*)g_realloc(nesting,(nbNest+1)*sizeof(int));
453 contStart=(int*)g_realloc(contStart,(nbNest+1)*sizeof(int));
454 contStart[nbNest]=dest->descr_cmd.size();
455 if ( escapePath ) {
456 nesting[nbNest++]=-1; // contient des bouts de coupure -> a part
457 } else {
458 nesting[nbNest++]=dadContour;
459 }
460 swdData[curBord].suivParc = -1;
461 AddContour (dest, nbP, orig, startBord, curBord,splitWhenForced);
462 }
463 // dest->Close();
464 }
465 back = true;
466 // retour en arriere
467 curBord = swdData[curBord].precParc;
468 //printf("retour vers %d\n",curBord);
469 if (curBord < 0)
470 break;
471 }
472 else
473 {
474 if (back)
475 {
476 back = false;
477 startBord = nb;
478 curStartPt=getEdge(nb).st;
479 } else {
480 if ( getEdge(curBord).en == curStartPt ) {
481 //printf("contour %i ",curStartPt);
483 bool escapePath=false;
484 int tb=curBord;
485 while ( tb >= 0 && tb < numberOfEdges() ) {
486 if ( ebData[tb].pathID == wildPath ) {
487 escapePath=true;
488 break;
489 }
490 tb=swdData[tb].precParc;
491 }
492 nesting=(int*)g_realloc(nesting,(nbNest+1)*sizeof(int));
493 contStart=(int*)g_realloc(contStart,(nbNest+1)*sizeof(int));
494 contStart[nbNest]=dest->descr_cmd.size();
495 if ( escapePath ) {
496 nesting[nbNest++]=-1; // contient des bouts de coupure -> a part
497 } else {
498 nesting[nbNest++]=dadContour;
499 }
501 swdData[curBord].suivParc = -1;
502 AddContour (dest, nbP, orig, startBord, curBord,splitWhenForced);
503 startBord=nb;
504 }
505 }
506 swdData[nb].misc = (void *) (1+nbNest);
507 swdData[nb].ind = searchInd++;
508 swdData[nb].precParc = curBord;
509 swdData[curBord].suivParc = nb;
510 curBord = nb;
511 //printf("suite %d\n",curBord);
512 }
513 }
514 while (1 /*swdData[curBord].precParc >= 0 */ );
515 // fin du cas non-oriente
516 }
517 }
518 while (lastPtUsed < numberOfPoints());
520 MakePointData (false);
521 MakeEdgeData (false);
522 MakeSweepDestData (false);
523 }
526 int
527 Shape::MakeTweak (int mode, Shape *a, double dec, JoinType join, double miter, bool do_profile, NR::Point c, NR::Point vector, double radius, NR::Matrix *i2doc)
528 {
529 Reset (0, 0);
530 MakeBackData(a->_has_back_data);
532 bool done_something = false;
534 double power;
535 if (mode == tweak_mode_push) {
536 power = NR::L2(vector);
537 } else {
538 power = dec;
539 }
541 if (power == 0)
542 {
543 _pts = a->_pts;
544 if (numberOfPoints() > maxPt)
545 {
546 maxPt = numberOfPoints();
547 if (_has_points_data) {
548 pData.resize(maxPt);
549 _point_data_initialised = false;
550 _bbox_up_to_date = false;
551 }
552 }
554 _aretes = a->_aretes;
555 if (numberOfEdges() > maxAr)
556 {
557 maxAr = numberOfEdges();
558 if (_has_edges_data)
559 eData.resize(maxAr);
560 if (_has_sweep_src_data)
561 swsData.resize(maxAr);
562 if (_has_sweep_dest_data)
563 swdData.resize(maxAr);
564 if (_has_raster_data)
565 swrData.resize(maxAr);
566 if (_has_back_data)
567 ebData.resize(maxAr);
568 }
569 return 0;
570 }
571 if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1 || a->type != shape_polygon)
572 return shape_input_err;
574 a->SortEdges ();
576 a->MakeSweepDestData (true);
577 a->MakeSweepSrcData (true);
579 for (int i = 0; i < a->numberOfEdges(); i++)
580 {
581 int stB = -1, enB = -1;
582 if (power <= 0 || mode == tweak_mode_push || mode == tweak_mode_repel || mode == tweak_mode_roughen) {
583 stB = a->CyclePrevAt (a->getEdge(i).st, i);
584 enB = a->CycleNextAt (a->getEdge(i).en, i);
585 } else {
586 stB = a->CycleNextAt (a->getEdge(i).st, i);
587 enB = a->CyclePrevAt (a->getEdge(i).en, i);
588 }
590 NR::Point stD, seD, enD;
591 double stL, seL, enL;
592 stD = a->getEdge(stB).dx;
593 seD = a->getEdge(i).dx;
594 enD = a->getEdge(enB).dx;
596 stL = sqrt (dot(stD,stD));
597 seL = sqrt (dot(seD,seD));
598 enL = sqrt (dot(enD,enD));
599 MiscNormalize (stD);
600 MiscNormalize (enD);
601 MiscNormalize (seD);
603 NR::Point ptP;
604 int stNo, enNo;
605 ptP = a->getPoint(a->getEdge(i).st).x;
607 if (mode == tweak_mode_push) {
608 power = 1;
609 }
611 NR::Point to_center = ptP * (*i2doc) - c;
612 NR::Point to_center_normalized = (1/NR::L2(to_center)) * to_center;
614 double this_power;
615 if (do_profile && i2doc) {
616 double alpha = 1;
617 double x;
618 if (mode == tweak_mode_repel) {
619 x = (NR::L2(to_center)/radius);
620 } else {
621 x = (NR::L2(ptP * (*i2doc) - c)/radius);
622 }
623 if (x > 1) {
624 this_power = 0;
625 } else if (x <= 0) {
626 if (mode == tweak_mode_repel) {
627 this_power = 0;
628 } else {
629 this_power = power;
630 }
631 } else {
632 this_power = power * (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
633 }
634 } else {
635 if (mode == tweak_mode_repel) {
636 this_power = 0;
637 } else {
638 this_power = power;
639 }
640 }
642 if (this_power != 0)
643 done_something = true;
645 NR::Point this_vec(0,0);
646 if (mode == tweak_mode_push) {
647 this_vec = this_power * vector;
648 } else if (mode == tweak_mode_repel) {
649 this_vec = this_power * to_center_normalized;
650 } else if (mode == tweak_mode_roughen) {
651 double angle = g_random_double_range(0, 2*M_PI);
652 this_vec = g_random_double_range(0, 1) * this_power * NR::Point(sin(angle), cos(angle));
653 }
655 int usePathID=-1;
656 int usePieceID=0;
657 double useT=0.0;
658 if ( a->_has_back_data ) {
659 if ( a->ebData[i].pathID >= 0 && a->ebData[stB].pathID == a->ebData[i].pathID && a->ebData[stB].pieceID == a->ebData[i].pieceID
660 && a->ebData[stB].tEn == a->ebData[i].tSt ) {
661 usePathID=a->ebData[i].pathID;
662 usePieceID=a->ebData[i].pieceID;
663 useT=a->ebData[i].tSt;
664 } else {
665 usePathID=a->ebData[i].pathID;
666 usePieceID=0;
667 useT=0;
668 }
669 }
671 if (mode == tweak_mode_push || mode == tweak_mode_repel || mode == tweak_mode_roughen) {
672 Path::DoLeftJoin (this, 0, join, ptP+this_vec, stD+this_vec, seD+this_vec, miter, stL, seL,
673 stNo, enNo,usePathID,usePieceID,useT);
674 a->swsData[i].stPt = enNo;
675 a->swsData[stB].enPt = stNo;
676 } else {
677 if (power > 0) {
678 Path::DoRightJoin (this, this_power, join, ptP, stD, seD, miter, stL, seL,
679 stNo, enNo,usePathID,usePieceID,useT);
680 a->swsData[i].stPt = enNo;
681 a->swsData[stB].enPt = stNo;
682 } else {
683 Path::DoLeftJoin (this, -this_power, join, ptP, stD, seD, miter, stL, seL,
684 stNo, enNo,usePathID,usePieceID,useT);
685 a->swsData[i].stPt = enNo;
686 a->swsData[stB].enPt = stNo;
687 }
688 }
689 }
691 if (power < 0 || mode == tweak_mode_push || mode == tweak_mode_repel || mode == tweak_mode_roughen)
692 {
693 for (int i = 0; i < numberOfEdges(); i++)
694 Inverse (i);
695 }
697 if ( _has_back_data ) {
698 for (int i = 0; i < a->numberOfEdges(); i++)
699 {
700 int nEd=AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
701 ebData[nEd]=a->ebData[i];
702 }
703 } else {
704 for (int i = 0; i < a->numberOfEdges(); i++)
705 {
706 AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
707 }
708 }
710 a->MakeSweepSrcData (false);
711 a->MakeSweepDestData (false);
713 return (done_something? 0 : shape_nothing_to_do);
714 }
717 // offsets
718 // take each edge, offset it, and make joins with previous at edge start and next at edge end (previous and
719 // next being with respect to the clockwise order)
720 // you gotta be very careful with the join, as anything but the right one will fuck everything up
721 // see PathStroke.cpp for the "right" joins
722 int
723 Shape::MakeOffset (Shape * a, double dec, JoinType join, double miter, bool do_profile, double cx, double cy, double radius, NR::Matrix *i2doc)
724 {
725 Reset (0, 0);
726 MakeBackData(a->_has_back_data);
728 bool done_something = false;
730 if (dec == 0)
731 {
732 _pts = a->_pts;
733 if (numberOfPoints() > maxPt)
734 {
735 maxPt = numberOfPoints();
736 if (_has_points_data) {
737 pData.resize(maxPt);
738 _point_data_initialised = false;
739 _bbox_up_to_date = false;
740 }
741 }
743 _aretes = a->_aretes;
744 if (numberOfEdges() > maxAr)
745 {
746 maxAr = numberOfEdges();
747 if (_has_edges_data)
748 eData.resize(maxAr);
749 if (_has_sweep_src_data)
750 swsData.resize(maxAr);
751 if (_has_sweep_dest_data)
752 swdData.resize(maxAr);
753 if (_has_raster_data)
754 swrData.resize(maxAr);
755 if (_has_back_data)
756 ebData.resize(maxAr);
757 }
758 return 0;
759 }
760 if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1 || a->type != shape_polygon)
761 return shape_input_err;
763 a->SortEdges ();
765 a->MakeSweepDestData (true);
766 a->MakeSweepSrcData (true);
768 for (int i = 0; i < a->numberOfEdges(); i++)
769 {
770 // int stP=a->swsData[i].stPt/*,enP=a->swsData[i].enPt*/;
771 int stB = -1, enB = -1;
772 if (dec > 0)
773 {
774 stB = a->CycleNextAt (a->getEdge(i).st, i);
775 enB = a->CyclePrevAt (a->getEdge(i).en, i);
776 }
777 else
778 {
779 stB = a->CyclePrevAt (a->getEdge(i).st, i);
780 enB = a->CycleNextAt (a->getEdge(i).en, i);
781 }
783 NR::Point stD, seD, enD;
784 double stL, seL, enL;
785 stD = a->getEdge(stB).dx;
786 seD = a->getEdge(i).dx;
787 enD = a->getEdge(enB).dx;
789 stL = sqrt (dot(stD,stD));
790 seL = sqrt (dot(seD,seD));
791 enL = sqrt (dot(enD,enD));
792 MiscNormalize (stD);
793 MiscNormalize (enD);
794 MiscNormalize (seD);
796 NR::Point ptP;
797 int stNo, enNo;
798 ptP = a->getPoint(a->getEdge(i).st).x;
800 double this_dec;
801 if (do_profile && i2doc) {
802 double alpha = 1;
803 double x = (NR::L2(ptP * (*i2doc) - NR::Point(cx,cy))/radius);
804 if (x > 1) {
805 this_dec = 0;
806 } else if (x <= 0) {
807 this_dec = dec;
808 } else {
809 this_dec = dec * (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
810 }
811 } else {
812 this_dec = dec;
813 }
815 if (this_dec != 0)
816 done_something = true;
818 int usePathID=-1;
819 int usePieceID=0;
820 double useT=0.0;
821 if ( a->_has_back_data ) {
822 if ( a->ebData[i].pathID >= 0 && a->ebData[stB].pathID == a->ebData[i].pathID && a->ebData[stB].pieceID == a->ebData[i].pieceID
823 && a->ebData[stB].tEn == a->ebData[i].tSt ) {
824 usePathID=a->ebData[i].pathID;
825 usePieceID=a->ebData[i].pieceID;
826 useT=a->ebData[i].tSt;
827 } else {
828 usePathID=a->ebData[i].pathID;
829 usePieceID=0;
830 useT=0;
831 }
832 }
833 if (dec > 0)
834 {
835 Path::DoRightJoin (this, this_dec, join, ptP, stD, seD, miter, stL, seL,
836 stNo, enNo,usePathID,usePieceID,useT);
837 a->swsData[i].stPt = enNo;
838 a->swsData[stB].enPt = stNo;
839 }
840 else
841 {
842 Path::DoLeftJoin (this, -this_dec, join, ptP, stD, seD, miter, stL, seL,
843 stNo, enNo,usePathID,usePieceID,useT);
844 a->swsData[i].stPt = enNo;
845 a->swsData[stB].enPt = stNo;
846 }
847 }
849 if (dec < 0)
850 {
851 for (int i = 0; i < numberOfEdges(); i++)
852 Inverse (i);
853 }
855 if ( _has_back_data ) {
856 for (int i = 0; i < a->numberOfEdges(); i++)
857 {
858 int nEd=AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
859 ebData[nEd]=a->ebData[i];
860 }
861 } else {
862 for (int i = 0; i < a->numberOfEdges(); i++)
863 {
864 AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
865 }
866 }
868 a->MakeSweepSrcData (false);
869 a->MakeSweepDestData (false);
871 return (done_something? 0 : shape_nothing_to_do);
872 }
876 // we found a contour, now reassemble the edges on it, instead of dumping them in the Path "dest" as a
877 // polyline. since it was a DFS, the precParc and suivParc make a nice doubly-linked list of the edges in
878 // the contour. the first and last edges of the contour are startBord and curBord
879 void
880 Shape::AddContour (Path * dest, int nbP, Path * *orig, int startBord, int curBord, bool splitWhenForced)
881 {
882 int bord = startBord;
884 {
885 dest->MoveTo (getPoint(getEdge(bord).st).x);
886 }
888 while (bord >= 0)
889 {
890 int nPiece = ebData[bord].pieceID;
891 int nPath = ebData[bord].pathID;
893 if (nPath < 0 || nPath >= nbP || orig[nPath] == NULL)
894 {
895 // segment batard
896 dest->LineTo (getPoint(getEdge(bord).en).x);
897 bord = swdData[bord].suivParc;
898 }
899 else
900 {
901 Path *from = orig[nPath];
902 if (nPiece < 0 || nPiece >= int(from->descr_cmd.size()))
903 {
904 // segment batard
905 dest->LineTo (getPoint(getEdge(bord).en).x);
906 bord = swdData[bord].suivParc;
907 }
908 else
909 {
910 int nType = from->descr_cmd[nPiece]->getType();
911 if (nType == descr_close || nType == descr_moveto
912 || nType == descr_forced)
913 {
914 // devrait pas arriver
915 dest->LineTo (getPoint(getEdge(bord).en).x);
916 bord = swdData[bord].suivParc;
917 }
918 else if (nType == descr_lineto)
919 {
920 bord = ReFormeLineTo (bord, curBord, dest, from);
921 }
922 else if (nType == descr_arcto)
923 {
924 bord = ReFormeArcTo (bord, curBord, dest, from);
925 }
926 else if (nType == descr_cubicto)
927 {
928 bord = ReFormeCubicTo (bord, curBord, dest, from);
929 }
930 else if (nType == descr_bezierto)
931 {
932 PathDescrBezierTo* nBData =
933 dynamic_cast<PathDescrBezierTo *>(from->descr_cmd[nPiece]);
935 if (nBData->nb == 0)
936 {
937 bord = ReFormeLineTo (bord, curBord, dest, from);
938 }
939 else
940 {
941 bord = ReFormeBezierTo (bord, curBord, dest, from);
942 }
943 }
944 else if (nType == descr_interm_bezier)
945 {
946 bord = ReFormeBezierTo (bord, curBord, dest, from);
947 }
948 else
949 {
950 // devrait pas arriver non plus
951 dest->LineTo (getPoint(getEdge(bord).en).x);
952 bord = swdData[bord].suivParc;
953 }
954 if (bord >= 0 && getPoint(getEdge(bord).st).totalDegree() > 2 ) {
955 dest->ForcePoint ();
956 } else if ( bord >= 0 && getPoint(getEdge(bord).st).oldDegree > 2 && getPoint(getEdge(bord).st).totalDegree() == 2) {
957 if ( splitWhenForced ) {
958 // pour les coupures
959 dest->ForcePoint ();
960 } else {
961 if ( _has_back_data ) {
962 int prevEdge=getPoint(getEdge(bord).st).incidentEdge[FIRST];
963 int nextEdge=getPoint(getEdge(bord).st).incidentEdge[LAST];
964 if ( getEdge(prevEdge).en != getEdge(bord).st ) {
965 int swai=prevEdge;prevEdge=nextEdge;nextEdge=swai;
966 }
967 if ( ebData[prevEdge].pieceID == ebData[nextEdge].pieceID && ebData[prevEdge].pathID == ebData[nextEdge].pathID ) {
968 if ( fabs(ebData[prevEdge].tEn-ebData[nextEdge].tSt) < 0.05 ) {
969 } else {
970 dest->ForcePoint ();
971 }
972 } else {
973 dest->ForcePoint ();
974 }
975 } else {
976 dest->ForcePoint ();
977 }
978 }
979 }
980 }
981 }
982 }
983 dest->Close ();
984 }
986 int
987 Shape::ReFormeLineTo (int bord, int curBord, Path * dest, Path * orig)
988 {
989 int nPiece = ebData[bord].pieceID;
990 int nPath = ebData[bord].pathID;
991 double /*ts=ebData[bord].tSt, */ te = ebData[bord].tEn;
992 NR::Point nx = getPoint(getEdge(bord).en).x;
993 bord = swdData[bord].suivParc;
994 while (bord >= 0)
995 {
996 if (getPoint(getEdge(bord).st).totalDegree() > 2
997 || getPoint(getEdge(bord).st).oldDegree > 2)
998 {
999 break;
1000 }
1001 if (ebData[bord].pieceID == nPiece && ebData[bord].pathID == nPath)
1002 {
1003 if (fabs (te - ebData[bord].tSt) > 0.0001)
1004 break;
1005 nx = getPoint(getEdge(bord).en).x;
1006 te = ebData[bord].tEn;
1007 }
1008 else
1009 {
1010 break;
1011 }
1012 bord = swdData[bord].suivParc;
1013 }
1014 {
1015 dest->LineTo (nx);
1016 }
1017 return bord;
1018 }
1020 int
1021 Shape::ReFormeArcTo (int bord, int curBord, Path * dest, Path * from)
1022 {
1023 int nPiece = ebData[bord].pieceID;
1024 int nPath = ebData[bord].pathID;
1025 double ts = ebData[bord].tSt, te = ebData[bord].tEn;
1026 // double px=pts[getEdge(bord).st].x,py=pts[getEdge(bord).st].y;
1027 NR::Point nx = getPoint(getEdge(bord).en).x;
1028 bord = swdData[bord].suivParc;
1029 while (bord >= 0)
1030 {
1031 if (getPoint(getEdge(bord).st).totalDegree() > 2
1032 || getPoint(getEdge(bord).st).oldDegree > 2)
1033 {
1034 break;
1035 }
1036 if (ebData[bord].pieceID == nPiece && ebData[bord].pathID == nPath)
1037 {
1038 if (fabs (te - ebData[bord].tSt) > 0.0001)
1039 {
1040 break;
1041 }
1042 nx = getPoint(getEdge(bord).en).x;
1043 te = ebData[bord].tEn;
1044 }
1045 else
1046 {
1047 break;
1048 }
1049 bord = swdData[bord].suivParc;
1050 }
1051 double sang, eang;
1052 PathDescrArcTo* nData = dynamic_cast<PathDescrArcTo *>(from->descr_cmd[nPiece]);
1053 bool nLarge = nData->large;
1054 bool nClockwise = nData->clockwise;
1055 Path::ArcAngles (from->PrevPoint (nPiece - 1), nData->p,nData->rx,nData->ry,nData->angle, nLarge, nClockwise, sang, eang);
1056 if (nClockwise)
1057 {
1058 if (sang < eang)
1059 sang += 2 * M_PI;
1060 }
1061 else
1062 {
1063 if (sang > eang)
1064 sang -= 2 * M_PI;
1065 }
1066 double delta = eang - sang;
1067 double ndelta = delta * (te - ts);
1068 if (ts > te)
1069 nClockwise = !nClockwise;
1070 if (ndelta < 0)
1071 ndelta = -ndelta;
1072 if (ndelta > M_PI)
1073 nLarge = true;
1074 else
1075 nLarge = false;
1076 /* if ( delta < 0 ) delta=-delta;
1077 if ( ndelta < 0 ) ndelta=-ndelta;
1078 if ( ( delta < M_PI && ndelta < M_PI ) || ( delta >= M_PI && ndelta >= M_PI ) ) {
1079 if ( ts < te ) {
1080 } else {
1081 nClockwise=!(nClockwise);
1082 }
1083 } else {
1084 // nLarge=!(nLarge);
1085 nLarge=false; // c'est un sous-segment -> l'arc ne peut que etre plus petit
1086 if ( ts < te ) {
1087 } else {
1088 nClockwise=!(nClockwise);
1089 }
1090 }*/
1091 {
1092 PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(from->descr_cmd[nPiece]);
1093 dest->ArcTo (nx, nData->rx,nData->ry,nData->angle, nLarge, nClockwise);
1094 }
1095 return bord;
1096 }
1098 int
1099 Shape::ReFormeCubicTo (int bord, int curBord, Path * dest, Path * from)
1100 {
1101 int nPiece = ebData[bord].pieceID;
1102 int nPath = ebData[bord].pathID;
1103 double ts = ebData[bord].tSt, te = ebData[bord].tEn;
1104 NR::Point nx = getPoint(getEdge(bord).en).x;
1105 bord = swdData[bord].suivParc;
1106 while (bord >= 0)
1107 {
1108 if (getPoint(getEdge(bord).st).totalDegree() > 2
1109 || getPoint(getEdge(bord).st).oldDegree > 2)
1110 {
1111 break;
1112 }
1113 if (ebData[bord].pieceID == nPiece && ebData[bord].pathID == nPath)
1114 {
1115 if (fabs (te - ebData[bord].tSt) > 0.0001)
1116 {
1117 break;
1118 }
1119 nx = getPoint(getEdge(bord).en).x;
1120 te = ebData[bord].tEn;
1121 }
1122 else
1123 {
1124 break;
1125 }
1126 bord = swdData[bord].suivParc;
1127 }
1128 NR::Point prevx = from->PrevPoint (nPiece - 1);
1130 NR::Point sDx, eDx;
1131 {
1132 PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(from->descr_cmd[nPiece]);
1133 Path::CubicTangent (ts, sDx, prevx,nData->start,nData->p,nData->end);
1134 Path::CubicTangent (te, eDx, prevx,nData->start,nData->p,nData->end);
1135 }
1136 sDx *= (te - ts);
1137 eDx *= (te - ts);
1138 {
1139 dest->CubicTo (nx,sDx,eDx);
1140 }
1141 return bord;
1142 }
1144 int
1145 Shape::ReFormeBezierTo (int bord, int curBord, Path * dest, Path * from)
1146 {
1147 int nPiece = ebData[bord].pieceID;
1148 int nPath = ebData[bord].pathID;
1149 double ts = ebData[bord].tSt, te = ebData[bord].tEn;
1150 int ps = nPiece, pe = nPiece;
1151 NR::Point px = getPoint(getEdge(bord).st).x;
1152 NR::Point nx = getPoint(getEdge(bord).en).x;
1153 int inBezier = -1, nbInterm = -1;
1154 int typ;
1155 typ = from->descr_cmd[nPiece]->getType();
1156 PathDescrBezierTo *nBData = NULL;
1157 if (typ == descr_bezierto)
1158 {
1159 nBData = dynamic_cast<PathDescrBezierTo *>(from->descr_cmd[nPiece]);
1160 inBezier = nPiece;
1161 nbInterm = nBData->nb;
1162 }
1163 else
1164 {
1165 int n = nPiece - 1;
1166 while (n > 0)
1167 {
1168 typ = from->descr_cmd[n]->getType();
1169 if (typ == descr_bezierto)
1170 {
1171 inBezier = n;
1172 nBData = dynamic_cast<PathDescrBezierTo *>(from->descr_cmd[n]);
1173 nbInterm = nBData->nb;
1174 break;
1175 }
1176 n--;
1177 }
1178 if (inBezier < 0)
1179 {
1180 bord = swdData[bord].suivParc;
1181 dest->LineTo (nx);
1182 return bord;
1183 }
1184 }
1185 bord = swdData[bord].suivParc;
1186 while (bord >= 0)
1187 {
1188 if (getPoint(getEdge(bord).st).totalDegree() > 2
1189 || getPoint(getEdge(bord).st).oldDegree > 2)
1190 {
1191 break;
1192 }
1193 if (ebData[bord].pathID == nPath)
1194 {
1195 if (ebData[bord].pieceID < inBezier
1196 || ebData[bord].pieceID >= inBezier + nbInterm)
1197 break;
1198 if (ebData[bord].pieceID == pe
1199 && fabs (te - ebData[bord].tSt) > 0.0001)
1200 break;
1201 if (ebData[bord].pieceID != pe
1202 && (ebData[bord].tSt > 0.0001 && ebData[bord].tSt < 0.9999))
1203 break;
1204 if (ebData[bord].pieceID != pe && (te > 0.0001 && te < 0.9999))
1205 break;
1206 nx = getPoint(getEdge(bord).en).x;
1207 te = ebData[bord].tEn;
1208 pe = ebData[bord].pieceID;
1209 }
1210 else
1211 {
1212 break;
1213 }
1214 bord = swdData[bord].suivParc;
1215 }
1217 g_return_val_if_fail(nBData != NULL, 0);
1219 if (pe == ps)
1220 {
1221 ReFormeBezierChunk (px, nx, dest, inBezier, nbInterm, from, ps,
1222 ts, te);
1223 }
1224 else if (ps < pe)
1225 {
1226 if (ts < 0.0001)
1227 {
1228 if (te > 0.9999)
1229 {
1230 dest->BezierTo (nx);
1231 for (int i = ps; i <= pe; i++)
1232 {
1233 PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]);
1234 dest->IntermBezierTo (nData->p);
1235 }
1236 dest->EndBezierTo ();
1237 }
1238 else
1239 {
1240 NR::Point tx;
1241 {
1242 PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe]);
1243 PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+1]);
1244 tx = (pnData->p + psData->p) / 2;
1245 }
1246 dest->BezierTo (tx);
1247 for (int i = ps; i < pe; i++)
1248 {
1249 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]);
1250 dest->IntermBezierTo (nData->p);
1251 }
1252 dest->EndBezierTo ();
1253 ReFormeBezierChunk (tx, nx, dest, inBezier, nbInterm,
1254 from, pe, 0.0, te);
1255 }
1256 }
1257 else
1258 {
1259 if (te > 0.9999)
1260 {
1261 NR::Point tx;
1262 {
1263 PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+1]);
1264 PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+2]);
1265 tx = (psData->p + pnData->p) / 2;
1266 }
1267 ReFormeBezierChunk (px, tx, dest, inBezier, nbInterm,
1268 from, ps, ts, 1.0);
1269 dest->BezierTo (nx);
1270 for (int i = ps + 1; i <= pe; i++)
1271 {
1272 PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]);
1273 dest->IntermBezierTo (nData->p);
1274 }
1275 dest->EndBezierTo ();
1276 }
1277 else
1278 {
1279 NR::Point tx;
1280 {
1281 PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+1]);
1282 PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+2]);
1283 tx = (pnData->p + psData->p) / 2;
1284 }
1285 ReFormeBezierChunk (px, tx, dest, inBezier, nbInterm,
1286 from, ps, ts, 1.0);
1287 {
1288 PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe]);
1289 PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+1]);
1290 tx = (pnData->p + psData->p) / 2;
1291 }
1292 dest->BezierTo (tx);
1293 for (int i = ps + 1; i <= pe; i++)
1294 {
1295 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]);
1296 dest->IntermBezierTo (nData->p);
1297 }
1298 dest->EndBezierTo ();
1299 ReFormeBezierChunk (tx, nx, dest, inBezier, nbInterm,
1300 from, pe, 0.0, te);
1301 }
1302 }
1303 }
1304 else
1305 {
1306 if (ts > 0.9999)
1307 {
1308 if (te < 0.0001)
1309 {
1310 dest->BezierTo (nx);
1311 for (int i = ps; i >= pe; i--)
1312 {
1313 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]);
1314 dest->IntermBezierTo (nData->p);
1315 }
1316 dest->EndBezierTo ();
1317 }
1318 else
1319 {
1320 NR::Point tx;
1321 {
1322 PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+1]);
1323 PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+2]);
1324 tx = (pnData->p + psData->p) / 2;
1325 }
1326 dest->BezierTo (tx);
1327 for (int i = ps; i > pe; i--)
1328 {
1329 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i+1]);
1330 dest->IntermBezierTo (nData->p);
1331 }
1332 dest->EndBezierTo ();
1333 ReFormeBezierChunk (tx, nx, dest, inBezier, nbInterm,
1334 from, pe, 1.0, te);
1335 }
1336 }
1337 else
1338 {
1339 if (te < 0.0001)
1340 {
1341 NR::Point tx;
1342 {
1343 PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps]);
1344 PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+1]);
1345 tx = (pnData->p + psData->p) / 2;
1346 }
1347 ReFormeBezierChunk (px, tx, dest, inBezier, nbInterm,
1348 from, ps, ts, 0.0);
1349 dest->BezierTo (nx);
1350 for (int i = ps + 1; i >= pe; i--)
1351 {
1352 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i]);
1353 dest->IntermBezierTo (nData->p);
1354 }
1355 dest->EndBezierTo ();
1356 }
1357 else
1358 {
1359 NR::Point tx;
1360 {
1361 PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps]);
1362 PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[ps+1]);
1363 tx = (pnData->p + psData->p) / 2;
1364 }
1365 ReFormeBezierChunk (px, tx, dest, inBezier, nbInterm,
1366 from, ps, ts, 0.0);
1367 {
1368 PathDescrIntermBezierTo* psData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+1]);
1369 PathDescrIntermBezierTo* pnData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[pe+2]);
1370 tx = (pnData->p + psData->p) / 2;
1371 }
1372 dest->BezierTo (tx);
1373 for (int i = ps + 1; i > pe; i--)
1374 {
1375 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[i]);
1376 dest->IntermBezierTo (nData->p);
1377 }
1378 dest->EndBezierTo ();
1379 ReFormeBezierChunk (tx, nx, dest, inBezier, nbInterm,
1380 from, pe, 1.0, te);
1381 }
1382 }
1383 }
1384 return bord;
1385 }
1387 void
1388 Shape::ReFormeBezierChunk (NR::Point px, NR::Point nx,
1389 Path * dest, int inBezier, int nbInterm,
1390 Path * from, int p, double ts, double te)
1391 {
1392 PathDescrBezierTo* nBData = dynamic_cast<PathDescrBezierTo*>(from->descr_cmd[inBezier]);
1393 NR::Point bstx = from->PrevPoint (inBezier - 1);
1394 NR::Point benx = nBData->p;
1396 NR::Point mx;
1397 if (p == inBezier)
1398 {
1399 // premier bout
1400 if (nbInterm <= 1)
1401 {
1402 // seul bout de la spline
1403 PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+1]);
1404 mx = nData->p;
1405 }
1406 else
1407 {
1408 // premier bout d'une spline qui en contient plusieurs
1409 PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+1]);
1410 mx = nData->p;
1411 nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+2]);
1412 benx = (nData->p + mx) / 2;
1413 }
1414 }
1415 else if (p == inBezier + nbInterm - 1)
1416 {
1417 // dernier bout
1418 // si nbInterm == 1, le cas a deja ete traite
1419 // donc dernier bout d'une spline qui en contient plusieurs
1420 PathDescrIntermBezierTo* nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+nbInterm]);
1421 mx = nData->p;
1422 nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[inBezier+nbInterm-1]);
1423 bstx = (nData->p + mx) / 2;
1424 }
1425 else
1426 {
1427 // la spline contient forcément plusieurs bouts, et ce n'est ni le premier ni le dernier
1428 PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[p+1]);
1429 mx = nData->p;
1430 nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[p]);
1431 bstx = (nData->p + mx) / 2;
1432 nData = dynamic_cast<PathDescrIntermBezierTo*>(from->descr_cmd[p+2]);
1433 benx = (nData->p + mx) / 2;
1434 }
1435 NR::Point cx;
1436 {
1437 Path::QuadraticPoint ((ts + te) / 2, cx, bstx, mx, benx);
1438 }
1439 cx = 2 * cx - (px + nx) / 2;
1440 {
1441 dest->BezierTo (nx);
1442 dest->IntermBezierTo (cx);
1443 dest->EndBezierTo ();
1444 }
1445 }
1447 #undef MiscNormalize