libs/flake

KoPathSegment.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002  * Copyright (C) 2008-2009 Jan Hambrecht <jaham@gmx.net>
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Library General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Library General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Library General Public License
00015  * along with this library; see the file COPYING.LIB.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
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         /* Copy control points  */
00084         for (int j = 0; j <= deg; j++) {
00085             Vtemp[0][j] = points[j];
00086         }
00087 
00088         /* Triangle computation */
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         // Calculate how often the control polygon crosses the x-axis
00120         // This is the upper limit for the number of roots.
00121         int xAxisCrossings = controlPolygonZeros( points );
00122 
00123         if (! xAxisCrossings) {
00124             // No solutions.
00125             return rootParams;
00126         }
00127         else if (xAxisCrossings == 1) {
00128             // Exactly one solution.
00129 
00130             // Stop recursion when the tree is deep enough
00131             if (depth >= MaxRecursionDepth) {
00132                 // if deep enough, return 1 solution at midpoint
00133                 rootParams.append( (points.first().x() + points.last().x()) / 2.0 );
00134                 return rootParams;
00135             }
00136             else if( isFlat( FlatnessTolerance ) ) {
00137                 // Calculate intersection of chord with x-axis.
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         // Many solutions. Do recursive midpoint subdivision.
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         // Find the  perpendicular distance from each interior control point to
00185         // the line connecting points[0] and points[degree]
00186         qreal * distance = new qreal[deg + 1];
00187 
00188         // Derive the implicit equation for line connecting first and last control points
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             // Compute distance from each of the points to that line
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         // Find the largest distance
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         // Implicit equation for zero line
00221         qreal a1 = 0.0;
00222         qreal b1 = 1.0;
00223         qreal c1 = 0.0;
00224 
00225         // Implicit equation for "above" line
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         // Implicit equation for "below" line
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         // Compute intercepts of bounding box
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         // adjust bounding rect of horizontal and vertical lines
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         // adjust bounding rect of horizontal and vertical lines
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          * The basic idea for calculating the axis aligned bounding box (AABB) for bezier segments
00460          * was found in comp.graphics.algorithms:
00461          * Use the points at the extrema of the curve to calculate the AABB.
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     // this function uses a technique known as bezier clipping to find the
00480     // intersections of the two bezier curves
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     //kDebug(30006) << "my boundingRect =" << myBound;
00493     //kDebug(30006) << "other boundingRect =" << otherBound;
00494     if (! myBound.intersects(otherBound)) {
00495         //kDebug(30006) << "segments do not intersect";
00496         return isects;
00497     }
00498 
00499     // short circuit lines intersection
00500     if (degree1 == 1 && degree2 == 1) {
00501         //kDebug(30006) << "intersecting two lines";
00502         isects += linesIntersection(segment);
00503         return isects;
00504     }
00505 
00506     // first calculate the fat line L by using the signed distances
00507     // of the control points from the chord
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     //kDebug(30006) << "using fat line: dmax =" << dmax << " dmin =" << dmin;
00533 
00534     /*
00535       the other segment is given as a bezier curve of the form:
00536      (1) P(t) = sum_i P_i * B_{n,i}(t)
00537      our chord line is of the form:
00538      (2) ax + by + c = 0
00539      we can determine the distance d(t) from any point P(t) to our chord
00540      by substituting formula (1) into formula (2):
00541      d(t) = sum_i d_i B_{n,i}(t), where d_i = a*x_i + b*y_i + c
00542      which forms another explicit bezier curve
00543      D(t) = (t,d(t)) = sum_i D_i B_{n,i}(t)
00544      now values of t for which P(t) lies outside of our fat line L
00545      corrsponds to values of t for which D(t) lies above d = dmax or
00546      below d = dmin
00547      we can determine parameter ranges of t for which P(t) is guaranteed
00548      to lie outside of L by identifying ranges of t which the convex hull
00549      of D(t) lies above dmax or below dmin
00550     */
00551     // now calculate the control points of D(t) by using the signed
00552     // distances of P_i to our chord
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         //kDebug(30006) << "invalid degree of segment -> exiting";
00573         return isects;
00574     }
00575 
00576     // get convex hull of the segment D(t)
00577     QList<QPointF> hull = dt.convexHull();
00578 
00579     // now calculate intersections with the line y1 = dmin, y2 = dmax
00580     // with the convex hull edges
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         //kDebug(30006) << "intersecting hull edge (" << p1 << p2 << ")";
00590         // hull edge is completely above dmax
00591         if (p1.y() > dmax && p2.y() > dmax)
00592             continue;
00593         // hull egde is completely below dmin
00594         if (p1.y() < dmin && p2.y() < dmin)
00595             continue;
00596         if (p1.x() == p2.x()) {
00597             // vertical edge
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                     //kDebug(30006) << "found intersection with dmax at " << p1.x() << "," << dmax;
00606                 } else {
00607                     intersectionsFoundMin = true;
00608                     //kDebug(30006) << "found intersection with dmin at " << p1.x() << "," << dmin;
00609                 }
00610             }
00611         } else if (p1.y() == p2.y()) {
00612             // horizontal line
00613             if (p1.y() == dmin || p1.y() == dmax) {
00614                 if (p1.y() == dmin) {
00615                     intersectionsFoundMin = true;
00616                     //kDebug(30006) << "found intersection with dmin at " << p1.x() << "," << dmin;
00617                     //kDebug(30006) << "found intersection with dmin at " << p2.x() << "," << dmin;
00618                 } else {
00619                     intersectionsFoundMax = true;
00620                     //kDebug(30006) << "found intersection with dmax at " << p1.x() << "," << dmax;
00621                     //kDebug(30006) << "found intersection with dmax at " << p2.x() << "," << dmax;
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                 //kDebug(30006) << "found intersection with dmax at " << t1 << "," << dmax;
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                 //kDebug(30006) << "found intersection with dmin at " << t2 << "," << dmin;
00646             }
00647         }
00648     }
00649 
00650     bool intersectionsFound = intersectionsFoundMin && intersectionsFoundMax;
00651 
00652     //if ( intersectionsFound )
00653     //    kDebug(30006) << "clipping segment to interval [" << tmin << "," << tmax << "]";
00654 
00655     if (! intersectionsFound || (1.0 - (tmax - tmin)) <= 0.2) {
00656         //kDebug(30006) << "could not clip enough -> split segment";
00657         // we could not reduce the interval significantly
00658         // so split the curve and calculate intersections
00659         // with the remaining parts
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         //kDebug(30006) << "Yay, we found an intersection";
00669         // the inteval is pretty small now, just calculate the intersection at this point
00670         isects.append(segment.pointAt(tmin));
00671     } else {
00672         QPair<KoPathSegment, KoPathSegment> clip1 = segment.splitAt(tmin);
00673         //kDebug(30006) << "splitting segment at" << tmin;
00674         qreal t = (tmax - tmin) / (1.0 - tmin);
00675         QPair<KoPathSegment, KoPathSegment> clip2 = clip1.second.splitAt(t);
00676         //kDebug(30006) << "splitting second part at" << t << "("<<tmax<<")";
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         /* quadric bezier (a0,a1,a2) to cubic bezier (b0,b1,b2,b3):
00709         *
00710         * b0 = a0
00711         * b1 = a0 + 2/3 * (a1-a0)
00712         * b2 = a1 + 1/3 * (a2-a1)
00713         * b3 = a2
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      * This algorithm is implemented based on an idea by Jens Gravesen:
00729      * "Adaptive subdivision and the length of Bezier curves" mat-report no. 1992-10, Mathematical Institute,
00730      * The Technical University of Denmark.
00731      *
00732      * By subdividing the curve at parameter value t you only have to find the length of a full Bezier curve.
00733      * If you denote the length of the control polygon by L1 i.e.:
00734      *   L1 = |P0 P1| +|P1 P2| +|P2 P3|
00735      *
00736      * and the length of the cord by L0 i.e.:
00737      *   L0 = |P0 P3|
00738      *
00739      * then
00740      *   L = 1/2*L0 + 1/2*L1
00741      *
00742      * is a good approximation to the length of the curve, and the difference
00743      *   ERR = L1-L0
00744      *
00745      * is a measure of the error. If the error is to large, then you just subdivide curve at parameter value
00746      * 1/2, and find the length of each half.
00747      * If m is the number of subdivisions then the error goes to zero as 2^-4m.
00748      * If you don't have a cubic curve but a curve of degree n then you put
00749      *   L = (2*L0 + (n-1)*L1)/(n+1)
00750      */
00751 
00752     int deg = degree();
00753 
00754     if (deg == -1)
00755         return 0.0;
00756 
00757     QList<QPointF> ctrlPoints = controlPoints();
00758 
00759     // calculate chord length
00760     qreal chordLen = chordLength();
00761 
00762     if (deg == 1) {
00763         return chordLen;
00764     }
00765 
00766     // calculate length of control polygon
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         // the error is still bigger than our tolerance -> split segment
00776         QPair<KoPathSegment, KoPathSegment> parts = splitAt(0.5);
00777         return parts.first.length(error) + parts.second.length(error);
00778     } else {
00779         // the error is smaller than our tolerance
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; // interval start
00810     qreal midT = 0.5;   // interval center
00811     qreal endT = 1.0;   // interval end
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         // new interval center
00821         midT = 0.5 * (startT + endT);
00822         // length at new interval center
00823         midLength = lengthAt(midT);
00824     }
00825 
00826     return midT;
00827 }
00828 
00829 bool KoPathSegment::isFlat(qreal tolerance) const
00830 {
00831     /*
00832      * Calculate the height of the bezier curve.
00833      * This is done by rotating the curve so that then chord
00834      * is parallel to the x-axis and the calculating the
00835      * parameters t for the extrema of the curve.
00836      * The curve points at the extrema are then used to
00837      * calculate the height.
00838      */
00839     if (degree() <= 1)
00840         return true;
00841 
00842     QPointF chord = d->second->point() - d->first->point();
00843     // calculate angle of chord to the x-axis
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     // the segments chord
00870     QPointF chord = d->second->point() - d->first->point();
00871     // the point relative to the segment
00872     QPointF relPoint = point - d->first->point();
00873     // project point to chord
00874     qreal scale = chord.x() * relPoint.x() + chord.y() * relPoint.y();
00875     scale /= chord.x() * chord.x() + chord.y() * chord.y();
00876 
00877     // the vector form the point to the projected point on the chord
00878     QPointF diffVec = scale * chord - relPoint;
00879 
00880     // the unsigned distance of the point to the chord
00881     qreal distance = sqrt(diffVec.x() * diffVec.x() + diffVec.y() * diffVec.y());
00882 
00883     // determine sign of the distance using the cross product
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         // easy just the two end points
00903         hull.append(d->first->point());
00904         hull.append(d->second->point());
00905     } else if (deg == 2) {
00906         // we want to have a counter-clockwise oriented triangle
00907         // of the three control points
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         // check on which side of the chord the control point is
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         // we want a counter-clockwise oriented polygon
00921         QPointF chord = d->second->point() - d->first->point();
00922         QPointF relP1 = d->first->controlPoint2() - d->first->point();
00923         // check on which side of the chord the control points are
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         // now we have a counter-clockwise triangle with the points i,j,k
00933         // we have to check where the last control points lies
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             // check if point is only right of the n-th edge
00945             if (! rightOfEdge[prev] && rightOfEdge[i] && ! rightOfEdge[next]) {
00946                 // insert by breaking the n-th edge
00947                 hull.insert(i + 1, lastPoint);
00948                 break;
00949             }
00950             // check if it is right of the n-th and right of the (n+1)-th edge
00951             if (rightOfEdge[i] && rightOfEdge[next]) {
00952                 // remove both edge, insert two new edges
00953                 hull[i+1] = lastPoint;
00954                 break;
00955             }
00956             // check if it is right of n-th and right of (n-1)-th edge
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     // points of the new segment after the split point
01024     QPointF p[3];
01025 
01026     // the De Casteljau algorithm
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     //kDebug(30006) << "intersecting two lines";
01071     /*
01072     we have to line segments:
01073 
01074     s1 = A + r * (B-A), s2 = C + s * (D-C) for r,s in [0,1]
01075 
01076         if s1 and s2 intersect we set s1 = s2 so we get these two equations:
01077 
01078     Ax + r * (Bx-Ax) = Cx + s * (Dx-Cx)
01079     Ay + r * (By-Ay) = Cy + s * (Dy-Cy)
01080 
01081     which we can solve to get r and s
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     // check if lines are collinear
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     // check if intersection is inside our line segments
01100     if (r < 0.0 || r > 1.0)
01101         return isects;
01102     if (s < 0.0 || s > 1.0)
01103         return isects;
01104 
01105     // calculate the actual intersection point
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      * The basic idea for calculating the extrama for bezier segments
01121      * was found in comp.graphics.algorithms:
01122      *
01123      * Both the x coordinate and the y coordinate are polynomial. Newton told
01124      * us that at a maximum or minimum the derivative will be zero.
01125      *
01126      * We have a helpful trick for the derivatives: use the curve r(t) defined by
01127      * differences of successive control points.
01128      * Setting r(t) to zero and using the x and y coordinates of differences of
01129      * successive control points lets us find the parameters t, where the original
01130      * bezier curve has a minimum or a maximum.
01131      */
01132     if (deg == 2) {
01133         /*
01134          * For quadratic bezier curves r(t) is a linear Bezier curve:
01135          *
01136          *  1
01137          * r(t) = Sum Bi,1(t) *Pi = B0,1(t) * P0 + B1,1(t) * P1
01138          * i=0
01139          *
01140          * r(t) = (1-t) * P0 + t * P1
01141          *
01142          * r(t) = (P1 - P0) * t + P0
01143          */
01144 
01145         // calcualting the differences between successive control points
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         // calculating the coefficents
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          * For cubic bezier curves r(t) is a quadratic Bezier curve:
01162          *
01163          *  2
01164          * r(t) = Sum Bi,2(t) *Pi = B0,2(t) * P0 + B1,2(t) * P1 + B2,2(t) * P2
01165          * i=0
01166          *
01167          * r(t) = (1-t)^2 * P0 + 2t(1-t) * P1 + t^2 * P2
01168          *
01169          * r(t) = (P2 - 2*P1 + P0) * t^2 + (2*P1 - 2*P0) * t + P0
01170          *
01171          */
01172         // calcualting the differences between successive control points
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         // calculating the coefficents
01178         QPointF a = x2 - 2.0 * x1 + x0;
01179         QPointF b = 2.0 * x1 - 2.0 * x0;
01180         QPointF c = x0;
01181 
01182         // calculating parameter t at minimum/maximum in x-direction
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         // calculating parameter t at minimum/maximum in y-direction
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     // use shortcut for line segments
01216     if (deg == 1) {
01217         // the segments chord
01218         QPointF chord = d->second->point() - d->first->point();
01219         // the point relative to the segment
01220         QPointF relPoint = point - d->first->point();
01221         // project point to chord (dot product)
01222         qreal scale = chord.x() * relPoint.x() + chord.y() * relPoint.y();
01223         // normalize using the chord length
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     /* This function solves the "nearest point on curve" problem. That means, it
01236     * calculates the point q (to be precise: it's parameter t) on this segment, which
01237     * is located nearest to the input point P.
01238     * The basic idea is best described (because it is freely available) in "Phoenix:
01239     * An Interactive Curve Design System Based on the Automatic Fitting of
01240     * Hand-Sketched Curves", Philip J. Schneider (Master thesis, University of
01241     * Washington).
01242     *
01243     * For the nearest point q = C(t) on this segment, the first derivative is
01244     * orthogonal to the distance vector "C(t) - P". In other words we are looking for
01245     * solutions of f(t) = ( C(t) - P ) * C'(t) = 0.
01246     * ( C(t) - P ) is a nth degree curve, C'(t) a n-1th degree curve => f(t) is a
01247     * (2n - 1)th degree curve and thus has up to 2n - 1 distinct solutions.
01248     * We solve the problem f(t) = 0 by using something called "Approximate Inversion Method".
01249     * Let's write f(t) explicitly (with c_i = p_i - P and d_j = p_{j+1} - p_j):
01250     *
01251     *         n                     n-1
01252     * f(t) = SUM c_i * B^n_i(t)  *  SUM d_j * B^{n-1}_j(t)
01253     *        i=0                    j=0
01254     *
01255     *         n  n-1
01256     *      = SUM SUM w_{ij} * B^{2n-1}_{i+j}(t)
01257     *        i=0 j=0
01258     *
01259     * with w_{ij} = c_i * d_j * z_{ij} and
01260     *
01261     *          BinomialCoeff( n, i ) * BinomialCoeff( n - i ,j )
01262     * z_{ij} = -----------------------------------------------
01263     *                   BinomialCoeff( 2n - 1, i + j )
01264     *
01265     * This Bernstein-Bezier polynom representation can now be solved for it's roots.
01266     */
01267 
01268     QList<QPointF> ctlPoints = controlPoints();
01269 
01270     // Calculate the c_i = point( i ) - P.
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     // Calculate the d_j = point( j + 1 ) - point( j ).
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     // Calculate the dot products of c_i and d_i.
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     // We don't need the c_i and d_i anymore.
01294     delete[]( d_j );
01295     delete[]( c_i );
01296 
01297     // Calculate the control points of the new 2n-1th degree curve.
01298     BezierSegment newCurve;
01299     newCurve.setDegree( 2 * deg - 1 );
01300     // Set up control points in the ( u, f(u) )-plane.
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     // Precomputed "z" for cubics
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     // Precomputed "z" for quadrics
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     // Set f(u)-values.
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             // p_k += products[j][i] * z[j][i].
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     // We don't need the c_i/d_i dot products and the z_{ij} anymore.
01327     delete[]( products );
01328 
01329     // Find roots.
01330     QList<qreal> rootParams = newCurve.roots();
01331 
01332     // Now compare the distances of the candidate points.
01333 
01334     // First candidate is the previous knot.
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     // Iterate over the found candidate params.
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     // Last candidate is the knot.
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     // Calculate how often the control polygon crosses the x-axis
01369     // This is the upper limit for the number of roots.
01370     int xAxisCrossings = BezierSegment::controlPolygonZeros( controlPoints() );
01371 
01372     if (! xAxisCrossings) {
01373         // No solutions.
01374     }
01375     else if (xAxisCrossings == 1 && isFlat( 0.01 / chordLength() )) {
01376         // Exactly one solution.
01377         // Calculate intersection of chord with x-axis.
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         // Many solutions. Do recursive midpoint subdivision.
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         B(t) = [x2 y2] = (1-t)^2*P0 + 2t*(1-t)*P1 + t^2*P2
01399 
01400                B(t) - (1-t)^2*P0 - t^2*P2
01401          P1 =  --------------------------
01402                        2t*(1-t)
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 }