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#include <kis_algebra_2d.h>
25
26
28 : KisPaintOp(painter)
29{
30 Q_UNUSED(image);
31 Q_UNUSED(node);
32
33 m_firstRun = true;
34
35 m_experimentOption.read(settings.data());
36
38 m_displaceCoeff = (m_experimentOption.displacement * 0.01 * 14) + 1; // 1..15 [7 default according alchemy]
39
41 m_speedMultiplier = (m_experimentOption.speed * 0.01 * 35); // 0..35 [15 default according alchemy]
44
48
49 //Sets the brush to pattern or foregroundColor
52 } else {
54 }
55
56 // Mirror options set with appropriate color, pattern, and fillStyle
57 if (m_useMirroring) {
64
65 }
66 else {
68 }
69}
70
75
77{
78 if (m_windingFill) {
79 m_path.setFillRule(Qt::WindingFill);
80 }
81
82 if (m_useMirroring) {
84
85 Q_FOREACH (const QRect & rect, changedRegion.rects()) {
88
89 }
90 }
91 else {
92 //Sets options when mirror is not selected
94
97
98 Q_FOREACH (const QRect & rect, changedRegion.rects()) {
100 }
101 }
102}
103
105 const KisPaintInformation& pi2)
106{
107 const qreal fadeFactor = 0.6;
108
109 QPointF diff = pi2.pos() - pi1.pos();
110 qreal realLength = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
111
112 if (realLength < 0.1) return pi2.pos();
113
114 qreal coeff = 0.5 * realLength * m_speedMultiplier;
115 m_savedSpeedCoeff = fadeFactor * m_savedSpeedCoeff + (1 - fadeFactor) * coeff;
116 QPointF newPoint = pi1.pos() + diff * m_savedSpeedCoeff / realLength;
117 m_savedSpeedPoint = fadeFactor * m_savedSpeedPoint + (1 - fadeFactor) * newPoint;
118
119 return m_savedSpeedPoint;
120}
121
123{
124 Q_UNUSED(currentDistance);
125 if (!painter()) return;
126
127 if (m_firstRun) {
128 m_firstRun = false;
129
130 m_path.moveTo(pi1.pos());
131 m_path.lineTo(pi2.pos());
132
133 m_center = pi1.pos();
134
136 m_lastPaintTime = 0;
137
140
143
144 }
145 else {
146
147 const QPointF pos1 = pi1.pos();
148 QPointF pos2 = pi2.pos();
149
150 if (m_speedEnabled) {
151 pos2 = speedCorrectedPosition(pi1, pi2);
152 }
153
154 int length = (pos2 - pos1).manhattanLength();
156
157 if (m_smoothingEnabled) {
159
161 QPointF pt = (m_savedSmoothingPoint + pos2) * 0.5;
162
163 // for updates approximate curve with two lines
164 m_savedPoints << m_path.currentPosition();
167 m_savedPoints << pt;
168
169 m_path.quadTo(m_savedSmoothingPoint, pt);
171
173 }
174 }
175 else {
176 m_path.lineTo(pos2);
177 m_savedPoints << pos1;
178 m_savedPoints << pos2;
179 }
180
181 if (m_displaceEnabled) {
182 if (m_path.elementCount() % 16 == 0) {
183 QRectF bounds = m_path.boundingRect();
185 bounds |= m_path.boundingRect();
186
187 qreal threshold = simplifyThreshold(bounds);
189 }
190 else {
192 }
193 }
194
198 const int timeThreshold = 40;
199 const int elapsedTime = pi2.currentTime() - m_lastPaintTime;
200
201 QRect pathBounds = m_path.boundingRect().toRect();
202 int distanceMetric = qMax(pathBounds.width(), pathBounds.height());
203
204 if (elapsedTime > timeThreshold ||
206 m_savedUpdateDistance > distanceMetric / 8)) {
207
208 if (m_displaceEnabled) {
217 const int pathSizeThreshold = 128;
218
219 KisRegion changedRegion;
220 if (distanceMetric < pathSizeThreshold) {
221
222 QRectF changedRect = m_path.boundingRect().toRect() |
223 m_lastPaintedPath.boundingRect().toRect();
224 changedRect.adjust(-1, -1, 1, 1);
225
226 changedRegion = changedRect.toRect();
227 }
228 else {
229 QPainterPath diff1 = m_path - m_lastPaintedPath;
230 QPainterPath diff2 = m_lastPaintedPath - m_path;
231
232 changedRegion = KritaUtils::splitPath(diff1 | diff2);
233 }
234
235 paintRegion(changedRegion);
237 }
238 else if (!m_savedPoints.isEmpty()) {
240 paintRegion(changedRegion);
241 }
242
243 m_savedPoints.clear();
246 }
247 }
248}
249
250
255
257{
258 Q_UNUSED(info);
259 return KisSpacingInformation(1.0);
260}
261
262bool tryMergePoints(QPainterPath &path,
263 const QPointF &startPoint,
264 const QPointF &endPoint,
265 qreal &distance,
266 qreal distanceThreshold,
267 bool lastSegment)
268{
269 qreal length = (endPoint - startPoint).manhattanLength();
270
271 if (lastSegment || length > distanceThreshold) {
272 if (distance != 0) {
273 path.lineTo(startPoint);
274 }
275 distance = 0;
276 return false;
277 }
278
279 distance += length;
280
281 if (distance > distanceThreshold) {
282 path.lineTo(endPoint);
283 distance = 0;
284 }
285
286 return true;
287}
288
290{
291 qreal maxDimension = qMax(bounds.width(), bounds.height());
292 return qMax(0.01 * maxDimension, 1.0);
293}
294
295QPointF KisExperimentPaintOp::getAngle(const QPointF& p1, const QPointF& p2, qreal distance)
296{
297 QPointF diff = p1 - p2;
298 qreal realLength = sqrt(diff.x() * diff.x() + diff.y() * diff.y());
299 return realLength > 0.5 ? p1 + diff * distance / realLength : p1;
300}
301
302QPainterPath KisExperimentPaintOp::applyDisplace(const QPainterPath& path, int speed)
303{
304 QPointF lastPoint = path.currentPosition();
305
306 QPainterPath newPath;
307 int count = path.elementCount();
308 int curveElementCounter = 0;
309 QPointF ctrl1;
310 QPointF ctrl2;
311 QPointF endPoint;
312 for (int i = 0; i < count; i++) {
313 QPainterPath::Element e = path.elementAt(i);
314 switch (e.type) {
315 case QPainterPath::MoveToElement: {
316 newPath.moveTo(getAngle(QPointF(e.x, e.y), lastPoint, speed));
317 break;
318 }
319 case QPainterPath::LineToElement: {
320 newPath.lineTo(getAngle(QPointF(e.x, e.y), lastPoint, speed));
321 break;
322 }
323 case QPainterPath::CurveToElement: {
324 curveElementCounter = 0;
325 endPoint = getAngle(QPointF(e.x, e.y), lastPoint, speed);
326 break;
327 }
328 case QPainterPath::CurveToDataElement: {
329 curveElementCounter++;
330
331 if (curveElementCounter == 1) {
332 ctrl1 = getAngle(QPointF(e.x, e.y), lastPoint, speed);
333 }
334 else if (curveElementCounter == 2) {
335 ctrl2 = getAngle(QPointF(e.x, e.y), lastPoint, speed);
336 newPath.cubicTo(ctrl1, ctrl2, endPoint);
337 }
338 break;
339 }
340 }
341
342 }// for
343
344 return newPath;
345}
346
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)
trySimplifyPath Tries to simplify a QPainterPath
KisRegion splitPath(const QPainterPath &path)
KisRegion splitTriangles(const QPointF &center, const QVector< QPointF > &points)
bool read(const KisPropertiesConfiguration *setting)
KisPainter * painter
KisPaintDeviceSP source() const