grail-3.1.0+16.04.20160125/ 0000755 0000156 0000165 00000000000 12651522501 015151 5 ustar pbuser pbgroup 0000000 0000000 grail-3.1.0+16.04.20160125/docs/ 0000755 0000156 0000165 00000000000 12651522501 016101 5 ustar pbuser pbgroup 0000000 0000000 grail-3.1.0+16.04.20160125/docs/pivot.txt 0000644 0000156 0000165 00000005724 12651522342 020016 0 ustar pbuser pbgroup 0000000 0000000 The pivot, p, is defined as the point, within the convex hull of the
contacts, which, after rotation and scaling, leaves the transformed
contacts as close to the actual positions as possible.
Let r_i be the starting points and s_i the actual ending points in a
transformation. Let D be the scaling, and R the rotation. Then, minimizing
L(p) = sum_i |D R (r_i - p) + p - s_i|^2 / N
yields the pivot. Let
rm = sum_i r_i / N,
p = rm + u,
q_i = s_i - rm - D R (r_i - rm),
and we get
L(p) = sum_i |(1 - D R) u - q_i|^2 / N.
With
L0 = sum_i norm2(q_i) / N,
T = (1 - D R)' (1 - D R),
m = sum_i q_i / N,
we can write this as
L(p) = L0 + u' T u - 2 m' (1 - D R) u.
To handle the constraint, we can approximate the hull with a circle
centered at rm. If we pick the average radius, P, the constraint becomes
|u| < P.
Relaxing [1] the expression (h >= 0) yields
L(p, h) = L0 + u' T u - 2 m' (1 - D R) u + h (|u|^2 - P^2),
leading to the linear equation
(T + h) u = (1 - D R)' m.
Further,
sm = sum_i s_i / N,
m = sum_i (s_i - rm - D R (r_i - rm)) / N = sm - rm,
thus m is the average displacement. In words, the pivot is the average
position plus a correction depending on the average displacement.
*
Some algebra solves the equation,
D' = D,
[D, R] = 0,
R = S + C,
S' = -S,
C' = C,
R + R' = 2 C,
T = (1 - D R)' (1 - D R) = 1 + D^2 - 2 D C,
which is a simple diagonal scaling operator. With
a = 1 - D C,
b = D S,
we can write this as
T = (1 - DC)^2 + D^2(1 - C^2) = (1 - DC)^2 + D^2 S^2 = a^2 + b^2.
Similarly, we can write
(1 - D R)' = ((a, b), (-b, a)),
and thusly,
u = Q(h) m,
with
Q(h) = ((a, b), (-b, a)) / (a^2 + b^2 + h).
When D R = 1, it follows that a^2 + b^2 = 0, and the relaxation ensures
that u is finite.
*
The drag is found by minimizing
E(d) = sum_i | D R (r_i - p) + p + d - s_i |^2 / N,
E(d) = d^2 + 2 d' ((1 - D R) u - m) + E0,
which leads to the linear equation
d = m - (1 - D R) u.
Explicitly,
d = m - (a ux - b uy, a uy + b ux).
Inserting the expression for u yields, after some algebra,
d = m (1 - (a^2 + b^2) / (a^2 + b^2 + h)).
When h = 0, d = 0, as expected.
When a^2 + b^2 = 0, d = m, also as expected.
For constrained cases, the drag is a fraction of the average displacement.
*
Time to look at measures for the relaxation parameter. Since d depends on
h, we can write the correction u(h) in terms of d instead. After som
algebra,
|u(h)| = (|m| - |d|) / sqrt(a^2 + b^2).
Conversely, d(h) can be written in terms of the constrained u(h) as
d(h) = m (1 - sqrt(a^2 + b^2) |u(h)| / |m|).
Since |u(0)| = |m| / sqrt(a^2 + b^2), we obtain
d(h) = m (1 - |u(h)| / |u(0)|).
*
We can now write down an explicit recipe for determining the pivot (p) and
drag (d), given the transformation parameters a and b.
w = (a mx + b my, a my - b mx).
If |w| = 0, then u = 0. Consequently p = rm, d = m, and we are done. Else,
u = w |m|^2 / |w|^2,
t = P / |u|.
If t >= 1, then p = rm + u, d = 0, and we are done. Else,
p = rm + t u,
d = (1 - t) m.
[1] See Lagrange relaxation
grail-3.1.0+16.04.20160125/docs/gestures.txt 0000644 0000156 0000165 00000005602 12651522342 020511 0 ustar pbuser pbgroup 0000000 0000000 Introduction
------------
This document describes how the gestures are extracted from multi-finger
actions. The process is divided into gestural transformations, gesture
recognition, and gesture instantiation.
Gestural Transformations
------------------------
All two-finger transformations are extracted. These are all exact, in the
sense that continuously transforming the original finger positions, frame
per frame, will exactly follow the actual finger positions.
In addition to two-finger transformations, a global gesture is also
extracted. The rotation and scaling is taken from the contact pair with the
longest distance between contacts. This approximates the behavior of a
region, such that complex transformations could, in principle, be happening
inside the region, but at a distance, the transformation will look like it
was performed with two fingers.
At each time step, a gestural transformation is goverened by rotation,
scaling and translation. The point around which rotation and scaling is
performed is called the pivot. To form as natural gestures as possible,
this point always lies within the area formed by the contacts themselves.
It is placed at the point which, after rotation and scaling, leaves the
transformed contacts as close to the actual positions as possible. The
translation, or drag, is always a fraction of the movement of the center
point. The fraction is called moveness, and is related to the distance
between the pivot and the center point. When the pivot is at the center
point, the moveness is one and the drag is the same as the center movement.
When the pivot is at one of the contacts, as in rotation around a finger,
the moveness is zero, and consequently the drag is zero. The relations
between the pivot, the moveness and the drag are detailed in the document
pivot.txt.
Gesture Recognition
-------------------
Each gestural transformation can give rise to one or several gesture
primitives. Based on the rotation, scaling, moveness and drag values, the
gesture primitives drag, pinch and rotate are recognized. The onset of a
gesture primitive is governed by a threshold and a timeout. Basically, if
performed distinctly enough, the gesture will be triggered, and will remain
active until a finger is lifted or added.
Gesture Instantiation
---------------------
Given a set of detected gesture primitives, only some will trigger actual
gesture events. First, the set of available primitives are matched against
available listeners. Primitives not listened for are dropped. The remaining
set is arranged according to priority. A pointer gesture has lower priority
than a two-finger gesture, which has lower priority than a tap, for
instance. As long as the gesture primitives of higher priority are expected
but not activated, all gestures are held back. Once one of the highest
priority primitives are activated, all primitives of lower priority are
cancelled, and events are emitted.
grail-3.1.0+16.04.20160125/docs/grail-problems.txt 0000644 0000156 0000165 00000004764 12651522342 021577 0 ustar pbuser pbgroup 0000000 0000000 Split gestures into recognition and application
-----------------------------------------------
Gesture detection is not an exact science, but the algorithms
inevitably involve decisions influenced by what gestures are available
to choose from. Therefore, the recognition should be considered an
inherent property of the device itself, regardless of user interaction
context. Just imagine having a keyboard that resized slightly
everytime you switched from chrome to gimp.
Tag events as being part of a gesture
-------------------------------------
One of the problems faced when replacing one set of events with
another is where and how this filtering should take place. One window
might listen for spiral gestures, whereas another window is a paint
program where you actually want to draw a spiral. By introducing a
relation between a gesture and an event, filtering becomes something
simple that can be performed close to the window. The relation can be
implemented as a bitmask representing what gestures the event is part
of.
Help the client to keep track of active gestures
------------------------------------------------
A gesture starts, continues, and either completes or aborts.
A client that listens to a subset of gestures will have to keep track.
Compatible to Qt approach.
Allow clients to listen to different gestures
---------------------------------------------
Clients listening on certain gestures will get those gesture events that
apply to the client, which makes it possible for different clients to
listen to different types of gestures. For instance, having an MT drawing
program next to a web browser, a two finger stroke in the drawing program
will result in two lines, whereas in the web browser, it will scroll the
text.
Propagate gesture events based on the focus point
-------------------------------------------------
Like pointer events, a gesture applies to a window or a window manager,
based on where it is performed on the screen. In order to route the gesture
events to the right window, we propagate them through the window hierarchy
based on the gesture focus points. Based on the target windows, their
respective gesture type masks, and the active set of gestures, final
gesture events are emitted to the right windows.
Allow Window Manager to grab system-wide gestures
-------------------------------------------------
Even if clients are not listening to global window-manager gestures, it
could well be that no other gestures should be displayed while the global
gesture is taking place.
grail-3.1.0+16.04.20160125/autogen.sh 0000755 0000156 0000165 00000000313 12651522342 017152 0 ustar pbuser pbgroup 0000000 0000000 #! /bin/sh
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
ORIGDIR=`pwd`
cd $srcdir
autoreconf --force --install || exit 1
cd $ORIGDIR || exit $?
test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
grail-3.1.0+16.04.20160125/include/ 0000755 0000156 0000165 00000000000 12651522500 016573 5 ustar pbuser pbgroup 0000000 0000000 grail-3.1.0+16.04.20160125/include/oif/ 0000755 0000156 0000165 00000000000 12651522501 017351 5 ustar pbuser pbgroup 0000000 0000000 grail-3.1.0+16.04.20160125/include/oif/grail.h 0000644 0000156 0000165 00000051776 12651522342 020643 0 ustar pbuser pbgroup 0000000 0000000 /*****************************************************************************
*
* grail - Multitouch Gesture Recognition Library
*
* Copyright (C) 2010-2011 Canonical Ltd.
*
* 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 .
*
****************************************************************************/
/**
* @file oif/grail.h
* Definitions of the main and platform-generic API
*/
#ifndef GRAIL_OIF_GRAIL_H_
#define GRAIL_OIF_GRAIL_H_
/* Macros that set symbol visibilities in shared libraries properly.
* Adapted from http://gcc.gnu.org/wiki/Visibility
*/
#if defined _WIN32 || defined __CYGWIN__
#ifdef BUILDING_GRAIL
#define GRAIL_PUBLIC __declspec(dllexport)
#else
#define GRAIL_PUBLIC __declspec(dllimport)
#endif
#else
#if defined __GNUC__
#define GRAIL_PUBLIC __attribute__ ((visibility("default")))
#else
#pragma message ("Compiler does not support symbol visibility.")
#define GRAIL_PUBLIC
#endif
#endif
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup v3 Grail 3.x
* @{
*/
/** An object for the context of the grail instance */
typedef struct UGHandle_* UGHandle;
/** An object for a gesture subscription */
typedef struct UGSubscription_* UGSubscription;
/** An object for an event */
typedef struct UGEvent_* UGEvent;
/** An object for a gesture state in time */
typedef const struct UGSlice_* UGSlice;
/** The status code denoting the result of a function call */
typedef enum UGStatus {
UGStatusSuccess = 0, /**< The call was successful */
UGStatusErrorGeneric, /**< A platform-dependent error occurred */
UGStatusErrorResources, /**< An error occurred due to insufficient resources */
UGStatusErrorNoEvent, /**< No events were available to get */
UGStatusErrorUnknownProperty, /**< The requested property value was not set */
UGStatusErrorInvalidValue, /**< The property value passed in is invalid */
UGStatusErrorInvalidDevice, /**< The requested device does not exist */
UGStatusErrorInvalidSubscription, /**< The subscription is invalid */
UGStatusErrorInvalidGesture, /**< The requested gesture does not exist */
UGStatusErrorInvalidIndex, /**< The requested touch index is invalid */
UGStatusErrorAtomicity, /**< The subscription has a different value for
UGSubscriptionPropertyAtomicGestures than other
subscriptions active on the window */
} UGStatus;
/** Subscription properties */
typedef enum UGSubscriptionProperty {
/**
* Device to subscribe to gesture events for
*
* Value type: UFDevice
*/
UGSubscriptionPropertyDevice,
/**
* Window to subscribe to gesture events for
*
* Value type: UFWindowId
*/
UGSubscriptionPropertyWindow,
/**
* Gesture types to subscribe for
*
* Value type: UGGestureTypeMask
*/
UGSubscriptionPropertyMask,
/**
* Number of touches required to begin gesture
*
* Value type: unsigned int
* Default value: 2 touches
*/
UGSubscriptionPropertyTouchesStart,
/**
* Minimum number of touches for gesture
*
* Value type: unsigned int
* Default value: 2 touches
*/
UGSubscriptionPropertyTouchesMinimum,
/**
* Maximum number of touches for gesture
*
* Value type: unsigned int
* Default value: 2 touches
*/
UGSubscriptionPropertyTouchesMaximum,
/**
* Timeout for recognizing a drag gesture
*
* Value type: 64-bit unsigned integer
* Default value: 300 ms
*/
UGSubscriptionPropertyDragTimeout,
/**
* Threshold value for recognizing a drag gesture
*
* Value type: float
* Default value: 0.0026 m
*
* The value is in units of meters.
*/
UGSubscriptionPropertyDragThreshold,
/**
* Timeout for recognizing a pinch gesture
*
* Value type: 64-bit unsigned integer
* Default value: 300 ms
*/
UGSubscriptionPropertyPinchTimeout,
/**
* Threshold value for recognizing a pinch gesture
*
* Value type: float
* Default value: 1.1
*
* The value is a proportionality representing how much a group of touches
* have moved closer or farther apart. For example, a threshold of 1.1 would
* be met if two touches moved from 1000 pixels apart to either 1100 or 909
* pixels apart.
*/
UGSubscriptionPropertyPinchThreshold,
/**
* Timeout for recognizing a rotate gesture
*
* Value type: 64-bit unsigned integer
* Default value: 500 ms
*/
UGSubscriptionPropertyRotateTimeout,
/**
* Threshold value for recognizing a rotate gesture
*
* Value type: float
* Default value: 0.125663706 (1/50th of a revolution)
*
* The value is in units of radians.
*/
UGSubscriptionPropertyRotateThreshold,
/**
* Timeout for recognizing a tap gesture
*
* Value type: 64-bit unsigned integer
* Default value: 300 ms
*/
UGSubscriptionPropertyTapTimeout,
/**
* Threshold value for recognizing a tap gesture
*
* Value type: float
* Default value: 0.0026 m
*
* For a tap to be recognized, the touches must not move more than the
* threshold value in any direction.
*/
UGSubscriptionPropertyTapThreshold,
/**
* Only support one gesture at a time
*
* Value type: int with boolean semantics
* Default value: False
*
* The first version of grail supported only one gesture at a time. When this
* property is true, grail will mimic this behavior. This results in the
* following:
*
* - The grail client must not attempt to accept or reject a gesture
* - If a gesture is active for a maximum of N touches, the addition of
* another touch will end the gesture. A new gesture is begun if another
* subscription's TouchesStart property equals the new number of touches.
* - All subscriptions for a window must have the same value for this
* property. If a client attempts to activate a subscription with a
* different value for this property than the already activated
* subscriptions for the window, UGStatusErrorAtomicity will be returned.
* - Gestures from multiple subscriptions may be active at the same time.
*
* There is one key difference between grail v1 behavior and the use of this
* option. The v1 behavior only supported one gesture per device. The use of
* this option only supports one gesture per device per window. The beginning
* of a gesture in a window does not inhibit gestures in other windows. It
* also does not guarantee that there are no active touches outside the
* window.
*/
UGSubscriptionPropertyAtomicGestures,
} UGSubscriptionProperty;
/** Event type */
typedef enum UGEventType {
UGEventTypeSlice = 0, /**< A new gesture slice */
} UGEventType;
/** Event properties */
typedef enum UGEventProperty {
/**
* Type of event
*
* Value type: UGEventType
*/
UGEventPropertyType = 0,
/**
* Slice of a gesture
*
* Value type: UGSlice
*/
UGEventPropertySlice,
/**
* Event time
*
* Value type: 64-bit unsigned int
*
* This property holds the time the event occurred in display server
* timespace. The time is provided in milliseconds (ms).
*/
UGEventPropertyTime,
} UGEventProperty;
/** Gesture type bit indices */
typedef enum UGGestureType {
UGGestureTypeDrag = 0x1, /**< Drag gesture */
UGGestureTypePinch = 0x2, /**< Pinch gesture */
UGGestureTypeRotate = 0x4, /**< Rotate gesture */
UGGestureTypeTap = 0x8, /**< Tap gesture */
UGGestureTypeTouch = 0x10, /**< Touch gesture */
} UGGestureType;
/** Bit-mask of gesture types */
typedef uint32_t UGGestureTypeMask;
/** 2D affine transformation */
typedef const float UGTransform[3][3];
/** Gesture slice state */
typedef enum UGGestureState {
UGGestureStateBegin = 0, /**< Gesture slice begin */
UGGestureStateUpdate, /**< Gesture slice update */
UGGestureStateEnd, /**< Gesture slice end */
} UGGestureState;
/**
* Gesture slice properties
*
* The coordinate system for gesture properties is determined by the device
* type. Direct devices provide screen coordinates. Indirect devices provide
* device coordinates.
*/
typedef enum UGSliceProperty {
/**
* Gesture ID
*
* Value type: unsigned int
*/
UGSlicePropertyId = 0,
/**
* Gesture set state
*
* Value type: UGGestureState
*/
UGSlicePropertyState,
/**
* Gesture subscription
*
* Value type: UGSubscription
*/
UGSlicePropertySubscription,
/**
* Recognized gestures
*
* Value type: UGGestureTypeMask
*/
UGSlicePropertyRecognized,
/**
* Number of touches
*
* Value type: unsigned int
*/
UGSlicePropertyNumTouches,
/**
* Touch frame
*
* Value type: UFFrame
*/
UGSlicePropertyFrame,
/**
* Original gesture center along the X axis
*
* Value type: float
*
* This value represents the original geometric center of the touches.
*/
UGSlicePropertyOriginalCenterX,
/**
* Original gesture center along the Y axis
*
* Value type: float
*
* This value represents the original geometric center of the touches.
*/
UGSlicePropertyOriginalCenterY,
/**
* Original radius of touches
*
* Value type: float
*
* This value represents the average of the square of the euclidean distance
* from the geometric center of the original touches to each touch.
*/
UGSlicePropertyOriginalRadius,
/**
* Best-fit 2D affine transformation of previous to current touch locations
*
* Value type: pointer to UGTransform
*
* The transformation is relative to the previous geometric center of the
* touches.
*/
UGSlicePropertyTransform,
/**
* Best-fit 2D affine transformation of original to current touch locations
*
* Value type: pointer to UGTransform
*
* The transformation is relative to the original geometric center of the
* touches.
*/
UGSlicePropertyCumulativeTransform,
/**
* Best-fit instant center of rotation along the X axis
*
* Value type: float
*/
UGSlicePropertyCenterOfRotationX,
/**
* Best-fit instant center of rotation along the Y axis
*
* Value type: float
*/
UGSlicePropertyCenterOfRotationY,
/**
* Whether the construction of all gestures containing the same touches is
* finished
*
* Value type: int with boolean semantics
*
* Grail events are serial. This property allows the client to determine if
* all the possible gestures from the set of touches in this gesture have been
* sent. When this value is true, the client will have received all the
* information needed to make a gesture accept and reject decision based on
* potentially overlapping gestures. An example is when both one and two touch
* gestures are subscribed on the same window with the same gesture types and
* thresholds. When this property is true for one touch gesture events, the
* client can be sure there are no other touches unless a two touch gesture
* event has already been sent.
*/
UGSlicePropertyConstructionFinished,
} UGSliceProperty;
/**
* Create a new grail context
*
* @param [out] handle The new grail context object
* @return UGStatusSuccess or UGStatusErrorResources
*/
GRAIL_PUBLIC
UGStatus grail_new(UGHandle *handle);
/**
* Delete a grail context
*
* @param [in] handle The grail context object
*/
GRAIL_PUBLIC
void grail_delete(UGHandle handle);
/**
* Get the event file descriptor for the frame context
*
* @param [in] handle The grail context object
* @return A file descriptor for the context
*
* When events are available for processing, the file descriptor will be
* readable. Perform an 8-byte read from the file descriptor to clear the state.
* Refer to the EVENTFD(2) man page for more details.
*/
GRAIL_PUBLIC
int grail_get_fd(UGHandle handle);
/**
* Create a new subscription object
*
* @param [out] subscription The new subscription object
* @return UGStatusSuccess or UGStatusErrorResources
*/
GRAIL_PUBLIC
UGStatus grail_subscription_new(UGSubscription* subscription);
/**
* Delete a subscription object
*
* @param [in] subscription The subscription object
*/
GRAIL_PUBLIC
void grail_subscription_delete(UGSubscription subscription);
/**
* Set a subscription property
*
* @param [in] subscription The subscription object
* @param [in] property The subscription property
* @param [in] value The new value of the property
* @return UGStatusSuccess or UGStatusInvalidProperty
*/
GRAIL_PUBLIC
UGStatus grail_subscription_set_property(UGSubscription subscription,
UGSubscriptionProperty property,
const void* value);
/**
* Get a subscription property
*
* @param [in] subscription The subscription object
* @param [in] property The subscription property
* @param [out] value The value of the property
* @return UGStatusSuccess or UGStatusInvalidProperty
*/
GRAIL_PUBLIC
UGStatus grail_subscription_get_property(UGSubscription subscription,
UGSubscriptionProperty property,
void* value);
/**
* Activate a subscription
*
* @param [in] handle The context object
* @param [in] subscription The subscription object
* @return UGStatusSuccess or UGStatusErrorInvalidDevice
*/
GRAIL_PUBLIC
UGStatus grail_subscription_activate(UGHandle handle, UGSubscription subscription);
/**
* Deactivate a subscription
*
* @param [in] handle The context object
* @param [in] subscription The subscription object
*/
GRAIL_PUBLIC
void grail_subscription_deactivate(UGHandle handle,
UGSubscription subscription);
/**
* Process a frame event
*
* @param [in] handle The context object
* @param [in] event The frame event
*/
GRAIL_PUBLIC
void grail_process_frame_event(UGHandle handle, const UFEvent event);
/**
* Get an event from the grail context
*
* @param [in] handle The context object
* @param [out] event The retrieved event
* @return UGStatusSuccess or UGStatusErrorNoEvent
*/
GRAIL_PUBLIC
UGStatus grail_get_event(UGHandle handle, UGEvent *event);
/**
* Update the grail state for the given server time
*
* @param [in] handle The context object
* @param [in] time The current server time
*
* The recognizer uses timeouts for deciding whether to accept or reject
* touches. Calling this function will perform any pending decisions based on
* the current server time.
*/
GRAIL_PUBLIC
void grail_update_time(UGHandle handle, uint64_t time);
/**
* Get the next timeout at which to update the grail state
*
* @param [in] handle The context object
* @return The next server time at which the grail state should be updated
*
* To update the grail state, call grail_update_time().
*/
GRAIL_PUBLIC
uint64_t grail_next_timeout(UGHandle handle);
/**
* Increment the reference count of an event
*
* @param [in] event The event object
*/
GRAIL_PUBLIC
void grail_event_ref(UGEvent event);
/**
* Decrement the reference count of an event
*
* @param [in] event The event object
*
* When the reference count reaches zero, the event is freed.
*/
GRAIL_PUBLIC
void grail_event_unref(UGEvent event);
/**
* Get the value of a property of an event
*
* @param [in] event The event object
* @param [in] property The property to retrieve a value for
* @param [out] value The value retrieved
* @return UGStatusSuccess or UGStatusErrorUnknownProperty
*/
GRAIL_PUBLIC
UGStatus grail_event_get_property(const UGEvent event, UGEventProperty property,
void *value);
/**
* Get the touch ID of a touch in a slice
*
* @param [in] slice The gesture slcie object
* @param [in] index The index of the touch in the slice
* @param [out] touch_id The touch ID of the touch
*/
GRAIL_PUBLIC
UGStatus grail_slice_get_touch_id(const UGSlice slice, unsigned int index,
UFTouchId *touch_id);
/**
* Get the value of a property of a gesture slice
*
* @param [in] slice The gesture slice object
* @param [in] property The property to retrieve a value for
* @param [out] value The value retrieved
* @return UGStatusSuccess or UGStatusErrorUnknownProperty
*/
GRAIL_PUBLIC
UGStatus grail_slice_get_property(const UGSlice slice, UGSliceProperty property,
void *value);
/**
* Accept gesture associated with gesture slice
*
* @param [in] handle The context object
* @param [in] id The ID of the gesture to accept
* @return UGStatusSuccess or UGStatusErrorInvalidGesture
*/
GRAIL_PUBLIC
UGStatus grail_accept_gesture(UGHandle handle, unsigned int id);
/**
* Reject gesture associated with gesture slice
*
* @param [in] handle The context object
* @param [in] id The ID of the gesture to reject
* @return UGStatusSuccess or UGStatusErrorInvalidGesture
*/
GRAIL_PUBLIC
UGStatus grail_reject_gesture(UGHandle handle, unsigned int id);
/**
* @defgroup v3-helpers Helper Functions
* These helper functions may be used in place of the generic property getters.
* They are limited to properties that are guaranteed to exist in all instances
* of the objects.
* @{
*/
/**
* Get the type of an event
*
* @param [in] event The event object
* @return The type of the event
*/
GRAIL_PUBLIC
UGEventType grail_event_get_type(const UGEvent event);
/**
* Get the time of an event
*
* @param [in] event The event object
* @return The time of the event
*/
GRAIL_PUBLIC
uint64_t grail_event_get_time(const UGEvent event);
/**
* Get the ID of a gesture from a slice
*
* @param [in] slice The gesture slice object
* @return The ID of the gesture
*/
GRAIL_PUBLIC
unsigned int grail_slice_get_id(const UGSlice slice);
/**
* Get the state of a gesture in a slice
*
* @param [in] slice The gesture slice object
* @return The state of the gesture in the slice
*/
GRAIL_PUBLIC
UGGestureState grail_slice_get_state(const UGSlice slice);
/**
* Get the subscription for the gesture from the slice
*
* @param [in] slice The gesture slice object
* @return The subscription
*/
GRAIL_PUBLIC
UGSubscription grail_slice_get_subscription(const UGSlice slice);
/**
* Get the gestures recognized through the slice
*
* @param [in] slice The gesture slice object
* @return The recognized gestures
*/
GRAIL_PUBLIC
UGGestureTypeMask grail_slice_get_recognized(const UGSlice slice);
/**
* Get the current number of touches in the slice
*
* @param [in] slice The gesture slice object
* @return The number of touches
*/
GRAIL_PUBLIC
unsigned int grail_slice_get_num_touches(const UGSlice slice);
/**
* Get the original centroid position of the gesture along the X axis
*
* @param [in] slice The gesture slice object
* @return The position
*/
GRAIL_PUBLIC
float grail_slice_get_original_center_x(const UGSlice slice);
/**
* Get the original centroid position of the gesture along the Y axis
*
* @param [in] slice The gesture slice object
* @return The position
*/
GRAIL_PUBLIC
float grail_slice_get_original_center_y(const UGSlice slice);
/**
* Get the original radius of the gesture
*
* @param [in] slice The gesture slice object
* @return The position
*/
GRAIL_PUBLIC
float grail_slice_get_original_radius(const UGSlice slice);
/**
* Get the instantaneous center of rotation of the gesture along the X axis
*
* @param [in] slice The gesture slice object
* @return The position
*/
GRAIL_PUBLIC
float grail_slice_get_center_of_rotation_x(const UGSlice slice);
/**
* Get the instantaneous center of rotation of the gesture along the Y axis
*
* @param [in] slice The gesture slice object
* @return The position
*/
GRAIL_PUBLIC
float grail_slice_get_center_of_rotation_y(const UGSlice slice);
/**
* Get the best-fit instantaneous 2D affine transformation for the gesture slice
*
* @param [in] slice The gesture slice object
* @return the transformation
*
* The returned transformation is owned by the gesture slice.
*/
GRAIL_PUBLIC
const UGTransform *grail_slice_get_transform(const UGSlice slice);
/**
* Get the best-fit cumulative 2D affine transformation for the gesture slice
*
* @param [in] slice The gesture slice object
* @return the transformation
*
* The returned transformation is owned by the gesture slice.
*/
GRAIL_PUBLIC
const UGTransform *grail_slice_get_cumulative_transform(const UGSlice slice);
/**
* Get the frame frame for the slice
*
* @param [in] slice The gesture slice object
* @return the frame
*/
GRAIL_PUBLIC
const UFFrame grail_slice_get_frame(const UGSlice slice);
/**
* Get whether construction has finished for all touches in the gesture
*
* @param [in] slice The gesture slice object
* @return whether construction has finished
*/
GRAIL_PUBLIC
int grail_slice_get_construction_finished(const UGSlice slice);
/** @} */
/** @} */
#ifdef __cplusplus
}
#endif
#endif // GRAIL_OIF_GRAIL_H_
grail-3.1.0+16.04.20160125/src/ 0000755 0000156 0000165 00000000000 12651522501 015740 5 ustar pbuser pbgroup 0000000 0000000 grail-3.1.0+16.04.20160125/src/slice.cpp 0000644 0000156 0000165 00000054053 12651522342 017555 0 ustar pbuser pbgroup 0000000 0000000 /*****************************************************************************
*
* grail - Gesture Recognition And Instantiation Library
*
* Copyright (C) 2010-2012 Canonical Ltd.
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3
* as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
****************************************************************************/
#include "config.h"
#include "slice.h"
#include
#include
#include
#include
#include
#include
#include "gesture.h"
#include "log.h"
#include "recognizer.h"
#include "subscription.h"
#include "touch.h"
namespace {
const UGTransform IDENTITY_TRANSFORM = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
} // namespace
namespace oif {
namespace grail {
/**
* @internal
* Create a gesture begin slice
*/
UGSlice::UGSlice(Gesture& gesture, UFEvent event,
const TouchMap& touches, UGGestureTypeMask recognized)
: id_(gesture.id()),
event_(event),
frame_(NULL),
touches_(touches),
time_(frame_event_get_time(event)),
state_(UGGestureStateBegin),
physically_ended_(false),
original_center_x_(0),
original_center_y_(0),
original_radius_(0),
original_angle_(0),
radius_(0),
angle_(0),
center_of_rotation_x_(0),
center_of_rotation_y_(0),
recognized_(recognized),
construction_finished_(false),
touch_count_changed_(false),
subscription_(gesture.subscription()) {
std::memcpy(transform_, IDENTITY_TRANSFORM, sizeof(IDENTITY_TRANSFORM));
std::memcpy(cumulative_transform_, IDENTITY_TRANSFORM,
sizeof(IDENTITY_TRANSFORM));
UFStatus status = frame_event_get_property(event, UFEventPropertyFrame,
&frame_);
if (status != UFStatusSuccess)
throw std::runtime_error("Warning: failed to get frame from event\n");
/* Hold a reference to the frame event */
frame_event_ref(event);
/* Compute initial gesture slice properties */
GetValues(gesture, touches, &original_center_x_, &original_center_y_,
&original_radius_, &original_angle_, true);
radius_ = original_radius_;
angle_ = original_angle_;
}
/**
* @internal
* Copy a gesture slice
*/
UGSlice::UGSlice(const SharedUGSlice& prev, bool end)
: id_(prev->id_),
event_(prev->event_),
frame_(prev->frame_),
touches_(prev->touches_),
time_(frame_event_get_time(event_)),
state_(end ? UGGestureStateEnd : UGGestureStateUpdate),
physically_ended_(end ? true : prev->physically_ended_),
original_center_x_(prev->original_center_x_),
original_center_y_(prev->original_center_y_),
original_radius_(prev->original_radius_),
original_angle_(prev->original_angle_),
radius_(prev->radius_),
angle_(prev->angle_),
center_of_rotation_x_(0),
center_of_rotation_y_(0),
recognized_(prev->recognized_),
construction_finished_(prev->construction_finished_),
touch_count_changed_(false),
subscription_(prev->subscription_) {
std::memcpy(transform_, IDENTITY_TRANSFORM, sizeof(IDENTITY_TRANSFORM));
std::memcpy(cumulative_transform_, prev->cumulative_transform_,
sizeof(prev->cumulative_transform_));
UFStatus status = frame_event_get_property(event_, UFEventPropertyFrame,
&frame_);
if (status != UFStatusSuccess)
throw std::runtime_error("Warning: failed to copy gesture slice\n");
/* Hold a reference to the frame event */
frame_event_ref(event_);
}
/**
* @internal
* Create a gesture update or end slice based on new touch frame
*/
UGSlice::UGSlice(const SharedUGSlice& prev, Gesture &gesture,
UFEvent event, const TouchMap& touches)
: id_(prev->id_),
event_(event ? : NULL),
frame_(NULL),
touches_(touches),
time_(frame_event_get_time(event)),
state_(UGGestureStateUpdate),
physically_ended_(prev->physically_ended_),
original_center_x_(prev->original_center_x_),
original_center_y_(prev->original_center_y_),
original_radius_(prev->original_radius_),
original_angle_(prev->original_angle_),
radius_(prev->radius_),
angle_(prev->angle_),
center_of_rotation_x_(0),
center_of_rotation_y_(0),
recognized_(prev->recognized_),
construction_finished_(prev->construction_finished()),
touch_count_changed_(touches_.size() != prev->touches_.size()),
subscription_(prev->subscription_) {
std::memcpy(transform_, IDENTITY_TRANSFORM, sizeof(IDENTITY_TRANSFORM));
/* If the number of touches changed, reset transformation */
if (touch_count_changed_) {
std::memcpy(cumulative_transform_, IDENTITY_TRANSFORM,
sizeof(IDENTITY_TRANSFORM));
original_radius_ = 0;
original_angle_ = 0;
} else {
std::memcpy(cumulative_transform_, prev->cumulative_transform_,
sizeof(prev->cumulative_transform_));
}
UFStatus status = frame_event_get_property(event, UFEventPropertyFrame,
&frame_);
if (status != UFStatusSuccess)
throw std::runtime_error("Warning: failed to get frame from event\n");
/* Hold a reference to the frame event */
if (event_)
frame_event_ref(event_);
if (touch_count_changed_) {
/* Compute initial gesture slice properties */
GetValues(gesture, touches, &original_center_x_, &original_center_y_,
&original_radius_, &original_angle_, true);
radius_ = original_radius_;
angle_ = original_angle_;
CheckGestureEnd();
} else {
/* Compute updated gesture slice properties */
SetTransforms(gesture);
SetCenterOfRotation();
CheckGestureEnd();
}
}
/**
* @internal
* Get gesture slice transformation properties
*/
void UGSlice::GetValues(Gesture &gesture, const TouchMap& touches,
float* x, float* y, float* radius, float* angle,
bool init) {
*x = 0;
*y = 0;
/* Accumulate X and Y positions */
for (const auto& pair : touches) {
const SharedTouch& grail_touch = pair.second;
UFTouch touch;
UFStatus status = frame_frame_get_touch_by_id(frame_, grail_touch->id(),
&touch);
if (status != UFStatusSuccess) {
LOG(Warn) << "failed to get touch from frame\n";
continue;
}
if (gesture.recognizer().device_direct()) {
*x += frame_touch_get_window_x(touch);
*y += frame_touch_get_window_y(touch);
} else {
*x += frame_touch_get_device_x(touch);
*y += frame_touch_get_device_y(touch);
}
}
/* Calculate centroid of touches */
*x /= touches.size();
*y /= touches.size();
/* Two or more touches are needed for radius and angle calculations */
if (touches.size() == 1)
return;
*radius = 0;
*angle = 0;
int num_angles = 0;
for (const auto& pair : touches) {
const SharedTouch& grail_touch = pair.second;
UFTouch touch;
float cur_x;
float cur_y;
UFStatus status = frame_frame_get_touch_by_id(frame_, grail_touch->id(),
&touch);
if (status != UFStatusSuccess) {
LOG(Warn) << "failed to get touch from frame\n";
continue;
}
if (gesture.recognizer().device_direct()) {
cur_x = frame_touch_get_window_x(touch);
cur_y = frame_touch_get_window_y(touch);
} else {
cur_x = frame_touch_get_device_x(touch);
cur_y = frame_touch_get_device_y(touch);
}
/* Accumulate distance from each point to the centroid */
*radius += std::sqrt(
(cur_x - *x) * (cur_x - *x) + (cur_y - *y) * (cur_y - *y));
/* Calculate the angle around a circle centered at the centroid from a
* theoretical point at (1, 0) to the current touch */
float new_angle = std::atan2(cur_y - *y, cur_x - *x);
if (init) {
/* If this is a new calculation, accumulate angles */
*angle += new_angle;
num_angles++;
} else if (frame_touch_get_state(touch) != UFTouchStateBegin) {
/* Update touch angle if the touch has moved */
float prev_angle = gesture.AngleForTouch(grail_touch->id());
if (new_angle - prev_angle < -M_PI)
new_angle += 2 * M_PI;
else if (new_angle - prev_angle > M_PI)
new_angle -= 2 * M_PI;
*angle += new_angle - prev_angle;
num_angles++;
}
/* Save the touch angle in the gesture state */
gesture.SetAngleForTouch(grail_touch->id(), new_angle);
}
/* Calculate the average angle of the touches */
*radius /= touches.size();
*angle /= num_angles;
if (!init)
*angle += angle_;
}
/**
* @internal
* Calculate the new 2 dimensional affine transformation for the slice
*/
void UGSlice::SetTransforms(Gesture &gesture) {
float center_x;
float center_y;
float new_radius = radius_;
float new_angle = angle_;
/* Get the transformation values for the updated touches */
GetValues(gesture, touches_, ¢er_x, ¢er_y, &new_radius, &new_angle,
false);
if (!touch_count_changed_) {
/* If the touch count has not changed, calculate new transforms */
float scale = radius_ ? new_radius / radius_ : 1;
transform_[0][0] = std::cos(new_angle - angle_) * scale;
transform_[0][1] = -std::sin(new_angle - angle_) * scale;
transform_[0][2] =
center_x - cumulative_transform_[0][2] - original_center_x_;
transform_[1][0] = -transform_[0][1];
transform_[1][1] = transform_[0][0];
transform_[1][2] =
center_y - cumulative_transform_[1][2] - original_center_y_;
scale = original_radius_ ? new_radius / original_radius_ : 1;
cumulative_transform_[0][0] = std::cos(new_angle - original_angle_) * scale;
cumulative_transform_[0][1] =
-std::sin(new_angle - original_angle_) * scale;
cumulative_transform_[0][2] = center_x - original_center_x_;
cumulative_transform_[1][0] = -cumulative_transform_[0][1];
cumulative_transform_[1][1] = cumulative_transform_[0][0];
cumulative_transform_[1][2] = center_y - original_center_y_;
} else {
/* If the touch count has changed, the transforms are set appropriately in
* the slice constructor. Update the transformation state values here. */
original_radius_ += new_radius - radius_;
original_angle_ += new_angle - angle_;
original_center_x_ +=
center_x - (original_center_x_ + cumulative_transform_[0][2]);
original_center_y_ +=
center_y - (original_center_y_ + cumulative_transform_[1][2]);
}
/* Save the new radius and angle */
radius_ = new_radius;
angle_ = new_angle;
}
/**
* @internal
* Determine the center of rotation point.
*
* For any given point q that is transformed by a 2D affine transformation
* matrix T about anchor point P the new point q' may be determined by the
* following equation:
*
* q' = T * (q - P) + P
*
* T and P are dependent, so we can modify one and find a new value for the
* other. We will label the original T and P as T0 and P0, and the new values
* will be labeled T1 and P1. We can find new values by solving the following
* equation:
*
* q' = T0 * (q - P0) + P0 = T1 * (q - P1) + P1
*
* In the calculations below, we use variables for the scalar values
* that make up T0, P0, T1, and P1:
*
* T0: [ a -b c ] P0: [ x0 ] T1: [ a -b 0 ] P1: [ x1 ]
* [ b a d ] [ y0 ] [ b a 0 ] [ y1 ]
* [ 0 0 1 ] [ 0 ] [ 0 0 1 ] [ 0 ]
*
* Note that rotation and scaling are independent of the anchor point, so a and
* b are equivalent between the transformation matrices.
*
* Since we know all the values of T0, P0, and T1, we can calculate the values
* x1 and y1 in P1.
*/
void UGSlice::SetCenterOfRotation() {
float a = transform_[0][0];
float b = transform_[1][0];
float c = transform_[0][2];
float d = transform_[0][2];
float x0 = original_center_x_ + cumulative_transform_[0][2];
float y0 = original_center_y_ + cumulative_transform_[1][2];
float x1;
float y1;
float div = a*a - 2*a + b*b + 1;
if (std::fabs(div) < 1e-5)
return;
x1 = (a*a*x0 - a*(2*x0+c) + b*b*x0 - b*d + c + x0) / div;
y1 = (a*a*y0 - a*(2*y0+d) + b*b*y0 + b*c + d + y0) / div;
center_of_rotation_x_ = x1;
center_of_rotation_y_ = y1;
}
/**
* @internal
* Check if the gesture has ended
*/
void UGSlice::CheckGestureEnd() {
/* Get number of physically non-ended touches */
unsigned int num_active_touches = 0;
for (const auto& pair : touches_) {
const SharedTouch& touch = pair.second;
if (!touch->pending_end() && !touch->ended())
num_active_touches++;
}
/* Check if currently active touches is outside range for subscription */
unsigned int touches_start = subscription_->touches_start();
unsigned int touches_min = subscription_->touches_min();
if ((!touches_min && num_active_touches < touches_start) ||
(touches_min && num_active_touches < touches_min))
physically_ended_ = true;
}
/**
* @internal
* Calculate the square of the cumulative drag of the gesture
*/
float UGSlice::CumulativeDrag2(float device_x_res, float device_y_res) const {
return std::fabs(cumulative_transform_[0][2] / device_x_res *
cumulative_transform_[0][2] / device_x_res +
cumulative_transform_[1][2] / device_y_res *
cumulative_transform_[1][2] / device_y_res);
}
/**
* @internal
* Calculate the cumulative pinch of the gesture.
*/
float UGSlice::CumulativePinch() const {
float pinch = original_radius_ ? radius_ / original_radius_ : 1;
return (pinch >= 1 ? pinch : 1 / pinch);
}
/**
* @internal
* Check if any subscribed gesture primitives have matched due to this slice
*/
UGGestureTypeMask UGSlice::CheckRecognition(const Gesture& gesture) {
const UGSubscription &subscription = *subscription_;
float res_x = gesture.recognizer().device_x_res();
float res_y = gesture.recognizer().device_y_res();
/* the cumulative time the gesture has been physically active */
uint64_t cumulative_time = time_ - gesture.start_time();
if ((subscription.mask() & UGGestureTypeDrag) &&
(!subscription.drag().timeout ||
cumulative_time < subscription.drag().timeout) &&
CumulativeDrag2(res_x, res_y) > (subscription.drag().threshold *
subscription.drag().threshold))
recognized_ |= UGGestureTypeDrag;
if ((subscription.mask() & UGGestureTypePinch) &&
(!subscription.pinch().timeout ||
cumulative_time < subscription.pinch().timeout) &&
CumulativePinch() > subscription.pinch().threshold)
recognized_ |= UGGestureTypePinch;
if ((subscription.mask() & UGGestureTypeRotate) &&
(!subscription.rotate().timeout ||
cumulative_time < subscription.rotate().timeout) &&
std::fabs(angle_ - original_angle_) > subscription.rotate().threshold)
recognized_ |= UGGestureTypeRotate;
if ((subscription.mask() & UGGestureTypeTap) &&
cumulative_time < subscription.tap().timeout &&
CumulativeDrag2(res_x, res_y) < (subscription.tap().threshold *
subscription.tap().threshold) &&
physically_ended_)
recognized_ |= UGGestureTypeTap;
if (subscription.mask() & UGGestureTypeTouch)
recognized_ |= UGGestureTypeTouch;
return recognized_;
}
UGStatus UGSlice::GetTouchId(unsigned int index, UFTouchId* touch_id) const {
if (index >= touches_.size())
return UGStatusErrorInvalidIndex;
auto it = touches_.cbegin();
std::advance(it, index);
*touch_id = it->first;
return UGStatusSuccess;
}
UGStatus UGSlice::GetProperty(UGSliceProperty property, void* value) const {
switch (property) {
case UGSlicePropertyId:
*reinterpret_cast(value) = id_;
return UGStatusSuccess;
case UGSlicePropertyState:
*reinterpret_cast(value) = state_;
return UGStatusSuccess;
case UGSlicePropertySubscription:
*reinterpret_cast(value) = subscription_;
return UGStatusSuccess;
case UGSlicePropertyRecognized:
*reinterpret_cast(value) = recognized_;
return UGStatusSuccess;
case UGSlicePropertyNumTouches:
*reinterpret_cast(value) = touches_.size();
return UGStatusSuccess;
case UGSlicePropertyFrame:
*reinterpret_cast(value) = frame_;
return UGStatusSuccess;
case UGSlicePropertyOriginalCenterX:
*reinterpret_cast(value) = original_center_x_;
return UGStatusSuccess;
case UGSlicePropertyOriginalCenterY:
*reinterpret_cast(value) = original_center_y_;
return UGStatusSuccess;
case UGSlicePropertyOriginalRadius:
*reinterpret_cast(value) = original_radius_;
return UGStatusSuccess;
case UGSlicePropertyTransform:
*reinterpret_cast(value) = &transform_;
return UGStatusSuccess;
case UGSlicePropertyCumulativeTransform:
*reinterpret_cast(value) = &cumulative_transform_;
return UGStatusSuccess;
case UGSlicePropertyCenterOfRotationX:
*reinterpret_cast(value) = center_of_rotation_x_;
return UGStatusSuccess;
case UGSlicePropertyCenterOfRotationY:
*reinterpret_cast(value) = center_of_rotation_y_;
return UGStatusSuccess;
case UGSlicePropertyConstructionFinished:
*reinterpret_cast(value) = construction_finished_;
return UGStatusSuccess;
}
return UGStatusErrorUnknownProperty;
}
UGSlice::~UGSlice() {
frame_event_unref(event_);
}
} // namespace grail
} // namespace oif
extern "C" {
UGStatus grail_slice_get_property(const UGSlice slice, UGSliceProperty property,
void* value) {
return static_cast(slice)->GetProperty(property,
value);
}
unsigned int grail_slice_get_id(const UGSlice slice) {
unsigned int id;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyId, &id);
MUST_SUCCEED(status);
return id;
}
UGGestureState grail_slice_get_state(const UGSlice slice) {
UGGestureState state;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyState, &state);
MUST_SUCCEED(status);
return state;
}
UGGestureTypeMask grail_slice_get_recognized(const UGSlice slice) {
UGGestureTypeMask mask;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyRecognized, &mask);
MUST_SUCCEED(status);
return mask;
}
UGSubscription grail_slice_get_subscription(const UGSlice slice) {
UGSubscription subscription;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertySubscription, &subscription);
MUST_SUCCEED(status);
return subscription;
}
unsigned int grail_slice_get_num_touches(const UGSlice slice) {
unsigned int num_touches;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyNumTouches, &num_touches);
MUST_SUCCEED(status);
return num_touches;
}
UGStatus grail_slice_get_touch_id(const UGSlice slice, unsigned int index,
UFTouchId *touch_id) {
return static_cast(slice)->GetTouchId(
index,
touch_id);
}
float grail_slice_get_original_center_x(const UGSlice slice) {
float x;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyOriginalCenterX, &x);
MUST_SUCCEED(status);
return x;
}
float grail_slice_get_original_center_y(const UGSlice slice) {
float y;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyOriginalCenterY, &y);
MUST_SUCCEED(status);
return y;
}
float grail_slice_get_original_radius(const UGSlice slice) {
float radius;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyOriginalRadius, &radius);
MUST_SUCCEED(status);
return radius;
}
float grail_slice_get_center_of_rotation_x(const UGSlice slice) {
float x;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyCenterOfRotationX, &x);
MUST_SUCCEED(status);
return x;
}
float grail_slice_get_center_of_rotation_y(const UGSlice slice) {
float y;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyCenterOfRotationY, &y);
MUST_SUCCEED(status);
return y;
}
const UGTransform *grail_slice_get_transform(const UGSlice slice) {
UGTransform *transform;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyTransform, &transform);
MUST_SUCCEED(status);
return transform;
}
const UGTransform *grail_slice_get_cumulative_transform(const UGSlice slice) {
UGTransform *transform;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyCumulativeTransform, &transform);
MUST_SUCCEED(status);
return transform;
}
const UFFrame grail_slice_get_frame(const UGSlice slice) {
UFFrame frame;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyFrame, &frame);
MUST_SUCCEED(status);
return frame;
}
int grail_slice_get_construction_finished(const UGSlice slice) {
int construction_finished;
UGStatus status =
static_cast(slice)->GetProperty(
UGSlicePropertyConstructionFinished,
&construction_finished);
MUST_SUCCEED(status);
return construction_finished;
}
} // extern "C"
grail-3.1.0+16.04.20160125/src/forward.h 0000644 0000156 0000165 00000004617 12651522342 017570 0 ustar pbuser pbgroup 0000000 0000000 /*****************************************************************************
*
* grail - Multitouch Gesture Recognition Library
*
* Copyright (C) 2011-2012 Canonical Ltd.
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3
* as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranties of
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
****************************************************************************/
#ifndef GRAIL_FORWARD_H_
#define GRAIL_FORWARD_H_
#include