From 5423ccd321c1c84b0e7736b10b2fe9eeb1833e16 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 28 Apr 2011 18:42:33 +0200 Subject: 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 --- src/grail-frame.c | 399 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 src/grail-frame.c (limited to 'src/grail-frame.c') 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 @@ +/***************************************************************************** + * + * grail - Gesture Recognition And Instantiation Library + * + * Copyright (C) 2010 Canonical Ltd. + * Copyright (C) 2010 Henrik Rydberg + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + ****************************************************************************/ + +#include "grail-impl.h" +#include +#include +#include + +static void set_center_velocity_and_radius(struct grail_impl *impl, + struct grail_element *slot) +{ + const struct utouch_contact **tc = slot->touches; + double x, y, vx, vy, r2, dx, dy; + int i; + + switch (slot->num_touches) { + case 1: + x = tc[0]->x; + y = tc[0]->y; + vx = tc[0]->vx; + vy = tc[0]->vy; + r2 = 0; + break; + case 2: + dx = 0.5 * (tc[1]->x - tc[0]->x); + dy = 0.5 * (tc[1]->y - tc[0]->y); + x = tc[0]->x + dx; + y = tc[0]->y + dy; + vx = 0.5 * (tc[0]->vx + tc[1]->vx); + vy = 0.5 * (tc[0]->vy + tc[1]->vy); + r2 = dx * dx + dy * dy; + break; + default: + x = y = vx = vy = r2 = 0; + for (i = 0; i < slot->num_touches; i++) { + x += tc[i]->x; + y += tc[i]->y; + vx += tc[i]->vx; + vy += tc[i]->vy; + } + x /= slot->num_touches; + y /= slot->num_touches; + vx /= slot->num_touches; + vy /= slot->num_touches; + for (i = 0; i < slot->num_touches; i++) { + dx = tc[i]->x - x; + dy = tc[i]->y - y; + r2 += dx * dx + dy * dy; + } + r2 /= slot->num_touches; + break; + } + + slot->center.x = x; + slot->center.y = y; + slot->velocity.x = 1000 * vx; + slot->velocity.y = 1000 * vy; + slot->radius2 = r2; +} + +static void set_moveness_pivot_and_drag(struct grail_impl *impl, + struct grail_element *slot, + double ds, double dc) +{ + const struct grail_control *ctl = impl->ctl; + const struct grail_element *pslot = slot->prev; + double mx = slot->center.x - pslot->center.x; + double my = slot->center.y - pslot->center.y; + float *T = slot->transform; + + slot->moveness = 1; + slot->pivot = pslot->center; + + if (slot->num_touches > 1) { + double wx = (1 - dc) * mx + ds * my; + double wy = (1 - dc) * my - ds * mx; + double w2 = wx * wx + wy * wy; + if (w2 > 0) { + double q = (mx * mx + my * my) / w2; + double s = ctl->pivot_unbound ? + q : sqrt(pslot->radius2 / w2); + if (s < q) { + slot->moveness = 1 - s / q; + slot->pivot.x += s * wx; + slot->pivot.y += s * wy; + } else { + slot->moveness = 0; + slot->pivot.x += q * wx; + slot->pivot.y += q * wy; + } + } + } + + mx *= slot->moveness; + my *= slot->moveness; + + T[0] = dc; + T[1] = ds; + T[2] = (1 - dc) * slot->pivot.x - ds * slot->pivot.y + mx; + T[3] = -ds; + T[4] = dc; + T[5] = (1 - dc) * slot->pivot.y + ds * slot->pivot.x + my; + + slot->drag.x = pslot->drag.x + mx; + slot->drag.y = pslot->drag.y + my; +} + +static void start_slot(struct grail_impl *impl, + struct grail_element *slot, + const struct utouch_frame *touch) +{ + float *T = slot->transform; + + slot->id = impl->seqid++ & GRAIL_ID_MAX; + slot->expect_mask = GRAIL_EXPECT_MASK; + slot->active_mask = 0; + slot->start_time = touch->time; + set_center_velocity_and_radius(impl, slot); + slot->start_center = slot->center; + T[0] = T[4] = 1; + T[1] = T[2] = T[3] = T[5] = 0; + slot->pivot = slot->center; + slot->drag.x = 0; + slot->drag.y = 0; + slot->scale2 = 1; + slot->angle = 0; +} + +static void update_slot(struct grail_impl *impl, + struct grail_element *slot, + double ds, double dc) +{ + const struct grail_element *pslot = slot->prev; + + slot->id = pslot->id; + slot->start_time = pslot->start_time; + slot->start_center = pslot->start_center; + slot->expect_mask = pslot->expect_mask; + slot->active_mask = pslot->active_mask; + + set_center_velocity_and_radius(impl, slot); + set_moveness_pivot_and_drag(impl, slot, ds, dc); + + slot->scale2 = pslot->scale2 * (ds * ds + dc * dc); + slot->angle = pslot->angle + ds / dc; /* atan2(ds, dc) */ +} + +static void stop_slot(struct grail_impl *impl, + struct grail_element *slot) +{ + const struct grail_element *pslot = slot->prev; + float *T = slot->transform; + + slot->id = -1; + slot->num_touches = 0; + slot->start_time = pslot->start_time; + slot->start_center = pslot->start_center; + slot->expect_mask = 0; + slot->active_mask = pslot->active_mask; + slot->center = pslot->center; + slot->velocity = pslot->velocity; + slot->radius2 = pslot->radius2; + T[0] = T[4] = 1; + T[1] = T[2] = T[3] = T[5] = 0; + slot->moveness = 1; + slot->pivot = pslot->pivot; + slot->drag = pslot->drag; + slot->scale2 = pslot->scale2; + slot->angle = pslot->angle; +} + +static void set_slot_one(struct grail_impl *impl, + struct grail_element *slot, + const struct utouch_frame *touch, + const struct utouch_contact *t1) +{ + const struct grail_element *pslot = slot->prev; + const struct utouch_contact *p1 = pslot->touches[0]; + + if (!t1->active) { + stop_slot(impl, slot); + return; + } + + slot->touches[0] = t1; + slot->num_touches = 1; + + if (pslot->num_touches != slot->num_touches || t1->id != p1->id) { + start_slot(impl, slot, touch); + return; + } + + update_slot(impl, slot, 0, 1); +} + +static void set_slot_two(struct grail_impl *impl, + struct grail_element *slot, + const struct utouch_frame *touch, + const struct utouch_contact *t1, + const struct utouch_contact *t2) +{ + const struct grail_element *pslot = slot->prev; + const struct utouch_contact *p1 = pslot->touches[0]; + const struct utouch_contact *p2 = pslot->touches[1]; + double tx, ty, px, py, d2; + + if (!t1->active || !t2->active) { + stop_slot(impl, slot); + return; + } + + slot->touches[0] = t1; + slot->touches[1] = t2; + slot->num_touches = 2; + + if (pslot->num_touches != slot->num_touches || + t1->id != p1->id || t2->id != p2->id) { + start_slot(impl, slot, touch); + return; + } + + tx = t2->x - t1->x; + ty = t2->y - t1->y; + px = p2->x - p1->x; + py = p2->y - p1->y; + + d2 = px * px + py * py; + if (d2 > 0) { + px /= d2; + py /= d2; + } + + update_slot(impl, slot, tx * py - ty * px, tx * px + ty * py); +} + +static void set_slot_multi(struct grail_impl *impl, + struct grail_element *slot, + struct grail_frame *frame, + const struct utouch_frame *touch) +{ + const struct grail_element *pslot = slot->prev; + struct grail_element **slots = frame->slots; + int i, j, n = impl->num_touches; + struct grail_element *best = 0; + + if (touch->num_active < 3) { + stop_slot(impl, slot); + return; + } + + memcpy(slot->touches, touch->active, + touch->num_active * sizeof(slot->touches[0])); + slot->num_touches = touch->num_active; + + if (pslot->num_touches != slot->num_touches) { + start_slot(impl, slot, touch); + return; + } + + for (i = 0; i < slot->num_touches; i++) { + if (slot->touches[i]->id != pslot->touches[i]->id) { + start_slot(impl, slot, touch); + return; + } + } + + for (i = 0; i < impl->num_touches; i++) { + for (j = i + 1; j < impl->num_touches; j++) { + struct grail_element *s = slots[n++]; + if (!s->num_touches) + continue; + if (!best || s->radius2 > best->radius2) + best = s; + } + } + + update_slot(impl, slot, best->transform[1], best->transform[0]); +} + +static void set_slots(struct grail_impl *impl, + struct grail_frame *frame, + const struct utouch_frame *touch) +{ + struct grail_element **slots = frame->slots; + struct utouch_contact *const *tc = touch->slots; + int i, j, n = 0; + + for (i = 0; i < impl->num_touches; i++) + set_slot_one(impl, slots[n++], touch, tc[i]); + + for (i = 0; i < impl->num_touches; i++) + for (j = i + 1; j < impl->num_touches; j++) + set_slot_two(impl, slots[n++], touch, tc[i], tc[j]); + + set_slot_multi(impl, slots[n++], frame, touch); +} + +static void collect_transforms(struct grail_impl *impl, + struct grail_frame *frame, + const struct utouch_frame *touch) +{ + const struct utouch_surface *s = utouch_frame_get_surface(impl->fh); + const struct grail_control *ctl = impl->ctl; + float c_x = ctl->bar_center_x * (s->mapped_max_x - s->mapped_min_x); + float c_y = ctl->bar_center_y * (s->mapped_max_y - s->mapped_min_y); + float d_x = ctl->bar_drag_x * (s->mapped_max_x - s->mapped_min_x); + float d_y = ctl->bar_drag_y * (s->mapped_max_y - s->mapped_min_y); + float ds2 = ctl->bar_scale * ctl->bar_scale; + float dt; + int i; + + for (i = 0; i < impl->num_slots; i++) { + struct grail_element *s = frame->slots[i]; + + if (!s->num_touches) + continue; + + dt = touch->time - s->start_time; + if (dt > ctl->glue_ms) { + unsigned int mask = s->active_mask; + + if (fabs(s->center.x - s->start_center.x) > c_x) + mask |= GRAIL_EXPECT_CENTER_X; + if (fabs(s->center.y - s->start_center.y) > c_y) + mask |= GRAIL_EXPECT_CENTER_Y; + if (fabs(s->drag.x) > d_x) + mask |= GRAIL_EXPECT_DRAG_X; + if (fabs(s->drag.y) > d_y) + mask |= GRAIL_EXPECT_DRAG_Y; + if (fabs(s->scale2 - 1) > ds2) + mask |= GRAIL_EXPECT_SCALE; + if (fabs(s->angle) > ctl->bar_angle) + mask |= GRAIL_EXPECT_ANGLE; + + s->active_mask = mask; + + if (dt < ctl->drop_x_ms) + mask |= GRAIL_EXPECT_CENTER_X; + if (dt < ctl->drop_y_ms) + mask |= GRAIL_EXPECT_CENTER_Y; + if (dt < ctl->drop_x_ms) + mask |= GRAIL_EXPECT_DRAG_X; + if (dt < ctl->drop_y_ms) + mask |= GRAIL_EXPECT_DRAG_Y; + if (dt < ctl->drop_scale_ms) + mask |= GRAIL_EXPECT_SCALE; + if (dt < ctl->drop_angle_ms) + mask |= GRAIL_EXPECT_ANGLE; + + s->expect_mask &= mask; + } + + frame->ongoing[frame->num_ongoing++] = s; + } +} + +const struct grail_frame GRAIL_PUBLIC * +grail_pump_frame(grail_handle ge, const struct utouch_frame *touch) +{ + struct grail_impl *impl = ge->impl; + struct grail_frame *frame = impl->frames[impl->nextframe]; + const struct grail_frame *prev = frame->prev; + int i; + + if (touch->slot_revision == touch->prev->slot_revision && + !prev->num_ongoing) + return 0; + + frame->touch = touch; + frame->num_ongoing = 0; + for (i = 0; i < impl->num_slots; i++) + frame->slots[i]->prev = prev->slots[i]; + + set_slots(impl, frame, touch); + collect_transforms(impl, frame, touch); + + impl->nextframe = (impl->nextframe + 1) % impl->num_frames; + + return frame; +} -- cgit v1.2.3