~chasedouglas/grail/comparison.centroid

« back to all changes in this revision

Viewing changes to src/grail-frame.c

  • Committer: Henrik Rydberg
  • Date: 2011-04-12 16:35:23 UTC
  • Revision ID: rydberg@bitmath.org-20110412163523-t66j4j7kfv69be1e
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>

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
28
static 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
 
 
80
static 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_element *pslot = slot->prev;
 
85
        double mx = slot->center.x - pslot->center.x;
 
86
        double my = slot->center.y - pslot->center.y;
 
87
        float *T = slot->transform;
 
88
 
 
89
        slot->moveness = 1;
 
90
        slot->pivot = pslot->center;
 
91
 
 
92
        if (slot->num_touches > 1) {
 
93
                double wx = (1 - dc) * mx + ds * my;
 
94
                double wy = (1 - dc) * my - ds * mx;
 
95
                double w2 = wx * wx + wy * wy;
 
96
                if (w2 > 0) {
 
97
                        double s = sqrt(pslot->radius2 / w2);
 
98
                        double q = (mx * mx + my * my) / w2;
 
99
                        if (s < q) {
 
100
                                slot->moveness = 1 - s / q;
 
101
                                slot->pivot.x += s * wx;
 
102
                                slot->pivot.y += s * wy;
 
103
                        } else {
 
104
                                slot->moveness = 0;
 
105
                                slot->pivot.x += q * wx;
 
106
                                slot->pivot.y += q * wy;
 
107
                        }
 
108
                }
 
109
        }
 
110
 
 
111
        mx *= slot->moveness;
 
112
        my *= slot->moveness;
 
113
 
 
114
        T[0] = dc;
 
115
        T[1] = ds;
 
116
        T[2] = (1 - dc) * slot->pivot.x - ds * slot->pivot.y + mx;
 
117
        T[3] = -ds;
 
118
        T[4] = dc;
 
119
        T[5] = (1 - dc) * slot->pivot.y + ds * slot->pivot.x + my;
 
120
 
 
121
        slot->drag.x = pslot->drag.x + mx;
 
122
        slot->drag.y = pslot->drag.y + my;
 
123
}
 
124
 
 
125
static void start_slot(struct grail_impl *impl,
 
126
                       struct grail_element *slot,
 
127
                       const struct utouch_frame *touch)
 
128
{
 
129
        float *T = slot->transform;
 
130
 
 
131
        slot->id = impl->seqid++ & GRAIL_ID_MAX;
 
132
        slot->expect_mask = GRAIL_EXPECT_MASK;
 
133
        slot->active_mask = 0;
 
134
        slot->start_time = touch->time;
 
135
        set_center_velocity_and_radius(impl, slot);
 
136
        T[0] = T[4] = 1;
 
137
        T[1] = T[2] = T[3] = T[5] = 0;
 
138
        slot->pivot = slot->center;
 
139
        slot->drag.x = 0;
 
140
        slot->drag.y = 0;
 
141
        slot->scale2 = 1;
 
142
        slot->angle = 0;
 
143
}
 
144
 
 
145
static void update_slot(struct grail_impl *impl,
 
146
                        struct grail_element *slot,
 
147
                        double ds, double dc)
 
148
{
 
149
        const struct grail_element *pslot = slot->prev;
 
150
 
 
151
        slot->id = pslot->id;
 
152
        slot->start_time = pslot->start_time;
 
153
        slot->expect_mask = pslot->expect_mask;
 
154
        slot->active_mask = pslot->active_mask;
 
155
 
 
156
        set_center_velocity_and_radius(impl, slot);
 
157
        set_moveness_pivot_and_drag(impl, slot, ds, dc);
 
158
 
 
159
        slot->scale2 = pslot->scale2 * (ds * ds + dc * dc);
 
160
        slot->angle = pslot->angle + ds / dc; /* atan2(ds, dc) */
 
161
}
 
162
 
 
163
static void stop_slot(struct grail_impl *impl,
 
164
                      struct grail_element *slot)
 
165
{
 
166
        const struct grail_element *pslot = slot->prev;
 
167
        float *T = slot->transform;
 
168
 
 
169
        slot->id = -1;
 
170
        slot->num_touches = 0;
 
171
        slot->start_time = pslot->start_time;
 
172
        slot->expect_mask = 0;
 
173
        slot->active_mask = pslot->active_mask;
 
174
        slot->center = pslot->center;
 
175
        slot->velocity = pslot->velocity;
 
176
        slot->radius2 = pslot->radius2;
 
177
        T[0] = T[4] = 1;
 
178
        T[1] = T[2] = T[3] = T[5] = 0;
 
179
        slot->moveness = 1;
 
180
        slot->pivot = pslot->pivot;
 
181
        slot->drag = pslot->drag;
 
182
        slot->scale2 = pslot->scale2;
 
183
        slot->angle = pslot->angle;
 
184
}
 
185
 
 
186
static void set_slot_one(struct grail_impl *impl,
 
187
                         struct grail_element *slot,
 
188
                         const struct utouch_frame *touch,
 
189
                         const struct utouch_contact *t1)
 
190
{
 
191
        const struct grail_element *pslot = slot->prev;
 
192
        const struct utouch_contact *p1 = pslot->touches[0];
 
193
 
 
194
        if (!t1->active) {
 
195
                stop_slot(impl, slot);
 
196
                return;
 
197
        }
 
198
 
 
199
        slot->touches[0] = t1;
 
200
        slot->num_touches = 1;
 
201
 
 
202
        if (pslot->num_touches != slot->num_touches || t1->id != p1->id) {
 
203
                start_slot(impl, slot, touch);
 
204
                return;
 
205
        }
 
206
 
 
207
        update_slot(impl, slot, 0, 1);
 
208
}
 
209
 
 
210
static void set_slot_two(struct grail_impl *impl,
 
211
                         struct grail_element *slot,
 
212
                         const struct utouch_frame *touch,
 
213
                         const struct utouch_contact *t1,
 
214
                         const struct utouch_contact *t2)
 
215
{
 
216
        const struct grail_element *pslot = slot->prev;
 
217
        const struct utouch_contact *p1 = pslot->touches[0];
 
218
        const struct utouch_contact *p2 = pslot->touches[1];
 
219
        double tx, ty, px, py, d2;
 
220
 
 
221
        if (!t1->active || !t2->active) {
 
222
                stop_slot(impl, slot);
 
223
                return;
 
224
        }
 
225
 
 
226
        slot->touches[0] = t1;
 
227
        slot->touches[1] = t2;
 
228
        slot->num_touches = 2;
 
229
 
 
230
        if (pslot->num_touches != slot->num_touches ||
 
231
            t1->id != p1->id || t2->id != p2->id) {
 
232
                start_slot(impl, slot, touch);
 
233
                return;
 
234
        }
 
235
 
 
236
        tx = t2->x - t1->x;
 
237
        ty = t2->y - t1->y;
 
238
        px = p2->x - p1->x;
 
239
        py = p2->y - p1->y;
 
240
 
 
241
        d2 = px * px + py * py;
 
242
        if (d2 > 0) {
 
243
                px /= d2;
 
244
                py /= d2;
 
245
        }
 
246
 
 
247
        update_slot(impl, slot, tx * py - ty * px, tx * px + ty * py);
 
248
}
 
249
 
 
250
static void set_slot_multi(struct grail_impl *impl,
 
251
                           struct grail_element *slot,
 
252
                           struct grail_frame *frame,
 
253
                           const struct utouch_frame *touch)
 
254
{
 
255
        const struct grail_element *pslot = slot->prev;
 
256
        struct grail_element **slots = frame->slots;
 
257
        int i, j, n = impl->num_touches;
 
258
        struct grail_element *best = 0;
 
259
 
 
260
        if (touch->num_active < 3) {
 
261
                stop_slot(impl, slot);
 
262
                return;
 
263
        }
 
264
 
 
265
        memcpy(slot->touches, touch->active,
 
266
               touch->num_active * sizeof(slot->touches[0]));
 
267
        slot->num_touches = touch->num_active;
 
268
 
 
269
        if (pslot->num_touches != slot->num_touches) {
 
270
                start_slot(impl, slot, touch);
 
271
                return;
 
272
        }
 
273
 
 
274
        for (i = 0; i < slot->num_touches; i++) {
 
275
                if (slot->touches[i]->id != pslot->touches[i]->id) {
 
276
                        start_slot(impl, slot, touch);
 
277
                        return;
 
278
                }
 
279
        }
 
280
 
 
281
        for (i = 0; i < impl->num_touches; i++) {
 
282
                for (j = i + 1; j < impl->num_touches; j++) {
 
283
                        struct grail_element *s = slots[n++];
 
284
                        if (!s->num_touches)
 
285
                                continue;
 
286
                        if (!best || s->radius2 > best->radius2)
 
287
                                best = s;
 
288
                }
 
289
        }
 
290
 
 
291
        update_slot(impl, slot, best->transform[1], best->transform[0]);
 
292
}
 
293
 
 
294
static void set_slots(struct grail_impl *impl,
 
295
                      struct grail_frame *frame,
 
296
                      const struct utouch_frame *touch)
 
297
{
 
298
        struct grail_element **slots = frame->slots;
 
299
        struct utouch_contact *const *tc = touch->slots;
 
300
        int i, j, n = 0;
 
301
 
 
302
        for (i = 0; i < impl->num_touches; i++)
 
303
                set_slot_one(impl, slots[n++], touch, tc[i]);
 
304
 
 
305
        for (i = 0; i < impl->num_touches; i++)
 
306
                for (j = i + 1; j < impl->num_touches; j++)
 
307
                        set_slot_two(impl, slots[n++], touch, tc[i], tc[j]);
 
308
 
 
309
        set_slot_multi(impl, slots[n++], frame, touch);
 
310
}
 
311
 
 
312
static void collect_transforms(struct grail_impl *impl,
 
313
                               struct grail_frame *frame,
 
314
                               const struct utouch_frame *touch)
 
315
{
 
316
        const struct utouch_surface *s = utouch_frame_get_surface(impl->fh);
 
317
        const struct grail_control *ctl = impl->ctl;
 
318
        float dx = ctl->bar_x * (s->mapped_max_x - s->mapped_min_x);
 
319
        float dy = ctl->bar_y * (s->mapped_max_y - s->mapped_min_y);
 
320
        float ds2 = ctl->bar_scale * ctl->bar_scale;
 
321
        float dt;
 
322
        int i;
 
323
 
 
324
        for (i = 0; i < impl->num_slots; i++) {
 
325
                struct grail_element *s = frame->slots[i];
 
326
 
 
327
                if (!s->num_touches)
 
328
                        continue;
 
329
 
 
330
                dt = touch->time - s->start_time;
 
331
                if (dt > ctl->glue_ms) {
 
332
                        unsigned int mask = s->active_mask;
 
333
 
 
334
                        if (s->moveness > ctl->thresh_drag) {
 
335
                                if (fabs(s->drag.x) > dx)
 
336
                                        mask |= GRAIL_EXPECT_X;
 
337
                                if (fabs(s->drag.y) > dy)
 
338
                                        mask |= GRAIL_EXPECT_Y;
 
339
                        }
 
340
                        if (s->moveness < ctl->thresh_scale) {
 
341
                                if (fabs(s->scale2 - 1) > ds2)
 
342
                                        mask |= GRAIL_EXPECT_SCALE;
 
343
                                if (fabs(s->angle) > ctl->bar_angle)
 
344
                                        mask |= GRAIL_EXPECT_ANGLE;
 
345
                        }
 
346
 
 
347
                        s->active_mask = mask;
 
348
 
 
349
                        if (dt < ctl->drop_x_ms)
 
350
                                mask |= GRAIL_EXPECT_X;
 
351
                        if (dt < ctl->drop_y_ms)
 
352
                                mask |= GRAIL_EXPECT_Y;
 
353
                        if (dt < ctl->drop_scale_ms)
 
354
                                mask |= GRAIL_EXPECT_SCALE;
 
355
                        if (dt < ctl->drop_angle_ms)
 
356
                                mask |= GRAIL_EXPECT_ANGLE;
 
357
 
 
358
                        s->expect_mask &= mask;
 
359
                }
 
360
 
 
361
                frame->ongoing[frame->num_ongoing++] = s;
 
362
        }
 
363
}
 
364
 
 
365
const struct grail_frame *grail_pump_frame(grail_handle ge,
 
366
                                            const struct utouch_frame *touch)
 
367
{
 
368
        struct grail_impl *impl = ge->impl;
 
369
        struct grail_frame *frame = impl->frames[impl->nextframe];
 
370
        const struct grail_frame *prev = frame->prev;
 
371
        int i;
 
372
 
 
373
        if (touch->slot_revision == touch->prev->slot_revision &&
 
374
            !prev->num_ongoing)
 
375
                return 0;
 
376
 
 
377
        frame->touch = touch;
 
378
        frame->num_ongoing = 0;
 
379
        for (i = 0; i < impl->num_slots; i++)
 
380
                frame->slots[i]->prev = prev->slots[i];
 
381
 
 
382
        set_slots(impl, frame, touch);
 
383
        collect_transforms(impl, frame, touch);
 
384
 
 
385
        impl->nextframe = (impl->nextframe + 1) % impl->num_frames;
 
386
 
 
387
        return frame;
 
388
}