93673185fea0db1851eb44347e36451efe196228
[platform/core/api/maps-service.git] / src / view / inertial_gesture.cpp
1 /* Copyright (c) 2010-2014 Samsung Electronics Co., Ltd. All rights reserved.
2  *
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include "inertial_gesture.h"
19 #include "maps_util.h"
20 #include <glib.h>
21 #include "gesture_detector_statemachine.h"
22 #include <unistd.h>
23 #include <time.h>
24
25 extern void _maps_view_set_idle_listener(maps_view_h view,
26                                         void (*callback)(void *user_data),
27                                         void *user_data);
28
29 extern void _maps_view_halt_inertial_camera(maps_view_h view);
30
31 view::inertial_gesture::inertial_gesture(maps_view_h view)
32         : gesture_detector(view)
33           , _d(NULL)
34           , transiting(false)
35           , transiting_trajectory(false)
36 {
37         reset();
38
39         _d = new gesture_detector_statemachine(view);
40
41         //_maps_view_set_idle_listener(view, on_idle, this);
42 }
43
44 view::inertial_gesture::~inertial_gesture()
45 {
46         //_maps_view_set_idle_listener(_view, NULL, NULL);
47
48         if (_d)
49                 delete _d;
50         _d = NULL;
51 }
52
53 view::inertial_gesture::inertial_gesture(const inertial_gesture &src)
54         : gesture_detector(NULL)
55           , _d(NULL)
56           , transiting(false)
57           , transiting_trajectory(false)
58 {
59 }
60
61 view::inertial_gesture &view::inertial_gesture::operator=(const inertial_gesture &src)
62 {
63         return *this;
64 }
65
66 void view::inertial_gesture::tap(int finger_no, const touch_point &tp)
67 {
68         MAPS_LOGI("TRANSITION finger %d tap time: %d",
69                   finger_no, tp._timestamp);
70
71         _maps_view_halt_inertial_camera(_view);
72
73         if (transiting) { /* Halt the transition */
74                 for (int i = 0; i < MAX_FINGERS; i ++) {
75                         if (!transiting_part[i])
76                                 continue;
77
78                         unsigned int timestamp = _last[i]._timestamp + get_transition_time(i);
79
80                         const touch_point tp(_cur_x[i], _cur_y[i], timestamp);
81                         MAPS_LOGI("TRANSITION finger %d up FAKE BRAKE time: %d", i, tp._timestamp);
82                         _d->up(i, tp);
83                 }
84
85                 _d->halt_gesture();
86                 reset();
87         }
88
89         _down[finger_no] = tp;
90         _last[finger_no] = tp;
91         _prev[finger_no] = tp;
92         _cur_x[finger_no] = tp._x;
93         _cur_y[finger_no] = tp._y;
94         _inertial_start[finger_no] = tp;
95
96         if (_d)
97                 _d->tap(finger_no, tp);
98 }
99
100 void view::inertial_gesture::move(int finger_no, const touch_point &tp)
101 {
102         MAPS_LOGI("TRANSITION finger %d move", finger_no);
103
104         _maps_view_halt_inertial_camera(_view);
105         update_inertial_start_point(finger_no);
106
107         _prev[finger_no] = _last[finger_no];
108         _last[finger_no] = tp;
109         _cur_x[finger_no] = tp._x;
110         _cur_y[finger_no] = tp._y;
111
112         /* check if moved enough as much as effective length */
113         int trajectory = get_trajectory_effective_length(_down[finger_no], tp);
114         MAPS_LOGD("trajectory=%d", trajectory);
115         if (trajectory >= __CLICK_AREA)
116                 transiting_trajectory = true;
117
118         if (_d)
119                 _d->move(finger_no, tp);
120 }
121
122 void view::inertial_gesture::up(int finger_no, const touch_point &tp)
123 {
124         MAPS_LOGI("TRANSITION finger %d up time: %d", finger_no, tp._timestamp);
125
126         _last[finger_no] = tp;
127
128         const int delta_x = _last[finger_no]._x - _inertial_start[finger_no]._x;
129         const int delta_y = _last[finger_no]._y - _inertial_start[finger_no]._y;
130         unsigned int dt = _last[finger_no]._timestamp - _inertial_start[finger_no]._timestamp;
131
132         /* check if moved enough as much as effective length */
133         int trajectory_total = get_trajectory_effective_length(_down[finger_no], tp);
134         int trajectory_last  = get_trajectory_effective_length(_prev[finger_no], tp);
135
136         /* check minimal size of click-area to be recognized as a valid gesture */
137         if (trajectory_total >= __CLICK_AREA)
138                 transiting_trajectory = true;
139
140         /* to prevent inertial movement even by tapping gesture */
141         if (trajectory_last <= __CLICK_AREA / 3.)
142                 transiting_trajectory = false;
143
144         if (dt == 0 || !transiting_trajectory) {
145                 _derivative_x[finger_no] = .0;
146                 _derivative_y[finger_no] = .0;
147         } else {
148                 _derivative_x[finger_no] = 10. * (delta_x) / dt;
149                 _derivative_y[finger_no] = 10. * (delta_y) / dt;
150         }
151         _dt[finger_no] = MIN(1.*dt, 1.);
152
153         /* Continue "move" with negative acceleration */
154         transiting_part[finger_no] = true;
155         transiting_start[finger_no] = get_cur_time();
156         transiting = true;
157 }
158
159 bool view::inertial_gesture::next_transition_step()
160 {
161         MAPS_LOGI("TRANSITION get next transition step");
162         transiting = false;
163
164         //static const double dt = 1.;
165         for (int i = 0; i < MAX_FINGERS; i ++) {
166                 if (!transiting_part[i])
167                         continue;
168
169                 transiting_part[i] = false;
170
171                 if ((ABS(_derivative_x[i]) > __ACCURACY) || (ABS(_derivative_y[i]) > __ACCURACY)) {
172                         _cur_x[i] = get_next_point(_cur_x[i], _derivative_x[i], _dt[i]);
173                         _derivative_x[i] = get_next_derivative(_derivative_x[i], _dt[i]);
174                         transiting_part[i] |= ABS(_derivative_x[i]) > __ACCURACY;
175
176                         _cur_y[i] = get_next_point(_cur_y[i], _derivative_y[i], _dt[i]);
177                         _derivative_y[i] = get_next_derivative(_derivative_y[i], _dt[i]);
178                         transiting_part[i] |= ABS(_derivative_y[i]) > __ACCURACY;
179                 }
180
181                 unsigned int timestamp = _last[i]._timestamp + get_transition_time(i);
182
183                 const touch_point tp(_cur_x[i], _cur_y[i], timestamp);
184                 if (transiting_part[i]) {
185                         /* MAPS_LOGI("TRANSITION finger %d move FAKE time: %d", i, tp._timestamp); */
186                         _d->move(i, tp);
187                 } else {
188                         /* MAPS_LOGI("TRANSITION finger %d up FAKE time: %d", i, tp._timestamp); */
189                         _d->up(i, tp);
190                 }
191
192                 transiting = true; /* There were changes, so may be one more
193                                       step in transient is needed */
194         }
195
196         if (!transiting)
197                 reset();
198
199         return transiting;
200 }
201
202 double view::inertial_gesture::get_next_point(const double &start,
203                                               const double &derivative,
204                                               const double &dt)
205 {
206         /* Simple square parabola */
207         return (start + derivative * dt);
208 }
209
210 double view::inertial_gesture::get_next_derivative(const double &derivative,
211                                                    const double &dt)
212 {
213         /* Simple square parabola */
214         /*if (derivative > 0)
215                 return (derivative + __ACCEL * dt);
216         else
217                 return (derivative - __ACCEL * dt);*/
218
219         /* Exponential spped down */
220         if (_d->_info._fingers_pressed <= 1)
221                 return (derivative * .9);
222         else
223                 return (derivative * .5);
224 }
225
226 unsigned int view::inertial_gesture::get_cur_time()
227 {
228         struct timespec ts;
229         unsigned int theTick = 0U;
230         clock_gettime(CLOCK_REALTIME, &ts);
231         theTick  = ts.tv_nsec / 1000000;
232         theTick += ts.tv_sec * 1000;
233         return theTick;
234 }
235
236 unsigned int view::inertial_gesture::get_transition_time(int finger_no) const
237 {
238         return get_cur_time() - transiting_start[finger_no];
239 }
240
241 /*
242 void view::inertial_gesture::on_idle(void *data)
243 {
244         inertial_gesture *ig = (inertial_gesture *)data;
245         if (ig && ig->transiting) {
246                 MAPS_LOGI("TRANSITION on idle");
247                 ig->next_transition_step();
248                 g_usleep(5*1000);
249         }
250 }
251 */
252
253 void view::inertial_gesture::reset()
254 {
255         transiting = false;
256         transiting_trajectory = false;
257         for (int i = 0; i < MAX_FINGERS; i ++) {
258                 transiting_part[i] = false;
259                 transiting_start[i] = 0;
260         }
261 }
262
263 void view::inertial_gesture::update_inertial_start_point(int finger_no)
264 {
265         const int margin = 3;
266         if (((_last[finger_no]._x > _prev[finger_no]._x + margin) && (_last[finger_no]._x < _inertial_start[finger_no]._x - margin)) ||
267                 ((_last[finger_no]._x < _prev[finger_no]._x - margin) && (_last[finger_no]._x > _inertial_start[finger_no]._x + margin)) ||
268                 ((_last[finger_no]._y > _prev[finger_no]._y + margin) && (_last[finger_no]._y < _inertial_start[finger_no]._y - margin)) ||
269                 ((_last[finger_no]._y < _prev[finger_no]._y - margin) && (_last[finger_no]._y > _inertial_start[finger_no]._y + margin))) {
270                 _inertial_start[finger_no] = _last[finger_no];
271                 MAPS_LOGD("reset inertial start (%d,%d)", _inertial_start[finger_no]._x, _inertial_start[finger_no]._y);
272         }
273 }
274