7#ifndef __KIS_ALGEBRA_2D_H
8#define __KIS_ALGEBRA_2D_H
14#include <QPainterPath>
18#include <kritaglobal_export.h>
20#include <boost/optional.hpp>
53 return a.x() * b.x() + a.y() * b.y();
59 return a.x() * b.y() - a.y() * b.x();
65 return std::sqrt(
pow2(a.x()) +
pow2(a.y()));
81template <
typename Po
int>
82Point
lerp(
const Point &pt1,
const Point &pt2, qreal t)
84 return pt1 + (pt2 - pt1) * t;
92 return x >= T(0) ? T(1) : T(-1);
100 return x == T(0) ? T(0) : x > T(0) ? T(1) : T(-1);
108 T strippedX = qAbs(x);
109 return y >= T(0) ? strippedX : -strippedX;
113typename std::enable_if<std::is_integral<T>::value, T>::type
116 const bool a_neg = a < T(0);
117 const bool b_neg = b < T(0);
121 }
else if (a_neg == b_neg) {
124 const T a_abs = qAbs(a);
125 const T b_abs = qAbs(b);
127 return - 1 - (a_abs - T(1)) / b_abs;
134 T result = a.x() != 0 ? T(-a.y() / a.x(), 1) : T(-1, 0);
164 return qRound(
value);
185 const int numPoints = polygon.size();
186 for (
int i = 1; i <= numPoints; i++) {
188 int next = i == numPoints ? 0 : i;
191 (polygon[next].x() - polygon[prev].x()) *
192 (polygon[next].y() + polygon[prev].y());
195 return doubleSum >= 0 ? 1 : -1;
212QPointF KRITAGLOBAL_EXPORT
transformAsBase(
const QPointF &pt,
const QPointF &base1,
const QPointF &base2);
225 *rc = QRect(pt, QSize(1, 1));
229 static const qreal
eps = 1e-10;
230 *rc = QRectF(pt, QSizeF(
eps,
eps));
234template <
class Po
int,
class Rect>
245 if (pt.x() <
bounds->left()) {
247 }
else if (pt.x() >
bounds->right()) {
255 if (pt.y() <
bounds->top()) {
257 }
else if (pt.y() >
bounds->bottom()) {
258 bounds->setBottom(pt.y());
262template <
class Po
int,
class Rect>
265 if (pt.x() <
bounds->left()) {
267 }
else if (pt.x() >
bounds->right()) {
271 if (pt.y() <
bounds->top()) {
273 }
else if (pt.y() >
bounds->bottom()) {
274 bounds->setBottom(pt.y());
278template <
template <
class T>
class Container,
class Point,
class Rect>
281 Q_FOREACH (
const Point &pt, points) {
286template <
template <
class T>
class Container,
class Point>
287inline typename PointTypeTraits<Point>::rect_type
292 Q_FOREACH (
const Point &pt, points) {
299template <
class Po
int,
class Rect>
302 if (pt.x() >
bounds.right()) {
306 if (pt.x() <
bounds.left()) {
310 if (pt.y() >
bounds.bottom()) {
311 pt.ry() =
bounds.bottom();
314 if (pt.y() <
bounds.top()) {
321template <
class Po
int>
322inline typename PointTypeTraits<Point>::rect_type
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()));
330 QPointF a = line.p1();
331 QPointF b = line.p2();
339 return qMax(size.width(), size.height());
344 return qMin(size.width(), size.height());
356 typedef decltype(
rect.x()) CoordType;
358 CoordType w =
rect.width() * coeff;
359 CoordType h =
rect.height() * coeff;
361 return rect.adjusted(-w, -h, w, h);
370 typedef decltype(Rect().size()) Size;
371 typedef decltype(Rect().top()) ValueType;
373 if (rc.width() < size.width() ||
374 rc.height() < size.height()) {
376 ValueType width = qMax(rc.width(), size.width());
377 ValueType height = qMax(rc.height(), size.height());
379 rc = Rect(rc.topLeft(), Size(width, height));
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);
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);
439bool KRITAGLOBAL_EXPORT
intersectLineRect(QLineF &line,
const QRect
rect,
bool extendFirst,
bool extendSecond);
442bool KRITAGLOBAL_EXPORT
intersectLineConvexPolygon(QLineF &line,
const QPolygonF polygon,
bool extendFirst,
bool extendSecond);
464void KRITAGLOBAL_EXPORT
cropLineToRect(QLineF &line,
const QRect
rect,
bool extendFirst,
bool extendSecond);
466void KRITAGLOBAL_EXPORT
cropLineToConvexPolygon(QLineF &line,
const QPolygonF polygon,
bool extendFirst,
bool extendSecond);
476template <
class Po
int>
477inline Point
abs(
const Point &pt) {
478 return Point(qAbs(pt.x()), qAbs(pt.y()));
481template<typename T, typename std::enable_if<std::is_integral<T>::value, T>::type* =
nullptr>
490template<typename T, typename std::enable_if<std::is_floating_point<T>::value, T>::type* =
nullptr>
499template<
typename T,
typename std::enable_if<std::is_same<
decltype(T().x()),
decltype(T().y())>
::value, T>::type* =
nullptr>
515 : m_a(a), m_p(b - a), m_norm_p_inv(1.0 /
norm(m_p))
525 const qreal val =
value(pt);
529 qreal
value(
const QPointF &pt)
const {
533 int pos(
const QPointF &pt)
const {
538 return QLineF(m_a, m_a + m_p);
553 m_radius_sq(
pow2(radius)),
554 m_fadeCoeff(1.0 / (
pow2(radius + 1.0) - m_radius_sq))
559 const qreal val =
value(pt);
564 qreal
value(
const QPointF &pt)
const {
568 int pos(
const QPointF &pt)
const {
569 return signZZ(valueSq(pt));
574 return (valSq - m_radius_sq) * m_fadeCoeff;
598 QPointF endPoint {QPointF()};
599 QPointF controlPoint1 {QPointF()};
600 QPointF controlPoint2 {QPointF()};
609 endPoint = _endPoint;
623 return VectorPathPoint(BezierTo, _endPoint, _controlPoint1, _controlPoint2);
632 QPointF startPoint {QPointF()};
633 QPointF endPoint {QPointF()};
634 QPointF controlPoint1 {QPointF()};
635 QPointF controlPoint2 {QPointF()};
642 , startPoint(_start.endPoint)
643 , endPoint(_end.endPoint)
644 , controlPoint1(_end.controlPoint1)
645 , controlPoint2(_end.controlPoint2)
658 int pointsCount()
const;
659 VectorPathPoint pointAt(
int i)
const;
660 int segmentsCount()
const;
662 std::optional<Segment> segmentAtAsSegment(
int i)
const;
664 QLineF segmentAtAsLine(
int i)
const;
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);
672 int pathIndexToSegmentIndex(
int index);
673 int segmentIndexToPathIndex(
int index);
676 VectorPath trulySimplified(qreal epsDegrees = 0.5)
const;
681 bool fuzzyComparePointsCyclic(
const VectorPath& path, qreal
eps = 0.0f)
const;
684 QPainterPath asPainterPath()
const;
740 const QPointF &c2, qreal
r2);
753 return rc.topLeft() + QPointF(pt.x() * rc.width(), pt.y() * rc.height());
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);
772 const qreal coeff = std::sqrt(
pow2(rc.width()) +
pow2(rc.height())) / std::sqrt(2.0);
773 return value * coeff;
781 const qreal coeff = std::sqrt(
pow2(rc.width()) +
pow2(rc.height())) / std::sqrt(2.0);
782 return coeff != 0 ?
value / coeff : 0;
804bool KRITAGLOBAL_EXPORT
fuzzyMatrixCompare(
const QTransform &t1,
const QTransform &t2, qreal delta);
820template <
template<
typename>
class Cont,
class Point>
823 if (c1.size() != c2.size())
return false;
825 const qreal
eps = delta;
827 return std::mismatch(c1.cbegin(),
830 [
eps] (
const QPointF &pt1,
const QPointF &pt2) {
831 return fuzzyPointCompare(pt1, pt2, eps);
841template<
class Rect,
typename Difference = decltype(Rect::w
idth())>
843 typedef decltype(
r1.topLeft()) Point;
845 const Point d1 =
abs(
r1.topLeft() -
r2.topLeft());
846 const Point d2 =
abs(
r1.bottomRight() -
r2.bottomRight());
848 const Difference maxError = std::max({d1.x(), d1.y(), d2.x(), d2.y()});
849 return maxError < tolerance;
854template<
class Polygon,
typename Difference = decltype(Polygon::first())>
856 typedef decltype(poly.first()) Point;
858 auto sameVert = [tolerance] (Point
p1, Point
p2) {
859 return std::abs(
p2.x() -
p1.x()) < tolerance;
863 auto sameHoriz = [tolerance] (Point
p1, Point
p2) {
864 return std::abs(
p2.y() -
p1.y()) < tolerance;
867 const int rectCorners = 4;
868 if (poly.length() != rectCorners) {
869 if (poly.length() != rectCorners + 1 || !
fuzzyPointCompare(poly[0], poly[rectCorners], tolerance)) {
874 bool correctRect = sameVert(poly[0], poly[1]) && sameHoriz(poly[1], poly[2]) && sameVert(poly[2], poly[3]) && sameHoriz(poly[3], poly[0]);
878 return sameHoriz(poly[0], poly[1]) && sameVert(poly[1], poly[2]) && sameHoriz(poly[2], poly[3]) && sameVert(poly[3], poly[0]);
885 const int numPoints = polygon.size();
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());
893 qreal prevAngle = secondAngle;
894 secondAngle -= baseAngle;
897 for (
int i = 2; i < numPoints; i++) {
898 int next = i == (numPoints - 1) ? 0 : i + 1;
902 qreal nextAngle = std::atan2(polygon[next].y() - polygon[i].y(), polygon[next].x() - polygon[i].x());
904 qreal newPrevAngle = nextAngle;
905 nextAngle -= baseAngle;
906 nextAngle -= prevAngle;
907 prevAngle = newPrevAngle;
908 if (nextAngle < -
M_PI) {
910 }
else if (nextAngle >
M_PI) {
927 return QTransform::fromScale(scaleX, scaleY);
946 return QTransform::fromTranslate(dx, dy);
963 translateTransform() *
977 qreal proj[3] = {0.0, 0.0, 1.0};
985std::pair<QPointF, QTransform> KRITAGLOBAL_EXPORT
transformEllipse(
const QPointF &axes,
const QTransform &fullLocalToGlobal);
987QPointF KRITAGLOBAL_EXPORT
alignForZoom(
const QPointF &pt, qreal zoom);
997 return y0 + (y1 - y0) * (x - x0) / (x1 - x0);
1006boost::optional<QPointF>
intersectLines(
const QLineF &boundedLine,
const QLineF &unboundedLine);
1015 const QPointF &
q1,
const QPointF &
q2);
1030boost::optional<QPointF> KRITAGLOBAL_EXPORT
findTrianglePointNearest(
const QPointF &
p1,
const QPointF &
p2, qreal a, qreal b,
const QPointF &nearest);
1032bool isOnLine(
const QLineF &line,
const QPointF &point,
const qreal
eps,
bool boundedStart,
bool boundedEnd,
bool includeEnds);
1050 const QPointF &base,
const QPointF &newBase,
1051 const QPointF &wingA,
const QPointF &wingB);
1067 const QPointF &base,
const QPointF &newBase,
1071QPointF KRITAGLOBAL_EXPORT
findNearestPointOnLine(
const QPointF &point,
const QLineF &line,
bool unbounded =
true);
1113 return (
m_n * maxRange +
m_d / 2) /
m_d;
1126 return (
m_n * maxRange +
m_d / 2) /
m_d;
1198QPainterPath KRITAGLOBAL_EXPORT
removeGutterSmart(
const QPainterPath& shape1,
int index1,
const QPainterPath& shape2,
int index2,
bool isSameShape);
1219QPainterPath KRITAGLOBAL_EXPORT
trySimplifyPath(
const QPainterPath &path, qreal lengthThreshold);
1223bool KRITAGLOBAL_EXPORT
isInsideShape(
const QPainterPath &path,
const QPointF &point);
qreal length(const QPointF &vec)
float value(const T *src, size_t ch)
Eigen::Matrix< double, 4, 2 > R
qreal distance(const QPointF &p1, const QPointF &p2)
a simple class to generate Halton sequence
int currentValue(int maxRange) const
HaltonSequenceGenerator(int base)
int generate(int maxRange)
qreal currentValue() const
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
QPainterPath m_originalPath
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
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 ¢er1, qreal r1, const QPointF ¢er2, 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)
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)
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)
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 transform() 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)