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