Warning, file /office/calligra/libs/flake/KoPathSegment.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 * Copyright (C) 2008-2009 Jan Hambrecht <jaham@gmx.net> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Library General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2 of the License, or (at your option) any later version. 0008 * 0009 * This library is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 * Library General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU Library General Public License 0015 * along with this library; see the file COPYING.LIB. If not, write to 0016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 * Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "KoPathSegment.h" 0021 #include "KoPathPoint.h" 0022 #include <FlakeDebug.h> 0023 #include <QPainterPath> 0024 #include <QTransform> 0025 #include <math.h> 0026 0027 /// Maximal recursion depth for finding root params 0028 const int MaxRecursionDepth = 64; 0029 /// Flatness tolerance for finding root params 0030 const qreal FlatnessTolerance = ldexp(1.0,-MaxRecursionDepth-1); 0031 0032 class BezierSegment 0033 { 0034 public: 0035 BezierSegment(int degree = 0, QPointF *p = 0) 0036 { 0037 if (degree) { 0038 for (int i = 0; i <= degree; ++i) 0039 points.append(p[i]); 0040 } 0041 } 0042 0043 void setDegree(int degree) 0044 { 0045 points.clear(); 0046 if (degree) { 0047 points.reserve(degree+1); 0048 for (int i = 0; i <= degree; ++i) 0049 points.append(QPointF()); 0050 } 0051 } 0052 0053 int degree() const 0054 { 0055 return points.count() - 1; 0056 } 0057 0058 QPointF point(int index) const 0059 { 0060 if (static_cast<int>(index) > degree()) 0061 return QPointF(); 0062 0063 return points[index]; 0064 } 0065 0066 void setPoint(int index, const QPointF &p) 0067 { 0068 if (static_cast<int>(index) > degree()) 0069 return; 0070 0071 points[index] = p; 0072 } 0073 0074 QPointF evaluate(qreal t, BezierSegment *left, BezierSegment *right) const 0075 { 0076 int deg = degree(); 0077 if (! deg) 0078 return QPointF(); 0079 0080 QVector<QVector<QPointF> > Vtemp(deg + 1); 0081 for (int i = 0; i <= deg; ++i) 0082 Vtemp[i].resize(deg + 1); 0083 0084 /* Copy control points */ 0085 for (int j = 0; j <= deg; j++) { 0086 Vtemp[0][j] = points[j]; 0087 } 0088 0089 /* Triangle computation */ 0090 for (int i = 1; i <= deg; i++) { 0091 for (int j =0 ; j <= deg - i; j++) { 0092 Vtemp[i][j].rx() = (1.0 - t) * Vtemp[i-1][j].x() + t * Vtemp[i-1][j+1].x(); 0093 Vtemp[i][j].ry() = (1.0 - t) * Vtemp[i-1][j].y() + t * Vtemp[i-1][j+1].y(); 0094 } 0095 } 0096 0097 if (left) { 0098 left->setDegree(deg); 0099 for (int j = 0; j <= deg; j++) { 0100 left->setPoint(j, Vtemp[j][0]); 0101 } 0102 } 0103 if (right) { 0104 right->setDegree(deg); 0105 for (int j = 0; j <= deg; j++) { 0106 right->setPoint(j, Vtemp[deg-j][j]); 0107 } 0108 } 0109 0110 return (Vtemp[deg][0]); 0111 } 0112 0113 QList<qreal> roots(int depth = 0) const 0114 { 0115 QList<qreal> rootParams; 0116 0117 if (! degree()) 0118 return rootParams; 0119 0120 // Calculate how often the control polygon crosses the x-axis 0121 // This is the upper limit for the number of roots. 0122 int xAxisCrossings = controlPolygonZeros(points); 0123 0124 if (! xAxisCrossings) { 0125 // No solutions. 0126 return rootParams; 0127 } 0128 else if (xAxisCrossings == 1) { 0129 // Exactly one solution. 0130 0131 // Stop recursion when the tree is deep enough 0132 if (depth >= MaxRecursionDepth) { 0133 // if deep enough, return 1 solution at midpoint 0134 rootParams.append((points.first().x() + points.last().x()) / 2.0); 0135 return rootParams; 0136 } 0137 else if (isFlat(FlatnessTolerance)) { 0138 // Calculate intersection of chord with x-axis. 0139 QPointF chord = points.last() - points.first(); 0140 QPointF segStart = points.first(); 0141 rootParams.append((chord.x() * segStart.y() - chord.y() * segStart.x()) / - chord.y()); 0142 return rootParams; 0143 } 0144 } 0145 0146 // Many solutions. Do recursive midpoint subdivision. 0147 BezierSegment left, right; 0148 evaluate(0.5, &left, &right); 0149 rootParams += left.roots(depth+1); 0150 rootParams += right.roots(depth+1); 0151 0152 return rootParams; 0153 } 0154 0155 static uint controlPolygonZeros(const QVector<QPointF> &controlPoints) 0156 { 0157 int controlPointCount = controlPoints.count(); 0158 if (controlPointCount < 2) 0159 return 0; 0160 0161 int signChanges = 0; 0162 0163 int currSign = controlPoints[0].y() < 0.0 ? -1 : 1; 0164 int oldSign; 0165 0166 for (short i = 1; i < controlPointCount; ++i) { 0167 oldSign = currSign; 0168 currSign = controlPoints[i].y() < 0.0 ? -1 : 1; 0169 0170 if (currSign != oldSign) { 0171 ++signChanges; 0172 } 0173 } 0174 0175 0176 return signChanges; 0177 } 0178 0179 int isFlat (qreal tolerance) const 0180 { 0181 int deg = degree(); 0182 0183 // Find the perpendicular distance from each interior control point to 0184 // the line connecting points[0] and points[degree] 0185 qreal *distance = new qreal[deg + 1]; 0186 0187 // Derive the implicit equation for line connecting first and last control points 0188 qreal a = points[0].y() - points[deg].y(); 0189 qreal b = points[deg].x() - points[0].x(); 0190 qreal c = points[0].x() * points[deg].y() - points[deg].x() * points[0].y(); 0191 0192 qreal abSquared = (a * a) + (b * b); 0193 0194 for (int i = 1; i < deg; i++) { 0195 // Compute distance from each of the points to that line 0196 distance[i] = a * points[i].x() + b * points[i].y() + c; 0197 if (distance[i] > 0.0) { 0198 distance[i] = (distance[i] * distance[i]) / abSquared; 0199 } 0200 if (distance[i] < 0.0) { 0201 distance[i] = -((distance[i] * distance[i]) / abSquared); 0202 } 0203 } 0204 0205 0206 // Find the largest distance 0207 qreal max_distance_above = 0.0; 0208 qreal max_distance_below = 0.0; 0209 for (int i = 1; i < deg; i++) { 0210 if (distance[i] < 0.0) { 0211 max_distance_below = qMin(max_distance_below, distance[i]); 0212 } 0213 if (distance[i] > 0.0) { 0214 max_distance_above = qMax(max_distance_above, distance[i]); 0215 } 0216 } 0217 delete [] distance; 0218 0219 // Implicit equation for zero line 0220 qreal a1 = 0.0; 0221 qreal b1 = 1.0; 0222 qreal c1 = 0.0; 0223 0224 // Implicit equation for "above" line 0225 qreal a2 = a; 0226 qreal b2 = b; 0227 qreal c2 = c + max_distance_above; 0228 0229 qreal det = a1 * b2 - a2 * b1; 0230 qreal dInv = 1.0/det; 0231 0232 qreal intercept_1 = (b1 * c2 - b2 * c1) * dInv; 0233 0234 // Implicit equation for "below" line 0235 a2 = a; 0236 b2 = b; 0237 c2 = c + max_distance_below; 0238 0239 det = a1 * b2 - a2 * b1; 0240 dInv = 1.0/det; 0241 0242 qreal intercept_2 = (b1 * c2 - b2 * c1) * dInv; 0243 0244 // Compute intercepts of bounding box 0245 qreal left_intercept = qMin(intercept_1, intercept_2); 0246 qreal right_intercept = qMax(intercept_1, intercept_2); 0247 0248 qreal error = 0.5 * (right_intercept-left_intercept); 0249 0250 return (error < tolerance); 0251 } 0252 0253 #ifndef NDEBUG 0254 void printDebug() const 0255 { 0256 int index = 0; 0257 // There exists a problem on msvc with for(each) and QVector<QPointF> 0258 for (int i = 0; i < points.count(); ++i) { 0259 const QPointF p(points[i]); 0260 debugFlake << QString("P%1 ").arg(index++) << p; 0261 } 0262 } 0263 #endif 0264 0265 private: 0266 QVector<QPointF> points; 0267 }; 0268 0269 class Q_DECL_HIDDEN KoPathSegment::Private 0270 { 0271 public: 0272 Private(KoPathSegment *qq, KoPathPoint *p1, KoPathPoint *p2) 0273 : first(p1), 0274 second(p2), 0275 q(qq) 0276 { 0277 } 0278 0279 /// calculates signed distance of given point from segment chord 0280 qreal distanceFromChord(const QPointF &point) const; 0281 0282 /// Returns the chord length, i.e. the distance between first and last control point 0283 qreal chordLength() const; 0284 0285 /// Returns intersection of lines if one exists 0286 QVector<QPointF> linesIntersection(const KoPathSegment &segment) const; 0287 0288 /// Returns parameters for curve extrema 0289 QList<qreal> extrema() const; 0290 0291 /// Returns parameters for curve roots 0292 QList<qreal> roots() const; 0293 0294 /** 0295 * The DeCasteljau algorithm for parameter t. 0296 * @param t the parameter to evaluate at 0297 * @param p1 the new control point of the segment start (for cubic curves only) 0298 * @param p2 the first control point at t 0299 * @param p3 the new point at t 0300 * @param p4 the second control point at t 0301 * @param p3 the new control point of the segment end (for cubic curves only) 0302 */ 0303 void deCasteljau(qreal t, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4, QPointF *p5) const; 0304 0305 KoPathPoint *first; 0306 KoPathPoint *second; 0307 KoPathSegment *q; 0308 }; 0309 0310 void KoPathSegment::Private::deCasteljau(qreal t, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4, QPointF *p5) const 0311 { 0312 if (!q->isValid()) 0313 return; 0314 0315 int deg = q->degree(); 0316 QPointF q[4]; 0317 0318 q[0] = first->point(); 0319 if (deg == 2) { 0320 q[1] = first->activeControlPoint2() ? first->controlPoint2() : second->controlPoint1(); 0321 } else if (deg == 3) { 0322 q[1] = first->controlPoint2(); 0323 q[2] = second->controlPoint1(); 0324 } 0325 q[deg] = second->point(); 0326 0327 // points of the new segment after the split point 0328 QPointF p[3]; 0329 0330 // the De Casteljau algorithm 0331 for (unsigned short j = 1; j <= deg; ++j) { 0332 for (unsigned short i = 0; i <= deg - j; ++i) { 0333 q[i] = (1.0 - t) * q[i] + t * q[i + 1]; 0334 } 0335 p[j - 1] = q[0]; 0336 } 0337 0338 if (deg == 2) { 0339 if (p2) 0340 *p2 = p[0]; 0341 if (p3) 0342 *p3 = p[1]; 0343 if (p4) 0344 *p4 = q[1]; 0345 } else if (deg == 3) { 0346 if (p1) 0347 *p1 = p[0]; 0348 if (p2) 0349 *p2 = p[1]; 0350 if (p3) 0351 *p3 = p[2]; 0352 if (p4) 0353 *p4 = q[1]; 0354 if (p5) 0355 *p5 = q[2]; 0356 } 0357 } 0358 0359 QList<qreal> KoPathSegment::Private::roots() const 0360 { 0361 QList<qreal> rootParams; 0362 0363 if (!q->isValid()) 0364 return rootParams; 0365 0366 // Calculate how often the control polygon crosses the x-axis 0367 // This is the upper limit for the number of roots. 0368 int xAxisCrossings = BezierSegment::controlPolygonZeros(q->controlPoints()); 0369 0370 if (!xAxisCrossings) { 0371 // No solutions. 0372 } 0373 else if (xAxisCrossings == 1 && q->isFlat(0.01 / chordLength())) { 0374 // Exactly one solution. 0375 // Calculate intersection of chord with x-axis. 0376 QPointF chord = second->point() - first->point(); 0377 QPointF segStart = first->point(); 0378 rootParams.append((chord.x() * segStart.y() - chord.y() * segStart.x()) / - chord.y()); 0379 } 0380 else { 0381 // Many solutions. Do recursive midpoint subdivision. 0382 QPair<KoPathSegment, KoPathSegment> splitSegments = q->splitAt(0.5); 0383 rootParams += splitSegments.first.d->roots(); 0384 rootParams += splitSegments.second.d->roots(); 0385 } 0386 0387 return rootParams; 0388 } 0389 0390 QList<qreal> KoPathSegment::Private::extrema() const 0391 { 0392 int deg = q->degree(); 0393 if (deg <= 1) 0394 return QList<qreal>(); 0395 0396 QList<qreal> params; 0397 0398 /* 0399 * The basic idea for calculating the extrema for bezier segments 0400 * was found in comp.graphics.algorithms: 0401 * 0402 * Both the x coordinate and the y coordinate are polynomial. Newton told 0403 * us that at a maximum or minimum the derivative will be zero. 0404 * 0405 * We have a helpful trick for the derivatives: use the curve r(t) defined by 0406 * differences of successive control points. 0407 * Setting r(t) to zero and using the x and y coordinates of differences of 0408 * successive control points lets us find the parameters t, where the original 0409 * bezier curve has a minimum or a maximum. 0410 */ 0411 if (deg == 2) { 0412 /* 0413 * For quadratic bezier curves r(t) is a linear Bezier curve: 0414 * 0415 * 1 0416 * r(t) = Sum Bi,1(t) *Pi = B0,1(t) * P0 + B1,1(t) * P1 0417 * i=0 0418 * 0419 * r(t) = (1-t) * P0 + t * P1 0420 * 0421 * r(t) = (P1 - P0) * t + P0 0422 */ 0423 0424 // calculating the differences between successive control points 0425 QPointF cp = first->activeControlPoint2() ? 0426 first->controlPoint2() : second->controlPoint1(); 0427 QPointF x0 = cp - first->point(); 0428 QPointF x1 = second->point() - cp; 0429 0430 // calculating the coefficients 0431 QPointF a = x1 - x0; 0432 QPointF c = x0; 0433 0434 if (a.x() != 0.0) 0435 params.append(-c.x() / a.x()); 0436 if (a.y() != 0.0) 0437 params.append(-c.y() / a.y()); 0438 } else if (deg == 3) { 0439 /* 0440 * For cubic bezier curves r(t) is a quadratic Bezier curve: 0441 * 0442 * 2 0443 * r(t) = Sum Bi,2(t) *Pi = B0,2(t) * P0 + B1,2(t) * P1 + B2,2(t) * P2 0444 * i=0 0445 * 0446 * r(t) = (1-t)^2 * P0 + 2t(1-t) * P1 + t^2 * P2 0447 * 0448 * r(t) = (P2 - 2*P1 + P0) * t^2 + (2*P1 - 2*P0) * t + P0 0449 * 0450 */ 0451 // calcualting the differences between successive control points 0452 QPointF x0 = first->controlPoint2() - first->point(); 0453 QPointF x1 = second->controlPoint1() - first->controlPoint2(); 0454 QPointF x2 = second->point() - second->controlPoint1(); 0455 0456 // calculating the coefficents 0457 QPointF a = x2 - 2.0 * x1 + x0; 0458 QPointF b = 2.0 * x1 - 2.0 * x0; 0459 QPointF c = x0; 0460 0461 // calculating parameter t at minimum/maximum in x-direction 0462 if (a.x() == 0.0) { 0463 params.append(- c.x() / b.x()); 0464 } else { 0465 qreal rx = b.x() * b.x() - 4.0 * a.x() * c.x(); 0466 if (rx < 0.0) 0467 rx = 0.0; 0468 params.append((-b.x() + sqrt(rx)) / (2.0*a.x())); 0469 params.append((-b.x() - sqrt(rx)) / (2.0*a.x())); 0470 } 0471 0472 // calculating parameter t at minimum/maximum in y-direction 0473 if (a.y() == 0.0) { 0474 params.append(- c.y() / b.y()); 0475 } else { 0476 qreal ry = b.y() * b.y() - 4.0 * a.y() * c.y(); 0477 if (ry < 0.0) 0478 ry = 0.0; 0479 params.append((-b.y() + sqrt(ry)) / (2.0*a.y())); 0480 params.append((-b.y() - sqrt(ry)) / (2.0*a.y())); 0481 } 0482 } 0483 0484 return params; 0485 } 0486 0487 qreal KoPathSegment::Private::distanceFromChord(const QPointF &point) const 0488 { 0489 // the segments chord 0490 QPointF chord = second->point() - first->point(); 0491 // the point relative to the segment 0492 QPointF relPoint = point - first->point(); 0493 // project point to chord 0494 qreal scale = chord.x() * relPoint.x() + chord.y() * relPoint.y(); 0495 scale /= chord.x() * chord.x() + chord.y() * chord.y(); 0496 0497 // the vector form the point to the projected point on the chord 0498 QPointF diffVec = scale * chord - relPoint; 0499 0500 // the unsigned distance of the point to the chord 0501 qreal distance = sqrt(diffVec.x() * diffVec.x() + diffVec.y() * diffVec.y()); 0502 0503 // determine sign of the distance using the cross product 0504 if (chord.x()*relPoint.y() - chord.y()*relPoint.x() > 0) { 0505 return distance; 0506 } else { 0507 return -distance; 0508 } 0509 } 0510 0511 qreal KoPathSegment::Private::chordLength() const 0512 { 0513 QPointF chord = second->point() - first->point(); 0514 return sqrt(chord.x() * chord.x() + chord.y() * chord.y()); 0515 } 0516 0517 QVector<QPointF> KoPathSegment::Private::linesIntersection(const KoPathSegment &segment) const 0518 { 0519 //debugFlake << "intersecting two lines"; 0520 /* 0521 we have to line segments: 0522 0523 s1 = A + r * (B-A), s2 = C + s * (D-C) for r,s in [0,1] 0524 0525 if s1 and s2 intersect we set s1 = s2 so we get these two equations: 0526 0527 Ax + r * (Bx-Ax) = Cx + s * (Dx-Cx) 0528 Ay + r * (By-Ay) = Cy + s * (Dy-Cy) 0529 0530 which we can solve to get r and s 0531 */ 0532 QVector<QPointF> isects; 0533 QPointF A = first->point(); 0534 QPointF B = second->point(); 0535 QPointF C = segment.first()->point(); 0536 QPointF D = segment.second()->point(); 0537 0538 qreal denom = (B.x() - A.x()) * (D.y() - C.y()) - (B.y() - A.y()) * (D.x() - C.x()); 0539 qreal num_r = (A.y() - C.y()) * (D.x() - C.x()) - (A.x() - C.x()) * (D.y() - C.y()); 0540 // check if lines are collinear 0541 if (denom == 0.0 && num_r == 0.0) 0542 return isects; 0543 0544 qreal num_s = (A.y() - C.y()) * (B.x() - A.x()) - (A.x() - C.x()) * (B.y() - A.y()); 0545 qreal r = num_r / denom; 0546 qreal s = num_s / denom; 0547 0548 // check if intersection is inside our line segments 0549 if (r < 0.0 || r > 1.0) 0550 return isects; 0551 if (s < 0.0 || s > 1.0) 0552 return isects; 0553 0554 // calculate the actual intersection point 0555 isects.append(A + r * (B - A)); 0556 0557 return isects; 0558 } 0559 0560 0561 /////////////////// 0562 KoPathSegment::KoPathSegment(KoPathPoint * first, KoPathPoint * second) 0563 : d(new Private(this, first, second)) 0564 { 0565 } 0566 0567 KoPathSegment::KoPathSegment(const KoPathSegment & segment) 0568 : d(new Private(this, 0, 0)) 0569 { 0570 if (! segment.first() || segment.first()->parent()) 0571 setFirst(segment.first()); 0572 else 0573 setFirst(new KoPathPoint(*segment.first())); 0574 0575 if (! segment.second() || segment.second()->parent()) 0576 setSecond(segment.second()); 0577 else 0578 setSecond(new KoPathPoint(*segment.second())); 0579 } 0580 0581 KoPathSegment::KoPathSegment(const QPointF &p0, const QPointF &p1) 0582 : d(new Private(this, new KoPathPoint(), new KoPathPoint())) 0583 { 0584 d->first->setPoint(p0); 0585 d->second->setPoint(p1); 0586 } 0587 0588 KoPathSegment::KoPathSegment(const QPointF &p0, const QPointF &p1, const QPointF &p2) 0589 : d(new Private(this, new KoPathPoint(), new KoPathPoint())) 0590 { 0591 d->first->setPoint(p0); 0592 d->first->setControlPoint2(p1); 0593 d->second->setPoint(p2); 0594 } 0595 0596 KoPathSegment::KoPathSegment(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3) 0597 : d(new Private(this, new KoPathPoint(), new KoPathPoint())) 0598 { 0599 d->first->setPoint(p0); 0600 d->first->setControlPoint2(p1); 0601 d->second->setControlPoint1(p2); 0602 d->second->setPoint(p3); 0603 } 0604 0605 KoPathSegment &KoPathSegment::operator=(const KoPathSegment &rhs) 0606 { 0607 if (this == &rhs) 0608 return (*this); 0609 0610 if (! rhs.first() || rhs.first()->parent()) 0611 setFirst(rhs.first()); 0612 else 0613 setFirst(new KoPathPoint(*rhs.first())); 0614 0615 if (! rhs.second() || rhs.second()->parent()) 0616 setSecond(rhs.second()); 0617 else 0618 setSecond(new KoPathPoint(*rhs.second())); 0619 0620 return (*this); 0621 } 0622 0623 KoPathSegment::~KoPathSegment() 0624 { 0625 if (d->first && ! d->first->parent()) 0626 delete d->first; 0627 if (d->second && ! d->second->parent()) 0628 delete d->second; 0629 delete d; 0630 } 0631 0632 KoPathPoint *KoPathSegment::first() const 0633 { 0634 return d->first; 0635 } 0636 0637 void KoPathSegment::setFirst(KoPathPoint *first) 0638 { 0639 if (d->first && !d->first->parent()) 0640 delete d->first; 0641 d->first = first; 0642 } 0643 0644 KoPathPoint *KoPathSegment::second() const 0645 { 0646 return d->second; 0647 } 0648 0649 void KoPathSegment::setSecond(KoPathPoint *second) 0650 { 0651 if (d->second && !d->second->parent()) 0652 delete d->second; 0653 d->second = second; 0654 } 0655 0656 bool KoPathSegment::isValid() const 0657 { 0658 return (d->first && d->second); 0659 } 0660 0661 bool KoPathSegment::operator==(const KoPathSegment &rhs) const 0662 { 0663 if (!isValid() && !rhs.isValid()) 0664 return true; 0665 if (isValid() && !rhs.isValid()) 0666 return false; 0667 if (!isValid() && rhs.isValid()) 0668 return false; 0669 0670 return (*first() == *rhs.first() && *second() == *rhs.second()); 0671 } 0672 0673 int KoPathSegment::degree() const 0674 { 0675 if (!d->first || !d->second) 0676 return -1; 0677 0678 bool c1 = d->first->activeControlPoint2(); 0679 bool c2 = d->second->activeControlPoint1(); 0680 if (!c1 && !c2) 0681 return 1; 0682 if (c1 && c2) 0683 return 3; 0684 return 2; 0685 } 0686 0687 QPointF KoPathSegment::pointAt(qreal t) const 0688 { 0689 if (!isValid()) 0690 return QPointF(); 0691 0692 if (degree() == 1) { 0693 return d->first->point() + t * (d->second->point() - d->first->point()); 0694 } else { 0695 QPointF splitP; 0696 0697 d->deCasteljau(t, 0, 0, &splitP, 0, 0); 0698 0699 return splitP; 0700 } 0701 } 0702 0703 QRectF KoPathSegment::controlPointRect() const 0704 { 0705 if (!isValid()) 0706 return QRectF(); 0707 0708 const QVector<QPointF> points = controlPoints(); 0709 QRectF bbox(points.first(), points.first()); 0710 // There exists a problem on msvc with for(each) and QVector<QPointF> 0711 for (int i = 0; i < points.count(); ++i) { 0712 const QPointF p(points[i]); 0713 bbox.setLeft(qMin(bbox.left(), p.x())); 0714 bbox.setRight(qMax(bbox.right(), p.x())); 0715 bbox.setTop(qMin(bbox.top(), p.y())); 0716 bbox.setBottom(qMax(bbox.bottom(), p.y())); 0717 } 0718 0719 if (degree() == 1) { 0720 // adjust bounding rect of horizontal and vertical lines 0721 if (bbox.height() == 0.0) 0722 bbox.setHeight(0.1); 0723 if (bbox.width() == 0.0) 0724 bbox.setWidth(0.1); 0725 } 0726 0727 return bbox; 0728 } 0729 0730 QRectF KoPathSegment::boundingRect() const 0731 { 0732 if (!isValid()) 0733 return QRectF(); 0734 0735 QRectF rect = QRectF(d->first->point(), d->second->point()).normalized(); 0736 0737 if (degree() == 1) { 0738 // adjust bounding rect of horizontal and vertical lines 0739 if (rect.height() == 0.0) 0740 rect.setHeight(0.1); 0741 if (rect.width() == 0.0) 0742 rect.setWidth(0.1); 0743 } else { 0744 /* 0745 * The basic idea for calculating the axis aligned bounding box (AABB) for bezier segments 0746 * was found in comp.graphics.algorithms: 0747 * Use the points at the extrema of the curve to calculate the AABB. 0748 */ 0749 for (qreal t : d->extrema()) { 0750 if (t >= 0.0 && t <= 1.0) { 0751 QPointF p = pointAt(t); 0752 rect.setLeft(qMin(rect.left(), p.x())); 0753 rect.setRight(qMax(rect.right(), p.x())); 0754 rect.setTop(qMin(rect.top(), p.y())); 0755 rect.setBottom(qMax(rect.bottom(), p.y())); 0756 } 0757 } 0758 } 0759 0760 return rect; 0761 } 0762 0763 QVector<QPointF> KoPathSegment::intersections(const KoPathSegment &segment) const 0764 { 0765 // this function uses a technique known as bezier clipping to find the 0766 // intersections of the two bezier curves 0767 0768 QVector<QPointF> isects; 0769 0770 if (!isValid() || !segment.isValid()) 0771 return isects; 0772 0773 int degree1 = degree(); 0774 int degree2 = segment.degree(); 0775 0776 QRectF myBound = boundingRect(); 0777 QRectF otherBound = segment.boundingRect(); 0778 //debugFlake << "my boundingRect =" << myBound; 0779 //debugFlake << "other boundingRect =" << otherBound; 0780 if (!myBound.intersects(otherBound)) { 0781 //debugFlake << "segments do not intersect"; 0782 return isects; 0783 } 0784 0785 // short circuit lines intersection 0786 if (degree1 == 1 && degree2 == 1) { 0787 //debugFlake << "intersecting two lines"; 0788 isects += d->linesIntersection(segment); 0789 return isects; 0790 } 0791 0792 // first calculate the fat line L by using the signed distances 0793 // of the control points from the chord 0794 qreal dmin, dmax; 0795 if (degree1 == 1) { 0796 dmin = 0.0; 0797 dmax = 0.0; 0798 } else if (degree1 == 2) { 0799 qreal d1; 0800 if (d->first->activeControlPoint2()) 0801 d1 = d->distanceFromChord(d->first->controlPoint2()); 0802 else 0803 d1 = d->distanceFromChord(d->second->controlPoint1()); 0804 dmin = qMin(qreal(0.0), qreal(0.5 * d1)); 0805 dmax = qMax(qreal(0.0), qreal(0.5 * d1)); 0806 } else { 0807 qreal d1 = d->distanceFromChord(d->first->controlPoint2()); 0808 qreal d2 = d->distanceFromChord(d->second->controlPoint1()); 0809 if (d1*d2 > 0.0) { 0810 dmin = 0.75 * qMin(qreal(0.0), qMin(d1, d2)); 0811 dmax = 0.75 * qMax(qreal(0.0), qMax(d1, d2)); 0812 } else { 0813 dmin = 4.0 / 9.0 * qMin(qreal(0.0), qMin(d1, d2)); 0814 dmax = 4.0 / 9.0 * qMax(qreal(0.0), qMax(d1, d2)); 0815 } 0816 } 0817 0818 //debugFlake << "using fat line: dmax =" << dmax << " dmin =" << dmin; 0819 0820 /* 0821 the other segment is given as a bezier curve of the form: 0822 (1) P(t) = sum_i P_i * B_{n,i}(t) 0823 our chord line is of the form: 0824 (2) ax + by + c = 0 0825 we can determine the distance d(t) from any point P(t) to our chord 0826 by substituting formula (1) into formula (2): 0827 d(t) = sum_i d_i B_{n,i}(t), where d_i = a*x_i + b*y_i + c 0828 which forms another explicit bezier curve 0829 D(t) = (t,d(t)) = sum_i D_i B_{n,i}(t) 0830 now values of t for which P(t) lies outside of our fat line L 0831 corresponds to values of t for which D(t) lies above d = dmax or 0832 below d = dmin 0833 we can determine parameter ranges of t for which P(t) is guaranteed 0834 to lie outside of L by identifying ranges of t which the convex hull 0835 of D(t) lies above dmax or below dmin 0836 */ 0837 // now calculate the control points of D(t) by using the signed 0838 // distances of P_i to our chord 0839 KoPathSegment dt; 0840 if (degree2 == 1) { 0841 QPointF p0(0.0, d->distanceFromChord(segment.first()->point())); 0842 QPointF p1(1.0, d->distanceFromChord(segment.second()->point())); 0843 dt = KoPathSegment(p0, p1); 0844 } else if (degree2 == 2) { 0845 QPointF p0(0.0, d->distanceFromChord(segment.first()->point())); 0846 QPointF p1 = segment.first()->activeControlPoint2() 0847 ? QPointF(0.5, d->distanceFromChord(segment.first()->controlPoint2())) 0848 : QPointF(0.5, d->distanceFromChord(segment.second()->controlPoint1())); 0849 QPointF p2(1.0, d->distanceFromChord(segment.second()->point())); 0850 dt = KoPathSegment(p0, p1, p2); 0851 } else if (degree2 == 3) { 0852 QPointF p0(0.0, d->distanceFromChord(segment.first()->point())); 0853 QPointF p1(1. / 3., d->distanceFromChord(segment.first()->controlPoint2())); 0854 QPointF p2(2. / 3., d->distanceFromChord(segment.second()->controlPoint1())); 0855 QPointF p3(1.0, d->distanceFromChord(segment.second()->point())); 0856 dt = KoPathSegment(p0, p1, p2, p3); 0857 } else { 0858 //debugFlake << "invalid degree of segment -> exiting"; 0859 return isects; 0860 } 0861 0862 // get convex hull of the segment D(t) 0863 const QVector<QPointF> hull = dt.convexHull(); 0864 0865 // now calculate intersections with the line y1 = dmin, y2 = dmax 0866 // with the convex hull edges 0867 int hullCount = hull.count(); 0868 qreal tmin = 1.0, tmax = 0.0; 0869 bool intersectionsFoundMax = false; 0870 bool intersectionsFoundMin = false; 0871 0872 for (int i = 0; i < hullCount; ++i) { 0873 QPointF p1 = hull[i]; 0874 QPointF p2 = hull[(i+1) % hullCount]; 0875 //debugFlake << "intersecting hull edge (" << p1 << p2 << ")"; 0876 // hull edge is completely above dmax 0877 if (p1.y() > dmax && p2.y() > dmax) 0878 continue; 0879 // hull egde is completely below dmin 0880 if (p1.y() < dmin && p2.y() < dmin) 0881 continue; 0882 if (p1.x() == p2.x()) { 0883 // vertical edge 0884 bool dmaxIntersection = (dmax < qMax(p1.y(), p2.y()) && dmax > qMin(p1.y(), p2.y())); 0885 bool dminIntersection = (dmin < qMax(p1.y(), p2.y()) && dmin > qMin(p1.y(), p2.y())); 0886 if (dmaxIntersection || dminIntersection) { 0887 tmin = qMin(tmin, p1.x()); 0888 tmax = qMax(tmax, p1.x()); 0889 if (dmaxIntersection) { 0890 intersectionsFoundMax = true; 0891 //debugFlake << "found intersection with dmax at " << p1.x() << "," << dmax; 0892 } else { 0893 intersectionsFoundMin = true; 0894 //debugFlake << "found intersection with dmin at " << p1.x() << "," << dmin; 0895 } 0896 } 0897 } else if (p1.y() == p2.y()) { 0898 // horizontal line 0899 if (p1.y() == dmin || p1.y() == dmax) { 0900 if (p1.y() == dmin) { 0901 intersectionsFoundMin = true; 0902 //debugFlake << "found intersection with dmin at " << p1.x() << "," << dmin; 0903 //debugFlake << "found intersection with dmin at " << p2.x() << "," << dmin; 0904 } else { 0905 intersectionsFoundMax = true; 0906 //debugFlake << "found intersection with dmax at " << p1.x() << "," << dmax; 0907 //debugFlake << "found intersection with dmax at " << p2.x() << "," << dmax; 0908 } 0909 tmin = qMin(tmin, p1.x()); 0910 tmin = qMin(tmin, p2.x()); 0911 tmax = qMax(tmax, p1.x()); 0912 tmax = qMax(tmax, p2.x()); 0913 } 0914 } else { 0915 qreal dx = p2.x() - p1.x(); 0916 qreal dy = p2.y() - p1.y(); 0917 qreal m = dy / dx; 0918 qreal n = p1.y() - m * p1.x(); 0919 qreal t1 = (dmax - n) / m; 0920 if (t1 >= 0.0 && t1 <= 1.0) { 0921 tmin = qMin(tmin, t1); 0922 tmax = qMax(tmax, t1); 0923 intersectionsFoundMax = true; 0924 //debugFlake << "found intersection with dmax at " << t1 << "," << dmax; 0925 } 0926 qreal t2 = (dmin - n) / m; 0927 if (t2 >= 0.0 && t2 < 1.0) { 0928 tmin = qMin(tmin, t2); 0929 tmax = qMax(tmax, t2); 0930 intersectionsFoundMin = true; 0931 //debugFlake << "found intersection with dmin at " << t2 << "," << dmin; 0932 } 0933 } 0934 } 0935 0936 bool intersectionsFound = intersectionsFoundMin && intersectionsFoundMax; 0937 0938 //if (intersectionsFound) 0939 // debugFlake << "clipping segment to interval [" << tmin << "," << tmax << "]"; 0940 0941 if (!intersectionsFound || (1.0 - (tmax - tmin)) <= 0.2) { 0942 //debugFlake << "could not clip enough -> split segment"; 0943 // we could not reduce the interval significantly 0944 // so split the curve and calculate intersections 0945 // with the remaining parts 0946 QPair<KoPathSegment, KoPathSegment> parts = splitAt(0.5); 0947 if (d->chordLength() < 1e-5) 0948 isects += parts.first.second()->point(); 0949 else { 0950 isects += segment.intersections(parts.first); 0951 isects += segment.intersections(parts.second); 0952 } 0953 } else if (qAbs(tmin - tmax) < 1e-5) { 0954 //debugFlake << "Yay, we found an intersection"; 0955 // the inteval is pretty small now, just calculate the intersection at this point 0956 isects.append(segment.pointAt(tmin)); 0957 } else { 0958 QPair<KoPathSegment, KoPathSegment> clip1 = segment.splitAt(tmin); 0959 //debugFlake << "splitting segment at" << tmin; 0960 qreal t = (tmax - tmin) / (1.0 - tmin); 0961 QPair<KoPathSegment, KoPathSegment> clip2 = clip1.second.splitAt(t); 0962 //debugFlake << "splitting second part at" << t << "("<<tmax<<")"; 0963 isects += clip2.first.intersections(*this); 0964 } 0965 0966 return isects; 0967 } 0968 0969 KoPathSegment KoPathSegment::mapped(const QTransform &matrix) const 0970 { 0971 if (!isValid()) 0972 return *this; 0973 0974 KoPathPoint * p1 = new KoPathPoint(*d->first); 0975 KoPathPoint * p2 = new KoPathPoint(*d->second); 0976 p1->map(matrix); 0977 p2->map(matrix); 0978 0979 return KoPathSegment(p1, p2); 0980 } 0981 0982 KoPathSegment KoPathSegment::toCubic() const 0983 { 0984 if (! isValid()) 0985 return KoPathSegment(); 0986 0987 KoPathPoint * p1 = new KoPathPoint(*d->first); 0988 KoPathPoint * p2 = new KoPathPoint(*d->second); 0989 0990 if (degree() == 1) { 0991 p1->setControlPoint2(p1->point() + 0.3 * (p2->point() - p1->point())); 0992 p2->setControlPoint1(p2->point() + 0.3 * (p1->point() - p2->point())); 0993 } else if (degree() == 2) { 0994 /* quadric bezier (a0,a1,a2) to cubic bezier (b0,b1,b2,b3): 0995 * 0996 * b0 = a0 0997 * b1 = a0 + 2/3 * (a1-a0) 0998 * b2 = a1 + 1/3 * (a2-a1) 0999 * b3 = a2 1000 */ 1001 QPointF a1 = p1->activeControlPoint2() ? p1->controlPoint2() : p2->controlPoint1(); 1002 QPointF b1 = p1->point() + 2.0 / 3.0 * (a1 - p1->point()); 1003 QPointF b2 = a1 + 1.0 / 3.0 * (p2->point() - a1); 1004 p1->setControlPoint2(b1); 1005 p2->setControlPoint1(b2); 1006 } 1007 1008 return KoPathSegment(p1, p2); 1009 } 1010 1011 qreal KoPathSegment::length(qreal error) const 1012 { 1013 /* 1014 * This algorithm is implemented based on an idea by Jens Gravesen: 1015 * "Adaptive subdivision and the length of Bezier curves" mat-report no. 1992-10, Mathematical Institute, 1016 * The Technical University of Denmark. 1017 * 1018 * By subdividing the curve at parameter value t you only have to find the length of a full Bezier curve. 1019 * If you denote the length of the control polygon by L1 i.e.: 1020 * L1 = |P0 P1| +|P1 P2| +|P2 P3| 1021 * 1022 * and the length of the cord by L0 i.e.: 1023 * L0 = |P0 P3| 1024 * 1025 * then 1026 * L = 1/2*L0 + 1/2*L1 1027 * 1028 * is a good approximation to the length of the curve, and the difference 1029 * ERR = L1-L0 1030 * 1031 * is a measure of the error. If the error is to large, then you just subdivide curve at parameter value 1032 * 1/2, and find the length of each half. 1033 * If m is the number of subdivisions then the error goes to zero as 2^-4m. 1034 * If you don't have a cubic curve but a curve of degree n then you put 1035 * L = (2*L0 + (n-1)*L1)/(n+1) 1036 */ 1037 1038 int deg = degree(); 1039 1040 if (deg == -1) 1041 return 0.0; 1042 1043 const QVector<QPointF> ctrlPoints = controlPoints(); 1044 1045 // calculate chord length 1046 qreal chordLen = d->chordLength(); 1047 1048 if (deg == 1) { 1049 return chordLen; 1050 } 1051 1052 // calculate length of control polygon 1053 qreal polyLength = 0.0; 1054 1055 for (int i = 0; i < deg; ++i) { 1056 QPointF ctrlSegment = ctrlPoints[i+1] - ctrlPoints[i]; 1057 polyLength += sqrt(ctrlSegment.x() * ctrlSegment.x() + ctrlSegment.y() * ctrlSegment.y()); 1058 } 1059 1060 if ((polyLength - chordLen) > error) { 1061 // the error is still bigger than our tolerance -> split segment 1062 QPair<KoPathSegment, KoPathSegment> parts = splitAt(0.5); 1063 return parts.first.length(error) + parts.second.length(error); 1064 } else { 1065 // the error is smaller than our tolerance 1066 if (deg == 3) 1067 return 0.5 * chordLen + 0.5 * polyLength; 1068 else 1069 return (2.0 * chordLen + polyLength) / 3.0; 1070 } 1071 } 1072 1073 qreal KoPathSegment::lengthAt(qreal t, qreal error) const 1074 { 1075 if (t == 0.0) 1076 return 0.0; 1077 if (t == 1.0) 1078 return length(error); 1079 1080 QPair<KoPathSegment, KoPathSegment> parts = splitAt(t); 1081 return parts.first.length(error); 1082 } 1083 1084 qreal KoPathSegment::paramAtLength(qreal length, qreal tolerance) const 1085 { 1086 const int deg = degree(); 1087 // invalid degree or invalid specified length 1088 if (deg < 1 || length <= 0.0) { 1089 return 0.0; 1090 } 1091 1092 if (deg == 1) { 1093 // make sure we return a maximum value of 1.0 1094 return qMin(qreal(1.0), length / d->chordLength()); 1095 } 1096 1097 // for curves we need to make sure, that the specified length 1098 // value does not exceed the actual length of the segment 1099 // if that happens, we return 1.0 to avoid infinite looping 1100 if (length >= d->chordLength() && length >= this->length(tolerance)) { 1101 return 1.0; 1102 } 1103 1104 qreal startT = 0.0; // interval start 1105 qreal midT = 0.5; // interval center 1106 qreal endT = 1.0; // interval end 1107 1108 // divide and conquer, split a midpoint and check 1109 // on which side of the midpoint to continue 1110 qreal midLength = lengthAt(0.5); 1111 while (qAbs(midLength - length) / length > tolerance) { 1112 if (midLength < length) 1113 startT = midT; 1114 else 1115 endT = midT; 1116 1117 // new interval center 1118 midT = 0.5 * (startT + endT); 1119 // length at new interval center 1120 midLength = lengthAt(midT); 1121 } 1122 1123 return midT; 1124 } 1125 1126 bool KoPathSegment::isFlat(qreal tolerance) const 1127 { 1128 /* 1129 * Calculate the height of the bezier curve. 1130 * This is done by rotating the curve so that then chord 1131 * is parallel to the x-axis and the calculating the 1132 * parameters t for the extrema of the curve. 1133 * The curve points at the extrema are then used to 1134 * calculate the height. 1135 */ 1136 if (degree() <= 1) 1137 return true; 1138 1139 QPointF chord = d->second->point() - d->first->point(); 1140 // calculate angle of chord to the x-axis 1141 qreal chordAngle = atan2(chord.y(), chord.x()); 1142 QTransform m; 1143 m.translate(d->first->point().x(), d->first->point().y()); 1144 m.rotate(chordAngle * M_PI / 180.0); 1145 m.translate(-d->first->point().x(), -d->first->point().y()); 1146 1147 KoPathSegment s = mapped(m); 1148 1149 qreal minDist = 0.0; 1150 qreal maxDist = 0.0; 1151 1152 for (qreal t : s.d->extrema()) { 1153 if (t >= 0.0 && t <= 1.0) { 1154 QPointF p = pointAt(t); 1155 qreal dist = s.d->distanceFromChord(p); 1156 minDist = qMin(dist, minDist); 1157 maxDist = qMax(dist, maxDist); 1158 } 1159 } 1160 1161 return (maxDist - minDist <= tolerance); 1162 } 1163 1164 QVector<QPointF> KoPathSegment::convexHull() const 1165 { 1166 QVector<QPointF> hull; 1167 1168 int deg = degree(); 1169 if (deg == 1) { 1170 // easy just the two end points 1171 hull.append(d->first->point()); 1172 hull.append(d->second->point()); 1173 } else if (deg == 2) { 1174 // we want to have a counter-clockwise oriented triangle 1175 // of the three control points 1176 QPointF chord = d->second->point() - d->first->point(); 1177 QPointF cp = d->first->activeControlPoint2() ? d->first->controlPoint2() : d->second->controlPoint1(); 1178 QPointF relP = cp - d->first->point(); 1179 // check on which side of the chord the control point is 1180 bool pIsRight = (chord.x() * relP.y() - chord.y() * relP.x() > 0); 1181 hull.append(d->first->point()); 1182 if (pIsRight) 1183 hull.append(cp); 1184 hull.append(d->second->point()); 1185 if (! pIsRight) 1186 hull.append(cp); 1187 } else if (deg == 3) { 1188 // we want a counter-clockwise oriented polygon 1189 QPointF chord = d->second->point() - d->first->point(); 1190 QPointF relP1 = d->first->controlPoint2() - d->first->point(); 1191 // check on which side of the chord the control points are 1192 bool p1IsRight = (chord.x() * relP1.y() - chord.y() * relP1.x() > 0); 1193 hull.append(d->first->point()); 1194 if (p1IsRight) 1195 hull.append(d->first->controlPoint2()); 1196 hull.append(d->second->point()); 1197 if (! p1IsRight) 1198 hull.append(d->first->controlPoint2()); 1199 1200 // now we have a counter-clockwise triangle with the points i,j,k 1201 // we have to check where the last control points lies 1202 bool rightOfEdge[3]; 1203 QPointF lastPoint = d->second->controlPoint1(); 1204 for (int i = 0; i < 3; ++i) { 1205 QPointF relP = lastPoint - hull[i]; 1206 QPointF edge = hull[(i+1)%3] - hull[i]; 1207 rightOfEdge[i] = (edge.x() * relP.y() - edge.y() * relP.x() > 0); 1208 } 1209 for (int i = 0; i < 3; ++i) { 1210 int prev = (3 + i - 1) % 3; 1211 int next = (i + 1) % 3; 1212 // check if point is only right of the n-th edge 1213 if (! rightOfEdge[prev] && rightOfEdge[i] && ! rightOfEdge[next]) { 1214 // insert by breaking the n-th edge 1215 hull.insert(i + 1, lastPoint); 1216 break; 1217 } 1218 // check if it is right of the n-th and right of the (n+1)-th edge 1219 if (rightOfEdge[i] && rightOfEdge[next]) { 1220 // remove both edge, insert two new edges 1221 hull[i+1] = lastPoint; 1222 break; 1223 } 1224 // check if it is right of n-th and right of (n-1)-th edge 1225 if (rightOfEdge[i] && rightOfEdge[prev]) { 1226 hull[i] = lastPoint; 1227 break; 1228 } 1229 } 1230 } 1231 1232 return hull; 1233 } 1234 1235 QPair<KoPathSegment, KoPathSegment> KoPathSegment::splitAt(qreal t) const 1236 { 1237 QPair<KoPathSegment, KoPathSegment> results; 1238 if (!isValid()) 1239 return results; 1240 1241 if (degree() == 1) { 1242 QPointF p = d->first->point() + t * (d->second->point() - d->first->point()); 1243 results.first = KoPathSegment(d->first->point(), p); 1244 results.second = KoPathSegment(p, d->second->point()); 1245 } else { 1246 QPointF newCP2, newCP1, splitP, splitCP1, splitCP2; 1247 1248 d->deCasteljau(t, &newCP2, &splitCP1, &splitP, &splitCP2, &newCP1); 1249 1250 if (degree() == 2) { 1251 if (second()->activeControlPoint1()) { 1252 KoPathPoint *s1p1 = new KoPathPoint(0, d->first->point()); 1253 KoPathPoint *s1p2 = new KoPathPoint(0, splitP); 1254 s1p2->setControlPoint1(splitCP1); 1255 KoPathPoint *s2p1 = new KoPathPoint(0, splitP); 1256 KoPathPoint *s2p2 = new KoPathPoint(0, d->second->point()); 1257 s2p2->setControlPoint1(splitCP2); 1258 results.first = KoPathSegment(s1p1, s1p2); 1259 results.second = KoPathSegment(s2p1, s2p2); 1260 } else { 1261 results.first = KoPathSegment(d->first->point(), splitCP1, splitP); 1262 results.second = KoPathSegment(splitP, splitCP2, d->second->point()); 1263 } 1264 } else { 1265 results.first = KoPathSegment(d->first->point(), newCP2, splitCP1, splitP); 1266 results.second = KoPathSegment(splitP, splitCP2, newCP1, d->second->point()); 1267 } 1268 } 1269 1270 return results; 1271 } 1272 1273 QVector<QPointF> KoPathSegment::controlPoints() const 1274 { 1275 QVector<QPointF> controlPoints; 1276 1277 controlPoints.append(d->first->point()); 1278 if (d->first->activeControlPoint2()) 1279 controlPoints.append(d->first->controlPoint2()); 1280 if (d->second->activeControlPoint1()) 1281 controlPoints.append(d->second->controlPoint1()); 1282 controlPoints.append(d->second->point()); 1283 1284 return controlPoints; 1285 } 1286 1287 qreal KoPathSegment::nearestPoint(const QPointF &point) const 1288 { 1289 if (!isValid()) 1290 return -1.0; 1291 1292 const int deg = degree(); 1293 1294 // use shortcut for line segments 1295 if (deg == 1) { 1296 // the segments chord 1297 QPointF chord = d->second->point() - d->first->point(); 1298 // the point relative to the segment 1299 QPointF relPoint = point - d->first->point(); 1300 // project point to chord (dot product) 1301 qreal scale = chord.x() * relPoint.x() + chord.y() * relPoint.y(); 1302 // normalize using the chord length 1303 scale /= chord.x() * chord.x() + chord.y() * chord.y(); 1304 1305 if (scale < 0.0) { 1306 return 0.0; 1307 } else if (scale > 1.0) { 1308 return 1.0; 1309 } else { 1310 return scale; 1311 } 1312 } 1313 1314 /* This function solves the "nearest point on curve" problem. That means, it 1315 * calculates the point q (to be precise: it's parameter t) on this segment, which 1316 * is located nearest to the input point P. 1317 * The basic idea is best described (because it is freely available) in "Phoenix: 1318 * An Interactive Curve Design System Based on the Automatic Fitting of 1319 * Hand-Sketched Curves", Philip J. Schneider (Master thesis, University of 1320 * Washington). 1321 * 1322 * For the nearest point q = C(t) on this segment, the first derivative is 1323 * orthogonal to the distance vector "C(t) - P". In other words we are looking for 1324 * solutions of f(t) = (C(t) - P) * C'(t) = 0. 1325 * (C(t) - P) is a nth degree curve, C'(t) a n-1th degree curve => f(t) is a 1326 * (2n - 1)th degree curve and thus has up to 2n - 1 distinct solutions. 1327 * We solve the problem f(t) = 0 by using something called "Approximate Inversion Method". 1328 * Let's write f(t) explicitly (with c_i = p_i - P and d_j = p_{j+1} - p_j): 1329 * 1330 * n n-1 1331 * f(t) = SUM c_i * B^n_i(t) * SUM d_j * B^{n-1}_j(t) 1332 * i=0 j=0 1333 * 1334 * n n-1 1335 * = SUM SUM w_{ij} * B^{2n-1}_{i+j}(t) 1336 * i=0 j=0 1337 * 1338 * with w_{ij} = c_i * d_j * z_{ij} and 1339 * 1340 * BinomialCoeff(n, i) * BinomialCoeff(n - i ,j) 1341 * z_{ij} = ----------------------------------------------- 1342 * BinomialCoeff(2n - 1, i + j) 1343 * 1344 * This Bernstein-Bezier polynom representation can now be solved for it's roots. 1345 */ 1346 1347 const QVector<QPointF> ctlPoints = controlPoints(); 1348 1349 // Calculate the c_i = point(i) - P. 1350 QPointF * c_i = new QPointF[ deg + 1 ]; 1351 1352 for (int i = 0; i <= deg; ++i) { 1353 c_i[ i ] = ctlPoints[ i ] - point; 1354 } 1355 1356 // Calculate the d_j = point(j + 1) - point(j). 1357 QPointF *d_j = new QPointF[deg]; 1358 1359 for (int j = 0; j <= deg - 1; ++j) { 1360 d_j[j] = 3.0 * (ctlPoints[j+1] - ctlPoints[j]); 1361 } 1362 1363 // Calculate the dot products of c_i and d_i. 1364 qreal *products = new qreal[deg * (deg + 1)]; 1365 1366 for (int j = 0; j <= deg - 1; ++j) { 1367 for (int i = 0; i <= deg; ++i) { 1368 products[j * (deg + 1) + i] = d_j[j].x() * c_i[i].x() + d_j[j].y() * c_i[i].y(); 1369 } 1370 } 1371 1372 // We don't need the c_i and d_i anymore. 1373 delete[] d_j ; 1374 delete[] c_i ; 1375 1376 // Calculate the control points of the new 2n-1th degree curve. 1377 BezierSegment newCurve; 1378 newCurve.setDegree(2 * deg - 1); 1379 // Set up control points in the (u, f(u))-plane. 1380 for (unsigned short u = 0; u <= 2 * deg - 1; ++u) { 1381 newCurve.setPoint(u, QPointF(static_cast<qreal>(u) / static_cast<qreal>(2 * deg - 1), 0.0)); 1382 } 1383 1384 // Precomputed "z" for cubics 1385 static const qreal 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}; 1386 // Precomputed "z" for quadrics 1387 static const qreal z2[2*3] = {1.0, 2./3., 1./3., 1./3., 2./3., 1.0}; 1388 1389 const qreal *z = degree() == 3 ? z3 : z2; 1390 1391 // Set f(u)-values. 1392 for (int k = 0; k <= 2 * deg - 1; ++k) { 1393 int min = qMin(k, deg); 1394 1395 for (unsigned short i = qMax(0, k - (deg - 1)); i <= min; ++i) { 1396 unsigned short j = k - i; 1397 1398 // p_k += products[j][i] * z[j][i]. 1399 QPointF currentPoint = newCurve.point(k); 1400 currentPoint.ry() += products[j * (deg + 1) + i] * z[j * (deg + 1) + i]; 1401 newCurve.setPoint(k, currentPoint); 1402 } 1403 } 1404 1405 // We don't need the c_i/d_i dot products and the z_{ij} anymore. 1406 delete[] products; 1407 1408 // Find roots. 1409 QList<qreal> rootParams = newCurve.roots(); 1410 1411 // Now compare the distances of the candidate points. 1412 1413 // First candidate is the previous knot. 1414 QPointF dist = d->first->point() - point; 1415 qreal distanceSquared = dist.x() * dist.x() + dist.y() * dist.y(); 1416 qreal oldDistanceSquared; 1417 qreal resultParam = 0.0; 1418 1419 // Iterate over the found candidate params. 1420 for (qreal root : rootParams) { 1421 dist = point - pointAt(root); 1422 oldDistanceSquared = distanceSquared; 1423 distanceSquared = dist.x() * dist.x() + dist.y() * dist.y(); 1424 1425 if (distanceSquared < oldDistanceSquared) 1426 resultParam = root; 1427 } 1428 1429 // Last candidate is the knot. 1430 dist = d->second->point() - point; 1431 oldDistanceSquared = distanceSquared; 1432 distanceSquared = dist.x() * dist.x() + dist.y() * dist.y(); 1433 1434 if (distanceSquared < oldDistanceSquared) 1435 resultParam = 1.0; 1436 1437 return resultParam; 1438 } 1439 1440 KoPathSegment KoPathSegment::interpolate(const QPointF &p0, const QPointF &p1, const QPointF &p2, qreal t) 1441 { 1442 if (t <= 0.0 || t >= 1.0) 1443 return KoPathSegment(); 1444 1445 /* 1446 B(t) = [x2 y2] = (1-t)^2*P0 + 2t*(1-t)*P1 + t^2*P2 1447 1448 B(t) - (1-t)^2*P0 - t^2*P2 1449 P1 = -------------------------- 1450 2t*(1-t) 1451 */ 1452 1453 QPointF c1 = p1 - (1.0-t) * (1.0-t)*p0 - t * t * p2; 1454 1455 qreal denom = 2.0 * t * (1.0-t); 1456 1457 c1.rx() /= denom; 1458 c1.ry() /= denom; 1459 1460 return KoPathSegment(p0, c1, p2); 1461 } 1462 1463 #if 0 1464 void KoPathSegment::printDebug() const 1465 { 1466 int deg = degree(); 1467 debugFlake << "degree:" << deg; 1468 if (deg < 1) 1469 return; 1470 1471 debugFlake << "P0:" << d->first->point(); 1472 if (deg == 1) { 1473 debugFlake << "P2:" << d->second->point(); 1474 } else if (deg == 2) { 1475 if (d->first->activeControlPoint2()) 1476 debugFlake << "P1:" << d->first->controlPoint2(); 1477 else 1478 debugFlake << "P1:" << d->second->controlPoint1(); 1479 debugFlake << "P2:" << d->second->point(); 1480 } else if (deg == 3) { 1481 debugFlake << "P1:" << d->first->controlPoint2(); 1482 debugFlake << "P2:" << d->second->controlPoint1(); 1483 debugFlake << "P3:" << d->second->point(); 1484 } 1485 } 1486 #endif