Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_experiment_paintop.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2010-2011 Lukáš Tvrdý <lukast.dev@gmail.com>
3 * SPDX-FileCopyrightText: 2012 Dmitry Kazakov <dimula73@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
10
11#include <cmath>
12
13#include <QPainterPath>
14
16
17#include <kis_debug.h>
18
19#include <kis_paint_device.h>
20#include <kis_painter.h>
21#include <kis_image.h>
23#include <krita_utils.h>
24
25
27 : KisPaintOp(painter)
28{
29 Q_UNUSED(image);
30 Q_UNUSED(node);
31
32 m_firstRun = true;
33
34 m_experimentOption.read(settings.data());
35
37 m_displaceCoeff = (m_experimentOption.displacement * 0.01 * 14) + 1; // 1..15 [7 default according alchemy]
38
40 m_speedMultiplier = (m_experimentOption.speed * 0.01 * 35); // 0..35 [15 default according alchemy]
43
47
48 //Sets the brush to pattern or foregroundColor
51 } else {
53 }
54
55 // Mirror options set with appropriate color, pattern, and fillStyle
56 if (m_useMirroring) {
63
64 }
65 else {
67 }
68}
69
74
76{
77 if (m_windingFill) {
78 m_path.setFillRule(Qt::WindingFill);
79 }
80
81 if (m_useMirroring) {
83
84 Q_FOREACH (const QRect & rect, changedRegion.rects()) {
87
88 }
89 }
90 else {
91 //Sets options when mirror is not selected
93
96
97 Q_FOREACH (const QRect & rect, changedRegion.rects()) {
99 }
100 }
101}
102
104 const KisPaintInformation& pi2)
105{
106 const qreal fadeFactor = 0.6;
107
108 QPointF diff = pi2.pos() - pi1.pos();
109 qreal realLength = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
110
111 if (realLength < 0.1) return pi2.pos();
112
113 qreal coeff = 0.5 * realLength * m_speedMultiplier;
114 m_savedSpeedCoeff = fadeFactor * m_savedSpeedCoeff + (1 - fadeFactor) * coeff;
115 QPointF newPoint = pi1.pos() + diff * m_savedSpeedCoeff / realLength;
116 m_savedSpeedPoint = fadeFactor * m_savedSpeedPoint + (1 - fadeFactor) * newPoint;
117
118 return m_savedSpeedPoint;
119}
120
122{
123 Q_UNUSED(currentDistance);
124 if (!painter()) return;
125
126 if (m_firstRun) {
127 m_firstRun = false;
128
129 m_path.moveTo(pi1.pos());
130 m_path.lineTo(pi2.pos());
131
132 m_center = pi1.pos();
133
135 m_lastPaintTime = 0;
136
139
142
143 }
144 else {
145
146 const QPointF pos1 = pi1.pos();
147 QPointF pos2 = pi2.pos();
148
149 if (m_speedEnabled) {
150 pos2 = speedCorrectedPosition(pi1, pi2);
151 }
152
153 int length = (pos2 - pos1).manhattanLength();
155
156 if (m_smoothingEnabled) {
158
160 QPointF pt = (m_savedSmoothingPoint + pos2) * 0.5;
161
162 // for updates approximate curve with two lines
163 m_savedPoints << m_path.currentPosition();
166 m_savedPoints << pt;
167
168 m_path.quadTo(m_savedSmoothingPoint, pt);
170
172 }
173 }
174 else {
175 m_path.lineTo(pos2);
176 m_savedPoints << pos1;
177 m_savedPoints << pos2;
178 }
179
180 if (m_displaceEnabled) {
181 if (m_path.elementCount() % 16 == 0) {
182 QRectF bounds = m_path.boundingRect();
184 bounds |= m_path.boundingRect();
185
186 qreal threshold = simplifyThreshold(bounds);
188 }
189 else {
191 }
192 }
193
197 const int timeThreshold = 40;
198 const int elapsedTime = pi2.currentTime() - m_lastPaintTime;
199
200 QRect pathBounds = m_path.boundingRect().toRect();
201 int distanceMetric = qMax(pathBounds.width(), pathBounds.height());
202
203 if (elapsedTime > timeThreshold ||
205 m_savedUpdateDistance > distanceMetric / 8)) {
206
207 if (m_displaceEnabled) {
216 const int pathSizeThreshold = 128;
217
218 KisRegion changedRegion;
219 if (distanceMetric < pathSizeThreshold) {
220
221 QRectF changedRect = m_path.boundingRect().toRect() |
222 m_lastPaintedPath.boundingRect().toRect();
223 changedRect.adjust(-1, -1, 1, 1);
224
225 changedRegion = changedRect.toRect();
226 }
227 else {
228 QPainterPath diff1 = m_path - m_lastPaintedPath;
229 QPainterPath diff2 = m_lastPaintedPath - m_path;
230
231 changedRegion = KritaUtils::splitPath(diff1 | diff2);
232 }
233
234 paintRegion(changedRegion);
236 }
237 else if (!m_savedPoints.isEmpty()) {
239 paintRegion(changedRegion);
240 }
241
242 m_savedPoints.clear();
245 }
246 }
247}
248
249
254
256{
257 Q_UNUSED(info);
258 return KisSpacingInformation(1.0);
259}
260
261bool tryMergePoints(QPainterPath &path,
262 const QPointF &startPoint,
263 const QPointF &endPoint,
264 qreal &distance,
265 qreal distanceThreshold,
266 bool lastSegment)
267{
268 qreal length = (endPoint - startPoint).manhattanLength();
269
270 if (lastSegment || length > distanceThreshold) {
271 if (distance != 0) {
272 path.lineTo(startPoint);
273 }
274 distance = 0;
275 return false;
276 }
277
278 distance += length;
279
280 if (distance > distanceThreshold) {
281 path.lineTo(endPoint);
282 distance = 0;
283 }
284
285 return true;
286}
287
289{
290 qreal maxDimension = qMax(bounds.width(), bounds.height());
291 return qMax(0.01 * maxDimension, 1.0);
292}
293
294QPointF KisExperimentPaintOp::getAngle(const QPointF& p1, const QPointF& p2, qreal distance)
295{
296 QPointF diff = p1 - p2;
297 qreal realLength = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
298 return realLength > 0.5 ? p1 + diff * distance / realLength : p1;
299}
300
301QPainterPath KisExperimentPaintOp::applyDisplace(const QPainterPath& path, int speed)
302{
303 QPointF lastPoint = path.currentPosition();
304
305 QPainterPath newPath;
306 int count = path.elementCount();
307 int curveElementCounter = 0;
308 QPointF ctrl1;
309 QPointF ctrl2;
310 QPointF endPoint;
311 for (int i = 0; i < count; i++) {
312 QPainterPath::Element e = path.elementAt(i);
313 switch (e.type) {
314 case QPainterPath::MoveToElement: {
315 newPath.moveTo(getAngle(QPointF(e.x, e.y), lastPoint, speed));
316 break;
317 }
318 case QPainterPath::LineToElement: {
319 newPath.lineTo(getAngle(QPointF(e.x, e.y), lastPoint, speed));
320 break;
321 }
322 case QPainterPath::CurveToElement: {
323 curveElementCounter = 0;
324 endPoint = getAngle(QPointF(e.x, e.y), lastPoint, speed);
325 break;
326 }
327 case QPainterPath::CurveToDataElement: {
328 curveElementCounter++;
329
330 if (curveElementCounter == 1) {
331 ctrl1 = getAngle(QPointF(e.x, e.y), lastPoint, speed);
332 }
333 else if (curveElementCounter == 2) {
334 ctrl2 = getAngle(QPointF(e.x, e.y), lastPoint, speed);
335 newPath.cubicTo(ctrl1, ctrl2, endPoint);
336 }
337 break;
338 }
339 }
340
341 }// for
342
343 return newPath;
344}
345
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
QPointF p2
QPointF p1
const QString COMPOSITE_COPY
qreal distance(const QPointF &p1, const QPointF &p2)
void paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance) override
KisPaintDeviceSP m_originalDevice
KisSpacingInformation paintAt(const KisPaintInformation &info) override
QVector< QPointF > m_savedPoints
KisPainter::FillStyle m_fillStyle
QPointF speedCorrectedPosition(const KisPaintInformation &pi1, const KisPaintInformation &pi2)
KisExperimentPaintOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image)
static qreal simplifyThreshold(const QRectF &bounds)
KisExperimentOpOptionData m_experimentOption
void paintRegion(const KisRegion &changedRegion)
KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override
static QPointF getAngle(const QPointF &p1, const QPointF &p2, qreal distance)
static QPainterPath applyDisplace(const QPainterPath &path, int speed)
KisPaintDeviceSP createCompositionSourceDevice() const
const QPointF & pos() const
qreal currentTime() const
Number of ms since the beginning of the stroke.
@ FillStyleForegroundColor
bool hasMirroring() const
KoColor paintColor
void renderDabWithMirroringNonIncremental(QRect rc, KisPaintDeviceSP dab)
void setFillStyle(FillStyle fillStyle)
Set the current style with which to fill.
void fillPainterPath(const QPainterPath &path)
void setPattern(const KoPatternSP pattern)
Set the current pattern.
void setPaintColor(const KoColor &color)
void setAntiAliasPolygonFill(bool antiAliasPolygonFill)
Set whether a polygon's filled area should be anti-aliased or not. The default is true.
void setCompositeOpId(const KoCompositeOp *op)
KoPatternSP pattern
QRect boundingRect() const
QVector< QRect > rects() const
#define bounds(x, a, b)
bool tryMergePoints(QPainterPath &path, const QPointF &startPoint, const QPointF &endPoint, qreal &distance, qreal distanceThreshold, bool lastSegment)
QPainterPath trySimplifyPath(const QPainterPath &path, qreal lengthThreshold)
KisRegion splitPath(const QPainterPath &path)
KisRegion splitTriangles(const QPointF &center, const QVector< QPointF > &points)
bool read(const KisPropertiesConfiguration *setting)
KisPainter * painter
KisPaintDeviceSP source() const