5
* Created by fred on Mon Nov 03 2003.
11
#include "livarot/path-description.h"
13
#include <libnr/nr-matrix.h>
14
#include <2geom/transforms.h>
17
* path description -> polyline
18
* and Path -> Shape (the Fill() function at the bottom)
19
* nathing fancy here: take each command and append an approximation of it to the polyline
22
void Path::ConvertWithBackData(double treshhold)
24
if ( descr_flags & descr_adding_bezier ) {
28
if ( descr_flags & descr_doing_subpath ) {
34
if ( descr_cmd.empty() ) {
42
// The initial moveto.
44
int const firstTyp = descr_cmd[0]->getType();
45
if ( firstTyp == descr_moveto ) {
46
curX = dynamic_cast<PathDescrMoveTo *>(descr_cmd[0])->p;
49
curX[Geom::X] = curX[Geom::Y] = 0;
51
lastMoveTo = AddPoint(curX, 0, 0.0, true);
54
// And the rest, one by one.
55
while ( curP < int(descr_cmd.size()) ) {
57
int const nType = descr_cmd[curP]->getType();
62
AddForcedPoint(curX, curP, 1.0);
68
PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo*>(descr_cmd[curP]);
70
lastMoveTo = AddPoint(nextX, curP, 0.0, true);
77
nextX = pts[lastMoveTo].p;
78
int n = AddPoint(nextX, curP, 1.0, false);
79
if (n > 0) pts[n].closed = true;
85
PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[curP]);
87
AddPoint(nextX,curP,1.0,false);
94
PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[curP]);
96
RecCubicTo(curX, nData->start, nextX, nData->end, treshhold, 8, 0.0, 1.0, curP);
97
AddPoint(nextX, curP, 1.0, false);
104
PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[curP]);
106
DoArc(curX, nextX, nData->rx, nData->ry, nData->angle, nData->large, nData->clockwise, treshhold, curP);
107
AddPoint(nextX, curP, 1.0, false);
113
case descr_bezierto: {
114
PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[curP]);
115
int nbInterm = nBData->nb;
119
PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
121
if ( nbInterm >= 1 ) {
122
Geom::Point bx = curX;
123
Geom::Point cx = curX;
124
Geom::Point dx = curX;
128
nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
132
for (int k = 0; k < nbInterm - 1; k++) {
138
nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
143
AddPoint(stx,curP - 1+k,1.0,false);
149
RecBezierTo(cx, stx, mx, treshhold, 8, 0.0, 1.0, curP + k);
162
if ( nbInterm > 1 ) {
163
AddPoint(stx, curP + nbInterm - 2, 1.0, false);
169
RecBezierTo(cx, stx, mx, treshhold, 8, 0.0, 1.0, curP + nbInterm - 1);
176
AddPoint(nextX, curP - 1 + nbInterm, 1.0, false);
179
curP += 1 + nbInterm;
188
void Path::Convert(double treshhold)
190
if ( descr_flags & descr_adding_bezier ) {
194
if ( descr_flags & descr_doing_subpath ) {
200
if ( descr_cmd.empty() ) {
210
int const firstTyp = descr_cmd[0]->getType();
211
if ( firstTyp == descr_moveto ) {
212
curX = dynamic_cast<PathDescrMoveTo *>(descr_cmd[0])->p;
215
curX[0] = curX[1] = 0;
217
lastMoveTo = AddPoint(curX, true);
219
descr_cmd[0]->associated = lastMoveTo;
221
// et le reste, 1 par 1
222
while ( curP < int(descr_cmd.size()) ) {
224
int const nType = descr_cmd[curP]->getType();
229
descr_cmd[curP]->associated = AddForcedPoint(curX);
235
PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[curP]);
237
lastMoveTo = AddPoint(nextX, true);
238
descr_cmd[curP]->associated = lastMoveTo;
246
nextX = pts[lastMoveTo].p;
247
descr_cmd[curP]->associated = AddPoint(nextX, false);
248
if ( descr_cmd[curP]->associated < 0 ) {
250
descr_cmd[curP]->associated = 0;
252
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
255
if ( descr_cmd[curP]->associated > 0 ) {
256
pts[descr_cmd[curP]->associated].closed = true;
263
PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[curP]);
265
descr_cmd[curP]->associated = AddPoint(nextX, false);
266
if ( descr_cmd[curP]->associated < 0 ) {
268
descr_cmd[curP]->associated = 0;
270
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
278
case descr_cubicto: {
279
PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[curP]);
281
RecCubicTo(curX, nData->start, nextX, nData->end, treshhold, 8);
282
descr_cmd[curP]->associated = AddPoint(nextX,false);
283
if ( descr_cmd[curP]->associated < 0 ) {
285
descr_cmd[curP]->associated = 0;
287
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
296
PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[curP]);
298
DoArc(curX, nextX, nData->rx, nData->ry, nData->angle, nData->large, nData->clockwise, treshhold);
299
descr_cmd[curP]->associated = AddPoint(nextX, false);
300
if ( descr_cmd[curP]->associated < 0 ) {
302
descr_cmd[curP]->associated = 0;
304
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
312
case descr_bezierto: {
313
PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[curP]);
314
int nbInterm = nBData->nb;
320
PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
322
if ( nbInterm == 1 ) {
323
Geom::Point const midX = nData->p;
324
RecBezierTo(midX, curX, nextX, treshhold, 8);
325
} else if ( nbInterm > 1 ) {
326
Geom::Point bx = curX;
327
Geom::Point cx = curX;
328
Geom::Point dx = curX;
332
nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
336
for (int k = 0; k < nbInterm - 1; k++) {
342
nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
344
Geom::Point stx = (bx + cx) / 2;
346
descr_cmd[ip - 2]->associated = AddPoint(stx, false);
347
if ( descr_cmd[ip - 2]->associated < 0 ) {
349
descr_cmd[ip - 2]->associated = 0;
351
descr_cmd[ip - 2]->associated = descr_cmd[ip - 3]->associated;
357
Geom::Point const mx = (cx + dx) / 2;
358
RecBezierTo(cx, stx, mx, treshhold, 8);
369
Geom::Point stx = (bx + cx) / 2;
371
descr_cmd[ip - 1]->associated = AddPoint(stx, false);
372
if ( descr_cmd[ip - 1]->associated < 0 ) {
374
descr_cmd[ip - 1]->associated = 0;
376
descr_cmd[ip - 1]->associated = descr_cmd[ip - 2]->associated;
381
Geom::Point mx = (cx + dx) / 2;
382
RecBezierTo(cx, stx, mx, treshhold, 8);
387
descr_cmd[curBD]->associated = AddPoint(nextX, false);
388
if ( descr_cmd[curBD]->associated < 0 ) {
390
descr_cmd[curBD]->associated = 0;
392
descr_cmd[curBD]->associated = descr_cmd[curBD - 1]->associated;
406
#define POINT_RELATION_TO_AREA(pt, area) ((pt)[0] < (area)->x0 ? 1 : ((pt)[0] > (area)->x1 ? 2 : ((pt)[1] < (area)->y0 ? 3 : ((pt)[1] > (area)->y1 ? 4 : 0))))
408
void Path::Convert(NRRectL *area, double treshhold)
410
if ( descr_flags & descr_adding_bezier ) {
414
if ( descr_flags & descr_doing_subpath ) {
420
if ( descr_cmd.empty() ) {
427
short last_point_relation = 0;
428
short curent_point_relation = 0;
429
bool last_start_elimination = false;
430
bool start_elimination = false;
431
bool replace = false;
435
int const firstTyp = descr_cmd[0]->getType();
436
if ( firstTyp == descr_moveto ) {
437
curX = dynamic_cast<PathDescrMoveTo *>(descr_cmd[0])->p;
440
curX[0] = curX[1] = 0;
443
last_point_relation = POINT_RELATION_TO_AREA(curX, area);
444
lastMoveTo = AddPoint(curX, true);
446
descr_cmd[0]->associated = lastMoveTo;
448
// process nodes one by one
449
while ( curP < int(descr_cmd.size()) ) {
451
int const nType = descr_cmd[curP]->getType();
456
descr_cmd[curP]->associated = AddForcedPoint(curX);
457
last_point_relation = 0;
463
PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[curP]);
465
lastMoveTo = AddPoint(nextX, true);
466
descr_cmd[curP]->associated = lastMoveTo;
468
last_point_relation = POINT_RELATION_TO_AREA(nextX, area);
469
start_elimination = false;
476
nextX = pts[lastMoveTo].p;
477
descr_cmd[curP]->associated = AddPoint(nextX, false);
478
if ( descr_cmd[curP]->associated < 0 ) {
480
descr_cmd[curP]->associated = 0;
482
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
485
if ( descr_cmd[curP]->associated > 0 ) {
486
pts[descr_cmd[curP]->associated].closed = true;
488
last_point_relation = 0;
494
PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[curP]);
496
curent_point_relation = POINT_RELATION_TO_AREA(nextX, area);
498
last_start_elimination = start_elimination;
499
if (curent_point_relation > 0 && curent_point_relation == last_point_relation) {
500
if (!start_elimination) {
501
start_elimination = true;
504
descr_cmd[curP]->associated = ReplacePoint(nextX);
507
start_elimination = false;
511
descr_cmd[curP]->associated = AddPoint(nextX, false);
514
if ( descr_cmd[curP]->associated < 0 ) {
515
// point is not added as position is equal to the last added
516
start_elimination = last_start_elimination;
518
descr_cmd[curP]->associated = 0;
520
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
523
last_point_relation = curent_point_relation;
528
case descr_cubicto: {
529
PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[curP]);
532
curent_point_relation = POINT_RELATION_TO_AREA(nextX, area);
534
last_start_elimination = start_elimination;
535
if (curent_point_relation > 0 && curent_point_relation == last_point_relation &&
536
curent_point_relation == POINT_RELATION_TO_AREA(curX + (nData->start), area) &&
537
curent_point_relation == POINT_RELATION_TO_AREA(nextX + (nData->end), area))
539
if (!start_elimination) {
540
start_elimination = true;
543
descr_cmd[curP]->associated = ReplacePoint(nextX);
546
start_elimination = false;
550
RecCubicTo(curX, nData->start, nextX, nData->end, treshhold, 8);
551
descr_cmd[curP]->associated = AddPoint(nextX,false);
554
if ( descr_cmd[curP]->associated < 0 ) {
555
// point is not added as position is equal to the last added
556
start_elimination = last_start_elimination;
558
descr_cmd[curP]->associated = 0;
560
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
563
last_point_relation = curent_point_relation;
569
PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[curP]);
571
DoArc(curX, nextX, nData->rx, nData->ry, nData->angle, nData->large, nData->clockwise, treshhold);
572
descr_cmd[curP]->associated = AddPoint(nextX, false);
573
if ( descr_cmd[curP]->associated < 0 ) {
575
descr_cmd[curP]->associated = 0;
577
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
580
last_point_relation = 0;
586
case descr_bezierto: {
587
PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[curP]);
588
int nbInterm = nBData->nb;
594
PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
596
if ( nbInterm == 1 ) {
597
Geom::Point const midX = nData->p;
598
RecBezierTo(midX, curX, nextX, treshhold, 8);
599
} else if ( nbInterm > 1 ) {
600
Geom::Point bx = curX;
601
Geom::Point cx = curX;
602
Geom::Point dx = curX;
606
nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
610
for (int k = 0; k < nbInterm - 1; k++) {
616
nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
618
Geom::Point stx = (bx + cx) / 2;
620
descr_cmd[ip - 2]->associated = AddPoint(stx, false);
621
if ( descr_cmd[ip - 2]->associated < 0 ) {
623
descr_cmd[ip - 2]->associated = 0;
625
descr_cmd[ip - 2]->associated = descr_cmd[ip - 3]->associated;
631
Geom::Point const mx = (cx + dx) / 2;
632
RecBezierTo(cx, stx, mx, treshhold, 8);
643
Geom::Point stx = (bx + cx) / 2;
645
descr_cmd[ip - 1]->associated = AddPoint(stx, false);
646
if ( descr_cmd[ip - 1]->associated < 0 ) {
648
descr_cmd[ip - 1]->associated = 0;
650
descr_cmd[ip - 1]->associated = descr_cmd[ip - 2]->associated;
655
Geom::Point mx = (cx + dx) / 2;
656
RecBezierTo(cx, stx, mx, treshhold, 8);
661
descr_cmd[curBD]->associated = AddPoint(nextX, false);
662
if ( descr_cmd[curBD]->associated < 0 ) {
664
descr_cmd[curBD]->associated = 0;
666
descr_cmd[curBD]->associated = descr_cmd[curBD - 1]->associated;
670
last_point_relation = 0;
682
void Path::ConvertEvenLines(double treshhold)
684
if ( descr_flags & descr_adding_bezier ) {
688
if ( descr_flags & descr_doing_subpath ) {
694
if ( descr_cmd.empty() ) {
704
int const firstTyp = descr_cmd[0]->getType();
705
if ( firstTyp == descr_moveto ) {
706
curX = dynamic_cast<PathDescrMoveTo *>(descr_cmd[0])->p;
709
curX[0] = curX[1] = 0;
711
lastMoveTo = AddPoint(curX, true);
713
descr_cmd[0]->associated = lastMoveTo;
715
// et le reste, 1 par 1
716
while ( curP < int(descr_cmd.size()) ) {
718
int const nType = descr_cmd[curP]->getType();
723
descr_cmd[curP]->associated = AddForcedPoint(curX);
729
PathDescrMoveTo* nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[curP]);
731
lastMoveTo = AddPoint(nextX,true);
732
descr_cmd[curP]->associated = lastMoveTo;
740
nextX = pts[lastMoveTo].p;
743
nexcur = nextX - curX;
744
const double segL = Geom::L2(nexcur);
745
if ( (segL > treshhold) && (treshhold > 0) ) {
746
for (double i = treshhold; i < segL; i += treshhold) {
748
nX = (segL - i) * curX + i * nextX;
755
descr_cmd[curP]->associated = AddPoint(nextX,false);
756
if ( descr_cmd[curP]->associated < 0 ) {
758
descr_cmd[curP]->associated = 0;
760
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
763
if ( descr_cmd[curP]->associated > 0 ) {
764
pts[descr_cmd[curP]->associated].closed = true;
771
PathDescrLineTo* nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[curP]);
773
Geom::Point nexcur = nextX - curX;
774
const double segL = L2(nexcur);
775
if ( (segL > treshhold) && (treshhold > 0)) {
776
for (double i = treshhold; i < segL; i += treshhold) {
777
Geom::Point nX = ((segL - i) * curX + i * nextX) / segL;
782
descr_cmd[curP]->associated = AddPoint(nextX,false);
783
if ( descr_cmd[curP]->associated < 0 ) {
785
descr_cmd[curP]->associated = 0;
787
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
795
case descr_cubicto: {
796
PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[curP]);
798
RecCubicTo(curX, nData->start, nextX, nData->end, treshhold, 8, 4 * treshhold);
799
descr_cmd[curP]->associated = AddPoint(nextX, false);
800
if ( descr_cmd[curP]->associated < 0 ) {
802
descr_cmd[curP]->associated = 0;
804
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
813
PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[curP]);
815
DoArc(curX, nextX, nData->rx, nData->ry, nData->angle, nData->large, nData->clockwise, treshhold);
816
descr_cmd[curP]->associated =AddPoint(nextX, false);
817
if ( descr_cmd[curP]->associated < 0 ) {
819
descr_cmd[curP]->associated = 0;
821
descr_cmd[curP]->associated = descr_cmd[curP - 1]->associated;
830
case descr_bezierto: {
831
PathDescrBezierTo *nBData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[curP]);
832
int nbInterm = nBData->nb;
838
PathDescrIntermBezierTo *nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
840
if ( nbInterm == 1 ) {
841
Geom::Point const midX = nData->p;
842
RecBezierTo(midX, curX, nextX, treshhold, 8, 4 * treshhold);
843
} else if ( nbInterm > 1 ) {
844
Geom::Point bx = curX;
845
Geom::Point cx = curX;
846
Geom::Point dx = curX;
850
nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
854
for (int k = 0; k < nbInterm - 1; k++) {
860
nData = dynamic_cast<PathDescrIntermBezierTo *>(descr_cmd[ip]);
862
Geom::Point stx = (bx+cx) / 2;
864
descr_cmd[ip - 2]->associated = AddPoint(stx, false);
865
if ( descr_cmd[ip - 2]->associated < 0 ) {
867
descr_cmd[ip- 2]->associated = 0;
869
descr_cmd[ip - 2]->associated = descr_cmd[ip - 3]->associated;
875
Geom::Point const mx = (cx + dx) / 2;
876
RecBezierTo(cx, stx, mx, treshhold, 8, 4 * treshhold);
887
Geom::Point const stx = (bx + cx) / 2;
889
descr_cmd[ip - 1]->associated = AddPoint(stx, false);
890
if ( descr_cmd[ip - 1]->associated < 0 ) {
892
descr_cmd[ip - 1]->associated = 0;
894
descr_cmd[ip - 1]->associated = descr_cmd[ip - 2]->associated;
899
Geom::Point const mx = (cx + dx) / 2;
900
RecBezierTo(cx, stx, mx, treshhold, 8, 4 * treshhold);
905
descr_cmd[curBD]->associated = AddPoint(nextX, false);
906
if ( descr_cmd[curBD]->associated < 0 ) {
908
descr_cmd[curBD]->associated = 0;
910
descr_cmd[curBD]->associated = descr_cmd[curBD - 1]->associated;
919
if ( Geom::LInfty(curX - nextX) > 0.00001 ) {
925
const Geom::Point Path::PrevPoint(int i) const
927
/* TODO: I suspect this should assert `(unsigned) i < descr_nb'. We can probably change
928
the argument to unsigned. descr_nb should probably be changed to unsigned too. */
930
switch ( descr_cmd[i]->getType() ) {
932
PathDescrMoveTo *nData = dynamic_cast<PathDescrMoveTo *>(descr_cmd[i]);
936
PathDescrLineTo *nData = dynamic_cast<PathDescrLineTo *>(descr_cmd[i]);
940
PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
943
case descr_cubicto: {
944
PathDescrCubicTo *nData = dynamic_cast<PathDescrCubicTo *>(descr_cmd[i]);
947
case descr_bezierto: {
948
PathDescrBezierTo *nData = dynamic_cast<PathDescrBezierTo *>(descr_cmd[i]);
951
case descr_interm_bezier:
954
return PrevPoint(i - 1);
956
g_assert_not_reached();
957
return Geom::Point(0, 0);
961
// utilitaries: given a quadratic bezier curve (start point, control point, end point, ie that's a clamped curve),
962
// and an abcissis on it, get the point with that abcissis.
963
// warning: it's NOT a curvilign abcissis (or whatever you call that in english), so "t" is NOT the length of "start point"->"result point"
964
void Path::QuadraticPoint(double t, Geom::Point &oPt,
965
const Geom::Point &iS, const Geom::Point &iM, const Geom::Point &iE)
967
Geom::Point const ax = iE - 2 * iM + iS;
968
Geom::Point const bx = 2 * iM - 2 * iS;
969
Geom::Point const cx = iS;
971
oPt = t * t * ax + t * bx + cx;
973
// idem for cubic bezier patch
974
void Path::CubicTangent(double t, Geom::Point &oPt, const Geom::Point &iS, const Geom::Point &isD,
975
const Geom::Point &iE, const Geom::Point &ieD)
977
Geom::Point const ax = ieD - 2 * iE + 2 * iS + isD;
978
Geom::Point const bx = 3 * iE - ieD - 2 * isD - 3 * iS;
979
Geom::Point const cx = isD;
981
oPt = 3 * t * t * ax + 2 * t * bx + cx;
984
// extract interesting info of a SVG arc description
985
static void ArcAnglesAndCenter(Geom::Point const &iS, Geom::Point const &iE,
986
double rx, double ry, double angle,
987
bool large, bool wise,
988
double &sang, double &eang, Geom::Point &dr);
990
void Path::ArcAngles(const Geom::Point &iS, const Geom::Point &iE,
991
double rx, double ry, double angle, bool large, bool wise, double &sang, double &eang)
994
ArcAnglesAndCenter(iS, iE, rx, ry, angle, large, wise, sang, eang, dr);
997
/* N.B. If iS == iE then sang,eang,dr each become NaN. Probably a bug. */
998
static void ArcAnglesAndCenter(Geom::Point const &iS, Geom::Point const &iE,
999
double rx, double ry, double angle,
1000
bool large, bool wise,
1001
double &sang, double &eang, Geom::Point &dr)
1003
Geom::Point se = iE - iS;
1004
Geom::Point ca(cos(angle), sin(angle));
1005
Geom::Point cse(dot(se, ca), cross(se, ca));
1008
double const lensq = dot(cse,cse);
1009
Geom::Point csd = ( ( lensq < 4
1010
? sqrt( 1/lensq - .25 )
1014
Geom::Point ra = -csd - 0.5 * cse;
1015
if ( ra[0] <= -1 ) {
1017
} else if ( ra[0] >= 1 ) {
1022
sang = 2 * M_PI - sang;
1026
ra = -csd + 0.5 * cse;
1027
if ( ra[0] <= -1 ) {
1029
} else if ( ra[0] >= 1 ) {
1034
eang = 2 * M_PI - eang;
1040
ca[1] = -ca[1]; // because it's the inverse rotation
1042
dr[0] = dot(csd, ca);
1043
dr[1] = cross(csd, ca);
1056
if ( eang >= 2*M_PI ) {
1059
if ( sang >= 2*M_PI ) {
1072
if ( eang >= 2*M_PI ) {
1075
if ( sang >= 2*M_PI ) {
1081
dr += 0.5 * (iS + iE);
1086
void Path::DoArc(Geom::Point const &iS, Geom::Point const &iE,
1087
double const rx, double const ry, double const angle,
1088
bool const large, bool const wise, double const /*tresh*/)
1090
/* TODO: Check that our behaviour is standards-conformant if iS and iE are (much) further
1091
apart than the diameter. Also check that we do the right thing for negative radius.
1092
(Same for the other DoArc functions in this file.) */
1093
if ( rx <= 0.0001 || ry <= 0.0001 ) {
1095
// We always add a lineto afterwards, so this is fine.
1096
// [on ajoute toujours un lineto apres, donc c bon]
1101
Geom::Point dr_temp;
1102
ArcAnglesAndCenter(iS, iE, rx, ry, angle, large, wise, sang, eang, dr_temp);
1103
Geom::Point dr = dr_temp;
1104
/* TODO: This isn't as good numerically as treating iS and iE as primary. E.g. consider
1105
the case of low curvature (i.e. very large radius). */
1107
Geom::Scale const ar(rx, ry);
1108
Geom::Rotate cb( angle + sang );
1111
double const incr = -0.1;
1112
if ( sang < eang ) {
1115
Geom::Rotate const omega(incr);
1116
for (double b = sang + incr ; b > eang ; b += incr) {
1118
AddPoint( cb.vector() * ar + dr );
1123
double const incr = 0.1;
1124
if ( sang > eang ) {
1127
Geom::Rotate const omega(incr);
1128
for (double b = sang + incr ; b < eang ; b += incr) {
1130
AddPoint( cb.vector() * ar + dr);
1136
void Path::RecCubicTo( Geom::Point const &iS, Geom::Point const &isD,
1137
Geom::Point const &iE, Geom::Point const &ieD,
1138
double tresh, int lev, double maxL)
1140
Geom::Point se = iE - iS;
1141
const double dC = Geom::L2(se);
1144
const double sC = dot(isD,isD);
1145
const double eC = dot(ieD,ieD);
1146
if ( sC < tresh && eC < tresh ) {
1151
const double sC = fabs(cross(se, isD)) / dC;
1152
const double eC = fabs(cross(se, ieD)) / dC;
1153
if ( sC < tresh && eC < tresh ) {
1154
// presque tt droit -> attention si on nous demande de bien subdiviser les petits segments
1155
if ( maxL > 0 && dC > maxL ) {
1159
Geom::Point m = 0.5 * (iS + iE) + 0.125 * (isD - ieD);
1160
Geom::Point md = 0.75 * (iE - iS) - 0.125 * (isD + ieD);
1162
Geom::Point hisD = 0.5 * isD;
1163
Geom::Point hieD = 0.5 * ieD;
1165
RecCubicTo(iS, hisD, m, md, tresh, lev - 1, maxL);
1167
RecCubicTo(m, md, iE, hieD, tresh, lev - 1,maxL);
1178
Geom::Point m = 0.5 * (iS + iE) + 0.125 * (isD - ieD);
1179
Geom::Point md = 0.75 * (iE - iS) - 0.125 * (isD + ieD);
1181
Geom::Point hisD = 0.5 * isD;
1182
Geom::Point hieD = 0.5 * ieD;
1184
RecCubicTo(iS, hisD, m, md, tresh, lev - 1, maxL);
1186
RecCubicTo(m, md, iE, hieD, tresh, lev - 1,maxL);
1192
void Path::RecBezierTo(const Geom::Point &iP,
1193
const Geom::Point &iS,
1194
const Geom::Point &iE,
1195
double tresh, int lev, double maxL)
1201
Geom::Point ps = iS - iP;
1202
Geom::Point pe = iE - iP;
1203
Geom::Point se = iE - iS;
1204
double s = fabs(cross(pe, ps));
1206
const double l = L2(se);
1207
if ( maxL > 0 && l > maxL ) {
1208
const Geom::Point m = 0.25 * (iS + iE + 2 * iP);
1209
Geom::Point md = 0.5 * (iS + iP);
1210
RecBezierTo(md, iS, m, tresh, lev - 1, maxL);
1212
md = 0.5 * (iP + iE);
1213
RecBezierTo(md, m, iE, tresh, lev - 1, maxL);
1219
const Geom::Point m = 0.25 * (iS + iE + 2 * iP);
1220
Geom::Point md = 0.5 * (iS + iP);
1221
RecBezierTo(md, iS, m, tresh, lev - 1, maxL);
1223
md = 0.5 * (iP + iE);
1224
RecBezierTo(md, m, iE, tresh, lev - 1, maxL);
1229
void Path::DoArc(Geom::Point const &iS, Geom::Point const &iE,
1230
double const rx, double const ry, double const angle,
1231
bool const large, bool const wise, double const /*tresh*/, int const piece)
1233
/* TODO: Check that our behaviour is standards-conformant if iS and iE are (much) further
1234
apart than the diameter. Also check that we do the right thing for negative radius.
1235
(Same for the other DoArc functions in this file.) */
1236
if ( rx <= 0.0001 || ry <= 0.0001 ) {
1238
// We always add a lineto afterwards, so this is fine.
1239
// [on ajoute toujours un lineto apres, donc c bon]
1244
Geom::Point dr_temp;
1245
ArcAnglesAndCenter(iS, iE, rx, ry, angle, large, wise, sang, eang, dr_temp);
1246
Geom::Point dr = dr_temp;
1247
/* TODO: This isn't as good numerically as treating iS and iE as primary. E.g. consider
1248
the case of low curvature (i.e. very large radius). */
1250
Geom::Scale const ar(rx, ry);
1251
Geom::Rotate cb(angle + sang);
1254
double const incr = -0.1;
1255
if ( sang < eang ) {
1258
Geom::Rotate const omega(incr);
1259
for (double b = sang + incr; b > eang; b += incr) {
1261
AddPoint(cb.vector() * ar + dr, piece, (sang - b) / (sang - eang));
1266
double const incr = 0.1;
1267
if ( sang > eang ) {
1270
Geom::Rotate const omega(incr);
1271
for (double b = sang + incr ; b < eang ; b += incr) {
1273
AddPoint(cb.vector() * ar + dr, piece, (b - sang) / (eang - sang));
1278
void Path::RecCubicTo(Geom::Point const &iS, Geom::Point const &isD,
1279
Geom::Point const &iE, Geom::Point const &ieD,
1280
double tresh, int lev, double st, double et, int piece)
1282
const Geom::Point se = iE - iS;
1283
const double dC = Geom::L2(se);
1285
const double sC = dot(isD, isD);
1286
const double eC = dot(ieD, ieD);
1287
if ( sC < tresh && eC < tresh ) {
1291
const double sC = fabs(cross(se, isD)) / dC;
1292
const double eC = fabs(cross(se, ieD)) / dC;
1293
if ( sC < tresh && eC < tresh ) {
1302
Geom::Point m = 0.5 * (iS + iE) + 0.125 * (isD - ieD);
1303
Geom::Point md = 0.75 * (iE - iS) - 0.125 * (isD + ieD);
1304
double mt = (st + et) / 2;
1306
Geom::Point hisD = 0.5 * isD;
1307
Geom::Point hieD = 0.5 * ieD;
1309
RecCubicTo(iS, hisD, m, md, tresh, lev - 1, st, mt, piece);
1310
AddPoint(m, piece, mt);
1311
RecCubicTo(m, md, iE, hieD, tresh, lev - 1, mt, et, piece);
1317
void Path::RecBezierTo(Geom::Point const &iP,
1318
Geom::Point const &iS,
1319
Geom::Point const &iE,
1320
double tresh, int lev, double st, double et, int piece)
1326
Geom::Point ps = iS - iP;
1327
Geom::Point pe = iE - iP;
1328
const double s = fabs(cross(pe, ps));
1334
const double mt = (st + et) / 2;
1335
const Geom::Point m = 0.25 * (iS + iE + 2 * iP);
1336
RecBezierTo(0.5 * (iS + iP), iS, m, tresh, lev - 1, st, mt, piece);
1337
AddPoint(m, piece, mt);
1338
RecBezierTo(0.5 * (iP + iE), m, iE, tresh, lev - 1, mt, et, piece);
1344
void Path::DoArc(Geom::Point const &iS, Geom::Point const &iE,
1345
double const rx, double const ry, double const angle,
1346
bool const large, bool const wise, double const /*tresh*/,
1347
int const piece, offset_orig &/*orig*/)
1349
// Will never arrive here, as offsets are made of cubics.
1350
// [on n'arrivera jamais ici, puisque les offsets sont fait de cubiques]
1351
/* TODO: Check that our behaviour is standards-conformant if iS and iE are (much) further
1352
apart than the diameter. Also check that we do the right thing for negative radius.
1353
(Same for the other DoArc functions in this file.) */
1354
if ( rx <= 0.0001 || ry <= 0.0001 ) {
1356
// We always add a lineto afterwards, so this is fine.
1357
// [on ajoute toujours un lineto apres, donc c bon]
1362
Geom::Point dr_temp;
1363
ArcAnglesAndCenter(iS, iE, rx, ry, angle, large, wise, sang, eang, dr_temp);
1364
Geom::Point dr = dr_temp;
1365
/* TODO: This isn't as good numerically as treating iS and iE as primary. E.g. consider
1366
the case of low curvature (i.e. very large radius). */
1368
Geom::Scale const ar(rx, ry);
1369
Geom::Rotate cb(angle + sang);
1372
double const incr = -0.1;
1373
if ( sang < eang ) {
1376
Geom::Rotate const omega(incr);
1377
for (double b = sang + incr; b > eang ;b += incr) {
1379
AddPoint(cb.vector() * ar + dr, piece, (sang - b) / (sang - eang));
1383
double const incr = 0.1;
1384
if ( sang > eang ) {
1387
Geom::Rotate const omega(incr);
1388
for (double b = sang + incr ; b < eang ; b += incr) {
1390
AddPoint(cb.vector() * ar + dr, piece, (b - sang) / (eang - sang));
1396
void Path::RecCubicTo(Geom::Point const &iS, Geom::Point const &isD,
1397
Geom::Point const &iE, Geom::Point const &ieD,
1398
double tresh, int lev, double st, double et,
1399
int piece, offset_orig &orig)
1401
const Geom::Point se = iE - iS;
1402
const double dC = Geom::L2(se);
1403
bool doneSub = false;
1405
const double sC = dot(isD, isD);
1406
const double eC = dot(ieD, ieD);
1407
if ( sC < tresh && eC < tresh ) {
1411
const double sC = fabs(cross(se, isD)) / dC;
1412
const double eC = fabs(cross(se, ieD)) / dC;
1413
if ( sC < tresh && eC < tresh ) {
1422
// test des inversions
1431
orig.orig->PointAndTangentAt(orig.piece, orig.tSt * (1 - st) + orig.tEn * st, os_pos, os_tgt);
1432
orig.orig->PointAndTangentAt(orig.piece, orig.tSt * (1 - et) + orig.tEn * et, oe_pos, oe_tgt);
1435
Geom::Point n_tgt = isD;
1436
double si = dot(n_tgt, os_tgt);
1441
si = dot(n_tgt, oe_tgt);
1445
if ( stInv && enInv ) {
1447
AddPoint(os_pos, -1, 0.0);
1448
AddPoint(iE, piece, et);
1449
AddPoint(iS, piece, st);
1450
AddPoint(oe_pos, -1, 0.0);
1453
} else if ( ( stInv && !enInv ) || ( !stInv && enInv ) ) {
1459
if ( ( !stInv && !enInv && doneSub ) || lev <= 0 ) {
1464
const Geom::Point m = 0.5 * (iS+iE) + 0.125 * (isD - ieD);
1465
const Geom::Point md = 0.75 * (iE - iS) - 0.125 * (isD + ieD);
1466
const double mt = (st + et) / 2;
1467
const Geom::Point hisD = 0.5 * isD;
1468
const Geom::Point hieD = 0.5 * ieD;
1470
RecCubicTo(iS, hisD, m, md, tresh, lev - 1, st, mt, piece, orig);
1471
AddPoint(m, piece, mt);
1472
RecCubicTo(m, md, iE, hieD, tresh, lev - 1, mt, et, piece, orig);
1478
void Path::RecBezierTo(Geom::Point const &iP, Geom::Point const &iS,Geom::Point const &iE,
1479
double tresh, int lev, double st, double et,
1480
int piece, offset_orig& orig)
1482
bool doneSub = false;
1487
const Geom::Point ps = iS - iP;
1488
const Geom::Point pe = iE - iP;
1489
const double s = fabs(cross(pe, ps));
1494
// test des inversions
1507
PathDescrIntermBezierTo mid(iP);
1508
PathDescrBezierTo fin(iE, 1);
1510
TangentOnBezAt(0.0, iS, mid, fin, false, n_pos, n_tgt, n_len, n_rad);
1511
orig.orig->PointAndTangentAt(orig.piece, orig.tSt * (1 - st) + orig.tEn * st, os_pos, os_tgt);
1512
double si = dot(n_tgt, os_tgt);
1517
TangentOnBezAt(1.0, iS, mid, fin, false, n_pos, n_tgt, n_len, n_rad);
1518
orig.orig->PointAndTangentAt(orig.piece, orig.tSt * (1 - et) + orig.tEn * et, oe_pos, oe_tgt);
1519
si = dot(n_tgt, oe_tgt);
1524
if ( stInv && enInv ) {
1525
AddPoint(os_pos, -1, 0.0);
1526
AddPoint(iE, piece, et);
1527
AddPoint(iS, piece, st);
1528
AddPoint(oe_pos, -1, 0.0);
1533
if ( !stInv && !enInv && doneSub ) {
1538
double mt = (st + et) / 2;
1539
Geom::Point m = 0.25 * (iS + iE + 2 * iP);
1540
Geom::Point md = 0.5 * (iS + iP);
1541
RecBezierTo(md, iS, m, tresh, lev - 1, st, mt, piece, orig);
1542
AddPoint(m, piece, mt);
1543
md = 0.5 * (iP + iE);
1544
RecBezierTo(md, m, iE, tresh, lev - 1, mt, et, piece, orig);
1550
* put a polyline in a Shape instance, for further fun
1551
* pathID is the ID you want this Path instance to be associated with, for when you're going to recompose the polyline
1552
* in a path description ( you need to have prepared the back data for that, of course)
1555
void Path::Fill(Shape* dest, int pathID, bool justAdd, bool closeIfNeeded, bool invert)
1557
if ( dest == NULL ) {
1561
if ( justAdd == false ) {
1562
dest->Reset(pts.size(), pts.size());
1565
if ( pts.size() <= 1 ) {
1569
int first = dest->numberOfPoints();
1572
dest->MakeBackData(true);
1578
// invert && back && !weighted
1579
for (int i = 0; i < int(pts.size()); i++) {
1580
dest->AddPoint(pts[i].p);
1585
bool closed = false;
1588
while ( curP < int(pts.size()) ) {
1593
if ( pts[sbp].isMoveTo == polyline_moveto ) {
1595
if ( closeIfNeeded ) {
1596
if ( closed && lEdge >= 0 ) {
1597
dest->DisconnectStart(lEdge);
1598
dest->ConnectStart(first + lastM, lEdge);
1600
lEdge = dest->AddEdge(first + lastM, first+pathEnd);
1602
dest->ebData[lEdge].pathID = pathID;
1603
dest->ebData[lEdge].pieceID = pts[lm].piece;
1604
dest->ebData[lEdge].tSt = 1.0;
1605
dest->ebData[lEdge].tEn = 0.0;
1617
if ( Geom::LInfty(pts[sbp].p - pts[prp].p) >= 0.00001 ) {
1618
lEdge = dest->AddEdge(first + curP, first + pathEnd);
1620
dest->ebData[lEdge].pathID = pathID;
1621
dest->ebData[lEdge].pieceID = pts[sbp].piece;
1622
if ( pts[sbp].piece == pts[prp].piece ) {
1623
dest->ebData[lEdge].tSt = pts[sbp].t;
1624
dest->ebData[lEdge].tEn = pts[prp].t;
1626
dest->ebData[lEdge].tSt = pts[sbp].t;
1627
dest->ebData[lEdge].tEn = 0.0;
1631
if ( Geom::LInfty(pts[sbp].p - pts[lm].p) < 0.00001 ) {
1642
if ( closeIfNeeded ) {
1643
if ( closed && lEdge >= 0 ) {
1644
dest->DisconnectStart(lEdge);
1645
dest->ConnectStart(first + lastM, lEdge);
1648
lEdge = dest->AddEdge(first + lastM, first + pathEnd);
1650
dest->ebData[lEdge].pathID = pathID;
1651
dest->ebData[lEdge].pieceID = pts[lm].piece;
1652
dest->ebData[lEdge].tSt = 1.0;
1653
dest->ebData[lEdge].tEn = 0.0;
1662
// invert && !back && !weighted
1663
for (int i = 0; i < int(pts.size()); i++) {
1664
dest->AddPoint(pts[i].p);
1669
bool closed = false;
1671
while ( curP < int(pts.size()) ) {
1675
if ( pts[sbp].isMoveTo == polyline_moveto ) {
1676
if ( closeIfNeeded ) {
1677
if ( closed && lEdge >= 0 ) {
1678
dest->DisconnectStart(lEdge);
1679
dest->ConnectStart(first + lastM, lEdge);
1681
dest->AddEdge(first + lastM, first + pathEnd);
1689
if ( Geom::LInfty(pts[sbp].p - pts[prp].p) >= 0.00001 ) {
1690
lEdge = dest->AddEdge(first+curP, first+pathEnd);
1692
if ( Geom::LInfty(pts[sbp].p - pts[lm].p) < 0.00001 ) {
1702
if ( closeIfNeeded ) {
1703
if ( closed && lEdge >= 0 ) {
1704
dest->DisconnectStart(lEdge);
1705
dest->ConnectStart(first + lastM, lEdge);
1707
dest->AddEdge(first + lastM, first + pathEnd);
1718
// !invert && back && !weighted
1719
for (int i = 0; i < int(pts.size()); i++) {
1720
dest->AddPoint(pts[i].p);
1726
bool closed = false;
1728
while ( curP < int(pts.size()) ) {
1732
if ( pts[sbp].isMoveTo == polyline_moveto ) {
1733
if ( closeIfNeeded ) {
1734
if ( closed && lEdge >= 0 ) {
1735
dest->DisconnectEnd(lEdge);
1736
dest->ConnectEnd(first + lastM, lEdge);
1738
lEdge = dest->AddEdge(first + pathEnd, first+lastM);
1740
dest->ebData[lEdge].pathID = pathID;
1741
dest->ebData[lEdge].pieceID = pts[lm].piece;
1742
dest->ebData[lEdge].tSt = 0.0;
1743
dest->ebData[lEdge].tEn = 1.0;
1752
if ( Geom::LInfty(pts[sbp].p - pts[prp].p) >= 0.00001 ) {
1753
lEdge = dest->AddEdge(first + pathEnd, first + curP);
1754
dest->ebData[lEdge].pathID = pathID;
1755
dest->ebData[lEdge].pieceID = pts[sbp].piece;
1756
if ( pts[sbp].piece == pts[prp].piece ) {
1757
dest->ebData[lEdge].tSt = pts[prp].t;
1758
dest->ebData[lEdge].tEn = pts[sbp].t;
1760
dest->ebData[lEdge].tSt = 0.0;
1761
dest->ebData[lEdge].tEn = pts[sbp].t;
1764
if ( Geom::LInfty(pts[sbp].p - pts[lm].p) < 0.00001 ) {
1774
if ( closeIfNeeded ) {
1775
if ( closed && lEdge >= 0 ) {
1776
dest->DisconnectEnd(lEdge);
1777
dest->ConnectEnd(first + lastM, lEdge);
1780
lEdge = dest->AddEdge(first + pathEnd, first + lastM);
1782
dest->ebData[lEdge].pathID = pathID;
1783
dest->ebData[lEdge].pieceID = pts[lm].piece;
1784
dest->ebData[lEdge].tSt = 0.0;
1785
dest->ebData[lEdge].tEn = 1.0;
1793
// !invert && !back && !weighted
1794
for (int i = 0;i < int(pts.size()); i++) {
1795
dest->AddPoint(pts[i].p);
1801
bool closed = false;
1803
while ( curP < int(pts.size()) ) {
1807
if ( pts[sbp].isMoveTo == polyline_moveto ) {
1808
if ( closeIfNeeded ) {
1809
if ( closed && lEdge >= 0 ) {
1810
dest->DisconnectEnd(lEdge);
1811
dest->ConnectEnd(first + lastM, lEdge);
1813
dest->AddEdge(first + pathEnd, first + lastM);
1821
if ( Geom::LInfty(pts[sbp].p - pts[prp].p) >= 0.00001 ) {
1822
lEdge = dest->AddEdge(first+pathEnd, first+curP);
1824
if ( Geom::LInfty(pts[sbp].p - pts[lm].p) < 0.00001 ) {
1834
if ( closeIfNeeded ) {
1835
if ( closed && lEdge >= 0 ) {
1836
dest->DisconnectEnd(lEdge);
1837
dest->ConnectEnd(first + lastM, lEdge);
1839
dest->AddEdge(first + pathEnd, first + lastM);
1851
c-file-style:"stroustrup"
1852
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1853
indent-tabs-mode:nil
1857
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :