File indexing completed on 2024-05-12 15:57:01
0001 /* 0002 * SPDX-FileCopyrightText: 2008-2009 Jan Hambrecht <jaham@gmx.net> 0003 * SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "KisBezierUtils.h" 0009 0010 #include <tuple> 0011 #include <QStack> 0012 #include <QDebug> 0013 #include "kis_debug.h" 0014 0015 #include "KisBezierPatch.h" 0016 #include <iostream> 0017 0018 #include <config-gsl.h> 0019 0020 #ifdef HAVE_GSL 0021 #include <gsl/gsl_multimin.h> 0022 #endif 0023 0024 #include <Eigen/Dense> 0025 0026 namespace KisBezierUtils 0027 { 0028 0029 QVector<qreal> linearizeCurve(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, const qreal eps) 0030 { 0031 const qreal minStepSize = 2.0 / kisDistance(p0, p3); 0032 0033 QVector<qreal> steps; 0034 steps << 0.0; 0035 0036 0037 QStack<std::tuple<QPointF, QPointF, qreal>> stackedPoints; 0038 stackedPoints.push(std::make_tuple(p3, 3 * (p3 - p2), 1.0)); 0039 0040 QPointF lastP = p0; 0041 QPointF lastD = 3 * (p1 - p0); 0042 qreal lastT = 0.0; 0043 0044 while (!stackedPoints.isEmpty()) { 0045 QPointF p = std::get<0>(stackedPoints.top()); 0046 QPointF d = std::get<1>(stackedPoints.top()); 0047 qreal t = std::get<2>(stackedPoints.top()); 0048 0049 if (t - lastT < minStepSize || 0050 isLinearSegmentByDerivatives(lastP, lastD, p, d, eps)) { 0051 0052 lastP = p; 0053 lastD = d; 0054 lastT = t; 0055 steps << t; 0056 stackedPoints.pop(); 0057 } else { 0058 t = 0.5 * (lastT + t); 0059 p = bezierCurve(p0, p1, p2, p3, t); 0060 d = bezierCurveDeriv(p0, p1, p2, p3, t); 0061 0062 stackedPoints.push(std::make_tuple(p, d, t)); 0063 } 0064 } 0065 0066 return steps; 0067 } 0068 0069 QVector<qreal> mergeLinearizationSteps(const QVector<qreal> &a, const QVector<qreal> &b) 0070 { 0071 QVector<qreal> result; 0072 0073 std::merge(a.constBegin(), a.constEnd(), 0074 b.constBegin(), b.constEnd(), 0075 std::back_inserter(result)); 0076 result.erase( 0077 std::unique(result.begin(), result.end(), 0078 [] (qreal x, qreal y) { return qFuzzyCompare(x, y); }), 0079 result.end()); 0080 0081 return result; 0082 } 0083 0084 class BezierSegment 0085 { 0086 private: 0087 /// Maximal recursion depth for finding root params 0088 const int MaxRecursionDepth = 64; 0089 /// Flatness tolerance for finding root params 0090 const qreal FlatnessTolerance = ldexp(1.0,-MaxRecursionDepth-1); 0091 0092 public: 0093 BezierSegment(int degree = 0, QPointF *p = 0) 0094 { 0095 if (degree) { 0096 for (int i = 0; i <= degree; ++i) 0097 points.append(p[i]); 0098 } 0099 } 0100 0101 void setDegree(int degree) 0102 { 0103 points.clear(); 0104 if (degree) { 0105 for (int i = 0; i <= degree; ++i) 0106 points.append(QPointF()); 0107 } 0108 } 0109 0110 int degree() const 0111 { 0112 return points.count() - 1; 0113 } 0114 0115 QPointF point(int index) const 0116 { 0117 if (static_cast<int>(index) > degree()) 0118 return QPointF(); 0119 0120 return points[index]; 0121 } 0122 0123 void setPoint(int index, const QPointF &p) 0124 { 0125 if (static_cast<int>(index) > degree()) 0126 return; 0127 0128 points[index] = p; 0129 } 0130 0131 QPointF evaluate(qreal t, BezierSegment *left, BezierSegment *right) const 0132 { 0133 int deg = degree(); 0134 if (! deg) 0135 return QPointF(); 0136 0137 QVector<QVector<QPointF> > Vtemp(deg + 1); 0138 for (int i = 0; i <= deg; ++i) 0139 Vtemp[i].resize(deg + 1); 0140 0141 /* Copy control points */ 0142 for (int j = 0; j <= deg; j++) { 0143 Vtemp[0][j] = points[j]; 0144 } 0145 0146 /* Triangle computation */ 0147 for (int i = 1; i <= deg; i++) { 0148 for (int j =0 ; j <= deg - i; j++) { 0149 Vtemp[i][j].rx() = (1.0 - t) * Vtemp[i-1][j].x() + t * Vtemp[i-1][j+1].x(); 0150 Vtemp[i][j].ry() = (1.0 - t) * Vtemp[i-1][j].y() + t * Vtemp[i-1][j+1].y(); 0151 } 0152 } 0153 0154 if (left) { 0155 left->setDegree(deg); 0156 for (int j = 0; j <= deg; j++) { 0157 left->setPoint(j, Vtemp[j][0]); 0158 } 0159 } 0160 if (right) { 0161 right->setDegree(deg); 0162 for (int j = 0; j <= deg; j++) { 0163 right->setPoint(j, Vtemp[deg-j][j]); 0164 } 0165 } 0166 0167 return (Vtemp[deg][0]); 0168 } 0169 0170 QList<qreal> roots(int depth = 0) const 0171 { 0172 QList<qreal> rootParams; 0173 0174 if (! degree()) 0175 return rootParams; 0176 0177 // Calculate how often the control polygon crosses the x-axis 0178 // This is the upper limit for the number of roots. 0179 int xAxisCrossings = controlPolygonZeros(points); 0180 0181 if (! xAxisCrossings) { 0182 // No solutions. 0183 return rootParams; 0184 } 0185 else if (xAxisCrossings == 1) { 0186 // Exactly one solution. 0187 0188 // Stop recursion when the tree is deep enough 0189 if (depth >= MaxRecursionDepth) { 0190 // if deep enough, return 1 solution at midpoint 0191 rootParams.append((points.first().x() + points.last().x()) / 2.0); 0192 return rootParams; 0193 } 0194 else if (isFlat(FlatnessTolerance)) { 0195 // Calculate intersection of chord with x-axis. 0196 QPointF chord = points.last() - points.first(); 0197 QPointF segStart = points.first(); 0198 rootParams.append((chord.x() * segStart.y() - chord.y() * segStart.x()) / - chord.y()); 0199 return rootParams; 0200 } 0201 } 0202 0203 // Many solutions. Do recursive midpoint subdivision. 0204 BezierSegment left, right; 0205 evaluate(0.5, &left, &right); 0206 rootParams += left.roots(depth+1); 0207 rootParams += right.roots(depth+1); 0208 0209 return rootParams; 0210 } 0211 0212 static uint controlPolygonZeros(const QList<QPointF> &controlPoints) 0213 { 0214 int controlPointCount = controlPoints.count(); 0215 if (controlPointCount < 2) 0216 return 0; 0217 0218 int signChanges = 0; 0219 0220 int currSign = controlPoints[0].y() < 0.0 ? -1 : 1; 0221 int oldSign; 0222 0223 for (short i = 1; i < controlPointCount; ++i) { 0224 oldSign = currSign; 0225 currSign = controlPoints[i].y() < 0.0 ? -1 : 1; 0226 0227 if (currSign != oldSign) { 0228 ++signChanges; 0229 } 0230 } 0231 0232 0233 return signChanges; 0234 } 0235 0236 int isFlat (qreal tolerance) const 0237 { 0238 int deg = degree(); 0239 0240 // Find the perpendicular distance from each interior control point to 0241 // the line connecting points[0] and points[degree] 0242 qreal *distance = new qreal[deg + 1]; 0243 0244 // Derive the implicit equation for line connecting first and last control points 0245 qreal a = points[0].y() - points[deg].y(); 0246 qreal b = points[deg].x() - points[0].x(); 0247 qreal c = points[0].x() * points[deg].y() - points[deg].x() * points[0].y(); 0248 0249 qreal abSquared = (a * a) + (b * b); 0250 0251 for (int i = 1; i < deg; i++) { 0252 // Compute distance from each of the points to that line 0253 distance[i] = a * points[i].x() + b * points[i].y() + c; 0254 if (distance[i] > 0.0) { 0255 distance[i] = (distance[i] * distance[i]) / abSquared; 0256 } 0257 if (distance[i] < 0.0) { 0258 distance[i] = -((distance[i] * distance[i]) / abSquared); 0259 } 0260 } 0261 0262 0263 // Find the largest distance 0264 qreal max_distance_above = 0.0; 0265 qreal max_distance_below = 0.0; 0266 for (int i = 1; i < deg; i++) { 0267 if (distance[i] < 0.0) { 0268 max_distance_below = qMin(max_distance_below, distance[i]); 0269 } 0270 if (distance[i] > 0.0) { 0271 max_distance_above = qMax(max_distance_above, distance[i]); 0272 } 0273 } 0274 delete [] distance; 0275 0276 // Implicit equation for zero line 0277 qreal a1 = 0.0; 0278 qreal b1 = 1.0; 0279 qreal c1 = 0.0; 0280 0281 // Implicit equation for "above" line 0282 qreal a2 = a; 0283 qreal b2 = b; 0284 qreal c2 = c + max_distance_above; 0285 0286 qreal det = a1 * b2 - a2 * b1; 0287 qreal dInv = 1.0/det; 0288 0289 qreal intercept_1 = (b1 * c2 - b2 * c1) * dInv; 0290 0291 // Implicit equation for "below" line 0292 a2 = a; 0293 b2 = b; 0294 c2 = c + max_distance_below; 0295 0296 det = a1 * b2 - a2 * b1; 0297 dInv = 1.0/det; 0298 0299 qreal intercept_2 = (b1 * c2 - b2 * c1) * dInv; 0300 0301 // Compute intercepts of bounding box 0302 qreal left_intercept = qMin(intercept_1, intercept_2); 0303 qreal right_intercept = qMax(intercept_1, intercept_2); 0304 0305 qreal error = 0.5 * (right_intercept-left_intercept); 0306 0307 return (error < tolerance); 0308 } 0309 0310 #ifndef NDEBUG 0311 void printDebug() const 0312 { 0313 int index = 0; 0314 Q_FOREACH (const QPointF &p, points) { 0315 qDebug() << QString("P%1 ").arg(index++) << p; 0316 } 0317 } 0318 #endif 0319 0320 private: 0321 QList<QPointF> points; 0322 }; 0323 0324 qreal nearestPoint(const QList<QPointF> controlPoints, const QPointF &point, qreal *resultDistance, QPointF *resultPoint) 0325 { 0326 const int deg = controlPoints.size() - 1; 0327 0328 // use shortcut for line segments 0329 if (deg == 1) { 0330 // the segments chord 0331 QPointF chord = controlPoints.last() - controlPoints.first(); 0332 // the point relative to the segment 0333 QPointF relPoint = point - controlPoints.first(); 0334 // project point to chord (dot product) 0335 qreal scale = chord.x() * relPoint.x() + chord.y() * relPoint.y(); 0336 // normalize using the chord length 0337 scale /= chord.x() * chord.x() + chord.y() * chord.y(); 0338 0339 if (scale < 0.0) { 0340 return 0.0; 0341 } else if (scale > 1.0) { 0342 return 1.0; 0343 } else { 0344 return scale; 0345 } 0346 } 0347 0348 /* This function solves the "nearest point on curve" problem. That means, it 0349 * calculates the point q (to be precise: it's parameter t) on this segment, which 0350 * is located nearest to the input point P. 0351 * The basic idea is best described (because it is freely available) in "Phoenix: 0352 * An Interactive Curve Design System Based on the Automatic Fitting of 0353 * Hand-Sketched Curves", Philip J. Schneider (Master thesis, University of 0354 * Washington). 0355 * 0356 * For the nearest point q = C(t) on this segment, the first derivative is 0357 * orthogonal to the distance vector "C(t) - P". In other words we are looking for 0358 * solutions of f(t) = (C(t) - P) * C'(t) = 0. 0359 * (C(t) - P) is a nth degree curve, C'(t) a n-1th degree curve => f(t) is a 0360 * (2n - 1)th degree curve and thus has up to 2n - 1 distinct solutions. 0361 * We solve the problem f(t) = 0 by using something called "Approximate Inversion Method". 0362 * Let's write f(t) explicitly (with c_i = p_i - P and d_j = p_{j+1} - p_j): 0363 * 0364 * n n-1 0365 * f(t) = SUM c_i * B^n_i(t) * SUM d_j * B^{n-1}_j(t) 0366 * i=0 j=0 0367 * 0368 * n n-1 0369 * = SUM SUM w_{ij} * B^{2n-1}_{i+j}(t) 0370 * i=0 j=0 0371 * 0372 * with w_{ij} = c_i * d_j * z_{ij} and 0373 * 0374 * BinomialCoeff(n, i) * BinomialCoeff(n - i ,j) 0375 * z_{ij} = ----------------------------------------------- 0376 * BinomialCoeff(2n - 1, i + j) 0377 * 0378 * This Bernstein-Bezier polynom representation can now be solved for its roots. 0379 */ 0380 0381 QList<QPointF> ctlPoints = controlPoints; 0382 0383 // Calculate the c_i = point(i) - P. 0384 QPointF * c_i = new QPointF[ deg + 1 ]; 0385 0386 for (int i = 0; i <= deg; ++i) { 0387 c_i[ i ] = ctlPoints[ i ] - point; 0388 } 0389 0390 // Calculate the d_j = point(j + 1) - point(j). 0391 QPointF *d_j = new QPointF[deg]; 0392 0393 for (int j = 0; j <= deg - 1; ++j) { 0394 d_j[j] = 3.0 * (ctlPoints[j+1] - ctlPoints[j]); 0395 } 0396 0397 // Calculate the dot products of c_i and d_i. 0398 qreal *products = new qreal[deg * (deg + 1)]; 0399 0400 for (int j = 0; j <= deg - 1; ++j) { 0401 for (int i = 0; i <= deg; ++i) { 0402 products[j * (deg + 1) + i] = d_j[j].x() * c_i[i].x() + d_j[j].y() * c_i[i].y(); 0403 } 0404 } 0405 0406 // We don't need the c_i and d_i anymore. 0407 delete[] d_j ; 0408 delete[] c_i ; 0409 0410 // Calculate the control points of the new 2n-1th degree curve. 0411 BezierSegment newCurve; 0412 newCurve.setDegree(2 * deg - 1); 0413 // Set up control points in the (u, f(u))-plane. 0414 for (unsigned short u = 0; u <= 2 * deg - 1; ++u) { 0415 newCurve.setPoint(u, QPointF(static_cast<qreal>(u) / static_cast<qreal>(2 * deg - 1), 0.0)); 0416 } 0417 0418 // Precomputed "z" for cubics 0419 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}; 0420 // Precomputed "z" for quadrics 0421 static const qreal z2[2*3] = {1.0, 2./3., 1./3., 1./3., 2./3., 1.0}; 0422 0423 const qreal *z = deg == 3 ? z3 : z2; 0424 0425 // Set f(u)-values. 0426 for (int k = 0; k <= 2 * deg - 1; ++k) { 0427 int min = qMin(k, deg); 0428 0429 for (unsigned short i = qMax(0, k - (deg - 1)); i <= min; ++i) { 0430 unsigned short j = k - i; 0431 0432 // p_k += products[j][i] * z[j][i]. 0433 QPointF currentPoint = newCurve.point(k); 0434 currentPoint.ry() += products[j * (deg + 1) + i] * z[j * (deg + 1) + i]; 0435 newCurve.setPoint(k, currentPoint); 0436 } 0437 } 0438 0439 // We don't need the c_i/d_i dot products and the z_{ij} anymore. 0440 delete[] products; 0441 0442 // Find roots. 0443 QList<qreal> rootParams = newCurve.roots(); 0444 0445 // Now compare the distances of the candidate points. 0446 0447 // First candidate is the previous knot. 0448 qreal distanceSquared = kisSquareDistance(point, controlPoints.first()); 0449 qreal minDistanceSquared = distanceSquared; 0450 qreal resultParam = 0.0; 0451 if (resultDistance) { 0452 *resultDistance = std::sqrt(distanceSquared); 0453 } 0454 if (resultPoint) { 0455 *resultPoint = controlPoints.first(); 0456 } 0457 0458 // Iterate over the found candidate params. 0459 Q_FOREACH (qreal root, rootParams) { 0460 const QPointF rootPoint = bezierCurve(controlPoints, root); 0461 distanceSquared = kisSquareDistance(point, rootPoint); 0462 0463 if (distanceSquared < minDistanceSquared) { 0464 minDistanceSquared = distanceSquared; 0465 resultParam = root; 0466 if (resultDistance) { 0467 *resultDistance = std::sqrt(distanceSquared); 0468 } 0469 if (resultPoint) { 0470 *resultPoint = rootPoint; 0471 } 0472 } 0473 } 0474 0475 // Last candidate is the knot. 0476 distanceSquared = kisSquareDistance(point, controlPoints.last()); 0477 if (distanceSquared < minDistanceSquared) { 0478 minDistanceSquared = distanceSquared; 0479 resultParam = 1.0; 0480 if (resultDistance) { 0481 *resultDistance = std::sqrt(distanceSquared); 0482 } 0483 if (resultPoint) { 0484 *resultPoint = controlPoints.last(); 0485 } 0486 } 0487 0488 return resultParam; 0489 } 0490 0491 int controlPolygonZeros(const QList<QPointF> &controlPoints) 0492 { 0493 return static_cast<int>(BezierSegment::controlPolygonZeros(controlPoints)); 0494 } 0495 0496 namespace { 0497 0498 struct Params2D { 0499 QPointF p0, p1, p2, p3; // top curve 0500 QPointF q0, q1, q2, q3; // bottom curve 0501 QPointF r0, r1, r2, r3; // left curve 0502 QPointF s0, s1, s2, s3; // right curve 0503 0504 QPointF dstPoint; 0505 }; 0506 0507 struct LevelBasedPatchMethod 0508 { 0509 LevelBasedPatchMethod(qreal u, qreal v, const Params2D &p) 0510 { 0511 M_3 << 1, 0, 0, 0, 0512 -3, 3, 0, 0, 0513 3, -6, 3, 0, 0514 -1, 3, -3, 1; 0515 0516 M_3rel2abs << 1, 0, 0, 0, 0517 1, 1, 0, 0, 0518 0, 0, 1, 1, 0519 0, 0, 0, 1; 0520 0521 M_1 << -1, 1, 0, 0, 0522 1, -1, -1, 1; 0523 0524 PQ_left << p.p0.x(), p.p0.y(), 0525 p.p1.x(), p.p1.y(), 0526 p.q0.x(), p.q0.y(), 0527 p.q1.x(), p.q1.y(); 0528 0529 PQ_right << p.p3.x(), p.p3.y(), 0530 p.p2.x(), p.p2.y(), 0531 p.q3.x(), p.q3.y(), 0532 p.q2.x(), p.q2.y(); 0533 0534 RS_top << p.r0.x(), p.r0.y(), 0535 p.r1.x(), p.r1.y(), 0536 p.s0.x(), p.s0.y(), 0537 p.s1.x(), p.s1.y(); 0538 0539 RS_bottom << p.r3.x(), p.r3.y(), 0540 p.r2.x(), p.r2.y(), 0541 p.s3.x(), p.s3.y(), 0542 p.s2.x(), p.s2.y(); 0543 0544 R << p.r0.x(), p.r0.y(), 0545 p.r1.x(), p.r1.y(), 0546 p.r2.x(), p.r2.y(), 0547 p.r3.x(), p.r3.y(); 0548 0549 S << p.s0.x(), p.s0.y(), 0550 p.s1.x(), p.s1.y(), 0551 p.s2.x(), p.s2.y(), 0552 p.s3.x(), p.s3.y(); 0553 0554 P << p.p0.x(), p.p0.y(), 0555 p.p1.x(), p.p1.y(), 0556 p.p2.x(), p.p2.y(), 0557 p.p3.x(), p.p3.y(); 0558 0559 Q << p.q0.x(), p.q0.y(), 0560 p.q1.x(), p.q1.y(), 0561 p.q2.x(), p.q2.y(), 0562 p.q3.x(), p.q3.y(); 0563 0564 T_u3 << 1, u, pow2(u), pow3(u); 0565 T_v3 << 1, v, pow2(v), pow3(v); 0566 T_dot_u3 << 0, 1, 2 * u, 3 * pow2(u); 0567 T_dot_v3 << 0, 1, 2 * v, 3 * pow2(v); 0568 0569 T_u1 << 1, u; 0570 T_v1 << 1, v; 0571 T_dot_u1 << 0, 1; 0572 T_dot_v1 << 0, 1; 0573 } 0574 0575 Eigen::Matrix4d M_3; 0576 Eigen::Matrix4d M_3rel2abs; 0577 Eigen::Matrix<double, 2, 4> M_1; 0578 0579 0580 Eigen::Matrix<double, 4, 2> PQ_left; 0581 Eigen::Matrix<double, 4, 2> PQ_right; 0582 Eigen::Matrix<double, 4, 2> RS_top; 0583 Eigen::Matrix<double, 4, 2> RS_bottom; 0584 0585 Eigen::Matrix<double, 4, 2> R; 0586 Eigen::Matrix<double, 4, 2> S; 0587 Eigen::Matrix<double, 4, 2> P; 0588 Eigen::Matrix<double, 4, 2> Q; 0589 0590 Eigen::Matrix<double, 1, 4> T_u3; 0591 Eigen::Matrix<double, 1, 4> T_v3; 0592 Eigen::Matrix<double, 1, 4> T_dot_u3; 0593 Eigen::Matrix<double, 1, 4> T_dot_v3; 0594 0595 Eigen::Matrix<double, 1, 2> T_u1; 0596 Eigen::Matrix<double, 1, 2> T_v1; 0597 Eigen::Matrix<double, 1, 2> T_dot_u1; 0598 Eigen::Matrix<double, 1, 2> T_dot_v1; 0599 0600 QPointF value() const { 0601 Eigen::Matrix<double, 1, 2> L_1 = T_v3 * M_3 * R; 0602 Eigen::Matrix<double, 1, 2> L_2 = T_v1 * M_1 * PQ_left; 0603 Eigen::Matrix<double, 1, 2> L_3 = T_v1 * M_1 * PQ_right; 0604 Eigen::Matrix<double, 1, 2> L_4 = T_v3 * M_3 * S; 0605 0606 Eigen::Matrix<double, 4, 2> L_controls; 0607 L_controls << L_1, L_2, L_3, L_4; 0608 0609 Eigen::Matrix<double, 1, 2> L = T_u3 * M_3 * M_3rel2abs * L_controls; 0610 0611 0612 Eigen::Matrix<double, 1, 2> H_1 = T_u3 * M_3 * P; 0613 Eigen::Matrix<double, 1, 2> H_2 = T_u1 * M_1 * RS_top; 0614 Eigen::Matrix<double, 1, 2> H_3 = T_u1 * M_1 * RS_bottom; 0615 Eigen::Matrix<double, 1, 2> H_4 = T_u3 * M_3 * Q; 0616 0617 Eigen::Matrix<double, 4, 2> H_controls; 0618 H_controls << H_1, H_2, H_3, H_4; 0619 0620 Eigen::Matrix<double, 1, 2> H = T_v3 * M_3 * M_3rel2abs * H_controls; 0621 0622 Eigen::Matrix<double, 1, 2> result = 0.5 * (L + H); 0623 0624 return QPointF(result(0,0), result(0,1)); 0625 } 0626 0627 QPointF diffU() const { 0628 Eigen::Matrix<double, 1, 2> L_1 = T_v3 * M_3 * R; 0629 Eigen::Matrix<double, 1, 2> L_2 = T_v1 * M_1 * PQ_left; 0630 Eigen::Matrix<double, 1, 2> L_3 = T_v1 * M_1 * PQ_right; 0631 Eigen::Matrix<double, 1, 2> L_4 = T_v3 * M_3 * S; 0632 0633 Eigen::Matrix<double, 4, 2> L_controls; 0634 L_controls << L_1, L_2, L_3, L_4; 0635 0636 Eigen::Matrix<double, 1, 2> L = T_dot_u3 * M_3 * M_3rel2abs * L_controls; 0637 0638 0639 Eigen::Matrix<double, 1, 2> H_1 = T_dot_u3 * M_3 * P; 0640 Eigen::Matrix<double, 1, 2> H_2 = T_dot_u1 * M_1 * RS_top; 0641 Eigen::Matrix<double, 1, 2> H_3 = T_dot_u1 * M_1 * RS_bottom; 0642 Eigen::Matrix<double, 1, 2> H_4 = T_dot_u3 * M_3 * Q; 0643 0644 Eigen::Matrix<double, 4, 2> H_controls; 0645 H_controls << H_1, H_2, H_3, H_4; 0646 0647 Eigen::Matrix<double, 1, 2> H = T_v3 * M_3 * M_3rel2abs * H_controls; 0648 0649 Eigen::Matrix<double, 1, 2> result = 0.5 * (L + H); 0650 return QPointF(result(0,0), result(0,1)); 0651 } 0652 0653 QPointF diffV() const { 0654 Eigen::Matrix<double, 1, 2> L_1 = T_dot_v3 * M_3 * R; 0655 Eigen::Matrix<double, 1, 2> L_2 = T_dot_v1 * M_1 * PQ_left; 0656 Eigen::Matrix<double, 1, 2> L_3 = T_dot_v1 * M_1 * PQ_right; 0657 Eigen::Matrix<double, 1, 2> L_4 = T_dot_v3 * M_3 * S; 0658 0659 Eigen::Matrix<double, 4, 2> L_controls; 0660 L_controls << L_1, L_2, L_3, L_4; 0661 0662 Eigen::Matrix<double, 1, 2> L = T_u3 * M_3 * M_3rel2abs * L_controls; 0663 0664 Eigen::Matrix<double, 1, 2> H_1 = T_u3 * M_3 * P; 0665 Eigen::Matrix<double, 1, 2> H_2 = T_u1 * M_1 * RS_top; 0666 Eigen::Matrix<double, 1, 2> H_3 = T_u1 * M_1 * RS_bottom; 0667 Eigen::Matrix<double, 1, 2> H_4 = T_u3 * M_3 * Q; 0668 0669 Eigen::Matrix<double, 4, 2> H_controls; 0670 H_controls << H_1, H_2, H_3, H_4; 0671 0672 Eigen::Matrix<double, 1, 2> H = T_dot_v3 * M_3 * M_3rel2abs * H_controls; 0673 0674 Eigen::Matrix<double, 1, 2> result = 0.5 * (L + H); 0675 0676 return QPointF(result(0,0), result(0,1)); 0677 } 0678 0679 }; 0680 0681 struct SvgPatchMethod 0682 { 0683 private: 0684 0685 /** 0686 * TODO: optimize these function somehow! 0687 */ 0688 0689 static QPointF meshForwardMapping(qreal u, qreal v, const Params2D &p) { 0690 return p.r0 + pow3(u)*v*(p.p0 - 3*p.p1 + 3*p.p2 - p.p3 - p.q0 + 3*p.q1 - 3*p.q2 + p.q3) + pow3(u)*(-p.p0 + 3*p.p1 - 3*p.p2 + p.p3) + pow2(u)*v*(-3*p.p0 + 6*p.p1 - 3*p.p2 + 3*p.q0 - 6*p.q1 + 3*p.q2) + pow2(u)*(3*p.p0 - 6*p.p1 + 3*p.p2) + u*pow3(v)*(p.r0 - 3*p.r1 + 3*p.r2 - p.r3 - p.s0 + 3*p.s1 - 3*p.s2 + p.s3) + u*pow2(v)*(-3*p.r0 + 6*p.r1 - 3*p.r2 + 3*p.s0 - 6*p.s1+ 3*p.s2) + u*v*(2*p.p0 - 3*p.p1 + p.p3 - 2*p.q0 + 3*p.q1 - p.q3 + 3*p.r0 - 3*p.r1 - 3*p.s0 + 3*p.s1) + u*(-2*p.p0 + 3*p.p1 - p.p3 - p.r0 + p.s0) + pow3(v)*(-p.r0 + 3*p.r1 - 3*p.r2 + p.r3) + pow2(v)*(3*p.r0 - 6*p.r1 + 3*p.r2) + v*(-3*p.r0 + 3*p.r1); 0691 } 0692 0693 static QPointF meshForwardMappingDiffU(qreal u, qreal v, const Params2D &p) { 0694 return -2*p.p0 + 3*p.p1 - p.p3 - p.r0 + p.s0 + pow2(u)*v*(3*p.p0 - 9*p.p1 + 9*p.p2 - 3*p.p3 - 3*p.q0 + 9*p.q1 - 9*p.q2 + 3*p.q3) + pow2(u)*(-3*p.p0 + 9*p.p1 - 9*p.p2 + 3*p.p3) + u*v*(-6*p.p0 + 12*p.p1 - 6*p.p2 + 6*p.q0 - 12*p.q1 + 6*p.q2) + u*(6*p.p0 - 12*p.p1 + 6*p.p2) + pow3(v)*(p.r0 - 3*p.r1 + 3*p.r2 - p.r3 - p.s0 + 3*p.s1 - 3*p.s2 + p.s3) + pow2(v)*(-3*p.r0 + 6*p.r1 - 3*p.r2 + 3*p.s0 - 6*p.s1 + 3*p.s2) + v*(2*p.p0 - 3*p.p1 + p.p3 - 2*p.q0 + 3*p.q1 - p.q3 + 3*p.r0 - 3*p.r1 - 3*p.s0 + 3*p.s1); 0695 } 0696 0697 static QPointF meshForwardMappingDiffV(qreal u, qreal v, const Params2D &p) { 0698 return -3*p.r0 + 3*p.r1 + pow3(u)*(p.p0 - 3*p.p1 + 3*p.p2 - p.p3 - p.q0 + 3*p.q1 - 3*p.q2 + p.q3) + pow2(u)*(-3*p.p0 + 6*p.p1 - 3*p.p2 + 3*p.q0 - 6*p.q1 + 3*p.q2) + u*pow2(v)*(3*p.r0 - 9*p.r1 + 9*p.r2 - 3*p.r3 - 3*p.s0 + 9*p.s1 - 9*p.s2 + 3*p.s3) + u*v*(-6*p.r0 + 12*p.r1 - 6*p.r2 + 6*p.s0 - 12*p.s1 + 6*p.s2) + u*(2*p.p0 - 3*p.p1 + p.p3 - 2*p.q0 + 3*p.q1 - p.q3 + 3*p.r0 - 3*p.r1 - 3*p.s0 + 3*p.s1) + pow2(v)*(-3*p.r0 + 9*p.r1 - 9*p.r2 + 3*p.r3) + v*(6*p.r0 - 12*p.r1 + 6*p.r2); 0699 } 0700 0701 qreal u = 0.0; 0702 qreal v = 0.0; 0703 const Params2D p; 0704 0705 public: 0706 SvgPatchMethod(qreal _u, qreal _v, const Params2D &_p) 0707 : u(_u), v(_v), p(_p) 0708 { 0709 } 0710 0711 QPointF value() const { 0712 return meshForwardMapping(u, v, p); 0713 } 0714 0715 QPointF diffU() const { 0716 return meshForwardMappingDiffU(u, v, p); 0717 } 0718 0719 QPointF diffV() const { 0720 return meshForwardMappingDiffV(u, v, p); 0721 } 0722 0723 }; 0724 0725 template <class PatchMethod> 0726 double my_f(const gsl_vector * x, void *paramsPtr) 0727 { 0728 const Params2D *params = static_cast<const Params2D*>(paramsPtr); 0729 const QPointF pos(gsl_vector_get(x, 0), gsl_vector_get(x, 1)); 0730 0731 PatchMethod mat(pos.x(), pos.y(), *params); 0732 const QPointF S = mat.value(); 0733 0734 return kisSquareDistance(S, params->dstPoint); 0735 } 0736 0737 template <class PatchMethod> 0738 void my_fdf (const gsl_vector *x, void *paramsPtr, double *f, gsl_vector *df) 0739 { 0740 const Params2D *params = static_cast<const Params2D*>(paramsPtr); 0741 const QPointF pos(gsl_vector_get(x, 0), gsl_vector_get(x, 1)); 0742 0743 PatchMethod mat(pos.x(), pos.y(), *params); 0744 const QPointF S = mat.value(); 0745 const QPointF dU = mat.diffU(); 0746 const QPointF dV = mat.diffV(); 0747 0748 *f = kisSquareDistance(S, params->dstPoint); 0749 0750 gsl_vector_set(df, 0, 0751 2 * (S.x() - params->dstPoint.x()) * dU.x() + 0752 2 * (S.y() - params->dstPoint.y()) * dU.y()); 0753 gsl_vector_set(df, 1, 0754 2 * (S.x() - params->dstPoint.x()) * dV.x() + 0755 2 * (S.y() - params->dstPoint.y()) * dV.y()); 0756 } 0757 0758 template <class PatchMethod> 0759 void my_df (const gsl_vector *x, void *paramsPtr, 0760 gsl_vector *df) 0761 { 0762 const Params2D *params = static_cast<const Params2D*>(paramsPtr); 0763 const QPointF pos(gsl_vector_get(x, 0), gsl_vector_get(x, 1)); 0764 0765 PatchMethod mat(pos.x(), pos.y(), *params); 0766 const QPointF S = mat.value(); 0767 const QPointF dU = mat.diffU(); 0768 const QPointF dV = mat.diffV(); 0769 0770 gsl_vector_set(df, 0, 0771 2 * (S.x() - params->dstPoint.x()) * dU.x() + 0772 2 * (S.y() - params->dstPoint.y()) * dU.y()); 0773 gsl_vector_set(df, 1, 0774 2 * (S.x() - params->dstPoint.x()) * dV.x() + 0775 2 * (S.y() - params->dstPoint.y()) * dV.y()); 0776 } 0777 } 0778 0779 template <class PatchMethod> 0780 QPointF calculateLocalPosImpl(const std::array<QPointF, 12> &points, const QPointF &globalPoint) 0781 { 0782 QRectF patchBounds; 0783 0784 for (auto it = points.begin(); it != points.end(); ++it) { 0785 KisAlgebra2D::accumulateBounds(*it, &patchBounds); 0786 } 0787 0788 const QPointF approxStart = KisAlgebra2D::absoluteToRelative(globalPoint, patchBounds); 0789 KIS_SAFE_ASSERT_RECOVER_NOOP(QRectF(0,0,1.0,1.0).contains(approxStart)); 0790 0791 #ifdef HAVE_GSL 0792 const gsl_multimin_fdfminimizer_type *T = 0793 gsl_multimin_fdfminimizer_vector_bfgs2; 0794 gsl_multimin_fdfminimizer *s = 0; 0795 gsl_vector *x; 0796 gsl_multimin_function_fdf minex_func; 0797 0798 size_t iter = 0; 0799 int status; 0800 0801 /* Starting point */ 0802 x = gsl_vector_alloc (2); 0803 gsl_vector_set (x, 0, approxStart.x()); 0804 gsl_vector_set (x, 1, approxStart.y()); 0805 0806 Params2D p; 0807 0808 p.p0 = points[KisBezierPatch::TL]; 0809 p.p1 = points[KisBezierPatch::TL_HC]; 0810 p.p2 = points[KisBezierPatch::TR_HC]; 0811 p.p3 = points[KisBezierPatch::TR]; 0812 0813 p.q0 = points[KisBezierPatch::BL]; 0814 p.q1 = points[KisBezierPatch::BL_HC]; 0815 p.q2 = points[KisBezierPatch::BR_HC]; 0816 p.q3 = points[KisBezierPatch::BR]; 0817 0818 p.r0 = points[KisBezierPatch::TL]; 0819 p.r1 = points[KisBezierPatch::TL_VC]; 0820 p.r2 = points[KisBezierPatch::BL_VC]; 0821 p.r3 = points[KisBezierPatch::BL]; 0822 0823 p.s0 = points[KisBezierPatch::TR]; 0824 p.s1 = points[KisBezierPatch::TR_VC]; 0825 p.s2 = points[KisBezierPatch::BR_VC]; 0826 p.s3 = points[KisBezierPatch::BR]; 0827 0828 p.dstPoint = globalPoint; 0829 0830 /* Initialize method and iterate */ 0831 minex_func.n = 2; 0832 minex_func.f = my_f<PatchMethod>; 0833 minex_func.params = (void*)&p; 0834 minex_func.df = my_df<PatchMethod>; 0835 minex_func.fdf = my_fdf<PatchMethod>; 0836 0837 s = gsl_multimin_fdfminimizer_alloc (T, 2); 0838 gsl_multimin_fdfminimizer_set (s, &minex_func, x, 0.01, 0.1); 0839 0840 QPointF result; 0841 0842 0843 result.rx() = gsl_vector_get (s->x, 0); 0844 result.ry() = gsl_vector_get (s->x, 1); 0845 0846 do 0847 { 0848 iter++; 0849 status = gsl_multimin_fdfminimizer_iterate(s); 0850 0851 if (status) 0852 break; 0853 0854 status = gsl_multimin_test_gradient (s->gradient, 1e-4); 0855 0856 result.rx() = gsl_vector_get (s->x, 0); 0857 result.ry() = gsl_vector_get (s->x, 1); 0858 0859 if (status == GSL_SUCCESS) 0860 { 0861 result.rx() = gsl_vector_get (s->x, 0); 0862 result.ry() = gsl_vector_get (s->x, 1); 0863 //qDebug() << "******* Converged to minimum" << ppVar(result); 0864 0865 } 0866 } 0867 while (status == GSL_CONTINUE && iter < 10000); 0868 0869 // ENTER_FUNCTION()<< ppVar(iter) << ppVar(globalPoint) << ppVar(result); 0870 // ENTER_FUNCTION() << ppVar(meshForwardMapping(result.x(), result.y(), p)); 0871 0872 gsl_vector_free(x); 0873 gsl_multimin_fdfminimizer_free (s); 0874 0875 return result; 0876 #else 0877 return approxStart; 0878 #endif 0879 } 0880 0881 template <class PatchMethod> 0882 QPointF calculateGlobalPosImpl(const std::array<QPointF, 12> &points, const QPointF &localPoint) 0883 { 0884 Params2D p; 0885 0886 p.p0 = points[KisBezierPatch::TL]; 0887 p.p1 = points[KisBezierPatch::TL_HC]; 0888 p.p2 = points[KisBezierPatch::TR_HC]; 0889 p.p3 = points[KisBezierPatch::TR]; 0890 0891 p.q0 = points[KisBezierPatch::BL]; 0892 p.q1 = points[KisBezierPatch::BL_HC]; 0893 p.q2 = points[KisBezierPatch::BR_HC]; 0894 p.q3 = points[KisBezierPatch::BR]; 0895 0896 p.r0 = points[KisBezierPatch::TL]; 0897 p.r1 = points[KisBezierPatch::TL_VC]; 0898 p.r2 = points[KisBezierPatch::BL_VC]; 0899 p.r3 = points[KisBezierPatch::BL]; 0900 0901 p.s0 = points[KisBezierPatch::TR]; 0902 p.s1 = points[KisBezierPatch::TR_VC]; 0903 p.s2 = points[KisBezierPatch::BR_VC]; 0904 p.s3 = points[KisBezierPatch::BR]; 0905 0906 PatchMethod f(localPoint.x(), localPoint.y(), p); 0907 return f.value(); 0908 } 0909 0910 QPointF calculateLocalPos(const std::array<QPointF, 12> &points, const QPointF &globalPoint) 0911 { 0912 return calculateLocalPosImpl<LevelBasedPatchMethod>(points, globalPoint); 0913 } 0914 0915 QPointF calculateGlobalPos(const std::array<QPointF, 12> &points, const QPointF &localPoint) 0916 { 0917 return calculateGlobalPosImpl<LevelBasedPatchMethod>(points, localPoint); 0918 } 0919 0920 QPointF calculateLocalPosSVG2(const std::array<QPointF, 12> &points, const QPointF &globalPoint) 0921 { 0922 return calculateLocalPosImpl<SvgPatchMethod>(points, globalPoint); 0923 } 0924 0925 QPointF calculateGlobalPosSVG2(const std::array<QPointF, 12> &points, const QPointF &localPoint) 0926 { 0927 return calculateGlobalPosImpl<SvgPatchMethod>(points, localPoint); 0928 } 0929 0930 QPointF interpolateQuadric(const QPointF &p0, const QPointF &p2, const QPointF &pt, qreal t) 0931 { 0932 if (t <= 0.0 || t >= 1.0) 0933 return lerp(p0, p2, 0.5); 0934 0935 /* 0936 B(t) = [x2 y2] = (1-t)^2*P0 + 2t*(1-t)*P1 + t^2*P2 0937 0938 B(t) - (1-t)^2*P0 - t^2*P2 0939 P1 = -------------------------- 0940 2t*(1-t) 0941 */ 0942 0943 QPointF c1 = pt - (1.0-t) * (1.0-t)*p0 - t * t * p2; 0944 0945 qreal denom = 2.0 * t * (1.0-t); 0946 0947 c1.rx() /= denom; 0948 c1.ry() /= denom; 0949 0950 return c1; 0951 } 0952 0953 std::pair<QPointF, QPointF> offsetSegment(qreal t, const QPointF &offset) 0954 { 0955 /* 0956 * method from inkscape, original method and idea borrowed from Simon Budig 0957 * <simon@gimp.org> and the GIMP 0958 * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative() 0959 * 0960 * feel good is an arbitrary parameter that distributes the delta between handles 0961 * if t of the drag point is less than 1/6 distance form the endpoint only 0962 * the corresponding handle is adjusted. This matches the behavior in GIMP 0963 */ 0964 qreal feel_good; 0965 if (t <= 1.0 / 6.0) 0966 feel_good = 0; 0967 else if (t <= 0.5) 0968 feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2; 0969 else if (t <= 5.0 / 6.0) 0970 feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5; 0971 else 0972 feel_good = 1; 0973 0974 const QPointF moveP1 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * offset; 0975 const QPointF moveP2 = (feel_good/(3*t*t*(1-t))) * offset; 0976 0977 return std::make_pair(moveP1, moveP2); 0978 } 0979 0980 qreal curveLength(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, const qreal error) 0981 { 0982 /* 0983 * This algorithm is implemented based on an idea by Jens Gravesen: 0984 * "Adaptive subdivision and the length of Bezier curves" mat-report no. 1992-10, Mathematical Institute, 0985 * The Technical University of Denmark. 0986 * 0987 * By subdividing the curve at parameter value t you only have to find the length of a full Bezier curve. 0988 * If you denote the length of the control polygon by L1 i.e.: 0989 * L1 = |P0 P1| +|P1 P2| +|P2 P3| 0990 * 0991 * and the length of the cord by L0 i.e.: 0992 * L0 = |P0 P3| 0993 * 0994 * then 0995 * L = 1/2*L0 + 1/2*L1 0996 * 0997 * is a good approximation to the length of the curve, and the difference 0998 * ERR = L1-L0 0999 * 1000 * is a measure of the error. If the error is to large, then you just subdivide curve at parameter value 1001 * 1/2, and find the length of each half. 1002 * If m is the number of subdivisions then the error goes to zero as 2^-4m. 1003 * If you don't have a cubic curve but a curve of degree n then you put 1004 * L = (2*L0 + (n-1)*L1)/(n+1) 1005 */ 1006 1007 const int deg = bezierDegree(p0, p1, p2, p3); 1008 1009 if (deg == -1) 1010 return 0.0; 1011 1012 // calculate chord length 1013 const qreal chordLen = kisDistance(p0, p3); 1014 1015 if (deg == 1) { 1016 return chordLen; 1017 } 1018 1019 // calculate length of control polygon 1020 qreal polyLength = 0.0; 1021 1022 polyLength += kisDistance(p0, p1); 1023 polyLength += kisDistance(p1, p2); 1024 polyLength += kisDistance(p2, p3); 1025 1026 if ((polyLength - chordLen) > error) { 1027 QPointF q0, q1, q2, q3, q4; 1028 deCasteljau(p0, p1, p2, p3, 0.5, &q0, &q1, &q2, &q3, &q4); 1029 1030 return curveLength(p0, q0, q1, q2, error) + 1031 curveLength(q2, q3, q4, p3, error); 1032 } else { 1033 // the error is smaller than our tolerance 1034 if (deg == 3) 1035 return 0.5 * chordLen + 0.5 * polyLength; 1036 else 1037 return (2.0 * chordLen + polyLength) / 3.0; 1038 } 1039 } 1040 1041 qreal curveLengthAtPoint(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t, const qreal error) 1042 { 1043 QPointF q0, q1, q2, q3, q4; 1044 deCasteljau(p0, p1, p2, p3, t, &q0, &q1, &q2, &q3, &q4); 1045 1046 return curveLength(p0, q0, q1, q2, error); 1047 } 1048 1049 qreal curveParamBySegmentLength(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal expectedLength, qreal totalLength, const qreal error) 1050 { 1051 const qreal splitAtParam = expectedLength / totalLength; 1052 1053 QPointF q0, q1, q2, q3, q4; 1054 deCasteljau(p0, p1, p2, p3, splitAtParam, &q0, &q1, &q2, &q3, &q4); 1055 1056 const qreal portionLength = curveLength(p0, q0, q1, q2, error); 1057 1058 if (std::abs(portionLength - expectedLength) < error) { 1059 return splitAtParam; 1060 } else if (portionLength < expectedLength) { 1061 return splitAtParam + (1.0 - splitAtParam) * curveParamBySegmentLength(q2, q3, q4, p3, expectedLength - portionLength, totalLength - portionLength, error); 1062 } else { 1063 return splitAtParam * curveParamBySegmentLength(p0, q0, q1, q2, expectedLength, portionLength, error); 1064 } 1065 } 1066 1067 1068 qreal curveParamByProportion(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal proportion, const qreal error) 1069 { 1070 const qreal totalLength = curveLength(p0, p1, p2, p3, error); 1071 const qreal expectedLength = proportion * totalLength; 1072 1073 return curveParamBySegmentLength(p0, p1, p2, p3, expectedLength, totalLength, error); 1074 } 1075 1076 qreal curveProportionByParam(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t, const qreal error) 1077 { 1078 return curveLengthAtPoint(p0, p1, p2, p3, t, error) / curveLength(p0, p1, p2, p3, error); 1079 } 1080 1081 std::pair<QPointF, QPointF> removeBezierNode(const QPointF &p0, 1082 const QPointF &p1, 1083 const QPointF &p2, 1084 const QPointF &p3, 1085 const QPointF &q1, 1086 const QPointF &q2, 1087 const QPointF &q3) 1088 { 1089 /** 1090 * Calculates the curve control point after removal of a node 1091 * by minimizing squared error for the following problem: 1092 * 1093 * Given two consequent 3rd order curves P and Q with lengths Lp and Lq, 1094 * find 3rd order curve B, so that when splitting it at t = Lp / (Lp + Lq) 1095 * (using de Casteljau algorithm) the control points of the resulting 1096 * curves will have least square errors, compared to the corresponding 1097 * control points of curves P and Q. 1098 * 1099 * First we represent curves in matrix form: 1100 * 1101 * B(t) = T * M * B, 1102 * P(t) = T * Z1 * M * P, 1103 * Q(t) = T * Z2 * M * Q, 1104 * 1105 * where 1106 * T = [1, t, t^2, t^3] 1107 * M --- 4x4 matrix of Bezier coefficients 1108 * B, P, Q --- vector of control points for the curves 1109 * 1110 * Z1 --- conversion matrix that splits the curve into range [0.0...t] 1111 * Z2 --- conversion matrix that splits the curve into range [t...1.0] 1112 * 1113 * Then we represent vectors P and Q via B: 1114 * 1115 * P = M^(-1) * Z1 * M * B 1116 * Q = M^(-1) * Z2 * M * B 1117 * 1118 * which in block matrix form looks like: 1119 * 1120 * [ P ] [ M^(-1) * Z1 * M ] 1121 * | | = | | * B 1122 * [ Q ] [ M^(-1) * Z2 * M ] 1123 * 1124 * 1125 * [ M^(-1) * Z1 * M ] 1126 * let C = | |, 1127 * [ M^(-1) * Z2 * M ] 1128 * 1129 * [ P ] 1130 * R = | | 1131 * [ Q ] 1132 * 1133 * 1134 * then 1135 * 1136 * R = C * B 1137 * 1138 * applying normal equation to find a solution with least square error, 1139 * get the final result: 1140 * 1141 * B = (C'C)^(-1) * C' * R 1142 */ 1143 1144 const qreal lenP = KisBezierUtils::curveLength(p0, p1, p2, p3, 0.001); 1145 const qreal lenQ = KisBezierUtils::curveLength(p3, q1, q2, q3, 0.001); 1146 1147 const qreal z = lenP / (lenP + lenQ); 1148 1149 Eigen::Matrix4f M; 1150 M << 1, 0, 0, 0, 1151 -3, 3, 0, 0, 1152 3, -6, 3, 0, 1153 -1, 3, -3, 1; 1154 1155 Eigen::DiagonalMatrix<float, 4> Z_1; 1156 Z_1.diagonal() << 1, z, pow2(z), pow3(z); 1157 1158 Eigen::Matrix4f Z_2; 1159 Z_2 << 1, z, pow2(z), pow3(z), 1160 0, 1 - z, 2 * z * (1 - z), 3 * pow2(z) * (1 - z), 1161 0, 0, pow2(1 - z), 3 * z * pow2(1 - z), 1162 0, 0, 0, pow3(1 - z); 1163 1164 Eigen::Matrix<float, 8, 2> R; 1165 R << p0.x(), p0.y(), 1166 p1.x(), p1.y(), 1167 p2.x(), p2.y(), 1168 p3.x(), p3.y(), 1169 p3.x(), p3.y(), 1170 q1.x(), q1.y(), 1171 q2.x(), q2.y(), 1172 q3.x(), q3.y(); 1173 1174 Eigen::Matrix<float, 2, 2> B_const; 1175 B_const << p0.x(), p0.y(), 1176 q3.x(), q3.y(); 1177 1178 1179 Eigen::Matrix4f M1 = M.inverse() * Z_1 * M; 1180 Eigen::Matrix4f M2 = M.inverse() * Z_2 * M; 1181 1182 Eigen::Matrix<float, 8, 4> C; 1183 C << M1, M2; 1184 1185 Eigen::Matrix<float, 8, 2> C_const; 1186 C_const << C.col(0), C.col(3); 1187 1188 Eigen::Matrix<float, 8, 2> C_var; 1189 C_var << C.col(1), C.col(2); 1190 1191 Eigen::Matrix<float, 8, 2> R_var; 1192 R_var = R - C_const * B_const; 1193 1194 Eigen::Matrix<float, 6, 2> R_reduced; 1195 R_reduced = R_var.block(1, 0, 6, 2); 1196 1197 Eigen::Matrix<float, 6, 2> C_reduced; 1198 C_reduced = C_var.block(1, 0, 6, 2); 1199 1200 Eigen::Matrix<float, 2, 2> result; 1201 result = (C_reduced.transpose() * C_reduced).inverse() * C_reduced.transpose() * R_reduced; 1202 1203 QPointF resultP0(result(0, 0), result(0, 1)); 1204 QPointF resultP1(result(1, 0), result(1, 1)); 1205 1206 return std::make_pair(resultP0, resultP1); 1207 } 1208 QVector<qreal> intersectWithLineImpl(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, qreal eps, qreal alpha, qreal beta) 1209 { 1210 using KisAlgebra2D::intersectLines; 1211 1212 QVector<qreal> result; 1213 1214 const qreal length = 1215 kisDistance(p0, p1) + 1216 kisDistance(p1, p2) + 1217 kisDistance(p2, p3); 1218 1219 if (length < eps) { 1220 if (intersectLines(p0, p3, line.p1(), line.p2())) { 1221 result << alpha * 0.5 + beta; 1222 } 1223 } else { 1224 1225 const bool hasIntersections = 1226 intersectLines(p0, p1, line.p1(), line.p2()) || 1227 intersectLines(p1, p2, line.p1(), line.p2()) || 1228 intersectLines(p2, p3, line.p1(), line.p2()); 1229 1230 if (hasIntersections) { 1231 QPointF q0, q1, q2, q3, q4; 1232 1233 deCasteljau(p0, p1, p2, p3, 0.5, &q0, &q1, &q2, &q3, &q4); 1234 1235 result << intersectWithLineImpl(p0, q0, q1, q2, line, eps, 0.5 * alpha, beta); 1236 result << intersectWithLineImpl(q2, q3, q4, p3, line, eps, 0.5 * alpha, beta + 0.5 * alpha); 1237 } 1238 } 1239 return result; 1240 } 1241 1242 QVector<qreal> intersectWithLine(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, qreal eps) 1243 { 1244 return intersectWithLineImpl(p0, p1, p2, p3, line, eps, 1.0, 0.0); 1245 } 1246 1247 boost::optional<qreal> intersectWithLineNearest(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, const QPointF &nearestAnchor, qreal eps) 1248 { 1249 QVector<qreal> result = intersectWithLine(p0, p1, p2, p3, line, eps); 1250 1251 qreal minDistance = std::numeric_limits<qreal>::max(); 1252 boost::optional<qreal> nearestRoot; 1253 1254 Q_FOREACH (qreal root, result) { 1255 const QPointF pt = bezierCurve(p0, p1, p2, p3, root); 1256 const qreal distance = kisDistance(pt, nearestAnchor); 1257 1258 if (distance < minDistance) { 1259 minDistance = distance; 1260 nearestRoot = root; 1261 } 1262 } 1263 1264 return nearestRoot; 1265 } 1266 1267 }