Krita Source Code Documentation
Loading...
Searching...
No Matches
VanishingPointAssistant.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2008 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2010 Geoffry Song <goffrie@gmail.com>
4 * SPDX-FileCopyrightText: 2014 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
5 * SPDX-FileCopyrightText: 2017 Scott Petrovic <scottpetrovic@gmail.com>
6 *
7 * SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
11
12#include "kis_debug.h"
13#include <klocalizedstring.h>
14
15#include <QPainter>
16#include <QPainterPath>
17#include <QLinearGradient>
18#include <QTransform>
19
20#include <kis_canvas2.h>
22#include <kis_algebra_2d.h>
23#include <kis_dom_utils.h>
24#include <math.h>
25
27 : KisPaintingAssistant("vanishing point", i18n("Vanishing Point assistant"))
28{
29}
30
31VanishingPointAssistant::VanishingPointAssistant(const VanishingPointAssistant &rhs, QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap)
32 : KisPaintingAssistant(rhs, handleMap)
33 , m_canvas(rhs.m_canvas)
34 , m_referenceLineDensity(rhs.m_referenceLineDensity)
35{
36}
37
38KisPaintingAssistantSP VanishingPointAssistant::clone(QMap<KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP> &handleMap) const
39{
40 return KisPaintingAssistantSP(new VanishingPointAssistant(*this, handleMap));
41}
42
43QPointF VanishingPointAssistant::project(const QPointF& pt, const QPointF& strokeBegin, qreal /*moveThresholdPt*/)
44{
45 //Q_ASSERT(handles().size() == 1 || handles().size() == 5);
46
47 if (isLocal() && isAssistantComplete()) {
48 if (getLocalRect().contains(pt)) {
50 } else if (!m_hasBeenInsideLocalRect) { // isn't inside and wasn't inside before
51 return QPointF(qQNaN(), qQNaN());
52 }
53 }
54
55 //dbgKrita<<strokeBegin<< ", " <<*handles()[0];
56 QLineF snapLine = QLineF(*handles()[0], strokeBegin);
57
58
59 qreal dx = snapLine.dx();
60 qreal dy = snapLine.dy();
61
62 const qreal dx2 = dx * dx;
63 const qreal dy2 = dy * dy;
64 const qreal invsqrlen = 1.0 / (dx2 + dy2);
65
66 QPointF r(dx2 * pt.x() + dy2 * snapLine.x1() + dx * dy * (pt.y() - snapLine.y1()),
67 dx2 * snapLine.y1() + dy2 * pt.y() + dx * dy * (pt.x() - snapLine.x1()));
68
69 r *= invsqrlen;
70 return r;
71}
72
73QPointF VanishingPointAssistant::adjustPosition(const QPointF& pt, const QPointF& strokeBegin, const bool /*snapToAny*/, qreal moveThresholdPt)
74{
75 return project(pt, strokeBegin, moveThresholdPt);
76}
77
78void VanishingPointAssistant::adjustLine(QPointF &point, QPointF &strokeBegin)
79{
80 point = project(point, strokeBegin, 0.0);
81}
82
83void VanishingPointAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter* converter, bool cached, KisCanvas2* canvas, bool assistantVisible, bool previewVisible)
84{
85 // HACK ALERT: side handles aren't saved in old krita versions
86 // we need to just add a default position for now if we are loading a vanishing point
87 if (sideHandles().isEmpty()) {
88 QPointF vpPoint = *handles()[0]; // main vanishing point
89 addHandle(new KisPaintingAssistantHandle(vpPoint + QPointF(-70,0)), HandleType::SIDE);
90 addHandle(new KisPaintingAssistantHandle(vpPoint + QPointF(-140,0)), HandleType::SIDE);
91 addHandle(new KisPaintingAssistantHandle(vpPoint + QPointF(70,0)), HandleType::SIDE);
92 addHandle(new KisPaintingAssistantHandle(vpPoint + QPointF(140,0)), HandleType::SIDE);
93 }
94
95 gc.save();
96 gc.resetTransform();
97
98 QRect viewport= gc.viewport();
99
100 QPolygonF viewportAndLocalPoly = (isLocal() && isAssistantComplete()) ?
101 QPolygonF(QRectF(viewport)).intersected(converter->documentToWidgetTransform().map(QPolygonF(QRectF(getLocalRect())))) : QPolygonF(QRectF(viewport));
102
103 // draw controls when we are not editing
104 if (canvas && canvas->paintingAssistantsDecoration()->isEditingAssistants() == false && isAssistantComplete()) {
105
106 if (isSnappingActive() && previewVisible == true) {
107 //don't draw if invalid.
108
109 QTransform initialTransform = converter->documentToWidgetTransform();
110 QPointF startPoint = initialTransform.map(*handles()[0]);
111 QPointF mousePos = effectiveBrushPosition(converter, canvas);
112
113 QLineF snapLine= QLineF(startPoint, mousePos);
114
115 KisAlgebra2D::cropLineToConvexPolygon(snapLine, viewportAndLocalPoly, false, true);
116
117 QPainterPath path;
118
119 path.moveTo(snapLine.p2());
120 path.lineTo(snapLine.p1());
121
122 drawPreview(gc, path);//and we draw the preview.
123
124 }
125 }
126
127
128
129
130 // editor specific controls display
131 if (canvas && canvas->paintingAssistantsDecoration()->isEditingAssistants()) {
132
133 // draws a circle around the vanishing point node while editing
134 QTransform initialTransform = converter->documentToWidgetTransform();
135 QPointF p0 = initialTransform.map(*handles()[0]); // main vanishing point
136 QPointF p1 = initialTransform.map(*sideHandles()[0]);
137 QPointF p2 = initialTransform.map(*sideHandles()[1]);
138 QPointF p3 = initialTransform.map(*sideHandles()[2]);
139 QPointF p4 = initialTransform.map(*sideHandles()[3]);
140
141
142 QRectF ellipse = QRectF(QPointF(p0.x() -15, p0.y() -15), QSizeF(30, 30));
143
144 QPainterPath pathCenter;
145 pathCenter.addEllipse(ellipse);
146 drawPath(gc, pathCenter, isSnappingActive());
147
148 QColor paintingColor = effectiveAssistantColor();
149
150
151 // draw the lines connecting the different nodes
152 QPen penStyle(paintingColor, 2.0, Qt::SolidLine);
153
154 if (!isSnappingActive()) {
155 QColor snappingColor = paintingColor;
156 snappingColor.setAlpha(snappingColor.alpha() * 0.2);
157
158 penStyle.setColor(snappingColor);
159 }
160
161 gc.save();
162 gc.setPen(penStyle);
163 gc.drawLine(p0, p1);
164 gc.drawLine(p0, p3);
165 gc.drawLine(p1, p2);
166 gc.drawLine(p3, p4);
167 gc.restore();
168 }
169
170 QTransform initialTransform = converter->documentToWidgetTransform();
171
172 // draw the local rectangle
173 if (assistantVisible && isLocal() && isAssistantComplete()) {
174 // limited area rectangle
175 QPainterPath path;
176 QPointF p1 = *handles()[(int)LocalFirstHandle];
177 QPointF p3 = *handles()[(int)LocalSecondHandle];
178 QPointF p2 = QPointF(p1.x(), p3.y());
179 QPointF p4 = QPointF(p3.x(), p1.y());
180
181 path.moveTo(initialTransform.map(p1));
182
183 path.lineTo(initialTransform.map(p2));
184 path.lineTo(initialTransform.map(p3));
185 path.lineTo(initialTransform.map(p4));
186 path.lineTo(initialTransform.map(p1));
187 drawPath(gc, path, isSnappingActive());//and we draw the rectangle
188 }
189
190
191 // draw references guide for vanishing points at specified density
192 if (assistantVisible && this->isSnappingActive() ) {
193
194 // cycle through degrees from 0 to 180. We are doing an infinite line, so we don't need to go 360
195 QPointF p0 = initialTransform.map(*handles()[0]); // main vanishing point
196
197 for (int currentAngle=0; currentAngle <= 180; currentAngle = currentAngle + m_referenceLineDensity ) {
198
199 // determine the correct angle based on the iteration
200 float xPos = cos(currentAngle * M_PI / 180);
201 float yPos = sin(currentAngle * M_PI / 180);
202 QPointF unitAngle;
203 unitAngle.setX(p0.x() + xPos);
204 unitAngle.setY(p0.y() + yPos);
205
206 // find point
207 QLineF snapLine= QLineF(p0, unitAngle);
208 KisAlgebra2D::intersectLineConvexPolygon(snapLine, viewportAndLocalPoly, true, true);
209
210 // make a line from VP center to edge of canvas with that angle
211 QPainterPath path;
212 path.moveTo(snapLine.p1());
213 path.lineTo(snapLine.p2());
214 drawPreview(gc, path);//and we draw the preview.
215 }
216 }
217
218
219 gc.restore();
220
221 KisPaintingAssistant::drawAssistant(gc, updateRect, converter, cached, canvas, assistantVisible, previewVisible);
222}
223
224void VanishingPointAssistant::drawCache(QPainter& gc, const KisCoordinatesConverter *converter, bool assistantVisible)
225{
226 if (!m_canvas || !isAssistantComplete()) {
227 return;
228 }
229
230 if (assistantVisible == false || m_canvas->paintingAssistantsDecoration()->isEditingAssistants()) {
231 return;
232 }
233
234 QTransform initialTransform = converter->documentToWidgetTransform();
235 QPointF p0 = initialTransform.map(*handles()[0]);
236
237 // draws an "X"
238 QPainterPath path;
239 path.moveTo(QPointF(p0.x() - 10.0, p0.y() - 10.0));
240 path.lineTo(QPointF(p0.x() + 10.0, p0.y() + 10.0));
241
242 path.moveTo(QPointF(p0.x() - 10.0, p0.y() + 10.0));
243 path.lineTo(QPointF(p0.x() + 10.0, p0.y() - 10.0));
244
245
246 drawPath(gc, path, isSnappingActive());
247}
248
250{
251 if (handles().size() > LocalFirstHandle) {
252 return handles().at(LocalFirstHandle);
253 } else {
254 return nullptr;
255 }
256}
257
259{
260 if (handles().size() > LocalSecondHandle) {
261 return handles().at(LocalSecondHandle);
262 } else {
263 return nullptr;
264 }
265}
266
268{
269 int pointHandle = 0;
270 if (handles().size() > pointHandle) {
271 return *handles().at(pointHandle);
272 } else {
273 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(false, QPointF(0, 0));
274 return QPointF(0, 0);
275 }
276}
277
279{
280 // cannot have less than 1 degree value
281 if (value < 1.0) {
282 value = 1.0;
283 }
284
286}
287
292
294{
295 return handles().size() >= numHandles();
296}
297
299{
300 return true;
301}
302
303void VanishingPointAssistant::saveCustomXml(QXmlStreamWriter* xml)
304{
305 xml->writeStartElement("angleDensity");
306 xml->writeAttribute("value", KisDomUtils::toString( this->referenceLineDensity()));
307 xml->writeEndElement();
308 xml->writeStartElement("isLocal");
309 xml->writeAttribute("value", KisDomUtils::toString( (int)this->isLocal()));
310 xml->writeEndElement();
311}
312
313bool VanishingPointAssistant::loadCustomXml(QXmlStreamReader* xml)
314{
315 if (xml && xml->name() == "angleDensity") {
316 this->setReferenceLineDensity((float)KisDomUtils::toDouble(xml->attributes().value("value").toString()));
317 }
318 if (xml && xml->name() == "isLocal") {
319 this->setLocal((bool)KisDomUtils::toInt(xml->attributes().value("value").toString()));
320 }
321
322 return true;
323}
324
325
329
333
335{
336 return "vanishing point";
337}
338
340{
341 return i18n("Vanishing Point");
342}
343
float value(const T *src, size_t ch)
QPointF p0
QPointF p2
QPointF p3
QPointF p1
KisPaintingAssistantsDecorationSP paintingAssistantsDecoration() const
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 drawPath(QPainter &painter, const QPainterPath &path, bool drawActive=true)
void addHandle(KisPaintingAssistantHandleSP handle, HandleType type)
void drawPreview(QPainter &painter, const QPainterPath &path)
QRectF getLocalRect() const
getLocalRect The function deals with local handles not being topLeft and bottomRight gracefully and r...
const QList< KisPaintingAssistantHandleSP > & sideHandles() const
virtual void drawAssistant(QPainter &gc, const QRectF &updateRect, const KisCoordinatesConverter *converter, bool cached, KisCanvas2 *canvas=0, bool assistantVisible=true, bool previewVisible=true)
void setLocal(bool value)
setLocal
const QList< KisPaintingAssistantHandleSP > & handles() const
KisPaintingAssistant * createPaintingAssistant() const override
void drawAssistant(QPainter &gc, const QRectF &updateRect, const KisCoordinatesConverter *converter, bool cached=true, KisCanvas2 *canvas=nullptr, bool assistantVisible=true, bool previewVisible=true) override
QPointF project(const QPointF &pt, const QPointF &strokeBegin, qreal moveThresholdPt)
QPointF getDefaultEditorPosition() const override
void setReferenceLineDensity(float value)
void saveCustomXml(QXmlStreamWriter *xml) override
int numHandles() const override
KisPaintingAssistantHandleSP secondLocalHandle() const override
secondLocalHandle Note: this doesn't guarantee it will be the bottomRight corner! For that,...
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...
void adjustLine(QPointF &point, QPointF &strokeBegin) override
bool isAssistantComplete() const override
bool loadCustomXml(QXmlStreamReader *xml) override
QPointF adjustPosition(const QPointF &point, const QPointF &strokeBegin, bool snapToAny, qreal moveThresholdPt) override
KisPaintingAssistantHandleSP firstLocalHandle() const override
firstLocalHandle Note: this doesn't guarantee it will be the topleft corner! For that,...
bool canBeLocal() const override
canBeLocal
KisPaintingAssistantSP clone(QMap< KisPaintingAssistantHandleSP, KisPaintingAssistantHandleSP > &handleMap) const override
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define M_PI
Definition kis_global.h:111
QSharedPointer< KisPaintingAssistant > KisPaintingAssistantSP
Definition kis_types.h:189
void cropLineToConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
bool intersectLineConvexPolygon(QLineF &line, const QPolygonF polygon, bool extendFirst, bool extendSecond)
double toDouble(const QString &str, bool *ok=nullptr)
int toInt(const QString &str, bool *ok=nullptr)
QString toString(const QString &value)