Krita Source Code Documentation
Loading...
Searching...
No Matches
KisAlgebra2D Namespace Reference

Namespaces

namespace  Private
 

Classes

struct  DecomposedMatrix
 
class  HaltonSequenceGenerator
 a simple class to generate Halton sequence More...
 
class  OuterCircle
 
struct  PointTypeTraits
 
struct  PointTypeTraits< QPoint >
 
struct  PointTypeTraits< QPointF >
 
class  RightHalfPlane
 
class  VectorPath
 

Enumerations

enum  KisTransformComponent {
  Translate = 0x1 , Scale = 0x2 , Rotate = 0x4 , Shear = 0x8 ,
  Project = 0x10
}
 

Functions

template<class Point >
Point abs (const Point &pt)
 
QPointF absoluteToRelative (const QPointF &pt, const QRectF &rc)
 
qreal absoluteToRelative (const qreal value, const QRectF &rc)
 
QRectF absoluteToRelative (const QRectF &rel, const QRectF &rc)
 
template<template< class T > class Container, class Point >
PointTypeTraits< Point >::rect_type accumulateBounds (const Container< Point > &points)
 
template<template< class T > class Container, class Point , class Rect >
void accumulateBounds (const Container< Point > &points, Rect *bounds)
 
template<class Point , class Rect >
void accumulateBounds (const Point &pt, Rect *bounds)
 
template<class Point , class Rect >
void accumulateBoundsNonEmpty (const Point &pt, Rect *bounds)
 
void adjustIfOnPolygonBoundary (const QPolygonF &poly, int polygonDirection, QPointF *pt)
 
QPointF alignForZoom (const QPointF &pt, qreal zoom)
 
qreal angleBetweenVectors (const QPointF &v1, const QPointF &v2)
 
QRect approximateRectFromPoints (const QVector< QPoint > &points)
 
QRectF approximateRectFromPoints (const QVector< QPointF > &points)
 
template<class Rect , class Point , bool alignPixels>
Rect approximateRectFromPointsImpl (const QVector< Point > &points)
 
QRect approximateRectWithPointTransform (const QRect &rect, std::function< QPointF(QPointF)> func)
 
template<class Rect >
Rect blowRect (const Rect &rect, qreal coeff)
 
QPolygonF calculateConvexHull (const QPolygonF &polygon)
 calculateConvexHull Calculate the convex hull of the polygon using the QuickHull
 
QPolygonF calculateConvexHullFromPointsOverTheLine (const QPolygonF &points, const QLineF &line)
 
template<class Point , class Rect >
Point clampPoint (Point pt, const Rect &bounds)
 
QPolygonF combineConvexHullParts (const QPolygonF &leftPolygon, QPolygonF &rightPolygon, bool triangular)
 
KisTransformComponents compareTransformComponents (const QTransform &lhs, const QTransform &rhs)
 
KisTransformComponents componentsForTransform (const QTransform &t)
 
template<typename T >
copysign (T x, T y)
 
template<class Point >
PointTypeTraits< Point >::rect_type createRectFromCorners (Point corner1, Point corner2)
 
QRectF createRectFromCorners (QLineF line)
 
void cropLineToConvexPolygon (QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
 
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()).
 
template<class T >
PointTypeTraits< T >::value_type crossProduct (const T &a, const T &b)
 
QRectF cutOffRect (const QRectF &rc, const KisAlgebra2D::RightHalfPlane &p)
 
qreal directionBetweenPoints (const QPointF &p1, const QPointF &p2, qreal defaultAngle)
 
template<class T >
std::enable_if< std::is_integral< T >::value, T >::type divideFloor (T a, T b)
 
template<class T >
PointTypeTraits< T >::value_type dotProduct (const T &a, const T &b)
 
QPoint ensureInRect (QPoint pt, const QRect &bounds)
 
QPointF ensureInRect (QPointF pt, const QRectF &bounds)
 
template<class Point , class Rect >
Point ensureInRectImpl (Point pt, const Rect &bounds)
 
template<class Rect >
Rect ensureRectNotSmaller (Rect rc, const decltype(Rect().size()) &size)
 
template<class Size >
Size ensureSizeNotSmaller (const Size &size, const Size &bounds)
 
qreal findMinimumGoldenSection (std::function< qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter=100)
 
qreal findMinimumTernarySection (std::function< qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter=100)
 
QPointF findNearestPointOnLine (const QPointF &point, const QLineF &line, bool unbounded)
 
QVector< QPointF > findTrianglePoint (const QPointF &p1, const QPointF &p2, qreal a, qreal b)
 
boost::optional< QPointF > findTrianglePointNearest (const QPointF &p1, const QPointF &p2, qreal a, qreal b, const QPointF &nearest)
 
Eigen::Matrix3d fromQTransformStraight (const QTransform &t)
 
template<class Rect , typename Difference = decltype(Rect::width())>
bool fuzzyCompareRects (const Rect &r1, const Rect &r2, Difference tolerance)
 
bool fuzzyMatrixCompare (const QTransform &t1, const QTransform &t2, qreal delta)
 
template<template< typename > class Cont, class Point >
bool fuzzyPointCompare (const Cont< Point > &c1, const Cont< Point > &c2, qreal delta)
 
bool fuzzyPointCompare (const QPointF &p1, const QPointF &p2)
 
bool fuzzyPointCompare (const QPointF &p1, const QPointF &p2, qreal delta)
 
QLineF getLineFromElements (const QPainterPath &shape, int index)
 
QList< int > getLineSegmentCrossingLineIndexes (const QLineF &line, const QPainterPath &shape)
 
QPainterPath getOnePathFromRectangleCutThrough (const QList< QPointF > &points, const QLineF &line, bool left)
 
QList< QLineF > getParallelLines (const QLineF &line, const qreal distance)
 
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 (supposedly parallel) lines It is used in the Knife Tool If you just want to cut a rectangle, you can use the same line in both
 
QList< QLineF > intersectLineConcavePolygon (const QPolygonF polygon, const QLineF &line, bool extendFirst, bool extendSecond)
 
bool intersectLineConvexPolygon (QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
 
bool intersectLineRect (QLineF &line, const QRect rect, bool extend)
 
bool intersectLineRect (QLineF &line, const QRect rect, bool extendFirst, bool extendSecond)
 
boost::optional< QPointF > intersectLines (const QLineF &boundedLine, const QLineF &unboundedLine)
 
boost::optional< QPointF > intersectLines (const QPointF &p1, const QPointF &p2, const QPointF &q1, const QPointF &q2)
 
QVector< QPointF > intersectTwoCircles (const QPointF &center1, qreal r1, const QPointF &center2, qreal r2)
 
template<class T >
inwardUnitNormal (const T &a, int polygonDirection)
 
template<typename T >
bool isInRange (T x, T a, T b)
 
bool isInsideShape (const QPainterPath &path, const QPointF &point)
 
bool isInsideShape (const VectorPath &path, const QPointF &point)
 
bool isOnLine (const QLineF &line, const QPointF &point, const qreal eps, bool boundedStart, bool boundedEnd, bool includeEnds)
 
template<class Polygon , typename Difference = decltype(Polygon::first())>
bool isPolygonRect (const Polygon &poly, Difference tolerance)
 
template<class T >
bool isPolygonTrulyConvex (const QVector< T > &polygon)
 
template<typename R >
R lazyRound (qreal value)
 
template<>
int lazyRound< int > (qreal value)
 
template<>
qreal lazyRound< qreal > (qreal value)
 
template<class T >
leftUnitNormal (const T &a)
 
template<typename Point >
Point lerp (const Point &pt1, const Point &pt2, qreal t)
 
template<typename T >
linearReshapeFunc (T x, T x0, T x1, T y0, T y1)
 
int lineSideForPoint (const QLineF &line, const QPointF &point)
 
KisTransformComponents makeFullTransformComponents ()
 
QTransform mapToRect (const QRectF &rect)
 
QTransform mapToRectInverse (const QRectF &rect)
 
template<class Size >
auto maxDimension (Size size) -> decltype(size.width())
 
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)
 
template<class Size >
auto minDimension (Size size) -> decltype(size.width())
 
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
 
QPointF moveElasticPoint (const QPointF &pt, const QPointF &base, const QPointF &newBase, const QVector< QPointF > &anchorPoints)
 moveElasticPoint moves point pt based on the model of elasticity
 
QPointF movePointAlongTheLine (const QPointF &point, const QLineF &direction, qreal distance, bool ensureOnLine)
 movePointAlongTheLine moves the point a particular distance in the specified direction
 
QPointF movePointInTheDirection (const QPointF &point, const QPointF &direction, qreal distance)
 movePointAlongTheLine moves the point a particular distance in the specified direction
 
template<class T >
qreal norm (const T &a)
 
template<class Point >
Point normalize (const Point &a)
 
template<class T >
qreal normSquared (const T &a)
 
QDebug operator<< (QDebug debug, const VectorPath &path)
 
QDebug operator<< (QDebug debug, const VectorPath::VectorPathPoint &point)
 
qreal pointToLineDistSquared (const QPointF &pt, const QLineF &line)
 
template<class T >
int polygonDirection (const QVector< T > &polygon)
 
 Q_DECLARE_FLAGS (KisTransformComponents, KisTransformComponent)
 
int quadraticEquation (qreal a, qreal b, qreal c, qreal *x1, qreal *x2)
 
QPointF relativeToAbsolute (const QPointF &pt, const QRectF &rc)
 
QRectF relativeToAbsolute (const QRectF &rel, const QRectF &rc)
 
qreal relativeToAbsolute (qreal value, const QRectF &rc)
 
QPainterPath removeGutterOneEndSmart (const QPainterPath &shape1, int index1, const QPainterPath &shape2, int index2, QLineF middleLine, bool reverseFirstPoly, bool reverseSecondPoly)
 
QPainterPath removeGutterSmart (const QPainterPath &shape1, int index1, const QPainterPath &shape2, int index2, bool isSameShape)
 removeGutterSmart
 
QLineF reverseDirection (const QLineF &line)
 
template<class T >
rightUnitNormal (const T &a)
 
QVector< QPoint > sampleRectWithPoints (const QRect &rect)
 
QVector< QPointF > sampleRectWithPoints (const QRectF &rect)
 
template<class Rect , class Point >
QVector< Point > sampleRectWithPoints (const Rect &rect)
 
template<typename T >
signPZ (T x)
 
template<typename T >
signZZ (T x)
 
QPainterPath simplifyShape (const QPainterPath &path)
 
QPainterPath smallArrow ()
 
QTransform toQTransformStraight (const Eigen::Matrix3d &m)
 
QPointF transformAsBase (const QPointF &pt, const QPointF &base1, const QPointF &base2)
 
std::pair< QPointF, QTransform > transformEllipse (const QPointF &axes, const QTransform &fullLocalToGlobal)
 
bool tryMergePoints (QPainterPath &path, const QPointF &startPoint, const QPointF &endPoint, qreal &distance, qreal distanceThreshold, bool lastSegment)
 
QPainterPath trySimplifyPath (const QPainterPath &path, qreal lengthThreshold)
 trySimplifyPath Tries to simplify a QPainterPath
 
template<typename T >
wrapValue (T value, T min, T max)
 
template<typename T , typename std::enable_if< std::is_integral< T >::value, T >::type * = nullptr>
wrapValue (T value, T wrapBounds)
 

Enumeration Type Documentation

◆ KisTransformComponent

Enumerator
Translate 
Scale 
Rotate 
Shear 
Project 

Definition at line 15 of file KisTransformComponents.h.

Function Documentation

◆ abs()

template<class Point >
Point KisAlgebra2D::abs ( const Point & pt)
inline

Definition at line 477 of file kis_algebra_2d.h.

477 {
478 return Point(qAbs(pt.x()), qAbs(pt.y()));
479}

◆ absoluteToRelative() [1/3]

QPointF KisAlgebra2D::absoluteToRelative ( const QPointF & pt,
const QRectF & rc )
inline

Get the relative position of pt inside rectangle rc. The point can be outside the rectangle.

Definition at line 760 of file kis_algebra_2d.h.

760 {
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}

◆ absoluteToRelative() [2/3]

qreal KisAlgebra2D::absoluteToRelative ( const qreal value,
const QRectF & rc )
inline

Scales absolute isotropic value from absolute to relative coordinate system using SVG 1.1 rules (see chapter 7.10)

Definition at line 780 of file kis_algebra_2d.h.

780 {
781 const qreal coeff = std::sqrt(pow2(rc.width()) + pow2(rc.height())) / std::sqrt(2.0);
782 return coeff != 0 ? value / coeff : 0;
783}
float value(const T *src, size_t ch)
T pow2(const T &x)
Definition kis_global.h:166

References pow2(), and value().

◆ absoluteToRelative() [3/3]

QRectF KisAlgebra2D::absoluteToRelative ( const QRectF & rel,
const QRectF & rc )
inline

Scales absolute isotropic value from absolute to relative coordinate system using SVG 1.1 rules (see chapter 7.10)

Definition at line 797 of file kis_algebra_2d.h.

797 {
798 return QRectF(absoluteToRelative(rel.topLeft(), rc), absoluteToRelative(rel.bottomRight(), rc));
799}
QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc)

References absoluteToRelative().

◆ accumulateBounds() [1/3]

template<template< class T > class Container, class Point >
PointTypeTraits< Point >::rect_type KisAlgebra2D::accumulateBounds ( const Container< Point > & points)
inline

Definition at line 288 of file kis_algebra_2d.h.

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}
void accumulateBounds(const Point &pt, Rect *bounds)

References accumulateBounds().

◆ accumulateBounds() [2/3]

template<template< class T > class Container, class Point , class Rect >
void KisAlgebra2D::accumulateBounds ( const Container< Point > & points,
Rect * bounds )
inline

Definition at line 279 of file kis_algebra_2d.h.

280{
281 Q_FOREACH (const Point &pt, points) {
283 }
284}
#define bounds(x, a, b)

References accumulateBounds(), and bounds.

◆ accumulateBounds() [3/3]

template<class Point , class Rect >
void KisAlgebra2D::accumulateBounds ( const Point & pt,
Rect * bounds )
inline

Rect::left() is cheaper than Rect::right(), so check it first

Rect::top() is cheaper than Rect::bottom(), so check it first

Definition at line 235 of file kis_algebra_2d.h.

236{
237 if (bounds->isEmpty()) {
238 Private::resetEmptyRectangle(pt, bounds);
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}

References bounds, and KisAlgebra2D::Private::resetEmptyRectangle().

◆ accumulateBoundsNonEmpty()

template<class Point , class Rect >
void KisAlgebra2D::accumulateBoundsNonEmpty ( const Point & pt,
Rect * bounds )
inline

Definition at line 263 of file kis_algebra_2d.h.

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}

References bounds.

◆ adjustIfOnPolygonBoundary()

void KRITAGLOBAL_EXPORT KisAlgebra2D::adjustIfOnPolygonBoundary ( const QPolygonF & poly,
int polygonDirection,
QPointF * pt )

Definition at line 37 of file kis_algebra_2d.cpp.

38{
39 const int numPoints = poly.size();
40 for (int i = 0; i < numPoints; i++) {
41 int nextI = i + 1;
42 if (nextI >= numPoints) {
43 nextI = 0;
44 }
45
46 const QPointF &p0 = poly[i];
47 const QPointF &p1 = poly[nextI];
48
49 QPointF edge = p1 - p0;
50
51 qreal cross = crossProduct(edge, *pt - p0)
52 / (0.5 * edge.manhattanLength());
53
54 if (cross < 1.0 &&
55 isInRange(pt->x(), p0.x(), p1.x()) &&
56 isInRange(pt->y(), p0.y(), p1.y())) {
57
58 QPointF salt = 1.0e-3 * inwardUnitNormal(edge, polygonDirection);
59
60 QPointF adjustedPoint = *pt + salt;
61
62 // in case the polygon is self-intersecting, polygon direction
63 // might not help
64 if (kisDistanceToLine(adjustedPoint, QLineF(p0, p1)) < 1e-4) {
65 adjustedPoint = *pt - salt;
66
67#ifdef SANITY_CHECKS
68 if (kisDistanceToLine(adjustedPoint, QLineF(p0, p1)) < 1e-4) {
69 dbgKrita << ppVar(*pt);
70 dbgKrita << ppVar(adjustedPoint);
71 dbgKrita << ppVar(QLineF(p0, p1));
73
74 dbgKrita << ppVar(poly.containsPoint(*pt, Qt::OddEvenFill));
75
76 dbgKrita << ppVar(kisDistanceToLine(*pt, QLineF(p0, p1)));
77 dbgKrita << ppVar(kisDistanceToLine(adjustedPoint, QLineF(p0, p1)));
78 }
79
80 *pt = adjustedPoint;
81
82 KIS_ASSERT_RECOVER_NOOP(kisDistanceToLine(*pt, QLineF(p0, p1)) > 1e-4);
83#endif /* SANITY_CHECKS */
84 }
85 }
86 }
87}
QPointF p0
QPointF p1
bool isInRange(const QPointF &center, qreal range, const SpatialNodeData &data)
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define dbgKrita
Definition kis_debug.h:45
#define ppVar(var)
Definition kis_debug.h:155
qreal kisDistanceToLine(const QPointF &m, const QLineF &line)
Definition kis_global.h:234
const unsigned char salt[256][256]
Definition rand_salt.h:30

References crossProduct(), dbgKrita, inwardUnitNormal(), isInRange(), KIS_ASSERT_RECOVER_NOOP, kisDistanceToLine(), p0, p1, polygonDirection(), ppVar, and salt.

◆ alignForZoom()

QPointF KRITAGLOBAL_EXPORT KisAlgebra2D::alignForZoom ( const QPointF & pt,
qreal zoom )

Definition at line 970 of file kis_algebra_2d.cpp.

971{
972 return QPointF((pt * zoom).toPoint()) / zoom;
973}

◆ angleBetweenVectors()

qreal KRITAGLOBAL_EXPORT KisAlgebra2D::angleBetweenVectors ( const QPointF & v1,
const QPointF & v2 )

Definition at line 111 of file kis_algebra_2d.cpp.

112{
113 qreal a1 = std::atan2(v1.y(), v1.x());
114 qreal a2 = std::atan2(v2.y(), v2.x());
115
116 return a2 - a1;
117}

◆ approximateRectFromPoints() [1/2]

QRect KRITAGLOBAL_EXPORT KisAlgebra2D::approximateRectFromPoints ( const QVector< QPoint > & points)

Definition at line 545 of file kis_algebra_2d.cpp.

546 {
547 return approximateRectFromPointsImpl<QRect, QPoint, true>(points);
548 }

◆ approximateRectFromPoints() [2/2]

QRectF KRITAGLOBAL_EXPORT KisAlgebra2D::approximateRectFromPoints ( const QVector< QPointF > & points)

Definition at line 550 of file kis_algebra_2d.cpp.

551 {
552 return approximateRectFromPointsImpl<QRectF, QPointF, false>(points);
553 }

◆ approximateRectFromPointsImpl()

template<class Rect , class Point , bool alignPixels>
Rect KisAlgebra2D::approximateRectFromPointsImpl ( const QVector< Point > & points)

Definition at line 521 of file kis_algebra_2d.cpp.

522 {
523 using namespace boost::accumulators;
524 accumulator_set<qreal, stats<tag::min, tag::max > > accX;
525 accumulator_set<qreal, stats<tag::min, tag::max > > accY;
526
527 Q_FOREACH (const Point &pt, points) {
528 accX(pt.x());
529 accY(pt.y());
530 }
531
532 Rect resultRect;
533
534 if (alignPixels) {
535 resultRect.setCoords(std::floor(min(accX)), std::floor(min(accY)),
536 std::ceil(max(accX)), std::ceil(max(accY)));
537 } else {
538 resultRect.setCoords(min(accX), min(accY),
539 max(accX), max(accY));
540 }
541
542 return resultRect;
543 }
T min(T a, T b, T c)
constexpr std::enable_if< sizeof...(values)==0, size_t >::type max()

◆ approximateRectWithPointTransform()

QRect KRITAGLOBAL_EXPORT KisAlgebra2D::approximateRectWithPointTransform ( const QRect & rect,
std::function< QPointF(QPointF)> func )

Definition at line 555 of file kis_algebra_2d.cpp.

556 {
558
559 using namespace boost::accumulators;
560 accumulator_set<qreal, stats<tag::min, tag::max > > accX;
561 accumulator_set<qreal, stats<tag::min, tag::max > > accY;
562
563 Q_FOREACH (const QPoint &pt, points) {
564 QPointF dstPt = func(pt);
565
566 accX(dstPt.x());
567 accY(dstPt.y());
568 }
569
570 QRect resultRect;
571 resultRect.setCoords(std::floor(min(accX)), std::floor(min(accY)),
572 std::ceil(max(accX)), std::ceil(max(accY)));
573
574 return resultRect;
575 }
QVector< Point > sampleRectWithPoints(const Rect &rect)

References sampleRectWithPoints().

◆ blowRect()

template<class Rect >
Rect KisAlgebra2D::blowRect ( const Rect & rect,
qreal coeff )

Multiply width and height of rect by coeff keeping the center of the rectangle pinned

Definition at line 354 of file kis_algebra_2d.h.

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}

◆ calculateConvexHull()

QPolygonF KRITAGLOBAL_EXPORT KisAlgebra2D::calculateConvexHull ( const QPolygonF & polygon)

calculateConvexHull Calculate the convex hull of the polygon using the QuickHull

Parameters
polygonto find the convex hull of
Returns

Definition at line 1412 of file kis_algebra_2d.cpp.

1413{
1414 if (polygon.count() < 4) {
1415 return polygon;
1416 }
1417 QPointF leftPoint = polygon[0];
1418 QPointF rightPoint = polygon[1];
1419 if (leftPoint.x() > rightPoint.x()) { // swap them around
1420 leftPoint = polygon[1];
1421 rightPoint = polygon[0];
1422 }
1423
1424 Q_FOREACH(QPointF point, polygon) {
1425 if (point.x() < leftPoint.x()) {
1426 leftPoint = point;
1427 } else if (point.x() > rightPoint.x()) {
1428 rightPoint = point;
1429 }
1430 }
1431 QLineF line(leftPoint, rightPoint);
1432 QLineF lineOther(rightPoint, leftPoint);
1433 QPolygonF left;
1434 QPolygonF right;
1435
1436 Q_FOREACH(QPointF point, polygon) {
1437 qCritical() << "Checking point " << point << "and line" << line << ": " << lineSideForPoint(line, point);
1438 if (lineSideForPoint(line, point) > 0) {
1439 left << point;
1440 } else if (lineSideForPoint(line, point) < 0) {
1441 right << point;
1442 }
1443 }
1444
1445 qCritical() << "Using line: " << line << "for points: " << left;
1446 qCritical() << "Using line: " << lineOther << "for points: " << right;
1447
1449 right = calculateConvexHullFromPointsOverTheLine(right, lineOther);
1450
1451 qCritical() << "Then the result: " << left << right;
1452
1453 QPolygonF result = combineConvexHullParts(left, right, false);
1454
1455 return result;
1456
1457}
QPolygonF combineConvexHullParts(const QPolygonF &leftPolygon, QPolygonF &rightPolygon, bool triangular)
QPolygonF calculateConvexHullFromPointsOverTheLine(const QPolygonF &points, const QLineF &line)
int lineSideForPoint(const QLineF &line, const QPointF &point)

References calculateConvexHullFromPointsOverTheLine(), combineConvexHullParts(), and lineSideForPoint().

◆ calculateConvexHullFromPointsOverTheLine()

QPolygonF KisAlgebra2D::calculateConvexHullFromPointsOverTheLine ( const QPolygonF & points,
const QLineF & line )

Definition at line 1356 of file kis_algebra_2d.cpp.

1357{
1358 // all the points should be on the correct side already, no need to check it
1359 if (points.count() < 1) {
1360 QPolygonF result;
1361 result << line.p1() << line.p2() << line.p1();
1362 return result;
1363 }
1364 if (points.count() == 1) {
1365 QList<QPointF> list = points.toList();
1366
1367 QPolygonF result;
1368 result << line.p1();
1369 result << list[0];
1370 result << line.p2();
1371 result << line.p1();
1372
1373 return result;
1374 }
1375
1376 double maxDistance = 0;
1377 QPointF nextPoint = points[0];
1378 Q_FOREACH(QPointF point, points) {
1379 double distance = kisSquareDistanceToLine(point, line);
1380 if (distance > maxDistance) {
1381 maxDistance = distance;
1382 nextPoint = point;
1383 }
1384 }
1385
1386 QPolygonF left;
1387 QLineF lineForLeft = QLineF(line.p1(), nextPoint);
1388 QPolygonF right;
1389 QLineF lineForRight = QLineF(nextPoint, line.p2());
1390 QPolygonF triangle;
1391 triangle << line.p1() << line.p2() << nextPoint << line.p1();
1392 Q_FOREACH(QPointF point, points) {
1393 if (triangle.containsPoint(point, Qt::WindingFill)) {
1394 continue;
1395 }
1396 if (lineSideForPoint(lineForLeft, point) > 0) {
1397 left << point;
1398 } else if (lineSideForPoint(lineForRight, point) < 0) {
1399 right << point;
1400 }
1401 }
1402
1403 QPolygonF leftPolygon = calculateConvexHullFromPointsOverTheLine(left, lineForLeft);
1404 QPolygonF rightPolygon = calculateConvexHullFromPointsOverTheLine(right, lineForRight);
1405
1406 QPolygonF result = combineConvexHullParts(leftPolygon, rightPolygon, true);
1407
1408 return result;
1409}
qreal distance(const QPointF &p1, const QPointF &p2)
qreal kisSquareDistanceToLine(const QPointF &m, const QLineF &line)
Definition kis_global.h:256

References calculateConvexHullFromPointsOverTheLine(), combineConvexHullParts(), distance(), kisSquareDistanceToLine(), and lineSideForPoint().

◆ clampPoint()

template<class Point , class Rect >
Point KisAlgebra2D::clampPoint ( Point pt,
const Rect & bounds )
inline

Definition at line 300 of file kis_algebra_2d.h.

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}

References bounds.

◆ combineConvexHullParts()

QPolygonF KisAlgebra2D::combineConvexHullParts ( const QPolygonF & leftPolygon,
QPolygonF & rightPolygon,
bool triangular )

Definition at line 1327 of file kis_algebra_2d.cpp.

1327 {
1328 // resulting polygon should start at p1, go through all the points, go to p2, then to p1 again
1329 // triangular: whether the last point of right is equal to the first point in left or not
1330
1331 QPolygonF left = leftPolygon;
1332 QPolygonF right = rightPolygon;
1333
1334 left.removeLast(); // remove p1 from the end (but not the beginning)
1335 left.removeLast(); // remove nextPoint as well, since it's present in rightPolygon (twice)
1336
1337 right.removeLast(); // remove nextPoint from the end (but not the beginning)
1338
1339
1340 QPolygonF result;
1341 Q_FOREACH(QPointF point, left) {
1342 result << point;
1343 }
1344
1345 Q_FOREACH(QPointF point, right) {
1346 result << point;
1347 }
1348
1349 if (triangular) {
1350 result << left[0];
1351 }
1352
1353 return result;
1354}

◆ compareTransformComponents()

KisTransformComponents KRITAGLOBAL_EXPORT KisAlgebra2D::compareTransformComponents ( const QTransform & lhs,
const QTransform & rhs )

Definition at line 43 of file KisTransformComponents.cpp.

44{
47
48 KisTransformComponents result;
49
50 if (qFuzzyCompare(m1.dx, m2.dx) && qFuzzyCompare(m1.dy, m2.dy)) {
51 result.setFlag(KisTransformComponent::Translate);
52 }
53
54 if (qFuzzyCompare(m1.scaleX, m2.scaleX) && qFuzzyCompare(m1.scaleX, m2.scaleY)) {
55 result.setFlag(KisTransformComponent::Scale);
56 }
57
58 if (qFuzzyCompare(m1.shearXY, m2.shearXY)) {
59 result.setFlag(KisTransformComponent::Shear);
60 }
61
62 if (qFuzzyCompare(m1.angle, m2.angle)) {
63 result.setFlag(KisTransformComponent::Rotate);
64 }
65
66 if (qFuzzyCompare(m1.proj[0], m2.proj[0]) &&
67 qFuzzyCompare(m1.proj[1], m2.proj[1]) &&
68 qFuzzyCompare(m1.proj[2], m2.proj[2])) {
69
70 result.setFlag(KisTransformComponent::Project);
71 }
72
73 return result;
74}
static bool qFuzzyCompare(half p1, half p2)

References KisAlgebra2D::DecomposedMatrix::angle, KisAlgebra2D::DecomposedMatrix::dx, KisAlgebra2D::DecomposedMatrix::dy, KisAlgebra2D::DecomposedMatrix::proj, Project, qFuzzyCompare(), Rotate, Scale, KisAlgebra2D::DecomposedMatrix::scaleX, KisAlgebra2D::DecomposedMatrix::scaleY, Shear, KisAlgebra2D::DecomposedMatrix::shearXY, and Translate.

◆ componentsForTransform()

KisTransformComponents KRITAGLOBAL_EXPORT KisAlgebra2D::componentsForTransform ( const QTransform & t)

Definition at line 19 of file KisTransformComponents.cpp.

20{
21 KisTransformComponents result;
22
24
25 result.setFlag(KisTransformComponent::Translate,
26 !qFuzzyIsNull(m.dx) || !qFuzzyIsNull(m.dy));
27
28 result.setFlag(KisTransformComponent::Scale,
29 !qFuzzyCompare(m.scaleX, 1.0) || !qFuzzyCompare(m.scaleY, 1.0));
30
31 result.setFlag(KisTransformComponent::Shear,
32 !qFuzzyIsNull(m.shearXY));
33
34 result.setFlag(KisTransformComponent::Rotate,
35 !qFuzzyIsNull(m.angle));
36
37 result.setFlag(KisTransformComponent::Project,
38 !qFuzzyIsNull(m.proj[0]) || !qFuzzyIsNull(m.proj[1]) || !qFuzzyCompare(m.proj[2], 1.0));
39
40 return result;
41}
static bool qFuzzyIsNull(half h)

References KisAlgebra2D::DecomposedMatrix::angle, KisAlgebra2D::DecomposedMatrix::dx, KisAlgebra2D::DecomposedMatrix::dy, KisAlgebra2D::DecomposedMatrix::proj, Project, qFuzzyCompare(), qFuzzyIsNull(), Rotate, Scale, KisAlgebra2D::DecomposedMatrix::scaleX, KisAlgebra2D::DecomposedMatrix::scaleY, Shear, KisAlgebra2D::DecomposedMatrix::shearXY, and Translate.

◆ copysign()

template<typename T >
T KisAlgebra2D::copysign ( T x,
T y )
inline

Copies the sign of y into x and returns the result

Definition at line 107 of file kis_algebra_2d.h.

107 {
108 T strippedX = qAbs(x);
109 return y >= T(0) ? strippedX : -strippedX;
110}

◆ createRectFromCorners() [1/2]

template<class Point >
PointTypeTraits< Point >::rect_type KisAlgebra2D::createRectFromCorners ( Point corner1,
Point corner2 )
inline

Definition at line 323 of file kis_algebra_2d.h.

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}

◆ createRectFromCorners() [2/2]

QRectF KisAlgebra2D::createRectFromCorners ( QLineF line)
inline

Definition at line 328 of file kis_algebra_2d.h.

329{
330 QPointF a = line.p1();
331 QPointF b = line.p2();
332 return createRectFromCorners(a, b);
333}
PointTypeTraits< Point >::rect_type createRectFromCorners(Point corner1, Point corner2)

References createRectFromCorners().

◆ cropLineToConvexPolygon()

void KRITAGLOBAL_EXPORT KisAlgebra2D::cropLineToConvexPolygon ( QLineF & line,
const QPolygonF polygon,
bool extendFirst,
bool extendSecond )

Definition at line 1300 of file kis_algebra_2d.cpp.

1301{
1302 bool intersects = intersectLineConvexPolygon(line, polygon, extendFirst, extendSecond);
1303 if (!intersects) {
1304 line = QLineF(); // empty line to help with drawing
1305 }
1306}
bool intersectLineConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)

References intersectLineConvexPolygon().

◆ cropLineToRect()

void KRITAGLOBAL_EXPORT KisAlgebra2D::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()).

This is using intersectLineRect, but with the difference that it doesn't require the user to check the return value. It's useful for drawing code, since it let the developer not use if before drawing.

If the line intersects the rectangle, it will be modified to represent the intersecting line segment. If the line does not intersect the area, it will return an empty one-point line.

extendFirst and extendSecond parameters allow one to use this function in case of unbounded lines (if both are true), line segments (if both are false) or half-lines/rays (if one is true and another is false). 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.

Parameters
lineline segment
rectarea
extendFirstextend the line to the edge of the rect area even if the first point of the line segment lies inside the rectangle
extendSecondextend the line to the edge of the rect area even if the second point of the line segment lies inside the rectangle

Definition at line 1292 of file kis_algebra_2d.cpp.

1293{
1294 bool intersects = intersectLineRect(line, rect, extendFirst, extendSecond);
1295 if (!intersects) {
1296 line = QLineF(); // empty line to help with drawing
1297 }
1298}
bool intersectLineRect(QLineF &line, const QRect rect, bool extend)

References intersectLineRect().

◆ crossProduct()

template<class T >
PointTypeTraits< T >::value_type KisAlgebra2D::crossProduct ( const T & a,
const T & b )

Definition at line 57 of file kis_algebra_2d.h.

58{
59 return a.x() * b.y() - a.y() * b.x();
60}

◆ cutOffRect()

KRITAGLOBAL_EXPORT QRectF KisAlgebra2D::cutOffRect ( const QRectF & rc,
const KisAlgebra2D::RightHalfPlane & p )

Cuts off a portion of a rect rc defined by a half-plane p

Returns
the bounding rect of the resulting polygon

Definition at line 578 of file kis_algebra_2d.cpp.

579{
580 QVector<QPointF> points;
581
582 const QLineF cutLine = p.getLine();
583
584 points << rc.topLeft();
585 points << rc.topRight();
586 points << rc.bottomRight();
587 points << rc.bottomLeft();
588
589 QPointF p1 = points[3];
590 bool p1Valid = p.pos(p1) >= 0;
591
592 QVector<QPointF> resultPoints;
593
594 for (int i = 0; i < 4; i++) {
595 const QPointF p2 = points[i];
596 const bool p2Valid = p.pos(p2) >= 0;
597
598 if (p1Valid != p2Valid) {
599 QPointF intersection;
600 cutLine.intersects(QLineF(p1, p2), &intersection);
601 resultPoints << intersection;
602 }
603
604 if (p2Valid) {
605 resultPoints << p2;
606 }
607
608 p1 = p2;
609 p1Valid = p2Valid;
610 }
611
612 return approximateRectFromPoints(resultPoints);
613}
const Params2D p
QPointF p2
QRect approximateRectFromPoints(const QVector< QPoint > &points)

References approximateRectFromPoints(), p, p1, and p2.

◆ directionBetweenPoints()

qreal KRITAGLOBAL_EXPORT KisAlgebra2D::directionBetweenPoints ( const QPointF & p1,
const QPointF & p2,
qreal defaultAngle )

Computes an angle indicating the direction from p1 to p2. If p1 and p2 are too close together to compute an angle, defaultAngle is returned.

Definition at line 119 of file kis_algebra_2d.cpp.

120{
121 if (fuzzyPointCompare(p1, p2)) {
122 return defaultAngle;
123 }
124
125 const QVector2D diff(p2 - p1);
126 return std::atan2(diff.y(), diff.x());
127}
bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2)

References fuzzyPointCompare(), p1, and p2.

◆ divideFloor()

template<class T >
std::enable_if< std::is_integral< T >::value, T >::type KisAlgebra2D::divideFloor ( T a,
T b )

Definition at line 114 of file kis_algebra_2d.h.

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}

◆ dotProduct()

template<class T >
PointTypeTraits< T >::value_type KisAlgebra2D::dotProduct ( const T & a,
const T & b )

Definition at line 51 of file kis_algebra_2d.h.

52{
53 return a.x() * b.x() + a.y() * b.y();
54}

◆ ensureInRect() [1/2]

QPoint KRITAGLOBAL_EXPORT KisAlgebra2D::ensureInRect ( QPoint pt,
const QRect & bounds )

Definition at line 163 of file kis_algebra_2d.cpp.

164{
165 return ensureInRectImpl(pt, bounds);
166}
Point ensureInRectImpl(Point pt, const Rect &bounds)

References bounds, and ensureInRectImpl().

◆ ensureInRect() [2/2]

QPointF KRITAGLOBAL_EXPORT KisAlgebra2D::ensureInRect ( QPointF pt,
const QRectF & bounds )

Definition at line 168 of file kis_algebra_2d.cpp.

169{
170 return ensureInRectImpl(pt, bounds);
171}

References bounds, and ensureInRectImpl().

◆ ensureInRectImpl()

template<class Point , class Rect >
Point KisAlgebra2D::ensureInRectImpl ( Point pt,
const Rect & bounds )
inline

Definition at line 146 of file kis_algebra_2d.cpp.

147{
148 if (pt.x() > bounds.right()) {
149 pt.rx() = bounds.right();
150 } else if (pt.x() < bounds.left()) {
151 pt.rx() = bounds.left();
152 }
153
154 if (pt.y() > bounds.bottom()) {
155 pt.ry() = bounds.bottom();
156 } else if (pt.y() < bounds.top()) {
157 pt.ry() = bounds.top();
158 }
159
160 return pt;
161}

References bounds.

◆ ensureRectNotSmaller()

template<class Rect >
Rect KisAlgebra2D::ensureRectNotSmaller ( Rect rc,
const decltype(Rect().size()) & size )

Definition at line 368 of file kis_algebra_2d.h.

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}

◆ ensureSizeNotSmaller()

template<class Size >
Size KisAlgebra2D::ensureSizeNotSmaller ( const Size & size,
const Size & bounds )

Definition at line 386 of file kis_algebra_2d.h.

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}
T copysign(T x, T y)

References bounds, and copysign().

◆ findMinimumGoldenSection()

qreal KRITAGLOBAL_EXPORT KisAlgebra2D::findMinimumGoldenSection ( std::function< qreal(qreal)> f,
qreal xA,
qreal xB,
qreal eps,
int maxIter = 100 )

Definition at line 1484 of file kis_algebra_2d.cpp.

1485{
1486 // requirements:
1487 // only one local minimum between xA and xB
1488
1489 int i = 0;
1490 const double phi = 0.618033988749894; // 1/(golden ratio)
1491 qreal a = xA;
1492 qreal b = xB;
1493 qreal c = b - (b - a)*phi;
1494 qreal d = a + (b - a)*phi;
1495
1496 while (qAbs(b - a) > eps) {
1497 if (f(c) < f(d)) {
1498 b = d;
1499 } else {
1500 a = c;
1501 }
1502
1503 // Wikipedia says to recompute both c and d here to avoid loss of precision which may lead to incorrect results or infinite loop
1504 c = b - (b - a) * phi;
1505 d = a + (b - a) * phi;
1506
1507 i++;
1508 if (i > maxIter) {
1509 break;
1510 }
1511
1512 }
1513
1514 return (b + a)/2;
1515}
const qreal eps

References eps.

◆ findMinimumTernarySection()

qreal KRITAGLOBAL_EXPORT KisAlgebra2D::findMinimumTernarySection ( std::function< qreal(qreal)> f,
qreal xA,
qreal xB,
qreal eps,
int maxIter = 100 )

Definition at line 1517 of file kis_algebra_2d.cpp.

1518{
1519 // requirements:
1520 // only one local minimum between xA and xB
1521
1522 int i = 0;
1523 qreal l = qMin(xA, xB);
1524 qreal r = qMax(xA, xB);
1525
1526
1527 qreal m1 = l + (r - l)/3;
1528 qreal m2 = r - (r - l)/3;
1529
1530 while ((r - l) > eps) {
1531 qreal f1 = f(m1);
1532 qreal f2 = f(m2);
1533
1534 if (f1 > f2) {
1535 l = m1;
1536 } else {
1537 r = m2;
1538 }
1539
1540 // 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
1541 // so it's probably a good idea to do it here too
1542 m1 = l + (r - l)/3;
1543 m2 = r - (r - l)/3;
1544
1545 i++;
1546
1547 if (i > maxIter) {
1548 break;
1549 }
1550 }
1551
1552 return (l + r)/2;
1553}

References eps.

◆ findNearestPointOnLine()

QPointF KRITAGLOBAL_EXPORT KisAlgebra2D::findNearestPointOnLine ( const QPointF & point,
const QLineF & line,
bool unbounded )

Definition at line 1755 of file kis_algebra_2d.cpp.

1756{
1757 // TODO: can we reuse (or deduplicate with) KisBezierUtils.cpp:nearestPoint()?
1758
1759 if (line.length() == 0) {
1760 return point;
1761 }
1762
1763 QPointF lineVector = line.p2() - line.p1();
1764 QPointF lineNormalVector = QPointF(-lineVector.y(), lineVector.x());
1765
1766 QLineF pointToLineNormalLine = QLineF(point, point + lineNormalVector);
1767 QPointF result;
1768 QLineF::IntersectionType intersectionType = pointToLineNormalLine.intersects(line, &result);
1769 if (unbounded || intersectionType == QLineF::IntersectionType::BoundedIntersection) {
1770 return result;
1771 }
1772 qreal distance1 = kisDistance(line.p1(), result);
1773 qreal distance2 = kisDistance(line.p2(), result);
1774 if (distance1 + distance2 <= line.length() || qFuzzyCompare(distance1 + distance2, line.length())) {
1775 return result; // it's still on the line
1776 }
1777 if (distance1 < distance2) {
1778 return line.p1();
1779 }
1780 return line.p2();
1781}
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:190

References kisDistance().

◆ findTrianglePoint()

QVector< QPointF > KRITAGLOBAL_EXPORT KisAlgebra2D::findTrianglePoint ( const QPointF & p1,
const QPointF & p2,
qreal a,
qreal b )

Find possible positions for point p3, such that points \p1, \p2 and p3 form a triangle, such that the distance between p1 ad p3 is a and the distance between p2 and p3 is b. There might be 0, 1 or 2 such positions.

Definition at line 1017 of file kis_algebra_2d.cpp.

1018{
1019 using KisAlgebra2D::norm;
1021
1022 QVector<QPointF> result;
1023
1024 const QPointF p = p2 - p1;
1025
1026 const qreal pSq = dotProduct(p, p);
1027
1028 const qreal T = 0.5 * (-pow2(b) + pow2(a) + pSq);
1029
1030 if (p.isNull()) return result;
1031
1032 if (qAbs(p.x()) > qAbs(p.y())) {
1033 const qreal A = 1.0;
1034 const qreal B2 = -T * p.y() / pSq;
1035 const qreal C = pow2(T) / pSq - pow2(a * p.x()) / pSq;
1036
1037 const qreal D4 = pow2(B2) - A * C;
1038
1039 if (D4 > 0 || qFuzzyIsNull(D4)) {
1040 if (qFuzzyIsNull(D4)) {
1041 const qreal y = -B2 / A;
1042 const qreal x = (T - y * p.y()) / p.x();
1043 result << p1 + QPointF(x, y);
1044 } else {
1045 const qreal y1 = (-B2 + std::sqrt(D4)) / A;
1046 const qreal x1 = (T - y1 * p.y()) / p.x();
1047 result << p1 + QPointF(x1, y1);
1048
1049 const qreal y2 = (-B2 - std::sqrt(D4)) / A;
1050 const qreal x2 = (T - y2 * p.y()) / p.x();
1051 result << p1 + QPointF(x2, y2);
1052 }
1053 }
1054 } else {
1055 const qreal A = 1.0;
1056 const qreal B2 = -T * p.x() / pSq;
1057 const qreal C = pow2(T) / pSq - pow2(a * p.y()) / pSq;
1058
1059 const qreal D4 = pow2(B2) - A * C;
1060
1061 if (D4 > 0 || qFuzzyIsNull(D4)) {
1062 if (qFuzzyIsNull(D4)) {
1063 const qreal x = -B2 / A;
1064 const qreal y = (T - x * p.x()) / p.y();
1065 result << p1 + QPointF(x, y);
1066 } else {
1067 const qreal x1 = (-B2 + std::sqrt(D4)) / A;
1068 const qreal y1 = (T - x1 * p.x()) / p.y();
1069 result << p1 + QPointF(x1, y1);
1070
1071 const qreal x2 = (-B2 - std::sqrt(D4)) / A;
1072 const qreal y2 = (T - x2 * p.x()) / p.y();
1073 result << p1 + QPointF(x2, y2);
1074 }
1075 }
1076 }
1077
1078 return result;
1079}
static qreal B2(qreal u)
#define C(i, j)
qreal norm(const T &a)
PointTypeTraits< T >::value_type dotProduct(const T &a, const T &b)

References A, B2(), C, dotProduct(), norm(), p, p1, p2, pow2(), and qFuzzyIsNull().

◆ findTrianglePointNearest()

boost::optional< QPointF > KRITAGLOBAL_EXPORT KisAlgebra2D::findTrianglePointNearest ( const QPointF & p1,
const QPointF & p2,
qreal a,
qreal b,
const QPointF & nearest )

Find a point p3 that forms a triangle with \p1 and \p2 and is the nearest possible point to nearest

See also
findTrianglePoint

Definition at line 1081 of file kis_algebra_2d.cpp.

1082{
1083 const QVector<QPointF> points = findTrianglePoint(p1, p2, a, b);
1084
1085 boost::optional<QPointF> result;
1086
1087 if (points.size() == 1) {
1088 result = points.first();
1089 } else if (points.size() > 1) {
1090 result = kisDistance(points.first(), nearest) < kisDistance(points.last(), nearest) ? points.first() : points.last();
1091 }
1092
1093 return result;
1094}
QVector< QPointF > findTrianglePoint(const QPointF &p1, const QPointF &p2, qreal a, qreal b)

References findTrianglePoint(), kisDistance(), p1, and p2.

◆ fromQTransformStraight()

Eigen::Matrix3d KisAlgebra2D::fromQTransformStraight ( const QTransform & t)
inline

Definition at line 776 of file kis_algebra_2d.cpp.

777{
778 Eigen::Matrix3d m;
779
780 m << t.m11() , t.m12() , t.m13()
781 ,t.m21() , t.m22() , t.m23()
782 ,t.m31() , t.m32() , t.m33();
783
784 return m;
785}

◆ fuzzyCompareRects()

template<class Rect , typename Difference = decltype(Rect::width())>
bool KisAlgebra2D::fuzzyCompareRects ( const Rect & r1,
const Rect & r2,
Difference tolerance )

Compare two rectangles with tolerance tolerance. The tolerance means that the coordinates of top left and bottom right corners should not differ more than tolerance pixels.

Definition at line 842 of file kis_algebra_2d.h.

842 {
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}
QPointF r2
QPointF r1
Point abs(const Point &pt)

References abs(), r1, and r2.

◆ fuzzyMatrixCompare()

bool KRITAGLOBAL_EXPORT KisAlgebra2D::fuzzyMatrixCompare ( const QTransform & t1,
const QTransform & t2,
qreal delta )

Compare the matrices with tolerance delta

Definition at line 744 of file kis_algebra_2d.cpp.

744 {
745 return
746 qAbs(t1.m11() - t2.m11()) < delta &&
747 qAbs(t1.m12() - t2.m12()) < delta &&
748 qAbs(t1.m13() - t2.m13()) < delta &&
749 qAbs(t1.m21() - t2.m21()) < delta &&
750 qAbs(t1.m22() - t2.m22()) < delta &&
751 qAbs(t1.m23() - t2.m23()) < delta &&
752 qAbs(t1.m31() - t2.m31()) < delta &&
753 qAbs(t1.m32() - t2.m32()) < delta &&
754 qAbs(t1.m33() - t2.m33()) < delta;
755}

◆ fuzzyPointCompare() [1/3]

template<template< typename > class Cont, class Point >
bool KisAlgebra2D::fuzzyPointCompare ( const Cont< Point > & c1,
const Cont< Point > & c2,
qreal delta )

Returns true if points in two containers are equal with specified tolerance

Definition at line 821 of file kis_algebra_2d.h.

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}

References eps.

◆ fuzzyPointCompare() [2/3]

bool KRITAGLOBAL_EXPORT KisAlgebra2D::fuzzyPointCompare ( const QPointF & p1,
const QPointF & p2 )

Returns true if the two points are equal within some tolerance, where the tolerance is determined by Qt's built-in fuzzy comparison functions.

Definition at line 757 of file kis_algebra_2d.cpp.

758{
759 return qFuzzyCompare(p1.x(), p2.x()) && qFuzzyCompare(p1.y(), p2.y());
760}

References p1, p2, and qFuzzyCompare().

◆ fuzzyPointCompare() [3/3]

bool KRITAGLOBAL_EXPORT KisAlgebra2D::fuzzyPointCompare ( const QPointF & p1,
const QPointF & p2,
qreal delta )

Returns true if the two points are equal within the specified tolerance

Definition at line 763 of file kis_algebra_2d.cpp.

764{
765 return qAbs(p1.x() - p2.x()) < delta && qAbs(p1.y() - p2.y()) < delta;
766}

References p1, and p2.

◆ getLineFromElements()

QLineF KisAlgebra2D::getLineFromElements ( const QPainterPath & shape,
int index )

Definition at line 1801 of file kis_algebra_2d.cpp.

1802{
1803 QPainterPath::Element element1 = shape.elementAt(wrapValue(index, 0, shape.elementCount() - 1));
1804 QPainterPath::Element element2 = shape.elementAt(wrapValue(index + 1, 0, shape.elementCount() - 1));
1805 // it doesn't handle isCurveTo, just assumes it's a line... sorry!
1806
1807 return QLineF(element1, element2);
1808}
T wrapValue(T value, T wrapBounds)

References wrapValue().

◆ getLineSegmentCrossingLineIndexes()

QList< int > KRITAGLOBAL_EXPORT KisAlgebra2D::getLineSegmentCrossingLineIndexes ( const QLineF & line,
const QPainterPath & shape )

Definition at line 2013 of file kis_algebra_2d.cpp.

2014{
2015 VectorPath path(shape);
2016 QList<int> indexes;
2017
2018 for (int i = 0; i < path.segmentsCount(); i++) {
2019 QList<VectorPath::VectorPathPoint> points = path.segmentAt(i);
2020 if (points.count() < 2)
2021 continue;
2022 if (points[1].type == VectorPath::VectorPathPoint::BezierTo) {
2023
2024 qreal eps = 1e-5;
2025 QVector<qreal> intersections = KisBezierUtils::intersectWithLine(points[0].endPoint, points[1].controlPoint1, points[1].controlPoint2, points[1].endPoint, line, eps);
2026 for (int i = 0; i < intersections.length(); i++) {
2027 //
2028 QPointF p = KisBezierUtils::bezierCurve(points[0].endPoint, points[1].controlPoint1, points[1].controlPoint2, points[1].endPoint, intersections[i]);
2029
2030 bool onLine = isOnLine(line, p, eps, true, true, true);
2031 if (onLine) {
2032 indexes << path.segmentIndexToPathIndex(i);
2033 break; // we need just one
2034 }
2035 }
2036
2037 } else {
2038
2039 QLineF lineSegment = path.segmentAtAsLine(i);
2040 QPointF intersection;
2041 QLineF::IntersectionType type = lineSegment.intersects(line, &intersection);
2042 if (type == QLineF::IntersectionType::BoundedIntersection) {
2043 indexes << path.segmentIndexToPathIndex(i);
2044 }
2045 }
2046 }
2047 return indexes;
2048}
bool isOnLine(const QLineF &line, const QPointF &point, const qreal eps, bool boundedStart, bool boundedEnd, bool includeEnds)
QVector< qreal > intersectWithLine(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, qreal eps)
QPointF bezierCurve(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t)

References KisBezierUtils::bezierCurve(), KisAlgebra2D::VectorPath::VectorPathPoint::BezierTo, eps, KisBezierUtils::intersectWithLine(), isOnLine(), and p.

◆ getOnePathFromRectangleCutThrough()

QPainterPath KRITAGLOBAL_EXPORT KisAlgebra2D::getOnePathFromRectangleCutThrough ( const QList< QPointF > & points,
const QLineF & line,
bool left )

Definition at line 1661 of file kis_algebra_2d.cpp.

1661 {
1662
1663 auto onTheLine = [](QPointF first, QPointF second) {
1664 float delta = 0.1f;
1665 return qAbs(first.x() - second.x()) < delta || qAbs(first.y() - second.y()) < delta;
1666 };
1667
1668 bool started = false;
1669 QPainterPath path;
1670
1671 path.moveTo(line.p1());
1672 path.lineTo(line.p2());
1673
1674 QList<QPointF> availablePoints;
1675 int maxRectPointsNumber = 4; // in case the list has five points to count the first one twice
1676 for(int i = 0; i < maxRectPointsNumber; i++) {
1677 qreal whichSide = KisAlgebra2D::crossProduct(line.p2() - line.p1(), line.p2() - points[i]);
1678 if (whichSide*(left ? 1 : -1) >= 0) {
1679 availablePoints << points[i];
1680 }
1681 }
1682
1683 int startValue, increment;
1684 if (!left) {
1685 startValue = 2*availablePoints.length();
1686 increment = -1;
1687 } else {
1688 startValue = 0;
1689 increment = 1;
1690 }
1691
1692 auto stopFor = [](int index, int max, bool left) {
1693 if (!left) {
1694 return index <= 0;
1695 } else {
1696 return index >= max;
1697 }
1698 };
1699
1700 for (int i = startValue; !stopFor(i, 2*availablePoints.length(), left); i += increment) {
1701 int index = KisAlgebra2D::wrapValue(i, 0, (int)availablePoints.length());
1702
1703 if (onTheLine(availablePoints[index], path.currentPosition())) {
1704 if (!started) {
1705 started = true;
1706
1707 // TODO: if it's not the same point?
1708 path.lineTo(availablePoints[index]);
1709 if (onTheLine(availablePoints[index], line.p1())) {
1710 break;
1711 }
1712
1713 } else {
1714 // usually would add, unless it's the ending
1715 if (onTheLine(availablePoints[index], line.p1())) {
1716 path.lineTo(availablePoints[index]);
1717 break;
1718 }
1719 path.lineTo(availablePoints[index]);
1720 }
1721 }
1722 }
1723
1724
1725 path.lineTo(line.p1());
1726
1727 return path;
1728}
PointTypeTraits< T >::value_type crossProduct(const T &a, const T &b)

References crossProduct(), and wrapValue().

◆ getParallelLines()

QList< QLineF > KRITAGLOBAL_EXPORT KisAlgebra2D::getParallelLines ( const QLineF & line,
const qreal distance )

Definition at line 1649 of file kis_algebra_2d.cpp.

1649 {
1650
1651 QPointF lineNormalVector = QPointF(line.dy(), -line.dx());
1652 QLineF line1 = QLineF(movePointInTheDirection(line.p1(), lineNormalVector, distance), movePointInTheDirection(line.p2(), lineNormalVector, distance));
1653 QLineF line2 = QLineF(movePointInTheDirection(line.p1(), -lineNormalVector, distance), movePointInTheDirection(line.p2(), -lineNormalVector, distance));
1654
1655 if (line1.isNull() || line2.isNull()) {
1656 return QList<QLineF>();
1657 }
1658 return {line1, line2};
1659}
QPointF movePointInTheDirection(const QPointF &point, const QPointF &direction, qreal distance)
movePointAlongTheLine moves the point a particular distance in the specified direction

References distance(), and movePointInTheDirection().

◆ getPathsFromRectangleCutThrough()

QList< QPainterPath > KRITAGLOBAL_EXPORT KisAlgebra2D::getPathsFromRectangleCutThrough ( const QRectF & rect,
const QLineF & leftLine,
const QLineF & rightLine )

getPathsFromRectangleCutThrough get paths defining both sides of a rectangle cut through using two (supposedly parallel) lines It is used in the Knife Tool If you just want to cut a rectangle, you can use the same line in both

Parameters
rectrectangle to cut
leftLineleft line of the rectangle (used for the left-(top) side of the rectangle
rightLineright line of the rectangle (used for the right-(bottom) side of the rectangle
Returns

Definition at line 1730 of file kis_algebra_2d.cpp.

1730 {
1731
1732
1733 QPainterPath left;
1734 QPainterPath right;
1735
1736 left.moveTo(leftLine.p1());
1737 left.lineTo(leftLine.p2());
1738
1739 right.moveTo(rightLine.p1());
1740 right.lineTo(rightLine.p2());
1741
1742 QList<QPointF> rectPoints;
1743 rectPoints << rect.topLeft() << rect.bottomLeft() << rect.bottomRight() << rect.topRight();
1744
1745 left = getOnePathFromRectangleCutThrough(rectPoints, leftLine, true);
1746 right = getOnePathFromRectangleCutThrough(rectPoints, rightLine, false);
1747
1748 QList<QPainterPath> paths;
1749 paths << left << right;
1750
1751
1752 return paths;
1753}
QPainterPath getOnePathFromRectangleCutThrough(const QList< QPointF > &points, const QLineF &line, bool left)

References getOnePathFromRectangleCutThrough().

◆ intersectLineConcavePolygon()

QList< QLineF > KisAlgebra2D::intersectLineConcavePolygon ( const QPolygonF polygon,
const QLineF & line,
bool extendFirst,
bool extendSecond )

Definition at line 1459 of file kis_algebra_2d.cpp.

1459 {
1460
1461 // should use kis_convex_hull instead, that one uses boost
1462 QPolygonF convexHull = calculateConvexHull(polygon);
1463 if (convexHull.count() == polygon.count()) {
1464 QLineF resultLine = line;
1465 bool result = intersectLineConvexPolygon(resultLine, polygon, extendFirst, extendSecond);
1466 if (result) {
1467 return QList<QLineF>() << resultLine;
1468 } else {
1469 return QList<QLineF>();
1470 }
1471 }
1472
1473 // it was a concave polygon
1474 // intersectLines
1475
1476 // should be as easy as cutting every line, then sorting the points and creating the lines, except gotta take care of exceptions
1477
1478 KIS_ASSERT(false && "Not implemented yet");
1479
1480 return QList<QLineF>();
1481}
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
QPolygonF calculateConvexHull(const QPolygonF &polygon)
calculateConvexHull Calculate the convex hull of the polygon using the QuickHull

References calculateConvexHull(), intersectLineConvexPolygon(), and KIS_ASSERT.

◆ intersectLineConvexPolygon()

bool KRITAGLOBAL_EXPORT KisAlgebra2D::intersectLineConvexPolygon ( QLineF & line,
const QPolygonF polygon,
bool extendFirst,
bool extendSecond )

Definition at line 251 of file kis_algebra_2d.cpp.

252{
253 qreal epsilon = 1e-07;
254
255 if (polygon.size() == 0) {
256 return false;
257 }
258
259 // trivial case: no extends, all points inside the polygon
260 if (!extendFirst && !extendSecond && polygon.containsPoint(line.p1(), Qt::WindingFill) && polygon.containsPoint(line.p2(), Qt::WindingFill)) {
261 return true;
262 }
263
264 // Cyrus-Beck algorithm: https://en.wikipedia.org/wiki/Cyrus%E2%80%93Beck_algorithm
265
266 // parametric equation for the line:
267 // p(t) = t*P1 + (1-t)*P2
268
269 // we can't use infinity here, because the points would all end up as (+/- inf, +/- inf)
270 // which would be useless if we have a very specific direction
271 // hence we use just "a big number"
272 float aBigNumber = 100000; // that will keep t*Px still on the line // this is LIMITATION of the algorithm
273 float tmin = extendFirst ? -aBigNumber : 0; // line.p1() - first point, or infinity
274 float tmax = extendSecond ? aBigNumber : 1; // line.p2() - second point, or infinity
275
276
277
278 QList<QLineF> clippingLines;
279 QList<QPointF> normals;
280
281 bool clockwise = false;
282 {
283 // only temporary values to check whether the polygon is clockwise or counterclockwise
284 QPointF vec1 = polygon[1] - polygon[0];
285 QPointF vec2 = polygon[2] - polygon[0];
286
287 QLineF line1(QPointF(0, 0), vec1);
288 QLineF line2(QPointF(0, 0), vec2);
289
290 qreal angle = line1.angleTo(line2); // range: <0, 360) // <0, 180) means counter-clockwise
291 if (angle >= 180) {
292 clockwise = true;
293 }
294 }
295
296
297 for (int i = 0; i < polygon.size() - 1; i++) {
298 clippingLines.append(QLineF(polygon[i], polygon[i + 1]));
299 float xdiff = polygon[i].x() - polygon[i + 1].x();
300 float ydiff = polygon[i].y() - polygon[i + 1].y();
301
302 if (!clockwise) {
303 normals.append(QPointF(ydiff, -xdiff));
304 } else {
305 normals.append(QPointF(-ydiff, xdiff));
306 }
307
308 }
309
310 if (!polygon.isClosed()) {
311 int i = polygon.size() - 1;
312 clippingLines.append(QLineF(polygon[i], polygon[0]));
313 float xdiff = polygon[i].x() - polygon[0].x();
314 float ydiff = polygon[i].y() - polygon[0].y();
315
316 if (!clockwise) {
317 normals.append(QPointF(ydiff, -xdiff));
318 } else {
319 normals.append(QPointF(-ydiff, xdiff));
320 }
321
322 }
323
324 QPointF lineVec = line.p2() - line.p1();
325 // ax + by + c = 0
326 // c = -ax - by
327// qreal cFromStandardEquation = -lineVec.x()*line.p1().x() - lineVec.y()*line.p1().y();
328
329
330 QPointF p1 = line.p1();
331 QPointF p2 = line.p2();
332
333 qreal pA, pB, pC; // standard equation for the line: ax + by + c = 0
334 if (qAbs(lineVec.x()) < epsilon) {
335 // x1 ~= x2, line looks like: x = -pC
336 pB = 0;
337 pA = 1;
338 pC = -p1.x();
339 } else {
340 pB = 1; // let b = 1 to simplify
341 qreal tmp = (p1.y() - p2.y())/(p1.x() - p2.x());
342 pA = -tmp;
343 pC = tmp * p1.x() - p1.y();
344 }
345
346
347 int pEFound = 0;
348
349
350 for (int i = 0; i < clippingLines.size(); i++) {
351
352 // is the clipping line parallel to the line?
353 QPointF clipVec = clippingLines[i].p2() - clippingLines[i].p1();
354
355 if (qFuzzyCompare(clipVec.x()*lineVec.y(), clipVec.y()*lineVec.x())) {
356
357 // vectors are parallel
358 // 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
359
360 qreal distanceBetweenLines = qAbs(pA*clippingLines[i].p1().x() + pB*clippingLines[i].p1().y() + pC)
361 /(sqrt(pA*pA + pB*pB));
362
363
364 if (qAbs(distanceBetweenLines) < epsilon) {
365 // they are the same line
366
367 qreal t1;
368 qreal t2;
369
370 if (qAbs(p2.x() - p1.x()) > epsilon) {
371 t1 = (clippingLines[i].p1().x() - p1.x())/(p2.x() - p1.x());
372 t2 = (clippingLines[i].p2().x() - p1.x())/(p2.x() - p1.x());
373 } else {
374 t1 = (clippingLines[i].p1().y() - p1.y())/(p2.y() - p1.y());
375 t2 = (clippingLines[i].p2().y() - p1.y())/(p2.y() - p1.y());
376 }
377
378 qreal tmin1 = qMin(t1, t2);
379 qreal tmax2 = qMax(t1, t2);
380
381
382
383 if (tmin < tmin1) {
384 tmin = tmin1;
385 }
386 if (tmax > tmax2) {
387 tmax = tmax2;
388 }
389 continue;
390 }
391 else {
392 // if clipping line points are P1 and P2, and some other point is P(t) (given by parametric equation),
393 // and N is the normal vector
394 // and one of the points of the line we're intersecting is K,
395 // then we have a following equation:
396 // K = P(t) + a * N
397 // where t and a are unknown.
398 // after simplifying we get:
399 // t*(p2 - p1) + a*(n) = k - p1
400 // and since we have two axis, we get a linear system of two equations
401 // A * [t; a] = B
402
403 Eigen::Matrix2f A;
404 Eigen::Vector2f b;
405 A << p2.x() - p1.x(), normals[i].x(),
406 p2.y() - p1.y(), normals[i].y();
407 b << clippingLines[i].p1().x() - p1.x(),
408 clippingLines[i].p1().y() - p1.y();
409
410 Eigen::Vector2f ta = A.colPivHouseholderQr().solve(b);
411
412 qreal tt = ta(1);
413 if (tt < 0) {
414 return false;
415 }
416 }
417
418 }
419
420
421 boost::optional<QPointF> pEOptional = intersectLines(clippingLines[i], line); // bounded, unbounded
422
423
424 if (pEOptional) {
425 pEFound++;
426
427 QPointF pE = pEOptional.value();
428 QPointF n = normals[i];
429
430 QPointF A = line.p2() - line.p1();
431 QPointF B = line.p1() - pE;
432
433 qreal over = (n.x()*B.x() + n.y()*B.y());
434 qreal under = (n.x()*A.x() + n.y()*A.y());
435 qreal t = -over/under;
436
437// if (pE.x() != p2.x()) {
438// qreal maybet = (pE.x() - p1.x())/(p2.x() - p1.x());
439// }
440
441 qreal tminvalue = tmin * under + over;
442 qreal tmaxvalue = tmax * under + over;
443
444 if (tminvalue > 0 && tmaxvalue > 0) {
445 // both outside of the edge
446 return false;
447 }
448 if (tminvalue <= 0 && tmaxvalue <= 0) {
449 // noop, both inside
450 }
451 if (tminvalue*tmaxvalue < 0) {
452
453 // on two different sides
454 if (tminvalue > 0) {
455 // tmin outside, tmax inside
456 if (t > tmin) {
457 tmin = t;
458 }
459 } else {
460 if (t < tmax) {
461 tmax = t;
462 }
463 }
464 }
465
466
467
468 if (tmax < tmin) {
469 return false;
470 }
471 }
472 else {
473 }
474
475 }
476
477 if (pEFound == 0) {
478 return false;
479 }
480
481 QLineF response = QLineF(tmin*line.p2() + (1-tmin)*line.p1(), tmax*line.p2() + (1-tmax)*line.p1());
482 line = response;
483 return true;
484}
boost::optional< QPointF > intersectLines(const QLineF &boundedLine, const QLineF &unboundedLine)

References A, B, intersectLines(), p1, p2, and qFuzzyCompare().

◆ intersectLineRect() [1/2]

bool KRITAGLOBAL_EXPORT KisAlgebra2D::intersectLineRect ( QLineF & line,
const QRect rect,
bool extend )

Attempt to intersect a line to the area of the a rectangle.

If the line intersects the rectangle, it will be modified to represent the intersecting line segment and true is returned. If the line does not intersect the area, it remains unmodified and false will be returned. If the line is fully inside the rectangle, it's considered "intersecting" so true will be returned.

Parameters
lineline segment
rectarea
extendextend the line to the edges of the rect area even if the line segment fits inside the rectangle (so, consider the line to be a line defined by those two points, not a line segment)
Returns
true if successful

Definition at line 173 of file kis_algebra_2d.cpp.

174{
175 return intersectLineRect(line, rect, extend, extend);
176}

References intersectLineRect().

◆ intersectLineRect() [2/2]

bool KRITAGLOBAL_EXPORT KisAlgebra2D::intersectLineRect ( QLineF & line,
const QRect rect,
bool extendFirst,
bool extendSecond )

Attempt to intersect a line to the area of the a rectangle.

If the line intersects the rectangle, it will be modified to represent the intersecting line segment and true is returned. If the line does not intersect the area, it remains unmodified and false will be returned. If the line is fully inside the rectangle, it's considered "intersecting" so true will be returned.

extendFirst and extendSecond parameters allow one to use this function in case of unbounded lines (if both are true), line segments (if both are false) or half-lines/rays (if one is true and another is false). 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.

Parameters
lineline segment
rectarea
extendFirstextend the line to the edge of the rect area even if the first point of the line segment lies inside the rectangle
extendSecondextend the line to the edge of the rect area even if the second point of the line segment lies inside the rectangle
Returns
true if successful

Definition at line 178 of file kis_algebra_2d.cpp.

179{
180 // using Liang-Barsky algorithm
181 // using parametric equation for a line:
182 // X = x1 + t(x2-x1) = x1 + t*p2
183 // Y = y1 + t(y2-y1) = y1 + t*p4
184 // note that we have a line *segment* here, not a line
185 // t1 = 0, t2 = 1, no matter what points we got
186
187 double p1 = -(line.x2() - line.x1());
188 double p2 = -p1;
189 double p3 = -(line.y2() - line.y1());
190 double p4 = -p3;
191
193 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()});
194
195 float tmin = extendFirst ? -std::numeric_limits<float>::infinity() : 0; // line.p1() - first point, or infinity
196 float tmax = extendSecond ? std::numeric_limits<float>::infinity() : 1; // line.p2() - second point, or infinity
197
198 for (int i = 0; i < p.length(); i++) {
199 // now clipping
200 if (p[i] == 0 && q[i] < 0) {
201 // line is parallel to the boundary and outside of it
202 return false;
203 } else if (p[i] < 0) {
204 // line entry point (should be tmin)
205 double t = q[i]/p[i];
206 if (t > tmax) {
207 if (extendSecond) {
208 tmax = t;
209 } else {
210 return false;
211 }
212 }
213 if (t > tmin) {
214 tmin = t;
215 }
216
217
218 } else if (p[i] > 0) {
219 // line leaving point (should be tmax)
220 double t = q[i]/p[i];
221 if (t < tmin) {
222 if (extendFirst) {
223 tmin = t;
224 } else {
225 return false;
226 }
227 }
228 if (t < tmax) {
229 tmax = t;
230 }
231 }
232 }
233
234 QPointF pt1 = line.p1();
235 QPointF pt2 = line.p2();
236
237 if (extendFirst || tmin > 0) {
238 pt1 = QPointF(line.x1() + tmin*(line.x2() - line.x1()), line.y1() + tmin*(line.y2() - line.y1()));
239 }
240 if (extendSecond || tmax < 1) {
241 pt2 = QPointF(line.x1() + tmax*(line.x2() - line.x1()), line.y1() + tmax*(line.y2() - line.y1()));
242 }
243
244 line.setP1(pt1);
245 line.setP2(pt2);
246
247 return true;
248
249}
QPointF p3

References p, p1, p2, and p3.

◆ intersectLines() [1/2]

KRITAGLOBAL_EXPORT boost::optional< QPointF > KisAlgebra2D::intersectLines ( const QLineF & boundedLine,
const QLineF & unboundedLine )

Find intersection of a bounded line boundedLine with unbounded line unboundedLine (if an intersection exists)

Definition at line 975 of file kis_algebra_2d.cpp.

976{
977 const QPointF B1 = unboundedLine.p1();
978 const QPointF A1 = unboundedLine.p2() - unboundedLine.p1();
979
980 const QPointF B2 = boundedLine.p1();
981 const QPointF A2 = boundedLine.p2() - boundedLine.p1();
982
983 Eigen::Matrix<float, 2, 2> A;
984 A << A1.x(), -A2.x(),
985 A1.y(), -A2.y();
986
987 Eigen::Matrix<float, 2, 1> B;
988 B << B2.x() - B1.x(),
989 B2.y() - B1.y();
990
991 if (qFuzzyIsNull(A.determinant())) {
992 return boost::none;
993 }
994
995 Eigen::Matrix<float, 2, 1> T;
996
997 T = A.inverse() * B;
998
999 const qreal t2 = T(1,0);
1000 double epsilon = 1e-06; // to avoid precision issues
1001
1002 if (t2 < 0.0 || t2 > 1.0) {
1003 if (qAbs(t2) > epsilon && qAbs(t2 - 1.0) > epsilon) {
1004 return boost::none;
1005 }
1006 }
1007
1008 return t2 * A2 + B2;
1009}
static qreal B1(qreal u)

References A, B, B1(), B2(), and qFuzzyIsNull().

◆ intersectLines() [2/2]

KRITAGLOBAL_EXPORT boost::optional< QPointF > KisAlgebra2D::intersectLines ( const QPointF & p1,
const QPointF & p2,
const QPointF & q1,
const QPointF & q2 )

Find intersection of a bounded line p1, p2 with unbounded line q1, q2 (if an intersection exists)

Definition at line 1011 of file kis_algebra_2d.cpp.

1013{
1014 return intersectLines(QLineF(p1, p2), QLineF(q1, q2));
1015}
QPointF q1
QPointF q2

References intersectLines(), p1, p2, q1, and q2.

◆ intersectTwoCircles()

KRITAGLOBAL_EXPORT QVector< QPointF > KisAlgebra2D::intersectTwoCircles ( const QPointF & c1,
qreal r1,
const QPointF & c2,
qreal r2 )

Finds the points of intersections between two circles

Returns
the found circles, the result can have 0, 1 or 2 points

Definition at line 638 of file kis_algebra_2d.cpp.

640{
641 QVector<QPointF> points;
642
643 const QPointF diff = (center2 - center1);
644 const QPointF c1;
645 const QPointF c2 = diff;
646
647 const qreal centerDistance = norm(diff);
648
649 if (centerDistance > r1 + r2) return points;
650 if (centerDistance < qAbs(r1 - r2)) return points;
651
652 if (centerDistance < qAbs(r1 - r2) + 0.001) {
653 dbgKrita << "Skipping intersection" << ppVar(center1) << ppVar(center2) << ppVar(r1) << ppVar(r2) << ppVar(centerDistance) << ppVar(qAbs(r1-r2));
654 return points;
655 }
656
657 const qreal x_kp1 = diff.x();
658 const qreal y_kp1 = diff.y();
659
660 const qreal F2 =
661 0.5 * (pow2(x_kp1) +
662 pow2(y_kp1) + pow2(r1) - pow2(r2));
663
664 const qreal eps = 1e-6;
665
666 if (qAbs(diff.y()) < eps) {
667 qreal x = F2 / diff.x();
668 qreal y1, y2;
670 1, 0,
671 pow2(x) - pow2(r2),
672 &y1, &y2);
673
674 KIS_SAFE_ASSERT_RECOVER(result > 0) { return points; }
675
676 if (result == 1) {
677 points << QPointF(x, y1);
678 } else if (result == 2) {
680
681 QPointF p1(x, y1);
682 QPointF p2(x, y2);
683
684 if (p.pos(p1) >= 0) {
685 points << p1;
686 points << p2;
687 } else {
688 points << p2;
689 points << p1;
690 }
691 }
692 } else {
693 const qreal A = diff.x() / diff.y();
694 const qreal C = F2 / diff.y();
695
696 qreal x1, x2;
698 1 + pow2(A), -2 * A * C,
699 pow2(C) - pow2(r1),
700 &x1, &x2);
701
702 KIS_SAFE_ASSERT_RECOVER(result > 0) { return points; }
703
704 if (result == 1) {
705 points << QPointF(x1, C - x1 * A);
706 } else if (result == 2) {
708
709 QPointF p1(x1, C - x1 * A);
710 QPointF p2(x2, C - x2 * A);
711
712 if (p.pos(p1) >= 0) {
713 points << p1;
714 points << p2;
715 } else {
716 points << p2;
717 points << p1;
718 }
719 }
720 }
721
722 for (int i = 0; i < points.size(); i++) {
723 points[i] = center1 + points[i];
724 }
725
726 return points;
727}
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
int quadraticEquation(qreal a, qreal b, qreal c, qreal *x1, qreal *x2)

References A, C, dbgKrita, eps, KIS_SAFE_ASSERT_RECOVER, norm(), p, p1, p2, pow2(), ppVar, quadraticEquation(), r1, and r2.

◆ inwardUnitNormal()

template<class T >
T KisAlgebra2D::inwardUnitNormal ( const T & a,
int polygonDirection )

Definition at line 148 of file kis_algebra_2d.h.

149{
151}
T leftUnitNormal(const T &a)
int polygonDirection(const QVector< T > &polygon)

References leftUnitNormal(), and polygonDirection().

◆ isInRange()

template<typename T >
bool KisAlgebra2D::isInRange ( T x,
T a,
T b )

Definition at line 199 of file kis_algebra_2d.h.

199 {
200 T length = qAbs(a - b);
201 return qAbs(x - a) <= length && qAbs(x - b) <= length;
202}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82

References length().

◆ isInsideShape() [1/2]

bool KRITAGLOBAL_EXPORT KisAlgebra2D::isInsideShape ( const QPainterPath & path,
const QPointF & point )

Definition at line 2496 of file kis_algebra_2d.cpp.

2497{
2498 for (int i = 0; i < path.elementCount(); i++) {
2499 if (path.elementAt(i).type == QPainterPath::CurveToDataElement) {
2500 // contains control points
2501 return isInsideShape(VectorPath(path), point);
2502 }
2503 }
2504
2505 return path.toFillPolygon().containsPoint(point, Qt::WindingFill);
2506}
bool isInsideShape(const VectorPath &path, const QPointF &point)

References isInsideShape().

◆ isInsideShape() [2/2]

bool KRITAGLOBAL_EXPORT KisAlgebra2D::isInsideShape ( const VectorPath & path,
const QPointF & point )

Definition at line 2415 of file kis_algebra_2d.cpp.

2416{
2417 if (path.pointsCount() == 0) {
2418 return false;
2419 }
2420
2421 QRectF boundRect;
2422 accumulateBounds(path.pointAt(0).endPoint, &boundRect);
2423
2424 bool isPolygon = true;
2425
2426 for (int i = 0; i < path.segmentsCount(); i++) {
2427 QList<VectorPath::VectorPathPoint> points = path.segmentAt(i);
2428 accumulateBounds(points[0].endPoint, &boundRect);
2429 accumulateBounds(points[1].endPoint, &boundRect);
2430
2431 if (points[1].type == VectorPath::VectorPathPoint::BezierTo) {
2432 accumulateBounds(points[1].controlPoint1, &boundRect);
2433 accumulateBounds(points[1].controlPoint2, &boundRect);
2434 isPolygon = false;
2435 }
2436 }
2437
2438 if (!boundRect.contains(point)) {
2439 return false;
2440 }
2441
2442 if (isPolygon) {
2443 return path.asPainterPath().toFillPolygon().containsPoint(point, Qt::WindingFill);
2444 }
2445
2446 boundRect = kisGrowRect(boundRect, 5); // just safety margins
2447
2448 // NOTE: it would be way faster to do it taking into account the fact that the lines are vertical and horizontal
2449 // except for the last one maybe
2450 // but let's wait until it proves to be a problem
2451 QList<QLineF> lines;
2452 lines << QLineF(point, QPointF(boundRect.left(), point.y()));
2453 lines << QLineF(point, QPointF(boundRect.right(), point.y()));
2454 lines << QLineF(point, QPointF(point.x(), boundRect.top()));
2455 lines << QLineF(point, QPointF(point.x(), boundRect.bottom()));
2456 lines << QLineF(point, boundRect.topLeft()); // last resort, diagonal
2457
2458 int intersectionsCount = 0;
2459
2460 for (int k = 0; k < lines.length(); k++) {
2461 int intersections = 0;
2462 QLineF line = lines[k];
2463 bool checkNext = false;
2464
2465 for (int i = 0; i < path.segmentsCount(); i++) {
2466 std::optional<VectorPath::Segment> segmentOpt = path.segmentAtAsSegment(i);
2467 KIS_SAFE_ASSERT_RECOVER(segmentOpt.has_value()) {continue;}
2468 VectorPath::Segment segment = segmentOpt.value();
2469
2470 QList<QPointF> intersectionsHere = VectorPath::intersectSegmentWithLineBounded(line, segment);
2471 qreal eps = 1e-4;
2472
2473 for (int i = 0; i < intersectionsHere.count(); i++) {
2474 if (fuzzyPointCompare(intersectionsHere[i], segment.startPoint, eps) || fuzzyPointCompare(intersectionsHere[i], segment.endPoint, eps)) {
2475 checkNext = true;
2476 break;
2477 }
2478 }
2479
2480 if (checkNext) {
2481 break;
2482 }
2483
2484 intersections += intersectionsHere.count();
2485 }
2486
2487 intersectionsCount = intersections;
2488 if (!checkNext) {
2489 break;
2490 }
2491 }
2492
2493 return intersectionsCount%2 == 1;
2494}
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186

References accumulateBounds(), KisAlgebra2D::VectorPath::VectorPathPoint::BezierTo, KisAlgebra2D::VectorPath::Segment::endPoint, eps, fuzzyPointCompare(), KisAlgebra2D::VectorPath::intersectSegmentWithLineBounded(), KIS_SAFE_ASSERT_RECOVER, kisGrowRect(), and KisAlgebra2D::VectorPath::Segment::startPoint.

◆ isOnLine()

bool KisAlgebra2D::isOnLine ( const QLineF & line,
const QPointF & point,
const qreal eps,
bool boundedStart,
bool boundedEnd,
bool includeEnds )

Definition at line 2508 of file kis_algebra_2d.cpp.

2509{
2510 if (fuzzyPointCompare(point, line.p1(), eps)) {
2511 return includeEnds || !boundedStart;
2512 }
2513 if (fuzzyPointCompare(point, line.p2(), eps)) {
2514 return includeEnds || !boundedEnd;
2515 }
2516
2517 if (kisDistanceToLine(point, line) > eps) {
2518 return false;
2519 }
2520
2521 QPointF lineVector = line.p2() - line.p1();
2522 QPointF pointVector = point - line.p1();
2523 qreal t = -1;
2524 if (qAbs(pointVector.x()) < eps) {
2525 // use y
2526 t = pointVector.y()/lineVector.y();
2527 } else {
2528 t = pointVector.x()/lineVector.x();
2529 }
2530
2531 if (t < 0) {
2532 return !boundedStart;
2533 }
2534 if (t > 1.0) {
2535 return !boundedEnd;
2536 }
2537
2538 return true;
2539}

References eps, fuzzyPointCompare(), and kisDistanceToLine().

◆ isPolygonRect()

template<class Polygon , typename Difference = decltype(Polygon::first())>
bool KisAlgebra2D::isPolygonRect ( const Polygon & poly,
Difference tolerance )

Definition at line 855 of file kis_algebra_2d.h.

855 {
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}

References fuzzyPointCompare(), p1, and p2.

◆ isPolygonTrulyConvex()

template<class T >
bool KisAlgebra2D::isPolygonTrulyConvex ( const QVector< T > & polygon)

Definition at line 884 of file kis_algebra_2d.h.

884 {
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}
#define M_PI
Definition kis_global.h:111

References fuzzyPointCompare(), M_PI, sign(), and signZZ().

◆ lazyRound()

template<typename R >
R KisAlgebra2D::lazyRound ( qreal value)

Helper function to convert a qreal to int lazily. If the passed type is an integer, then the value is rounded. Otherwise it is just passed forward.

◆ lazyRound< int >()

template<>
int KisAlgebra2D::lazyRound< int > ( qreal value)
inline

Definition at line 162 of file kis_algebra_2d.h.

163{
164 return qRound(value);
165}

References value().

◆ lazyRound< qreal >()

template<>
qreal KisAlgebra2D::lazyRound< qreal > ( qreal value)
inline

Definition at line 168 of file kis_algebra_2d.h.

169{
170 return value;
171}

References value().

◆ leftUnitNormal()

template<class T >
T KisAlgebra2D::leftUnitNormal ( const T & a)

Definition at line 132 of file kis_algebra_2d.h.

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}

References crossProduct(), length(), and norm().

◆ lerp()

template<typename Point >
Point KisAlgebra2D::lerp ( const Point & pt1,
const Point & pt2,
qreal t )

Definition at line 82 of file kis_algebra_2d.h.

83{
84 return pt1 + (pt2 - pt1) * t;
85}

◆ linearReshapeFunc()

template<typename T >
T KisAlgebra2D::linearReshapeFunc ( T x,
T x0,
T x1,
T y0,
T y1 )
inline

Linearly reshape function x so that in range [x0, x1] it would cross points (x0, y0) and (x1, y1).

Definition at line 995 of file kis_algebra_2d.h.

996{
997 return y0 + (y1 - y0) * (x - x0) / (x1 - x0);
998}

◆ lineSideForPoint()

int KisAlgebra2D::lineSideForPoint ( const QLineF & line,
const QPointF & point )

Definition at line 1308 of file kis_algebra_2d.cpp.

1309{
1310 // TODO: do we really neede these explicit checks? Do they help with
1311 // precision?
1312
1313 if (fuzzyPointCompare(point, line.p1())) {
1314 return 0;
1315 }
1316 if (fuzzyPointCompare(point, line.p2())) {
1317 return 0;
1318 }
1319 if (qFuzzyCompare(line.length(), 0)) {
1320 return 0;
1321 }
1322
1323 qreal whichSide = KisAlgebra2D::crossProduct(line.p2() - line.p1(), point - line.p1());
1324 return qFuzzyIsNull(whichSide) ? 0 : (whichSide > 0 ? 1 : -1);
1325}

References crossProduct(), fuzzyPointCompare(), qFuzzyCompare(), and qFuzzyIsNull().

◆ makeFullTransformComponents()

KisTransformComponents KRITAGLOBAL_EXPORT KisAlgebra2D::makeFullTransformComponents ( )

Definition at line 13 of file KisTransformComponents.cpp.

14{
15 return KisTransformComponent::Translate | KisTransformComponent::Scale | KisTransformComponent::Rotate
16 | KisTransformComponent::Shear | KisTransformComponent::Project;
17}

References Project, Rotate, Scale, Shear, and Translate.

◆ mapToRect()

KRITAGLOBAL_EXPORT QTransform KisAlgebra2D::mapToRect ( const QRectF & rect)

Definition at line 729 of file kis_algebra_2d.cpp.

730{
731 return
732 QTransform(rect.width(), 0, 0, rect.height(),
733 rect.x(), rect.y());
734}

◆ mapToRectInverse()

KRITAGLOBAL_EXPORT QTransform KisAlgebra2D::mapToRectInverse ( const QRectF & rect)

Definition at line 736 of file kis_algebra_2d.cpp.

737{
738 return
739 QTransform::fromTranslate(-rect.x(), -rect.y()) *
740 QTransform::fromScale(rect.width() != 0 ? 1.0 / rect.width() : 0.0,
741 rect.height() != 0 ? 1.0 / rect.height() : 0.0);
742}

◆ maxDimension()

template<class Size >
auto KisAlgebra2D::maxDimension ( Size size) -> decltype(size.width())

Definition at line 338 of file kis_algebra_2d.h.

338 {
339 return qMax(size.width(), size.height());
340}

◆ mergeShapesWithGutter()

VectorPath KisAlgebra2D::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)

Parameters
shape1
shape2
oneEnd
otherEnd
index1index of the segment in the first shape
index2index of the segment in the second shape
Returns

Definition at line 1898 of file kis_algebra_2d.cpp.

1899{
1900 using VPoint = VectorPath::VectorPathPoint;
1901 QList<VPoint> result;
1902
1903 result.append(shape1.pointAt(0));
1904
1905 auto appendFrom = [] (QList<VPoint>& res, int start, int end, const VectorPath& shape) {
1906 start = qBound(0, start, shape.segmentsCount() - 1);
1907 end = qBound(0, end, shape.segmentsCount() - 1);
1908
1909 for (int i = start; i < end; i++) {
1910 QList<VPoint> segment = shape.segmentAt(i);
1911 KIS_SAFE_ASSERT_RECOVER_RETURN(segment.length() == 2);
1912 res.append(segment[1]);
1913 }
1914 };
1915
1916 VectorPath reversedShape2 = reverseSecondPoly ? shape2.reversed() : VectorPath(shape2);
1917 int reversedIndex2 = reverseSecondPoly ? (shape2.segmentsCount() - index2 - 1) : index2;
1918
1919 if (isSameShape) {
1920 int minIndex = qMin(index1, index2);
1921 int maxIndex = qMax(index1, index2);
1922
1923 // the indexes define a way to cut the shape into two shapes
1924 // and one of them is kind of more outer than the other
1925 // (they could intersect but... that's the user problem at that point, they can fix it point by point)
1926 // in normal situation, it's like an outer ring of edges and inner ring of edges (which might be just one edge, which is the most common case)
1927 // and we can find that out by checking whether the ends of the selected edges are inside or outside of the shape
1928 // and if they're inside a shape, we're assuming that's the resulting shape (and the other is discarded)
1929 // no islands in comic panels on my watch!
1930
1931 QList<VPoint> result1;
1932 // min index to max index
1933 if (minIndex < 0 || maxIndex >= shape1.segmentsCount()) {
1934 return shape1;
1935 }
1936
1937 result1 << VPoint::moveTo(shape1.segmentAt(minIndex)[1].endPoint);
1938 appendFrom(result1, minIndex + 1, maxIndex, shape1);
1939
1940 QList<VPoint> result2;
1941 result2 << VPoint::moveTo(shape1.segmentAt(maxIndex)[1].endPoint);
1942 appendFrom(result2, maxIndex + 1, shape1.segmentsCount(), shape1);
1943 appendFrom(result2, 0, minIndex, shape1);
1944
1945
1946 VectorPath minEnd = index1 > index2 ? oneEnd : otherEnd;
1947 VectorPath maxEnd = index1 > index2 ? otherEnd : oneEnd;
1948
1949 appendFrom(result1, 0, minEnd.segmentsCount(), minEnd);
1950 appendFrom(result2, 0, maxEnd.segmentsCount(), maxEnd);
1951
1952 VectorPath result1Vec(result1);
1953 VectorPath result2Vec(result2);
1954
1955
1956 // TODO: assumes winding fill
1957 if (isInsideShape(result2Vec, shape1.segmentAt(minIndex)[1].endPoint)) {
1958 // could check all
1959 return result2Vec;
1960 }
1961 return result1Vec;
1962
1963 } else {
1964 appendFrom(result, 0, index1, shape1);
1965 appendFrom(result, 0, oneEnd.segmentsCount(), oneEnd);
1966 appendFrom(result, reversedIndex2 + 1, reversedShape2.segmentsCount(), reversedShape2);
1967 appendFrom(result, 0, reversedIndex2, reversedShape2);
1968 appendFrom(result, 0, otherEnd.segmentsCount(), otherEnd);
1969 appendFrom(result, index1 + 1, shape1.segmentsCount(), shape1);
1970 }
1971
1972 return VectorPath(result);
1973
1974}
QList< VectorPathPoint > segmentAt(int i) const
VectorPath reversed() const
VectorPathPoint pointAt(int i) const
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128

References isInsideShape(), KIS_SAFE_ASSERT_RECOVER_RETURN, KisAlgebra2D::VectorPath::pointAt(), KisAlgebra2D::VectorPath::reversed(), KisAlgebra2D::VectorPath::segmentAt(), and KisAlgebra2D::VectorPath::segmentsCount().

◆ minDimension()

template<class Size >
auto KisAlgebra2D::minDimension ( Size size) -> decltype(size.width())

Definition at line 343 of file kis_algebra_2d.h.

343 {
344 return qMin(size.width(), size.height());
345}

◆ moveElasticPoint() [1/2]

QPointF KRITAGLOBAL_EXPORT KisAlgebra2D::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

Parameters
ptpoint in question, tied to points base, wingA and wingB using springs
baseinitial position of the dragged point
newBasefinal position of the dragged point
wingAfirst anchor point
wingBsecond anchor point
Returns
the new position of pt

The function requires (newBase - base) be much smaller than any of the distances between other points. If larger distances are necessary, then use integration.

Definition at line 1096 of file kis_algebra_2d.cpp.

1097{
1098 using KisAlgebra2D::norm;
1101
1102 const QPointF vecL = base - pt;
1103 const QPointF vecLa = wingA - pt;
1104 const QPointF vecLb = wingB - pt;
1105
1106 const qreal L = norm(vecL);
1107 const qreal La = norm(vecLa);
1108 const qreal Lb = norm(vecLb);
1109
1110 const qreal sinMuA = crossProduct(vecLa, vecL) / (La * L);
1111 const qreal sinMuB = crossProduct(vecL, vecLb) / (Lb * L);
1112
1113 const qreal cosMuA = dotProduct(vecLa, vecL) / (La * L);
1114 const qreal cosMuB = dotProduct(vecLb, vecL) / (Lb * L);
1115
1116 const qreal dL = dotProduct(newBase - base, -vecL) / L;
1117
1118
1119 const qreal divB = (cosMuB + La / Lb * sinMuB * cosMuA / sinMuA) / L +
1120 (cosMuB + sinMuB * cosMuA / sinMuA) / Lb;
1121 const qreal dLb = dL / ( L * divB);
1122
1123 const qreal divA = (cosMuA + Lb / La * sinMuA * cosMuB / sinMuB) / L +
1124 (cosMuA + sinMuA * cosMuB / sinMuB) / La;
1125 const qreal dLa = dL / ( L * divA);
1126
1127 boost::optional<QPointF> result =
1128 KisAlgebra2D::findTrianglePointNearest(wingA, wingB, La + dLa, Lb + dLb, pt);
1129
1130 const QPointF resultPoint = result ? *result : pt;
1131
1132 return resultPoint;
1133}
boost::optional< QPointF > findTrianglePointNearest(const QPointF &p1, const QPointF &p2, qreal a, qreal b, const QPointF &nearest)

References crossProduct(), dotProduct(), findTrianglePointNearest(), and norm().

◆ moveElasticPoint() [2/2]

QPointF KRITAGLOBAL_EXPORT KisAlgebra2D::moveElasticPoint ( const QPointF & pt,
const QPointF & base,
const QPointF & newBase,
const QVector< QPointF > & anchorPoints )

moveElasticPoint moves point pt based on the model of elasticity

Parameters
ptpoint in question, tied to points base, anchorPoints using springs
baseinitial position of the dragged point
newBasefinal position of the dragged point
anchorPointsanchor points
Returns
the new position of pt

The function expects (newBase - base) be much smaller than any of the distances between other points. If larger distances are necessary, then use integration.

Definition at line 1202 of file kis_algebra_2d.cpp.

1205{
1206 const QPointF offset = newBase - base;
1207
1208 QPointF newResultPoint = pt + offset;
1209
1210#ifdef HAVE_GSL
1211
1212 ElasticMotionData data;
1213 data.newBasePos = newBase;
1214 data.oldBasePos = base;
1215 data.anchorPoints = anchorPoints;
1216 data.oldResultPoint = pt;
1217
1218 const gsl_multimin_fminimizer_type *T =
1219 gsl_multimin_fminimizer_nmsimplex2;
1220 gsl_multimin_fminimizer *s = 0;
1221 gsl_vector *ss, *x;
1222 gsl_multimin_function minex_func;
1223
1224 size_t iter = 0;
1225 int status;
1226 double size;
1227
1228 /* Starting point */
1229 x = gsl_vector_alloc (2);
1230 gsl_vector_set (x, 0, newResultPoint.x());
1231 gsl_vector_set (x, 1, newResultPoint.y());
1232
1233 /* Set initial step sizes to 0.1 */
1234 ss = gsl_vector_alloc (2);
1235 gsl_vector_set (ss, 0, 0.1 * offset.x());
1236 gsl_vector_set (ss, 1, 0.1 * offset.y());
1237
1238 /* Initialize method and iterate */
1239 minex_func.n = 2;
1240 minex_func.f = elasticMotionError;
1241 minex_func.params = (void*)&data;
1242
1243 s = gsl_multimin_fminimizer_alloc (T, 2);
1244 gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
1245
1246 do
1247 {
1248 iter++;
1249 status = gsl_multimin_fminimizer_iterate(s);
1250
1251 if (status)
1252 break;
1253
1254 size = gsl_multimin_fminimizer_size (s);
1255 status = gsl_multimin_test_size (size, 1e-6);
1256
1262 if (status == GSL_SUCCESS && elasticMotionError(s->x, &data) > 0.5) {
1263 status = GSL_CONTINUE;
1264 }
1265
1266 if (status == GSL_SUCCESS)
1267 {
1268// qDebug() << "*******Converged to minimum";
1269// qDebug() << gsl_vector_get (s->x, 0)
1270// << gsl_vector_get (s->x, 1)
1271// << "|" << s->fval << size;
1272// qDebug() << ppVar(iter);
1273
1274 newResultPoint.rx() = gsl_vector_get (s->x, 0);
1275 newResultPoint.ry() = gsl_vector_get (s->x, 1);
1276 }
1277 }
1278 while (status == GSL_CONTINUE && iter < 10000);
1279
1280 if (status != GSL_SUCCESS) {
1281 ENTER_FUNCTION() << "failed to find point" << ppVar(pt) << ppVar(base) << ppVar(newBase);
1282 }
1283
1284 gsl_vector_free(x);
1285 gsl_vector_free(ss);
1286 gsl_multimin_fminimizer_free (s);
1287#endif
1288
1289 return newResultPoint;
1290}
#define ENTER_FUNCTION()
Definition kis_debug.h:178

References ENTER_FUNCTION, and ppVar.

◆ movePointAlongTheLine()

QPointF KRITAGLOBAL_EXPORT KisAlgebra2D::movePointAlongTheLine ( const QPointF & point,
const QLineF & direction,
qreal distance,
bool ensureOnLine )

movePointAlongTheLine moves the point a particular distance in the specified direction

Parameters
pointthe point to move
directionthe direction to move the point along
distancedistance to move the point
ensureOnLineif true, the algorithm will first change the point to the nearest point on the line, and then move it along the line (if not true, it's an equivalent of movePointInTheDirection, with QPointF being a vector of direction)
Returns
the new position of the moved point

Definition at line 1790 of file kis_algebra_2d.cpp.

1791{
1792 QPointF response = point;
1793 if (ensureOnLine) {
1794 response = findNearestPointOnLine(point, direction);
1795 }
1796 return movePointInTheDirection(response, direction.p2() - direction.p1(), distance);
1797}
QPointF findNearestPointOnLine(const QPointF &point, const QLineF &line, bool unbounded)

References distance(), findNearestPointOnLine(), and movePointInTheDirection().

◆ movePointInTheDirection()

QPointF KRITAGLOBAL_EXPORT KisAlgebra2D::movePointInTheDirection ( const QPointF & point,
const QPointF & direction,
qreal distance )

movePointAlongTheLine moves the point a particular distance in the specified direction

Parameters
pointthe point to move
directionthe direction to move the point along
distancedistance to move the point
Returns
the new position of the moved point

Definition at line 1783 of file kis_algebra_2d.cpp.

1784{
1785 QPointF response = point;
1786 QLineF line = QLineF(QPointF(0, 0), direction);
1787 return response + distance*direction/line.length();
1788}

References distance().

◆ norm()

template<class T >
qreal KisAlgebra2D::norm ( const T & a)

Definition at line 63 of file kis_algebra_2d.h.

64{
65 return std::sqrt(pow2(a.x()) + pow2(a.y()));
66}

References pow2().

◆ normalize()

template<class Point >
Point KisAlgebra2D::normalize ( const Point & a)

Definition at line 75 of file kis_algebra_2d.h.

76{
77 const qreal length = norm(a);
78 return (1.0 / length) * a;
79}

References length(), and norm().

◆ normSquared()

template<class T >
qreal KisAlgebra2D::normSquared ( const T & a)

Definition at line 69 of file kis_algebra_2d.h.

70{
71 return pow2(a.x()) + pow2(a.y());
72}

References pow2().

◆ operator<<() [1/2]

QDebug KRITAGLOBAL_EXPORT KisAlgebra2D::operator<< ( QDebug debug,
const VectorPath & path )

Definition at line 2391 of file kis_algebra_2d.cpp.

2392{
2393 debug << path.asPainterPath();
2394 return debug;
2395}

◆ operator<<() [2/2]

QDebug KRITAGLOBAL_EXPORT KisAlgebra2D::operator<< ( QDebug debug,
const VectorPath::VectorPathPoint & point )

Definition at line 2397 of file kis_algebra_2d.cpp.

2398{
2399 bool autoInsertSpaces = debug.autoInsertSpaces();
2400 debug.setAutoInsertSpaces(false);
2401 debug << (point.type == VectorPath::VectorPathPoint::MoveTo ? "(move " : (point.type == VectorPath::VectorPathPoint::BezierTo ? "(curve " : "(line "));
2402 debug << "(" << point.endPoint.x() << ", " << point.endPoint.y() << ")";
2403 if (point.type == VectorPath::VectorPathPoint::BezierTo) {
2404 debug << ": (";
2405 debug << "(" << point.controlPoint1.x() << ", " << point.controlPoint1.y() << ")";
2406 debug << ", ";
2407 debug << "(" << point.controlPoint2.x() << ", " << point.controlPoint2.y() << ")";
2408 debug << ")";
2409 }
2410 debug << ")";
2411 debug.setAutoInsertSpaces(autoInsertSpaces);
2412 return debug;
2413}

References KisAlgebra2D::VectorPath::VectorPathPoint::BezierTo, KisAlgebra2D::VectorPath::VectorPathPoint::controlPoint1, KisAlgebra2D::VectorPath::VectorPathPoint::controlPoint2, KisAlgebra2D::VectorPath::VectorPathPoint::endPoint, KisAlgebra2D::VectorPath::VectorPathPoint::MoveTo, and KisAlgebra2D::VectorPath::VectorPathPoint::type.

◆ pointToLineDistSquared()

qreal KRITAGLOBAL_EXPORT KisAlgebra2D::pointToLineDistSquared ( const QPointF & pt,
const QLineF & line )

Definition at line 1555 of file kis_algebra_2d.cpp.

1556{
1557 // distance = |(p2 - p1) x (p1 - pt)| / |p2 - p1|
1558
1559 // magnitude of (p2 - p1) x (p1 - pt)
1560 const qreal cross = (line.dx() * (line.y1() - pt.y()) - line.dy() * (line.x1() - pt.x()));
1561
1562 return cross * cross / (line.dx() * line.dx() + line.dy() * line.dy());
1563}

◆ polygonDirection()

template<class T >
int KisAlgebra2D::polygonDirection ( const QVector< T > & polygon)
Returns
1 if the polygon is counterclockwise -1 if the polygon is clockwise

Note: the sign is flipped because our 0y axis is reversed

Definition at line 181 of file kis_algebra_2d.h.

181 {
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}

◆ Q_DECLARE_FLAGS()

KisAlgebra2D::Q_DECLARE_FLAGS ( KisTransformComponents ,
KisTransformComponent  )

◆ quadraticEquation()

KRITAGLOBAL_EXPORT int KisAlgebra2D::quadraticEquation ( qreal a,
qreal b,
qreal c,
qreal * x1,
qreal * x2 )

Solves a quadratic equation in a form:

a * x^2 + b * x + c = 0

WARNING: Please note that a must be nonzero! Otherwise the equation is not quadratic! And this function doesn't check that!

Returns
the number of solutions. It can be 0, 1 or 2.

x1, x2 — the found solution. The variables are filled with data iff the corresponding solution is found. That is: 0 solutions — variables are not touched, 1 solution — x1 is filled with the result, 2 solutions — x1 and x2 are filled.

Definition at line 615 of file kis_algebra_2d.cpp.

616{
617 int numSolutions = 0;
618
619 const qreal D = pow2(b) - 4 * a * c;
620 const qreal eps = 1e-14;
621
622 if (qAbs(D) <= eps) {
623 *x1 = -b / (2 * a);
624 numSolutions = 1;
625 } else if (D < 0) {
626 return 0;
627 } else {
628 const qreal sqrt_D = std::sqrt(D);
629
630 *x1 = (-b + sqrt_D) / (2 * a);
631 *x2 = (-b - sqrt_D) / (2 * a);
632 numSolutions = 2;
633 }
634
635 return numSolutions;
636}
qreal D(qreal t, const QPointF &P0, const QPointF &P1, const QPointF &P2, const QPointF &P3, const QPointF &p)

References D(), eps, and pow2().

◆ relativeToAbsolute() [1/3]

QPointF KisAlgebra2D::relativeToAbsolute ( const QPointF & pt,
const QRectF & rc )
inline

Scale the relative point \pt into the bounds of rc. The point might be outside the rectangle.

Definition at line 752 of file kis_algebra_2d.h.

752 {
753 return rc.topLeft() + QPointF(pt.x() * rc.width(), pt.y() * rc.height());
754}

◆ relativeToAbsolute() [2/3]

QRectF KisAlgebra2D::relativeToAbsolute ( const QRectF & rel,
const QRectF & rc )
inline

Scales relative isotropic value from relative to absolute coordinate system using SVG 1.1 rules (see chapter 7.10)

Definition at line 789 of file kis_algebra_2d.h.

789 {
790 return QRectF(relativeToAbsolute(rel.topLeft(), rc), relativeToAbsolute(rel.bottomRight(), rc));
791}
QPointF relativeToAbsolute(const QPointF &pt, const QRectF &rc)

References relativeToAbsolute().

◆ relativeToAbsolute() [3/3]

qreal KisAlgebra2D::relativeToAbsolute ( qreal value,
const QRectF & rc )
inline

Scales relative isotropic value from relative to absolute coordinate system using SVG 1.1 rules (see chapter 7.10)

Definition at line 771 of file kis_algebra_2d.h.

771 {
772 const qreal coeff = std::sqrt(pow2(rc.width()) + pow2(rc.height())) / std::sqrt(2.0);
773 return value * coeff;
774}

References pow2(), and value().

◆ removeGutterOneEndSmart()

QPainterPath KisAlgebra2D::removeGutterOneEndSmart ( const QPainterPath & shape1,
int index1,
const QPainterPath & shape2,
int index2,
QLineF middleLine,
bool reverseFirstPoly,
bool reverseSecondPoly )

Definition at line 1817 of file kis_algebra_2d.cpp.

1818{
1819 // moving from shape1.index1.p1 to shape1.index2.p2
1820
1821 auto reverseIfNeeded = [] (const QLineF &line, bool reverse) {
1822 if (reverse) {
1823 return reverseDirection(line);
1824 }
1825 return line;
1826 };
1827
1828 QLineF line1 = reverseIfNeeded(getLineFromElements(shape1, index1), reverseFirstPoly);
1829 QLineF line2 = reverseIfNeeded(getLineFromElements(shape2, index2), reverseSecondPoly);
1830
1831 QLineF leftLine;
1832 QLineF rightLine;
1833
1834 int indexMultiplierFirst = reverseFirstPoly ? -1 : 1;
1835 int indexMultiplierSecond = reverseSecondPoly ? -1 : 1;
1836
1837 leftLine = reverseIfNeeded(getLineFromElements(shape1, index1 - indexMultiplierFirst*1), reverseFirstPoly);
1838 rightLine = reverseIfNeeded(getLineFromElements(shape2, index2 + indexMultiplierSecond*1), reverseSecondPoly);
1839
1840 qreal leftPointDistanceFromMiddle;
1841 qreal rightPointDistanceFromMiddle;
1842
1843 QPointF leftPoint = line1.p1();
1844 QPointF rightPoint = line2.p2();
1845
1846 leftPointDistanceFromMiddle = kisDistanceToLine(leftPoint, middleLine);
1847 rightPointDistanceFromMiddle = kisDistanceToLine(rightPoint, middleLine);
1848
1849 QPainterPath simpleResult;
1850 simpleResult.moveTo(leftPoint);
1851 simpleResult.lineTo(rightPoint);
1852
1853 auto makeTrianglePath = [leftPoint, rightPoint] (QPointF middle) {
1854 QPainterPath result;
1855 result.moveTo(leftPoint);
1856 result.lineTo(middle);
1857 result.lineTo(rightPoint);
1858 return result;
1859 };
1860
1861 QPointF intersectionPoint;
1862 QLineF::IntersectionType intersectionType = leftLine.intersects(rightLine, &intersectionPoint);
1863
1864 qreal distanceToMiddle = 0;
1865 if (intersectionType != QLineF::NoIntersection) {
1866 distanceToMiddle = kisDistanceToLine(intersectionPoint, middleLine);
1867 }
1868
1869
1870 if (intersectionType == QLineF::NoIntersection || distanceToMiddle > qMax(leftPointDistanceFromMiddle, rightPointDistanceFromMiddle)) {
1871
1872 // first arg: bounded line, second: unbounded
1873 boost::optional<QPointF> leftIntersection = intersectLines(line2, leftLine);
1874 boost::optional<QPointF> rightIntersection = intersectLines(line1, rightLine);
1875
1876 if (leftIntersection.has_value()) {
1877 return makeTrianglePath(leftIntersection.value());
1878 } else if (rightIntersection.has_value()) {
1879 return makeTrianglePath(rightIntersection.value());
1880 } else {
1881 return simpleResult;
1882 }
1883
1884 }
1885
1886 QPainterPath betterResult = makeTrianglePath(intersectionPoint);
1887
1888 return betterResult;
1889
1890}
QLineF getLineFromElements(const QPainterPath &shape, int index)
QLineF reverseDirection(const QLineF &line)

References getLineFromElements(), intersectLines(), kisDistanceToLine(), and reverseDirection().

◆ removeGutterSmart()

QPainterPath KRITAGLOBAL_EXPORT KisAlgebra2D::removeGutterSmart ( const QPainterPath & shape1,
int index1,
const QPainterPath & shape2,
int index2,
bool isSameShape )

removeGutterSmart

Parameters
shape1
index1
shape2
index2
Returns

Definition at line 1977 of file kis_algebra_2d.cpp.

1978{
1979 if (index1 + 1 >= shape1.elementCount() || index2 + 1 >= shape2.elementCount()) {
1980 return QPainterPath();
1981 }
1982
1983 QLineF line1 = getLineFromElements(shape1, index1);
1984 QLineF line2 = getLineFromElements(shape2, index2);
1985
1986 QVector<QPointF> poly1 = QVector<QPointF>() << line1.p1() << line1.p2() << line2.p1();
1987 QVector<QPointF> poly2 = QVector<QPointF>() << line1.p2() << line2.p1() << line2.p2();
1988 bool reverseSecondPoly = false;
1989 if (polygonDirection(poly1) != polygonDirection(poly2)) {
1990 line2 = QLineF(line2.p2(), line2.p1()); // quick fix to ensure that the shape will be correct (convex)
1991 reverseSecondPoly = true;
1992 }
1993
1994 QLineF middleLine = QLineF((line1.p1() + line2.p2())/2, (line1.p2() + line2.p1())/2);
1995
1996 // more difficult version:
1997 QPainterPath oneEnd = removeGutterOneEndSmart(shape1, index1, shape2, index2, middleLine, false, reverseSecondPoly);
1998 QPainterPath otherEnd = removeGutterOneEndSmart(shape2, index2, shape1, index1, middleLine, reverseSecondPoly, false);
1999
2000 VectorPath vectorShape1 = VectorPath(shape1);
2001 VectorPath vectorShape2 = VectorPath(shape2);
2002 VectorPath vectorOneEnd = VectorPath(oneEnd);
2003 VectorPath vectorOtherEnd = VectorPath(otherEnd);
2004
2005 VectorPath vectorResultHere = mergeShapesWithGutter(vectorShape1, vectorShape2, vectorOneEnd, vectorOtherEnd, vectorShape1.pathIndexToSegmentIndex(index1), vectorShape2.pathIndexToSegmentIndex(index2), reverseSecondPoly, isSameShape);
2006 return vectorResultHere.trulySimplified(1e-02).asPainterPath();
2007
2008
2009
2010}
QPainterPath removeGutterOneEndSmart(const QPainterPath &shape1, int index1, const QPainterPath &shape2, int index2, QLineF middleLine, bool reverseFirstPoly, bool reverseSecondPoly)
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)

References KisAlgebra2D::VectorPath::asPainterPath(), getLineFromElements(), mergeShapesWithGutter(), KisAlgebra2D::VectorPath::pathIndexToSegmentIndex(), polygonDirection(), removeGutterOneEndSmart(), and KisAlgebra2D::VectorPath::trulySimplified().

◆ reverseDirection()

QLineF KisAlgebra2D::reverseDirection ( const QLineF & line)

Definition at line 1811 of file kis_algebra_2d.cpp.

1812{
1813 return QLineF(line.p2(), line.p1());
1814}

◆ rightUnitNormal()

template<class T >
T KisAlgebra2D::rightUnitNormal ( const T & a)

Definition at line 142 of file kis_algebra_2d.h.

143{
144 return -leftUnitNormal(a);
145}

References leftUnitNormal().

◆ sampleRectWithPoints() [1/3]

QVector< QPoint > KRITAGLOBAL_EXPORT KisAlgebra2D::sampleRectWithPoints ( const QRect & rect)

Definition at line 509 of file kis_algebra_2d.cpp.

510 {
511 return sampleRectWithPoints<QRect, QPoint>(rect);
512 }

◆ sampleRectWithPoints() [2/3]

QVector< QPointF > KRITAGLOBAL_EXPORT KisAlgebra2D::sampleRectWithPoints ( const QRectF & rect)

Definition at line 514 of file kis_algebra_2d.cpp.

515 {
516 return sampleRectWithPoints<QRectF, QPointF>(rect);
517 }

◆ sampleRectWithPoints() [3/3]

template<class Rect , class Point >
QVector< Point > KisAlgebra2D::sampleRectWithPoints ( const Rect & rect)

Definition at line 487 of file kis_algebra_2d.cpp.

488 {
489 QVector<Point> points;
490
491 Point m1 = 0.5 * (rect.topLeft() + rect.topRight());
492 Point m2 = 0.5 * (rect.bottomLeft() + rect.bottomRight());
493
494 points << rect.topLeft();
495 points << m1;
496 points << rect.topRight();
497
498 points << 0.5 * (rect.topLeft() + rect.bottomLeft());
499 points << 0.5 * (m1 + m2);
500 points << 0.5 * (rect.topRight() + rect.bottomRight());
501
502 points << rect.bottomLeft();
503 points << m2;
504 points << rect.bottomRight();
505
506 return points;
507 }

◆ signPZ()

template<typename T >
T KisAlgebra2D::signPZ ( T x)

Usual sign() function with positive zero

Definition at line 91 of file kis_algebra_2d.h.

91 {
92 return x >= T(0) ? T(1) : T(-1);
93}

◆ signZZ()

template<typename T >
T KisAlgebra2D::signZZ ( T x)

Usual sign() function with zero returning zero

Definition at line 99 of file kis_algebra_2d.h.

99 {
100 return x == T(0) ? T(0) : x > T(0) ? T(1) : T(-1);
101}

◆ simplifyShape()

QPainterPath KisAlgebra2D::simplifyShape ( const QPainterPath & path)

Definition at line 1892 of file kis_algebra_2d.cpp.

1892 {
1893
1894 VectorPath vector(path);
1895 return vector.trulySimplified().asPainterPath();
1896}

References KisAlgebra2D::VectorPath::asPainterPath(), and KisAlgebra2D::VectorPath::trulySimplified().

◆ smallArrow()

QPainterPath KRITAGLOBAL_EXPORT KisAlgebra2D::smallArrow ( )

Definition at line 129 of file kis_algebra_2d.cpp.

130{
131 QPainterPath p;
132
133 p.moveTo(5, 2);
134 p.lineTo(-3, 8);
135 p.lineTo(-5, 5);
136 p.lineTo( 2, 0);
137 p.lineTo(-5,-5);
138 p.lineTo(-3,-8);
139 p.lineTo( 5,-2);
140 p.arcTo(QRectF(3, -2, 4, 4), 90, -180);
141
142 return p;
143}

References p.

◆ toQTransformStraight()

QTransform KisAlgebra2D::toQTransformStraight ( const Eigen::Matrix3d & m)
inline

Definition at line 769 of file kis_algebra_2d.cpp.

770{
771 return QTransform(m(0,0), m(0,1), m(0,2),
772 m(1,0), m(1,1), m(1,2),
773 m(2,0), m(2,1), m(2,2));
774}

◆ transformAsBase()

QPointF KRITAGLOBAL_EXPORT KisAlgebra2D::transformAsBase ( const QPointF & pt,
const QPointF & base1,
const QPointF & base2 )

Let pt, base1 are two vectors. base1 is uniformly scaled and then rotated into base2 using transformation matrix S * R. The function applies the same transformation to \pt and returns the result.

Definition at line 89 of file kis_algebra_2d.cpp.

89 {
90 qreal len1 = norm(base1);
91 if (len1 < 1e-5) return pt;
92 qreal sin1 = base1.y() / len1;
93 qreal cos1 = base1.x() / len1;
94
95 qreal len2 = norm(base2);
96 if (len2 < 1e-5) return QPointF();
97 qreal sin2 = base2.y() / len2;
98 qreal cos2 = base2.x() / len2;
99
100 qreal sinD = sin2 * cos1 - cos2 * sin1;
101 qreal cosD = cos1 * cos2 + sin1 * sin2;
102 qreal scaleD = len2 / len1;
103
104 QPointF result;
105 result.rx() = scaleD * (pt.x() * cosD - pt.y() * sinD);
106 result.ry() = scaleD * (pt.x() * sinD + pt.y() * cosD);
107
108 return result;
109}

References norm().

◆ transformEllipse()

std::pair< QPointF, QTransform > KRITAGLOBAL_EXPORT KisAlgebra2D::transformEllipse ( const QPointF & axes,
const QTransform & fullLocalToGlobal )

Definition at line 913 of file kis_algebra_2d.cpp.

914{
915 KisAlgebra2D::DecomposedMatrix decomposed(fullLocalToGlobal);
916 const QTransform localToGlobal =
917 decomposed.scaleTransform() *
918 decomposed.shearTransform() *
919
920 /* decomposed.projectTransform() * */
921 /*decomposed.translateTransform() * */
922
923 decomposed.rotateTransform();
924
925 const QTransform localEllipse = QTransform(1.0 / pow2(axes.x()), 0.0, 0.0,
926 0.0, 1.0 / pow2(axes.y()), 0.0,
927 0.0, 0.0, 1.0);
928
929
930 const QTransform globalToLocal = localToGlobal.inverted();
931
932 Eigen::Matrix3d eqM =
933 fromQTransformStraight(globalToLocal *
934 localEllipse *
935 globalToLocal.transposed());
936
937// std::cout << "eqM:" << std::endl << eqM << std::endl;
938
939 Eigen::EigenSolver<Eigen::Matrix3d> eigenSolver(eqM);
940
941 const Eigen::Matrix3d T = eigenSolver.eigenvalues().real().asDiagonal();
942 const Eigen::Matrix3d U = eigenSolver.eigenvectors().real();
943
944 const Eigen::Matrix3d Ti = eigenSolver.eigenvalues().imag().asDiagonal();
945 const Eigen::Matrix3d Ui = eigenSolver.eigenvectors().imag();
946
947 KIS_SAFE_ASSERT_RECOVER_NOOP(Ti.isZero());
949 KIS_SAFE_ASSERT_RECOVER_NOOP((U * U.transpose()).isIdentity());
950
951// std::cout << "T:" << std::endl << T << std::endl;
952// std::cout << "U:" << std::endl << U << std::endl;
953
954// std::cout << "Ti:" << std::endl << Ti << std::endl;
955// std::cout << "Ui:" << std::endl << Ui << std::endl;
956
957// std::cout << "UTU':" << std::endl << U * T * U.transpose() << std::endl;
958
959 const qreal newA = 1.0 / std::sqrt(T(0,0) * T(2,2));
960 const qreal newB = 1.0 / std::sqrt(T(1,1) * T(2,2));
961
962 const QTransform newGlobalToLocal = toQTransformStraight(U);
963 const QTransform newLocalToGlobal = QTransform::fromScale(-1,-1) *
964 newGlobalToLocal.inverted() *
965 decomposed.translateTransform();
966
967 return std::make_pair(QPointF(newA, newB), newLocalToGlobal);
968}
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
QTransform toQTransformStraight(const Eigen::Matrix3d &m)
Eigen::Matrix3d fromQTransformStraight(const QTransform &t)

References fromQTransformStraight(), KIS_SAFE_ASSERT_RECOVER_NOOP, pow2(), KisAlgebra2D::DecomposedMatrix::rotateTransform(), KisAlgebra2D::DecomposedMatrix::scaleTransform(), KisAlgebra2D::DecomposedMatrix::shearTransform(), toQTransformStraight(), and KisAlgebra2D::DecomposedMatrix::translateTransform().

◆ tryMergePoints()

bool KisAlgebra2D::tryMergePoints ( QPainterPath & path,
const QPointF & startPoint,
const QPointF & endPoint,
qreal & distance,
qreal distanceThreshold,
bool lastSegment )

Definition at line 1565 of file kis_algebra_2d.cpp.

1571{
1572 qreal length = (endPoint - startPoint).manhattanLength();
1573
1574 if (lastSegment || length > distanceThreshold) {
1575 if (lastSegment) {
1576 qreal wrappedLength =
1577 (endPoint - QPointF(path.elementAt(0))).manhattanLength();
1578
1579 if (length < distanceThreshold ||
1580 wrappedLength < distanceThreshold) {
1581
1582 return true;
1583 }
1584 }
1585
1586 distance = 0;
1587 return false;
1588 }
1589
1590 distance += length;
1591
1592 if (distance > distanceThreshold) {
1593 path.lineTo(endPoint);
1594 distance = 0;
1595 }
1596
1597 return true;
1598}

References distance(), and length().

◆ trySimplifyPath()

QPainterPath KRITAGLOBAL_EXPORT KisAlgebra2D::trySimplifyPath ( const QPainterPath & path,
qreal lengthThreshold )

trySimplifyPath Tries to simplify a QPainterPath

Parameters
path– path to simplify.
lengthThreshold– length at which points get merged.
Returns
path with less nodes.

Definition at line 1600 of file kis_algebra_2d.cpp.

1601{
1602 QPainterPath newPath;
1603 QPointF startPoint;
1604 qreal distance = 0;
1605
1606 int count = path.elementCount();
1607 for (int i = 0; i < count; i++) {
1608 QPainterPath::Element e = path.elementAt(i);
1609 QPointF endPoint = QPointF(e.x, e.y);
1610
1611 switch (e.type) {
1612 case QPainterPath::MoveToElement:
1613 newPath.moveTo(endPoint);
1614 break;
1615 case QPainterPath::LineToElement:
1616 if (!tryMergePoints(newPath, startPoint, endPoint,
1617 distance, lengthThreshold, i == count - 1)) {
1618
1619 newPath.lineTo(endPoint);
1620 }
1621 break;
1622 case QPainterPath::CurveToElement: {
1623 Q_ASSERT(i + 2 < count);
1624
1625 if (!tryMergePoints(newPath, startPoint, endPoint,
1626 distance, lengthThreshold, i == count - 1)) {
1627
1628 e = path.elementAt(i + 1);
1629 Q_ASSERT(e.type == QPainterPath::CurveToDataElement);
1630 QPointF ctrl1 = QPointF(e.x, e.y);
1631 e = path.elementAt(i + 2);
1632 Q_ASSERT(e.type == QPainterPath::CurveToDataElement);
1633 QPointF ctrl2 = QPointF(e.x, e.y);
1634 newPath.cubicTo(ctrl1, ctrl2, endPoint);
1635 }
1636
1637 i += 2;
1638 }
1639 default:
1640 ;
1641 }
1642 startPoint = endPoint;
1643 }
1644
1645 return newPath;
1646}
bool tryMergePoints(QPainterPath &path, const QPointF &startPoint, const QPointF &endPoint, qreal &distance, qreal distanceThreshold, bool lastSegment)

References distance(), and tryMergePoints().

◆ wrapValue() [1/2]

template<typename T >
T KisAlgebra2D::wrapValue ( T value,
T min,
T max )
inline

Definition at line 507 of file kis_algebra_2d.h.

507 {
508 return wrapValue(value - min, max - min) + min;
509}

References value(), and wrapValue().

◆ wrapValue() [2/2]

template<typename T , typename std::enable_if< std::is_integral< T >::value, T >::type * = nullptr>
T KisAlgebra2D::wrapValue ( T value,
T wrapBounds )
inline

Definition at line 482 of file kis_algebra_2d.h.

482 {
483 value %= wrapBounds;
484 if (value < 0) {
485 value += wrapBounds;
486 }
487 return value;
488}

References value().