Marble

kineticmodel.cpp
1/*
2 This file is part of the Ofi Labs X2 project.
3
4 SPDX-FileCopyrightText: 2010 Ariya Hidayat <ariya.hidayat@gmail.com>
5
6 SPDX-License-Identifier: BSD-3-Clause
7*/
8
9#include "kineticmodel.h"
10
11#include <QTimer>
12#include <QElapsedTimer>
13
14static const int KineticModelDefaultUpdateInterval = 15; // ms
15
16class KineticModelPrivate
17{
18public:
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
37KineticModelPrivate::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
52KineticModel::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
60KineticModel::~KineticModel()
61{
62
63}
64
65bool KineticModel::hasVelocity() const
66{
67 return !d_ptr->velocity.isNull();
68}
69
70int KineticModel::duration() const
71{
72 return d_ptr->duration;
73}
74
75void KineticModel::setDuration(int ms)
76{
77 d_ptr->duration = ms;
78}
79
80QPointF KineticModel::position() const
81{
82 return d_ptr->position;
83}
84
85void KineticModel::setPosition(const QPointF& position)
86{
87 setPosition( position.x(), position.y() );
88}
89
90void 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
113void 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
129void KineticModel::jumpToPosition(const QPointF& position)
130{
131 jumpToPosition( position.x(), position.y() );
132}
133
134void KineticModel::jumpToPosition(qreal posX, qreal posY)
135{
136 d_ptr->position.setX( posX );
137 d_ptr->position.setY( posY );
138}
139
140int KineticModel::updateInterval() const
141{
142 return d_ptr->ticker.interval();
143}
144
145void KineticModel::setUpdateInterval(int ms)
146{
147 d_ptr->ticker.setInterval(ms);
148}
149
150void 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
160void 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
186void 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 update(Part *part, const QByteArray &data, qint64 dataSize)
KGuiItem stop()
T qobject_cast(QObject *object)
qreal x() const const
qreal y() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 14 2024 11:54:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.