File indexing completed on 2025-01-05 03:59:21
0001 /* 0002 This file is part of the Ofi Labs X2 project. 0003 0004 SPDX-FileCopyrightText: 2010 Ariya Hidayat <ariya.hidayat@gmail.com> 0005 0006 SPDX-License-Identifier: BSD-3-Clause 0007 */ 0008 0009 #include "kineticmodel.h" 0010 0011 #include <QTimer> 0012 #include <QElapsedTimer> 0013 0014 static const int KineticModelDefaultUpdateInterval = 15; // ms 0015 0016 class KineticModelPrivate 0017 { 0018 public: 0019 QTimer ticker; 0020 0021 int duration; 0022 QPointF position; 0023 qreal heading; 0024 QPointF velocity; 0025 qreal velocityHeading; 0026 QPointF deacceleration; 0027 qreal deaccelerationHeading; 0028 0029 QElapsedTimer timestamp; 0030 QPointF lastPosition; 0031 qreal lastHeading; 0032 bool changingPosition; 0033 0034 KineticModelPrivate(); 0035 }; 0036 0037 KineticModelPrivate::KineticModelPrivate() 0038 : duration(1403) 0039 , position(0, 0) 0040 , heading(0) 0041 , velocity(0, 0) 0042 , velocityHeading(0) 0043 , deacceleration(0, 0) 0044 , deaccelerationHeading(0) 0045 , lastPosition(0, 0) 0046 , lastHeading(0) 0047 , changingPosition(true) 0048 { 0049 0050 } 0051 0052 KineticModel::KineticModel(QObject *parent) 0053 : QObject(parent) 0054 , d_ptr(new KineticModelPrivate) 0055 { 0056 connect(&d_ptr->ticker, SIGNAL(timeout()), SLOT(update())); 0057 d_ptr->ticker.setInterval(KineticModelDefaultUpdateInterval); 0058 } 0059 0060 KineticModel::~KineticModel() 0061 { 0062 0063 } 0064 0065 bool KineticModel::hasVelocity() const 0066 { 0067 return !d_ptr->velocity.isNull(); 0068 } 0069 0070 int KineticModel::duration() const 0071 { 0072 return d_ptr->duration; 0073 } 0074 0075 void KineticModel::setDuration(int ms) 0076 { 0077 d_ptr->duration = ms; 0078 } 0079 0080 QPointF KineticModel::position() const 0081 { 0082 return d_ptr->position; 0083 } 0084 0085 void KineticModel::setPosition(const QPointF& position) 0086 { 0087 setPosition( position.x(), position.y() ); 0088 } 0089 0090 void KineticModel::setPosition(qreal posX, qreal posY) 0091 { 0092 d_ptr->position.setX( posX ); 0093 d_ptr->position.setY( posY ); 0094 0095 int elapsed = d_ptr->timestamp.elapsed(); 0096 0097 // too fast gives less accuracy 0098 if (elapsed < d_ptr->ticker.interval() / 2) { 0099 return; 0100 } 0101 0102 qreal delta = static_cast<qreal>( elapsed ) / 1000.0; 0103 0104 QPointF lastSpeed = d_ptr->velocity; 0105 QPointF currentSpeed = ( d_ptr->position - d_ptr->lastPosition ) / delta; 0106 d_ptr->velocity = 0.2 * lastSpeed + 0.8 * currentSpeed; 0107 d_ptr->lastPosition = d_ptr->position; 0108 0109 d_ptr->changingPosition = true; 0110 d_ptr->timestamp.start(); 0111 } 0112 0113 void KineticModel::setHeading(qreal heading) 0114 { 0115 d_ptr->heading = heading; 0116 0117 int elapsed = d_ptr->timestamp.elapsed(); 0118 qreal delta = static_cast<qreal>( elapsed ) / 1000.0; 0119 0120 qreal lastSpeed = d_ptr->velocityHeading; 0121 qreal currentSpeed = delta ? ( d_ptr->heading - d_ptr->lastHeading ) / delta : 0; 0122 d_ptr->velocityHeading = 0.5 * lastSpeed + 0.2 * currentSpeed; 0123 d_ptr->lastHeading = d_ptr->heading; 0124 0125 d_ptr->changingPosition = false; 0126 d_ptr->timestamp.start(); 0127 } 0128 0129 void KineticModel::jumpToPosition(const QPointF& position) 0130 { 0131 jumpToPosition( position.x(), position.y() ); 0132 } 0133 0134 void KineticModel::jumpToPosition(qreal posX, qreal posY) 0135 { 0136 d_ptr->position.setX( posX ); 0137 d_ptr->position.setY( posY ); 0138 } 0139 0140 int KineticModel::updateInterval() const 0141 { 0142 return d_ptr->ticker.interval(); 0143 } 0144 0145 void KineticModel::setUpdateInterval(int ms) 0146 { 0147 d_ptr->ticker.setInterval(ms); 0148 } 0149 0150 void KineticModel::stop() 0151 { 0152 Q_D(KineticModel); 0153 0154 d->ticker.stop(); 0155 d->timestamp.start(); 0156 d->velocity = QPointF(0, 0); 0157 d->velocityHeading = 0; 0158 } 0159 0160 void KineticModel::start() 0161 { 0162 Q_D(KineticModel); 0163 0164 // prevent kinetic spinning if last mouse move is too long ago 0165 const int elapsed = d->timestamp.elapsed(); 0166 if ( elapsed > 2 * d->ticker.interval() ) { 0167 d->ticker.stop(); 0168 Q_EMIT finished(); 0169 return; 0170 } 0171 0172 d->deacceleration = d->velocity * 1000 / ( 1 + d_ptr->duration ); 0173 if (d->deacceleration.x() < 0) { 0174 d->deacceleration.setX( -d->deacceleration.x() ); 0175 } 0176 if (d->deacceleration.y() < 0) { 0177 d->deacceleration.setY( -d->deacceleration.y() ); 0178 } 0179 0180 d->deaccelerationHeading = qAbs(d->velocityHeading) * 1000 / ( 1 + d_ptr->duration ); 0181 0182 if (!d->ticker.isActive()) 0183 d->ticker.start(); 0184 } 0185 0186 void KineticModel::update() 0187 { 0188 Q_D(KineticModel); 0189 0190 int elapsed = qMin( static_cast<int>(d->timestamp.elapsed()), 100 ); // limit to 100msec to reduce catapult effect (bug 294608) 0191 qreal delta = static_cast<qreal>(elapsed) / 1000.0; 0192 0193 bool stop = false; 0194 if (d->changingPosition) { 0195 d->position += d->velocity * delta; 0196 QPointF vstep = d->deacceleration * delta; 0197 0198 if (d->velocity.x() < vstep.x() && d->velocity.x() >= -vstep.x()) { 0199 d->velocity.setX( 0 ); 0200 } else { 0201 if (d->velocity.x() > 0) 0202 d->velocity.setX( d->velocity.x() - vstep.x() ); 0203 else 0204 d->velocity.setX( d->velocity.x() + vstep.x() ); 0205 } 0206 0207 if (d->velocity.y() < vstep.y() && d->velocity.y() >= -vstep.y()) { 0208 d->velocity.setY( 0 ); 0209 } else { 0210 if (d->velocity.y() > 0) 0211 d->velocity.setY( d->velocity.y() - vstep.y() ); 0212 else 0213 d->velocity.setY( d->velocity.y() + vstep.y() ); 0214 } 0215 0216 stop = d->velocity.isNull(); 0217 0218 Q_EMIT positionChanged( d->position.x(), d->position.y() ); 0219 } else { 0220 d->heading += d->velocityHeading * delta; 0221 qreal vstep = d->deaccelerationHeading * delta; // Always positive. 0222 if ((d->velocityHeading < vstep && d->velocityHeading >= -vstep) || !vstep) { 0223 d->velocityHeading = 0; 0224 } else { 0225 d->velocityHeading += d->velocityHeading > 0 ? -1 * vstep : vstep; 0226 } 0227 0228 stop = !d->velocityHeading; 0229 0230 Q_EMIT headingChanged( d->heading ); 0231 } 0232 0233 if (stop) { 0234 Q_EMIT finished(); 0235 d->ticker.stop(); 0236 } 0237 0238 d->timestamp.start(); 0239 } 0240 0241 #include "moc_kineticmodel.cpp" 0242