00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "KoPathSegment.h"
00021 #include "KoPathPoint.h"
00022 #include <kdebug.h>
00023 #include <QtGui/QPainterPath>
00024 #include <QtGui/QMatrix>
00025 #include <math.h>
00026
00028 const int MaxRecursionDepth = 64;
00030 const double FlatnessTolerance = ldexp(1.0,-MaxRecursionDepth-1);
00031
00032 class BezierSegment
00033 {
00034 public:
00035 BezierSegment( uint degree = 0, QPointF * p = 0 )
00036 {
00037 if( degree ) {
00038 for( uint i = 0; i <= degree; ++i )
00039 points.append( p[i] );
00040 }
00041 }
00042
00043 void setDegree( uint degree )
00044 {
00045 points.clear();
00046 if( degree ) {
00047 for( uint i = 0; i <= degree; ++i )
00048 points.append( QPointF() );
00049 }
00050 }
00051
00052 int degree() const
00053 {
00054 return points.count()-1;
00055 }
00056
00057 QPointF point( uint index ) const
00058 {
00059 if (static_cast<int>(index) > degree())
00060 return QPointF();
00061
00062 return points[index];
00063 }
00064
00065 void setPoint( uint index, const QPointF &p )
00066 {
00067 if (static_cast<int>(index) > degree())
00068 return;
00069
00070 points[index] = p;
00071 }
00072
00073 QPointF evaluate( qreal t, BezierSegment * left, BezierSegment * right ) const
00074 {
00075 int deg = degree();
00076 if (! deg)
00077 return QPointF();
00078
00079 QVector< QVector<QPointF> > Vtemp( deg+1 );
00080 for( int i = 0; i <= deg; ++i )
00081 Vtemp[i].resize( deg+1 );
00082
00083
00084 for (int j = 0; j <= deg; j++) {
00085 Vtemp[0][j] = points[j];
00086 }
00087
00088
00089 for (int i = 1; i <= deg; i++) {
00090 for (int j =0 ; j <= deg - i; j++) {
00091 Vtemp[i][j].rx() = (1.0 - t) * Vtemp[i-1][j].x() + t * Vtemp[i-1][j+1].x();
00092 Vtemp[i][j].ry() = (1.0 - t) * Vtemp[i-1][j].y() + t * Vtemp[i-1][j+1].y();
00093 }
00094 }
00095
00096 if (left) {
00097 left->setDegree( deg );
00098 for (int j = 0; j <= deg; j++) {
00099 left->setPoint(j, Vtemp[j][0]);
00100 }
00101 }
00102 if (right) {
00103 right->setDegree( deg );
00104 for (int j = 0; j <= deg; j++) {
00105 right->setPoint(j, Vtemp[deg-j][j]);
00106 }
00107 }
00108
00109 return (Vtemp[deg][0]);
00110 }
00111
00112 QList<qreal> roots( int depth = 0 ) const
00113 {
00114 QList<qreal> rootParams;
00115
00116 if (! degree())
00117 return rootParams;
00118
00119
00120
00121 int xAxisCrossings = controlPolygonZeros( points );
00122
00123 if (! xAxisCrossings) {
00124
00125 return rootParams;
00126 }
00127 else if (xAxisCrossings == 1) {
00128
00129
00130
00131 if (depth >= MaxRecursionDepth) {
00132
00133 rootParams.append( (points.first().x() + points.last().x()) / 2.0 );
00134 return rootParams;
00135 }
00136 else if( isFlat( FlatnessTolerance ) ) {
00137
00138 QPointF chord = points.last() - points.first();
00139 QPointF segStart = points.first();
00140 rootParams.append( ( chord.x() * segStart.y() - chord.y() * segStart.x() ) / - chord.y() );
00141 return rootParams;
00142 }
00143 }
00144
00145
00146 BezierSegment left, right;
00147 evaluate( 0.5, &left, &right );
00148 rootParams += left.roots(depth+1);
00149 rootParams += right.roots(depth+1);
00150
00151 return rootParams;
00152 }
00153
00154 static uint controlPolygonZeros( const QList<QPointF> & controlPoints )
00155 {
00156 uint controlPointCount = controlPoints.count();
00157 if( controlPointCount < 2 )
00158 return 0;
00159
00160 uint signChanges = 0;
00161
00162 int currSign = controlPoints[0].y() < 0.0 ? -1 : 1;
00163 int oldSign;
00164
00165 for( unsigned short i = 1; i < controlPointCount; ++i )
00166 {
00167 oldSign = currSign;
00168 currSign = controlPoints[i].y() < 0.0 ? -1 : 1;
00169
00170 if( currSign != oldSign )
00171 {
00172 ++signChanges;
00173 }
00174 }
00175
00176
00177 return signChanges;
00178 }
00179
00180 int isFlat( qreal tolerance ) const
00181 {
00182 int deg = degree();
00183
00184
00185
00186 qreal * distance = new qreal[deg + 1];
00187
00188
00189 qreal a = points[0].y() - points[deg].y();
00190 qreal b = points[deg].x() - points[0].x();
00191 qreal c = points[0].x() * points[deg].y() - points[deg].x() * points[0].y();
00192
00193 qreal abSquared = (a * a) + (b * b);
00194
00195 for (int i = 1; i < deg; i++) {
00196
00197 distance[i] = a * points[i].x() + b * points[i].y() + c;
00198 if (distance[i] > 0.0) {
00199 distance[i] = (distance[i] * distance[i]) / abSquared;
00200 }
00201 if (distance[i] < 0.0) {
00202 distance[i] = -((distance[i] * distance[i]) / abSquared);
00203 }
00204 }
00205
00206
00207
00208 qreal max_distance_above = 0.0;
00209 qreal max_distance_below = 0.0;
00210 for (int i = 1; i < deg; i++) {
00211 if (distance[i] < 0.0) {
00212 max_distance_below = qMin(max_distance_below, distance[i]);
00213 };
00214 if (distance[i] > 0.0) {
00215 max_distance_above = qMax(max_distance_above, distance[i]);
00216 }
00217 }
00218 delete [] distance;
00219
00220
00221 qreal a1 = 0.0;
00222 qreal b1 = 1.0;
00223 qreal c1 = 0.0;
00224
00225
00226 qreal a2 = a;
00227 qreal b2 = b;
00228 qreal c2 = c + max_distance_above;
00229
00230 qreal det = a1 * b2 - a2 * b1;
00231 qreal dInv = 1.0/det;
00232
00233 qreal intercept_1 = (b1 * c2 - b2 * c1) * dInv;
00234
00235
00236 a2 = a;
00237 b2 = b;
00238 c2 = c + max_distance_below;
00239
00240 det = a1 * b2 - a2 * b1;
00241 dInv = 1.0/det;
00242
00243 qreal intercept_2 = (b1 * c2 - b2 * c1) * dInv;
00244
00245
00246 qreal left_intercept = qMin(intercept_1, intercept_2);
00247 qreal right_intercept = qMax(intercept_1, intercept_2);
00248
00249 qreal error = 0.5 * (right_intercept-left_intercept);
00250
00251 return (error < tolerance);
00252 }
00253
00254 void printDebug() const
00255 {
00256 int index = 0;
00257 foreach( const QPointF &p, points ) {
00258 kDebug(30006) << QString("P%1 ").arg(index++) << p;
00259 }
00260 }
00261
00262 private:
00263 QList<QPointF> points;
00264 };
00265
00266 class KoPathSegment::Private
00267 {
00268 public:
00269 Private(KoPathPoint * p1, KoPathPoint * p2) {
00270 first = p1;
00271 second = p2;
00272 }
00273
00274 KoPathPoint * first;
00275 KoPathPoint * second;
00276 };
00277
00278 KoPathSegment::KoPathSegment(KoPathPoint * first, KoPathPoint * second)
00279 : d(new Private(first, second))
00280 {
00281 }
00282
00283 KoPathSegment::KoPathSegment(const KoPathSegment & segment)
00284 : d(new Private(0, 0))
00285 {
00286 if (! segment.first() || segment.first()->parent())
00287 setFirst(segment.first());
00288 else
00289 setFirst(new KoPathPoint(*segment.first()));
00290
00291 if (! segment.second() || segment.second()->parent())
00292 setSecond(segment.second());
00293 else
00294 setSecond(new KoPathPoint(*segment.second()));
00295 }
00296
00297 KoPathSegment::KoPathSegment(const QPointF &p0, const QPointF &p1)
00298 : d(new Private(new KoPathPoint(), new KoPathPoint()))
00299 {
00300 d->first->setPoint(p0);
00301 d->second->setPoint(p1);
00302 }
00303
00304 KoPathSegment::KoPathSegment(const QPointF &p0, const QPointF &p1, const QPointF &p2)
00305 : d(new Private(new KoPathPoint(), new KoPathPoint()))
00306 {
00307 d->first->setPoint(p0);
00308 d->first->setControlPoint2(p1);
00309 d->second->setPoint(p2);
00310 }
00311
00312 KoPathSegment::KoPathSegment(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3)
00313 : d(new Private(new KoPathPoint(), new KoPathPoint()))
00314 {
00315 d->first->setPoint(p0);
00316 d->first->setControlPoint2(p1);
00317 d->second->setControlPoint1(p2);
00318 d->second->setPoint(p3);
00319 }
00320
00321 KoPathSegment & KoPathSegment::operator=(const KoPathSegment & rhs)
00322 {
00323 if (this == &rhs)
00324 return (*this);
00325
00326 if (! rhs.first() || rhs.first()->parent())
00327 setFirst(rhs.first());
00328 else
00329 setFirst(new KoPathPoint(*rhs.first()));
00330
00331 if (! rhs.second() || rhs.second()->parent())
00332 setSecond(rhs.second());
00333 else
00334 setSecond(new KoPathPoint(*rhs.second()));
00335
00336 return (*this);
00337 }
00338
00339 KoPathSegment::~KoPathSegment()
00340 {
00341 if (d->first && ! d->first->parent())
00342 delete d->first;
00343 if (d->second && ! d->second->parent())
00344 delete d->second;
00345 delete d;
00346 }
00347
00348 KoPathPoint * KoPathSegment::first() const
00349 {
00350 return d->first;
00351 }
00352
00353 void KoPathSegment::setFirst(KoPathPoint * first)
00354 {
00355 if (d->first && ! d->first->parent())
00356 delete d->first;
00357 d->first = first;
00358 }
00359
00360 KoPathPoint * KoPathSegment::second() const
00361 {
00362 return d->second;
00363 }
00364
00365 void KoPathSegment::setSecond(KoPathPoint * second)
00366 {
00367 if (d->second && ! d->second->parent())
00368 delete d->second;
00369 d->second = second;
00370 }
00371
00372 bool KoPathSegment::isValid() const
00373 {
00374 return (d->first && d->second);
00375 }
00376
00377 bool KoPathSegment::operator == (const KoPathSegment &rhs) const
00378 {
00379 if (! isValid() && ! rhs.isValid())
00380 return true;
00381 if (isValid() && ! rhs.isValid())
00382 return false;
00383 if (! isValid() && rhs.isValid())
00384 return false;
00385
00386 return (*first() == *rhs.first() && *second() == *rhs.second());
00387 }
00388
00389 int KoPathSegment::degree() const
00390 {
00391 if (! d->first || ! d->second)
00392 return -1;
00393
00394 bool c1 = d->first->activeControlPoint2();
00395 bool c2 = d->second->activeControlPoint1();
00396 if (! c1 && ! c2)
00397 return 1;
00398 if (c1 && c2)
00399 return 3;
00400 return 2;
00401 }
00402
00403 QPointF KoPathSegment::pointAt(qreal t) const
00404 {
00405 if (! isValid())
00406 return QPointF();
00407
00408 if (degree() == 1) {
00409 return d->first->point() + t * (d->second->point() - d->first->point());
00410 } else {
00411 QPointF splitP;
00412
00413 deCasteljau(t, 0, 0, &splitP, 0, 0);
00414
00415 return splitP;
00416 }
00417 }
00418
00419 QRectF KoPathSegment::controlPointRect() const
00420 {
00421 if (! isValid())
00422 return QRectF();
00423
00424 QList<QPointF> points = controlPoints();
00425 QRectF bbox(points.first(), points.first());
00426 foreach(const QPointF &p, points) {
00427 bbox.setLeft(qMin(bbox.left(), p.x()));
00428 bbox.setRight(qMax(bbox.right(), p.x()));
00429 bbox.setTop(qMin(bbox.top(), p.y()));
00430 bbox.setBottom(qMax(bbox.bottom(), p.y()));
00431 }
00432
00433 if (degree() == 1) {
00434
00435 if (bbox.height() == 0.0)
00436 bbox.setHeight(0.1);
00437 if (bbox.width() == 0.0)
00438 bbox.setWidth(0.1);
00439 }
00440
00441 return bbox;
00442 }
00443
00444 QRectF KoPathSegment::boundingRect() const
00445 {
00446 if (! isValid())
00447 return QRectF();
00448
00449 QRectF rect = QRectF(d->first->point(), d->second->point()).normalized();
00450
00451 if (degree() == 1) {
00452
00453 if (rect.height() == 0.0)
00454 rect.setHeight(0.1);
00455 if (rect.width() == 0.0)
00456 rect.setWidth(0.1);
00457 } else {
00458
00459
00460
00461
00462
00463 foreach(qreal t, extrema()) {
00464 if (t >= 0.0 && t <= 1.0) {
00465 QPointF p = pointAt(t);
00466 rect.setLeft(qMin(rect.left(), p.x()));
00467 rect.setRight(qMax(rect.right(), p.x()));
00468 rect.setTop(qMin(rect.top(), p.y()));
00469 rect.setBottom(qMax(rect.bottom(), p.y()));
00470 }
00471 }
00472 }
00473
00474 return rect;
00475 }
00476
00477 QList<QPointF> KoPathSegment::intersections(const KoPathSegment &segment) const
00478 {
00479
00480
00481
00482 QList<QPointF> isects;
00483
00484 if (! isValid() || ! segment.isValid())
00485 return isects;
00486
00487 int degree1 = degree();
00488 int degree2 = segment.degree();
00489
00490 QRectF myBound = boundingRect();
00491 QRectF otherBound = segment.boundingRect();
00492
00493
00494 if (! myBound.intersects(otherBound)) {
00495
00496 return isects;
00497 }
00498
00499
00500 if (degree1 == 1 && degree2 == 1) {
00501
00502 isects += linesIntersection(segment);
00503 return isects;
00504 }
00505
00506
00507
00508 qreal dmin, dmax;
00509 if (degree1 == 1) {
00510 dmin = 0.0;
00511 dmax = 0.0;
00512 } else if (degree1 == 2) {
00513 qreal d1;
00514 if (d->first->activeControlPoint2())
00515 d1 = distanceFromChord(d->first->controlPoint2());
00516 else
00517 d1 = distanceFromChord(d->second->controlPoint1());
00518 dmin = qMin(0.0, 0.5 * d1);
00519 dmax = qMax(0.0, 0.5 * d1);
00520 } else {
00521 qreal d1 = distanceFromChord(d->first->controlPoint2());
00522 qreal d2 = distanceFromChord(d->second->controlPoint1());
00523 if (d1*d2 > 0.0) {
00524 dmin = 0.75 * qMin(qreal(0.0), qMin(d1, d2));
00525 dmax = 0.75 * qMax(qreal(0.0), qMax(d1, d2));
00526 } else {
00527 dmin = 4.0 / 9.0 * qMin(qreal(0.0), qMin(d1, d2));
00528 dmax = 4.0 / 9.0 * qMax(qreal(0.0), qMax(d1, d2));
00529 }
00530 }
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553 KoPathSegment dt;
00554 if (degree2 == 1) {
00555 QPointF p0(0.0, distanceFromChord(segment.first()->point()));
00556 QPointF p1(1.0, distanceFromChord(segment.second()->point()));
00557 dt = KoPathSegment(p0, p1);
00558 } else if (degree2 == 2) {
00559 QPointF p0(0.0, distanceFromChord(segment.first()->point()));
00560 QPointF p1 = segment.first()->activeControlPoint2()
00561 ? QPointF(0.5, distanceFromChord(segment.first()->controlPoint2()))
00562 : QPointF(0.5, distanceFromChord(segment.second()->controlPoint1()));
00563 QPointF p2(1.0, distanceFromChord(segment.second()->point()));
00564 dt = KoPathSegment(p0, p1, p2);
00565 } else if (degree2 == 3) {
00566 QPointF p0(0.0, distanceFromChord(segment.first()->point()));
00567 QPointF p1(1. / 3., distanceFromChord(segment.first()->controlPoint2()));
00568 QPointF p2(2. / 3., distanceFromChord(segment.second()->controlPoint1()));
00569 QPointF p3(1.0, distanceFromChord(segment.second()->point()));
00570 dt = KoPathSegment(p0, p1, p2, p3);
00571 } else {
00572
00573 return isects;
00574 }
00575
00576
00577 QList<QPointF> hull = dt.convexHull();
00578
00579
00580
00581 int hullCount = hull.count();
00582 qreal tmin = 1.0, tmax = 0.0;
00583 bool intersectionsFoundMax = false;
00584 bool intersectionsFoundMin = false;
00585
00586 for (int i = 0; i < hullCount; ++i) {
00587 QPointF p1 = hull[i];
00588 QPointF p2 = hull[(i+1)%hullCount];
00589
00590
00591 if (p1.y() > dmax && p2.y() > dmax)
00592 continue;
00593
00594 if (p1.y() < dmin && p2.y() < dmin)
00595 continue;
00596 if (p1.x() == p2.x()) {
00597
00598 bool dmaxIntersection = (dmax < qMax(p1.y(), p2.y()) && dmax > qMin(p1.y(), p2.y()));
00599 bool dminIntersection = (dmin < qMax(p1.y(), p2.y()) && dmin > qMin(p1.y(), p2.y()));
00600 if (dmaxIntersection || dminIntersection) {
00601 tmin = qMin(tmin, p1.x());
00602 tmax = qMax(tmax, p1.x());
00603 if (dmaxIntersection) {
00604 intersectionsFoundMax = true;
00605
00606 } else {
00607 intersectionsFoundMin = true;
00608
00609 }
00610 }
00611 } else if (p1.y() == p2.y()) {
00612
00613 if (p1.y() == dmin || p1.y() == dmax) {
00614 if (p1.y() == dmin) {
00615 intersectionsFoundMin = true;
00616
00617
00618 } else {
00619 intersectionsFoundMax = true;
00620
00621
00622 }
00623 tmin = qMin(tmin, p1.x());
00624 tmin = qMin(tmin, p2.x());
00625 tmax = qMax(tmax, p1.x());
00626 tmax = qMax(tmax, p2.x());
00627 }
00628 } else {
00629 qreal dx = p2.x() - p1.x();
00630 qreal dy = p2.y() - p1.y();
00631 qreal m = dy / dx;
00632 qreal n = p1.y() - m * p1.x();
00633 qreal t1 = (dmax - n) / m;
00634 if (t1 >= 0.0 && t1 <= 1.0) {
00635 tmin = qMin(tmin, t1);
00636 tmax = qMax(tmax, t1);
00637 intersectionsFoundMax = true;
00638
00639 }
00640 qreal t2 = (dmin - n) / m;
00641 if (t2 >= 0.0 && t2 < 1.0) {
00642 tmin = qMin(tmin, t2);
00643 tmax = qMax(tmax, t2);
00644 intersectionsFoundMin = true;
00645
00646 }
00647 }
00648 }
00649
00650 bool intersectionsFound = intersectionsFoundMin && intersectionsFoundMax;
00651
00652
00653
00654
00655 if (! intersectionsFound || (1.0 - (tmax - tmin)) <= 0.2) {
00656
00657
00658
00659
00660 QPair<KoPathSegment, KoPathSegment> parts = splitAt(0.5);
00661 if (chordLength() < 1e-5)
00662 isects += parts.first.second()->point();
00663 else {
00664 isects += segment.intersections(parts.first);
00665 isects += segment.intersections(parts.second);
00666 }
00667 } else if (qAbs(tmin - tmax) < 1e-5) {
00668
00669
00670 isects.append(segment.pointAt(tmin));
00671 } else {
00672 QPair<KoPathSegment, KoPathSegment> clip1 = segment.splitAt(tmin);
00673
00674 qreal t = (tmax - tmin) / (1.0 - tmin);
00675 QPair<KoPathSegment, KoPathSegment> clip2 = clip1.second.splitAt(t);
00676
00677 isects += clip2.first.intersections(*this);
00678 }
00679
00680 return isects;
00681 }
00682
00683 KoPathSegment KoPathSegment::mapped(const QMatrix &matrix) const
00684 {
00685 if (! isValid())
00686 return *this;
00687
00688 KoPathPoint * p1 = new KoPathPoint(*d->first);
00689 KoPathPoint * p2 = new KoPathPoint(*d->second);
00690 p1->map(matrix);
00691 p2->map(matrix);
00692
00693 return KoPathSegment(p1, p2);
00694 }
00695
00696 KoPathSegment KoPathSegment::toCubic() const
00697 {
00698 if (! isValid())
00699 return KoPathSegment();
00700
00701 KoPathPoint * p1 = new KoPathPoint(*d->first);
00702 KoPathPoint * p2 = new KoPathPoint(*d->second);
00703
00704 if (degree() == 1) {
00705 p1->setControlPoint2(p1->point() + 0.3 * (p2->point() - p1->point()));
00706 p2->setControlPoint1(p2->point() + 0.3 * (p1->point() - p2->point()));
00707 } else if (degree() == 2) {
00708
00709
00710
00711
00712
00713
00714
00715 QPointF a1 = p1->activeControlPoint2() ? p1->controlPoint2() : p2->controlPoint1();
00716 QPointF b1 = p1->point() + 2.0 / 3.0 * (a1 - p1->point());
00717 QPointF b2 = a1 + 1.0 / 3.0 * (p2->point() - a1);
00718 p1->setControlPoint2(b1);
00719 p2->setControlPoint1(b2);
00720 }
00721
00722 return KoPathSegment(p1, p2);
00723 }
00724
00725 qreal KoPathSegment::length(qreal error) const
00726 {
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752 int deg = degree();
00753
00754 if (deg == -1)
00755 return 0.0;
00756
00757 QList<QPointF> ctrlPoints = controlPoints();
00758
00759
00760 qreal chordLen = chordLength();
00761
00762 if (deg == 1) {
00763 return chordLen;
00764 }
00765
00766
00767 qreal polyLength = 0.0;
00768
00769 for (int i = 0; i < deg; ++i) {
00770 QPointF ctrlSegment = ctrlPoints[i+1] - ctrlPoints[i];
00771 polyLength += sqrt(ctrlSegment.x() * ctrlSegment.x() + ctrlSegment.y() * ctrlSegment.y());
00772 }
00773
00774 if ((polyLength - chordLen) > error) {
00775
00776 QPair<KoPathSegment, KoPathSegment> parts = splitAt(0.5);
00777 return parts.first.length(error) + parts.second.length(error);
00778 } else {
00779
00780 if (deg == 3)
00781 return 0.5 * chordLen + 0.5 * polyLength;
00782 else
00783 return (2.0 * chordLen + polyLength) / 3.0;
00784 }
00785 }
00786
00787 qreal KoPathSegment::lengthAt(qreal t, qreal error) const
00788 {
00789 if (t == 0.0)
00790 return 0.0;
00791 if (t == 1.0)
00792 return length(error);
00793
00794 QPair<KoPathSegment, KoPathSegment> parts = splitAt(t);
00795 return parts.first.length(error);
00796 }
00797
00798 qreal KoPathSegment::paramAtLength(qreal length, qreal tolerance) const
00799 {
00800 int deg = degree();
00801 if (deg < 1)
00802 return 0.0;
00803 if (length <= 0.0)
00804 return 0.0;
00805
00806 if (deg == 1)
00807 return length / chordLength();
00808
00809 qreal startT = 0.0;
00810 qreal midT = 0.5;
00811 qreal endT = 1.0;
00812
00813 qreal midLength = lengthAt(0.5);
00814 while (qAbs(midLength - length) / length > tolerance) {
00815 if (midLength < length)
00816 startT = midT;
00817 else
00818 endT = midT;
00819
00820
00821 midT = 0.5 * (startT + endT);
00822
00823 midLength = lengthAt(midT);
00824 }
00825
00826 return midT;
00827 }
00828
00829 bool KoPathSegment::isFlat(qreal tolerance) const
00830 {
00831
00832
00833
00834
00835
00836
00837
00838
00839 if (degree() <= 1)
00840 return true;
00841
00842 QPointF chord = d->second->point() - d->first->point();
00843
00844 qreal chordAngle = atan2(chord.y(), chord.x());
00845 QMatrix m;
00846 m.translate(d->first->point().x(), d->first->point().y());
00847 m.rotate(chordAngle * M_PI / 180.0);
00848 m.translate(-d->first->point().x(), -d->first->point().y());
00849
00850 KoPathSegment s = mapped(m);
00851
00852 qreal minDist = 0.0;
00853 qreal maxDist = 0.0;
00854
00855 foreach(qreal t, s.extrema()) {
00856 if (t >= 0.0 && t <= 1.0) {
00857 QPointF p = pointAt(t);
00858 qreal dist = s.distanceFromChord(p);
00859 minDist = qMin(dist, minDist);
00860 maxDist = qMax(dist, maxDist);
00861 }
00862 }
00863
00864 return (maxDist - minDist <= tolerance);
00865 }
00866
00867 qreal KoPathSegment::distanceFromChord(const QPointF &point) const
00868 {
00869
00870 QPointF chord = d->second->point() - d->first->point();
00871
00872 QPointF relPoint = point - d->first->point();
00873
00874 qreal scale = chord.x() * relPoint.x() + chord.y() * relPoint.y();
00875 scale /= chord.x() * chord.x() + chord.y() * chord.y();
00876
00877
00878 QPointF diffVec = scale * chord - relPoint;
00879
00880
00881 qreal distance = sqrt(diffVec.x() * diffVec.x() + diffVec.y() * diffVec.y());
00882
00883
00884 if (chord.x()*relPoint.y() - chord.y()*relPoint.x() > 0) {
00885 return distance;
00886 } else {
00887 return -distance;
00888 }
00889 }
00890
00891 qreal KoPathSegment::chordLength() const
00892 {
00893 QPointF chord = d->second->point() - d->first->point();
00894 return sqrt(chord.x()*chord.x() + chord.y()*chord.y());
00895 }
00896
00897 QList<QPointF> KoPathSegment::convexHull() const
00898 {
00899 QList<QPointF> hull;
00900 int deg = degree();
00901 if (deg == 1) {
00902
00903 hull.append(d->first->point());
00904 hull.append(d->second->point());
00905 } else if (deg == 2) {
00906
00907
00908 QPointF chord = d->second->point() - d->first->point();
00909 QPointF cp = d->first->activeControlPoint2() ? d->first->controlPoint2() : d->second->controlPoint1();
00910 QPointF relP = cp - d->first->point();
00911
00912 bool pIsRight = (chord.x() * relP.y() - chord.y() * relP.x() > 0);
00913 hull.append(d->first->point());
00914 if (pIsRight)
00915 hull.append(cp);
00916 hull.append(d->second->point());
00917 if (! pIsRight)
00918 hull.append(cp);
00919 } else if (deg == 3) {
00920
00921 QPointF chord = d->second->point() - d->first->point();
00922 QPointF relP1 = d->first->controlPoint2() - d->first->point();
00923
00924 bool p1IsRight = (chord.x() * relP1.y() - chord.y() * relP1.x() > 0);
00925 hull.append(d->first->point());
00926 if (p1IsRight)
00927 hull.append(d->first->controlPoint2());
00928 hull.append(d->second->point());
00929 if (! p1IsRight)
00930 hull.append(d->first->controlPoint2());
00931
00932
00933
00934 bool rightOfEdge[3];
00935 QPointF lastPoint = d->second->controlPoint1();
00936 for (int i = 0; i < 3; ++i) {
00937 QPointF relP = lastPoint - hull[i];
00938 QPointF edge = hull[(i+1)%3] - hull[i];
00939 rightOfEdge[i] = (edge.x() * relP.y() - edge.y() * relP.x() > 0);
00940 }
00941 for (int i = 0; i < 3; ++i) {
00942 int prev = (3 + i - 1) % 3;
00943 int next = (i + 1) % 3;
00944
00945 if (! rightOfEdge[prev] && rightOfEdge[i] && ! rightOfEdge[next]) {
00946
00947 hull.insert(i + 1, lastPoint);
00948 break;
00949 }
00950
00951 if (rightOfEdge[i] && rightOfEdge[next]) {
00952
00953 hull[i+1] = lastPoint;
00954 break;
00955 }
00956
00957 if (rightOfEdge[i] && rightOfEdge[prev]) {
00958 hull[i] = lastPoint;
00959 break;
00960 }
00961 }
00962 }
00963
00964 return hull;
00965 }
00966
00967 QPair<KoPathSegment, KoPathSegment> KoPathSegment::splitAt(qreal t) const
00968 {
00969 QPair<KoPathSegment, KoPathSegment> results;
00970 if (! isValid())
00971 return results;
00972
00973 if (degree() == 1) {
00974 QPointF p = d->first->point() + t * (d->second->point() - d->first->point());
00975 results.first = KoPathSegment(d->first->point(), p);
00976 results.second = KoPathSegment(p, d->second->point());
00977 } else {
00978 QPointF newCP2, newCP1, splitP, splitCP1, splitCP2;
00979
00980 deCasteljau(t, &newCP2, &splitCP1, &splitP, &splitCP2, &newCP1);
00981
00982 if (degree() == 2) {
00983 if( second()->activeControlPoint1() ) {
00984 KoPathPoint * s1p1 = new KoPathPoint( 0, d->first->point() );
00985 KoPathPoint * s1p2 = new KoPathPoint( 0, splitP );
00986 s1p2->setControlPoint1( splitCP1 );
00987 KoPathPoint * s2p1 = new KoPathPoint( 0, splitP );
00988 KoPathPoint * s2p2 = new KoPathPoint( 0, d->second->point() );
00989 s2p2->setControlPoint1( splitCP2 );
00990 results.first = KoPathSegment( s1p1, s1p2 );
00991 results.second = KoPathSegment( s2p1, s2p2 );
00992 }
00993 else {
00994 results.first = KoPathSegment(d->first->point(), splitCP1, splitP);
00995 results.second = KoPathSegment(splitP, splitCP2, d->second->point());
00996 }
00997 } else {
00998 results.first = KoPathSegment(d->first->point(), newCP2, splitCP1, splitP);
00999 results.second = KoPathSegment(splitP, splitCP2, newCP1, d->second->point());
01000 }
01001 }
01002
01003 return results;
01004 }
01005
01006 void KoPathSegment::deCasteljau(qreal t, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4, QPointF *p5) const
01007 {
01008 if( ! isValid() )
01009 return;
01010
01011 int deg = degree();
01012 QPointF q[4];
01013
01014 q[0] = d->first->point();
01015 if (deg == 2) {
01016 q[1] = d->first->activeControlPoint2() ? d->first->controlPoint2() : d->second->controlPoint1();
01017 } else if (deg == 3) {
01018 q[1] = d->first->controlPoint2();
01019 q[2] = d->second->controlPoint1();
01020 }
01021 q[deg] = d->second->point();
01022
01023
01024 QPointF p[3];
01025
01026
01027 for (unsigned short j = 1; j <= deg; ++j) {
01028 for (unsigned short i = 0; i <= deg - j; ++i) {
01029 q[i] = (1.0 - t) * q[i] + t * q[i + 1];
01030 }
01031 p[j - 1] = q[0];
01032 }
01033
01034 if (deg == 2) {
01035 if (p2)
01036 *p2 = p[0];
01037 if (p3)
01038 *p3 = p[1];
01039 if (p4)
01040 *p4 = q[1];
01041 } else if (deg == 3) {
01042 if (p1)
01043 *p1 = p[0];
01044 if (p2)
01045 *p2 = p[1];
01046 if (p3)
01047 *p3 = p[2];
01048 if (p4)
01049 *p4 = q[1];
01050 if (p5)
01051 *p5 = q[2];
01052 }
01053 }
01054
01055 QList<QPointF> KoPathSegment::controlPoints() const
01056 {
01057 QList<QPointF> controlPoints;
01058 controlPoints.append(d->first->point());
01059 if (d->first->activeControlPoint2())
01060 controlPoints.append(d->first->controlPoint2());
01061 if (d->second->activeControlPoint1())
01062 controlPoints.append(d->second->controlPoint1());
01063 controlPoints.append(d->second->point());
01064
01065 return controlPoints;
01066 }
01067
01068 QList<QPointF> KoPathSegment::linesIntersection(const KoPathSegment &segment) const
01069 {
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083 QList<QPointF> isects;
01084 QPointF A = d->first->point();
01085 QPointF B = d->second->point();
01086 QPointF C = segment.first()->point();
01087 QPointF D = segment.second()->point();
01088
01089 qreal denom = (B.x() - A.x()) * (D.y() - C.y()) - (B.y() - A.y()) * (D.x() - C.x());
01090 qreal num_r = (A.y() - C.y()) * (D.x() - C.x()) - (A.x() - C.x()) * (D.y() - C.y());
01091
01092 if (denom == 0.0 && num_r == 0.0)
01093 return isects;
01094
01095 qreal num_s = (A.y() - C.y()) * (B.x() - A.x()) - (A.x() - C.x()) * (B.y() - A.y());
01096 qreal r = num_r / denom;
01097 qreal s = num_s / denom;
01098
01099
01100 if (r < 0.0 || r > 1.0)
01101 return isects;
01102 if (s < 0.0 || s > 1.0)
01103 return isects;
01104
01105
01106 isects.append(A + r * (B - A));
01107
01108 return isects;
01109 }
01110
01111 QList<qreal> KoPathSegment::extrema() const
01112 {
01113 int deg = degree();
01114 if (deg <= 1)
01115 return QList<qreal>();
01116
01117 QList<qreal> params;
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129
01130
01131
01132 if (deg == 2) {
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146 QPointF cp = d->first->activeControlPoint2() ?
01147 d->first->controlPoint2() : d->second->controlPoint1();
01148 QPointF x0 = cp - d->first->point();
01149 QPointF x1 = d->second->point() - cp;
01150
01151
01152 QPointF a = x1 - x0;
01153 QPointF c = x0;
01154
01155 if (a.x() != 0.0)
01156 params.append(-c.x() / a.x());
01157 if (a.y() != 0.0)
01158 params.append(-c.y() / a.y());
01159 } else if (deg == 3) {
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173 QPointF x0 = d->first->controlPoint2() - d->first->point();
01174 QPointF x1 = d->second->controlPoint1() - d->first->controlPoint2();
01175 QPointF x2 = d->second->point() - d->second->controlPoint1();
01176
01177
01178 QPointF a = x2 - 2.0 * x1 + x0;
01179 QPointF b = 2.0 * x1 - 2.0 * x0;
01180 QPointF c = x0;
01181
01182
01183 if (a.x() == 0.0) {
01184 params.append(- c.x() / b.x());
01185 } else {
01186 qreal rx = b.x() * b.x() - 4.0 * a.x() * c.x();
01187 if (rx < 0.0)
01188 rx = 0.0;
01189 params.append((-b.x() + sqrt(rx)) / (2.0*a.x()));
01190 params.append((-b.x() - sqrt(rx)) / (2.0*a.x()));
01191 }
01192
01193
01194 if (a.y() == 0.0) {
01195 params.append(- c.y() / b.y());
01196 } else {
01197 qreal ry = b.y() * b.y() - 4.0 * a.y() * c.y();
01198 if (ry < 0.0)
01199 ry = 0.0;
01200 params.append((-b.y() + sqrt(ry)) / (2.0*a.y()));
01201 params.append((-b.y() - sqrt(ry)) / (2.0*a.y()));
01202 }
01203 }
01204
01205 return params;
01206 }
01207
01208 qreal KoPathSegment::nearestPoint( const QPointF &point ) const
01209 {
01210 if (!isValid())
01211 return -1.0;
01212
01213 const int deg = degree();
01214
01215
01216 if (deg == 1) {
01217
01218 QPointF chord = d->second->point() - d->first->point();
01219
01220 QPointF relPoint = point - d->first->point();
01221
01222 qreal scale = chord.x() * relPoint.x() + chord.y() * relPoint.y();
01223
01224 scale /= chord.x() * chord.x() + chord.y() * chord.y();
01225
01226 if (scale < 0.0) {
01227 return 0.0;
01228 } else if (scale > 1.0) {
01229 return 1.0;
01230 } else {
01231 return scale;
01232 }
01233 }
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267
01268 QList<QPointF> ctlPoints = controlPoints();
01269
01270
01271 QPointF * c_i = new QPointF[ deg + 1 ];
01272
01273 for( int i = 0; i <= deg; ++i ) {
01274 c_i[ i ] = ctlPoints[ i ] - point;
01275 }
01276
01277
01278 QPointF * d_j = new QPointF[ deg ];
01279
01280 for( int j = 0; j <= deg - 1; ++j ) {
01281 d_j[ j ] = 3.0 * ( ctlPoints[ j+1 ] - ctlPoints[ j ] );
01282 }
01283
01284
01285 qreal * products = new qreal[ deg * ( deg + 1 ) ];
01286
01287 for( int j = 0; j <= deg - 1; ++j ) {
01288 for( int i = 0; i <= deg; ++i ) {
01289 products[ j * ( deg + 1 ) + i ] = d_j[ j ].x() * c_i[ i ].x() + d_j[ j ].y() * c_i[ i ].y();
01290 }
01291 }
01292
01293
01294 delete[]( d_j );
01295 delete[]( c_i );
01296
01297
01298 BezierSegment newCurve;
01299 newCurve.setDegree( 2 * deg - 1 );
01300
01301 for( unsigned short u = 0; u <= 2 * deg - 1; ++u ) {
01302 newCurve.setPoint(u, QPointF(static_cast<qreal>( u ) / static_cast<qreal>( 2 * deg - 1 ), 0.0) );
01303 }
01304
01305
01306 static double z3[3*4] = {1.0, 0.6, 0.3, 0.1, 0.4, 0.6, 0.6, 0.4, 0.1, 0.3, 0.6, 1.0};
01307
01308 static double z2[2*3] = {1.0, 2./3., 1./3., 1./3., 2./3., 1.0};
01309
01310 double * z = degree() == 3 ? z3 : z2;
01311
01312
01313 for( int k = 0; k <= 2 * deg - 1; ++k ) {
01314 int min = qMin( k, deg );
01315
01316 for(unsigned short i = qMax( 0, k - ( deg - 1 ) ); i <= min; ++i ) {
01317 unsigned short j = k - i;
01318
01319
01320 QPointF currentPoint = newCurve.point( k );
01321 currentPoint.ry() += products[ j * ( deg + 1 ) + i ] * z[ j * ( deg + 1 ) + i ];
01322 newCurve.setPoint( k, currentPoint );
01323 }
01324 }
01325
01326
01327 delete[]( products );
01328
01329
01330 QList<qreal> rootParams = newCurve.roots();
01331
01332
01333
01334
01335 QPointF dist = d->first->point() - point;
01336 qreal distanceSquared = dist.x() * dist.x() + dist.y() * dist.y();
01337 qreal oldDistanceSquared;
01338 qreal resultParam = 0.0;
01339
01340
01341 foreach( qreal root, rootParams ) {
01342 dist = point - pointAt( root );
01343 oldDistanceSquared = distanceSquared;
01344 distanceSquared = dist.x() * dist.x() + dist.y() * dist.y();
01345
01346 if (distanceSquared < oldDistanceSquared)
01347 resultParam = root;
01348 }
01349
01350
01351 dist = d->second->point() - point;
01352 oldDistanceSquared = distanceSquared;
01353 distanceSquared = dist.x() * dist.x() + dist.y() * dist.y();
01354
01355 if( distanceSquared < oldDistanceSquared )
01356 resultParam = 1.0;
01357
01358 return resultParam;
01359 }
01360
01361 QList<qreal> KoPathSegment::roots() const
01362 {
01363 QList<qreal> rootParams;
01364
01365 if (! isValid())
01366 return rootParams;
01367
01368
01369
01370 int xAxisCrossings = BezierSegment::controlPolygonZeros( controlPoints() );
01371
01372 if (! xAxisCrossings) {
01373
01374 }
01375 else if (xAxisCrossings == 1 && isFlat( 0.01 / chordLength() )) {
01376
01377
01378 QPointF chord = d->second->point() - d->first->point();
01379 QPointF segStart = d->first->point();
01380 rootParams.append( ( chord.x() * segStart.y() - chord.y() * segStart.x() ) / - chord.y() );
01381 }
01382 else {
01383
01384 QPair<KoPathSegment, KoPathSegment> splitSegments = splitAt( 0.5 );
01385 rootParams += splitSegments.first.roots();
01386 rootParams += splitSegments.second.roots();
01387 }
01388
01389 return rootParams;
01390 }
01391
01392 KoPathSegment KoPathSegment::interpolate( const QPointF &p0, const QPointF &p1, const QPointF &p2, qreal t )
01393 {
01394 if (t <= 0.0 || t >= 1.0)
01395 return KoPathSegment();
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405 QPointF c1 = p1 - (1.0-t)*(1.0-t)*p0 - t*t*p2;
01406
01407 qreal denom = 2.0*t*(1.0-t);
01408
01409 c1.rx() /= denom;
01410 c1.ry() /= denom;
01411
01412 return KoPathSegment( p0, c1, p2 );
01413 }
01414
01415 KoPathSegment KoPathSegment::convertToCubic() const
01416 {
01417 if (! isValid())
01418 return KoPathSegment();
01419
01420 int deg = degree();
01421
01422 QList<QPointF> ctrlPoints = controlPoints();
01423
01424 if (deg == 1) {
01425 QPointF p0 = ctrlPoints[0];
01426 QPointF p1 = p0 + 0.25 * (ctrlPoints[1]-p0);
01427 QPointF p2 = p1 + 0.25 * (ctrlPoints[1]-p0);
01428 QPointF p3 = ctrlPoints[1];
01429 return KoPathSegment(p0, p1, p2, p3);
01430 }
01431 else if (deg == 2) {
01432 QPointF p0 = ctrlPoints[0];
01433 QPointF p1 = p0 + 2.0/3.0 * (ctrlPoints[1]-p0);
01434 QPointF p2 = p1 + 1.0/3.0 * (ctrlPoints[2]-p0);
01435 QPointF p3 = ctrlPoints[2];
01436 return KoPathSegment(p0, p1, p2, p3);
01437 }
01438 else if (deg == 3) {
01439 return KoPathSegment(*this);
01440 }
01441
01442 return KoPathSegment();
01443 }
01444
01445 void KoPathSegment::printDebug() const
01446 {
01447 int deg = degree();
01448 kDebug(30006) << "degree:" << deg;
01449 if (deg < 1)
01450 return;
01451
01452 kDebug(30006) << "P0:" << d->first->point();
01453 if (deg == 1) {
01454 kDebug(30006) << "P2:" << d->second->point();
01455 } else if (deg == 2) {
01456 if (d->first->activeControlPoint2())
01457 kDebug(30006) << "P1:" << d->first->controlPoint2();
01458 else
01459 kDebug(30006) << "P1:" << d->second->controlPoint1();
01460 kDebug(30006) << "P2:" << d->second->point();
01461 } else if (deg == 3) {
01462 kDebug(30006) << "P1:" << d->first->controlPoint2();
01463 kDebug(30006) << "P2:" << d->second->controlPoint1();
01464 kDebug(30006) << "P3:" << d->second->point();
01465 }
01466 }