summaryrefslogtreecommitdiff
path: root/src/grail-frame.c
diff options
context:
space:
mode:
authorHenrik Rydberg <rydberg@euromail.se>2011-04-28 18:42:33 +0200
committerHenrik Rydberg <rydberg@euromail.se>2011-04-28 18:42:33 +0200
commit5423ccd321c1c84b0e7736b10b2fe9eeb1833e16 (patch)
treefc12f236a865dceb6008d9bc5691b0fd0cbe582b /src/grail-frame.c
parentd87cde46e25c058ffc76d3871ca4e2ceb3e57991 (diff)
Introduce gesture frames
This patch extends the API with parallel new/delete functions, aiming to eventually replace the open/close function. The new functions give access to the grail gesture frames, containing gestural transform information. This information is useful in its own right, and will eventually replace the internal recognizer. Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
Diffstat (limited to 'src/grail-frame.c')
-rw-r--r--src/grail-frame.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/src/grail-frame.c b/src/grail-frame.c
new file mode 100644
index 0000000..cf38452
--- /dev/null
+++ b/src/grail-frame.c
@@ -0,0 +1,399 @@
1/*****************************************************************************
2 *
3 * grail - Gesture Recognition And Instantiation Library
4 *
5 * Copyright (C) 2010 Canonical Ltd.
6 * Copyright (C) 2010 Henrik Rydberg <rydberg@bitmath.org>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 ****************************************************************************/
22
23#include "grail-impl.h"
24#include <stdlib.h>
25#include <string.h>
26#include <math.h>
27
28static void set_center_velocity_and_radius(struct grail_impl *impl,
29 struct grail_element *slot)
30{
31 const struct utouch_contact **tc = slot->touches;
32 double x, y, vx, vy, r2, dx, dy;
33 int i;
34
35 switch (slot->num_touches) {
36 case 1:
37 x = tc[0]->x;
38 y = tc[0]->y;
39 vx = tc[0]->vx;
40 vy = tc[0]->vy;
41 r2 = 0;
42 break;
43 case 2:
44 dx = 0.5 * (tc[1]->x - tc[0]->x);
45 dy = 0.5 * (tc[1]->y - tc[0]->y);
46 x = tc[0]->x + dx;
47 y = tc[0]->y + dy;
48 vx = 0.5 * (tc[0]->vx + tc[1]->vx);
49 vy = 0.5 * (tc[0]->vy + tc[1]->vy);
50 r2 = dx * dx + dy * dy;
51 break;
52 default:
53 x = y = vx = vy = r2 = 0;
54 for (i = 0; i < slot->num_touches; i++) {
55 x += tc[i]->x;
56 y += tc[i]->y;
57 vx += tc[i]->vx;
58 vy += tc[i]->vy;
59 }
60 x /= slot->num_touches;
61 y /= slot->num_touches;
62 vx /= slot->num_touches;
63 vy /= slot->num_touches;
64 for (i = 0; i < slot->num_touches; i++) {
65 dx = tc[i]->x - x;
66 dy = tc[i]->y - y;
67 r2 += dx * dx + dy * dy;
68 }
69 r2 /= slot->num_touches;
70 break;
71 }
72
73 slot->center.x = x;
74 slot->center.y = y;
75 slot->velocity.x = 1000 * vx;
76 slot->velocity.y = 1000 * vy;
77 slot->radius2 = r2;
78}
79
80static void set_moveness_pivot_and_drag(struct grail_impl *impl,
81 struct grail_element *slot,
82 double ds, double dc)
83{
84 const struct grail_control *ctl = impl->ctl;
85 const struct grail_element *pslot = slot->prev;
86 double mx = slot->center.x - pslot->center.x;
87 double my = slot->center.y - pslot->center.y;
88 float *T = slot->transform;
89
90 slot->moveness = 1;
91 slot->pivot = pslot->center;
92
93 if (slot->num_touches > 1) {
94 double wx = (1 - dc) * mx + ds * my;
95 double wy = (1 - dc) * my - ds * mx;
96 double w2 = wx * wx + wy * wy;
97 if (w2 > 0) {
98 double q = (mx * mx + my * my) / w2;
99 double s = ctl->pivot_unbound ?
100 q : sqrt(pslot->radius2 / w2);
101 if (s < q) {
102 slot->moveness = 1 - s / q;
103 slot->pivot.x += s * wx;
104 slot->pivot.y += s * wy;
105 } else {
106 slot->moveness = 0;
107 slot->pivot.x += q * wx;
108 slot->pivot.y += q * wy;
109 }
110 }
111 }
112
113 mx *= slot->moveness;
114 my *= slot->moveness;
115
116 T[0] = dc;
117 T[1] = ds;
118 T[2] = (1 - dc) * slot->pivot.x - ds * slot->pivot.y + mx;
119 T[3] = -ds;
120 T[4] = dc;
121 T[5] = (1 - dc) * slot->pivot.y + ds * slot->pivot.x + my;
122
123 slot->drag.x = pslot->drag.x + mx;
124 slot->drag.y = pslot->drag.y + my;
125}
126
127static void start_slot(struct grail_impl *impl,
128 struct grail_element *slot,
129 const struct utouch_frame *touch)
130{
131 float *T = slot->transform;
132
133 slot->id = impl->seqid++ & GRAIL_ID_MAX;
134 slot->expect_mask = GRAIL_EXPECT_MASK;
135 slot->active_mask = 0;
136 slot->start_time = touch->time;
137 set_center_velocity_and_radius(impl, slot);
138 slot->start_center = slot->center;
139 T[0] = T[4] = 1;
140 T[1] = T[2] = T[3] = T[5] = 0;
141 slot->pivot = slot->center;
142 slot->drag.x = 0;
143 slot->drag.y = 0;
144 slot->scale2 = 1;
145 slot->angle = 0;
146}
147
148static void update_slot(struct grail_impl *impl,
149 struct grail_element *slot,
150 double ds, double dc)
151{
152 const struct grail_element *pslot = slot->prev;
153
154 slot->id = pslot->id;
155 slot->start_time = pslot->start_time;
156 slot->start_center = pslot->start_center;
157 slot->expect_mask = pslot->expect_mask;
158 slot->active_mask = pslot->active_mask;
159
160 set_center_velocity_and_radius(impl, slot);
161 set_moveness_pivot_and_drag(impl, slot, ds, dc);
162
163 slot->scale2 = pslot->scale2 * (ds * ds + dc * dc);
164 slot->angle = pslot->angle + ds / dc; /* atan2(ds, dc) */
165}
166
167static void stop_slot(struct grail_impl *impl,
168 struct grail_element *slot)
169{
170 const struct grail_element *pslot = slot->prev;
171 float *T = slot->transform;
172
173 slot->id = -1;
174 slot->num_touches = 0;
175 slot->start_time = pslot->start_time;
176 slot->start_center = pslot->start_center;
177 slot->expect_mask = 0;
178 slot->active_mask = pslot->active_mask;
179 slot->center = pslot->center;
180 slot->velocity = pslot->velocity;
181 slot->radius2 = pslot->radius2;
182 T[0] = T[4] = 1;
183 T[1] = T[2] = T[3] = T[5] = 0;
184 slot->moveness = 1;
185 slot->pivot = pslot->pivot;
186 slot->drag = pslot->drag;
187 slot->scale2 = pslot->scale2;
188 slot->angle = pslot->angle;
189}
190
191static void set_slot_one(struct grail_impl *impl,
192 struct grail_element *slot,
193 const struct utouch_frame *touch,
194 const struct utouch_contact *t1)
195{
196 const struct grail_element *pslot = slot->prev;
197 const struct utouch_contact *p1 = pslot->touches[0];
198
199 if (!t1->active) {
200 stop_slot(impl, slot);
201 return;
202 }
203
204 slot->touches[0] = t1;
205 slot->num_touches = 1;
206
207 if (pslot->num_touches != slot->num_touches || t1->id != p1->id) {
208 start_slot(impl, slot, touch);
209 return;
210 }
211
212 update_slot(impl, slot, 0, 1);
213}
214
215static void set_slot_two(struct grail_impl *impl,
216 struct grail_element *slot,
217 const struct utouch_frame *touch,
218 const struct utouch_contact *t1,
219 const struct utouch_contact *t2)
220{
221 const struct grail_element *pslot = slot->prev;
222 const struct utouch_contact *p1 = pslot->touches[0];
223 const struct utouch_contact *p2 = pslot->touches[1];
224 double tx, ty, px, py, d2;
225
226 if (!t1->active || !t2->active) {
227 stop_slot(impl, slot);
228 return;
229 }
230
231 slot->touches[0] = t1;
232 slot->touches[1] = t2;
233 slot->num_touches = 2;
234
235 if (pslot->num_touches != slot->num_touches ||
236 t1->id != p1->id || t2->id != p2->id) {
237 start_slot(impl, slot, touch);
238 return;
239 }
240
241 tx = t2->x - t1->x;
242 ty = t2->y - t1->y;
243 px = p2->x - p1->x;
244 py = p2->y - p1->y;
245
246 d2 = px * px + py * py;
247 if (d2 > 0) {
248 px /= d2;
249 py /= d2;
250 }
251
252 update_slot(impl, slot, tx * py - ty * px, tx * px + ty * py);
253}
254
255static void set_slot_multi(struct grail_impl *impl,
256 struct grail_element *slot,
257 struct grail_frame *frame,
258 const struct utouch_frame *touch)
259{
260 const struct grail_element *pslot = slot->prev;
261 struct grail_element **slots = frame->slots;
262 int i, j, n = impl->num_touches;
263 struct grail_element *best = 0;
264
265 if (touch->num_active < 3) {
266 stop_slot(impl, slot);
267 return;
268 }
269
270 memcpy(slot->touches, touch->active,
271 touch->num_active * sizeof(slot->touches[0]));
272 slot->num_touches = touch->num_active;
273
274 if (pslot->num_touches != slot->num_touches) {
275 start_slot(impl, slot, touch);
276 return;
277 }
278
279 for (i = 0; i < slot->num_touches; i++) {
280 if (slot->touches[i]->id != pslot->touches[i]->id) {
281 start_slot(impl, slot, touch);
282 return;
283 }
284 }
285
286 for (i = 0; i < impl->num_touches; i++) {
287 for (j = i + 1; j < impl->num_touches; j++) {
288 struct grail_element *s = slots[n++];
289 if (!s->num_touches)
290 continue;
291 if (!best || s->radius2 > best->radius2)
292 best = s;
293 }
294 }
295
296 update_slot(impl, slot, best->transform[1], best->transform[0]);
297}
298
299static void set_slots(struct grail_impl *impl,
300 struct grail_frame *frame,
301 const struct utouch_frame *touch)
302{
303 struct grail_element **slots = frame->slots;
304 struct utouch_contact *const *tc = touch->slots;
305 int i, j, n = 0;
306
307 for (i = 0; i < impl->num_touches; i++)
308 set_slot_one(impl, slots[n++], touch, tc[i]);
309
310 for (i = 0; i < impl->num_touches; i++)
311 for (j = i + 1; j < impl->num_touches; j++)
312 set_slot_two(impl, slots[n++], touch, tc[i], tc[j]);
313
314 set_slot_multi(impl, slots[n++], frame, touch);
315}
316
317static void collect_transforms(struct grail_impl *impl,
318 struct grail_frame *frame,
319 const struct utouch_frame *touch)
320{
321 const struct utouch_surface *s = utouch_frame_get_surface(impl->fh);
322 const struct grail_control *ctl = impl->ctl;
323 float c_x = ctl->bar_center_x * (s->mapped_max_x - s->mapped_min_x);
324 float c_y = ctl->bar_center_y * (s->mapped_max_y - s->mapped_min_y);
325 float d_x = ctl->bar_drag_x * (s->mapped_max_x - s->mapped_min_x);
326 float d_y = ctl->bar_drag_y * (s->mapped_max_y - s->mapped_min_y);
327 float ds2 = ctl->bar_scale * ctl->bar_scale;
328 float dt;
329 int i;
330
331 for (i = 0; i < impl->num_slots; i++) {
332 struct grail_element *s = frame->slots[i];
333
334 if (!s->num_touches)
335 continue;
336
337 dt = touch->time - s->start_time;
338 if (dt > ctl->glue_ms) {
339 unsigned int mask = s->active_mask;
340
341 if (fabs(s->center.x - s->start_center.x) > c_x)
342 mask |= GRAIL_EXPECT_CENTER_X;
343 if (fabs(s->center.y - s->start_center.y) > c_y)
344 mask |= GRAIL_EXPECT_CENTER_Y;
345 if (fabs(s->drag.x) > d_x)
346 mask |= GRAIL_EXPECT_DRAG_X;
347 if (fabs(s->drag.y) > d_y)
348 mask |= GRAIL_EXPECT_DRAG_Y;
349 if (fabs(s->scale2 - 1) > ds2)
350 mask |= GRAIL_EXPECT_SCALE;
351 if (fabs(s->angle) > ctl->bar_angle)
352 mask |= GRAIL_EXPECT_ANGLE;
353
354 s->active_mask = mask;
355
356 if (dt < ctl->drop_x_ms)
357 mask |= GRAIL_EXPECT_CENTER_X;
358 if (dt < ctl->drop_y_ms)
359 mask |= GRAIL_EXPECT_CENTER_Y;
360 if (dt < ctl->drop_x_ms)
361 mask |= GRAIL_EXPECT_DRAG_X;
362 if (dt < ctl->drop_y_ms)
363 mask |= GRAIL_EXPECT_DRAG_Y;
364 if (dt < ctl->drop_scale_ms)
365 mask |= GRAIL_EXPECT_SCALE;
366 if (dt < ctl->drop_angle_ms)
367 mask |= GRAIL_EXPECT_ANGLE;
368
369 s->expect_mask &= mask;
370 }
371
372 frame->ongoing[frame->num_ongoing++] = s;
373 }
374}
375
376const struct grail_frame GRAIL_PUBLIC *
377grail_pump_frame(grail_handle ge, const struct utouch_frame *touch)
378{
379 struct grail_impl *impl = ge->impl;
380 struct grail_frame *frame = impl->frames[impl->nextframe];
381 const struct grail_frame *prev = frame->prev;
382 int i;
383
384 if (touch->slot_revision == touch->prev->slot_revision &&
385 !prev->num_ongoing)
386 return 0;
387
388 frame->touch = touch;
389 frame->num_ongoing = 0;
390 for (i = 0; i < impl->num_slots; i++)
391 frame->slots[i]->prev = prev->slots[i];
392
393 set_slots(impl, frame, touch);
394 collect_transforms(impl, frame, touch);
395
396 impl->nextframe = (impl->nextframe + 1) % impl->num_frames;
397
398 return frame;
399}