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