Marble

kineticmodel.cpp
1 /*
2  This file is part of the Ofi Labs X2 project.
3 
4  SPDX-FileCopyrightText: 2010 Ariya Hidayat <[email protected]>
5 
6  SPDX-License-Identifier: BSD-3-Clause
7 */
8 
9 #include "kineticmodel.h"
10 
11 #include <QTimer>
12 #include <QElapsedTimer>
13 
14 static const int KineticModelDefaultUpdateInterval = 15; // ms
15 
16 class KineticModelPrivate
17 {
18 public:
19  QTimer ticker;
20 
21  int duration;
22  QPointF position;
23  qreal heading;
24  QPointF velocity;
25  qreal velocityHeading;
26  QPointF deacceleration;
27  qreal deaccelerationHeading;
28 
29  QElapsedTimer timestamp;
30  QPointF lastPosition;
31  qreal lastHeading;
32  bool changingPosition;
33 
34  KineticModelPrivate();
35 };
36 
37 KineticModelPrivate::KineticModelPrivate()
38  : duration(1403)
39  , position(0, 0)
40  , heading(0)
41  , velocity(0, 0)
42  , velocityHeading(0)
43  , deacceleration(0, 0)
44  , deaccelerationHeading(0)
45  , lastPosition(0, 0)
46  , lastHeading(0)
47  , changingPosition(true)
48 {
49 
50 }
51 
52 KineticModel::KineticModel(QObject *parent)
53  : QObject(parent)
54  , d_ptr(new KineticModelPrivate)
55 {
56  connect(&d_ptr->ticker, SIGNAL(timeout()), SLOT(update()));
57  d_ptr->ticker.setInterval(KineticModelDefaultUpdateInterval);
58 }
59 
60 KineticModel::~KineticModel()
61 {
62 
63 }
64 
65 bool KineticModel::hasVelocity() const
66 {
67  return !d_ptr->velocity.isNull();
68 }
69 
70 int KineticModel::duration() const
71 {
72  return d_ptr->duration;
73 }
74 
75 void KineticModel::setDuration(int ms)
76 {
77  d_ptr->duration = ms;
78 }
79 
80 QPointF KineticModel::position() const
81 {
82  return d_ptr->position;
83 }
84 
85 void KineticModel::setPosition(const QPointF& position)
86 {
87  setPosition( position.x(), position.y() );
88 }
89 
90 void KineticModel::setPosition(qreal posX, qreal posY)
91 {
92  d_ptr->position.setX( posX );
93  d_ptr->position.setY( posY );
94 
95  int elapsed = d_ptr->timestamp.elapsed();
96 
97  // too fast gives less accuracy
98  if (elapsed < d_ptr->ticker.interval() / 2) {
99  return;
100  }
101 
102  qreal delta = static_cast<qreal>( elapsed ) / 1000.0;
103 
104  QPointF lastSpeed = d_ptr->velocity;
105  QPointF currentSpeed = ( d_ptr->position - d_ptr->lastPosition ) / delta;
106  d_ptr->velocity = 0.2 * lastSpeed + 0.8 * currentSpeed;
107  d_ptr->lastPosition = d_ptr->position;
108 
109  d_ptr->changingPosition = true;
110  d_ptr->timestamp.start();
111 }
112 
113 void KineticModel::setHeading(qreal heading)
114 {
115  d_ptr->heading = heading;
116 
117  int elapsed = d_ptr->timestamp.elapsed();
118  qreal delta = static_cast<qreal>( elapsed ) / 1000.0;
119 
120  qreal lastSpeed = d_ptr->velocityHeading;
121  qreal currentSpeed = delta ? ( d_ptr->heading - d_ptr->lastHeading ) / delta : 0;
122  d_ptr->velocityHeading = 0.5 * lastSpeed + 0.2 * currentSpeed;
123  d_ptr->lastHeading = d_ptr->heading;
124 
125  d_ptr->changingPosition = false;
126  d_ptr->timestamp.start();
127 }
128 
129 void KineticModel::jumpToPosition(const QPointF& position)
130 {
131  jumpToPosition( position.x(), position.y() );
132 }
133 
134 void KineticModel::jumpToPosition(qreal posX, qreal posY)
135 {
136  d_ptr->position.setX( posX );
137  d_ptr->position.setY( posY );
138 }
139 
140 int KineticModel::updateInterval() const
141 {
142  return d_ptr->ticker.interval();
143 }
144 
145 void KineticModel::setUpdateInterval(int ms)
146 {
147  d_ptr->ticker.setInterval(ms);
148 }
149 
150 void KineticModel::stop()
151 {
152  Q_D(KineticModel);
153 
154  d->ticker.stop();
155  d->timestamp.start();
156  d->velocity = QPointF(0, 0);
157  d->velocityHeading = 0;
158 }
159 
160 void KineticModel::start()
161 {
162  Q_D(KineticModel);
163 
164  // prevent kinetic spinning if last mouse move is too long ago
165  const int elapsed = d->timestamp.elapsed();
166  if ( elapsed > 2 * d->ticker.interval() ) {
167  d->ticker.stop();
168  emit finished();
169  return;
170  }
171 
172  d->deacceleration = d->velocity * 1000 / ( 1 + d_ptr->duration );
173  if (d->deacceleration.x() < 0) {
174  d->deacceleration.setX( -d->deacceleration.x() );
175  }
176  if (d->deacceleration.y() < 0) {
177  d->deacceleration.setY( -d->deacceleration.y() );
178  }
179 
180  d->deaccelerationHeading = qAbs(d->velocityHeading) * 1000 / ( 1 + d_ptr->duration );
181 
182  if (!d->ticker.isActive())
183  d->ticker.start();
184 }
185 
186 void KineticModel::update()
187 {
188  Q_D(KineticModel);
189 
190  int elapsed = qMin( static_cast<int>(d->timestamp.elapsed()), 100 ); // limit to 100msec to reduce catapult effect (bug 294608)
191  qreal delta = static_cast<qreal>(elapsed) / 1000.0;
192 
193  bool stop = false;
194  if (d->changingPosition) {
195  d->position += d->velocity * delta;
196  QPointF vstep = d->deacceleration * delta;
197 
198  if (d->velocity.x() < vstep.x() && d->velocity.x() >= -vstep.x()) {
199  d->velocity.setX( 0 );
200  } else {
201  if (d->velocity.x() > 0)
202  d->velocity.setX( d->velocity.x() - vstep.x() );
203  else
204  d->velocity.setX( d->velocity.x() + vstep.x() );
205  }
206 
207  if (d->velocity.y() < vstep.y() && d->velocity.y() >= -vstep.y()) {
208  d->velocity.setY( 0 );
209  } else {
210  if (d->velocity.y() > 0)
211  d->velocity.setY( d->velocity.y() - vstep.y() );
212  else
213  d->velocity.setY( d->velocity.y() + vstep.y() );
214  }
215 
216  stop = d->velocity.isNull();
217 
218  emit positionChanged( d->position.x(), d->position.y() );
219  } else {
220  d->heading += d->velocityHeading * delta;
221  qreal vstep = d->deaccelerationHeading * delta; // Always positive.
222  if ((d->velocityHeading < vstep && d->velocityHeading >= -vstep) || !vstep) {
223  d->velocityHeading = 0;
224  } else {
225  d->velocityHeading += d->velocityHeading > 0 ? -1 * vstep : vstep;
226  }
227 
228  stop = !d->velocityHeading;
229 
230  emit headingChanged( d->heading );
231  }
232 
233  if (stop) {
234  emit finished();
235  d->ticker.stop();
236  }
237 
238  d->timestamp.start();
239 }
240 
241 #include "moc_kineticmodel.cpp"
242 
void stop(Ekos::AlignState mode)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Q_SCRIPTABLE Q_NOREPLY void start()
void setX(qreal x)
qreal x() const const
qreal y() const const
void update(Part *part, const QByteArray &data, qint64 dataSize)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Sep 25 2023 03:50:19 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.