File indexing completed on 2024-05-12 15:56:43

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2008-2009 Jan Hambrecht <jaham@gmx.net>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "KoPathSegment.h"
0008 #include "KoPathPoint.h"
0009 #include <FlakeDebug.h>
0010 #include <QPainterPath>
0011 #include <QTransform>
0012 #include <math.h>
0013 
0014 #include "kis_global.h"
0015 
0016 #include <KisBezierUtils.h>
0017 
0018 class Q_DECL_HIDDEN KoPathSegment::Private
0019 {
0020 public:
0021     Private(KoPathSegment *qq, KoPathPoint *p1, KoPathPoint *p2)
0022         : first(p1),
0023         second(p2),
0024         q(qq)
0025     {
0026     }
0027 
0028     /// calculates signed distance of given point from segment chord
0029     qreal distanceFromChord(const QPointF &point) const;
0030 
0031     /// Returns the chord length, i.e. the distance between first and last control point
0032     qreal chordLength() const;
0033 
0034     /// Returns intersection of lines if one exists
0035     QList<QPointF> linesIntersection(const KoPathSegment &segment) const;
0036 
0037     /// Returns parameters for curve extrema
0038     QList<qreal> extrema() const;
0039 
0040     /// Returns parameters for curve roots
0041     QList<qreal> roots() const;
0042 
0043     /**
0044      * The DeCasteljau algorithm for parameter t.
0045      * @param t the parameter to evaluate at
0046      * @param p1 the new control point of the segment start (for cubic curves only)
0047      * @param p2 the first control point at t
0048      * @param p3 the new point at t
0049      * @param p4 the second control point at t
0050      * @param p3 the new control point of the segment end (for cubic curbes only)
0051      */
0052     void deCasteljau(qreal t, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4, QPointF *p5) const;
0053 
0054     KoPathPoint *first;
0055     KoPathPoint *second;
0056     KoPathSegment *q;
0057 };
0058 
0059 void KoPathSegment::Private::deCasteljau(qreal t, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4, QPointF *p5) const
0060 {
0061     if (!q->isValid())
0062       return;
0063 
0064     int deg = q->degree();
0065     QPointF q[4];
0066 
0067     q[0] = first->point();
0068     if (deg == 2) {
0069         q[1] = first->activeControlPoint2() ? first->controlPoint2() : second->controlPoint1();
0070     } else if (deg == 3) {
0071         q[1] = first->controlPoint2();
0072         q[2] = second->controlPoint1();
0073     }
0074     if (deg >= 0) {
0075         q[deg] = second->point();
0076     }
0077 
0078     // points of the new segment after the split point
0079     QPointF p[3];
0080 
0081     // the De Casteljau algorithm
0082     for (unsigned short j = 1; j <= deg; ++j) {
0083         for (unsigned short i = 0; i <= deg - j; ++i) {
0084             q[i] = (1.0 - t) * q[i] + t * q[i + 1];
0085         }
0086         p[j - 1] = q[0];
0087     }
0088 
0089     if (deg == 2) {
0090         if (p2)
0091             *p2 = p[0];
0092         if (p3)
0093             *p3 = p[1];
0094         if (p4)
0095             *p4 = q[1];
0096     } else if (deg == 3) {
0097         if (p1)
0098             *p1 = p[0];
0099         if (p2)
0100             *p2 = p[1];
0101         if (p3)
0102             *p3 = p[2];
0103         if (p4)
0104             *p4 = q[1];
0105         if (p5)
0106             *p5 = q[2];
0107     }
0108 }
0109 
0110 QList<qreal> KoPathSegment::Private::roots() const
0111 {
0112     QList<qreal> rootParams;
0113 
0114     if (!q->isValid())
0115         return rootParams;
0116 
0117     // Calculate how often the control polygon crosses the x-axis
0118     // This is the upper limit for the number of roots.
0119     int xAxisCrossings = KisBezierUtils::controlPolygonZeros(q->controlPoints());
0120 
0121     if (!xAxisCrossings) {
0122         // No solutions.
0123     }
0124     else if (xAxisCrossings == 1 && q->isFlat(0.01 / chordLength())) {
0125         // Exactly one solution.
0126         // Calculate intersection of chord with x-axis.
0127         QPointF chord = second->point() - first->point();
0128         QPointF segStart = first->point();
0129         rootParams.append((chord.x() * segStart.y() - chord.y() * segStart.x()) / - chord.y());
0130     }
0131     else {
0132         // Many solutions. Do recursive midpoint subdivision.
0133         QPair<KoPathSegment, KoPathSegment> splitSegments = q->splitAt(0.5);
0134         rootParams += splitSegments.first.d->roots();
0135         rootParams += splitSegments.second.d->roots();
0136     }
0137 
0138     return rootParams;
0139 }
0140 
0141 QList<qreal> KoPathSegment::Private::extrema() const
0142 {
0143     int deg = q->degree();
0144     if (deg <= 1)
0145         return QList<qreal>();
0146 
0147     QList<qreal> params;
0148 
0149     /*
0150      * The basic idea for calculating the extrama for bezier segments
0151      * was found in comp.graphics.algorithms:
0152      *
0153      * Both the x coordinate and the y coordinate are polynomial. Newton told
0154      * us that at a maximum or minimum the derivative will be zero.
0155      *
0156      * We have a helpful trick for the derivatives: use the curve r(t) defined by
0157      * differences of successive control points.
0158      * Setting r(t) to zero and using the x and y coordinates of differences of
0159      * successive control points lets us find the parameters t, where the original
0160      * bezier curve has a minimum or a maximum.
0161      */
0162     if (deg == 2) {
0163         /*
0164          * For quadratic bezier curves r(t) is a linear Bezier curve:
0165          *
0166          *  1
0167          * r(t) = Sum Bi,1(t) *Pi = B0,1(t) * P0 + B1,1(t) * P1
0168          * i=0
0169          *
0170          * r(t) = (1-t) * P0 + t * P1
0171          *
0172          * r(t) = (P1 - P0) * t + P0
0173          */
0174 
0175         // calculating the differences between successive control points
0176         QPointF cp = first->activeControlPoint2() ?
0177                      first->controlPoint2() : second->controlPoint1();
0178         QPointF x0 = cp - first->point();
0179         QPointF x1 = second->point() - cp;
0180 
0181         // calculating the coefficients
0182         QPointF a = x1 - x0;
0183         QPointF c = x0;
0184 
0185         if (a.x() != 0.0)
0186             params.append(-c.x() / a.x());
0187         if (a.y() != 0.0)
0188             params.append(-c.y() / a.y());
0189     } else if (deg == 3) {
0190         /*
0191          * For cubic bezier curves r(t) is a quadratic Bezier curve:
0192          *
0193          *  2
0194          * r(t) = Sum Bi,2(t) *Pi = B0,2(t) * P0 + B1,2(t) * P1 + B2,2(t) * P2
0195          * i=0
0196          *
0197          * r(t) = (1-t)^2 * P0 + 2t(1-t) * P1 + t^2 * P2
0198          *
0199          * r(t) = (P2 - 2*P1 + P0) * t^2 + (2*P1 - 2*P0) * t + P0
0200          *
0201          */
0202         // calculating the differences between successive control points
0203         QPointF x0 = first->controlPoint2() - first->point();
0204         QPointF x1 = second->controlPoint1() - first->controlPoint2();
0205         QPointF x2 = second->point() - second->controlPoint1();
0206 
0207         // calculating the coefficients
0208         QPointF a = x2 - 2.0 * x1 + x0;
0209         QPointF b = 2.0 * x1 - 2.0 * x0;
0210         QPointF c = x0;
0211 
0212         // calculating parameter t at minimum/maximum in x-direction
0213         if (a.x() == 0.0) {
0214             params.append(- c.x() / b.x());
0215         } else {
0216             qreal rx = b.x() * b.x() - 4.0 * a.x() * c.x();
0217             if (rx < 0.0)
0218                 rx = 0.0;
0219             params.append((-b.x() + sqrt(rx)) / (2.0*a.x()));
0220             params.append((-b.x() - sqrt(rx)) / (2.0*a.x()));
0221         }
0222 
0223         // calculating parameter t at minimum/maximum in y-direction
0224         if (a.y() == 0.0) {
0225             params.append(- c.y() / b.y());
0226         } else {
0227             qreal ry = b.y() * b.y() - 4.0 * a.y() * c.y();
0228             if (ry < 0.0)
0229                 ry = 0.0;
0230             params.append((-b.y() + sqrt(ry)) / (2.0*a.y()));
0231             params.append((-b.y() - sqrt(ry)) / (2.0*a.y()));
0232         }
0233     }
0234 
0235     return params;
0236 }
0237 
0238 qreal KoPathSegment::Private::distanceFromChord(const QPointF &point) const
0239 {
0240     // the segments chord
0241     QPointF chord = second->point() - first->point();
0242     // the point relative to the segment
0243     QPointF relPoint = point - first->point();
0244     // project point to chord
0245     qreal scale = chord.x() * relPoint.x() + chord.y() * relPoint.y();
0246     scale /= chord.x() * chord.x() + chord.y() * chord.y();
0247 
0248     // the vector form the point to the projected point on the chord
0249     QPointF diffVec = scale * chord - relPoint;
0250 
0251     // the unsigned distance of the point to the chord
0252     qreal distance = sqrt(diffVec.x() * diffVec.x() + diffVec.y() * diffVec.y());
0253 
0254     // determine sign of the distance using the cross product
0255     if (chord.x()*relPoint.y() - chord.y()*relPoint.x() > 0) {
0256         return distance;
0257     } else {
0258         return -distance;
0259     }
0260 }
0261 
0262 qreal KoPathSegment::Private::chordLength() const
0263 {
0264     QPointF chord = second->point() - first->point();
0265     return sqrt(chord.x() * chord.x() + chord.y() * chord.y());
0266 }
0267 
0268 QList<QPointF> KoPathSegment::Private::linesIntersection(const KoPathSegment &segment) const
0269 {
0270     //debugFlake << "intersecting two lines";
0271     /*
0272     we have to line segments:
0273 
0274     s1 = A + r * (B-A), s2 = C + s * (D-C) for r,s in [0,1]
0275 
0276         if s1 and s2 intersect we set s1 = s2 so we get these two equations:
0277 
0278     Ax + r * (Bx-Ax) = Cx + s * (Dx-Cx)
0279     Ay + r * (By-Ay) = Cy + s * (Dy-Cy)
0280 
0281     which we can solve to get r and s
0282     */
0283     QList<QPointF> isects;
0284     QPointF A = first->point();
0285     QPointF B = second->point();
0286     QPointF C = segment.first()->point();
0287     QPointF D = segment.second()->point();
0288 
0289     qreal denom = (B.x() - A.x()) * (D.y() - C.y()) - (B.y() - A.y()) * (D.x() - C.x());
0290     qreal num_r = (A.y() - C.y()) * (D.x() - C.x()) - (A.x() - C.x()) * (D.y() - C.y());
0291     // check if lines are collinear
0292     if (denom == 0.0 && num_r == 0.0)
0293         return isects;
0294 
0295     qreal num_s = (A.y() - C.y()) * (B.x() - A.x()) - (A.x() - C.x()) * (B.y() - A.y());
0296     qreal r = num_r / denom;
0297     qreal s = num_s / denom;
0298 
0299     // check if intersection is inside our line segments
0300     if (r < 0.0 || r > 1.0)
0301         return isects;
0302     if (s < 0.0 || s > 1.0)
0303         return isects;
0304 
0305     // calculate the actual intersection point
0306     isects.append(A + r * (B - A));
0307 
0308     return isects;
0309 }
0310 
0311 
0312 ///////////////////
0313 KoPathSegment::KoPathSegment(KoPathPoint * first, KoPathPoint * second)
0314     : d(new Private(this, first, second))
0315 {
0316 }
0317 
0318 KoPathSegment::KoPathSegment(const KoPathSegment & segment)
0319     : d(new Private(this, 0, 0))
0320 {
0321     if (! segment.first() || segment.first()->parent())
0322         setFirst(segment.first());
0323     else
0324         setFirst(new KoPathPoint(*segment.first()));
0325 
0326     if (! segment.second() || segment.second()->parent())
0327         setSecond(segment.second());
0328     else
0329         setSecond(new KoPathPoint(*segment.second()));
0330 }
0331 
0332 KoPathSegment::KoPathSegment(const QPointF &p0, const QPointF &p1)
0333     : d(new Private(this, new KoPathPoint(), new KoPathPoint()))
0334 {
0335     d->first->setPoint(p0);
0336     d->second->setPoint(p1);
0337 }
0338 
0339 KoPathSegment::KoPathSegment(const QPointF &p0, const QPointF &p1, const QPointF &p2)
0340     : d(new Private(this, new KoPathPoint(), new KoPathPoint()))
0341 {
0342     d->first->setPoint(p0);
0343     d->first->setControlPoint2(p1);
0344     d->second->setPoint(p2);
0345 }
0346 
0347 KoPathSegment::KoPathSegment(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3)
0348     : d(new Private(this, new KoPathPoint(), new KoPathPoint()))
0349 {
0350     d->first->setPoint(p0);
0351     d->first->setControlPoint2(p1);
0352     d->second->setControlPoint1(p2);
0353     d->second->setPoint(p3);
0354 }
0355 
0356 KoPathSegment &KoPathSegment::operator=(const KoPathSegment &rhs)
0357 {
0358     if (this == &rhs)
0359         return (*this);
0360 
0361     if (! rhs.first() || rhs.first()->parent())
0362         setFirst(rhs.first());
0363     else
0364         setFirst(new KoPathPoint(*rhs.first()));
0365 
0366     if (! rhs.second() || rhs.second()->parent())
0367         setSecond(rhs.second());
0368     else
0369         setSecond(new KoPathPoint(*rhs.second()));
0370 
0371     return (*this);
0372 }
0373 
0374 KoPathSegment::~KoPathSegment()
0375 {
0376     if (d->first && ! d->first->parent())
0377         delete d->first;
0378     if (d->second && ! d->second->parent())
0379         delete d->second;
0380     delete d;
0381 }
0382 
0383 KoPathPoint *KoPathSegment::first() const
0384 {
0385     return d->first;
0386 }
0387 
0388 void KoPathSegment::setFirst(KoPathPoint *first)
0389 {
0390     if (d->first && !d->first->parent())
0391         delete d->first;
0392     d->first = first;
0393 }
0394 
0395 KoPathPoint *KoPathSegment::second() const
0396 {
0397     return d->second;
0398 }
0399 
0400 void KoPathSegment::setSecond(KoPathPoint *second)
0401 {
0402     if (d->second && !d->second->parent())
0403         delete d->second;
0404     d->second = second;
0405 }
0406 
0407 bool KoPathSegment::isValid() const
0408 {
0409     return (d->first && d->second);
0410 }
0411 
0412 bool KoPathSegment::operator==(const KoPathSegment &rhs) const
0413 {
0414     if (!isValid() && !rhs.isValid())
0415         return true;
0416     if (isValid() && !rhs.isValid())
0417         return false;
0418     if (!isValid() && rhs.isValid())
0419         return false;
0420 
0421     return (*first() == *rhs.first() && *second() == *rhs.second());
0422 }
0423 
0424 int KoPathSegment::degree() const
0425 {
0426     if (!d->first || !d->second)
0427         return -1;
0428 
0429     bool c1 = d->first->activeControlPoint2();
0430     bool c2 = d->second->activeControlPoint1();
0431     if (!c1 && !c2)
0432         return 1;
0433     if (c1 && c2)
0434         return 3;
0435     return 2;
0436 }
0437 
0438 QPointF KoPathSegment::pointAt(qreal t) const
0439 {
0440     if (!isValid())
0441         return QPointF();
0442 
0443     if (degree() == 1) {
0444         return d->first->point() + t * (d->second->point() - d->first->point());
0445     } else {
0446         QPointF splitP;
0447 
0448         d->deCasteljau(t, 0, 0, &splitP, 0, 0);
0449 
0450         return splitP;
0451     }
0452 }
0453 
0454 QRectF KoPathSegment::controlPointRect() const
0455 {
0456     if (!isValid())
0457         return QRectF();
0458 
0459     QList<QPointF> points = controlPoints();
0460     QRectF bbox(points.first(), points.first());
0461     Q_FOREACH (const QPointF &p, points) {
0462         bbox.setLeft(qMin(bbox.left(), p.x()));
0463         bbox.setRight(qMax(bbox.right(), p.x()));
0464         bbox.setTop(qMin(bbox.top(), p.y()));
0465         bbox.setBottom(qMax(bbox.bottom(), p.y()));
0466     }
0467 
0468     if (degree() == 1) {
0469         // adjust bounding rect of horizontal and vertical lines
0470         if (bbox.height() == 0.0)
0471             bbox.setHeight(0.1);
0472         if (bbox.width() == 0.0)
0473             bbox.setWidth(0.1);
0474     }
0475 
0476     return bbox;
0477 }
0478 
0479 QRectF KoPathSegment::boundingRect() const
0480 {
0481     if (!isValid())
0482         return QRectF();
0483 
0484     QRectF rect = QRectF(d->first->point(), d->second->point()).normalized();
0485 
0486     if (degree() == 1) {
0487         // adjust bounding rect of horizontal and vertical lines
0488         if (rect.height() == 0.0)
0489             rect.setHeight(0.1);
0490         if (rect.width() == 0.0)
0491             rect.setWidth(0.1);
0492     } else {
0493         /*
0494          * The basic idea for calculating the axis aligned bounding box (AABB) for bezier segments
0495          * was found in comp.graphics.algorithms:
0496          * Use the points at the extrema of the curve to calculate the AABB.
0497          */
0498         foreach (qreal t, d->extrema()) {
0499             if (t >= 0.0 && t <= 1.0) {
0500                 QPointF p = pointAt(t);
0501                 rect.setLeft(qMin(rect.left(), p.x()));
0502                 rect.setRight(qMax(rect.right(), p.x()));
0503                 rect.setTop(qMin(rect.top(), p.y()));
0504                 rect.setBottom(qMax(rect.bottom(), p.y()));
0505             }
0506         }
0507     }
0508 
0509     return rect;
0510 }
0511 
0512 QList<QPointF> KoPathSegment::intersections(const KoPathSegment &segment) const
0513 {
0514     // this function uses a technique known as bezier clipping to find the
0515     // intersections of the two bezier curves
0516 
0517     QList<QPointF> isects;
0518 
0519     if (!isValid() || !segment.isValid())
0520         return isects;
0521 
0522     int degree1 = degree();
0523     int degree2 = segment.degree();
0524 
0525     QRectF myBound = boundingRect();
0526     QRectF otherBound = segment.boundingRect();
0527     //debugFlake << "my boundingRect =" << myBound;
0528     //debugFlake << "other boundingRect =" << otherBound;
0529     if (!myBound.intersects(otherBound)) {
0530         //debugFlake << "segments do not intersect";
0531         return isects;
0532     }
0533 
0534     // short circuit lines intersection
0535     if (degree1 == 1 && degree2 == 1) {
0536         //debugFlake << "intersecting two lines";
0537         isects += d->linesIntersection(segment);
0538         return isects;
0539     }
0540 
0541     // first calculate the fat line L by using the signed distances
0542     // of the control points from the chord
0543     qreal dmin, dmax;
0544     if (degree1 == 1) {
0545         dmin = 0.0;
0546         dmax = 0.0;
0547     } else if (degree1 == 2) {
0548         qreal d1;
0549         if (d->first->activeControlPoint2())
0550             d1 = d->distanceFromChord(d->first->controlPoint2());
0551         else
0552             d1 = d->distanceFromChord(d->second->controlPoint1());
0553         dmin = qMin(qreal(0.0), qreal(0.5 * d1));
0554         dmax = qMax(qreal(0.0), qreal(0.5 * d1));
0555     } else {
0556         qreal d1 = d->distanceFromChord(d->first->controlPoint2());
0557         qreal d2 = d->distanceFromChord(d->second->controlPoint1());
0558         if (d1*d2 > 0.0) {
0559             dmin = 0.75 * qMin(qreal(0.0), qMin(d1, d2));
0560             dmax = 0.75 * qMax(qreal(0.0), qMax(d1, d2));
0561         } else {
0562             dmin = 4.0 / 9.0 * qMin(qreal(0.0), qMin(d1, d2));
0563             dmax = 4.0 / 9.0 * qMax(qreal(0.0), qMax(d1, d2));
0564         }
0565     }
0566 
0567     //debugFlake << "using fat line: dmax =" << dmax << " dmin =" << dmin;
0568 
0569     /*
0570       the other segment is given as a bezier curve of the form:
0571      (1) P(t) = sum_i P_i * B_{n,i}(t)
0572      our chord line is of the form:
0573      (2) ax + by + c = 0
0574      we can determine the distance d(t) from any point P(t) to our chord
0575      by substituting formula (1) into formula (2):
0576      d(t) = sum_i d_i B_{n,i}(t), where d_i = a*x_i + b*y_i + c
0577      which forms another explicit bezier curve
0578      D(t) = (t,d(t)) = sum_i D_i B_{n,i}(t)
0579      now values of t for which P(t) lies outside of our fat line L
0580      corresponds to values of t for which D(t) lies above d = dmax or
0581      below d = dmin
0582      we can determine parameter ranges of t for which P(t) is guaranteed
0583      to lie outside of L by identifying ranges of t which the convex hull
0584      of D(t) lies above dmax or below dmin
0585     */
0586     // now calculate the control points of D(t) by using the signed
0587     // distances of P_i to our chord
0588     KoPathSegment dt;
0589     if (degree2 == 1) {
0590         QPointF p0(0.0, d->distanceFromChord(segment.first()->point()));
0591         QPointF p1(1.0, d->distanceFromChord(segment.second()->point()));
0592         dt = KoPathSegment(p0, p1);
0593     } else if (degree2 == 2) {
0594         QPointF p0(0.0, d->distanceFromChord(segment.first()->point()));
0595         QPointF p1 = segment.first()->activeControlPoint2()
0596                      ? QPointF(0.5, d->distanceFromChord(segment.first()->controlPoint2()))
0597                      : QPointF(0.5, d->distanceFromChord(segment.second()->controlPoint1()));
0598         QPointF p2(1.0, d->distanceFromChord(segment.second()->point()));
0599         dt = KoPathSegment(p0, p1, p2);
0600     } else if (degree2 == 3) {
0601         QPointF p0(0.0, d->distanceFromChord(segment.first()->point()));
0602         QPointF p1(1. / 3., d->distanceFromChord(segment.first()->controlPoint2()));
0603         QPointF p2(2. / 3., d->distanceFromChord(segment.second()->controlPoint1()));
0604         QPointF p3(1.0, d->distanceFromChord(segment.second()->point()));
0605         dt = KoPathSegment(p0, p1, p2, p3);
0606     } else {
0607         //debugFlake << "invalid degree of segment -> exiting";
0608         return isects;
0609     }
0610 
0611     // get convex hull of the segment D(t)
0612     QList<QPointF> hull = dt.convexHull();
0613 
0614     // now calculate intersections with the line y1 = dmin, y2 = dmax
0615     // with the convex hull edges
0616     int hullCount = hull.count();
0617     qreal tmin = 1.0, tmax = 0.0;
0618     bool intersectionsFoundMax = false;
0619     bool intersectionsFoundMin = false;
0620 
0621     for (int i = 0; i < hullCount; ++i) {
0622         QPointF p1 = hull[i];
0623         QPointF p2 = hull[(i+1) % hullCount];
0624         //debugFlake << "intersecting hull edge (" << p1 << p2 << ")";
0625         // hull edge is completely above dmax
0626         if (p1.y() > dmax && p2.y() > dmax)
0627             continue;
0628         // hull edge is completely below dmin
0629         if (p1.y() < dmin && p2.y() < dmin)
0630             continue;
0631         if (p1.x() == p2.x()) {
0632             // vertical edge
0633             bool dmaxIntersection = (dmax < qMax(p1.y(), p2.y()) && dmax > qMin(p1.y(), p2.y()));
0634             bool dminIntersection = (dmin < qMax(p1.y(), p2.y()) && dmin > qMin(p1.y(), p2.y()));
0635             if (dmaxIntersection || dminIntersection) {
0636                 tmin = qMin(tmin, p1.x());
0637                 tmax = qMax(tmax, p1.x());
0638                 if (dmaxIntersection) {
0639                     intersectionsFoundMax = true;
0640                     //debugFlake << "found intersection with dmax at " << p1.x() << "," << dmax;
0641                 } else {
0642                     intersectionsFoundMin = true;
0643                     //debugFlake << "found intersection with dmin at " << p1.x() << "," << dmin;
0644                 }
0645             }
0646         } else if (p1.y() == p2.y()) {
0647             // horizontal line
0648             if (p1.y() == dmin || p1.y() == dmax) {
0649                 if (p1.y() == dmin) {
0650                     intersectionsFoundMin = true;
0651                     //debugFlake << "found intersection with dmin at " << p1.x() << "," << dmin;
0652                     //debugFlake << "found intersection with dmin at " << p2.x() << "," << dmin;
0653                 } else {
0654                     intersectionsFoundMax = true;
0655                     //debugFlake << "found intersection with dmax at " << p1.x() << "," << dmax;
0656                     //debugFlake << "found intersection with dmax at " << p2.x() << "," << dmax;
0657                 }
0658                 tmin = qMin(tmin, p1.x());
0659                 tmin = qMin(tmin, p2.x());
0660                 tmax = qMax(tmax, p1.x());
0661                 tmax = qMax(tmax, p2.x());
0662             }
0663         } else {
0664             qreal dx = p2.x() - p1.x();
0665             qreal dy = p2.y() - p1.y();
0666             qreal m = dy / dx;
0667             qreal n = p1.y() - m * p1.x();
0668             qreal t1 = (dmax - n) / m;
0669             if (t1 >= 0.0 && t1 <= 1.0) {
0670                 tmin = qMin(tmin, t1);
0671                 tmax = qMax(tmax, t1);
0672                 intersectionsFoundMax = true;
0673                 //debugFlake << "found intersection with dmax at " << t1 << "," << dmax;
0674             }
0675             qreal t2 = (dmin - n) / m;
0676             if (t2 >= 0.0 && t2 < 1.0) {
0677                 tmin = qMin(tmin, t2);
0678                 tmax = qMax(tmax, t2);
0679                 intersectionsFoundMin = true;
0680                 //debugFlake << "found intersection with dmin at " << t2 << "," << dmin;
0681             }
0682         }
0683     }
0684 
0685     bool intersectionsFound = intersectionsFoundMin && intersectionsFoundMax;
0686 
0687     //if (intersectionsFound)
0688     //    debugFlake << "clipping segment to interval [" << tmin << "," << tmax << "]";
0689 
0690     if (!intersectionsFound || (1.0 - (tmax - tmin)) <= 0.2) {
0691         //debugFlake << "could not clip enough -> split segment";
0692         // we could not reduce the interval significantly
0693         // so split the curve and calculate intersections
0694         // with the remaining parts
0695         QPair<KoPathSegment, KoPathSegment> parts = splitAt(0.5);
0696         if (d->chordLength() < 1e-5)
0697             isects += parts.first.second()->point();
0698         else {
0699             isects += segment.intersections(parts.first);
0700             isects += segment.intersections(parts.second);
0701         }
0702     } else if (qAbs(tmin - tmax) < 1e-5) {
0703         //debugFlake << "Yay, we found an intersection";
0704         // the interval is pretty small now, just calculate the intersection at this point
0705         isects.append(segment.pointAt(tmin));
0706     } else {
0707         QPair<KoPathSegment, KoPathSegment> clip1 = segment.splitAt(tmin);
0708         //debugFlake << "splitting segment at" << tmin;
0709         qreal t = (tmax - tmin) / (1.0 - tmin);
0710         QPair<KoPathSegment, KoPathSegment> clip2 = clip1.second.splitAt(t);
0711         //debugFlake << "splitting second part at" << t << "("<<tmax<<")";
0712         isects += clip2.first.intersections(*this);
0713     }
0714 
0715     return isects;
0716 }
0717 
0718 KoPathSegment KoPathSegment::mapped(const QTransform &matrix) const
0719 {
0720     if (!isValid())
0721         return *this;
0722 
0723     KoPathPoint * p1 = new KoPathPoint(*d->first);
0724     KoPathPoint * p2 = new KoPathPoint(*d->second);
0725     p1->map(matrix);
0726     p2->map(matrix);
0727 
0728     return KoPathSegment(p1, p2);
0729 }
0730 
0731 KoPathSegment KoPathSegment::toCubic() const
0732 {
0733     if (! isValid())
0734         return KoPathSegment();
0735 
0736     KoPathPoint * p1 = new KoPathPoint(*d->first);
0737     KoPathPoint * p2 = new KoPathPoint(*d->second);
0738 
0739     if (degree() == 1) {
0740         p1->setControlPoint2(p1->point() + 0.3 * (p2->point() - p1->point()));
0741         p2->setControlPoint1(p2->point() + 0.3 * (p1->point() - p2->point()));
0742     } else if (degree() == 2) {
0743         /* quadric bezier (a0,a1,a2) to cubic bezier (b0,b1,b2,b3):
0744         *
0745         * b0 = a0
0746         * b1 = a0 + 2/3 * (a1-a0)
0747         * b2 = a1 + 1/3 * (a2-a1)
0748         * b3 = a2
0749         */
0750         QPointF a1 = p1->activeControlPoint2() ? p1->controlPoint2() : p2->controlPoint1();
0751         QPointF b1 = p1->point() + 2.0 / 3.0 * (a1 - p1->point());
0752         QPointF b2 = a1 + 1.0 / 3.0 * (p2->point() - a1);
0753         p1->setControlPoint2(b1);
0754         p2->setControlPoint1(b2);
0755     }
0756 
0757     return KoPathSegment(p1, p2);
0758 }
0759 
0760 qreal KoPathSegment::length(qreal error) const
0761 {
0762     /*
0763      * This algorithm is implemented based on an idea by Jens Gravesen:
0764      * "Adaptive subdivision and the length of Bezier curves" mat-report no. 1992-10, Mathematical Institute,
0765      * The Technical University of Denmark.
0766      *
0767      * By subdividing the curve at parameter value t you only have to find the length of a full Bezier curve.
0768      * If you denote the length of the control polygon by L1 i.e.:
0769      *   L1 = |P0 P1| +|P1 P2| +|P2 P3|
0770      *
0771      * and the length of the cord by L0 i.e.:
0772      *   L0 = |P0 P3|
0773      *
0774      * then
0775      *   L = 1/2*L0 + 1/2*L1
0776      *
0777      * is a good approximation to the length of the curve, and the difference
0778      *   ERR = L1-L0
0779      *
0780      * is a measure of the error. If the error is to large, then you just subdivide curve at parameter value
0781      * 1/2, and find the length of each half.
0782      * If m is the number of subdivisions then the error goes to zero as 2^-4m.
0783      * If you don't have a cubic curve but a curve of degree n then you put
0784      *   L = (2*L0 + (n-1)*L1)/(n+1)
0785      */
0786 
0787     int deg = degree();
0788 
0789     if (deg == -1)
0790         return 0.0;
0791 
0792     QList<QPointF> ctrlPoints = controlPoints();
0793 
0794     // calculate chord length
0795     qreal chordLen = d->chordLength();
0796 
0797     if (deg == 1) {
0798         return chordLen;
0799     }
0800 
0801     // calculate length of control polygon
0802     qreal polyLength = 0.0;
0803 
0804     for (int i = 0; i < deg; ++i) {
0805         QPointF ctrlSegment = ctrlPoints[i+1] - ctrlPoints[i];
0806         polyLength += sqrt(ctrlSegment.x() * ctrlSegment.x() + ctrlSegment.y() * ctrlSegment.y());
0807     }
0808 
0809     if ((polyLength - chordLen) > error) {
0810         // the error is still bigger than our tolerance -> split segment
0811         QPair<KoPathSegment, KoPathSegment> parts = splitAt(0.5);
0812         return parts.first.length(error) + parts.second.length(error);
0813     } else {
0814         // the error is smaller than our tolerance
0815         if (deg == 3)
0816             return 0.5 * chordLen + 0.5 * polyLength;
0817         else
0818             return (2.0 * chordLen + polyLength) / 3.0;
0819     }
0820 }
0821 
0822 qreal KoPathSegment::lengthAt(qreal t, qreal error) const
0823 {
0824     if (t == 0.0)
0825         return 0.0;
0826     if (t == 1.0)
0827         return length(error);
0828 
0829     QPair<KoPathSegment, KoPathSegment> parts = splitAt(t);
0830     return parts.first.length(error);
0831 }
0832 
0833 qreal KoPathSegment::paramAtLength(qreal length, qreal tolerance) const
0834 {
0835     const int deg = degree();
0836     // invalid degree or invalid specified length
0837     if (deg < 1 || length <= 0.0) {
0838         return 0.0;
0839     }
0840 
0841     if (deg == 1) {
0842         // make sure we return a maximum value of 1.0
0843         return qMin(qreal(1.0), length / d->chordLength());
0844     }
0845 
0846     // for curves we need to make sure, that the specified length
0847     // value does not exceed the actual length of the segment
0848     // if that happens, we return 1.0 to avoid infinite looping
0849     if (length >= d->chordLength() && length >= this->length(tolerance)) {
0850         return 1.0;
0851     }
0852 
0853     qreal startT = 0.0; // interval start
0854     qreal midT = 0.5;   // interval center
0855     qreal endT = 1.0;   // interval end
0856 
0857     // divide and conquer, split a midpoint and check
0858     // on which side of the midpoint to continue
0859     qreal midLength = lengthAt(0.5);
0860     while (qAbs(midLength - length) / length > tolerance) {
0861         if (midLength < length)
0862             startT = midT;
0863         else
0864             endT = midT;
0865 
0866         // new interval center
0867         midT = 0.5 * (startT + endT);
0868         // length at new interval center
0869         midLength = lengthAt(midT);
0870     }
0871 
0872     return midT;
0873 }
0874 
0875 bool KoPathSegment::isFlat(qreal tolerance) const
0876 {
0877     /*
0878      * Calculate the height of the bezier curve.
0879      * This is done by rotating the curve so that then chord
0880      * is parallel to the x-axis and the calculating the
0881      * parameters t for the extrema of the curve.
0882      * The curve points at the extrema are then used to
0883      * calculate the height.
0884      */
0885     if (degree() <= 1)
0886         return true;
0887 
0888     QPointF chord = d->second->point() - d->first->point();
0889     // calculate angle of chord to the x-axis
0890     qreal chordAngle = atan2(chord.y(), chord.x());
0891     QTransform m;
0892     m.translate(d->first->point().x(), d->first->point().y());
0893     m.rotate(chordAngle * M_PI / 180.0);
0894     m.translate(-d->first->point().x(), -d->first->point().y());
0895 
0896     KoPathSegment s = mapped(m);
0897 
0898     qreal minDist = 0.0;
0899     qreal maxDist = 0.0;
0900 
0901     foreach (qreal t, s.d->extrema()) {
0902         if (t >= 0.0 && t <= 1.0) {
0903             QPointF p = pointAt(t);
0904             qreal dist = s.d->distanceFromChord(p);
0905             minDist = qMin(dist, minDist);
0906             maxDist = qMax(dist, maxDist);
0907         }
0908     }
0909 
0910     return (maxDist - minDist <= tolerance);
0911 }
0912 
0913 QList<QPointF> KoPathSegment::convexHull() const
0914 {
0915     QList<QPointF> hull;
0916     int deg = degree();
0917     if (deg == 1) {
0918         // easy just the two end points
0919         hull.append(d->first->point());
0920         hull.append(d->second->point());
0921     } else if (deg == 2) {
0922         // we want to have a counter-clockwise oriented triangle
0923         // of the three control points
0924         QPointF chord = d->second->point() - d->first->point();
0925         QPointF cp = d->first->activeControlPoint2() ? d->first->controlPoint2() : d->second->controlPoint1();
0926         QPointF relP = cp - d->first->point();
0927         // check on which side of the chord the control point is
0928         bool pIsRight = (chord.x() * relP.y() - chord.y() * relP.x() > 0);
0929         hull.append(d->first->point());
0930         if (pIsRight)
0931             hull.append(cp);
0932         hull.append(d->second->point());
0933         if (! pIsRight)
0934             hull.append(cp);
0935     } else if (deg == 3) {
0936         // we want a counter-clockwise oriented polygon
0937         QPointF chord = d->second->point() - d->first->point();
0938         QPointF relP1 = d->first->controlPoint2() - d->first->point();
0939         // check on which side of the chord the control points are
0940         bool p1IsRight = (chord.x() * relP1.y() - chord.y() * relP1.x() > 0);
0941         hull.append(d->first->point());
0942         if (p1IsRight)
0943             hull.append(d->first->controlPoint2());
0944         hull.append(d->second->point());
0945         if (! p1IsRight)
0946             hull.append(d->first->controlPoint2());
0947 
0948         // now we have a counter-clockwise triangle with the points i,j,k
0949         // we have to check where the last control points lies
0950         bool rightOfEdge[3];
0951         QPointF lastPoint = d->second->controlPoint1();
0952         for (int i = 0; i < 3; ++i) {
0953             QPointF relP = lastPoint - hull[i];
0954             QPointF edge = hull[(i+1)%3] - hull[i];
0955             rightOfEdge[i] = (edge.x() * relP.y() - edge.y() * relP.x() > 0);
0956         }
0957         for (int i = 0; i < 3; ++i) {
0958             int prev = (3 + i - 1) % 3;
0959             int next = (i + 1) % 3;
0960             // check if point is only right of the n-th edge
0961             if (! rightOfEdge[prev] && rightOfEdge[i] && ! rightOfEdge[next]) {
0962                 // insert by breaking the n-th edge
0963                 hull.insert(i + 1, lastPoint);
0964                 break;
0965             }
0966             // check if it is right of the n-th and right of the (n+1)-th edge
0967             if (rightOfEdge[i] && rightOfEdge[next]) {
0968                 // remove both edge, insert two new edges
0969                 hull[i+1] = lastPoint;
0970                 break;
0971             }
0972             // check if it is right of n-th and right of (n-1)-th edge
0973             if (rightOfEdge[i] && rightOfEdge[prev]) {
0974                 hull[i] = lastPoint;
0975                 break;
0976             }
0977         }
0978     }
0979 
0980     return hull;
0981 }
0982 
0983 QPair<KoPathSegment, KoPathSegment> KoPathSegment::splitAt(qreal t) const
0984 {
0985     QPair<KoPathSegment, KoPathSegment> results;
0986     if (!isValid())
0987         return results;
0988 
0989     if (degree() == 1) {
0990         QPointF p = d->first->point() + t * (d->second->point() - d->first->point());
0991         results.first = KoPathSegment(d->first->point(), p);
0992         results.second = KoPathSegment(p, d->second->point());
0993     } else {
0994         QPointF newCP2, newCP1, splitP, splitCP1, splitCP2;
0995 
0996         d->deCasteljau(t, &newCP2, &splitCP1, &splitP, &splitCP2, &newCP1);
0997 
0998         if (degree() == 2) {
0999             if (second()->activeControlPoint1()) {
1000                 KoPathPoint *s1p1 = new KoPathPoint(0, d->first->point());
1001                 KoPathPoint *s1p2 = new KoPathPoint(0, splitP);
1002                 s1p2->setControlPoint1(splitCP1);
1003                 KoPathPoint *s2p1 = new KoPathPoint(0, splitP);
1004                 KoPathPoint *s2p2 = new KoPathPoint(0, d->second->point());
1005                 s2p2->setControlPoint1(splitCP2);
1006                 results.first = KoPathSegment(s1p1, s1p2);
1007                 results.second = KoPathSegment(s2p1, s2p2);
1008             } else {
1009                 results.first = KoPathSegment(d->first->point(), splitCP1, splitP);
1010                 results.second = KoPathSegment(splitP, splitCP2, d->second->point());
1011             }
1012         } else {
1013             results.first = KoPathSegment(d->first->point(), newCP2, splitCP1, splitP);
1014             results.second = KoPathSegment(splitP, splitCP2, newCP1, d->second->point());
1015         }
1016     }
1017 
1018     return results;
1019 }
1020 
1021 QList<QPointF> KoPathSegment::controlPoints() const
1022 {
1023     QList<QPointF> controlPoints;
1024     controlPoints.append(d->first->point());
1025     if (d->first->activeControlPoint2())
1026         controlPoints.append(d->first->controlPoint2());
1027     if (d->second->activeControlPoint1())
1028         controlPoints.append(d->second->controlPoint1());
1029     controlPoints.append(d->second->point());
1030 
1031     return controlPoints;
1032 }
1033 
1034 qreal KoPathSegment::nearestPoint(const QPointF &point) const
1035 {
1036     if (!isValid())
1037         return -1.0;
1038 
1039     return KisBezierUtils::nearestPoint(controlPoints(), point);
1040 }
1041 
1042 KoPathSegment KoPathSegment::interpolate(const QPointF &p0, const QPointF &p1, const QPointF &p2, qreal t)
1043 {
1044     if (t <= 0.0 || t >= 1.0)
1045         return KoPathSegment();
1046 
1047     /*
1048         B(t) = [x2 y2] = (1-t)^2*P0 + 2t*(1-t)*P1 + t^2*P2
1049 
1050                B(t) - (1-t)^2*P0 - t^2*P2
1051          P1 =  --------------------------
1052                        2t*(1-t)
1053     */
1054 
1055     QPointF c1 = p1 - (1.0-t) * (1.0-t)*p0 - t * t * p2;
1056 
1057     qreal denom = 2.0 * t * (1.0-t);
1058 
1059     c1.rx() /= denom;
1060     c1.ry() /= denom;
1061 
1062     return KoPathSegment(p0, c1, p2);
1063 }
1064 
1065 #if 0
1066 void KoPathSegment::printDebug() const
1067 {
1068     int deg = degree();
1069     debugFlake << "degree:" << deg;
1070     if (deg < 1)
1071         return;
1072 
1073     debugFlake << "P0:" << d->first->point();
1074     if (deg == 1) {
1075         debugFlake << "P2:" << d->second->point();
1076     } else if (deg == 2) {
1077         if (d->first->activeControlPoint2())
1078             debugFlake << "P1:" << d->first->controlPoint2();
1079         else
1080             debugFlake << "P1:" << d->second->controlPoint1();
1081         debugFlake << "P2:" << d->second->point();
1082     } else if (deg == 3) {
1083         debugFlake << "P1:" << d->first->controlPoint2();
1084         debugFlake << "P2:" << d->second->controlPoint1();
1085         debugFlake << "P3:" << d->second->point();
1086     }
1087 }
1088 #endif