12#include <klocalizedstring.h>
15#include <QPainterPath>
16#include <QLinearGradient>
38 , m_subdivisions(rhs.m_subdivisions)
39 , m_snapLine(rhs.m_snapLine)
40 , m_cachedTransform(rhs.m_cachedTransform)
41 , m_cachedPolygon(rhs.m_cachedPolygon)
42 , m_cacheValid(rhs.m_cacheValid)
43 , m_cache(rhs.m_cache)
45 for (
int i = 0; i < 4; ++i) {
57 const static QPointF nullPoint(std::numeric_limits<qreal>::quiet_NaN(), std::numeric_limits<qreal>::quiet_NaN());
61 if (snapToAnyDirection ||
m_snapLine.isNull()) {
69 if (!poly.containsPoint(strokeBegin, Qt::OddEvenFill)) {
79 const QTransform inverse =
transform.inverted(&invertible);
86 const QPointF start = inverse.map(strokeBegin);
87 const QLineF verticalLine = QLineF(strokeBegin,
transform.map(start + QPointF(0, 1)));
88 const QLineF horizontalLine = QLineF(strokeBegin,
transform.map(start + QPointF(1, 0)));
100 invsqrlen = 1.0 / (dx2 + dy2);
110 return project(pt, strokeBegin, snapToAny, moveThresholdPt);
115 point =
project(point, strokeBegin,
true, 0.0);
128 return poly.containsPoint(pt, Qt::OddEvenFill);
163 QPointF intersection(0, 0);
166 QRect viewport= gc.viewport();
170 QPointF intersectTransformed(0, 0);
172 if (poly.containsPoint(initialTransform.inverted().map(mousePos), Qt::OddEvenFill)==
true){
174 if (fmod(QLineF(poly[0], poly[1]).angle(), 180.0)>=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)+2.0 || fmod(QLineF(poly[0], poly[1]).angle(), 180.0)<=fmod(QLineF(poly[2], poly[3]).angle(), 180.0)-2.0) {
175 if (QLineF(poly[0], poly[1]).intersects(QLineF(poly[2], poly[3]), &intersection) != QLineF::NoIntersection) {
176 intersectTransformed = initialTransform.map(intersection);
177 snapLine = QLineF(intersectTransformed, mousePos);
179 bounds= QRect(snapLine.p1().toPoint(), snapLine.p2().toPoint());
181 if (
bounds.contains(intersectTransformed.toPoint())){
182 path2.moveTo(intersectTransformed);
183 path2.lineTo(snapLine.p2());
186 path2.moveTo(snapLine.p1());
187 path2.lineTo(snapLine.p2());
191 if (fmod(QLineF(poly[1], poly[2]).angle(), 180.0)>=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)+2.0 || fmod(QLineF(poly[1], poly[2]).angle(), 180.0)<=fmod(QLineF(poly[3], poly[0]).angle(), 180.0)-2.0){
192 if (QLineF(poly[1], poly[2]).intersects(QLineF(poly[3], poly[0]), &intersection) != QLineF::NoIntersection) {
193 intersectTransformed = initialTransform.map(intersection);
194 snapLine = QLineF(intersectTransformed, mousePos);
196 bounds= QRect(snapLine.p1().toPoint(), snapLine.p2().toPoint());
199 if (
bounds.contains(intersectTransformed.toPoint())){
200 path2.moveTo(intersectTransformed);
201 path2.lineTo(snapLine.p2());
204 path2.moveTo(snapLine.p1());
205 path2.lineTo(snapLine.p2());
218 if (assistantVisible) {
229 path.addPolygon(poly);
233 gc.setPen(QColor(0, 0, 0, 125));
240 QLineF line = QLineF(QPointF(0.0, y * step), QPointF(1.0, y * step));
242 path.moveTo(line.p1());
243 path.lineTo(line.p2());
247 QLineF line = QLineF(QPointF(x * step, 0.0), QPointF(x * step, 1.0));
249 path.moveTo(line.p1());
250 path.lineTo(line.p2());
268 Q_UNUSED(assistantVisible);
273 QPointF centroid(0, 0);
274 for (
int i = 0; i < 4; ++i) {
278 return centroid * 0.25;
284 for (
int i = 0; i <= 4; ++i) {
302 if (!QTransform::squareToQuad(poly,
transform)) {
303 qWarning(
"Failed to create perspective mapping");
307 for (
int i = 0; i < 4; ++i) {
334 xml->writeStartElement(
"subdivisions");
336 xml->writeEndElement();
341 if (xml && xml->name() ==
"subdivisions") {
359 return "perspective";
364 return i18n(
"Perspective");
QTransform documentToWidgetTransform() 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)
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
QString id() const override
KisPaintingAssistant * createPaintingAssistant() const override
QString name() const override
~PerspectiveAssistantFactory() override
PerspectiveAssistantFactory()
bool getTransform(QPolygonF &polyOut, QTransform &transformOut) const
void drawAssistant(QPainter &gc, const QRectF &updateRect, const KisCoordinatesConverter *converter, bool cached=true, KisCanvas2 *canvas=0, bool assistantVisible=true, bool previewVisible=true) override
QTransform m_cachedTransform
PerspectiveAssistant(QObject *parent=0)
bool isAssistantComplete() const override
void endStroke() override
QPointF project(const QPointF &pt, const QPointF &strokeBegin, const bool snapToAnyDirection, qreal moveThresholdPt)
QPointF getDefaultEditorPosition() const override
void saveCustomXml(QXmlStreamWriter *xml) override
QPolygonF m_cachedPolygon
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...
bool contains(const QPointF &point) const override
KisPaintingAssistantSP clone(QMap< KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP > &handleMap) const override
qreal distance(const QPointF &point) const override
QPointF m_cachedPoints[4]
PerspectiveBasedAssistantHelper::CacheData m_cache
bool isActive() const override
void adjustLine(QPointF &point, QPointF &strokeBegin) override
bool loadCustomXml(QXmlStreamReader *xml) override
void setSubdivisions(int subdivisions)
QPointF adjustPosition(const QPointF &point, const QPointF &strokeBegin, const bool snapToAny, qreal moveThresholdPt) override
boost::optional< QPointF > vanishingPoint1
boost::optional< QPointF > vanishingPoint2
static QPolygonF getAllConnectedTetragon(const QList< KisPaintingAssistantHandleSP > &handles)
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)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
QSharedPointer< KisPaintingAssistant > KisPaintingAssistantSP
qreal pointToLineDistSquared(const QPointF &pt, const QLineF &line)
void cropLineToRect(QLineF &line, const QRect rect, bool extendFirst, bool extendSecond)
bool intersectLineRect(QLineF &line, const QRect rect, bool extend)
int toInt(const QString &str, bool *ok=nullptr)
QString toString(const QString &value)