Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_algebra_2d.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef __KIS_ALGEBRA_2D_H
8#define __KIS_ALGEBRA_2D_H
9
10#include <QPoint>
11#include <QPointF>
12#include <QVector>
13#include <QPolygonF>
14#include <QPainterPath>
15#include <QTransform>
16#include <cmath>
17#include <kis_global.h>
18#include <kritaglobal_export.h>
19#include <functional>
20#include <boost/optional.hpp>
21#include <optional>
22
23class QPainterPath;
24class QTransform;
25
26namespace KisAlgebra2D {
27
28template <class T>
30{
31};
32
33template <>
34struct PointTypeTraits<QPoint>
35{
36 typedef int value_type;
37 typedef qreal calculation_type;
38 typedef QRect rect_type;
39};
40
41template <>
42struct PointTypeTraits<QPointF>
43{
44 typedef qreal value_type;
45 typedef qreal calculation_type;
46 typedef QRectF rect_type;
47};
48
49
50template <class T>
51typename PointTypeTraits<T>::value_type dotProduct(const T &a, const T &b)
52{
53 return a.x() * b.x() + a.y() * b.y();
54}
55
56template <class T>
57typename PointTypeTraits<T>::value_type crossProduct(const T &a, const T &b)
58{
59 return a.x() * b.y() - a.y() * b.x();
60}
61
62template <class T>
63qreal norm(const T &a)
64{
65 return std::sqrt(pow2(a.x()) + pow2(a.y()));
66}
67
68template <class T>
69qreal normSquared(const T &a)
70{
71 return pow2(a.x()) + pow2(a.y());
72}
73
74template <class Point>
75Point normalize(const Point &a)
76{
77 const qreal length = norm(a);
78 return (1.0 / length) * a;
79}
80
81template <typename Point>
82Point lerp(const Point &pt1, const Point &pt2, qreal t)
83{
84 return pt1 + (pt2 - pt1) * t;
85}
86
90template <typename T>
91T signPZ(T x) {
92 return x >= T(0) ? T(1) : T(-1);
93}
94
98template <typename T>
99T signZZ(T x) {
100 return x == T(0) ? T(0) : x > T(0) ? T(1) : T(-1);
101}
102
106template <typename T>
107 inline T copysign(T x, T y) {
108 T strippedX = qAbs(x);
109 return y >= T(0) ? strippedX : -strippedX;
110}
111
112template<class T>
113typename std::enable_if<std::is_integral<T>::value, T>::type
114divideFloor(T a, T b)
115{
116 const bool a_neg = a < T(0);
117 const bool b_neg = b < T(0);
118
119 if (a == T(0)) {
120 return 0;
121 } else if (a_neg == b_neg) {
122 return a / b;
123 } else {
124 const T a_abs = qAbs(a);
125 const T b_abs = qAbs(b);
126
127 return - 1 - (a_abs - T(1)) / b_abs;
128 }
129}
130
131template <class T>
132T leftUnitNormal(const T &a)
133{
134 T result = a.x() != 0 ? T(-a.y() / a.x(), 1) : T(-1, 0);
135 qreal length = norm(result);
136 result *= (crossProduct(a, result) >= 0 ? 1 : -1) / length;
137
138 return -result;
139}
140
141template <class T>
142T rightUnitNormal(const T &a)
143{
144 return -leftUnitNormal(a);
145}
146
147template <class T>
149{
151}
152
158template<typename R>
160
161template<>
162inline int lazyRound<int>(qreal value)
163{
164 return qRound(value);
165}
166
167template<>
168inline qreal lazyRound<qreal>(qreal value)
169{
170 return value;
171}
172
180template <class T>
181int polygonDirection(const QVector<T> &polygon) {
182
183 typename PointTypeTraits<T>::value_type doubleSum = 0;
184
185 const int numPoints = polygon.size();
186 for (int i = 1; i <= numPoints; i++) {
187 int prev = i - 1;
188 int next = i == numPoints ? 0 : i;
189
190 doubleSum +=
191 (polygon[next].x() - polygon[prev].x()) *
192 (polygon[next].y() + polygon[prev].y());
193 }
194
195 return doubleSum >= 0 ? 1 : -1;
196}
197
198template <typename T>
199bool isInRange(T x, T a, T b) {
200 T length = qAbs(a - b);
201 return qAbs(x - a) <= length && qAbs(x - b) <= length;
202}
203
204void KRITAGLOBAL_EXPORT adjustIfOnPolygonBoundary(const QPolygonF &poly, int polygonDirection, QPointF *pt);
205
212QPointF KRITAGLOBAL_EXPORT transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2);
213
214qreal KRITAGLOBAL_EXPORT angleBetweenVectors(const QPointF &v1, const QPointF &v2);
215
220qreal KRITAGLOBAL_EXPORT directionBetweenPoints(const QPointF &p1, const QPointF &p2,
221 qreal defaultAngle);
222
223namespace Private {
224 inline void resetEmptyRectangle(const QPoint &pt, QRect *rc) {
225 *rc = QRect(pt, QSize(1, 1));
226 }
227
228 inline void resetEmptyRectangle(const QPointF &pt, QRectF *rc) {
229 static const qreal eps = 1e-10;
230 *rc = QRectF(pt, QSizeF(eps, eps));
231 }
232}
233
234template <class Point, class Rect>
235inline void accumulateBounds(const Point &pt, Rect *bounds)
236{
237 if (bounds->isEmpty()) {
239 }
240
245 if (pt.x() < bounds->left()) {
246 bounds->setLeft(pt.x());
247 } else if (pt.x() > bounds->right()) {
248 bounds->setRight(pt.x());
249 }
250
255 if (pt.y() < bounds->top()) {
256 bounds->setTop(pt.y());
257 } else if (pt.y() > bounds->bottom()) {
258 bounds->setBottom(pt.y());
259 }
260}
261
262template <class Point, class Rect>
263inline void accumulateBoundsNonEmpty(const Point &pt, Rect *bounds)
264{
265 if (pt.x() < bounds->left()) {
266 bounds->setLeft(pt.x());
267 } else if (pt.x() > bounds->right()) {
268 bounds->setRight(pt.x());
269 }
270
271 if (pt.y() < bounds->top()) {
272 bounds->setTop(pt.y());
273 } else if (pt.y() > bounds->bottom()) {
274 bounds->setBottom(pt.y());
275 }
276}
277
278template <template <class T> class Container, class Point, class Rect>
279inline void accumulateBounds(const Container<Point> &points, Rect *bounds)
280{
281 Q_FOREACH (const Point &pt, points) {
283 }
284}
285
286template <template <class T> class Container, class Point>
287inline typename PointTypeTraits<Point>::rect_type
288accumulateBounds(const Container<Point> &points)
289{
290 typename PointTypeTraits<Point>::rect_type result;
291
292 Q_FOREACH (const Point &pt, points) {
293 accumulateBounds(pt, &result);
294 }
295
296 return result;
297}
298
299template <class Point, class Rect>
300inline Point clampPoint(Point pt, const Rect &bounds)
301{
302 if (pt.x() > bounds.right()) {
303 pt.rx() = bounds.right();
304 }
305
306 if (pt.x() < bounds.left()) {
307 pt.rx() = bounds.left();
308 }
309
310 if (pt.y() > bounds.bottom()) {
311 pt.ry() = bounds.bottom();
312 }
313
314 if (pt.y() < bounds.top()) {
315 pt.ry() = bounds.top();
316 }
317
318 return pt;
319}
320
321template <class Point>
322inline typename PointTypeTraits<Point>::rect_type
323createRectFromCorners(Point corner1, Point corner2)
324{
325 return typename PointTypeTraits<Point>::rect_type(qMin(corner1.x(), corner2.x()), qMin(corner1.y(), corner2.y()), qAbs(corner1.x() - corner2.x()), qAbs(corner1.y() - corner2.y()));
326}
327
328inline QRectF createRectFromCorners(QLineF line)
329{
330 QPointF a = line.p1();
331 QPointF b = line.p2();
332 return createRectFromCorners(a, b);
333}
334
335
336
337template <class Size>
338auto maxDimension(Size size) -> decltype(size.width()) {
339 return qMax(size.width(), size.height());
340}
341
342template <class Size>
343auto minDimension(Size size) -> decltype(size.width()) {
344 return qMin(size.width(), size.height());
345}
346
347QPainterPath KRITAGLOBAL_EXPORT smallArrow();
348
353template <class Rect>
354Rect blowRect(const Rect &rect, qreal coeff)
355{
356 typedef decltype(rect.x()) CoordType;
357
358 CoordType w = rect.width() * coeff;
359 CoordType h = rect.height() * coeff;
360
361 return rect.adjusted(-w, -h, w, h);
362}
363
364QPoint KRITAGLOBAL_EXPORT ensureInRect(QPoint pt, const QRect &bounds);
365QPointF KRITAGLOBAL_EXPORT ensureInRect(QPointF pt, const QRectF &bounds);
366
367template <class Rect>
368Rect ensureRectNotSmaller(Rect rc, const decltype(Rect().size()) &size)
369{
370 typedef decltype(Rect().size()) Size;
371 typedef decltype(Rect().top()) ValueType;
372
373 if (rc.width() < size.width() ||
374 rc.height() < size.height()) {
375
376 ValueType width = qMax(rc.width(), size.width());
377 ValueType height = qMax(rc.height(), size.height());
378
379 rc = Rect(rc.topLeft(), Size(width, height));
380 }
381
382 return rc;
383}
384
385template <class Size>
386Size ensureSizeNotSmaller(const Size &size, const Size &bounds)
387{
388 Size result = size;
389
390 const auto widthBound = qAbs(bounds.width());
391 auto width = result.width();
392 if (qAbs(width) < widthBound) {
393 width = copysign(widthBound, width);
394 result.setWidth(width);
395 }
396
397 const auto heightBound = qAbs(bounds.height());
398 auto height = result.height();
399 if (qAbs(height) < heightBound) {
400 height = copysign(heightBound, height);
401 result.setHeight(height);
402 }
403
404 return result;
405}
406
420bool KRITAGLOBAL_EXPORT intersectLineRect(QLineF &line, const QRect rect, bool extend);
421
439bool KRITAGLOBAL_EXPORT intersectLineRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond);
440
441// the same but with a convex polygon; uses Cyrus-Beck algorithm
442bool KRITAGLOBAL_EXPORT intersectLineConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond);
443
444//QList<QLineF> KRITAGLOBAL_EXPORT intersectLineConcavePolygon(const QPolygonF polygon, const QLineF& line, bool extendFirst, bool extendSecond);
445
464void KRITAGLOBAL_EXPORT cropLineToRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond);
465
466void KRITAGLOBAL_EXPORT cropLineToConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond);
467
473QPolygonF KRITAGLOBAL_EXPORT calculateConvexHull(const QPolygonF &polygon);
474
475
476template <class Point>
477inline Point abs(const Point &pt) {
478 return Point(qAbs(pt.x()), qAbs(pt.y()));
479}
480
481template<typename T, typename std::enable_if<std::is_integral<T>::value, T>::type* = nullptr>
482inline T wrapValue(T value, T wrapBounds) {
483 value %= wrapBounds;
484 if (value < 0) {
485 value += wrapBounds;
486 }
487 return value;
488}
489
490template<typename T, typename std::enable_if<std::is_floating_point<T>::value, T>::type* = nullptr>
491inline T wrapValue(T value, T wrapBounds) {
492 value = std::fmod(value, wrapBounds);
493 if (value < 0) {
494 value += wrapBounds;
495 }
496 return value;
497}
498
499template<typename T, typename std::enable_if<std::is_same<decltype(T().x()), decltype(T().y())>::value, T>::type* = nullptr>
500inline T wrapValue(T value, T wrapBounds) {
501 value.rx() = wrapValue(value.x(), wrapBounds.x());
502 value.ry() = wrapValue(value.y(), wrapBounds.y());
503 return value;
504}
505
506template<typename T>
507inline T wrapValue(T value, T min, T max) {
508 return wrapValue(value - min, max - min) + min;
509}
510
512public:
513
514 RightHalfPlane(const QPointF &a, const QPointF &b)
515 : m_a(a), m_p(b - a), m_norm_p_inv(1.0 / norm(m_p))
516 {
517 }
518
519 RightHalfPlane(const QLineF &line)
520 : RightHalfPlane(line.p1(), line.p2())
521 {
522 }
523
524 qreal valueSq(const QPointF &pt) const {
525 const qreal val = value(pt);
526 return signZZ(val) * pow2(val);
527 }
528
529 qreal value(const QPointF &pt) const {
530 return crossProduct(m_p, pt - m_a) * m_norm_p_inv;
531 }
532
533 int pos(const QPointF &pt) const {
534 return signZZ(value(pt));
535 }
536
537 QLineF getLine() const {
538 return QLineF(m_a, m_a + m_p);
539 }
540
541private:
542 const QPointF m_a;
543 const QPointF m_p;
544 const qreal m_norm_p_inv;
545};
546
548public:
549
550 OuterCircle(const QPointF &c, qreal radius)
551 : m_c(c),
552 m_radius(radius),
553 m_radius_sq(pow2(radius)),
554 m_fadeCoeff(1.0 / (pow2(radius + 1.0) - m_radius_sq))
555 {
556 }
557
558 qreal valueSq(const QPointF &pt) const {
559 const qreal val = value(pt);
560
561 return signZZ(val) * pow2(val);
562 }
563
564 qreal value(const QPointF &pt) const {
565 return kisDistance(pt, m_c) - m_radius;
566 }
567
568 int pos(const QPointF &pt) const {
569 return signZZ(valueSq(pt));
570 }
571
572 qreal fadeSq(const QPointF &pt) const {
573 const qreal valSq = kisSquareDistance(pt, m_c);
574 return (valSq - m_radius_sq) * m_fadeCoeff;
575 }
576
577private:
578 const QPointF m_c;
579 const qreal m_radius;
580 const qreal m_radius_sq;
581 const qreal m_fadeCoeff;
582};
583
584
585// wrapper for QPainterPath providing sane line segment indexing
586class KRITAGLOBAL_EXPORT VectorPath
587{
588public:
590 {
591 typedef enum Type {
594 BezierTo
595 } Type;
596
597
598 QPointF endPoint {QPointF()};
599 QPointF controlPoint1 {QPointF()};
600 QPointF controlPoint2 {QPointF()};
601
602 Type type {MoveTo};
603
606
607 VectorPathPoint(Type _type, QPointF _endPoint, QPointF c1 = QPointF(), QPointF c2 = QPointF()) {
608 type = _type;
609 endPoint = _endPoint;
610 controlPoint1 = c1;
611 controlPoint2 = c2;
612 }
613
614 static VectorPathPoint moveTo(QPointF _endPoint) {
615 return VectorPathPoint(MoveTo, _endPoint);
616 }
617
618 static VectorPathPoint lineTo(QPointF _endPoint) {
619 return VectorPathPoint(LineTo, _endPoint);
620 }
621
622 static VectorPathPoint bezierTo(QPointF _endPoint, QPointF _controlPoint1, QPointF _controlPoint2) {
623 return VectorPathPoint(BezierTo, _endPoint, _controlPoint1, _controlPoint2);
624 }
625 };
626
627 struct Segment
628 {
631
632 QPointF startPoint {QPointF()};
633 QPointF endPoint {QPointF()};
634 QPointF controlPoint1 {QPointF()};
635 QPointF controlPoint2 {QPointF()};
636
637 VectorPathPoint::Type type {VectorPathPoint::MoveTo};
638
640 : start(_start)
641 , end(_end)
642 , startPoint(_start.endPoint)
643 , endPoint(_end.endPoint)
644 , controlPoint1(_end.controlPoint1)
645 , controlPoint2(_end.controlPoint2)
646 , type(_end.type)
647 {
648
649 }
650
651 };
652
653public:
654 VectorPath(const QPainterPath& path);
656
657
658 int pointsCount() const;
659 VectorPathPoint pointAt(int i) const;
660 int segmentsCount() const;
661 QList<VectorPathPoint> segmentAt(int i) const;
662 std::optional<Segment> segmentAtAsSegment(int i) const;
663
664 QLineF segmentAtAsLine(int i) const;
665
666
667 static QList<QPointF> intersectSegmentWithLineBounded(const QLineF &line, const Segment &segment);
668 static QList<QPointF> intersectSegmentWithLineBounded(const QLineF &line, const VectorPathPoint &p1, const VectorPathPoint &p2);
669
670
671
672 int pathIndexToSegmentIndex(int index);
673 int segmentIndexToPathIndex(int index);
674
675
676 VectorPath trulySimplified(qreal epsDegrees = 0.5) const;
677 // not open-path friendly
678 VectorPath reversed() const;
679
680
681 bool fuzzyComparePointsCyclic(const VectorPath& path, qreal eps = 0.0f) const;
682
683
684 QPainterPath asPainterPath() const;
685
686private:
687 QPainterPath m_originalPath;
689
690
691
692};
693
694QDebug KRITAGLOBAL_EXPORT operator<<(QDebug debug, const VectorPath &path);
695QDebug KRITAGLOBAL_EXPORT operator<<(QDebug debug, const VectorPath::VectorPathPoint &point);
696
697
698QVector<QPoint> KRITAGLOBAL_EXPORT sampleRectWithPoints(const QRect &rect);
699QVector<QPointF> KRITAGLOBAL_EXPORT sampleRectWithPoints(const QRectF &rect);
700
701QRect KRITAGLOBAL_EXPORT approximateRectFromPoints(const QVector<QPoint> &points);
702QRectF KRITAGLOBAL_EXPORT approximateRectFromPoints(const QVector<QPointF> &points);
703
704QRect KRITAGLOBAL_EXPORT approximateRectWithPointTransform(const QRect &rect, std::function<QPointF(QPointF)> func);
705
706
711KRITAGLOBAL_EXPORT
712QRectF cutOffRect(const QRectF &rc, const KisAlgebra2D::RightHalfPlane &p);
713
714
731KRITAGLOBAL_EXPORT
732int quadraticEquation(qreal a, qreal b, qreal c, qreal *x1, qreal *x2);
733
738KRITAGLOBAL_EXPORT
739QVector<QPointF> intersectTwoCircles(const QPointF &c1, qreal r1,
740 const QPointF &c2, qreal r2);
741
742KRITAGLOBAL_EXPORT
743QTransform mapToRect(const QRectF &rect);
744
745KRITAGLOBAL_EXPORT
746QTransform mapToRectInverse(const QRectF &rect);
747
752inline QPointF relativeToAbsolute(const QPointF &pt, const QRectF &rc) {
753 return rc.topLeft() + QPointF(pt.x() * rc.width(), pt.y() * rc.height());
754}
755
760inline QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc) {
761 const QPointF rel = pt - rc.topLeft();
762 return QPointF(rc.width() > 0 ? rel.x() / rc.width() : 0,
763 rc.height() > 0 ? rel.y() / rc.height() : 0);
764
765}
766
771inline qreal relativeToAbsolute(qreal value, const QRectF &rc) {
772 const qreal coeff = std::sqrt(pow2(rc.width()) + pow2(rc.height())) / std::sqrt(2.0);
773 return value * coeff;
774}
775
780inline qreal absoluteToRelative(const qreal value, const QRectF &rc) {
781 const qreal coeff = std::sqrt(pow2(rc.width()) + pow2(rc.height())) / std::sqrt(2.0);
782 return coeff != 0 ? value / coeff : 0;
783}
784
789inline QRectF relativeToAbsolute(const QRectF &rel, const QRectF &rc) {
790 return QRectF(relativeToAbsolute(rel.topLeft(), rc), relativeToAbsolute(rel.bottomRight(), rc));
791}
792
797inline QRectF absoluteToRelative(const QRectF &rel, const QRectF &rc) {
798 return QRectF(absoluteToRelative(rel.topLeft(), rc), absoluteToRelative(rel.bottomRight(), rc));
799}
800
804bool KRITAGLOBAL_EXPORT fuzzyMatrixCompare(const QTransform &t1, const QTransform &t2, qreal delta);
805
810bool KRITAGLOBAL_EXPORT fuzzyPointCompare(const QPointF &p1, const QPointF &p2);
811
815bool KRITAGLOBAL_EXPORT fuzzyPointCompare(const QPointF &p1, const QPointF &p2, qreal delta);
816
820template <template<typename> class Cont, class Point>
821bool fuzzyPointCompare(const Cont<Point> &c1, const Cont<Point> &c2, qreal delta)
822{
823 if (c1.size() != c2.size()) return false;
824
825 const qreal eps = delta;
826
827 return std::mismatch(c1.cbegin(),
828 c1.cend(),
829 c2.cbegin(),
830 [eps] (const QPointF &pt1, const QPointF &pt2) {
831 return fuzzyPointCompare(pt1, pt2, eps);
832 })
833 .first == c1.cend();
834}
835
841template<class Rect, typename Difference = decltype(Rect::width())>
842bool fuzzyCompareRects(const Rect &r1, const Rect &r2, Difference tolerance) {
843 typedef decltype(r1.topLeft()) Point;
844
845 const Point d1 = abs(r1.topLeft() - r2.topLeft());
846 const Point d2 = abs(r1.bottomRight() - r2.bottomRight());
847
848 const Difference maxError = std::max({d1.x(), d1.y(), d2.x(), d2.y()});
849 return maxError < tolerance;
850}
851
852
853
854template<class Polygon, typename Difference = decltype(Polygon::first())>
855bool isPolygonRect(const Polygon &poly, Difference tolerance) {
856 typedef decltype(poly.first()) Point;
857
858 auto sameVert = [tolerance] (Point p1, Point p2) {
859 return std::abs(p2.x() - p1.x()) < tolerance;
860 };
861
862
863 auto sameHoriz = [tolerance] (Point p1, Point p2) {
864 return std::abs(p2.y() - p1.y()) < tolerance;
865 };
866
867 const int rectCorners = 4;
868 if (poly.length() != rectCorners) {
869 if (poly.length() != rectCorners + 1 || !fuzzyPointCompare(poly[0], poly[rectCorners], tolerance)) {
870 return false;
871 }
872 }
873
874 bool correctRect = sameVert(poly[0], poly[1]) && sameHoriz(poly[1], poly[2]) && sameVert(poly[2], poly[3]) && sameHoriz(poly[3], poly[0]);
875 if (correctRect) {
876 return true;
877 }
878 return sameHoriz(poly[0], poly[1]) && sameVert(poly[1], poly[2]) && sameHoriz(poly[2], poly[3]) && sameVert(poly[3], poly[0]);
879
880}
881
882
883template <class T>
884bool isPolygonTrulyConvex(const QVector<T> &polygon) {
885 const int numPoints = polygon.size();
886 if (numPoints < 3)
887 return true;
888
889 qreal baseAngle = std::atan2(polygon[1].y() - polygon[0].y(), polygon[1].x() - polygon[0].x());
890 qreal secondAngle = std::atan2(polygon[2].y() - polygon[1].y(), polygon[2].x() - polygon[1].x());
891
892
893 qreal prevAngle = secondAngle;
894 secondAngle -= baseAngle;
895
896 qreal sign = signZZ(secondAngle);
897 for (int i = 2; i < numPoints; i++) {
898 int next = i == (numPoints - 1) ? 0 : i + 1;
899 if (next == 0 && fuzzyPointCompare(polygon[next], polygon[i])) {
900 break;
901 }
902 qreal nextAngle = std::atan2(polygon[next].y() - polygon[i].y(), polygon[next].x() - polygon[i].x());
903
904 qreal newPrevAngle = nextAngle;
905 nextAngle -= baseAngle;
906 nextAngle -= prevAngle;
907 prevAngle = newPrevAngle;
908 if (nextAngle < -M_PI) {
909 nextAngle += 2*M_PI;
910 } else if (nextAngle > M_PI) {
911 nextAngle -= 2*M_PI;
912 }
913 if (int(signZZ(nextAngle)) != sign) {
914 return false;
915 }
916 }
917 return true;
918}
919
920struct KRITAGLOBAL_EXPORT DecomposedMatrix {
922
923 DecomposedMatrix(const QTransform &t0);
924
925 inline QTransform scaleTransform() const
926 {
927 return QTransform::fromScale(scaleX, scaleY);
928 }
929
930 inline QTransform shearTransform() const
931 {
932 QTransform t;
933 t.shear(shearXY, 0);
934 return t;
935 }
936
937 inline QTransform rotateTransform() const
938 {
939 QTransform t;
940 t.rotate(angle);
941 return t;
942 }
943
944 inline QTransform translateTransform() const
945 {
946 return QTransform::fromTranslate(dx, dy);
947 }
948
949 inline QTransform projectTransform() const
950 {
951 return
952 QTransform(
953 1,0,proj[0],
954 0,1,proj[1],
955 0,0,proj[2]);
956 }
957
958 inline QTransform transform() const {
959 return
960 scaleTransform() *
961 shearTransform() *
962 rotateTransform() *
963 translateTransform() *
964 projectTransform();
965 }
966
967 inline bool isValid() const {
968 return valid;
969 }
970
971 qreal scaleX = 1.0;
972 qreal scaleY = 1.0;
973 qreal shearXY = 0.0;
974 qreal angle = 0.0;
975 qreal dx = 0.0;
976 qreal dy = 0.0;
977 qreal proj[3] = {0.0, 0.0, 1.0};
978
979private:
980 bool valid = true;
981};
982
983// NOTE: tiar: this seems to ignore perspective transformation, be wary and use the class in PerspectiveEllipseAssistant.cpp instead
984// this is only good for rotation, translation and possibly shear
985std::pair<QPointF, QTransform> KRITAGLOBAL_EXPORT transformEllipse(const QPointF &axes, const QTransform &fullLocalToGlobal);
986
987QPointF KRITAGLOBAL_EXPORT alignForZoom(const QPointF &pt, qreal zoom);
988
989
994template <typename T>
995inline T linearReshapeFunc(T x, T x0, T x1, T y0, T y1)
996{
997 return y0 + (y1 - y0) * (x - x0) / (x1 - x0);
998}
999
1000
1005KRITAGLOBAL_EXPORT
1006boost::optional<QPointF> intersectLines(const QLineF &boundedLine, const QLineF &unboundedLine);
1007
1008
1013KRITAGLOBAL_EXPORT
1014boost::optional<QPointF> intersectLines(const QPointF &p1, const QPointF &p2,
1015 const QPointF &q1, const QPointF &q2);
1016
1022QVector<QPointF> KRITAGLOBAL_EXPORT findTrianglePoint(const QPointF &p1, const QPointF &p2, qreal a, qreal b);
1023
1030boost::optional<QPointF> KRITAGLOBAL_EXPORT findTrianglePointNearest(const QPointF &p1, const QPointF &p2, qreal a, qreal b, const QPointF &nearest);
1031
1032bool isOnLine(const QLineF &line, const QPointF &point, const qreal eps, bool boundedStart, bool boundedEnd, bool includeEnds);
1033
1034
1049QPointF KRITAGLOBAL_EXPORT moveElasticPoint(const QPointF &pt,
1050 const QPointF &base, const QPointF &newBase,
1051 const QPointF &wingA, const QPointF &wingB);
1052
1066QPointF KRITAGLOBAL_EXPORT moveElasticPoint(const QPointF &pt,
1067 const QPointF &base, const QPointF &newBase,
1068 const QVector<QPointF> &anchorPoints);
1069
1070
1071QPointF KRITAGLOBAL_EXPORT findNearestPointOnLine(const QPointF &point, const QLineF &line, bool unbounded = true);
1072
1080QPointF KRITAGLOBAL_EXPORT movePointInTheDirection(const QPointF& point, const QPointF& direction, qreal distance);
1081
1082
1092QPointF KRITAGLOBAL_EXPORT movePointAlongTheLine(const QPointF& point, const QLineF& direction, qreal distance, bool ensureOnLine);
1093
1104{
1105public:
1107 : m_base(base)
1108 {
1109 }
1110
1111 int generate(int maxRange) {
1113 return (m_n * maxRange + m_d / 2) / m_d;
1114 }
1115
1116 qreal generate() {
1118 return qreal(m_n) / m_d;
1119 }
1120
1121 void step() {
1123 }
1124
1125 int currentValue(int maxRange) const {
1126 return (m_n * maxRange + m_d / 2) / m_d;
1127 }
1128
1129 qreal currentValue() const{
1130 return qreal(m_n) / m_d;
1131 }
1132
1133private:
1134 inline void generationStep() {
1135 int x = m_d - m_n;
1136
1137 if (x == 1) {
1138 m_n = 1;
1139 m_d *= m_base;
1140 } else {
1141 int y = m_d / m_base;
1142 while (x <= y) {
1143 y /= m_base;
1144 }
1145 m_n = (m_base + 1) * y - x;
1146 }
1147 }
1148
1149private:
1150 int m_n = 0;
1151 int m_d = 1;
1152 const int m_base = 0;
1153};
1154
1155
1156// find minimum of the function f(x) between points xA and xB, with eps precision, using Golden Ratio Section
1157// requirements: only one local minimum between xA and xB
1158// Golden Section is supposed to be usually faster than Ternary Section
1159// NOTE: tiar: this function was debugged and should be working correctly but is not used anywhere any longer
1160qreal KRITAGLOBAL_EXPORT findMinimumGoldenSection(std::function<qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter);
1161
1162// find minimum of the function f(x) between points xA and xB, with eps precision, using Ternary Section
1163// requirements: only one local minimum between xA and xB
1164// Golden Section is supposed to be usually faster than Ternary Section
1165// NOTE: tiar: this function was debugged and should be working correctly but is not used anywhere any longer
1166qreal KRITAGLOBAL_EXPORT findMinimumTernarySection(std::function<qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter);
1167
1168// kis_global has the same function, this needs to be removed
1169qreal KRITAGLOBAL_EXPORT pointToLineDistSquared(const QPointF& pt, const QLineF& line);
1170
1171QList<QLineF> KRITAGLOBAL_EXPORT getParallelLines(const QLineF& line, const qreal distance);
1172
1173QPainterPath KRITAGLOBAL_EXPORT getOnePathFromRectangleCutThrough(const QList<QPointF> &points, const QLineF &line, bool left);
1174
1184QList<QPainterPath> KRITAGLOBAL_EXPORT getPathsFromRectangleCutThrough(const QRectF &rect, const QLineF &leftLine, const QLineF &rightLine);
1185
1186
1187// isAlgebra2D::getLineSegmentCrossingLineIndexes(QLineF const&, QPainterPath const&)
1188QList<int> KRITAGLOBAL_EXPORT getLineSegmentCrossingLineIndexes(const QLineF &line, const QPainterPath& shape);
1189
1198QPainterPath KRITAGLOBAL_EXPORT removeGutterSmart(const QPainterPath& shape1, int index1, const QPainterPath& shape2, int index2, bool isSameShape);
1199
1200
1211VectorPath mergeShapesWithGutter(const VectorPath& shape1, const VectorPath& shape2, const VectorPath& oneEnd, const VectorPath& otherEnd, int index1, int index2, bool reverseSecondPoly, bool isSameShape);
1219QPainterPath KRITAGLOBAL_EXPORT trySimplifyPath(const QPainterPath &path, qreal lengthThreshold);
1220
1221
1222bool KRITAGLOBAL_EXPORT isInsideShape(const VectorPath &path, const QPointF &point);
1223bool KRITAGLOBAL_EXPORT isInsideShape(const QPainterPath &path, const QPointF &point);
1224
1225
1226
1227
1228}
1229
1230#endif /* __KIS_ALGEBRA_2D_H */
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
float value(const T *src, size_t ch)
QPointF r2
QPointF q1
const Params2D p
Eigen::Matrix< double, 4, 2 > R
QPointF r1
QPointF p2
QPointF q2
QPointF p1
qreal distance(const QPointF &p1, const QPointF &p2)
a simple class to generate Halton sequence
OuterCircle(const QPointF &c, qreal radius)
qreal valueSq(const QPointF &pt) const
qreal value(const QPointF &pt) const
int pos(const QPointF &pt) const
qreal fadeSq(const QPointF &pt) const
RightHalfPlane(const QPointF &a, const QPointF &b)
qreal value(const QPointF &pt) const
int pos(const QPointF &pt) const
RightHalfPlane(const QLineF &line)
qreal valueSq(const QPointF &pt) const
QList< VectorPathPoint > m_points
#define bounds(x, a, b)
const qreal eps
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:190
T pow2(const T &x)
Definition kis_global.h:166
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:194
#define M_PI
Definition kis_global.h:111
void resetEmptyRectangle(const QPoint &pt, QRect *rc)
boost::optional< QPointF > findTrianglePointNearest(const QPointF &p1, const QPointF &p2, qreal a, qreal b, const QPointF &nearest)
QVector< QPointF > intersectTwoCircles(const QPointF &center1, qreal r1, const QPointF &center2, qreal r2)
auto maxDimension(Size size) -> decltype(size.width())
QPointF relativeToAbsolute(const QPointF &pt, const QRectF &rc)
QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc)
Rect blowRect(const Rect &rect, qreal coeff)
qreal normSquared(const T &a)
T leftUnitNormal(const T &a)
QVector< Point > sampleRectWithPoints(const Rect &rect)
T linearReshapeFunc(T x, T x0, T x1, T y0, T y1)
QPointF movePointInTheDirection(const QPointF &point, const QPointF &direction, qreal distance)
movePointAlongTheLine moves the point a particular distance in the specified direction
T wrapValue(T value, T wrapBounds)
QRect approximateRectFromPoints(const QVector< QPoint > &points)
Point abs(const Point &pt)
std::enable_if< std::is_integral< T >::value, T >::type divideFloor(T a, T b)
QList< QPainterPath > getPathsFromRectangleCutThrough(const QRectF &rect, const QLineF &leftLine, const QLineF &rightLine)
getPathsFromRectangleCutThrough get paths defining both sides of a rectangle cut through using two (s...
auto minDimension(Size size) -> decltype(size.width())
void adjustIfOnPolygonBoundary(const QPolygonF &poly, int polygonDirection, QPointF *pt)
VectorPath mergeShapesWithGutter(const VectorPath &shape1, const VectorPath &shape2, const VectorPath &oneEnd, const VectorPath &otherEnd, int index1, int index2, bool reverseSecondPoly, bool isSameShape)
mergeShapesWithGutter merges two shapes with a gutter shape (defined as two paths)
std::pair< QPointF, QTransform > transformEllipse(const QPointF &axes, const QTransform &fullLocalToGlobal)
qreal findMinimumGoldenSection(std::function< qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter=100)
QPolygonF calculateConvexHull(const QPolygonF &polygon)
calculateConvexHull Calculate the convex hull of the polygon using the QuickHull
bool fuzzyMatrixCompare(const QTransform &t1, const QTransform &t2, qreal delta)
Point lerp(const Point &pt1, const Point &pt2, qreal t)
void accumulateBounds(const Point &pt, Rect *bounds)
qreal directionBetweenPoints(const QPointF &p1, const QPointF &p2, qreal defaultAngle)
void cropLineToConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
QTransform mapToRectInverse(const QRectF &rect)
QPointF moveElasticPoint(const QPointF &pt, const QPointF &base, const QPointF &newBase, const QPointF &wingA, const QPointF &wingB)
moveElasticPoint moves point pt based on the model of elasticity
QPainterPath trySimplifyPath(const QPainterPath &path, qreal lengthThreshold)
trySimplifyPath Tries to simplify a QPainterPath
qreal angleBetweenVectors(const QPointF &v1, const QPointF &v2)
T copysign(T x, T y)
qreal pointToLineDistSquared(const QPointF &pt, const QLineF &line)
Rect ensureRectNotSmaller(Rect rc, const decltype(Rect().size()) &size)
QPoint ensureInRect(QPoint pt, const QRect &bounds)
int quadraticEquation(qreal a, qreal b, qreal c, qreal *x1, qreal *x2)
Size ensureSizeNotSmaller(const Size &size, const Size &bounds)
QPointF alignForZoom(const QPointF &pt, qreal zoom)
Point normalize(const Point &a)
void cropLineToRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond)
Crop line to rect; if it doesn't intersect, just return an empty line (QLineF()).
QPainterPath removeGutterSmart(const QPainterPath &shape1, int index1, const QPainterPath &shape2, int index2, bool isSameShape)
removeGutterSmart
bool fuzzyCompareRects(const Rect &r1, const Rect &r2, Difference tolerance)
bool isInRange(T x, T a, T b)
QDebug operator<<(QDebug debug, const VectorPath &path)
Point clampPoint(Point pt, const Rect &bounds)
QPainterPath smallArrow()
qreal lazyRound< qreal >(qreal value)
qreal norm(const T &a)
QPointF movePointAlongTheLine(const QPointF &point, const QLineF &direction, qreal distance, bool ensureOnLine)
movePointAlongTheLine moves the point a particular distance in the specified direction
bool isPolygonRect(const Polygon &poly, Difference tolerance)
QVector< QPointF > findTrianglePoint(const QPointF &p1, const QPointF &p2, qreal a, qreal b)
QPointF findNearestPointOnLine(const QPointF &point, const QLineF &line, bool unbounded)
bool isPolygonTrulyConvex(const QVector< T > &polygon)
bool isInsideShape(const VectorPath &path, const QPointF &point)
PointTypeTraits< T >::value_type dotProduct(const T &a, const T &b)
void accumulateBoundsNonEmpty(const Point &pt, Rect *bounds)
QRectF cutOffRect(const QRectF &rc, const KisAlgebra2D::RightHalfPlane &p)
qreal findMinimumTernarySection(std::function< qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter=100)
boost::optional< QPointF > intersectLines(const QLineF &boundedLine, const QLineF &unboundedLine)
PointTypeTraits< T >::value_type crossProduct(const T &a, const T &b)
QPointF transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2)
R lazyRound(qreal value)
int polygonDirection(const QVector< T > &polygon)
T inwardUnitNormal(const T &a, int polygonDirection)
bool intersectLineConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
bool isOnLine(const QLineF &line, const QPointF &point, const qreal eps, bool boundedStart, bool boundedEnd, bool includeEnds)
T rightUnitNormal(const T &a)
int lazyRound< int >(qreal value)
QList< QLineF > getParallelLines(const QLineF &line, const qreal distance)
QRect approximateRectWithPointTransform(const QRect &rect, std::function< QPointF(QPointF)> func)
QList< int > getLineSegmentCrossingLineIndexes(const QLineF &line, const QPainterPath &shape)
bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2)
bool intersectLineRect(QLineF &line, const QRect rect, bool extend)
QTransform mapToRect(const QRectF &rect)
QPainterPath getOnePathFromRectangleCutThrough(const QList< QPointF > &points, const QLineF &line, bool left)
PointTypeTraits< Point >::rect_type createRectFromCorners(Point corner1, Point corner2)
QTransform shearTransform() const
QTransform projectTransform() const
QTransform rotateTransform() const
QTransform scaleTransform() const
QTransform translateTransform() const
Segment(VectorPathPoint _start, VectorPathPoint _end)
VectorPathPoint(Type _type, QPointF _endPoint, QPointF c1=QPointF(), QPointF c2=QPointF())
static VectorPathPoint moveTo(QPointF _endPoint)
static VectorPathPoint bezierTo(QPointF _endPoint, QPointF _controlPoint1, QPointF _controlPoint2)
static VectorPathPoint lineTo(QPointF _endPoint)