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

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kis_algebra_2d.h"
0008 
0009 #include <QTransform>
0010 #include <QPainterPath>
0011 #include <kis_debug.h>
0012 
0013 #include <boost/accumulators/accumulators.hpp>
0014 #include <boost/accumulators/statistics/stats.hpp>
0015 #include <boost/accumulators/statistics/min.hpp>
0016 #include <boost/accumulators/statistics/max.hpp>
0017 
0018 #include <array>
0019 #include <QVector2D>
0020 #include <QVector3D>
0021 
0022 #include <QtMath>
0023 
0024 #include <config-gsl.h>
0025 
0026 #ifdef HAVE_GSL
0027 #include <gsl/gsl_multimin.h>
0028 #endif /*HAVE_GSL*/
0029 
0030 #include <Eigen/Eigenvalues>
0031 
0032 #define SANITY_CHECKS
0033 
0034 namespace KisAlgebra2D {
0035 
0036 void adjustIfOnPolygonBoundary(const QPolygonF &poly, int polygonDirection, QPointF *pt)
0037 {
0038     const int numPoints = poly.size();
0039     for (int i = 0; i < numPoints; i++) {
0040         int nextI = i + 1;
0041         if (nextI >= numPoints) {
0042             nextI = 0;
0043         }
0044 
0045         const QPointF &p0 = poly[i];
0046         const QPointF &p1 = poly[nextI];
0047 
0048         QPointF edge = p1 - p0;
0049 
0050         qreal cross = crossProduct(edge, *pt - p0)
0051             / (0.5 * edge.manhattanLength());
0052 
0053         if (cross < 1.0 &&
0054             isInRange(pt->x(), p0.x(), p1.x()) &&
0055             isInRange(pt->y(), p0.y(), p1.y())) {
0056 
0057             QPointF salt = 1.0e-3 * inwardUnitNormal(edge, polygonDirection);
0058 
0059             QPointF adjustedPoint = *pt + salt;
0060 
0061             // in case the polygon is self-intersecting, polygon direction
0062             // might not help
0063             if (kisDistanceToLine(adjustedPoint, QLineF(p0, p1)) < 1e-4) {
0064                 adjustedPoint = *pt - salt;
0065 
0066 #ifdef SANITY_CHECKS
0067                 if (kisDistanceToLine(adjustedPoint, QLineF(p0, p1)) < 1e-4) {
0068                     dbgKrita << ppVar(*pt);
0069                     dbgKrita << ppVar(adjustedPoint);
0070                     dbgKrita << ppVar(QLineF(p0, p1));
0071                     dbgKrita << ppVar(salt);
0072 
0073                     dbgKrita << ppVar(poly.containsPoint(*pt, Qt::OddEvenFill));
0074 
0075                     dbgKrita << ppVar(kisDistanceToLine(*pt, QLineF(p0, p1)));
0076                     dbgKrita << ppVar(kisDistanceToLine(adjustedPoint, QLineF(p0, p1)));
0077                 }
0078 
0079                 *pt = adjustedPoint;
0080 
0081                 KIS_ASSERT_RECOVER_NOOP(kisDistanceToLine(*pt, QLineF(p0, p1)) > 1e-4);
0082 #endif /* SANITY_CHECKS */
0083             }
0084         }
0085     }
0086 }
0087 
0088 QPointF transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2) {
0089     qreal len1 = norm(base1);
0090     if (len1 < 1e-5) return pt;
0091     qreal sin1 = base1.y() / len1;
0092     qreal cos1 = base1.x() / len1;
0093 
0094     qreal len2 = norm(base2);
0095     if (len2 < 1e-5) return QPointF();
0096     qreal sin2 = base2.y() / len2;
0097     qreal cos2 = base2.x() / len2;
0098 
0099     qreal sinD = sin2 * cos1 - cos2 * sin1;
0100     qreal cosD = cos1 * cos2 + sin1 * sin2;
0101     qreal scaleD = len2 / len1;
0102 
0103     QPointF result;
0104     result.rx() = scaleD * (pt.x() * cosD - pt.y() * sinD);
0105     result.ry() = scaleD * (pt.x() * sinD + pt.y() * cosD);
0106 
0107     return result;
0108 }
0109 
0110 qreal angleBetweenVectors(const QPointF &v1, const QPointF &v2)
0111 {
0112     qreal a1 = std::atan2(v1.y(), v1.x());
0113     qreal a2 = std::atan2(v2.y(), v2.x());
0114 
0115     return a2 - a1;
0116 }
0117 
0118 qreal directionBetweenPoints(const QPointF &p1, const QPointF &p2, qreal defaultAngle)
0119 {
0120     if (fuzzyPointCompare(p1, p2)) {
0121         return defaultAngle;
0122     }
0123 
0124     const QVector2D diff(p2 - p1);
0125     return std::atan2(diff.y(), diff.x());
0126 }
0127 
0128 QPainterPath smallArrow()
0129 {
0130     QPainterPath p;
0131 
0132     p.moveTo(5, 2);
0133     p.lineTo(-3, 8);
0134     p.lineTo(-5, 5);
0135     p.lineTo( 2, 0);
0136     p.lineTo(-5,-5);
0137     p.lineTo(-3,-8);
0138     p.lineTo( 5,-2);
0139     p.arcTo(QRectF(3, -2, 4, 4), 90, -180);
0140 
0141     return p;
0142 }
0143 
0144 template <class Point, class Rect>
0145 inline Point ensureInRectImpl(Point pt, const Rect &bounds)
0146 {
0147     if (pt.x() > bounds.right()) {
0148         pt.rx() = bounds.right();
0149     } else if (pt.x() < bounds.left()) {
0150         pt.rx() = bounds.left();
0151     }
0152 
0153     if (pt.y() > bounds.bottom()) {
0154         pt.ry() = bounds.bottom();
0155     } else if (pt.y() < bounds.top()) {
0156         pt.ry() = bounds.top();
0157     }
0158 
0159     return pt;
0160 }
0161 
0162 QPoint ensureInRect(QPoint pt, const QRect &bounds)
0163 {
0164     return ensureInRectImpl(pt, bounds);
0165 }
0166 
0167 QPointF ensureInRect(QPointF pt, const QRectF &bounds)
0168 {
0169     return ensureInRectImpl(pt, bounds);
0170 }
0171 
0172 bool intersectLineRect(QLineF &line, const QRect rect, bool extend)
0173 {
0174     return intersectLineRect(line, rect, extend, extend);
0175 }
0176 
0177 bool intersectLineRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond)
0178 {
0179     // using Liang-Barsky algorithm
0180     // using parametric equation for a line:
0181     // X = x1 + t(x2-x1) = x1 + t*p2
0182     // Y = y1 + t(y2-y1) = y1 + t*p4
0183     // note that we have a line *segment* here, not a line
0184     // t1 = 0, t2 = 1, no matter what points we got
0185 
0186     double p1 = -(line.x2() - line.x1());
0187     double p2 = -p1;
0188     double p3 = -(line.y2() - line.y1());
0189     double p4 = -p3;
0190 
0191     QVector<double> p = QVector<double>({p1, p2, p3, p4});
0192     QVector<double> q = QVector<double>({line.x1() - rect.x(), rect.x() + rect.width() - line.x1(), line.y1() - rect.y(), rect.y() + rect.height() - line.y1()});
0193 
0194     float tmin = extendFirst ? -std::numeric_limits<float>::infinity() : 0; // line.p1() - first point, or infinity
0195     float tmax = extendSecond ? std::numeric_limits<float>::infinity() : 1; // line.p2() - second point, or infinity
0196 
0197     for (int i = 0; i < p.length(); i++) {
0198         // now clipping
0199         if (p[i] == 0 && q[i] < 0) {
0200             // line is parallel to the boundary and outside of it
0201             return false;
0202         } else if (p[i] < 0) {
0203             // line entry point (should be tmin)
0204             double t = q[i]/p[i];
0205             if (t > tmax) {
0206                 if (extendSecond) {
0207                     tmax = t;
0208                 } else {
0209                     return false;
0210                 }
0211             }
0212             if (t > tmin) {
0213                 tmin = t;
0214             }
0215 
0216 
0217         } else if (p[i] > 0) {
0218             // line leaving point (should be tmax)
0219             double t = q[i]/p[i];
0220             if (t < tmin) {
0221                 if (extendFirst) {
0222                     tmin = t;
0223                 } else {
0224                     return false;
0225                 }
0226             }
0227             if (t < tmax) {
0228                 tmax = t;
0229             }
0230         }
0231     }
0232 
0233     QPointF pt1 = line.p1();
0234     QPointF pt2 = line.p2();
0235 
0236     if (extendFirst || tmin > 0) {
0237         pt1 = QPointF(line.x1() + tmin*(line.x2() - line.x1()), line.y1() + tmin*(line.y2() - line.y1()));
0238     }
0239     if (extendSecond || tmax < 1) {
0240         pt2 = QPointF(line.x1() + tmax*(line.x2() - line.x1()), line.y1() + tmax*(line.y2() - line.y1()));
0241     }
0242 
0243     line.setP1(pt1);
0244     line.setP2(pt2);
0245 
0246     return true;
0247 
0248 }
0249 
0250 bool intersectLineConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
0251 {
0252     qreal epsilon = 1e-07;
0253 
0254     if (polygon.size() == 0) {
0255         return false;
0256     }
0257 
0258     // trivial case: no extends, all points inside the polygon
0259     if (!extendFirst && !extendSecond && polygon.containsPoint(line.p1(), Qt::WindingFill) && polygon.containsPoint(line.p2(), Qt::WindingFill)) {
0260         return true;
0261     }
0262 
0263     // Cyrus-Beck algorithm: https://en.wikipedia.org/wiki/Cyrus%E2%80%93Beck_algorithm
0264 
0265     // parametric equaltion for the line:
0266     // p(t) = t*P1 + (1-t)*P2
0267 
0268     // we can't use infinity here, because the points would all end up as (+/- inf, +/- inf)
0269     // which would be useless if we have a very specific direction
0270     // hence we use just "a big number"
0271     float aBigNumber = 100000; // that will keep t*Px still on the line // this is LIMITATION of the algorithm
0272     float tmin = extendFirst ? -aBigNumber : 0; // line.p1() - first point, or infinity
0273     float tmax = extendSecond ? aBigNumber : 1; // line.p2() - second point, or infinity
0274 
0275 
0276 
0277     QList<QLineF> clippingLines;
0278     QList<QPointF> normals;
0279 
0280     bool clockwise = false;
0281     {
0282         // only temporary values to check whether the polygon is clockwise or counterclockwise
0283         QPointF vec1 = polygon[1] - polygon[0];
0284         QPointF vec2 = polygon[2] - polygon[0];
0285 
0286         QLineF line1(QPointF(0, 0), vec1);
0287         QLineF line2(QPointF(0, 0), vec2);
0288 
0289         qreal angle = line1.angleTo(line2); // range: <0, 360) // <0, 180) means counter-clockwise
0290         if (angle >= 180) {
0291             clockwise = true;
0292         }
0293     }
0294 
0295 
0296     for (int i = 0; i < polygon.size() - 1; i++) {
0297         clippingLines.append(QLineF(polygon[i], polygon[i + 1]));
0298         float xdiff = polygon[i].x() - polygon[i + 1].x();
0299         float ydiff = polygon[i].y() - polygon[i + 1].y();
0300 
0301         if (!clockwise) {
0302             normals.append(QPointF(ydiff, -xdiff));
0303         } else {
0304             normals.append(QPointF(-ydiff, xdiff));
0305         }
0306 
0307     }
0308 
0309     if (!polygon.isClosed()) {
0310         int i = polygon.size() - 1;
0311         clippingLines.append(QLineF(polygon[i], polygon[0]));
0312         float xdiff = polygon[i].x() - polygon[0].x();
0313         float ydiff = polygon[i].y() - polygon[0].y();
0314 
0315         if (!clockwise) {
0316             normals.append(QPointF(ydiff, -xdiff));
0317         } else {
0318             normals.append(QPointF(-ydiff, xdiff));
0319         }
0320 
0321     }
0322 
0323     QPointF lineVec = line.p2() - line.p1();
0324     // ax + by + c = 0
0325     // c = -ax - by
0326 //    qreal cFromStandardEquation = -lineVec.x()*line.p1().x() - lineVec.y()*line.p1().y();
0327 
0328 
0329     QPointF p1 = line.p1();
0330     QPointF p2 = line.p2();
0331 
0332     qreal pA, pB, pC; // standard equation for the line: ax + by + c = 0
0333     if (qAbs(lineVec.x()) < epsilon) {
0334         // x1 ~= x2, line looks like: x = -pC
0335         pB = 0;
0336         pA = 1;
0337         pC = -p1.x();
0338     } else {
0339         pB = 1; // let b = 1 to simplify
0340         qreal tmp = (p1.y() - p2.y())/(p1.x() - p2.x());
0341         pA = -tmp;
0342         pC = tmp * p1.x() - p1.y();
0343     }
0344 
0345 
0346     int pEFound = 0;
0347 
0348 
0349     for (int i = 0; i < clippingLines.size(); i++) {
0350 
0351         // is the clipping line parallel to the line?
0352         QPointF clipVec = clippingLines[i].p2() - clippingLines[i].p1();
0353 
0354         if (qFuzzyCompare(clipVec.x()*lineVec.y(), clipVec.y()*lineVec.x())) {
0355 
0356             // vectors are parallel
0357             // let's check if one of the clipping points is on the line (extended) to see if it's the same line; if not, proceed normally
0358 
0359             qreal distanceBetweenLines = qAbs(pA*clippingLines[i].p1().x() + pB*clippingLines[i].p1().y() + pC)
0360                     /(sqrt(pA*pA + pB*pB));
0361 
0362 
0363             if (qAbs(distanceBetweenLines) < epsilon) {
0364                 // they are the same line
0365 
0366                 qreal t1;
0367                 qreal t2;
0368 
0369                 if (qAbs(p2.x() - p1.x()) > epsilon) {
0370                     t1 = (clippingLines[i].p1().x() - p1.x())/(p2.x() - p1.x());
0371                     t2 = (clippingLines[i].p2().x() - p1.x())/(p2.x() - p1.x());
0372                 } else {
0373                     t1 = (clippingLines[i].p1().y() - p1.y())/(p2.y() - p1.y());
0374                     t2 = (clippingLines[i].p2().y() - p1.y())/(p2.y() - p1.y());
0375                 }
0376 
0377                 qreal tmin1 = qMin(t1, t2);
0378                 qreal tmax2 = qMax(t1, t2);
0379 
0380 
0381 
0382                 if (tmin < tmin1) {
0383                     tmin = tmin1;
0384                 }
0385                 if (tmax > tmax2) {
0386                     tmax = tmax2;
0387                 }
0388                 continue;
0389             }
0390             else {
0391                 // if clipping line points are P1 and P2, and some other point is P(t) (given by parametric equation),
0392                 // and N is the normal vector
0393                 // and one of the points of the line we're intersecting is K,
0394                 // then we have a following equation:
0395                 // K = P(t) + a * N
0396                 // where t and a are unknown.
0397                 // after simplifying we get:
0398                 // t*(p2 - p1) + a*(n) = k - p1
0399                 // and since we have two axis, we get a linear system of two equations
0400                 // A * [t; a] = B
0401 
0402                 Eigen::Matrix2f A;
0403                 Eigen::Vector2f b;
0404                 A << p2.x() - p1.x(), normals[i].x(),
0405                      p2.y() - p1.y(), normals[i].y();
0406                 b << clippingLines[i].p1().x() - p1.x(),
0407                      clippingLines[i].p1().y() - p1.y();
0408 
0409                 Eigen::Vector2f ta = A.colPivHouseholderQr().solve(b);
0410 
0411                 qreal tt = ta(1);
0412                 if (tt < 0) {
0413                     return false;
0414                 }
0415             }
0416 
0417         }
0418 
0419 
0420         boost::optional<QPointF> pEOptional = intersectLines(clippingLines[i], line); // bounded, unbounded
0421 
0422 
0423         if (pEOptional) {
0424             pEFound++;
0425 
0426             QPointF pE = pEOptional.value();
0427             QPointF n = normals[i];
0428 
0429             QPointF A = line.p2() - line.p1();
0430             QPointF B = line.p1() - pE;
0431 
0432             qreal over = (n.x()*B.x() + n.y()*B.y());
0433             qreal under = (n.x()*A.x() + n.y()*A.y());
0434             qreal t = -over/under;
0435 
0436 //            if (pE.x() != p2.x()) {
0437 //                qreal maybet = (pE.x() - p1.x())/(p2.x() - p1.x());
0438 //            }
0439 
0440             qreal tminvalue = tmin * under + over;
0441             qreal tmaxvalue = tmax * under + over;
0442 
0443             if (tminvalue > 0 && tmaxvalue > 0) {
0444                 // both outside of the edge
0445                 return false;
0446             }
0447             if (tminvalue <= 0 && tmaxvalue <= 0) {
0448                 // noop, both inside
0449             }
0450             if (tminvalue*tmaxvalue < 0) {
0451 
0452                 // on two different sides
0453                 if (tminvalue > 0) {
0454                     // tmin outside, tmax inside
0455                     if (t > tmin) {
0456                         tmin = t;
0457                     }
0458                 } else {
0459                     if (t < tmax) {
0460                         tmax = t;
0461                     }
0462                 }
0463             }
0464 
0465 
0466 
0467             if (tmax < tmin) {
0468                 return false;
0469             }
0470         }
0471         else {
0472         }
0473 
0474     }
0475 
0476     if (pEFound == 0) {
0477         return false;
0478     }
0479 
0480     QLineF response = QLineF(tmin*line.p2() + (1-tmin)*line.p1(), tmax*line.p2() + (1-tmax)*line.p1());
0481     line = response;
0482     return true;
0483 }
0484 
0485     template <class Rect, class Point>
0486     QVector<Point> sampleRectWithPoints(const Rect &rect)
0487     {
0488         QVector<Point> points;
0489 
0490         Point m1 = 0.5 * (rect.topLeft() + rect.topRight());
0491         Point m2 = 0.5 * (rect.bottomLeft() + rect.bottomRight());
0492 
0493         points << rect.topLeft();
0494         points << m1;
0495         points << rect.topRight();
0496 
0497         points << 0.5 * (rect.topLeft() + rect.bottomLeft());
0498         points << 0.5 * (m1 + m2);
0499         points << 0.5 * (rect.topRight() + rect.bottomRight());
0500 
0501         points << rect.bottomLeft();
0502         points << m2;
0503         points << rect.bottomRight();
0504 
0505         return points;
0506     }
0507 
0508     QVector<QPoint> sampleRectWithPoints(const QRect &rect)
0509     {
0510         return sampleRectWithPoints<QRect, QPoint>(rect);
0511     }
0512 
0513     QVector<QPointF> sampleRectWithPoints(const QRectF &rect)
0514     {
0515         return sampleRectWithPoints<QRectF, QPointF>(rect);
0516     }
0517 
0518 
0519     template <class Rect, class Point, bool alignPixels>
0520     Rect approximateRectFromPointsImpl(const QVector<Point> &points)
0521     {
0522         using namespace boost::accumulators;
0523         accumulator_set<qreal, stats<tag::min, tag::max > > accX;
0524         accumulator_set<qreal, stats<tag::min, tag::max > > accY;
0525 
0526         Q_FOREACH (const Point &pt, points) {
0527             accX(pt.x());
0528             accY(pt.y());
0529         }
0530 
0531         Rect resultRect;
0532 
0533         if (alignPixels) {
0534             resultRect.setCoords(std::floor(min(accX)), std::floor(min(accY)),
0535                                  std::ceil(max(accX)), std::ceil(max(accY)));
0536         } else {
0537             resultRect.setCoords(min(accX), min(accY),
0538                                  max(accX), max(accY));
0539         }
0540 
0541         return resultRect;
0542     }
0543 
0544     QRect approximateRectFromPoints(const QVector<QPoint> &points)
0545     {
0546         return approximateRectFromPointsImpl<QRect, QPoint, true>(points);
0547     }
0548 
0549     QRectF approximateRectFromPoints(const QVector<QPointF> &points)
0550     {
0551         return approximateRectFromPointsImpl<QRectF, QPointF, false>(points);
0552     }
0553 
0554     QRect approximateRectWithPointTransform(const QRect &rect, std::function<QPointF(QPointF)> func)
0555     {
0556         QVector<QPoint> points = sampleRectWithPoints(rect);
0557 
0558         using namespace boost::accumulators;
0559         accumulator_set<qreal, stats<tag::min, tag::max > > accX;
0560         accumulator_set<qreal, stats<tag::min, tag::max > > accY;
0561 
0562         Q_FOREACH (const QPoint &pt, points) {
0563             QPointF dstPt = func(pt);
0564 
0565             accX(dstPt.x());
0566             accY(dstPt.y());
0567         }
0568 
0569         QRect resultRect;
0570         resultRect.setCoords(std::floor(min(accX)), std::floor(min(accY)),
0571                              std::ceil(max(accX)), std::ceil(max(accY)));
0572 
0573         return resultRect;
0574     }
0575 
0576 
0577 QRectF cutOffRect(const QRectF &rc, const KisAlgebra2D::RightHalfPlane &p)
0578 {
0579     QVector<QPointF> points;
0580 
0581     const QLineF cutLine = p.getLine();
0582 
0583     points << rc.topLeft();
0584     points << rc.topRight();
0585     points << rc.bottomRight();
0586     points << rc.bottomLeft();
0587 
0588     QPointF p1 = points[3];
0589     bool p1Valid = p.pos(p1) >= 0;
0590 
0591     QVector<QPointF> resultPoints;
0592 
0593     for (int i = 0; i < 4; i++) {
0594         const QPointF p2 = points[i];
0595         const bool p2Valid = p.pos(p2) >= 0;
0596 
0597         if (p1Valid != p2Valid) {
0598             QPointF intersection;
0599             cutLine.intersect(QLineF(p1, p2), &intersection);
0600             resultPoints << intersection;
0601         }
0602 
0603         if (p2Valid) {
0604             resultPoints << p2;
0605         }
0606 
0607         p1 = p2;
0608         p1Valid = p2Valid;
0609     }
0610 
0611     return approximateRectFromPoints(resultPoints);
0612 }
0613 
0614 int quadraticEquation(qreal a, qreal b, qreal c, qreal *x1, qreal *x2)
0615 {
0616     int numSolutions = 0;
0617 
0618     const qreal D = pow2(b) - 4 * a * c;
0619     const qreal eps = 1e-14;
0620 
0621     if (qAbs(D) <= eps) {
0622         *x1 = -b / (2 * a);
0623         numSolutions = 1;
0624     } else if (D < 0) {
0625         return 0;
0626     } else {
0627         const qreal sqrt_D = std::sqrt(D);
0628 
0629         *x1 = (-b + sqrt_D) / (2 * a);
0630         *x2 = (-b - sqrt_D) / (2 * a);
0631         numSolutions = 2;
0632     }
0633 
0634     return numSolutions;
0635 }
0636 
0637 QVector<QPointF> intersectTwoCircles(const QPointF &center1, qreal r1,
0638                                      const QPointF &center2, qreal r2)
0639 {
0640     QVector<QPointF> points;
0641 
0642     const QPointF diff = (center2 - center1);
0643     const QPointF c1;
0644     const QPointF c2 = diff;
0645 
0646     const qreal centerDistance = norm(diff);
0647 
0648     if (centerDistance > r1 + r2) return points;
0649     if (centerDistance < qAbs(r1 - r2)) return points;
0650 
0651     if (centerDistance < qAbs(r1 - r2) + 0.001) {
0652         dbgKrita << "Skipping intersection" << ppVar(center1) << ppVar(center2) << ppVar(r1) << ppVar(r2) << ppVar(centerDistance) << ppVar(qAbs(r1-r2));
0653         return points;
0654     }
0655 
0656     const qreal x_kp1 = diff.x();
0657     const qreal y_kp1 = diff.y();
0658 
0659     const qreal F2 =
0660         0.5 * (pow2(x_kp1) +
0661                pow2(y_kp1) + pow2(r1) - pow2(r2));
0662 
0663     const qreal eps = 1e-6;
0664 
0665     if (qAbs(diff.y()) < eps) {
0666         qreal x = F2 / diff.x();
0667         qreal y1, y2;
0668         int result = KisAlgebra2D::quadraticEquation(
0669             1, 0,
0670             pow2(x) - pow2(r2),
0671             &y1, &y2);
0672 
0673         KIS_SAFE_ASSERT_RECOVER(result > 0) { return points; }
0674 
0675         if (result == 1) {
0676             points << QPointF(x, y1);
0677         } else if (result == 2) {
0678             KisAlgebra2D::RightHalfPlane p(c1, c2);
0679 
0680             QPointF p1(x, y1);
0681             QPointF p2(x, y2);
0682 
0683             if (p.pos(p1) >= 0) {
0684                 points << p1;
0685                 points << p2;
0686             } else {
0687                 points << p2;
0688                 points << p1;
0689             }
0690         }
0691     } else {
0692         const qreal A = diff.x() / diff.y();
0693         const qreal C = F2 / diff.y();
0694 
0695         qreal x1, x2;
0696         int result = KisAlgebra2D::quadraticEquation(
0697             1 + pow2(A), -2 * A * C,
0698             pow2(C) - pow2(r1),
0699             &x1, &x2);
0700 
0701         KIS_SAFE_ASSERT_RECOVER(result > 0) { return points; }
0702 
0703         if (result == 1) {
0704             points << QPointF(x1, C - x1 * A);
0705         } else if (result == 2) {
0706             KisAlgebra2D::RightHalfPlane p(c1, c2);
0707 
0708             QPointF p1(x1, C - x1 * A);
0709             QPointF p2(x2, C - x2 * A);
0710 
0711             if (p.pos(p1) >= 0) {
0712                 points << p1;
0713                 points << p2;
0714             } else {
0715                 points << p2;
0716                 points << p1;
0717             }
0718         }
0719     }
0720 
0721     for (int i = 0; i < points.size(); i++) {
0722         points[i] = center1 + points[i];
0723     }
0724 
0725     return points;
0726 }
0727 
0728 QTransform mapToRect(const QRectF &rect)
0729 {
0730     return
0731         QTransform(rect.width(), 0, 0, rect.height(),
0732                    rect.x(), rect.y());
0733 }
0734 
0735 QTransform mapToRectInverse(const QRectF &rect)
0736 {
0737     return
0738         QTransform::fromTranslate(-rect.x(), -rect.y()) *
0739         QTransform::fromScale(rect.width() != 0 ? 1.0 / rect.width() : 0.0,
0740                               rect.height() != 0 ? 1.0 / rect.height() : 0.0);
0741 }
0742 
0743 bool fuzzyMatrixCompare(const QTransform &t1, const QTransform &t2, qreal delta) {
0744     return
0745             qAbs(t1.m11() - t2.m11()) < delta &&
0746             qAbs(t1.m12() - t2.m12()) < delta &&
0747             qAbs(t1.m13() - t2.m13()) < delta &&
0748             qAbs(t1.m21() - t2.m21()) < delta &&
0749             qAbs(t1.m22() - t2.m22()) < delta &&
0750             qAbs(t1.m23() - t2.m23()) < delta &&
0751             qAbs(t1.m31() - t2.m31()) < delta &&
0752             qAbs(t1.m32() - t2.m32()) < delta &&
0753             qAbs(t1.m33() - t2.m33()) < delta;
0754 }
0755 
0756 bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2)
0757 {
0758     return qFuzzyCompare(p1.x(), p2.x()) && qFuzzyCompare(p1.y(), p2.y());
0759 }
0760 
0761 
0762 bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2, qreal delta)
0763 {
0764     return qAbs(p1.x() - p2.x()) < delta && qAbs(p1.y() - p2.y()) < delta;
0765 }
0766 
0767 
0768 inline QTransform toQTransformStraight(const Eigen::Matrix3d &m)
0769 {
0770     return QTransform(m(0,0), m(0,1), m(0,2),
0771                       m(1,0), m(1,1), m(1,2),
0772                       m(2,0), m(2,1), m(2,2));
0773 }
0774 
0775 inline Eigen::Matrix3d fromQTransformStraight(const QTransform &t)
0776 {
0777     Eigen::Matrix3d m;
0778 
0779     m << t.m11() , t.m12() , t.m13()
0780         ,t.m21() , t.m22() , t.m23()
0781         ,t.m31() , t.m32() , t.m33();
0782 
0783     return m;
0784 }
0785 
0786 /********************************************************/
0787 /*             DecomposedMatix                          */
0788 /********************************************************/
0789 
0790 DecomposedMatix::DecomposedMatix()
0791 {
0792 }
0793 
0794 DecomposedMatix::DecomposedMatix(const QTransform &t0)
0795 {
0796     QTransform t(t0);
0797 
0798     QTransform projMatrix;
0799 
0800     if (t.m33() == 0.0 || t0.determinant() == 0.0) {
0801         qWarning() << "Cannot decompose matrix!" << t;
0802         valid = false;
0803         return;
0804     }
0805 
0806     if (t.type() == QTransform::TxProject) {
0807         QTransform affineTransform(t.toAffine());
0808         projMatrix = affineTransform.inverted() * t;
0809 
0810         t = affineTransform;
0811         proj[0] = projMatrix.m13();
0812         proj[1] = projMatrix.m23();
0813         proj[2] = projMatrix.m33();
0814     }
0815 
0816     // can't use QVector3D, because they have too little accuracy for ellipse in perspective calculations
0817     std::array<Eigen::Vector3d, 3> rows;
0818 
0819 
0820     //  << t.m11() << t.m12() << t.m13())
0821     rows[0] = Eigen::Vector3d(t.m11(), t.m12(), t.m13());
0822     rows[1] = Eigen::Vector3d(t.m21(), t.m22(), t.m23());
0823     rows[2] = Eigen::Vector3d(t.m31(), t.m32(), t.m33());
0824 
0825     if (!qFuzzyCompare(t.m33(), 1.0)) {
0826         const qreal invM33 = 1.0 / t.m33();
0827 
0828         for (auto &row : rows) {
0829             row *= invM33;
0830         }
0831     }
0832 
0833     dx = rows[2].x();
0834     dy = rows[2].y();
0835 
0836     rows[2] = Eigen::Vector3d(0,0,1);
0837 
0838 
0839     scaleX = rows[0].norm();
0840     rows[0] *= 1.0 / scaleX;
0841 
0842     shearXY = rows[0].dot(rows[1]);
0843     rows[1] = rows[1] - shearXY * rows[0];
0844 
0845     scaleY = rows[1].norm();
0846     rows[1] *= 1.0 / scaleY;
0847     shearXY *= 1.0 / scaleY;
0848 
0849     // If determinant is negative, one axis was flipped.
0850     qreal determinant = rows[0].x() * rows[1].y() - rows[0].y() * rows[1].x();
0851     if (determinant < 0) {
0852         // Flip axis with minimum unit vector dot product.
0853         if (rows[0].x() < rows[1].y()) {
0854             scaleX = -scaleX;
0855             rows[0] = -rows[0];
0856         } else {
0857             scaleY = -scaleY;
0858             rows[1] = -rows[1];
0859         }
0860         shearXY = - shearXY;
0861     }
0862 
0863     angle = kisRadiansToDegrees(std::atan2(rows[0].y(), rows[0].x()));
0864 
0865     if (angle != 0.0) {
0866         // Rotate(-angle) = [cos(angle), sin(angle), -sin(angle), cos(angle)]
0867         //                = [row0x, -row0y, row0y, row0x]
0868         // Thanks to the normalization above.
0869 
0870         qreal sn = -rows[0].y();
0871         qreal cs = rows[0].x();
0872         qreal m11 = rows[0].x();
0873         qreal m12 = rows[0].y();
0874         qreal m21 = rows[1].x();
0875         qreal m22 = rows[1].y();
0876         rows[0][0] = (cs * m11 + sn * m21);
0877         rows[0][1] = (cs * m12 + sn * m22);
0878         rows[1][0] = (-sn * m11 + cs * m21);
0879         rows[1][1] = (-sn * m12 + cs * m22);
0880     }
0881 
0882     QTransform leftOver(
0883                 rows[0].x(), rows[0].y(), rows[0].z(),
0884             rows[1].x(), rows[1].y(), rows[1].z(),
0885             rows[2].x(), rows[2].y(), rows[2].z());
0886 
0887     if (/*true || */!fuzzyMatrixCompare(leftOver, QTransform(), 1e-4)) {
0888         // what's wrong?
0889         ENTER_FUNCTION() << "FAILING THE ASSERT BELOW!";
0890         ENTER_FUNCTION() << ppVar(leftOver);
0891         ENTER_FUNCTION() << "matrix to decompose was: " << ppVar(t0);
0892         ENTER_FUNCTION() << ppVar(t.m33()) << ppVar(t0.determinant());
0893         Eigen::Matrix3d mat1 = fromQTransformStraight(QTransform());
0894         Eigen::Matrix3d mat2 = fromQTransformStraight(leftOver);
0895         Eigen::Matrix3d mat3 = mat2 - mat1;
0896         ENTER_FUNCTION() << mat3(0, 0) << mat3(0, 1) << mat3(0, 2);
0897         ENTER_FUNCTION() << mat3(1, 0) << mat3(1, 1) << mat3(1, 2);
0898         ENTER_FUNCTION() << mat3(2, 0) << mat3(2, 1) << mat3(2, 2);
0899         //ENTER_FUNCTION() << ppVar(mat1 - mat2);
0900     }
0901 
0902 
0903     KIS_SAFE_ASSERT_RECOVER_NOOP(fuzzyMatrixCompare(leftOver, QTransform(), 1e-4));
0904     KIS_ASSERT(fuzzyMatrixCompare(leftOver, QTransform(), 1e-4));
0905 }
0906 
0907 
0908 std::pair<QPointF, QTransform> transformEllipse(const QPointF &axes, const QTransform &fullLocalToGlobal)
0909 {
0910     KisAlgebra2D::DecomposedMatix decomposed(fullLocalToGlobal);
0911     const QTransform localToGlobal =
0912             decomposed.scaleTransform() *
0913             decomposed.shearTransform() *
0914 
0915             /* decomposed.projectTransform() * */
0916             /*decomposed.translateTransform() * */
0917 
0918             decomposed.rotateTransform();
0919 
0920     const QTransform localEllipse = QTransform(1.0 / pow2(axes.x()), 0.0, 0.0,
0921                                                0.0, 1.0 / pow2(axes.y()), 0.0,
0922                                                0.0, 0.0, 1.0);
0923 
0924 
0925     const QTransform globalToLocal = localToGlobal.inverted();
0926 
0927     Eigen::Matrix3d eqM =
0928         fromQTransformStraight(globalToLocal *
0929                                localEllipse *
0930                                globalToLocal.transposed());
0931 
0932 //    std::cout << "eqM:" << std::endl << eqM << std::endl;
0933 
0934     Eigen::EigenSolver<Eigen::Matrix3d> eigenSolver(eqM);
0935 
0936     const Eigen::Matrix3d T = eigenSolver.eigenvalues().real().asDiagonal();
0937     const Eigen::Matrix3d U = eigenSolver.eigenvectors().real();
0938 
0939     const Eigen::Matrix3d Ti = eigenSolver.eigenvalues().imag().asDiagonal();
0940     const Eigen::Matrix3d Ui = eigenSolver.eigenvectors().imag();
0941 
0942     KIS_SAFE_ASSERT_RECOVER_NOOP(Ti.isZero());
0943     KIS_SAFE_ASSERT_RECOVER_NOOP(Ui.isZero());
0944     KIS_SAFE_ASSERT_RECOVER_NOOP((U * U.transpose()).isIdentity());
0945 
0946 //    std::cout << "T:" << std::endl << T << std::endl;
0947 //    std::cout << "U:" << std::endl << U << std::endl;
0948 
0949 //    std::cout << "Ti:" << std::endl << Ti << std::endl;
0950 //    std::cout << "Ui:" << std::endl << Ui << std::endl;
0951 
0952 //    std::cout << "UTU':" << std::endl << U * T * U.transpose() << std::endl;
0953 
0954     const qreal newA = 1.0 / std::sqrt(T(0,0) * T(2,2));
0955     const qreal newB = 1.0 / std::sqrt(T(1,1) * T(2,2));
0956 
0957     const QTransform newGlobalToLocal = toQTransformStraight(U);
0958     const QTransform newLocalToGlobal = QTransform::fromScale(-1,-1) *
0959             newGlobalToLocal.inverted() *
0960             decomposed.translateTransform();
0961 
0962     return std::make_pair(QPointF(newA, newB), newLocalToGlobal);
0963 }
0964 
0965 QPointF alignForZoom(const QPointF &pt, qreal zoom)
0966 {
0967     return QPointF((pt * zoom).toPoint()) / zoom;
0968 }
0969 
0970 boost::optional<QPointF> intersectLines(const QLineF &boundedLine, const QLineF &unboundedLine)
0971 {
0972     const QPointF B1 = unboundedLine.p1();
0973     const QPointF A1 = unboundedLine.p2() - unboundedLine.p1();
0974 
0975     const QPointF B2 = boundedLine.p1();
0976     const QPointF A2 = boundedLine.p2() - boundedLine.p1();
0977 
0978     Eigen::Matrix<float, 2, 2> A;
0979     A << A1.x(), -A2.x(),
0980          A1.y(), -A2.y();
0981 
0982     Eigen::Matrix<float, 2, 1> B;
0983     B << B2.x() - B1.x(),
0984          B2.y() - B1.y();
0985 
0986     if (qFuzzyIsNull(A.determinant())) {
0987         return boost::none;
0988     }
0989 
0990     Eigen::Matrix<float, 2, 1> T;
0991 
0992     T = A.inverse() * B;
0993 
0994     const qreal t2 = T(1,0);
0995     double epsilon = 1e-06; // to avoid precision issues
0996 
0997     if (t2 < 0.0 || t2 > 1.0) {
0998         if (qAbs(t2) > epsilon && qAbs(t2 - 1.0) > epsilon) {
0999             return boost::none;
1000         }
1001     }
1002 
1003     return t2 * A2 + B2;
1004 }
1005 
1006 boost::optional<QPointF> intersectLines(const QPointF &p1, const QPointF &p2,
1007                                         const QPointF &q1, const QPointF &q2)
1008 {
1009     return intersectLines(QLineF(p1, p2), QLineF(q1, q2));
1010 }
1011 
1012 QVector<QPointF> findTrianglePoint(const QPointF &p1, const QPointF &p2, qreal a, qreal b)
1013 {
1014     using KisAlgebra2D::norm;
1015     using KisAlgebra2D::dotProduct;
1016 
1017     QVector<QPointF> result;
1018 
1019     const QPointF p = p2 - p1;
1020 
1021     const qreal pSq = dotProduct(p, p);
1022 
1023     const qreal T =  0.5 * (-pow2(b) + pow2(a) + pSq);
1024 
1025     if (p.isNull()) return result;
1026 
1027     if (qAbs(p.x()) > qAbs(p.y())) {
1028         const qreal A = 1.0;
1029         const qreal B2 = -T * p.y() / pSq;
1030         const qreal C = pow2(T) / pSq - pow2(a * p.x()) / pSq;
1031 
1032         const qreal D4 = pow2(B2) - A * C;
1033 
1034         if (D4 > 0 || qFuzzyIsNull(D4)) {
1035             if (qFuzzyIsNull(D4)) {
1036                 const qreal y = -B2 / A;
1037                 const qreal x = (T - y * p.y()) / p.x();
1038                 result << p1 + QPointF(x, y);
1039             } else {
1040                 const qreal y1 = (-B2 + std::sqrt(D4)) / A;
1041                 const qreal x1 = (T - y1 * p.y()) / p.x();
1042                 result << p1 + QPointF(x1, y1);
1043 
1044                 const qreal y2 = (-B2 - std::sqrt(D4)) / A;
1045                 const qreal x2 = (T - y2 * p.y()) / p.x();
1046                 result << p1 + QPointF(x2, y2);
1047             }
1048         }
1049     } else {
1050         const qreal A = 1.0;
1051         const qreal B2 = -T * p.x() / pSq;
1052         const qreal C = pow2(T) / pSq - pow2(a * p.y()) / pSq;
1053 
1054         const qreal D4 = pow2(B2) - A * C;
1055 
1056         if (D4 > 0 || qFuzzyIsNull(D4)) {
1057             if (qFuzzyIsNull(D4)) {
1058                 const qreal x = -B2 / A;
1059                 const qreal y = (T - x * p.x()) / p.y();
1060                 result << p1 + QPointF(x, y);
1061             } else {
1062                 const qreal x1 = (-B2 + std::sqrt(D4)) / A;
1063                 const qreal y1 = (T - x1 * p.x()) / p.y();
1064                 result << p1 + QPointF(x1, y1);
1065 
1066                 const qreal x2 = (-B2 - std::sqrt(D4)) / A;
1067                 const qreal y2 = (T - x2 * p.x()) / p.y();
1068                 result << p1 + QPointF(x2, y2);
1069             }
1070         }
1071     }
1072 
1073     return result;
1074 }
1075 
1076 boost::optional<QPointF> findTrianglePointNearest(const QPointF &p1, const QPointF &p2, qreal a, qreal b, const QPointF &nearest)
1077 {
1078     const QVector<QPointF> points = findTrianglePoint(p1, p2, a, b);
1079 
1080     boost::optional<QPointF> result;
1081 
1082     if (points.size() == 1) {
1083         result = points.first();
1084     } else if (points.size() > 1) {
1085         result = kisDistance(points.first(), nearest) < kisDistance(points.last(), nearest) ? points.first() : points.last();
1086     }
1087 
1088     return result;
1089 }
1090 
1091 QPointF moveElasticPoint(const QPointF &pt, const QPointF &base, const QPointF &newBase, const QPointF &wingA, const QPointF &wingB)
1092 {
1093     using KisAlgebra2D::norm;
1094     using KisAlgebra2D::dotProduct;
1095     using KisAlgebra2D::crossProduct;
1096 
1097     const QPointF vecL = base - pt;
1098     const QPointF vecLa = wingA - pt;
1099     const QPointF vecLb = wingB - pt;
1100 
1101     const qreal L = norm(vecL);
1102     const qreal La = norm(vecLa);
1103     const qreal Lb = norm(vecLb);
1104 
1105     const qreal sinMuA = crossProduct(vecLa, vecL) / (La * L);
1106     const qreal sinMuB = crossProduct(vecL, vecLb) / (Lb * L);
1107 
1108     const qreal cosMuA = dotProduct(vecLa, vecL) / (La * L);
1109     const qreal cosMuB = dotProduct(vecLb, vecL) / (Lb * L);
1110 
1111     const qreal dL = dotProduct(newBase - base, -vecL) / L;
1112 
1113 
1114     const qreal divB = (cosMuB + La / Lb * sinMuB * cosMuA / sinMuA) / L +
1115             (cosMuB + sinMuB * cosMuA / sinMuA) / Lb;
1116     const qreal dLb = dL / ( L * divB);
1117 
1118     const qreal divA = (cosMuA + Lb / La * sinMuA * cosMuB / sinMuB) / L +
1119             (cosMuA + sinMuA * cosMuB / sinMuB) / La;
1120     const qreal dLa = dL / ( L * divA);
1121 
1122     boost::optional<QPointF> result =
1123         KisAlgebra2D::findTrianglePointNearest(wingA, wingB, La + dLa, Lb + dLb, pt);
1124 
1125     const QPointF resultPoint = result ? *result : pt;
1126 
1127     return resultPoint;
1128 }
1129 
1130 #ifdef HAVE_GSL
1131 
1132 struct ElasticMotionData
1133 {
1134     QPointF oldBasePos;
1135     QPointF newBasePos;
1136     QVector<QPointF> anchorPoints;
1137 
1138     QPointF oldResultPoint;
1139 };
1140 
1141 double elasticMotionError(const gsl_vector * x, void *paramsPtr)
1142 {
1143     using KisAlgebra2D::norm;
1144     using KisAlgebra2D::dotProduct;
1145     using KisAlgebra2D::crossProduct;
1146 
1147     const QPointF newResultPoint(gsl_vector_get(x, 0), gsl_vector_get(x, 1));
1148 
1149     const ElasticMotionData *p = static_cast<const ElasticMotionData*>(paramsPtr);
1150 
1151     const QPointF vecL = newResultPoint - p->newBasePos;
1152     const qreal L = norm(vecL);
1153 
1154     const qreal deltaL = L - kisDistance(p->oldBasePos, p->oldResultPoint);
1155 
1156     QVector<qreal> deltaLi;
1157     QVector<qreal> Li;
1158     QVector<qreal> cosMuI;
1159     QVector<qreal> sinMuI;
1160     Q_FOREACH (const QPointF &anchorPoint, p->anchorPoints) {
1161         const QPointF vecLi = newResultPoint - anchorPoint;
1162         const qreal _Li = norm(vecLi);
1163 
1164         Li << _Li;
1165         deltaLi << _Li - kisDistance(p->oldResultPoint, anchorPoint);
1166         cosMuI << dotProduct(vecLi, vecL) / (_Li * L);
1167         sinMuI << crossProduct(vecL, vecLi) / (_Li * L);
1168     }
1169 
1170     qreal finalError = 0;
1171 
1172     qreal tangentialForceSum = 0;
1173     for (int i = 0; i < p->anchorPoints.size(); i++) {
1174         const qreal sum = deltaLi[i] * sinMuI[i] / Li[i];
1175         tangentialForceSum += sum;
1176     }
1177 
1178     finalError += pow2(tangentialForceSum);
1179 
1180     qreal normalForceSum = 0;
1181     {
1182         qreal sum = 0;
1183         for (int i = 0; i < p->anchorPoints.size(); i++) {
1184             sum += deltaLi[i] * cosMuI[i] / Li[i];
1185         }
1186         normalForceSum = (-deltaL) / L - sum;
1187     }
1188 
1189     finalError += pow2(normalForceSum);
1190 
1191     return finalError;
1192 }
1193 
1194 
1195 #endif /* HAVE_GSL */
1196 
1197 QPointF moveElasticPoint(const QPointF &pt,
1198                          const QPointF &base, const QPointF &newBase,
1199                          const QVector<QPointF> &anchorPoints)
1200 {
1201     const QPointF offset = newBase - base;
1202 
1203     QPointF newResultPoint = pt + offset;
1204 
1205 #ifdef HAVE_GSL
1206 
1207     ElasticMotionData data;
1208     data.newBasePos = newBase;
1209     data.oldBasePos = base;
1210     data.anchorPoints = anchorPoints;
1211     data.oldResultPoint = pt;
1212 
1213     const gsl_multimin_fminimizer_type *T =
1214         gsl_multimin_fminimizer_nmsimplex2;
1215     gsl_multimin_fminimizer *s = 0;
1216     gsl_vector *ss, *x;
1217     gsl_multimin_function minex_func;
1218 
1219     size_t iter = 0;
1220     int status;
1221     double size;
1222 
1223     /* Starting point */
1224     x = gsl_vector_alloc (2);
1225     gsl_vector_set (x, 0, newResultPoint.x());
1226     gsl_vector_set (x, 1, newResultPoint.y());
1227 
1228     /* Set initial step sizes to 0.1 */
1229     ss = gsl_vector_alloc (2);
1230     gsl_vector_set (ss, 0, 0.1 * offset.x());
1231     gsl_vector_set (ss, 1, 0.1 * offset.y());
1232 
1233     /* Initialize method and iterate */
1234     minex_func.n = 2;
1235     minex_func.f = elasticMotionError;
1236     minex_func.params = (void*)&data;
1237 
1238     s = gsl_multimin_fminimizer_alloc (T, 2);
1239     gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
1240 
1241     do
1242     {
1243         iter++;
1244         status = gsl_multimin_fminimizer_iterate(s);
1245 
1246         if (status)
1247             break;
1248 
1249         size = gsl_multimin_fminimizer_size (s);
1250         status = gsl_multimin_test_size (size, 1e-6);
1251 
1252         /**
1253          * Sometimes the algorithm may converge to a wrond point,
1254          * then just try to force it search better or return invalid
1255          * result.
1256          */
1257         if (status == GSL_SUCCESS && elasticMotionError(s->x, &data) > 0.5) {
1258             status = GSL_CONTINUE;
1259         }
1260 
1261         if (status == GSL_SUCCESS)
1262         {
1263 //             qDebug() << "*******Converged to minimum";
1264 //             qDebug() << gsl_vector_get (s->x, 0)
1265 //                      << gsl_vector_get (s->x, 1)
1266 //                      << "|" << s->fval << size;
1267 //             qDebug() << ppVar(iter);
1268 
1269              newResultPoint.rx() = gsl_vector_get (s->x, 0);
1270              newResultPoint.ry() = gsl_vector_get (s->x, 1);
1271         }
1272     }
1273     while (status == GSL_CONTINUE && iter < 10000);
1274 
1275     if (status != GSL_SUCCESS) {
1276         ENTER_FUNCTION() << "failed to find point" << ppVar(pt) << ppVar(base) << ppVar(newBase);
1277     }
1278 
1279     gsl_vector_free(x);
1280     gsl_vector_free(ss);
1281     gsl_multimin_fminimizer_free (s);
1282 #endif
1283 
1284     return newResultPoint;
1285 }
1286 
1287 void cropLineToRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond)
1288 {
1289     bool intersects = intersectLineRect(line, rect, extendFirst, extendSecond);
1290     if (!intersects) {
1291         line = QLineF(); // empty line to help with drawing
1292     }
1293 }
1294 
1295 void cropLineToConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
1296 {
1297     bool intersects = intersectLineConvexPolygon(line, polygon, extendFirst, extendSecond);
1298     if (!intersects) {
1299         line = QLineF(); // empty line to help with drawing
1300     }
1301 }
1302 
1303 
1304 qreal findMinimumGoldenSection(std::function<qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter = 100)
1305 {
1306     // requirements:
1307     // only one local minimum between xA and xB
1308 
1309     int i = 0;
1310     const double phi = 0.618033988749894; // 1/(golden ratio)
1311     qreal a = xA;
1312     qreal b = xB;
1313     qreal c = b - (b - a)*phi;
1314     qreal d = a + (b - a)*phi;
1315 
1316     while (qAbs(b - a) > eps) {
1317         if (f(c) < f(d)) {
1318             b = d;
1319         } else {
1320             a = c;
1321         }
1322 
1323         // Wikipedia says to recompute both c and d here to avoid loss of precision which may lead to incorrect results or infinite loop
1324         c = b - (b - a) * phi;
1325         d = a + (b - a) * phi;
1326 
1327         i++;
1328         if (i > maxIter) {
1329             break;
1330         }
1331 
1332     }
1333 
1334     return (b + a)/2;
1335 }
1336 
1337 qreal findMinimumTernarySection(std::function<qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter = 100)
1338 {
1339     // requirements:
1340     // only one local minimum between xA and xB
1341 
1342     int i = 0;
1343     qreal l = qMin(xA, xB);
1344     qreal r = qMax(xA, xB);
1345 
1346 
1347     qreal m1 = l + (r - l)/3;
1348     qreal m2 = r - (r - l)/3;
1349 
1350     while ((r - l) > eps) {
1351         qreal f1 = f(m1);
1352         qreal f2 = f(m2);
1353 
1354         if (f1 > f2) {
1355             l = m1;
1356         } else {
1357             r = m2;
1358         }
1359 
1360         // In Golden Section, Wikipedia says to recompute both c and d here to avoid loss of precision which may lead to incorrect results or infinite loop
1361         // so it's probably a good idea to do it here too
1362         m1 = l + (r - l)/3;
1363         m2 = r - (r - l)/3;
1364 
1365         i++;
1366 
1367         if (i > maxIter) {
1368             break;
1369         }
1370     }
1371 
1372     return (l + r)/2;
1373 }
1374 
1375 }