9#include "kineticmodel.h" 
   11#include <QElapsedTimer> 
   14static const int KineticModelDefaultUpdateInterval = 15; 
 
   16class KineticModelPrivate
 
   25    qreal velocityHeading;
 
   26    QPointF deacceleration;
 
   27    qreal deaccelerationHeading;
 
   29    QElapsedTimer timestamp;
 
   32    bool changingPosition;
 
   34    KineticModelPrivate();
 
   37KineticModelPrivate::KineticModelPrivate()
 
   43    , deacceleration(0, 0)
 
   44    , deaccelerationHeading(0)
 
   47    , changingPosition(true)
 
   51KineticModel::KineticModel(
QObject *parent)
 
   53    , d_ptr(new KineticModelPrivate)
 
   56    d_ptr->ticker.setInterval(KineticModelDefaultUpdateInterval);
 
   59KineticModel::~KineticModel() = 
default;
 
   61bool KineticModel::hasVelocity()
 const 
   63    return !d_ptr->velocity.isNull();
 
   66int KineticModel::duration()
 const 
   68    return d_ptr->duration;
 
   71void KineticModel::setDuration(
int ms)
 
   76QPointF KineticModel::position()
 const 
   78    return d_ptr->position;
 
   81void KineticModel::setPosition(
const QPointF &position)
 
   83    setPosition(position.x(), position.y());
 
   86void KineticModel::setPosition(qreal posX, qreal posY)
 
   88    d_ptr->position.setX(posX);
 
   89    d_ptr->position.setY(posY);
 
   91    int elapsed = d_ptr->timestamp.elapsed();
 
   94    if (elapsed < d_ptr->ticker.interval() / 2) {
 
   98    qreal delta = 
static_cast<qreal
>(elapsed) / 1000.0;
 
  100    QPointF lastSpeed = d_ptr->velocity;
 
  101    QPointF currentSpeed = (d_ptr->position - d_ptr->lastPosition) / delta;
 
  102    d_ptr->velocity = 0.2 * lastSpeed + 0.8 * currentSpeed;
 
  103    d_ptr->lastPosition = d_ptr->position;
 
  105    d_ptr->changingPosition = 
true;
 
  106    d_ptr->timestamp.start();
 
  109void KineticModel::setHeading(qreal heading)
 
  111    d_ptr->heading = heading;
 
  113    int elapsed = d_ptr->timestamp.elapsed();
 
  114    qreal delta = 
static_cast<qreal
>(elapsed) / 1000.0;
 
  116    qreal lastSpeed = d_ptr->velocityHeading;
 
  117    qreal currentSpeed = delta ? (d_ptr->heading - d_ptr->lastHeading) / delta : 0;
 
  118    d_ptr->velocityHeading = 0.5 * lastSpeed + 0.2 * currentSpeed;
 
  119    d_ptr->lastHeading = d_ptr->heading;
 
  121    d_ptr->changingPosition = 
false;
 
  122    d_ptr->timestamp.start();
 
  125void KineticModel::jumpToPosition(
const QPointF &position)
 
  127    jumpToPosition(position.x(), position.y());
 
  130void KineticModel::jumpToPosition(qreal posX, qreal posY)
 
  132    d_ptr->position.setX(posX);
 
  133    d_ptr->position.setY(posY);
 
  136int KineticModel::updateInterval()
 const 
  138    return d_ptr->ticker.interval();
 
  141void KineticModel::setUpdateInterval(
int ms)
 
  143    d_ptr->ticker.setInterval(ms);
 
  146void KineticModel::stop()
 
  151    d->timestamp.start();
 
  152    d->velocity = QPointF(0, 0);
 
  153    d->velocityHeading = 0;
 
  156void KineticModel::start()
 
  161    const int elapsed = d->timestamp.elapsed();
 
  162    if (elapsed > 2 * d->ticker.interval()) {
 
  168    d->deacceleration = d->velocity * 1000 / (1 + d_ptr->duration);
 
  169    if (d->deacceleration.x() < 0) {
 
  170        d->deacceleration.setX(-d->deacceleration.x());
 
  172    if (d->deacceleration.y() < 0) {
 
  173        d->deacceleration.setY(-d->deacceleration.y());
 
  176    d->deaccelerationHeading = qAbs(d->velocityHeading) * 1000 / (1 + d_ptr->duration);
 
  178    if (!d->ticker.isActive())
 
  182void KineticModel::update()
 
  186    int elapsed = qMin(
static_cast<int>(d->timestamp.elapsed()), 100); 
 
  187    qreal delta = 
static_cast<qreal
>(elapsed) / 1000.0;
 
  190    if (d->changingPosition) {
 
  191        d->position += d->velocity * delta;
 
  192        QPointF vstep = d->deacceleration * delta;
 
  194        if (d->velocity.x() < vstep.
x() && d->velocity.x() >= -vstep.
x()) {
 
  197            if (d->velocity.x() > 0)
 
  198                d->velocity.setX(d->velocity.x() - vstep.
x());
 
  200                d->velocity.setX(d->velocity.x() + vstep.
x());
 
  203        if (d->velocity.y() < vstep.
y() && d->velocity.y() >= -vstep.
y()) {
 
  206            if (d->velocity.y() > 0)
 
  207                d->velocity.setY(d->velocity.y() - vstep.
y());
 
  209                d->velocity.setY(d->velocity.y() + vstep.
y());
 
  212        stop = d->velocity.isNull();
 
  214        Q_EMIT positionChanged(d->position.x(), d->position.y());
 
  216        d->heading += d->velocityHeading * delta;
 
  217        qreal vstep = d->deaccelerationHeading * delta; 
 
  218        if ((d->velocityHeading < vstep && d->velocityHeading >= -vstep) || !vstep) {
 
  219            d->velocityHeading = 0;
 
  221            d->velocityHeading += d->velocityHeading > 0 ? -1 * vstep : vstep;
 
  224        stop = !d->velocityHeading;
 
  226        Q_EMIT headingChanged(d->heading);
 
  234    d->timestamp.start();
 
  237#include "moc_kineticmodel.cpp" 
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)