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 <QElapsedTimer>
12#include <QTimer>
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
51KineticModel::KineticModel(QObject *parent)
52 : QObject(parent)
53 , d_ptr(new KineticModelPrivate)
54{
55 connect(&d_ptr->ticker, &QTimer::timeout, this, &KineticModel::update);
56 d_ptr->ticker.setInterval(KineticModelDefaultUpdateInterval);
57}
58
59KineticModel::~KineticModel() = default;
60
61bool KineticModel::hasVelocity() const
62{
63 return !d_ptr->velocity.isNull();
64}
65
66int KineticModel::duration() const
67{
68 return d_ptr->duration;
69}
70
71void KineticModel::setDuration(int ms)
72{
73 d_ptr->duration = ms;
74}
75
76QPointF KineticModel::position() const
77{
78 return d_ptr->position;
79}
80
81void KineticModel::setPosition(const QPointF &position)
82{
83 setPosition(position.x(), position.y());
84}
85
86void KineticModel::setPosition(qreal posX, qreal posY)
87{
88 d_ptr->position.setX(posX);
89 d_ptr->position.setY(posY);
90
91 int elapsed = d_ptr->timestamp.elapsed();
92
93 // too fast gives less accuracy
94 if (elapsed < d_ptr->ticker.interval() / 2) {
95 return;
96 }
97
98 qreal delta = static_cast<qreal>(elapsed) / 1000.0;
99
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;
104
105 d_ptr->changingPosition = true;
106 d_ptr->timestamp.start();
107}
108
109void KineticModel::setHeading(qreal heading)
110{
111 d_ptr->heading = heading;
112
113 int elapsed = d_ptr->timestamp.elapsed();
114 qreal delta = static_cast<qreal>(elapsed) / 1000.0;
115
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;
120
121 d_ptr->changingPosition = false;
122 d_ptr->timestamp.start();
123}
124
125void KineticModel::jumpToPosition(const QPointF &position)
126{
127 jumpToPosition(position.x(), position.y());
128}
129
130void KineticModel::jumpToPosition(qreal posX, qreal posY)
131{
132 d_ptr->position.setX(posX);
133 d_ptr->position.setY(posY);
134}
135
136int KineticModel::updateInterval() const
137{
138 return d_ptr->ticker.interval();
139}
140
141void KineticModel::setUpdateInterval(int ms)
142{
143 d_ptr->ticker.setInterval(ms);
144}
145
146void KineticModel::stop()
147{
148 Q_D(KineticModel);
149
150 d->ticker.stop();
151 d->timestamp.start();
152 d->velocity = QPointF(0, 0);
153 d->velocityHeading = 0;
154}
155
156void KineticModel::start()
157{
158 Q_D(KineticModel);
159
160 // prevent kinetic spinning if last mouse move is too long ago
161 const int elapsed = d->timestamp.elapsed();
162 if (elapsed > 2 * d->ticker.interval()) {
163 d->ticker.stop();
164 Q_EMIT finished();
165 return;
166 }
167
168 d->deacceleration = d->velocity * 1000 / (1 + d_ptr->duration);
169 if (d->deacceleration.x() < 0) {
170 d->deacceleration.setX(-d->deacceleration.x());
171 }
172 if (d->deacceleration.y() < 0) {
173 d->deacceleration.setY(-d->deacceleration.y());
174 }
175
176 d->deaccelerationHeading = qAbs(d->velocityHeading) * 1000 / (1 + d_ptr->duration);
177
178 if (!d->ticker.isActive())
179 d->ticker.start();
180}
181
182void KineticModel::update()
183{
184 Q_D(KineticModel);
185
186 int elapsed = qMin(static_cast<int>(d->timestamp.elapsed()), 100); // limit to 100msec to reduce catapult effect (bug 294608)
187 qreal delta = static_cast<qreal>(elapsed) / 1000.0;
188
189 bool stop = false;
190 if (d->changingPosition) {
191 d->position += d->velocity * delta;
192 QPointF vstep = d->deacceleration * delta;
193
194 if (d->velocity.x() < vstep.x() && d->velocity.x() >= -vstep.x()) {
195 d->velocity.setX(0);
196 } else {
197 if (d->velocity.x() > 0)
198 d->velocity.setX(d->velocity.x() - vstep.x());
199 else
200 d->velocity.setX(d->velocity.x() + vstep.x());
201 }
202
203 if (d->velocity.y() < vstep.y() && d->velocity.y() >= -vstep.y()) {
204 d->velocity.setY(0);
205 } else {
206 if (d->velocity.y() > 0)
207 d->velocity.setY(d->velocity.y() - vstep.y());
208 else
209 d->velocity.setY(d->velocity.y() + vstep.y());
210 }
211
212 stop = d->velocity.isNull();
213
214 Q_EMIT positionChanged(d->position.x(), d->position.y());
215 } else {
216 d->heading += d->velocityHeading * delta;
217 qreal vstep = d->deaccelerationHeading * delta; // Always positive.
218 if ((d->velocityHeading < vstep && d->velocityHeading >= -vstep) || !vstep) {
219 d->velocityHeading = 0;
220 } else {
221 d->velocityHeading += d->velocityHeading > 0 ? -1 * vstep : vstep;
222 }
223
224 stop = !d->velocityHeading;
225
226 Q_EMIT headingChanged(d->heading);
227 }
228
229 if (stop) {
230 Q_EMIT finished();
231 d->ticker.stop();
232 }
233
234 d->timestamp.start();
235}
236
237#include "moc_kineticmodel.cpp"
KGuiItem stop()
Q_EMITQ_EMIT
qreal x() const const
qreal y() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.