summaryrefslogtreecommitdiff
path: root/src/grail-frame.c
diff options
context:
space:
mode:
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}