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 #ifndef __KIS_ALGEBRA_2D_H
0008 #define __KIS_ALGEBRA_2D_H
0009 
0010 #include <QPoint>
0011 #include <QPointF>
0012 #include <QVector>
0013 #include <QPolygonF>
0014 #include <QTransform>
0015 #include <cmath>
0016 #include <kis_global.h>
0017 #include <kritaglobal_export.h>
0018 #include <functional>
0019 #include <boost/optional.hpp>
0020 
0021 class QPainterPath;
0022 class QTransform;
0023 
0024 namespace KisAlgebra2D {
0025 
0026 template <class T>
0027 struct PointTypeTraits
0028 {
0029 };
0030 
0031 template <>
0032 struct PointTypeTraits<QPoint>
0033 {
0034     typedef int value_type;
0035     typedef qreal calculation_type;
0036     typedef QRect rect_type;
0037 };
0038 
0039 template <>
0040 struct PointTypeTraits<QPointF>
0041 {
0042     typedef qreal value_type;
0043     typedef qreal calculation_type;
0044     typedef QRectF rect_type;
0045 };
0046 
0047 
0048 template <class T>
0049 typename PointTypeTraits<T>::value_type dotProduct(const T &a, const T &b)
0050 {
0051     return a.x() * b.x() + a.y() * b.y();
0052 }
0053 
0054 template <class T>
0055 typename PointTypeTraits<T>::value_type crossProduct(const T &a, const T &b)
0056 {
0057     return a.x() * b.y() - a.y() * b.x();
0058 }
0059 
0060 template <class T>
0061 qreal norm(const T &a)
0062 {
0063     return std::sqrt(pow2(a.x()) + pow2(a.y()));
0064 }
0065 
0066 template <class Point>
0067 Point normalize(const Point &a)
0068 {
0069     const qreal length = norm(a);
0070     return (1.0 / length) * a;
0071 }
0072 
0073 template <typename Point>
0074 Point lerp(const Point &pt1, const Point &pt2, qreal t)
0075 {
0076     return pt1 + (pt2 - pt1) * t;
0077 }
0078 
0079 /**
0080  * Usual sign() function with positive zero
0081  */
0082 template <typename T>
0083 T signPZ(T x) {
0084     return x >= T(0) ? T(1) : T(-1);
0085 }
0086 
0087 /**
0088  * Usual sign() function with zero returning zero
0089  */
0090 template <typename T>
0091 T signZZ(T x) {
0092     return x == T(0) ? T(0) : x > T(0) ? T(1) : T(-1);
0093 }
0094 
0095 /**
0096  * Copies the sign of \p y into \p x and returns the result
0097  */
0098 template <typename T>
0099     inline T copysign(T x, T y) {
0100     T strippedX = qAbs(x);
0101     return y >= T(0) ? strippedX : -strippedX;
0102 }
0103 
0104 template<class T>
0105 typename std::enable_if<std::is_integral<T>::value, T>::type
0106 divideFloor(T a, T b)
0107 {
0108     const bool a_neg = a < T(0);
0109     const bool b_neg = b < T(0);
0110 
0111     if (a == T(0)) {
0112         return 0;
0113     } else if (a_neg == b_neg) {
0114         return a / b;
0115     } else {
0116         const T a_abs = qAbs(a);
0117         const T b_abs = qAbs(b);
0118 
0119         return - 1 - (a_abs - T(1)) / b_abs;
0120     }
0121 }
0122 
0123 template <class T>
0124 T leftUnitNormal(const T &a)
0125 {
0126     T result = a.x() != 0 ? T(-a.y() / a.x(), 1) : T(-1, 0);
0127     qreal length = norm(result);
0128     result *= (crossProduct(a, result) >= 0 ? 1 : -1) / length;
0129 
0130     return -result;
0131 }
0132 
0133 template <class T>
0134 T rightUnitNormal(const T &a)
0135 {
0136     return -leftUnitNormal(a);
0137 }
0138 
0139 template <class T>
0140 T inwardUnitNormal(const T &a, int polygonDirection)
0141 {
0142     return polygonDirection * leftUnitNormal(a);
0143 }
0144 
0145 /**
0146  * \return 1 if the polygon is counterclockwise
0147  *        -1 if the polygon is clockwise
0148  *
0149  * Note: the sign is flipped because our 0y axis
0150  *       is reversed
0151  */
0152 template <class T>
0153 int polygonDirection(const QVector<T> &polygon) {
0154 
0155     typename PointTypeTraits<T>::value_type doubleSum = 0;
0156 
0157     const int numPoints = polygon.size();
0158     for (int i = 1; i <= numPoints; i++) {
0159         int prev = i - 1;
0160         int next = i == numPoints ? 0 : i;
0161 
0162         doubleSum +=
0163             (polygon[next].x() - polygon[prev].x()) *
0164             (polygon[next].y() + polygon[prev].y());
0165     }
0166 
0167     return doubleSum >= 0 ? 1 : -1;
0168 }
0169 
0170 template <typename T>
0171 bool isInRange(T x, T a, T b) {
0172     T length = qAbs(a - b);
0173     return qAbs(x - a) <= length && qAbs(x - b) <= length;
0174 }
0175 
0176 void KRITAGLOBAL_EXPORT adjustIfOnPolygonBoundary(const QPolygonF &poly, int polygonDirection, QPointF *pt);
0177 
0178 /**
0179  * Let \p pt, \p base1 are two vectors. \p base1 is uniformly scaled
0180  * and then rotated into \p base2 using transformation matrix S *
0181  * R. The function applies the same transformation to \pt and returns
0182  * the result.
0183  **/
0184 QPointF KRITAGLOBAL_EXPORT transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2);
0185 
0186 qreal KRITAGLOBAL_EXPORT angleBetweenVectors(const QPointF &v1, const QPointF &v2);
0187 
0188 /**
0189  * Computes an angle indicating the direction from p1 to p2. If p1 and p2 are too close together to
0190  * compute an angle, defaultAngle is returned.
0191  */
0192 qreal KRITAGLOBAL_EXPORT directionBetweenPoints(const QPointF &p1, const QPointF &p2,
0193                                                 qreal defaultAngle);
0194 
0195 namespace Private {
0196     inline void resetEmptyRectangle(const QPoint &pt, QRect *rc) {
0197         *rc = QRect(pt, QSize(1, 1));
0198     }
0199 
0200     inline void resetEmptyRectangle(const QPointF &pt, QRectF *rc) {
0201         static const qreal eps = 1e-10;
0202         *rc = QRectF(pt, QSizeF(eps, eps));
0203     }
0204 }
0205 
0206 template <class Point, class Rect>
0207 inline void accumulateBounds(const Point &pt, Rect *bounds)
0208 {
0209     if (bounds->isEmpty()) {
0210         Private::resetEmptyRectangle(pt, bounds);
0211     }
0212 
0213     if (pt.x() > bounds->right()) {
0214         bounds->setRight(pt.x());
0215     }
0216 
0217     if (pt.x() < bounds->left()) {
0218         bounds->setLeft(pt.x());
0219     }
0220 
0221     if (pt.y() > bounds->bottom()) {
0222         bounds->setBottom(pt.y());
0223     }
0224 
0225     if (pt.y() < bounds->top()) {
0226         bounds->setTop(pt.y());
0227     }
0228 }
0229 
0230 template <template <class T> class Container, class Point, class Rect>
0231 inline void accumulateBounds(const Container<Point> &points, Rect *bounds)
0232 {
0233     Q_FOREACH (const Point &pt, points) {
0234         accumulateBounds(pt, bounds);
0235     }
0236 }
0237 
0238 template <template <class T> class Container, class Point>
0239 inline typename PointTypeTraits<Point>::rect_type
0240 accumulateBounds(const Container<Point> &points)
0241 {
0242     typename PointTypeTraits<Point>::rect_type result;
0243 
0244     Q_FOREACH (const Point &pt, points) {
0245         accumulateBounds(pt, &result);
0246     }
0247 
0248     return result;
0249 }
0250 
0251 template <class Point, class Rect>
0252 inline Point clampPoint(Point pt, const Rect &bounds)
0253 {
0254     if (pt.x() > bounds.right()) {
0255         pt.rx() = bounds.right();
0256     }
0257 
0258     if (pt.x() < bounds.left()) {
0259         pt.rx() = bounds.left();
0260     }
0261 
0262     if (pt.y() > bounds.bottom()) {
0263         pt.ry() = bounds.bottom();
0264     }
0265 
0266     if (pt.y() < bounds.top()) {
0267         pt.ry() = bounds.top();
0268     }
0269 
0270     return pt;
0271 }
0272 
0273 template <class Size>
0274 auto maxDimension(Size size) -> decltype(size.width()) {
0275     return qMax(size.width(), size.height());
0276 }
0277 
0278 template <class Size>
0279 auto minDimension(Size size) -> decltype(size.width()) {
0280     return qMin(size.width(), size.height());
0281 }
0282 
0283 QPainterPath KRITAGLOBAL_EXPORT smallArrow();
0284 
0285 /**
0286  * Multiply width and height of \p rect by \p coeff keeping the
0287  * center of the rectangle pinned
0288  */
0289 template <class Rect>
0290 Rect blowRect(const Rect &rect, qreal coeff)
0291 {
0292     typedef decltype(rect.x()) CoordType;
0293 
0294     CoordType w = rect.width() * coeff;
0295     CoordType h = rect.height() * coeff;
0296 
0297     return rect.adjusted(-w, -h, w, h);
0298 }
0299 
0300 QPoint KRITAGLOBAL_EXPORT ensureInRect(QPoint pt, const QRect &bounds);
0301 QPointF KRITAGLOBAL_EXPORT ensureInRect(QPointF pt, const QRectF &bounds);
0302 
0303 template <class Rect>
0304 Rect ensureRectNotSmaller(Rect rc, const decltype(Rect().size()) &size)
0305 {
0306     typedef decltype(Rect().size()) Size;
0307     typedef decltype(Rect().top()) ValueType;
0308 
0309     if (rc.width() < size.width() ||
0310         rc.height() < size.height()) {
0311 
0312         ValueType width = qMax(rc.width(), size.width());
0313         ValueType  height = qMax(rc.height(), size.height());
0314 
0315         rc = Rect(rc.topLeft(), Size(width, height));
0316     }
0317 
0318     return rc;
0319 }
0320 
0321 template <class Size>
0322 Size ensureSizeNotSmaller(const Size &size, const Size &bounds)
0323 {
0324     Size result = size;
0325 
0326     const auto widthBound = qAbs(bounds.width());
0327     auto width = result.width();
0328     if (qAbs(width) < widthBound) {
0329         width = copysign(widthBound, width);
0330         result.setWidth(width);
0331     }
0332 
0333     const auto heightBound = qAbs(bounds.height());
0334     auto height = result.height();
0335     if (qAbs(height) < heightBound) {
0336         height = copysign(heightBound, height);
0337         result.setHeight(height);
0338     }
0339 
0340     return result;
0341 }
0342 
0343 /**
0344  * Attempt to intersect a line to the area of the a rectangle.
0345  *
0346  * If the line intersects the rectangle, it will be modified to represent the intersecting line segment and true is returned.
0347  * If the line does not intersect the area, it remains unmodified and false will be returned.
0348  * If the line is fully inside the rectangle, it's considered "intersecting" so true will be returned.
0349  *
0350  * @param line line segment
0351  * @param rect area
0352  * @param extend extend the line to the edges of the rect area even if the line segment fits inside the rectangle
0353  *     (so, consider the line to be a line defined by those two points, not a line segment)
0354  * @return true if successful
0355  */
0356 bool KRITAGLOBAL_EXPORT intersectLineRect(QLineF &line, const QRect rect, bool extend);
0357 
0358 /**
0359  * Attempt to intersect a line to the area of the a rectangle.
0360  *
0361  * If the line intersects the rectangle, it will be modified to represent the intersecting line segment and true is returned.
0362  * If the line does not intersect the area, it remains unmodified and false will be returned.
0363  * If the line is fully inside the rectangle, it's considered "intersecting" so true will be returned.
0364  *
0365  * extendFirst and extendSecond parameters allow one to use this function in case of unbounded lines (if both are true),
0366  * line segments (if both are false) or half-lines/rays (if one is true and another is false).
0367  * Note that which point is the "first" and which is the "second" is determined by which is the p1() and which is p2() in QLineF.
0368  *
0369  * @param line line segment
0370  * @param rect area
0371  * @param extendFirst extend the line to the edge of the rect area even if the first point of the line segment lies inside the rectangle
0372  * @param extendSecond extend the line to the edge of the rect area even if the second point of the line segment lies inside the rectangle
0373  * @return true if successful
0374  */
0375 bool KRITAGLOBAL_EXPORT intersectLineRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond);
0376 
0377 // the same but with a convex polygon; uses Cyrus-Beck algorithm
0378 bool KRITAGLOBAL_EXPORT intersectLineConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond);
0379 
0380 /**
0381  * Crop line to rect; if it doesn't intersect, just return an empty line (QLineF()).
0382  *
0383  * This is using intersectLineRect, but with the difference that it doesn't require the user to check the return value.
0384  * It's useful for drawing code, since it let the developer not use `if` before drawing.
0385  *
0386  * If the line intersects the rectangle, it will be modified to represent the intersecting line segment.
0387  * If the line does not intersect the area, it will return an empty one-point line.
0388  *
0389  * extendFirst and extendSecond parameters allow one to use this function in case of unbounded lines (if both are true),
0390  * line segments (if both are false) or half-lines/rays (if one is true and another is false).
0391  * Note that which point is the "first" and which is the "second" is determined by which is the p1() and which is p2() in QLineF.
0392  *
0393  * @param line line segment
0394  * @param rect area
0395  * @param extendFirst extend the line to the edge of the rect area even if the first point of the line segment lies inside the rectangle
0396  * @param extendSecond extend the line to the edge of the rect area even if the second point of the line segment lies inside the rectangle
0397  */
0398 void KRITAGLOBAL_EXPORT cropLineToRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond);
0399 
0400 void KRITAGLOBAL_EXPORT cropLineToConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond);
0401 
0402 
0403 
0404 
0405 template <class Point>
0406 inline Point abs(const Point &pt) {
0407     return Point(qAbs(pt.x()), qAbs(pt.y()));
0408 }
0409 
0410 template<typename T, typename std::enable_if<std::is_integral<T>::value, T>::type* = nullptr>
0411 inline T wrapValue(T value, T wrapBounds) {
0412     value %= wrapBounds;
0413     if (value < 0) {
0414         value += wrapBounds;
0415     }
0416     return value;
0417 }
0418 
0419 template<typename T, typename std::enable_if<std::is_floating_point<T>::value, T>::type* = nullptr>
0420 inline T wrapValue(T value, T wrapBounds) {
0421     value = std::fmod(value, wrapBounds);
0422     if (value < 0) {
0423         value += wrapBounds;
0424     }
0425     return value;
0426 }
0427 
0428 template<typename T, typename std::enable_if<std::is_same<decltype(T().x()), decltype(T().y())>::value, T>::type* = nullptr>
0429 inline T wrapValue(T value, T wrapBounds) {
0430     value.rx() = wrapValue(value.x(), wrapBounds.x());
0431     value.ry() = wrapValue(value.y(), wrapBounds.y());
0432     return value;
0433 }
0434 
0435 template<typename T>
0436 inline T wrapValue(T value, T min, T max) {
0437     return wrapValue(value - min, max - min) + min;
0438 }
0439 
0440 class RightHalfPlane {
0441 public:
0442 
0443     RightHalfPlane(const QPointF &a, const QPointF &b)
0444         : m_a(a), m_p(b - a), m_norm_p_inv(1.0 / norm(m_p))
0445     {
0446     }
0447 
0448     RightHalfPlane(const QLineF &line)
0449         : RightHalfPlane(line.p1(), line.p2())
0450     {
0451     }
0452 
0453     qreal valueSq(const QPointF &pt) const {
0454         const qreal val = value(pt);
0455         return signZZ(val) * pow2(val);
0456     }
0457 
0458     qreal value(const QPointF &pt) const {
0459         return crossProduct(m_p, pt - m_a) * m_norm_p_inv;
0460     }
0461 
0462     int pos(const QPointF &pt) const {
0463         return signZZ(value(pt));
0464     }
0465 
0466     QLineF getLine() const {
0467         return QLineF(m_a, m_a + m_p);
0468     }
0469 
0470 private:
0471     const QPointF m_a;
0472     const QPointF m_p;
0473     const qreal m_norm_p_inv;
0474 };
0475 
0476 class OuterCircle {
0477 public:
0478 
0479     OuterCircle(const QPointF &c, qreal radius)
0480         : m_c(c),
0481           m_radius(radius),
0482           m_radius_sq(pow2(radius)),
0483           m_fadeCoeff(1.0 / (pow2(radius + 1.0) - m_radius_sq))
0484     {
0485     }
0486 
0487     qreal valueSq(const QPointF &pt) const {
0488         const qreal val = value(pt);
0489 
0490         return signZZ(val) * pow2(val);
0491     }
0492 
0493     qreal value(const QPointF &pt) const {
0494         return kisDistance(pt, m_c) - m_radius;
0495     }
0496 
0497     int pos(const QPointF &pt) const {
0498         return signZZ(valueSq(pt));
0499     }
0500 
0501     qreal fadeSq(const QPointF &pt) const {
0502         const qreal valSq = kisSquareDistance(pt, m_c);
0503         return (valSq - m_radius_sq) * m_fadeCoeff;
0504     }
0505 
0506 private:
0507     const QPointF m_c;
0508     const qreal m_radius;
0509     const qreal m_radius_sq;
0510     const qreal m_fadeCoeff;
0511 };
0512 
0513 QVector<QPoint> KRITAGLOBAL_EXPORT sampleRectWithPoints(const QRect &rect);
0514 QVector<QPointF> KRITAGLOBAL_EXPORT sampleRectWithPoints(const QRectF &rect);
0515 
0516 QRect KRITAGLOBAL_EXPORT approximateRectFromPoints(const QVector<QPoint> &points);
0517 QRectF KRITAGLOBAL_EXPORT approximateRectFromPoints(const QVector<QPointF> &points);
0518 
0519 QRect KRITAGLOBAL_EXPORT approximateRectWithPointTransform(const QRect &rect, std::function<QPointF(QPointF)> func);
0520 
0521 
0522 /**
0523  * Cuts off a portion of a rect \p rc defined by a half-plane \p p
0524  * \return the bounding rect of the resulting polygon
0525  */
0526 KRITAGLOBAL_EXPORT
0527 QRectF cutOffRect(const QRectF &rc, const KisAlgebra2D::RightHalfPlane &p);
0528 
0529 
0530 /**
0531  * Solves a quadratic equation in a form:
0532  *
0533  * a * x^2 + b * x + c = 0
0534  *
0535  * WARNING: Please note that \p a *must* be nonzero!  Otherwise the
0536  * equation is not quadratic! And this function doesn't check that!
0537  *
0538  * \return the number of solutions. It can be 0, 1 or 2.
0539  *
0540  * \p x1, \p x2 --- the found solution. The variables are filled with
0541  *                  data iff the corresponding solution is found. That
0542  *                  is: 0 solutions --- variabled are not touched, 1
0543  *                  solution --- x1 is filled with the result, 2
0544  *                  solutions --- x1 and x2 are filled.
0545  */
0546 KRITAGLOBAL_EXPORT
0547 int quadraticEquation(qreal a, qreal b, qreal c, qreal *x1, qreal *x2);
0548 
0549 /**
0550  * Finds the points of intersections between two circles
0551  * \return the found circles, the result can have 0, 1 or 2 points
0552  */
0553 KRITAGLOBAL_EXPORT
0554 QVector<QPointF> intersectTwoCircles(const QPointF &c1, qreal r1,
0555                                      const QPointF &c2, qreal r2);
0556 
0557 KRITAGLOBAL_EXPORT
0558 QTransform mapToRect(const QRectF &rect);
0559 
0560 KRITAGLOBAL_EXPORT
0561 QTransform mapToRectInverse(const QRectF &rect);
0562 
0563 /**
0564  * Scale the relative point \pt into the bounds of \p rc. The point might be
0565  * outside the rectangle.
0566  */
0567 inline QPointF relativeToAbsolute(const QPointF &pt, const QRectF &rc) {
0568     return rc.topLeft() + QPointF(pt.x() * rc.width(), pt.y() * rc.height());
0569 }
0570 
0571 /**
0572  * Get the relative position of \p pt inside rectangle \p rc. The point can be
0573  * outside the rectangle.
0574  */
0575 inline QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc) {
0576     const QPointF rel = pt - rc.topLeft();
0577     return QPointF(rc.width() > 0 ? rel.x() / rc.width() : 0,
0578                    rc.height() > 0 ? rel.y() / rc.height() : 0);
0579 
0580 }
0581 
0582 /**
0583  * Scales relative isotropic value from relative to absolute coordinate system
0584  * using SVG 1.1 rules (see chapter 7.10)
0585  */
0586 inline qreal relativeToAbsolute(qreal value, const QRectF &rc) {
0587     const qreal coeff = std::sqrt(pow2(rc.width()) + pow2(rc.height())) / std::sqrt(2.0);
0588     return value * coeff;
0589 }
0590 
0591 /**
0592  * Scales absolute isotropic value from absolute to relative coordinate system
0593  * using SVG 1.1 rules (see chapter 7.10)
0594  */
0595 inline qreal absoluteToRelative(const qreal value, const QRectF &rc) {
0596     const qreal coeff = std::sqrt(pow2(rc.width()) + pow2(rc.height())) / std::sqrt(2.0);
0597     return coeff != 0 ? value / coeff : 0;
0598 }
0599 
0600 /**
0601  * Scales relative isotropic value from relative to absolute coordinate system
0602  * using SVG 1.1 rules (see chapter 7.10)
0603  */
0604 inline QRectF relativeToAbsolute(const QRectF &rel, const QRectF &rc) {
0605     return QRectF(relativeToAbsolute(rel.topLeft(), rc), relativeToAbsolute(rel.bottomRight(), rc));
0606 }
0607 
0608 /**
0609  * Scales absolute isotropic value from absolute to relative coordinate system
0610  * using SVG 1.1 rules (see chapter 7.10)
0611  */
0612 inline QRectF absoluteToRelative(const QRectF &rel, const QRectF &rc) {
0613     return QRectF(absoluteToRelative(rel.topLeft(), rc), absoluteToRelative(rel.bottomRight(), rc));
0614 }
0615 
0616 /**
0617  * Compare the matrices with tolerance \p delta
0618  */
0619 bool KRITAGLOBAL_EXPORT fuzzyMatrixCompare(const QTransform &t1, const QTransform &t2, qreal delta);
0620 
0621 /**
0622  * Returns true if the two points are equal within some tolerance, where the tolerance is determined
0623  * by Qt's built-in fuzzy comparison functions.
0624  */
0625 bool KRITAGLOBAL_EXPORT fuzzyPointCompare(const QPointF &p1, const QPointF &p2);
0626 
0627 /**
0628  * Returns true if the two points are equal within the specified tolerance
0629  */
0630 bool KRITAGLOBAL_EXPORT fuzzyPointCompare(const QPointF &p1, const QPointF &p2, qreal delta);
0631 
0632 /**
0633  * Returns true if points in two containers are equal with specified tolerance
0634  */
0635 template <template<typename> class Cont, class Point>
0636 bool fuzzyPointCompare(const Cont<Point> &c1, const Cont<Point> &c2, qreal delta)
0637 {
0638     if (c1.size() != c2.size()) return false;
0639 
0640     const qreal eps = delta;
0641 
0642     return std::mismatch(c1.cbegin(),
0643                          c1.cend(),
0644                          c2.cbegin(),
0645                          [eps] (const QPointF &pt1, const QPointF &pt2) {
0646                                return fuzzyPointCompare(pt1, pt2, eps);
0647                          })
0648             .first == c1.cend();
0649 }
0650 
0651 /**
0652  * Compare two rectangles with tolerance \p tolerance. The tolerance means that the
0653  * coordinates of top left and bottom right corners should not differ more than \p tolerance
0654  * pixels.
0655  */
0656 template<class Rect, typename Difference = decltype(Rect::width())>
0657 bool fuzzyCompareRects(const Rect &r1, const Rect &r2, Difference tolerance) {
0658     typedef decltype(r1.topLeft()) Point;
0659 
0660     const Point d1 = abs(r1.topLeft() - r2.topLeft());
0661     const Point d2 = abs(r1.bottomRight() - r2.bottomRight());
0662 
0663     const Difference maxError = std::max({d1.x(), d1.y(), d2.x(), d2.y()});
0664     return maxError < tolerance;
0665 }
0666 
0667 struct KRITAGLOBAL_EXPORT DecomposedMatix {
0668     DecomposedMatix();
0669 
0670     DecomposedMatix(const QTransform &t0);
0671 
0672     inline QTransform scaleTransform() const
0673     {
0674         return QTransform::fromScale(scaleX, scaleY);
0675     }
0676 
0677     inline QTransform shearTransform() const
0678     {
0679         QTransform t;
0680         t.shear(shearXY, 0);
0681         return t;
0682     }
0683 
0684     inline QTransform rotateTransform() const
0685     {
0686         QTransform t;
0687         t.rotate(angle);
0688         return t;
0689     }
0690 
0691     inline QTransform translateTransform() const
0692     {
0693         return QTransform::fromTranslate(dx, dy);
0694     }
0695 
0696     inline QTransform projectTransform() const
0697     {
0698         return
0699             QTransform(
0700                 1,0,proj[0],
0701                 0,1,proj[1],
0702                 0,0,proj[2]);
0703     }
0704 
0705     inline QTransform transform() const {
0706         return
0707             scaleTransform() *
0708             shearTransform() *
0709             rotateTransform() *
0710             translateTransform() *
0711             projectTransform();
0712     }
0713 
0714     inline bool isValid() const {
0715         return valid;
0716     }
0717 
0718     qreal scaleX = 1.0;
0719     qreal scaleY = 1.0;
0720     qreal shearXY = 0.0;
0721     qreal angle = 0.0;
0722     qreal dx = 0.0;
0723     qreal dy = 0.0;
0724     qreal proj[3] = {0.0, 0.0, 1.0};
0725 
0726 private:
0727     bool valid = true;
0728 };
0729 
0730 // NOTE: tiar: this seems to ignore perspective transformation, be wary and use the class in PerspectiveEllipseAssistant.cpp instead
0731 // this is only good for rotation, translation and possibly shear
0732 std::pair<QPointF, QTransform> KRITAGLOBAL_EXPORT transformEllipse(const QPointF &axes, const QTransform &fullLocalToGlobal);
0733 
0734 QPointF KRITAGLOBAL_EXPORT alignForZoom(const QPointF &pt, qreal zoom);
0735 
0736 
0737 /**
0738  * Linearly reshape function \p x so that in range [x0, x1]
0739  * it would cross points (x0, y0) and (x1, y1).
0740  */
0741 template <typename T>
0742 inline T linearReshapeFunc(T x, T x0, T x1, T y0, T y1)
0743 {
0744     return y0 + (y1 - y0) * (x - x0) / (x1 - x0);
0745 }
0746 
0747 
0748 /**
0749  * Find intersection of a bounded line \p boundedLine with unbounded
0750  * line \p unboundedLine (if an intersection exists)
0751  */
0752 KRITAGLOBAL_EXPORT
0753 boost::optional<QPointF> intersectLines(const QLineF &boundedLine, const QLineF &unboundedLine);
0754 
0755 
0756 /**
0757  * Find intersection of a bounded line \p p1, \p p2 with unbounded
0758  * line \p q1, \p q2 (if an intersection exists)
0759  */
0760 KRITAGLOBAL_EXPORT
0761 boost::optional<QPointF> intersectLines(const QPointF &p1, const QPointF &p2,
0762                                         const QPointF &q1, const QPointF &q2);
0763 
0764 /**
0765  * Find possible positions for point p3, such that points \p1, \p2 and p3 form
0766  * a triangle, such that the distance between p1 ad p3 is \p a and the distance
0767  * between p2 and p3 is b. There might be 0, 1 or 2 such positions.
0768  */
0769 QVector<QPointF> KRITAGLOBAL_EXPORT findTrianglePoint(const QPointF &p1, const QPointF &p2, qreal a, qreal b);
0770 
0771 /**
0772  * Find a point p3 that forms a triangle with \p1 and \p2 and is the nearest
0773  * possible point to \p nearest
0774  *
0775  * \see findTrianglePoint
0776  */
0777 boost::optional<QPointF> KRITAGLOBAL_EXPORT findTrianglePointNearest(const QPointF &p1, const QPointF &p2, qreal a, qreal b, const QPointF &nearest);
0778 
0779 /**
0780  * @brief moveElasticPoint moves point \p pt based on the model of elasticity
0781  * @param pt point in question, tied to points \p base, \p wingA and \p wingB
0782  *           using springs
0783  * @param base initial position of the dragged point
0784  * @param newBase final position of tht dragged point
0785  * @param wingA first anchor point
0786  * @param wingB second anchor point
0787  * @return the new position of \p pt
0788  *
0789  * The function requires (\p newBase - \p base) be much smaller than any
0790  * of the distances between other points. If larger distances are necessary,
0791  * then use integration.
0792  */
0793 QPointF KRITAGLOBAL_EXPORT moveElasticPoint(const QPointF &pt,
0794                                             const QPointF &base, const QPointF &newBase,
0795                                             const QPointF &wingA, const QPointF &wingB);
0796 
0797 /**
0798  * @brief moveElasticPoint moves point \p pt based on the model of elasticity
0799  * @param pt point in question, tied to points \p base, \p anchorPoints
0800  *           using springs
0801  * @param base initial position of the dragged point
0802  * @param newBase final position of tht dragged point
0803  * @param anchorPoints anchor points
0804  * @return the new position of \p pt
0805  *
0806  * The function expects (\p newBase - \p base) be much smaller than any
0807  * of the distances between other points. If larger distances are necessary,
0808  * then use integration.
0809  */
0810 QPointF KRITAGLOBAL_EXPORT moveElasticPoint(const QPointF &pt,
0811                                             const QPointF &base, const QPointF &newBase,
0812                                             const QVector<QPointF> &anchorPoints);
0813 
0814 
0815 /**
0816  * @brief a simple class to generate Halton sequence
0817  *
0818  * This sequence of numbers can be used to sample areas
0819  * in somewhat uniform way. See Wikipedia for more info:
0820  *
0821  * https://en.wikipedia.org/wiki/Halton_sequence
0822  */
0823 
0824 class HaltonSequenceGenerator
0825 {
0826 public:
0827     HaltonSequenceGenerator(int base)
0828         : m_base(base)
0829     {
0830     }
0831 
0832     int generate(int maxRange) {
0833         generationStep();
0834         return (m_n * maxRange + m_d / 2) / m_d;
0835     }
0836 
0837     qreal generate() {
0838         generationStep();
0839         return qreal(m_n) / m_d;
0840     }
0841 
0842 private:
0843     inline void generationStep() {
0844         int x = m_d - m_n;
0845 
0846         if (x == 1) {
0847             m_n = 1;
0848             m_d *= m_base;
0849         } else {
0850             int y = m_d / m_base;
0851             while (x <= y) {
0852                 y /= m_base;
0853             }
0854             m_n = (m_base + 1) * y - x;
0855         }
0856     }
0857 
0858 private:
0859     int m_n = 0;
0860     int m_d = 1;
0861     const int m_base = 0;
0862 };
0863 
0864 
0865 // find minimum of the function f(x) between points xA and xB, with eps precision, using Golden Ratio Section
0866 // requirements: only one local minimum between xA and xB
0867 // Golden Section is supposed to be usually faster than Ternary Section
0868 // NOTE: tiar: this function was debugged and should be working correctly but is not used anywhere any longer
0869 qreal findMinimumGoldenSection(std::function<qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter);
0870 
0871 // find minimum of the function f(x) between points xA and xB, with eps precision, using Ternary Section
0872 // requirements: only one local minimum between xA and xB
0873 // Golden Section is supposed to be usually faster than Ternary Section
0874 // NOTE: tiar: this function was debugged and should be working correctly but is not used anywhere any longer
0875 qreal findMinimumTernarySection(std::function<qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter);
0876 
0877 
0878 }
0879 
0880 
0881 #endif /* __KIS_ALGEBRA_2D_H */