1 /*
2 * Copyright (c) 2007,2010 Cyrille Berger <cberger@cberger.net>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <brushengine/kis_paint_information.h>
20
21 #include <QDomElement>
22 #include <boost/optional.hpp>
23
24 #include "kis_paintop.h"
25 #include "kis_algebra_2d.h"
26 #include "kis_lod_transform.h"
27 #include "kis_spacing_information.h"
28
29 #include <kis_dom_utils.h>
30
31 struct KisPaintInformation::Private {
PrivateKisPaintInformation::Private32 Private(const QPointF & pos_,
33 qreal pressure_,
34 qreal xTilt_, qreal yTilt_,
35 qreal rotation_,
36 qreal tangentialPressure_,
37 qreal perspective_,
38 qreal time_,
39 qreal speed_,
40 bool isHoveringMode_)
41 :
42 pos(pos_),
43 pressure(pressure_),
44 xTilt(xTilt_),
45 yTilt(yTilt_),
46 rotation(rotation_),
47 tangentialPressure(tangentialPressure_),
48 perspective(perspective_),
49 time(time_),
50 speed(speed_),
51 isHoveringMode(isHoveringMode_),
52 randomSource(0),
53 perStrokeRandomSource(0),
54 levelOfDetail(0)
55 {
56 }
57
58
59
~PrivateKisPaintInformation::Private60 ~Private() {
61 KIS_ASSERT_RECOVER_NOOP(!sanityIsRegistered);
62 }
PrivateKisPaintInformation::Private63 Private(const Private &rhs) {
64 copy(rhs);
65 }
operator =KisPaintInformation::Private66 Private& operator=(const Private &rhs) {
67 copy(rhs);
68 return *this;
69 }
70
copyKisPaintInformation::Private71 void copy(const Private &rhs) {
72 pos = rhs.pos;
73 pressure = rhs.pressure;
74 xTilt = rhs.xTilt;
75 yTilt = rhs.yTilt;
76 rotation = rhs.rotation;
77 tangentialPressure = rhs.tangentialPressure;
78 perspective = rhs.perspective;
79 time = rhs.time;
80 speed = rhs.speed;
81 isHoveringMode = rhs.isHoveringMode;
82 randomSource = rhs.randomSource;
83 perStrokeRandomSource = rhs.perStrokeRandomSource;
84 sanityIsRegistered = false; // HINT: we do not copy registration mark!
85 directionHistoryInfo = rhs.directionHistoryInfo;
86 canvasRotation = rhs.canvasRotation;
87 canvasMirroredH = rhs.canvasMirroredH;
88 canvasMirroredV = rhs.canvasMirroredV;
89
90 if (rhs.drawingAngleOverride) {
91 drawingAngleOverride = *rhs.drawingAngleOverride;
92 }
93
94 levelOfDetail = rhs.levelOfDetail;
95 }
96
97
98 QPointF pos;
99 qreal pressure;
100 qreal xTilt;
101 qreal yTilt;
102 qreal rotation;
103 qreal tangentialPressure;
104 qreal perspective;
105 qreal time;
106 qreal speed;
107 bool isHoveringMode;
108 KisRandomSourceSP randomSource;
109 KisPerStrokeRandomSourceSP perStrokeRandomSource;
110 qreal canvasRotation {0};
111 bool canvasMirroredH {false};
112 bool canvasMirroredV {false};
113
114 boost::optional<qreal> drawingAngleOverride;
115 bool sanityIsRegistered = false;
116
117 struct DirectionHistoryInfo {
DirectionHistoryInfoKisPaintInformation::Private::DirectionHistoryInfo118 DirectionHistoryInfo() {}
DirectionHistoryInfoKisPaintInformation::Private::DirectionHistoryInfo119 DirectionHistoryInfo(qreal _totalDistance,
120 int _currentDabSeqNo,
121 qreal _lastAngle,
122 QPointF _lastPosition,
123 qreal _lastMaxPressure,
124 boost::optional<qreal> _lockedDrawingAngle)
125 : totalStrokeLength(_totalDistance),
126 currentDabSeqNo(_currentDabSeqNo),
127 lastAngle(_lastAngle),
128 lastPosition(_lastPosition),
129 lastMaxPressure(_lastMaxPressure),
130 lockedDrawingAngle(_lockedDrawingAngle)
131 {
132 }
133
134 qreal totalStrokeLength = 0.0;
135 int currentDabSeqNo = 0;
136 qreal lastAngle = 0.0;
137 QPointF lastPosition;
138 qreal lastMaxPressure = 0.0;
139 boost::optional<qreal> lockedDrawingAngle;
140 };
141 boost::optional<DirectionHistoryInfo> directionHistoryInfo;
142
143 int levelOfDetail;
144
registerDistanceInfoKisPaintInformation::Private145 void registerDistanceInfo(KisDistanceInformation *di) {
146 directionHistoryInfo = DirectionHistoryInfo(di->scalarDistanceApprox(),
147 di->currentDabSeqNo(),
148 di->lastDrawingAngle(),
149 di->lastPosition(),
150 di->maxPressure(),
151 di->lockedDrawingAngleOptional());
152
153
154 KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityIsRegistered);
155 sanityIsRegistered = true;
156 }
157
unregisterDistanceInfoKisPaintInformation::Private158 void unregisterDistanceInfo() {
159 sanityIsRegistered = false;
160 }
161 };
162
163 KisPaintInformation::DistanceInformationRegistrar::
DistanceInformationRegistrar(KisPaintInformation * _p,KisDistanceInformation * distanceInfo)164 DistanceInformationRegistrar(KisPaintInformation *_p, KisDistanceInformation *distanceInfo)
165 : p(_p)
166 {
167 p->d->registerDistanceInfo(distanceInfo);
168 }
169
DistanceInformationRegistrar(KisPaintInformation::DistanceInformationRegistrar && rhs)170 KisPaintInformation::DistanceInformationRegistrar::DistanceInformationRegistrar(KisPaintInformation::DistanceInformationRegistrar &&rhs)
171 : p(0)
172 {
173 std::swap(p, rhs.p);
174 }
175
176 KisPaintInformation::DistanceInformationRegistrar::
~DistanceInformationRegistrar()177 ~DistanceInformationRegistrar()
178 {
179 if (p) {
180 p->d->unregisterDistanceInfo();
181 }
182 }
183
KisPaintInformation(const QPointF & pos,qreal pressure,qreal xTilt,qreal yTilt,qreal rotation,qreal tangentialPressure,qreal perspective,qreal time,qreal speed)184 KisPaintInformation::KisPaintInformation(const QPointF & pos,
185 qreal pressure,
186 qreal xTilt, qreal yTilt,
187 qreal rotation,
188 qreal tangentialPressure,
189 qreal perspective,
190 qreal time,
191 qreal speed)
192 : d(new Private(pos,
193 pressure,
194 xTilt, yTilt,
195 rotation,
196 tangentialPressure,
197 perspective,
198 time,
199 speed,
200 false))
201 {
202 }
203
KisPaintInformation(const QPointF & pos,qreal pressure,qreal xTilt,qreal yTilt,qreal rotation)204 KisPaintInformation::KisPaintInformation(const QPointF & pos,
205 qreal pressure,
206 qreal xTilt,
207 qreal yTilt,
208 qreal rotation)
209 : d(new Private(pos,
210 pressure,
211 xTilt, yTilt,
212 rotation,
213 0.0,
214 1.0,
215 0.0,
216 0.0,
217 false))
218 {
219
220 }
221
KisPaintInformation(const QPointF & pos,qreal pressure)222 KisPaintInformation::KisPaintInformation(const QPointF &pos,
223 qreal pressure)
224 : d(new Private(pos,
225 pressure,
226 0.0, 0.0,
227 0.0,
228 0.0,
229 1.0,
230 0.0,
231 0.0,
232 false))
233 {
234 }
235
KisPaintInformation(const KisPaintInformation & rhs)236 KisPaintInformation::KisPaintInformation(const KisPaintInformation& rhs)
237 : d(new Private(*rhs.d))
238 {
239 }
240
operator =(const KisPaintInformation & rhs)241 void KisPaintInformation::operator=(const KisPaintInformation & rhs)
242 {
243 *d = *rhs.d;
244 }
245
~KisPaintInformation()246 KisPaintInformation::~KisPaintInformation()
247 {
248 delete d;
249 }
250
isHoveringMode() const251 bool KisPaintInformation::isHoveringMode() const
252 {
253 return d->isHoveringMode;
254 }
255
256
257 KisPaintInformation
createHoveringModeInfo(const QPointF & pos,qreal pressure,qreal xTilt,qreal yTilt,qreal rotation,qreal tangentialPressure,qreal perspective,qreal speed,qreal canvasrotation,bool canvasMirroredH,bool canvasMirroredV)258 KisPaintInformation::createHoveringModeInfo(const QPointF &pos,
259 qreal pressure,
260 qreal xTilt, qreal yTilt,
261 qreal rotation,
262 qreal tangentialPressure,
263 qreal perspective,
264 qreal speed,
265 qreal canvasrotation,
266 bool canvasMirroredH,
267 bool canvasMirroredV)
268 {
269 KisPaintInformation info(pos,
270 pressure,
271 xTilt, yTilt,
272 rotation,
273 tangentialPressure,
274 perspective, 0, speed);
275 info.d->isHoveringMode = true;
276 info.d->canvasRotation = canvasrotation;
277 info.d->canvasMirroredH = canvasMirroredH;
278 info.d->canvasMirroredV = canvasMirroredV;
279 return info;
280 }
281
282
canvasRotation() const283 qreal KisPaintInformation::canvasRotation() const
284 {
285 return d->canvasRotation;
286 }
287
setCanvasRotation(qreal rotation)288 void KisPaintInformation::setCanvasRotation(qreal rotation)
289 {
290 d->canvasRotation = normalizeAngleDegrees(rotation);
291 }
292
canvasMirroredH() const293 bool KisPaintInformation::canvasMirroredH() const
294 {
295 return d->canvasMirroredH;
296 }
297
setCanvasMirroredH(bool value)298 void KisPaintInformation::setCanvasMirroredH(bool value)
299 {
300 d->canvasMirroredH = value;
301 }
302
canvasMirroredV() const303 bool KisPaintInformation::canvasMirroredV() const
304 {
305 return d->canvasMirroredV;
306 }
307
setCanvasMirroredV(bool value)308 void KisPaintInformation::setCanvasMirroredV(bool value)
309 {
310 d->canvasMirroredV = value;
311 }
312
toXML(QDomDocument &,QDomElement & e) const313 void KisPaintInformation::toXML(QDomDocument&, QDomElement& e) const
314 {
315 // hovering mode infos are not supposed to be saved
316 KIS_ASSERT_RECOVER_NOOP(!d->isHoveringMode);
317
318 e.setAttribute("pointX", QString::number(pos().x(), 'g', 15));
319 e.setAttribute("pointY", QString::number(pos().y(), 'g', 15));
320 e.setAttribute("pressure", QString::number(pressure(), 'g', 15));
321 e.setAttribute("xTilt", QString::number(xTilt(), 'g', 15));
322 e.setAttribute("yTilt", QString::number(yTilt(), 'g', 15));
323 e.setAttribute("rotation", QString::number(rotation(), 'g', 15));
324 e.setAttribute("tangentialPressure", QString::number(tangentialPressure(), 'g', 15));
325 e.setAttribute("perspective", QString::number(perspective(), 'g', 15));
326 e.setAttribute("time", QString::number(d->time, 'g', 15));
327 e.setAttribute("speed", QString::number(d->speed, 'g', 15));
328 }
329
fromXML(const QDomElement & e)330 KisPaintInformation KisPaintInformation::fromXML(const QDomElement& e)
331 {
332 qreal pointX = qreal(KisDomUtils::toDouble(e.attribute("pointX", "0.0")));
333 qreal pointY = qreal(KisDomUtils::toDouble(e.attribute("pointY", "0.0")));
334 qreal pressure = qreal(KisDomUtils::toDouble(e.attribute("pressure", "0.0")));
335 qreal rotation = qreal(KisDomUtils::toDouble(e.attribute("rotation", "0.0")));
336 qreal tangentialPressure = qreal(KisDomUtils::toDouble(e.attribute("tangentialPressure", "0.0")));
337 qreal perspective = qreal(KisDomUtils::toDouble(e.attribute("perspective", "0.0")));
338 qreal xTilt = qreal(KisDomUtils::toDouble(e.attribute("xTilt", "0.0")));
339 qreal yTilt = qreal(KisDomUtils::toDouble(e.attribute("yTilt", "0.0")));
340 qreal time = KisDomUtils::toDouble(e.attribute("time", "0"));
341 qreal speed = KisDomUtils::toDouble(e.attribute("speed", "0"));
342
343 return KisPaintInformation(QPointF(pointX, pointY), pressure, xTilt, yTilt,
344 rotation, tangentialPressure, perspective, time, speed);
345 }
346
pos() const347 const QPointF& KisPaintInformation::pos() const
348 {
349 return d->pos;
350 }
351
setPos(const QPointF & p)352 void KisPaintInformation::setPos(const QPointF& p)
353 {
354 d->pos = p;
355 }
356
pressure() const357 qreal KisPaintInformation::pressure() const
358 {
359 return d->pressure;
360 }
361
setPressure(qreal p)362 void KisPaintInformation::setPressure(qreal p)
363 {
364 d->pressure = p;
365 }
366
xTilt() const367 qreal KisPaintInformation::xTilt() const
368 {
369 return d->xTilt;
370 }
371
yTilt() const372 qreal KisPaintInformation::yTilt() const
373 {
374 return d->yTilt;
375 }
376
overrideDrawingAngle(qreal angle)377 void KisPaintInformation::overrideDrawingAngle(qreal angle)
378 {
379 d->drawingAngleOverride = angle;
380 }
381
drawingAngleSafe(const KisDistanceInformation & distance) const382 qreal KisPaintInformation::drawingAngleSafe(const KisDistanceInformation &distance) const
383 {
384 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!d->directionHistoryInfo, 0.0);
385 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(distance.hasLastDabInformation(), 0.0);
386 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!d->drawingAngleOverride, 0.0);
387
388 return KisAlgebra2D::directionBetweenPoints(distance.lastPosition(),
389 pos(),
390 distance.lastDrawingAngle());
391
392 }
393
394 KisPaintInformation::DistanceInformationRegistrar
registerDistanceInformation(KisDistanceInformation * distance)395 KisPaintInformation::registerDistanceInformation(KisDistanceInformation *distance)
396 {
397 return DistanceInformationRegistrar(this, distance);
398 }
399
drawingAngle(bool considerLockedAngle) const400 qreal KisPaintInformation::drawingAngle(bool considerLockedAngle) const
401 {
402 if (d->drawingAngleOverride) return *d->drawingAngleOverride;
403
404 if (!d->directionHistoryInfo) {
405 warnKrita << "KisPaintInformation::drawingAngleSafe()" << "DirectionHistoryInfo object is not available";
406 return 0.0;
407 }
408
409 if (considerLockedAngle &&
410 d->directionHistoryInfo->lockedDrawingAngle) {
411
412 return *d->directionHistoryInfo->lockedDrawingAngle;
413 }
414
415 // If the start and end positions are the same, we can't compute an angle. In that case, use the
416 // provided default.
417 return KisAlgebra2D::directionBetweenPoints(d->directionHistoryInfo->lastPosition,
418 pos(),
419 d->directionHistoryInfo->lastAngle);
420 }
421
drawingDirectionVector() const422 QPointF KisPaintInformation::drawingDirectionVector() const
423 {
424 const qreal angle = drawingAngle(false);
425 return QPointF(cos(angle), sin(angle));
426 }
427
drawingDistance() const428 qreal KisPaintInformation::drawingDistance() const
429 {
430 if (!d->directionHistoryInfo) {
431 warnKrita << "KisPaintInformation::drawingDistance()" << "DirectionHistoryInfo object is not available";
432 return 1.0;
433 }
434
435 QVector2D diff(pos() - d->directionHistoryInfo->lastPosition);
436 qreal length = diff.length();
437
438 if (d->levelOfDetail) {
439 length *= KisLodTransform::lodToInvScale(d->levelOfDetail);
440 }
441
442 return length;
443 }
444
maxPressure() const445 qreal KisPaintInformation::maxPressure() const
446 {
447 if (!d->directionHistoryInfo) {
448 warnKrita << "KisPaintInformation::maxPressure()" << "DirectionHistoryInfo object is not available";
449 return d->pressure;
450 }
451
452 return qMax(d->directionHistoryInfo->lastMaxPressure, d->pressure);
453 }
454
drawingSpeed() const455 qreal KisPaintInformation::drawingSpeed() const
456 {
457 return d->speed;
458 }
459
rotation() const460 qreal KisPaintInformation::rotation() const
461 {
462 return d->rotation;
463 }
464
tangentialPressure() const465 qreal KisPaintInformation::tangentialPressure() const
466 {
467 return d->tangentialPressure;
468 }
469
perspective() const470 qreal KisPaintInformation::perspective() const
471 {
472 return d->perspective;
473 }
474
currentTime() const475 qreal KisPaintInformation::currentTime() const
476 {
477 return d->time;
478 }
479
currentDabSeqNo() const480 int KisPaintInformation::currentDabSeqNo() const
481 {
482 if (!d->directionHistoryInfo) {
483 warnKrita << "KisPaintInformation::currentDabSeqNo()" << "DirectionHistoryInfo object is not available";
484 return 0;
485 }
486
487 return d->directionHistoryInfo->currentDabSeqNo;
488 }
489
totalStrokeLength() const490 qreal KisPaintInformation::totalStrokeLength() const
491 {
492 if (!d->directionHistoryInfo) {
493 warnKrita << "KisPaintInformation::totalStrokeLength()" << "DirectionHistoryInfo object is not available";
494 return 0;
495 }
496
497 return d->directionHistoryInfo->totalStrokeLength;
498 }
499
randomSource() const500 KisRandomSourceSP KisPaintInformation::randomSource() const
501 {
502 if (!d->randomSource) {
503 qWarning() << "Accessing uninitialized random source!";
504 d->randomSource = new KisRandomSource();
505 }
506
507 return d->randomSource;
508 }
509
setRandomSource(KisRandomSourceSP value)510 void KisPaintInformation::setRandomSource(KisRandomSourceSP value)
511 {
512 d->randomSource = value;
513 }
514
perStrokeRandomSource() const515 KisPerStrokeRandomSourceSP KisPaintInformation::perStrokeRandomSource() const
516 {
517 if (!d->perStrokeRandomSource) {
518 qWarning() << "Accessing uninitialized per stroke random source!";
519 d->perStrokeRandomSource = new KisPerStrokeRandomSource();
520 }
521
522 return d->perStrokeRandomSource;
523 }
524
setPerStrokeRandomSource(KisPerStrokeRandomSourceSP value)525 void KisPaintInformation::setPerStrokeRandomSource(KisPerStrokeRandomSourceSP value)
526 {
527 d->perStrokeRandomSource = value;
528 }
529
setLevelOfDetail(int levelOfDetail)530 void KisPaintInformation::setLevelOfDetail(int levelOfDetail)
531 {
532 d->levelOfDetail = levelOfDetail;
533 }
534
operator <<(QDebug dbg,const KisPaintInformation & info)535 QDebug operator<<(QDebug dbg, const KisPaintInformation &info)
536 {
537 #ifdef NDEBUG
538 Q_UNUSED(info);
539 #else
540 dbg.nospace() << "Position: " << info.pos();
541 dbg.nospace() << ", Pressure: " << info.pressure();
542 dbg.nospace() << ", X Tilt: " << info.xTilt();
543 dbg.nospace() << ", Y Tilt: " << info.yTilt();
544 dbg.nospace() << ", Rotation: " << info.rotation();
545 dbg.nospace() << ", Tangential Pressure: " << info.tangentialPressure();
546 dbg.nospace() << ", Perspective: " << info.perspective();
547 dbg.nospace() << ", Drawing Angle: " << info.drawingAngle();
548 dbg.nospace() << ", Drawing Speed: " << info.drawingSpeed();
549 dbg.nospace() << ", Drawing Distance: " << info.drawingDistance();
550 dbg.nospace() << ", Time: " << info.currentTime();
551 #endif
552 return dbg.space();
553 }
554
mixOnlyPosition(qreal t,const KisPaintInformation & mixedPi,const KisPaintInformation & basePi)555 KisPaintInformation KisPaintInformation::mixOnlyPosition(qreal t, const KisPaintInformation& mixedPi, const KisPaintInformation& basePi)
556 {
557 QPointF pt = (1 - t) * mixedPi.pos() + t * basePi.pos();
558 return mixImpl(pt, t, mixedPi, basePi, true, false);
559 }
560
mix(qreal t,const KisPaintInformation & pi1,const KisPaintInformation & pi2)561 KisPaintInformation KisPaintInformation::mix(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2)
562 {
563 QPointF pt = (1 - t) * pi1.pos() + t * pi2.pos();
564 return mix(pt, t, pi1, pi2);
565 }
566
mix(const QPointF & p,qreal t,const KisPaintInformation & pi1,const KisPaintInformation & pi2)567 KisPaintInformation KisPaintInformation::mix(const QPointF& p, qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2)
568 {
569 return mixImpl(p, t, pi1, pi2, false, true);
570 }
571
mixWithoutTime(qreal t,const KisPaintInformation & pi1,const KisPaintInformation & pi2)572 KisPaintInformation KisPaintInformation::mixWithoutTime(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2)
573 {
574 QPointF pt = (1 - t) * pi1.pos() + t * pi2.pos();
575 return mixWithoutTime(pt, t, pi1, pi2);
576 }
577
mixWithoutTime(const QPointF & p,qreal t,const KisPaintInformation & pi1,const KisPaintInformation & pi2)578 KisPaintInformation KisPaintInformation::mixWithoutTime(const QPointF& p, qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2)
579 {
580 return mixImpl(p, t, pi1, pi2, false, false);
581 }
582
mixOtherOnlyPosition(qreal t,const KisPaintInformation & other)583 void KisPaintInformation::mixOtherOnlyPosition(qreal t, const KisPaintInformation& other)
584 {
585 QPointF pt = (1 - t) * other.pos() + t * this->pos();
586 this->mixOtherImpl(pt, t, other, true, false);
587 }
588
mixOtherWithoutTime(qreal t,const KisPaintInformation & other)589 void KisPaintInformation::mixOtherWithoutTime(qreal t, const KisPaintInformation& other)
590 {
591 QPointF pt = (1 - t) * other.pos() + t * this->pos();
592 this->mixOtherImpl(pt, t, other, false, false);
593 }
594
mixImpl(const QPointF & p,qreal t,const KisPaintInformation & pi1,const KisPaintInformation & pi2,bool posOnly,bool mixTime)595 KisPaintInformation KisPaintInformation::mixImpl(const QPointF &p, qreal t, const KisPaintInformation &pi1, const KisPaintInformation &pi2, bool posOnly, bool mixTime)
596 {
597 KisPaintInformation result(pi2);
598 result.mixOtherImpl(p, t, pi1, posOnly, mixTime);
599 return result;
600 }
601
mixOtherImpl(const QPointF & p,qreal t,const KisPaintInformation & other,bool posOnly,bool mixTime)602 void KisPaintInformation::mixOtherImpl(const QPointF &p, qreal t, const KisPaintInformation &other, bool posOnly, bool mixTime)
603 {
604 if (posOnly) {
605 this->d->pos = p;
606 this->d->isHoveringMode = false;
607 this->d->levelOfDetail = 0;
608 return;
609 }
610 else {
611 qreal pressure = (1 - t) * other.pressure() + t * this->pressure();
612 qreal xTilt = (1 - t) * other.xTilt() + t * this->xTilt();
613 qreal yTilt = (1 - t) * other.yTilt() + t * this->yTilt();
614
615 qreal rotation = other.rotation();
616
617 if (other.rotation() != this->rotation()) {
618 qreal a1 = kisDegreesToRadians(other.rotation());
619 qreal a2 = kisDegreesToRadians(this->rotation());
620 qreal distance = shortestAngularDistance(a2, a1);
621
622 rotation = kisRadiansToDegrees(incrementInDirection(a1, t * distance, a2));
623 }
624
625 qreal tangentialPressure = (1 - t) * other.tangentialPressure() + t * this->tangentialPressure();
626 qreal perspective = (1 - t) * other.perspective() + t * this->perspective();
627 qreal time = mixTime ? ((1 - t) * other.currentTime() + t * this->currentTime()) : this->currentTime();
628 qreal speed = (1 - t) * other.drawingSpeed() + t * this->drawingSpeed();
629
630 KIS_ASSERT_RECOVER_NOOP(other.isHoveringMode() == this->isHoveringMode());
631 *(this->d) = Private(p, pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, time, speed, other.isHoveringMode());
632 this->d->canvasRotation = other.d->canvasRotation;
633 this->d->canvasMirroredH = other.d->canvasMirroredH;
634 this->d->canvasMirroredV = other.d->canvasMirroredV;
635 this->d->randomSource = other.d->randomSource;
636 this->d->perStrokeRandomSource = other.d->perStrokeRandomSource;
637 // this->d->isHoveringMode = other.isHoveringMode();
638 this->d->levelOfDetail = other.d->levelOfDetail;
639 }
640 }
641
tiltDirection(const KisPaintInformation & info,bool normalize)642 qreal KisPaintInformation::tiltDirection(const KisPaintInformation& info, bool normalize)
643 {
644 qreal xTilt = info.xTilt();
645 qreal yTilt = info.yTilt();
646 // radians -PI, PI
647 qreal tiltDirection = atan2(-xTilt, yTilt);
648 // if normalize is true map to 0.0..1.0
649 return normalize ? (tiltDirection / (2 * M_PI) + 0.5) : tiltDirection;
650 }
651
tiltElevation(const KisPaintInformation & info,qreal maxTiltX,qreal maxTiltY,bool normalize)652 qreal KisPaintInformation::tiltElevation(const KisPaintInformation& info, qreal maxTiltX, qreal maxTiltY, bool normalize)
653 {
654 qreal xTilt = qBound(qreal(-1.0), info.xTilt() / maxTiltX , qreal(1.0));
655 qreal yTilt = qBound(qreal(-1.0), info.yTilt() / maxTiltY , qreal(1.0));
656
657 qreal e;
658 if (fabs(xTilt) > fabs(yTilt)) {
659 e = sqrt(qreal(1.0) + yTilt * yTilt);
660 } else {
661 e = sqrt(qreal(1.0) + xTilt * xTilt);
662 }
663
664 qreal cosAlpha = sqrt(xTilt * xTilt + yTilt * yTilt) / e;
665 qreal tiltElevation = acos(cosAlpha); // in radians in [0, 0.5 * PI]
666
667 // mapping to 0.0..1.0 if normalize is true
668 return normalize ? (tiltElevation / (M_PI * qreal(0.5))) : tiltElevation;
669 }
670