1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtSensors module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | |
41 | #include "qtwistsensorgesturerecognizer.h" |
42 | |
43 | #include <QtCore/qmath.h> |
44 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | #define TIMER_TIMEOUT 750 |
48 | QTwistSensorGestureRecognizer::QTwistSensorGestureRecognizer(QObject *parent) |
49 | : QSensorGestureRecognizer(parent) |
50 | , orientationReading(0) |
51 | , active(0) |
52 | , detecting(0) |
53 | , checking(0) |
54 | , increaseCount(0) |
55 | , decreaseCount(0) |
56 | , lastAngle(0) |
57 | , detectedAngle(0) |
58 | { |
59 | } |
60 | |
61 | QTwistSensorGestureRecognizer::~QTwistSensorGestureRecognizer() |
62 | { |
63 | } |
64 | |
65 | void QTwistSensorGestureRecognizer::create() |
66 | { |
67 | } |
68 | |
69 | QString QTwistSensorGestureRecognizer::id() const |
70 | { |
71 | return QString("QtSensors.twist" ); |
72 | } |
73 | |
74 | bool QTwistSensorGestureRecognizer::start() |
75 | { |
76 | if (QtSensorGestureSensorHandler::instance()->startSensor(sensor: QtSensorGestureSensorHandler::Accel)) { |
77 | if (QtSensorGestureSensorHandler::instance()->startSensor(sensor: QtSensorGestureSensorHandler::Orientation)) { |
78 | active = true; |
79 | connect(sender: QtSensorGestureSensorHandler::instance(),SIGNAL(orientationReadingChanged(QOrientationReading*)), |
80 | receiver: this,SLOT(orientationReadingChanged(QOrientationReading*))); |
81 | |
82 | connect(sender: QtSensorGestureSensorHandler::instance(),SIGNAL(accelReadingChanged(QAccelerometerReading*)), |
83 | receiver: this,SLOT(accelChanged(QAccelerometerReading*))); |
84 | } else { |
85 | QtSensorGestureSensorHandler::instance()->stopSensor(sensor: QtSensorGestureSensorHandler::Accel); |
86 | active = false; |
87 | } |
88 | } else { |
89 | |
90 | active = false; |
91 | } |
92 | |
93 | return active; |
94 | } |
95 | |
96 | bool QTwistSensorGestureRecognizer::stop() |
97 | { |
98 | QtSensorGestureSensorHandler::instance()->stopSensor(sensor: QtSensorGestureSensorHandler::Accel); |
99 | QtSensorGestureSensorHandler::instance()->stopSensor(sensor: QtSensorGestureSensorHandler::Orientation); |
100 | disconnect(sender: QtSensorGestureSensorHandler::instance(),SIGNAL(orientationReadingChanged(QOrientationReading*)), |
101 | receiver: this,SLOT(orientationReadingChanged(QOrientationReading*))); |
102 | |
103 | disconnect(sender: QtSensorGestureSensorHandler::instance(),SIGNAL(accelReadingChanged(QAccelerometerReading*)), |
104 | receiver: this,SLOT(accelChanged(QAccelerometerReading*))); |
105 | |
106 | reset(); |
107 | orientationList.clear(); |
108 | active = false; |
109 | return active; |
110 | } |
111 | |
112 | bool QTwistSensorGestureRecognizer::isActive() |
113 | { |
114 | return active; |
115 | } |
116 | |
117 | void QTwistSensorGestureRecognizer::orientationReadingChanged(QOrientationReading *reading) |
118 | { |
119 | orientationReading = reading; |
120 | if (orientationList.count() == 3) |
121 | orientationList.removeFirst(); |
122 | |
123 | orientationList.append(t: reading->orientation()); |
124 | |
125 | if (orientationList.count() == 3 |
126 | && orientationList.at(i: 2) == QOrientationReading::FaceUp |
127 | && (orientationList.at(i: 1) == QOrientationReading::RightUp |
128 | || orientationList.at(i: 1) == QOrientationReading::LeftUp)) { |
129 | checkTwist(); |
130 | } |
131 | |
132 | checkOrientation(); |
133 | } |
134 | |
135 | bool QTwistSensorGestureRecognizer::checkOrientation() |
136 | { |
137 | if (orientationReading->orientation() == QOrientationReading::TopDown |
138 | || orientationReading->orientation() == QOrientationReading::FaceDown) { |
139 | reset(); |
140 | return false; |
141 | } |
142 | return true; |
143 | } |
144 | |
145 | void QTwistSensorGestureRecognizer::accelChanged(QAccelerometerReading *reading) |
146 | { |
147 | if (orientationReading == 0) |
148 | return; |
149 | |
150 | const qreal x = reading->x(); |
151 | const qreal y = reading->y(); |
152 | const qreal z = reading->z(); |
153 | |
154 | if (!detecting && !checking&& dataList.count() > 21) |
155 | dataList.removeFirst(); |
156 | |
157 | qreal angle = qRadiansToDegrees(radians: qAtan(v: x / qSqrt(v: y * y + z * z))); |
158 | |
159 | if (qAbs(t: angle) > 2) { |
160 | if (detecting) { |
161 | if ((angle > 0 && angle < lastAngle) |
162 | || (angle < 0 && angle > lastAngle)) { |
163 | decreaseCount++; |
164 | } else { |
165 | if (decreaseCount > 0) |
166 | decreaseCount--; |
167 | } |
168 | } |
169 | |
170 | if (!detecting && ((angle > 0 && angle > lastAngle) |
171 | || (angle < 0 && angle < lastAngle)) |
172 | && ((angle > 0 && lastAngle > 0) |
173 | || (angle < 0 && lastAngle < 0))) { |
174 | increaseCount++; |
175 | } else |
176 | if (!detecting && increaseCount > 3 && qAbs(t: angle) > 30) { |
177 | decreaseCount = 0; |
178 | detecting = true; |
179 | detectedAngle = qRadiansToDegrees(radians: qAtan(v: y / qSqrt(v: x * x + z * z))); |
180 | } |
181 | } else { |
182 | increaseCount = 0; |
183 | increaseCount = 0; |
184 | } |
185 | |
186 | lastAngle = angle; |
187 | if (detecting && decreaseCount >= 4 && qAbs(t: angle) < 25) { |
188 | checkTwist(); |
189 | } |
190 | |
191 | twistAccelData data; |
192 | data.x = x; |
193 | data.y = y; |
194 | data.z = z; |
195 | |
196 | if (qAbs(t: x) > 1) |
197 | dataList.append(t: data); |
198 | |
199 | if (qAbs(t: z) > 15.0) { |
200 | reset(); |
201 | } |
202 | |
203 | } |
204 | |
205 | void QTwistSensorGestureRecognizer::checkTwist() |
206 | { |
207 | checking = true; |
208 | int lastx = 0; |
209 | bool ok = false; |
210 | bool spinpoint = false; |
211 | |
212 | if (detectedAngle < 0) { |
213 | reset(); |
214 | return; |
215 | } |
216 | |
217 | //// check for orientation changes first |
218 | if (orientationList.count() < 2) |
219 | return; |
220 | |
221 | if (orientationList.count() > 2) |
222 | if (orientationList.at(i: 0) == orientationList.at(i: 2) |
223 | && (orientationList.at(i: 1) == QOrientationReading::LeftUp |
224 | || orientationList.at(i: 1) == QOrientationReading::RightUp)) { |
225 | ok = true; |
226 | if (orientationList.at(i: 1) == QOrientationReading::RightUp) |
227 | detectedAngle = 1; |
228 | else |
229 | detectedAngle = -1; |
230 | } |
231 | |
232 | // now the manual increase/decrease count |
233 | if (!ok) { |
234 | if (increaseCount < 1 || decreaseCount < 3) |
235 | return; |
236 | |
237 | if (increaseCount > 6 && decreaseCount > 4) { |
238 | ok = true; |
239 | if (orientationList.at(i: 1) == QOrientationReading::RightUp) |
240 | detectedAngle = 1; |
241 | else |
242 | detectedAngle = -1; |
243 | } |
244 | } |
245 | // now we're really grasping for anything |
246 | if (!ok) |
247 | for (int i = 0; i < dataList.count(); i++) { |
248 | twistAccelData curData = dataList.at(i); |
249 | if (!spinpoint && qAbs(t: curData.x) < 1) |
250 | continue; |
251 | if (curData.z >= 0 ) { |
252 | if (!spinpoint && (curData.x > lastx || curData.x < lastx) && curData.x - lastx > 1) { |
253 | ok = true; |
254 | } else if (spinpoint && (curData.x < lastx || curData.x > lastx)&& lastx - curData.x > 1) { |
255 | ok = true; |
256 | } else { |
257 | ok = false; |
258 | } |
259 | } else if (!spinpoint && curData.z < 0) { |
260 | spinpoint = true; |
261 | } else if (spinpoint && curData.z > 9) { |
262 | break; |
263 | } |
264 | |
265 | lastx = curData.x; |
266 | } |
267 | if (ok) { |
268 | if (detectedAngle > 0) { |
269 | Q_EMIT twistLeft(); |
270 | Q_EMIT detected("twistLeft" ); |
271 | } else { |
272 | Q_EMIT twistRight(); |
273 | Q_EMIT detected("twistRight" ); |
274 | } |
275 | } |
276 | reset(); |
277 | } |
278 | |
279 | void QTwistSensorGestureRecognizer::reset() |
280 | { |
281 | detecting = false; |
282 | checking = false; |
283 | dataList.clear(); |
284 | increaseCount = 0; |
285 | decreaseCount = 0; |
286 | lastAngle = 0; |
287 | } |
288 | |
289 | |
290 | |
291 | QT_END_NAMESPACE |
292 | |