File indexing completed on 2025-01-26 04:05:51
0001 /* 0002 * SPDX-FileCopyrightText: 2007, 2010 Cyrille Berger <cberger@cberger.net> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <brushengine/kis_paint_information.h> 0008 0009 #include <QDomElement> 0010 #include <boost/optional.hpp> 0011 0012 #include "kis_paintop.h" 0013 #include "kis_algebra_2d.h" 0014 #include "kis_lod_transform.h" 0015 #include "kis_spacing_information.h" 0016 0017 #include <kis_dom_utils.h> 0018 0019 struct KisPaintInformation::Private { 0020 Private(const QPointF & pos_, 0021 qreal pressure_, 0022 qreal xTilt_, qreal yTilt_, 0023 qreal rotation_, 0024 qreal tangentialPressure_, 0025 qreal perspective_, 0026 qreal time_, 0027 qreal speed_, 0028 bool isHoveringMode_) 0029 : 0030 pos(pos_), 0031 pressure(pressure_), 0032 xTilt(xTilt_), 0033 yTilt(yTilt_), 0034 rotation(rotation_), 0035 tangentialPressure(tangentialPressure_), 0036 perspective(perspective_), 0037 time(time_), 0038 speed(speed_), 0039 isHoveringMode(isHoveringMode_), 0040 randomSource(0), 0041 perStrokeRandomSource(0), 0042 levelOfDetail(0) 0043 { 0044 } 0045 0046 0047 0048 ~Private() { 0049 KIS_ASSERT_RECOVER_NOOP(!sanityIsRegistered); 0050 } 0051 Private(const Private &rhs) { 0052 copy(rhs); 0053 } 0054 Private& operator=(const Private &rhs) { 0055 copy(rhs); 0056 return *this; 0057 } 0058 0059 void copy(const Private &rhs) { 0060 pos = rhs.pos; 0061 pressure = rhs.pressure; 0062 xTilt = rhs.xTilt; 0063 yTilt = rhs.yTilt; 0064 rotation = rhs.rotation; 0065 tangentialPressure = rhs.tangentialPressure; 0066 perspective = rhs.perspective; 0067 time = rhs.time; 0068 speed = rhs.speed; 0069 isHoveringMode = rhs.isHoveringMode; 0070 randomSource = rhs.randomSource; 0071 perStrokeRandomSource = rhs.perStrokeRandomSource; 0072 sanityIsRegistered = false; // HINT: we do not copy registration mark! 0073 directionHistoryInfo = rhs.directionHistoryInfo; 0074 canvasRotation = rhs.canvasRotation; 0075 canvasMirroredH = rhs.canvasMirroredH; 0076 canvasMirroredV = rhs.canvasMirroredV; 0077 0078 if (rhs.drawingAngleOverride) { 0079 drawingAngleOverride = *rhs.drawingAngleOverride; 0080 } 0081 0082 levelOfDetail = rhs.levelOfDetail; 0083 } 0084 0085 0086 QPointF pos; 0087 qreal pressure; 0088 qreal xTilt; 0089 qreal yTilt; 0090 qreal rotation; 0091 qreal tangentialPressure; 0092 qreal perspective; 0093 qreal time; 0094 qreal speed; 0095 bool isHoveringMode; 0096 KisRandomSourceSP randomSource; 0097 KisPerStrokeRandomSourceSP perStrokeRandomSource; 0098 qreal canvasRotation {0}; 0099 bool canvasMirroredH {false}; 0100 bool canvasMirroredV {false}; 0101 0102 boost::optional<qreal> drawingAngleOverride; 0103 bool sanityIsRegistered = false; 0104 0105 struct DirectionHistoryInfo { 0106 DirectionHistoryInfo() {} 0107 DirectionHistoryInfo(qreal _totalDistance, 0108 int _currentDabSeqNo, 0109 qreal _lastAngle, 0110 QPointF _lastPosition, 0111 qreal _lastMaxPressure, 0112 boost::optional<qreal> _lockedDrawingAngle) 0113 : totalStrokeLength(_totalDistance), 0114 currentDabSeqNo(_currentDabSeqNo), 0115 lastAngle(_lastAngle), 0116 lastPosition(_lastPosition), 0117 lastMaxPressure(_lastMaxPressure), 0118 lockedDrawingAngle(_lockedDrawingAngle) 0119 { 0120 } 0121 0122 qreal totalStrokeLength = 0.0; 0123 int currentDabSeqNo = 0; 0124 qreal lastAngle = 0.0; 0125 QPointF lastPosition; 0126 qreal lastMaxPressure = 0.0; 0127 boost::optional<qreal> lockedDrawingAngle; 0128 }; 0129 boost::optional<DirectionHistoryInfo> directionHistoryInfo; 0130 0131 int levelOfDetail; 0132 0133 void registerDistanceInfo(KisDistanceInformation *di) { 0134 directionHistoryInfo = DirectionHistoryInfo(di->scalarDistanceApprox(), 0135 di->currentDabSeqNo(), 0136 di->lastDrawingAngle(), 0137 di->lastPosition(), 0138 di->maxPressure(), 0139 di->lockedDrawingAngleOptional()); 0140 0141 0142 KIS_SAFE_ASSERT_RECOVER_NOOP(!sanityIsRegistered); 0143 sanityIsRegistered = true; 0144 } 0145 0146 void unregisterDistanceInfo() { 0147 sanityIsRegistered = false; 0148 } 0149 }; 0150 0151 KisPaintInformation::DistanceInformationRegistrar:: 0152 DistanceInformationRegistrar(KisPaintInformation *_p, KisDistanceInformation *distanceInfo) 0153 : p(_p) 0154 { 0155 p->d->registerDistanceInfo(distanceInfo); 0156 } 0157 0158 KisPaintInformation::DistanceInformationRegistrar::DistanceInformationRegistrar(KisPaintInformation::DistanceInformationRegistrar &&rhs) 0159 : p(0) 0160 { 0161 std::swap(p, rhs.p); 0162 } 0163 0164 KisPaintInformation::DistanceInformationRegistrar:: 0165 ~DistanceInformationRegistrar() 0166 { 0167 if (p) { 0168 p->d->unregisterDistanceInfo(); 0169 } 0170 } 0171 0172 KisPaintInformation::KisPaintInformation(const QPointF & pos, 0173 qreal pressure, 0174 qreal xTilt, qreal yTilt, 0175 qreal rotation, 0176 qreal tangentialPressure, 0177 qreal perspective, 0178 qreal time, 0179 qreal speed) 0180 : d(new Private(pos, 0181 pressure, 0182 xTilt, yTilt, 0183 rotation, 0184 tangentialPressure, 0185 perspective, 0186 time, 0187 speed, 0188 false)) 0189 { 0190 } 0191 0192 KisPaintInformation::KisPaintInformation(const QPointF & pos, 0193 qreal pressure, 0194 qreal xTilt, 0195 qreal yTilt, 0196 qreal rotation) 0197 : d(new Private(pos, 0198 pressure, 0199 xTilt, yTilt, 0200 rotation, 0201 0.0, 0202 1.0, 0203 0.0, 0204 0.0, 0205 false)) 0206 { 0207 0208 } 0209 0210 KisPaintInformation::KisPaintInformation(const QPointF &pos, 0211 qreal pressure) 0212 : d(new Private(pos, 0213 pressure, 0214 0.0, 0.0, 0215 0.0, 0216 0.0, 0217 1.0, 0218 0.0, 0219 0.0, 0220 false)) 0221 { 0222 } 0223 0224 KisPaintInformation::KisPaintInformation(const KisPaintInformation& rhs) 0225 : d(new Private(*rhs.d)) 0226 { 0227 } 0228 0229 void KisPaintInformation::operator=(const KisPaintInformation & rhs) 0230 { 0231 *d = *rhs.d; 0232 } 0233 0234 KisPaintInformation::~KisPaintInformation() 0235 { 0236 delete d; 0237 } 0238 0239 bool KisPaintInformation::isHoveringMode() const 0240 { 0241 return d->isHoveringMode; 0242 } 0243 0244 0245 KisPaintInformation 0246 KisPaintInformation::createHoveringModeInfo(const QPointF &pos, 0247 qreal pressure, 0248 qreal xTilt, qreal yTilt, 0249 qreal rotation, 0250 qreal tangentialPressure, 0251 qreal perspective, 0252 qreal speed, 0253 qreal canvasrotation, 0254 bool canvasMirroredH, 0255 bool canvasMirroredV) 0256 { 0257 KisPaintInformation info(pos, 0258 pressure, 0259 xTilt, yTilt, 0260 rotation, 0261 tangentialPressure, 0262 perspective, 0, speed); 0263 info.d->isHoveringMode = true; 0264 info.d->canvasRotation = canvasrotation; 0265 info.d->canvasMirroredH = canvasMirroredH; 0266 info.d->canvasMirroredV = canvasMirroredV; 0267 return info; 0268 } 0269 0270 0271 qreal KisPaintInformation::canvasRotation() const 0272 { 0273 return d->canvasRotation; 0274 } 0275 0276 void KisPaintInformation::setCanvasRotation(qreal rotation) 0277 { 0278 d->canvasRotation = normalizeAngleDegrees(rotation); 0279 } 0280 0281 bool KisPaintInformation::canvasMirroredH() const 0282 { 0283 return d->canvasMirroredH; 0284 } 0285 0286 void KisPaintInformation::setCanvasMirroredH(bool value) 0287 { 0288 d->canvasMirroredH = value; 0289 } 0290 0291 bool KisPaintInformation::canvasMirroredV() const 0292 { 0293 return d->canvasMirroredV; 0294 } 0295 0296 void KisPaintInformation::setCanvasMirroredV(bool value) 0297 { 0298 d->canvasMirroredV = value; 0299 } 0300 0301 void KisPaintInformation::toXML(QDomDocument&, QDomElement& e) const 0302 { 0303 // hovering mode infos are not supposed to be saved 0304 KIS_ASSERT_RECOVER_NOOP(!d->isHoveringMode); 0305 0306 e.setAttribute("pointX", QString::number(pos().x(), 'g', 15)); 0307 e.setAttribute("pointY", QString::number(pos().y(), 'g', 15)); 0308 e.setAttribute("pressure", QString::number(pressure(), 'g', 15)); 0309 e.setAttribute("xTilt", QString::number(xTilt(), 'g', 15)); 0310 e.setAttribute("yTilt", QString::number(yTilt(), 'g', 15)); 0311 e.setAttribute("rotation", QString::number(rotation(), 'g', 15)); 0312 e.setAttribute("tangentialPressure", QString::number(tangentialPressure(), 'g', 15)); 0313 e.setAttribute("perspective", QString::number(perspective(), 'g', 15)); 0314 e.setAttribute("time", QString::number(d->time, 'g', 15)); 0315 e.setAttribute("speed", QString::number(d->speed, 'g', 15)); 0316 } 0317 0318 KisPaintInformation KisPaintInformation::fromXML(const QDomElement& e) 0319 { 0320 qreal pointX = qreal(KisDomUtils::toDouble(e.attribute("pointX", "0.0"))); 0321 qreal pointY = qreal(KisDomUtils::toDouble(e.attribute("pointY", "0.0"))); 0322 qreal pressure = qreal(KisDomUtils::toDouble(e.attribute("pressure", "0.0"))); 0323 qreal rotation = qreal(KisDomUtils::toDouble(e.attribute("rotation", "0.0"))); 0324 qreal tangentialPressure = qreal(KisDomUtils::toDouble(e.attribute("tangentialPressure", "0.0"))); 0325 qreal perspective = qreal(KisDomUtils::toDouble(e.attribute("perspective", "0.0"))); 0326 qreal xTilt = qreal(KisDomUtils::toDouble(e.attribute("xTilt", "0.0"))); 0327 qreal yTilt = qreal(KisDomUtils::toDouble(e.attribute("yTilt", "0.0"))); 0328 qreal time = KisDomUtils::toDouble(e.attribute("time", "0")); 0329 qreal speed = KisDomUtils::toDouble(e.attribute("speed", "0")); 0330 0331 return KisPaintInformation(QPointF(pointX, pointY), pressure, xTilt, yTilt, 0332 rotation, tangentialPressure, perspective, time, speed); 0333 } 0334 0335 const QPointF& KisPaintInformation::pos() const 0336 { 0337 return d->pos; 0338 } 0339 0340 void KisPaintInformation::setPos(const QPointF& p) 0341 { 0342 d->pos = p; 0343 } 0344 0345 qreal KisPaintInformation::pressure() const 0346 { 0347 return d->pressure; 0348 } 0349 0350 void KisPaintInformation::setPressure(qreal p) 0351 { 0352 d->pressure = p; 0353 } 0354 0355 qreal KisPaintInformation::xTilt() const 0356 { 0357 return d->xTilt; 0358 } 0359 0360 qreal KisPaintInformation::yTilt() const 0361 { 0362 return d->yTilt; 0363 } 0364 0365 void KisPaintInformation::overrideDrawingAngle(qreal angle) 0366 { 0367 d->drawingAngleOverride = angle; 0368 } 0369 0370 qreal KisPaintInformation::drawingAngleSafe(const KisDistanceInformation &distance) const 0371 { 0372 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!d->directionHistoryInfo, 0.0); 0373 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(distance.hasLastDabInformation(), 0.0); 0374 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!d->drawingAngleOverride, 0.0); 0375 0376 return KisAlgebra2D::directionBetweenPoints(distance.lastPosition(), 0377 pos(), 0378 distance.lastDrawingAngle()); 0379 0380 } 0381 0382 KisPaintInformation::DistanceInformationRegistrar 0383 KisPaintInformation::registerDistanceInformation(KisDistanceInformation *distance) 0384 { 0385 return DistanceInformationRegistrar(this, distance); 0386 } 0387 0388 qreal KisPaintInformation::drawingAngle(bool considerLockedAngle) const 0389 { 0390 if (d->drawingAngleOverride) return *d->drawingAngleOverride; 0391 0392 if (!d->directionHistoryInfo) { 0393 warnKrita << "KisPaintInformation::drawingAngleSafe()" << "DirectionHistoryInfo object is not available"; 0394 return 0.0; 0395 } 0396 0397 if (considerLockedAngle && 0398 d->directionHistoryInfo->lockedDrawingAngle) { 0399 0400 return *d->directionHistoryInfo->lockedDrawingAngle; 0401 } 0402 0403 // If the start and end positions are the same, we can't compute an angle. In that case, use the 0404 // provided default. 0405 return KisAlgebra2D::directionBetweenPoints(d->directionHistoryInfo->lastPosition, 0406 pos(), 0407 d->directionHistoryInfo->lastAngle); 0408 } 0409 0410 QPointF KisPaintInformation::drawingDirectionVector() const 0411 { 0412 const qreal angle = drawingAngle(false); 0413 return QPointF(cos(angle), sin(angle)); 0414 } 0415 0416 qreal KisPaintInformation::drawingDistance() const 0417 { 0418 if (!d->directionHistoryInfo) { 0419 warnKrita << "KisPaintInformation::drawingDistance()" << "DirectionHistoryInfo object is not available"; 0420 return 1.0; 0421 } 0422 0423 QVector2D diff(pos() - d->directionHistoryInfo->lastPosition); 0424 qreal length = diff.length(); 0425 0426 if (d->levelOfDetail) { 0427 length *= KisLodTransform::lodToInvScale(d->levelOfDetail); 0428 } 0429 0430 return length; 0431 } 0432 0433 qreal KisPaintInformation::maxPressure() const 0434 { 0435 if (!d->directionHistoryInfo) { 0436 warnKrita << "KisPaintInformation::maxPressure()" << "DirectionHistoryInfo object is not available"; 0437 return d->pressure; 0438 } 0439 0440 return qMax(d->directionHistoryInfo->lastMaxPressure, d->pressure); 0441 } 0442 0443 qreal KisPaintInformation::drawingSpeed() const 0444 { 0445 return d->speed; 0446 } 0447 0448 void KisPaintInformation::setCurrentTime(qreal time) const { 0449 0450 d->time = time; 0451 } 0452 0453 qreal KisPaintInformation::rotation() const 0454 { 0455 return d->rotation; 0456 } 0457 0458 qreal KisPaintInformation::tangentialPressure() const 0459 { 0460 return d->tangentialPressure; 0461 } 0462 0463 qreal KisPaintInformation::perspective() const 0464 { 0465 return d->perspective; 0466 } 0467 0468 qreal KisPaintInformation::currentTime() const 0469 { 0470 return d->time; 0471 } 0472 0473 int KisPaintInformation::currentDabSeqNo() const 0474 { 0475 if (!d->directionHistoryInfo) { 0476 warnKrita << "KisPaintInformation::currentDabSeqNo()" << "DirectionHistoryInfo object is not available"; 0477 return 0; 0478 } 0479 0480 return d->directionHistoryInfo->currentDabSeqNo; 0481 } 0482 0483 qreal KisPaintInformation::totalStrokeLength() const 0484 { 0485 if (!d->directionHistoryInfo) { 0486 warnKrita << "KisPaintInformation::totalStrokeLength()" << "DirectionHistoryInfo object is not available"; 0487 return 0; 0488 } 0489 0490 return d->directionHistoryInfo->totalStrokeLength; 0491 } 0492 0493 KisRandomSourceSP KisPaintInformation::randomSource() const 0494 { 0495 if (!d->randomSource) { 0496 qWarning() << "Accessing uninitialized random source!"; 0497 qDebug().noquote() << kisBacktrace(); 0498 d->randomSource = new KisRandomSource(); 0499 } 0500 0501 return d->randomSource; 0502 } 0503 0504 void KisPaintInformation::setRandomSource(KisRandomSourceSP value) 0505 { 0506 d->randomSource = value; 0507 } 0508 0509 KisPerStrokeRandomSourceSP KisPaintInformation::perStrokeRandomSource() const 0510 { 0511 if (!d->perStrokeRandomSource) { 0512 qWarning() << "Accessing uninitialized per stroke random source!"; 0513 d->perStrokeRandomSource = new KisPerStrokeRandomSource(); 0514 } 0515 0516 return d->perStrokeRandomSource; 0517 } 0518 0519 void KisPaintInformation::setPerStrokeRandomSource(KisPerStrokeRandomSourceSP value) 0520 { 0521 d->perStrokeRandomSource = value; 0522 } 0523 0524 void KisPaintInformation::setLevelOfDetail(int levelOfDetail) 0525 { 0526 d->levelOfDetail = levelOfDetail; 0527 } 0528 0529 QDebug operator<<(QDebug dbg, const KisPaintInformation &info) 0530 { 0531 #ifdef NDEBUG 0532 Q_UNUSED(info); 0533 #else 0534 dbg.nospace() << "Position: " << info.pos(); 0535 dbg.nospace() << ", Pressure: " << info.pressure(); 0536 dbg.nospace() << ", X Tilt: " << info.xTilt(); 0537 dbg.nospace() << ", Y Tilt: " << info.yTilt(); 0538 dbg.nospace() << ", Rotation: " << info.rotation(); 0539 dbg.nospace() << ", Tangential Pressure: " << info.tangentialPressure(); 0540 dbg.nospace() << ", Perspective: " << info.perspective(); 0541 dbg.nospace() << ", Drawing Angle: " << info.drawingAngle(); 0542 dbg.nospace() << ", Drawing Speed: " << info.drawingSpeed(); 0543 dbg.nospace() << ", Drawing Distance: " << info.drawingDistance(); 0544 dbg.nospace() << ", Time: " << info.currentTime(); 0545 #endif 0546 return dbg.space(); 0547 } 0548 0549 KisPaintInformation KisPaintInformation::mixOnlyPosition(qreal t, const KisPaintInformation& mixedPi, const KisPaintInformation& basePi) 0550 { 0551 QPointF pt = (1 - t) * mixedPi.pos() + t * basePi.pos(); 0552 return mixImpl(pt, t, mixedPi, basePi, true, false); 0553 } 0554 0555 KisPaintInformation KisPaintInformation::mix(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) 0556 { 0557 QPointF pt = (1 - t) * pi1.pos() + t * pi2.pos(); 0558 return mix(pt, t, pi1, pi2); 0559 } 0560 0561 KisPaintInformation KisPaintInformation::mix(const QPointF& p, qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) 0562 { 0563 return mixImpl(p, t, pi1, pi2, false, true); 0564 } 0565 0566 KisPaintInformation KisPaintInformation::mixWithoutTime(qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) 0567 { 0568 QPointF pt = (1 - t) * pi1.pos() + t * pi2.pos(); 0569 return mixWithoutTime(pt, t, pi1, pi2); 0570 } 0571 0572 KisPaintInformation KisPaintInformation::mixWithoutTime(const QPointF& p, qreal t, const KisPaintInformation& pi1, const KisPaintInformation& pi2) 0573 { 0574 return mixImpl(p, t, pi1, pi2, false, false); 0575 } 0576 0577 void KisPaintInformation::mixOtherOnlyPosition(qreal t, const KisPaintInformation& other) 0578 { 0579 QPointF pt = (1 - t) * other.pos() + t * this->pos(); 0580 this->mixOtherImpl(pt, t, other, true, false); 0581 } 0582 0583 void KisPaintInformation::mixOtherWithoutTime(qreal t, const KisPaintInformation& other) 0584 { 0585 QPointF pt = (1 - t) * other.pos() + t * this->pos(); 0586 this->mixOtherImpl(pt, t, other, false, false); 0587 } 0588 0589 KisPaintInformation KisPaintInformation::mixImpl(const QPointF &p, qreal t, const KisPaintInformation &pi1, const KisPaintInformation &pi2, bool posOnly, bool mixTime) 0590 { 0591 KisPaintInformation result(pi2); 0592 result.mixOtherImpl(p, t, pi1, posOnly, mixTime); 0593 return result; 0594 } 0595 0596 void KisPaintInformation::mixOtherImpl(const QPointF &p, qreal t, const KisPaintInformation &other, bool posOnly, bool mixTime) 0597 { 0598 if (posOnly) { 0599 this->d->pos = p; 0600 this->d->isHoveringMode = false; 0601 this->d->levelOfDetail = 0; 0602 return; 0603 } 0604 else { 0605 qreal pressure = (1 - t) * other.pressure() + t * this->pressure(); 0606 qreal xTilt = (1 - t) * other.xTilt() + t * this->xTilt(); 0607 qreal yTilt = (1 - t) * other.yTilt() + t * this->yTilt(); 0608 0609 qreal rotation = other.rotation(); 0610 0611 if (other.rotation() != this->rotation()) { 0612 qreal a1 = kisDegreesToRadians(other.rotation()); 0613 qreal a2 = kisDegreesToRadians(this->rotation()); 0614 qreal distance = shortestAngularDistance(a2, a1); 0615 0616 rotation = kisRadiansToDegrees(incrementInDirection(a1, t * distance, a2)); 0617 } 0618 0619 qreal tangentialPressure = (1 - t) * other.tangentialPressure() + t * this->tangentialPressure(); 0620 qreal perspective = (1 - t) * other.perspective() + t * this->perspective(); 0621 qreal time = mixTime ? ((1 - t) * other.currentTime() + t * this->currentTime()) : this->currentTime(); 0622 qreal speed = (1 - t) * other.drawingSpeed() + t * this->drawingSpeed(); 0623 0624 KIS_ASSERT_RECOVER_NOOP(other.isHoveringMode() == this->isHoveringMode()); 0625 *(this->d) = Private(p, pressure, xTilt, yTilt, rotation, tangentialPressure, perspective, time, speed, other.isHoveringMode()); 0626 this->d->canvasRotation = other.d->canvasRotation; 0627 this->d->canvasMirroredH = other.d->canvasMirroredH; 0628 this->d->canvasMirroredV = other.d->canvasMirroredV; 0629 this->d->randomSource = other.d->randomSource; 0630 this->d->perStrokeRandomSource = other.d->perStrokeRandomSource; 0631 // this->d->isHoveringMode = other.isHoveringMode(); 0632 this->d->levelOfDetail = other.d->levelOfDetail; 0633 } 0634 } 0635 0636 qreal KisPaintInformation::tiltDirection(const KisPaintInformation& info, bool normalize) 0637 { 0638 qreal xTilt = info.xTilt(); 0639 qreal yTilt = info.yTilt(); 0640 // radians -PI, PI 0641 qreal tiltDirection = atan2(-xTilt, yTilt); 0642 // if normalize is true map to 0.0..1.0 0643 return normalize ? (tiltDirection / (2 * M_PI) + 0.5) : tiltDirection; 0644 } 0645 0646 qreal KisPaintInformation::tiltElevation(const KisPaintInformation& info, qreal maxTiltX, qreal maxTiltY, bool normalize) 0647 { 0648 qreal xTilt = qBound(qreal(-1.0), info.xTilt() / maxTiltX , qreal(1.0)); 0649 qreal yTilt = qBound(qreal(-1.0), info.yTilt() / maxTiltY , qreal(1.0)); 0650 0651 qreal e; 0652 if (fabs(xTilt) > fabs(yTilt)) { 0653 e = sqrt(qreal(1.0) + yTilt * yTilt); 0654 } else { 0655 e = sqrt(qreal(1.0) + xTilt * xTilt); 0656 } 0657 0658 qreal cosAlpha = sqrt(xTilt * xTilt + yTilt * yTilt) / e; 0659 qreal tiltElevation = acos(cosAlpha); // in radians in [0, 0.5 * PI] 0660 0661 // mapping to 0.0..1.0 if normalize is true 0662 return normalize ? (tiltElevation / (M_PI * qreal(0.5))) : tiltElevation; 0663 }