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