diff options
| author | Henrik Rydberg <rydberg@euromail.se> | 2011-04-28 18:42:33 +0200 |
|---|---|---|
| committer | Henrik Rydberg <rydberg@euromail.se> | 2011-04-28 18:42:33 +0200 |
| commit | 5423ccd321c1c84b0e7736b10b2fe9eeb1833e16 (patch) | |
| tree | fc12f236a865dceb6008d9bc5691b0fd0cbe582b /src/grail-frame.c | |
| parent | d87cde46e25c058ffc76d3871ca4e2ceb3e57991 (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.c | 399 |
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 | |||
| 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_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 | |||
| 127 | static 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 | |||
| 148 | static 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 | |||
| 167 | static 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 | |||
| 191 | static 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 | |||
| 215 | static 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 | |||
| 255 | static 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 | |||
| 299 | static 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 | |||
| 317 | static 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 | |||
| 376 | const struct grail_frame GRAIL_PUBLIC * | ||
| 377 | grail_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 | } | ||
