10#include <QPainterPath>
13#include <boost/accumulators/accumulators.hpp>
14#include <boost/accumulators/statistics/stats.hpp>
15#include <boost/accumulators/statistics/min.hpp>
16#include <boost/accumulators/statistics/max.hpp>
24#include <config-gsl.h>
27#include <gsl/gsl_multimin.h>
30#include <Eigen/Eigenvalues>
39 const int numPoints = poly.size();
40 for (
int i = 0; i < numPoints; i++) {
42 if (nextI >= numPoints) {
46 const QPointF &
p0 = poly[i];
47 const QPointF &
p1 = poly[nextI];
49 QPointF edge =
p1 -
p0;
52 / (0.5 * edge.manhattanLength());
60 QPointF adjustedPoint = *pt +
salt;
65 adjustedPoint = *pt -
salt;
89QPointF
transformAsBase(
const QPointF &pt,
const QPointF &base1,
const QPointF &base2) {
90 qreal len1 =
norm(base1);
91 if (len1 < 1e-5)
return pt;
92 qreal sin1 = base1.y() / len1;
93 qreal cos1 = base1.x() / len1;
95 qreal len2 =
norm(base2);
96 if (len2 < 1e-5)
return QPointF();
97 qreal sin2 = base2.y() / len2;
98 qreal cos2 = base2.x() / len2;
100 qreal sinD = sin2 * cos1 - cos2 * sin1;
101 qreal cosD = cos1 * cos2 + sin1 * sin2;
102 qreal scaleD = len2 / len1;
105 result.rx() = scaleD * (pt.x() * cosD - pt.y() * sinD);
106 result.ry() = scaleD * (pt.x() * sinD + pt.y() * cosD);
113 qreal a1 = std::atan2(v1.y(), v1.x());
114 qreal a2 = std::atan2(v2.y(), v2.x());
125 const QVector2D diff(
p2 -
p1);
126 return std::atan2(diff.y(), diff.x());
140 p.arcTo(QRectF(3, -2, 4, 4), 90, -180);
145template <
class Po
int,
class Rect>
148 if (pt.x() >
bounds.right()) {
150 }
else if (pt.x() <
bounds.left()) {
154 if (pt.y() >
bounds.bottom()) {
155 pt.ry() =
bounds.bottom();
156 }
else if (pt.y() <
bounds.top()) {
187 double p1 = -(line.x2() - line.x1());
189 double p3 = -(line.y2() - line.y1());
195 float tmin = extendFirst ? -std::numeric_limits<float>::infinity() : 0;
196 float tmax = extendSecond ? std::numeric_limits<float>::infinity() : 1;
198 for (
int i = 0; i <
p.length(); i++) {
200 if (
p[i] == 0 && q[i] < 0) {
203 }
else if (
p[i] < 0) {
205 double t = q[i]/
p[i];
218 }
else if (
p[i] > 0) {
220 double t = q[i]/
p[i];
234 QPointF pt1 = line.p1();
235 QPointF pt2 = line.p2();
237 if (extendFirst || tmin > 0) {
238 pt1 = QPointF(line.x1() + tmin*(line.x2() - line.x1()), line.y1() + tmin*(line.y2() - line.y1()));
240 if (extendSecond || tmax < 1) {
241 pt2 = QPointF(line.x1() + tmax*(line.x2() - line.x1()), line.y1() + tmax*(line.y2() - line.y1()));
253 qreal epsilon = 1e-07;
255 if (polygon.size() == 0) {
260 if (!extendFirst && !extendSecond && polygon.containsPoint(line.p1(), Qt::WindingFill) && polygon.containsPoint(line.p2(), Qt::WindingFill)) {
272 float aBigNumber = 100000;
273 float tmin = extendFirst ? -aBigNumber : 0;
274 float tmax = extendSecond ? aBigNumber : 1;
281 bool clockwise =
false;
284 QPointF vec1 = polygon[1] - polygon[0];
285 QPointF vec2 = polygon[2] - polygon[0];
287 QLineF line1(QPointF(0, 0), vec1);
288 QLineF line2(QPointF(0, 0), vec2);
290 qreal angle = line1.angleTo(line2);
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();
303 normals.append(QPointF(ydiff, -xdiff));
305 normals.append(QPointF(-ydiff, xdiff));
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();
317 normals.append(QPointF(ydiff, -xdiff));
319 normals.append(QPointF(-ydiff, xdiff));
324 QPointF lineVec = line.p2() - line.p1();
330 QPointF
p1 = line.p1();
331 QPointF
p2 = line.p2();
334 if (qAbs(lineVec.x()) < epsilon) {
350 for (
int i = 0; i < clippingLines.size(); i++) {
353 QPointF clipVec = clippingLines[i].p2() - clippingLines[i].p1();
355 if (
qFuzzyCompare(clipVec.x()*lineVec.y(), clipVec.y()*lineVec.x())) {
360 qreal distanceBetweenLines = qAbs(pA*clippingLines[i].
p1().x() + pB*clippingLines[i].
p1().y() + pC)
361 /(sqrt(pA*pA + pB*pB));
364 if (qAbs(distanceBetweenLines) < epsilon) {
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());
374 t1 = (clippingLines[i].p1().y() -
p1.y())/(
p2.y() -
p1.y());
375 t2 = (clippingLines[i].p2().y() -
p1.y())/(
p2.y() -
p1.y());
378 qreal tmin1 = qMin(t1, t2);
379 qreal tmax2 = qMax(t1, t2);
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();
410 Eigen::Vector2f ta =
A.colPivHouseholderQr().solve(b);
421 boost::optional<QPointF> pEOptional =
intersectLines(clippingLines[i], line);
427 QPointF pE = pEOptional.value();
428 QPointF n = normals[i];
430 QPointF
A = line.p2() - line.p1();
431 QPointF
B = line.p1() - pE;
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;
441 qreal tminvalue = tmin * under + over;
442 qreal tmaxvalue = tmax * under + over;
444 if (tminvalue > 0 && tmaxvalue > 0) {
448 if (tminvalue <= 0 && tmaxvalue <= 0) {
451 if (tminvalue*tmaxvalue < 0) {
481 QLineF response = QLineF(tmin*line.p2() + (1-tmin)*line.p1(), tmax*line.p2() + (1-tmax)*line.p1());
486 template <
class Rect,
class Po
int>
491 Point m1 = 0.5 * (
rect.topLeft() +
rect.topRight());
492 Point m2 = 0.5 * (
rect.bottomLeft() +
rect.bottomRight());
494 points <<
rect.topLeft();
496 points <<
rect.topRight();
498 points << 0.5 * (
rect.topLeft() +
rect.bottomLeft());
499 points << 0.5 * (m1 + m2);
500 points << 0.5 * (
rect.topRight() +
rect.bottomRight());
502 points <<
rect.bottomLeft();
504 points <<
rect.bottomRight();
511 return sampleRectWithPoints<QRect, QPoint>(
rect);
516 return sampleRectWithPoints<QRectF, QPointF>(
rect);
520 template <
class Rect,
class Po
int,
bool alignPixels>
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;
527 Q_FOREACH (
const Point &pt, points) {
535 resultRect.setCoords(std::floor(min(accX)), std::floor(min(accY)),
536 std::ceil(max(accX)), std::ceil(max(accY)));
538 resultRect.setCoords(min(accX), min(accY),
539 max(accX), max(accY));
547 return approximateRectFromPointsImpl<QRect, QPoint, true>(points);
552 return approximateRectFromPointsImpl<QRectF, QPointF, false>(points);
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;
563 Q_FOREACH (
const QPoint &pt, points) {
564 QPointF dstPt = func(pt);
571 resultRect.setCoords(std::floor(min(accX)), std::floor(min(accY)),
572 std::ceil(max(accX)), std::ceil(max(accY)));
582 const QLineF cutLine =
p.getLine();
584 points << rc.topLeft();
585 points << rc.topRight();
586 points << rc.bottomRight();
587 points << rc.bottomLeft();
589 QPointF
p1 = points[3];
590 bool p1Valid =
p.pos(
p1) >= 0;
594 for (
int i = 0; i < 4; i++) {
595 const QPointF
p2 = points[i];
596 const bool p2Valid =
p.pos(
p2) >= 0;
598 if (p1Valid != p2Valid) {
599 QPointF intersection;
600 cutLine.intersects(QLineF(
p1,
p2), &intersection);
601 resultPoints << intersection;
617 int numSolutions = 0;
619 const qreal
D =
pow2(b) - 4 * a * c;
620 const qreal
eps = 1e-14;
622 if (qAbs(
D) <=
eps) {
628 const qreal sqrt_D = std::sqrt(
D);
630 *x1 = (-b + sqrt_D) / (2 * a);
631 *x2 = (-b - sqrt_D) / (2 * a);
639 const QPointF ¢er2, qreal
r2)
643 const QPointF diff = (center2 - center1);
645 const QPointF c2 = diff;
647 const qreal centerDistance =
norm(diff);
649 if (centerDistance >
r1 +
r2)
return points;
650 if (centerDistance < qAbs(
r1 -
r2))
return points;
652 if (centerDistance < qAbs(
r1 -
r2) + 0.001) {
657 const qreal x_kp1 = diff.x();
658 const qreal y_kp1 = diff.y();
664 const qreal
eps = 1e-6;
666 if (qAbs(diff.y()) <
eps) {
667 qreal x = F2 / diff.x();
677 points << QPointF(x, y1);
678 }
else if (result == 2) {
684 if (
p.pos(
p1) >= 0) {
693 const qreal
A = diff.x() / diff.y();
694 const qreal
C = F2 / diff.y();
705 points << QPointF(x1,
C - x1 *
A);
706 }
else if (result == 2) {
709 QPointF
p1(x1,
C - x1 *
A);
710 QPointF
p2(x2,
C - x2 *
A);
712 if (
p.pos(
p1) >= 0) {
722 for (
int i = 0; i < points.size(); i++) {
723 points[i] = center1 + points[i];
732 QTransform(
rect.width(), 0, 0,
rect.height(),
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);
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;
765 return qAbs(
p1.x() -
p2.x()) < delta && qAbs(
p1.y() -
p2.y()) < delta;
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));
780 m << t.m11() , t.m12() , t.m13()
781 ,t.m21() , t.m22() , t.m23()
782 ,t.m31() , t.m32() , t.m33();
799 QTransform projMatrix;
801 if (t.m33() == 0.0 || t0.determinant() == 0.0) {
802 qWarning() <<
"Cannot decompose matrix!" << t;
807 if (t.type() == QTransform::TxProject) {
808 QTransform affineTransform(
813 projMatrix = affineTransform.inverted() * t;
816 proj[0] = projMatrix.m13();
817 proj[1] = projMatrix.m23();
818 proj[2] = projMatrix.m33();
822 std::array<Eigen::Vector3d, 3> rows;
826 rows[0] = Eigen::Vector3d(t.m11(), t.m12(), t.m13());
827 rows[1] = Eigen::Vector3d(t.m21(), t.m22(), t.m23());
828 rows[2] = Eigen::Vector3d(t.m31(), t.m32(), t.m33());
831 const qreal invM33 = 1.0 / t.m33();
833 for (
auto &row : rows) {
841 rows[2] = Eigen::Vector3d(0,0,1);
847 shearXY = rows[0].dot(rows[1]);
848 rows[1] = rows[1] -
shearXY * rows[0];
855 qreal determinant = rows[0].x() * rows[1].y() - rows[0].y() * rows[1].x();
856 if (determinant < 0) {
858 if (rows[0].x() < rows[1].y()) {
875 qreal sn = -rows[0].y();
876 qreal cs = rows[0].x();
877 qreal m11 = rows[0].x();
878 qreal m12 = rows[0].y();
879 qreal m21 = rows[1].x();
880 qreal m22 = rows[1].y();
881 rows[0][0] = (cs * m11 + sn * m21);
882 rows[0][1] = (cs * m12 + sn * m22);
883 rows[1][0] = (-sn * m11 + cs * m21);
884 rows[1][1] = (-sn * m12 + cs * m22);
888 rows[0].x(), rows[0].y(), rows[0].z(),
889 rows[1].x(), rows[1].y(), rows[1].z(),
890 rows[2].x(), rows[2].y(), rows[2].z());
900 Eigen::Matrix3d mat3 = mat2 - mat1;
913std::pair<QPointF, QTransform>
transformEllipse(
const QPointF &axes,
const QTransform &fullLocalToGlobal)
916 const QTransform localToGlobal =
925 const QTransform localEllipse = QTransform(1.0 /
pow2(axes.x()), 0.0, 0.0,
926 0.0, 1.0 /
pow2(axes.y()), 0.0,
930 const QTransform globalToLocal = localToGlobal.inverted();
932 Eigen::Matrix3d eqM =
935 globalToLocal.transposed());
939 Eigen::EigenSolver<Eigen::Matrix3d> eigenSolver(eqM);
941 const Eigen::Matrix3d T = eigenSolver.eigenvalues().real().asDiagonal();
942 const Eigen::Matrix3d U = eigenSolver.eigenvectors().real();
944 const Eigen::Matrix3d Ti = eigenSolver.eigenvalues().imag().asDiagonal();
945 const Eigen::Matrix3d
Ui = eigenSolver.eigenvectors().imag();
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));
963 const QTransform newLocalToGlobal = QTransform::fromScale(-1,-1) *
964 newGlobalToLocal.inverted() *
967 return std::make_pair(QPointF(newA, newB), newLocalToGlobal);
972 return QPointF((pt * zoom).toPoint()) / zoom;
975boost::optional<QPointF>
intersectLines(
const QLineF &boundedLine,
const QLineF &unboundedLine)
977 const QPointF
B1 = unboundedLine.p1();
978 const QPointF A1 = unboundedLine.p2() - unboundedLine.p1();
980 const QPointF
B2 = boundedLine.p1();
981 const QPointF A2 = boundedLine.p2() - boundedLine.p1();
983 Eigen::Matrix<float, 2, 2>
A;
984 A << A1.x(), -A2.x(),
987 Eigen::Matrix<float, 2, 1>
B;
988 B <<
B2.x() -
B1.x(),
995 Eigen::Matrix<float, 2, 1> T;
999 const qreal t2 = T(1,0);
1000 double epsilon = 1e-06;
1002 if (t2 < 0.0 || t2 > 1.0) {
1003 if (qAbs(t2) > epsilon && qAbs(t2 - 1.0) > epsilon) {
1008 return t2 * A2 +
B2;
1012 const QPointF &
q1,
const QPointF &
q2)
1024 const QPointF
p =
p2 -
p1;
1028 const qreal T = 0.5 * (-
pow2(b) +
pow2(a) + pSq);
1030 if (
p.isNull())
return result;
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;
1041 const qreal y = -
B2 /
A;
1042 const qreal x = (T - y *
p.y()) /
p.x();
1043 result <<
p1 + QPointF(x, y);
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);
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);
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;
1063 const qreal x = -
B2 /
A;
1064 const qreal y = (T - x *
p.x()) /
p.y();
1065 result <<
p1 + QPointF(x, y);
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);
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);
1085 boost::optional<QPointF> result;
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();
1096QPointF
moveElasticPoint(
const QPointF &pt,
const QPointF &base,
const QPointF &newBase,
const QPointF &wingA,
const QPointF &wingB)
1102 const QPointF vecL = base - pt;
1103 const QPointF vecLa = wingA - pt;
1104 const QPointF vecLb = wingB - pt;
1106 const qreal L =
norm(vecL);
1107 const qreal La =
norm(vecLa);
1108 const qreal Lb =
norm(vecLb);
1110 const qreal sinMuA =
crossProduct(vecLa, vecL) / (La * L);
1111 const qreal sinMuB =
crossProduct(vecL, vecLb) / (Lb * L);
1113 const qreal cosMuA =
dotProduct(vecLa, vecL) / (La * L);
1114 const qreal cosMuB =
dotProduct(vecLb, vecL) / (Lb * L);
1116 const qreal dL =
dotProduct(newBase - base, -vecL) / L;
1119 const qreal divB = (cosMuB + La / Lb * sinMuB * cosMuA / sinMuA) / L +
1120 (cosMuB + sinMuB * cosMuA / sinMuA) / Lb;
1121 const qreal dLb = dL / ( L * divB);
1123 const qreal divA = (cosMuA + Lb / La * sinMuA * cosMuB / sinMuB) / L +
1124 (cosMuA + sinMuA * cosMuB / sinMuB) / La;
1125 const qreal dLa = dL / ( L * divA);
1127 boost::optional<QPointF> result =
1130 const QPointF resultPoint = result ? *result : pt;
1137struct ElasticMotionData
1143 QPointF oldResultPoint;
1146double elasticMotionError(
const gsl_vector * x,
void *paramsPtr)
1152 const QPointF newResultPoint(gsl_vector_get(x, 0), gsl_vector_get(x, 1));
1154 const ElasticMotionData *
p =
static_cast<const ElasticMotionData*
>(paramsPtr);
1156 const QPointF vecL = newResultPoint -
p->newBasePos;
1157 const qreal L =
norm(vecL);
1159 const qreal deltaL = L -
kisDistance(
p->oldBasePos,
p->oldResultPoint);
1165 Q_FOREACH (
const QPointF &anchorPoint,
p->anchorPoints) {
1166 const QPointF vecLi = newResultPoint - anchorPoint;
1167 const qreal _Li =
norm(vecLi);
1170 deltaLi << _Li -
kisDistance(
p->oldResultPoint, anchorPoint);
1171 cosMuI <<
dotProduct(vecLi, vecL) / (_Li * L);
1175 qreal finalError = 0;
1177 qreal tangentialForceSum = 0;
1178 for (
int i = 0; i <
p->anchorPoints.size(); i++) {
1179 const qreal sum = deltaLi[i] * sinMuI[i] / Li[i];
1180 tangentialForceSum += sum;
1183 finalError +=
pow2(tangentialForceSum);
1185 qreal normalForceSum = 0;
1188 for (
int i = 0; i <
p->anchorPoints.size(); i++) {
1189 sum += deltaLi[i] * cosMuI[i] / Li[i];
1191 normalForceSum = (-deltaL) / L - sum;
1194 finalError +=
pow2(normalForceSum);
1203 const QPointF &base,
const QPointF &newBase,
1206 const QPointF offset = newBase - base;
1208 QPointF newResultPoint = pt + offset;
1212 ElasticMotionData data;
1213 data.newBasePos = newBase;
1214 data.oldBasePos = base;
1215 data.anchorPoints = anchorPoints;
1216 data.oldResultPoint = pt;
1218 const gsl_multimin_fminimizer_type *T =
1219 gsl_multimin_fminimizer_nmsimplex2;
1220 gsl_multimin_fminimizer *s = 0;
1222 gsl_multimin_function minex_func;
1229 x = gsl_vector_alloc (2);
1230 gsl_vector_set (x, 0, newResultPoint.x());
1231 gsl_vector_set (x, 1, newResultPoint.y());
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());
1240 minex_func.f = elasticMotionError;
1241 minex_func.params = (
void*)&data;
1243 s = gsl_multimin_fminimizer_alloc (T, 2);
1244 gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
1249 status = gsl_multimin_fminimizer_iterate(s);
1254 size = gsl_multimin_fminimizer_size (s);
1255 status = gsl_multimin_test_size (size, 1e-6);
1262 if (status == GSL_SUCCESS && elasticMotionError(s->x, &data) > 0.5) {
1263 status = GSL_CONTINUE;
1266 if (status == GSL_SUCCESS)
1274 newResultPoint.rx() = gsl_vector_get (s->x, 0);
1275 newResultPoint.ry() = gsl_vector_get (s->x, 1);
1278 while (status == GSL_CONTINUE && iter < 10000);
1280 if (status != GSL_SUCCESS) {
1285 gsl_vector_free(ss);
1286 gsl_multimin_fminimizer_free (s);
1289 return newResultPoint;
1324 return qFuzzyIsNull(whichSide) ? 0 : (whichSide > 0 ? 1 : -1);
1331 QPolygonF left = leftPolygon;
1332 QPolygonF right = rightPolygon;
1341 Q_FOREACH(QPointF point, left) {
1345 Q_FOREACH(QPointF point, right) {
1359 if (points.count() < 1) {
1361 result << line.p1() << line.p2() << line.p1();
1364 if (points.count() == 1) {
1368 result << line.p1();
1370 result << line.p2();
1371 result << line.p1();
1376 double maxDistance = 0;
1377 QPointF nextPoint = points[0];
1378 Q_FOREACH(QPointF point, points) {
1387 QLineF lineForLeft = QLineF(line.p1(), nextPoint);
1389 QLineF lineForRight = QLineF(nextPoint, line.p2());
1391 triangle << line.p1() << line.p2() << nextPoint << line.p1();
1392 Q_FOREACH(QPointF point, points) {
1393 if (triangle.containsPoint(point, Qt::WindingFill)) {
1414 if (polygon.count() < 4) {
1417 QPointF leftPoint = polygon[0];
1418 QPointF rightPoint = polygon[1];
1419 if (leftPoint.x() > rightPoint.x()) {
1420 leftPoint = polygon[1];
1421 rightPoint = polygon[0];
1424 Q_FOREACH(QPointF point, polygon) {
1425 if (point.x() < leftPoint.x()) {
1427 }
else if (point.x() > rightPoint.x()) {
1431 QLineF line(leftPoint, rightPoint);
1432 QLineF lineOther(rightPoint, leftPoint);
1436 Q_FOREACH(QPointF point, polygon) {
1437 qCritical() <<
"Checking point " << point <<
"and line" << line <<
": " <<
lineSideForPoint(line, point);
1445 qCritical() <<
"Using line: " << line <<
"for points: " << left;
1446 qCritical() <<
"Using line: " << lineOther <<
"for points: " << right;
1451 qCritical() <<
"Then the result: " << left << right;
1463 if (convexHull.count() == polygon.count()) {
1464 QLineF resultLine = line;
1490 const double phi = 0.618033988749894;
1493 qreal c = b - (b - a)*phi;
1494 qreal d = a + (b - a)*phi;
1496 while (qAbs(b - a) >
eps) {
1504 c = b - (b - a) * phi;
1505 d = a + (b - a) * phi;
1523 qreal l = qMin(xA, xB);
1524 qreal r = qMax(xA, xB);
1527 qreal m1 = l + (r - l)/3;
1528 qreal m2 = r - (r - l)/3;
1530 while ((r - l) >
eps) {
1560 const qreal cross = (line.dx() * (line.y1() - pt.y()) - line.dy() * (line.x1() - pt.x()));
1562 return cross * cross / (line.dx() * line.dx() + line.dy() * line.dy());
1566 const QPointF &startPoint,
1567 const QPointF &endPoint,
1569 qreal distanceThreshold,
1572 qreal
length = (endPoint - startPoint).manhattanLength();
1574 if (lastSegment ||
length > distanceThreshold) {
1576 qreal wrappedLength =
1577 (endPoint - QPointF(path.elementAt(0))).manhattanLength();
1579 if (
length < distanceThreshold ||
1580 wrappedLength < distanceThreshold) {
1592 if (
distance > distanceThreshold) {
1593 path.lineTo(endPoint);
1602 QPainterPath newPath;
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);
1612 case QPainterPath::MoveToElement:
1613 newPath.moveTo(endPoint);
1615 case QPainterPath::LineToElement:
1617 distance, lengthThreshold, i == count - 1)) {
1619 newPath.lineTo(endPoint);
1622 case QPainterPath::CurveToElement: {
1623 Q_ASSERT(i + 2 < count);
1626 distance, lengthThreshold, i == count - 1)) {
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);
1642 startPoint = endPoint;
1651 QPointF lineNormalVector = QPointF(line.dy(), -line.dx());
1655 if (line1.isNull() || line2.isNull()) {
1658 return {line1, line2};
1663 auto onTheLine = [](QPointF first, QPointF second) {
1665 return qAbs(first.x() - second.x()) < delta || qAbs(first.y() - second.y()) < delta;
1668 bool started =
false;
1671 path.moveTo(line.p1());
1672 path.lineTo(line.p2());
1675 int maxRectPointsNumber = 4;
1676 for(
int i = 0; i < maxRectPointsNumber; i++) {
1678 if (whichSide*(left ? 1 : -1) >= 0) {
1679 availablePoints << points[i];
1683 int startValue, increment;
1685 startValue = 2*availablePoints.length();
1692 auto stopFor = [](
int index,
int max,
bool left) {
1696 return index >= max;
1700 for (
int i = startValue; !stopFor(i, 2*availablePoints.length(), left); i += increment) {
1703 if (onTheLine(availablePoints[index], path.currentPosition())) {
1708 path.lineTo(availablePoints[index]);
1709 if (onTheLine(availablePoints[index], line.p1())) {
1715 if (onTheLine(availablePoints[index], line.p1())) {
1716 path.lineTo(availablePoints[index]);
1719 path.lineTo(availablePoints[index]);
1725 path.lineTo(line.p1());
1736 left.moveTo(leftLine.p1());
1737 left.lineTo(leftLine.p2());
1739 right.moveTo(rightLine.p1());
1740 right.lineTo(rightLine.p2());
1743 rectPoints <<
rect.topLeft() <<
rect.bottomLeft() <<
rect.bottomRight() <<
rect.topRight();
1749 paths << left << right;
1759 if (line.length() == 0) {
1763 QPointF lineVector = line.p2() - line.p1();
1764 QPointF lineNormalVector = QPointF(-lineVector.y(), lineVector.x());
1766 QLineF pointToLineNormalLine = QLineF(point, point + lineNormalVector);
1768 QLineF::IntersectionType intersectionType = pointToLineNormalLine.intersects(line, &result);
1769 if (unbounded || intersectionType == QLineF::IntersectionType::BoundedIntersection) {
1774 if (distance1 + distance2 <= line.length() || qFuzzyCompare(distance1 + distance2, line.length())) {
1777 if (distance1 < distance2) {
1785 QPointF response = point;
1786 QLineF line = QLineF(QPointF(0, 0), direction);
1787 return response +
distance*direction/line.length();
1792 QPointF response = point;
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));
1807 return QLineF(element1, element2);
1813 return QLineF(line.p2(), line.p1());
1817QPainterPath
removeGutterOneEndSmart(
const QPainterPath &shape1,
int index1,
const QPainterPath &shape2,
int index2, QLineF middleLine,
bool reverseFirstPoly,
bool reverseSecondPoly)
1821 auto reverseIfNeeded = [] (
const QLineF &line,
bool reverse) {
1834 int indexMultiplierFirst = reverseFirstPoly ? -1 : 1;
1835 int indexMultiplierSecond = reverseSecondPoly ? -1 : 1;
1837 leftLine = reverseIfNeeded(
getLineFromElements(shape1, index1 - indexMultiplierFirst*1), reverseFirstPoly);
1838 rightLine = reverseIfNeeded(
getLineFromElements(shape2, index2 + indexMultiplierSecond*1), reverseSecondPoly);
1840 qreal leftPointDistanceFromMiddle;
1841 qreal rightPointDistanceFromMiddle;
1843 QPointF leftPoint = line1.p1();
1844 QPointF rightPoint = line2.p2();
1849 QPainterPath simpleResult;
1850 simpleResult.moveTo(leftPoint);
1851 simpleResult.lineTo(rightPoint);
1853 auto makeTrianglePath = [leftPoint, rightPoint] (QPointF middle) {
1854 QPainterPath result;
1855 result.moveTo(leftPoint);
1856 result.lineTo(middle);
1857 result.lineTo(rightPoint);
1861 QPointF intersectionPoint;
1862 QLineF::IntersectionType intersectionType = leftLine.intersects(rightLine, &intersectionPoint);
1864 qreal distanceToMiddle = 0;
1865 if (intersectionType != QLineF::NoIntersection) {
1870 if (intersectionType == QLineF::NoIntersection || distanceToMiddle > qMax(leftPointDistanceFromMiddle, rightPointDistanceFromMiddle)) {
1873 boost::optional<QPointF> leftIntersection =
intersectLines(line2, leftLine);
1874 boost::optional<QPointF> rightIntersection =
intersectLines(line1, rightLine);
1876 if (leftIntersection.has_value()) {
1877 return makeTrianglePath(leftIntersection.value());
1878 }
else if (rightIntersection.has_value()) {
1879 return makeTrianglePath(rightIntersection.value());
1881 return simpleResult;
1886 QPainterPath betterResult = makeTrianglePath(intersectionPoint);
1888 return betterResult;
1903 result.append(shape1.
pointAt(0));
1906 start = qBound(0, start, shape.segmentsCount() - 1);
1907 end = qBound(0, end, shape.segmentsCount() - 1);
1909 for (
int i = start; i < end; i++) {
1912 res.append(segment[1]);
1917 int reversedIndex2 = reverseSecondPoly ? (shape2.
segmentsCount() - index2 - 1) : index2;
1920 int minIndex = qMin(index1, index2);
1921 int maxIndex = qMax(index1, index2);
1937 result1 << VPoint::moveTo(shape1.
segmentAt(minIndex)[1].endPoint);
1938 appendFrom(result1, minIndex + 1, maxIndex, shape1);
1941 result2 << VPoint::moveTo(shape1.
segmentAt(maxIndex)[1].endPoint);
1942 appendFrom(result2, maxIndex + 1, shape1.
segmentsCount(), shape1);
1943 appendFrom(result2, 0, minIndex, shape1);
1946 VectorPath minEnd = index1 > index2 ? oneEnd : otherEnd;
1947 VectorPath maxEnd = index1 > index2 ? otherEnd : oneEnd;
1964 appendFrom(result, 0, index1, shape1);
1966 appendFrom(result, reversedIndex2 + 1, reversedShape2.
segmentsCount(), reversedShape2);
1967 appendFrom(result, 0, reversedIndex2, reversedShape2);
1969 appendFrom(result, index1 + 1, shape1.
segmentsCount(), shape1);
1977QPainterPath
removeGutterSmart(
const QPainterPath &shape1,
int index1,
const QPainterPath &shape2,
int index2,
bool isSameShape)
1979 if (index1 + 1 >= shape1.elementCount() || index2 + 1 >= shape2.elementCount()) {
1980 return QPainterPath();
1988 bool reverseSecondPoly =
false;
1990 line2 = QLineF(line2.p2(), line2.p1());
1991 reverseSecondPoly =
true;
1994 QLineF middleLine = QLineF((line1.p1() + line2.p2())/2, (line1.p2() + line2.p1())/2);
1997 QPainterPath oneEnd =
removeGutterOneEndSmart(shape1, index1, shape2, index2, middleLine,
false, reverseSecondPoly);
1998 QPainterPath otherEnd =
removeGutterOneEndSmart(shape2, index2, shape1, index1, middleLine, reverseSecondPoly,
false);
2018 for (
int i = 0; i < path.segmentsCount(); i++) {
2020 if (points.count() < 2)
2026 for (
int i = 0; i < intersections.length(); i++) {
2028 QPointF
p =
KisBezierUtils::bezierCurve(points[0].endPoint, points[1].controlPoint1, points[1].controlPoint2, points[1].endPoint, intersections[i]);
2030 bool onLine =
isOnLine(line,
p,
eps,
true,
true,
true);
2032 indexes << path.segmentIndexToPathIndex(i);
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);
2055 for (
int i = 0; i < path.elementCount(); i++) {
2056 QPainterPath::Element el = path.elementAt(i);
2058 case QPainterPath::MoveToElement:
2059 m_points.append(VPoint(VPoint::MoveTo, el));
2061 case QPainterPath::LineToElement:
2062 m_points.append(VPoint(VPoint::LineTo, el));
2064 case QPainterPath::CurveToDataElement:
2067 case QPainterPath::CurveToElement:
2072 m_points.append(VPoint(VPoint::BezierTo, path.elementAt(i + 2), el, path.elementAt(i + 1)));
2112 if (segment.count() == 2) {
2115 return std::nullopt;
2121 if (points.length() < 1) {
2124 return QLineF(points[0].endPoint, points[1].endPoint);
2132 QPointF intersection;
2133 if (segLine.intersects(line, &intersection) == QLineF::BoundedIntersection) {
2143 for (
int i = 0; i < intersections.count(); i++) {
2145 bool onLine =
isOnLine(line,
p,
eps,
true,
true,
true);
2165 for (
int i = 1; i <
m_points.length(); i++) {
2166 if (index < pathIndex) {
2170 if (index == pathIndex) {
2185 for (
int i = 1; i <
m_points.length(); i++) {
2186 if (i - 1 == index) {
2188 return pathIndex - 2;
2198 return pathIndex - 2;
2215 VPoint lineBeginPoint = VPoint(VPoint::MoveTo, QPointF(0, 0));
2216 VPoint previousPoint =
m_points[0];
2220 auto onTheLine = [
eps] (QPointF start, QPointF middle, QPointF end) {
2221 QLineF line1(start, end);
2222 QLineF line2(start, middle);
2224 return (qAbs(
crossProduct(end - start, middle - start)/line1.length()/line2.length()) <
eps);
2227 for (
int i = 1; i <
m_points.length(); i++) {
2228 if (
m_points[i].type == VPoint::MoveTo) {
2229 if (previousPoint.type == VPoint::MoveTo) {
2233 response << previousPoint;
2237 }
else if (
m_points[i].type == VPoint::LineTo) {
2238 if (previousPoint.type == VPoint::LineTo) {
2240 if (!onTheLine(lineBeginPoint.endPoint, previousPoint.endPoint,
m_points[i].endPoint)) {
2241 response << previousPoint;
2242 lineBeginPoint = previousPoint;
2247 response << previousPoint;
2248 lineBeginPoint = previousPoint;
2253 response << previousPoint;
2258 response << previousPoint;
2263 if (response.length() < 3) {
2267 VPoint start = response[0];
2268 VPoint second = response[1];
2270 if (start.type == VPoint::MoveTo) {
2273 if (second.type == VPoint::LineTo && onTheLine(response[response.length()-2].endPoint, start.endPoint, second.endPoint)) {
2274 response.pop_front();
2275 response[0].type = VPoint::MoveTo;
2276 response[response.length() - 1].endPoint = second.endPoint;
2280 }
else if (response[0].type == VPoint::LineTo) {
2283 if (second.type == VPoint::LineTo && onTheLine(previousPoint.endPoint, QPointF(0, 0), start.endPoint)) {
2285 qCritical() <<
"We're in this case";
2303 response.append(VPoint(VPoint::MoveTo,
m_points[
m_points.length() - 1].endPoint));
2304 for (
int i =
m_points.length() - 1; i > 0; i--) {
2307 point.endPoint =
m_points[i - 1].endPoint;
2308 response.append(point);
2323 auto comparePoints = [fuzzy,
eps] (QPointF left, QPointF right) {
2332 VPoint leftStart = path.pointAt(0);
2334 for (
int i = 0; i < path.pointsCount(); i++) {
2335 if (comparePoints(leftStart.endPoint, path.pointAt(i).endPoint)) {
2336 rightStartingPoints << i;
2340 Q_FOREACH(
int startId, rightStartingPoints) {
2341 bool isEqual =
true;
2344 if (!comparePoints(
pointAt(i).endPoint, path.pointAt(rightId).
endPoint)) {
2354 if (!comparePoints(
pointAt(i).endPoint, path.pointAt(rightId).
endPoint)) {
2373 QPainterPath response;
2376 case VPoint::MoveTo:
2379 case VPoint::LineTo:
2382 case VPoint::BezierTo:
2387 response.closeSubpath();
2393 debug << path.asPainterPath();
2399 bool autoInsertSpaces = debug.autoInsertSpaces();
2400 debug.setAutoInsertSpaces(
false);
2411 debug.setAutoInsertSpaces(autoInsertSpaces);
2417 if (path.pointsCount() == 0) {
2424 bool isPolygon =
true;
2426 for (
int i = 0; i < path.segmentsCount(); i++) {
2438 if (!boundRect.contains(point)) {
2443 return path.asPainterPath().toFillPolygon().containsPoint(point, Qt::WindingFill);
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());
2458 int intersectionsCount = 0;
2460 for (
int k = 0; k < lines.length(); k++) {
2461 int intersections = 0;
2462 QLineF line = lines[k];
2463 bool checkNext =
false;
2465 for (
int i = 0; i < path.segmentsCount(); i++) {
2466 std::optional<VectorPath::Segment> segmentOpt = path.segmentAtAsSegment(i);
2473 for (
int i = 0; i < intersectionsHere.count(); i++) {
2484 intersections += intersectionsHere.count();
2487 intersectionsCount = intersections;
2493 return intersectionsCount%2 == 1;
2498 for (
int i = 0; i < path.elementCount(); i++) {
2499 if (path.elementAt(i).type == QPainterPath::CurveToDataElement) {
2505 return path.toFillPolygon().containsPoint(point, Qt::WindingFill);
2508bool isOnLine(
const QLineF &line,
const QPointF &point,
const qreal
eps,
bool boundedStart,
bool boundedEnd,
bool includeEnds)
2511 return includeEnds || !boundedStart;
2514 return includeEnds || !boundedEnd;
2521 QPointF lineVector = line.p2() - line.p1();
2522 QPointF pointVector = point - line.p1();
2524 if (qAbs(pointVector.x()) <
eps) {
2526 t = pointVector.y()/lineVector.y();
2528 t = pointVector.x()/lineVector.x();
2532 return !boundedStart;
qreal length(const QPointF &vec)
qreal distance(const QPointF &p1, const QPointF &p2)
qreal D(qreal t, const QPointF &P0, const QPointF &P1, const QPointF &P2, const QPointF &P3, const QPointF &p)
QList< VectorPathPoint > segmentAt(int i) const
VectorPath reversed() const
VectorPath(const QPainterPath &path)
int pathIndexToSegmentIndex(int index)
static QList< QPointF > intersectSegmentWithLineBounded(const QLineF &line, const Segment &segment)
QPainterPath asPainterPath() const
bool fuzzyComparePointsCyclic(const VectorPath &path, qreal eps=0.0f) const
QList< VectorPathPoint > m_points
std::optional< Segment > segmentAtAsSegment(int i) const
VectorPathPoint pointAt(int i) const
VectorPath trulySimplified(qreal epsDegrees=0.5) const
int segmentIndexToPathIndex(int index)
QPainterPath m_originalPath
int segmentsCount() const
QLineF segmentAtAsLine(int i) const
static bool qFuzzyCompare(half p1, half p2)
static bool qFuzzyIsNull(half h)
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_BREAK(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_ASSERT_RECOVER_NOOP(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
T kisGrowRect(const T &rect, U offset)
T kisRadiansToDegrees(T radians)
qreal kisSquareDistanceToLine(const QPointF &m, const QLineF &line)
T kisDegreesToRadians(T degrees)
qreal kisDistanceToLine(const QPointF &m, const QLineF &line)
QPainterPath removeGutterOneEndSmart(const QPainterPath &shape1, int index1, const QPainterPath &shape2, int index2, QLineF middleLine, bool reverseFirstPoly, bool reverseSecondPoly)
boost::optional< QPointF > findTrianglePointNearest(const QPointF &p1, const QPointF &p2, qreal a, qreal b, const QPointF &nearest)
QVector< QPointF > intersectTwoCircles(const QPointF ¢er1, qreal r1, const QPointF ¢er2, qreal r2)
QLineF getLineFromElements(const QPainterPath &shape, int index)
bool tryMergePoints(QPainterPath &path, const QPointF &startPoint, const QPointF &endPoint, qreal &distance, qreal distanceThreshold, bool lastSegment)
Point ensureInRectImpl(Point pt, const Rect &bounds)
QTransform toQTransformStraight(const Eigen::Matrix3d &m)
QVector< Point > sampleRectWithPoints(const Rect &rect)
QPointF movePointInTheDirection(const QPointF &point, const QPointF &direction, qreal distance)
movePointAlongTheLine moves the point a particular distance in the specified direction
T wrapValue(T value, T wrapBounds)
QRect approximateRectFromPoints(const QVector< QPoint > &points)
QList< QPainterPath > getPathsFromRectangleCutThrough(const QRectF &rect, const QLineF &leftLine, const QLineF &rightLine)
getPathsFromRectangleCutThrough get paths defining both sides of a rectangle cut through using two (s...
void adjustIfOnPolygonBoundary(const QPolygonF &poly, int polygonDirection, QPointF *pt)
QPainterPath simplifyShape(const QPainterPath &path)
VectorPath mergeShapesWithGutter(const VectorPath &shape1, const VectorPath &shape2, const VectorPath &oneEnd, const VectorPath &otherEnd, int index1, int index2, bool reverseSecondPoly, bool isSameShape)
mergeShapesWithGutter merges two shapes with a gutter shape (defined as two paths)
std::pair< QPointF, QTransform > transformEllipse(const QPointF &axes, const QTransform &fullLocalToGlobal)
qreal findMinimumGoldenSection(std::function< qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter=100)
QPolygonF calculateConvexHull(const QPolygonF &polygon)
calculateConvexHull Calculate the convex hull of the polygon using the QuickHull
bool fuzzyMatrixCompare(const QTransform &t1, const QTransform &t2, qreal delta)
void accumulateBounds(const Point &pt, Rect *bounds)
qreal directionBetweenPoints(const QPointF &p1, const QPointF &p2, qreal defaultAngle)
void cropLineToConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
QTransform mapToRectInverse(const QRectF &rect)
QPointF moveElasticPoint(const QPointF &pt, const QPointF &base, const QPointF &newBase, const QPointF &wingA, const QPointF &wingB)
moveElasticPoint moves point pt based on the model of elasticity
QPainterPath trySimplifyPath(const QPainterPath &path, qreal lengthThreshold)
trySimplifyPath Tries to simplify a QPainterPath
qreal angleBetweenVectors(const QPointF &v1, const QPointF &v2)
qreal pointToLineDistSquared(const QPointF &pt, const QLineF &line)
QPoint ensureInRect(QPoint pt, const QRect &bounds)
int quadraticEquation(qreal a, qreal b, qreal c, qreal *x1, qreal *x2)
QPointF alignForZoom(const QPointF &pt, qreal zoom)
QLineF reverseDirection(const QLineF &line)
void cropLineToRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond)
Crop line to rect; if it doesn't intersect, just return an empty line (QLineF()).
QPainterPath removeGutterSmart(const QPainterPath &shape1, int index1, const QPainterPath &shape2, int index2, bool isSameShape)
removeGutterSmart
bool isInRange(T x, T a, T b)
QDebug operator<<(QDebug debug, const VectorPath &path)
QList< QLineF > intersectLineConcavePolygon(const QPolygonF polygon, const QLineF &line, bool extendFirst, bool extendSecond)
QPainterPath smallArrow()
QPointF movePointAlongTheLine(const QPointF &point, const QLineF &direction, qreal distance, bool ensureOnLine)
movePointAlongTheLine moves the point a particular distance in the specified direction
QPolygonF combineConvexHullParts(const QPolygonF &leftPolygon, QPolygonF &rightPolygon, bool triangular)
QVector< QPointF > findTrianglePoint(const QPointF &p1, const QPointF &p2, qreal a, qreal b)
QPointF findNearestPointOnLine(const QPointF &point, const QLineF &line, bool unbounded)
bool isInsideShape(const VectorPath &path, const QPointF &point)
PointTypeTraits< T >::value_type dotProduct(const T &a, const T &b)
QRectF cutOffRect(const QRectF &rc, const KisAlgebra2D::RightHalfPlane &p)
QPolygonF calculateConvexHullFromPointsOverTheLine(const QPolygonF &points, const QLineF &line)
qreal findMinimumTernarySection(std::function< qreal(qreal)> f, qreal xA, qreal xB, qreal eps, int maxIter=100)
boost::optional< QPointF > intersectLines(const QLineF &boundedLine, const QLineF &unboundedLine)
PointTypeTraits< T >::value_type crossProduct(const T &a, const T &b)
QPointF transformAsBase(const QPointF &pt, const QPointF &base1, const QPointF &base2)
int polygonDirection(const QVector< T > &polygon)
T inwardUnitNormal(const T &a, int polygonDirection)
int lineSideForPoint(const QLineF &line, const QPointF &point)
Rect approximateRectFromPointsImpl(const QVector< Point > &points)
Eigen::Matrix3d fromQTransformStraight(const QTransform &t)
bool intersectLineConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
bool isOnLine(const QLineF &line, const QPointF &point, const qreal eps, bool boundedStart, bool boundedEnd, bool includeEnds)
QList< QLineF > getParallelLines(const QLineF &line, const qreal distance)
QRect approximateRectWithPointTransform(const QRect &rect, std::function< QPointF(QPointF)> func)
QList< int > getLineSegmentCrossingLineIndexes(const QLineF &line, const QPainterPath &shape)
bool fuzzyPointCompare(const QPointF &p1, const QPointF &p2)
bool intersectLineRect(QLineF &line, const QRect rect, bool extend)
QTransform mapToRect(const QRectF &rect)
QPainterPath getOnePathFromRectangleCutThrough(const QList< QPointF > &points, const QLineF &line, bool left)
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)
const unsigned char salt[256][256]
QTransform shearTransform() const
QTransform rotateTransform() const
QTransform scaleTransform() const
QTransform translateTransform() const
VectorPathPoint::Type type
static VectorPathPoint moveTo(QPointF _endPoint)
static VectorPathPoint lineTo(QPointF _endPoint)