9#include <klocalizedstring.h>
12#include <QPainterPath>
13#include <QLinearGradient>
19#include <Eigen/Eigenvalues>
125 finalVertices << QPointF(-1, 0) << QPointF(1, 0) << QPointF(0, 1);
130 QTransform transform;
138 if (!QTransform::squareToQuad(
polygon, transform)) {
153 QPointF ptR =
originalTransform.map(QPointF(0.5 - 1/(2*sqrt(2)), 0.5 - 1/(2*sqrt(2))));
160 Eigen::MatrixXd
A(5, 5);
161 A << ptR.x() * ptR.y(), ptR.y() * ptR.y(), ptR.x(), ptR.y(), 1.0,
162 pt1.x() * pt1.y(), pt1.y() * pt1.y(), pt1.x(), pt1.y(), 1.0,
163 pt2.x() * pt2.y(), pt2.y() * pt2.y(), pt2.x(), pt2.y(), 1.0,
164 pt3.x() * pt3.y(), pt3.y() * pt3.y(), pt3.x(), pt3.y(), 1.0,
165 pt4.x() * pt4.y(), pt4.y() * pt4.y(), pt4.x(), pt4.y(), 1.0;
167 Eigen::VectorXd bVector(5);
168 bVector << - ptR.x() * ptR.x(), - pt1.x() * pt1.x(), - pt2.x() * pt2.x(), - pt3.x() * pt3.x(), - pt4.x() * pt4.x();
170 Eigen::VectorXd xSolution =
A.fullPivLu().solve(bVector);
176 qreal b = xSolution(0);
178 qreal c = xSolution(1);
179 qreal d = xSolution(2);
181 qreal e = xSolution(3);
182 qreal f = xSolution(4);
194 finalEllipseCenter << ((double)b*e - 2*c*d)/(4*c - b*b) << ((
double)b*d - 2*e)/(4*c - b*b);
203 qreal aprim = K*K*a - K*L*b + L*L*c;
204 qreal bprim = 2*K*L*a + K*K*b - L*L*b - 2*K*L*c;
205 qreal cprim = L*L*a + K*L*b + K*K*c;
206 qreal dprim = K*d - L*e;
207 qreal eprim = L*d + K*e;
226 qreal rx = sqrt(qPow(rerotatedCenter.x(), 2) + qPow(rerotatedCenter.y(), 2)*cprim/aprim - fprim/aprim);
227 qreal ry = sqrt(rx*rx*aprim/cprim);
234 qreal cprim_recreated = (rx*rx)/(ry*ry);
235 qreal dprim_recreated = -2*rerotatedCenter.x();
236 qreal eprim_recreated = -2*rerotatedCenter.y()*(rx*rx)/(ry*ry);
237 qreal fprim_recreated = qPow(rerotatedCenter.x(), 2) + qPow(rerotatedCenter.y(), 2)*(rx*rx)/(ry*ry) - (rx*rx);
239 if (debug) qCritical() <<
"recreated equation (with 1): " << 1 << 0 << cprim_recreated << dprim_recreated << eprim_recreated << fprim_recreated;
240 if (debug) qCritical() <<
"recreated equation: (actual)" << aprim << 0 << aprim*cprim_recreated << aprim*dprim_recreated << aprim*eprim_recreated << aprim*fprim_recreated;
243 auto fuzzyCompareWithEps = [
eps] (qreal a, qreal b) {
return abs(a - b) <
eps; };
252 auto convertToPreviousCoordsSystem = [K, L] (QPointF
p) {
return QPointF(K*
p.x() + L*
p.y(), K*
p.y() - L*
p.x()); };
256 QPointF leftVertexRerotated = rerotatedCenter + QPointF(-rx, 0);
257 QPointF rightVertedRerotated = rerotatedCenter + QPointF(rx, 0);
258 QPointF topVertedRerotated = rerotatedCenter + QPointF(0, ry);
260 QPointF leftVertexFinal = convertToPreviousCoordsSystem(leftVertexRerotated);
261 QPointF rightVertexFinal = convertToPreviousCoordsSystem(rightVertedRerotated);
262 QPointF topVertexFinal = convertToPreviousCoordsSystem(topVertedRerotated);
265 result << leftVertexFinal << rightVertexFinal << topVertexFinal;
283 return (b*b - 4*a*c) < 0;
288 if (formula.size() != 6) {
290 formula << a << b << c << d << e << f;
303 if (point.size() != 2) {
355 Q_UNUSED(strokeBegin);
358 d->ellipseInPolygon.setSimpleEllipseVertices(
d->simpleEllipse);
360 return d->simpleEllipse.project(pt);
365 return project(pt, strokeBegin);
371 strokeBegin = QPointF();
379 bool isEditing =
false;
390 QPolygonF poly =
d->ellipseInPolygon.polygon;
391 QTransform
transform =
d->ellipseInPolygon.originalTransform;
396 if (
d->cache.vanishingPoint1) {
397 drawX(gc, initialTransform.map(
d->cache.vanishingPoint1.get()));
399 if (
d->cache.vanishingPoint2) {
400 drawX(gc, initialTransform.map(
d->cache.vanishingPoint2.get()));
405 if (
isEllipseValid() && (assistantVisible || previewVisible || isEditing)) {
407 gc.setTransform(initialTransform);
408 gc.setTransform(
d->simpleEllipse.getTransform().inverted(),
true);
411 path.addEllipse(QPointF(0.0, 0.0),
d->simpleEllipse.semiMajor(),
d->simpleEllipse.semiMinor());
413 if (assistantVisible || isEditing) {
421 axes.moveTo(QPointF(-
d->simpleEllipse.semiMajor(), 0));
422 axes.lineTo(QPointF(
d->simpleEllipse.semiMajor(), 0));
424 axes.moveTo(QPointF(0, -
d->simpleEllipse.semiMinor()));
425 axes.lineTo(QPointF(0,
d->simpleEllipse.semiMinor()));
431 p.setStyle(Qt::DotLine);
434 color.setAlpha(color.alpha()*0.2);
446 gc.setTransform(
d->ellipseInPolygon.originalTransform,
true);
449 QPointF pt1 = QPointF(0.5, 1.0);
450 QPointF pt2 = QPointF(1.0, 0.5);
451 QPointF pt3 = QPointF(0.5, 0.0);
452 QPointF pt4 = QPointF(0.0, 0.5);
454 QPainterPath touchingLine;
456 touchingLine.moveTo(pt1);
457 touchingLine.lineTo(pt3);
459 touchingLine.moveTo(pt2);
460 touchingLine.lineTo(pt4);
462 if (assistantVisible) {
470 if (assistantVisible || isEditing) {
475 QPolygonF polyAllConnected;
478 path.addPolygon(polyAllConnected);
482 path.addPolygon(poly);
486 gc.setPen(QColor(0, 0, 0, 125));
489 for (
int y = 0; y <= 1; ++y)
491 QLineF line = QLineF(QPointF(0.0, y), QPointF(1.0, y));
493 path.moveTo(line.p1());
494 path.lineTo(line.p2());
496 for (
int x = 0; x <= 1; ++x)
498 QLineF line = QLineF(QPointF(x, 0.0), QPointF(x, 1.0));
500 path.moveTo(line.p1());
501 path.lineTo(line.p2());
518 Q_UNUSED(assistantVisible);
527 if (
d->ellipseInPolygon.setSimpleEllipseVertices(
d->simpleEllipse)) {
528 return d->simpleEllipse.boundingRect().adjusted(-2, -2, 2, 2).toAlignedRect();
536 QPointF centroid(0, 0);
537 for (
int i = 0; i < 4; ++i) {
541 return centroid * 0.25;
552 d->cacheValid =
false;
554 if (
d->cachedPoints.size() ==
handles().size()) {
555 for (
int i = 0; i <
handles().size(); ++i) {
556 if (
d->cachedPoints[i] != *
handles()[i])
break;
557 if (i ==
handles().size() - 1) {
559 d->cacheValid =
true;
566 for (
int i = 0; i <
handles().size(); ++i) {
571 QPolygonF poly = QPolygonF(
d->cachedPoints);
575 poly = QPolygonF(
d->cachedPoints);
576 poly <<
d->cachedPoints[0];
577 d->ellipseInPolygon.updateToPolygon(poly);
578 d->cacheValid =
true;
582 d->ellipseInPolygon.updateToPolygon(poly);
583 if (
d->ellipseInPolygon.isValid()) {
584 d->ellipseInPolygon.setSimpleEllipseVertices(
d->simpleEllipse);
588 d->cacheValid =
true;
602 return poly.containsPoint(point, Qt::OddEvenFill);
626 return "perspective ellipse";
631 return i18n(
"Perspective Ellipse");
QVector< QPointF > finalVertices
static bool formulaRepresentsAnEllipse(double a, double b, double c)
formulaRepresentsAnEllipse parameters are first three coefficients from a formula: ax^2 + bxy + cy^2 ...
QVector< double > finalEllipseCenter
bool updateToPolygon(QVector< QPointF > _polygon)
updateToPolygon This function makes all the necessary calculations and updates all data,...
double finalAxisReverseAngleSin
void setFormula(QVector< double > &formula, double a, double b, double c, double d, double e, double f)
double finalAxisReverseAngleCos
QVector< QPointF > polygon
void setPoint(QVector< double > &point, double x, double y)
QVector< double > rerotatedFormula
QVector< double > finalFormula
QTransform originalTransform
bool setSimpleEllipseVertices(Ellipse &ellipse) const
setSimpleEllipseVertices sets vertices of this ellipse to the "simple ellipse" class to be drawn and ...
bool set(const QPointF &m1, const QPointF &m2, const QPointF &p)
KisPaintingAssistantsDecorationSP paintingAssistantsDecoration() const
QTransform documentToWidgetTransform() const
virtual QRect boundingRect() const
void drawX(QPainter &painter, const QPointF &pt)
QPointF effectiveBrushPosition(const KisCoordinatesConverter *converter, KisCanvas2 *canvas) const
Query the effective brush position to be used for preview lines. This is intended to be used for pain...
void drawError(QPainter &painter, const QPainterPath &path)
bool isSnappingActive() const
void drawPath(QPainter &painter, const QPainterPath &path, bool drawActive=true)
void drawPreview(QPainter &painter, const QPainterPath &path)
virtual void transform(const QTransform &transform)
QColor effectiveAssistantColor() const
virtual void drawAssistant(QPainter &gc, const QRectF &updateRect, const KisCoordinatesConverter *converter, bool cached, KisCanvas2 *canvas=0, bool assistantVisible=true, bool previewVisible=true)
const QList< KisPaintingAssistantHandleSP > & handles() const
bool isEditingAssistants()
static void updateCacheData(CacheData &cache, const QPolygonF &poly)
static qreal distanceInGrid(const QList< KisPaintingAssistantHandleSP > &handles, bool isAssistantComplete, const QPointF &point)
static bool getTetragon(const QList< KisPaintingAssistantHandleSP > &handles, bool isAssistantComplete, QPolygonF &outPolygon)
QString id() const override
QString name() const override
PerspectiveEllipseAssistantFactory()
~PerspectiveEllipseAssistantFactory() override
KisPaintingAssistant * createPaintingAssistant() const override
PerspectiveBasedAssistantHelper::CacheData cache
QVector< QPointF > cachedPoints
EllipseInPolygon ellipseInPolygon
void adjustLine(QPointF &point, QPointF &strokeBegin) override
QScopedPointer< Private > d
QPointF adjustPosition(const QPointF &point, const QPointF &strokeBegin, const bool snapToAny, qreal moveThresholdPt) override
QRect boundingRect() const override
bool contains(const QPointF &point) const override
PerspectiveEllipseAssistant(QObject *parent=0)
void drawCache(QPainter &gc, const KisCoordinatesConverter *converter, bool assistantVisible=true) override
performance layer where the graphics can be drawn from a cache instead of generated every render upda...
QPointF getDefaultEditorPosition() const override
QPointF project(const QPointF &pt, const QPointF &strokeBegin)
KisPaintingAssistantSP clone(QMap< KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP > &handleMap) const override
bool isAssistantComplete() const override
void drawAssistant(QPainter &gc, const QRectF &updateRect, const KisCoordinatesConverter *converter, bool cached, KisCanvas2 *canvas, bool assistantVisible=true, bool previewVisible=true) override
qreal distance(const QPointF &point) const override
~PerspectiveEllipseAssistant()
bool isActive() const override
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
QSharedPointer< KisPaintingAssistant > KisPaintingAssistantSP
void cropLineToRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond)