/** * clutter_gesture_action_get_velocity: * @action: a #ClutterGestureAction * @point: the touch point index, with 0 being the first touch * point received by the action * @velocity_x: (out) (allow-none): return location for the latest motion * event's X velocity * @velocity_y: (out) (allow-none): return location for the latest motion * event's Y velocity * * Retrieves the velocity, in stage pixels per millisecond, of the * latest motion event during the dragging. * * Since: 1.12 */ gfloat clutter_gesture_action_get_velocity (ClutterGestureAction *action, guint point, gfloat *velocity_x, gfloat *velocity_y) { gfloat d_x, d_y, distance, velocity; gint64 d_t; g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), 0); g_return_val_if_fail (action->priv->points->len > point, 0); distance = clutter_gesture_action_get_motion_delta (action, point, &d_x, &d_y); d_t = g_array_index (action->priv->points, GesturePoint, point).last_delta_time; if (velocity_x) *velocity_x = d_t > FLOAT_EPSILON ? d_x / d_t : 0; if (velocity_y) *velocity_y = d_t > FLOAT_EPSILON ? d_y / d_t : 0; velocity = d_t > FLOAT_EPSILON ? distance / d_t : 0; return velocity; }
/** * clutter_pan_action_get_motion_delta: * @self: A #ClutterPanAction * @point: the touch point index, with 0 being the first touch * point received by the action * @delta_x: (out) (allow-none): return location for the X delta * @delta_y: (out) (allow-none): return location for the Y delta * * Retrieves the delta, in stage space, dependent on the current state * of the #ClutterPanAction. If it is inactive, both fields will be * set to 0. If it is panning by user action, the values will be equivalent * to those returned by clutter_gesture_action_get_motion_delta(). * If it is interpolating with some form of kinetic scrolling, the values * will be equivalent to those returned by * clutter_pan_action_get_interpolated_delta(). This is a convenience * method designed to be used in replacement "pan" signal handlers. */ gfloat clutter_pan_action_get_motion_delta (ClutterPanAction *self, guint point, gfloat *delta_x, gfloat *delta_y) { ClutterPanActionPrivate *priv; g_return_val_if_fail (CLUTTER_IS_PAN_ACTION (self), 0.0f); priv = self->priv; switch (priv->state) { case PAN_STATE_INACTIVE: if (delta_x) *delta_x = 0; if (delta_y) *delta_y = 0; return 0; case PAN_STATE_PANNING: return clutter_gesture_action_get_motion_delta (CLUTTER_GESTURE_ACTION (self), point, delta_x, delta_y); case PAN_STATE_INTERPOLATING: return clutter_pan_action_get_interpolated_delta (self, delta_x, delta_y); default: g_assert_not_reached (); } }
static gboolean on_pan (ClutterPanAction *action, ClutterActor *scroll, gboolean is_interpolated, gpointer *user_data) { gfloat delta_x, delta_y; const ClutterEvent *event = NULL; if (is_interpolated) clutter_pan_action_get_interpolated_delta (action, &delta_x, &delta_y); else { clutter_gesture_action_get_motion_delta (CLUTTER_GESTURE_ACTION (action), 0, &delta_x, &delta_y); event = clutter_gesture_action_get_last_event (CLUTTER_GESTURE_ACTION (action), 0); } g_print ("[%s] panning dx:%.2f dy:%.2f\n", event == NULL ? "INTERPOLATED" : event->type == CLUTTER_MOTION ? "MOTION" : event->type == CLUTTER_TOUCH_UPDATE ? "TOUCH UPDATE" : "?", delta_x, delta_y); return TRUE; }
static void gesture_end (ClutterGestureAction *gesture, ClutterActor *actor) { ClutterPanAction *self = CLUTTER_PAN_ACTION (gesture); ClutterPanActionPrivate *priv = self->priv; gfloat velocity, velocity_x, velocity_y; gfloat delta_x, delta_y; gfloat tau; gint duration; clutter_gesture_action_get_release_coords (CLUTTER_GESTURE_ACTION (self), 0, &priv->release_x, &priv->release_y); if (!priv->should_interpolate) { priv->state = PAN_STATE_INACTIVE; return; } priv->state = PAN_STATE_INTERPOLATING; clutter_gesture_action_get_motion_delta (gesture, 0, &delta_x, &delta_y); velocity = clutter_gesture_action_get_velocity (gesture, 0, &velocity_x, &velocity_y); /* Exponential timing constant v(t) = v(0) * exp(-t/tau) * tau = 1000ms / (frame_per_second * - ln(decay_per_frame)) * with frame_per_second = 60 and decay_per_frame = 0.95, tau ~= 325ms * see http://ariya.ofilabs.com/2011/10/flick-list-with-its-momentum-scrolling-and-deceleration.html */ tau = 1000.0f / (reference_fps * - logf (priv->deceleration_rate)); /* See where the decreasing velocity reaches $min_velocity px/ms * v(t) = v(0) * exp(-t/tau) = min_velocity * t = - tau * ln( min_velocity / |v(0)|) */ duration = - tau * logf (min_velocity / (ABS (velocity) * priv->acceleration_factor)); /* Target point: x(t) = v(0) * tau * [1 - exp(-t/tau)] */ priv->target_x = velocity_x * priv->acceleration_factor * tau * (1 - exp ((float)-duration / tau)); priv->target_y = velocity_y * priv->acceleration_factor * tau * (1 - exp ((float)-duration / tau)); if (ABS (velocity) * priv->acceleration_factor > min_velocity && duration > FLOAT_EPSILON) { priv->interpolated_x = priv->interpolated_y = 0.0f; priv->deceleration_timeline = clutter_timeline_new (duration); clutter_timeline_set_progress_mode (priv->deceleration_timeline, CLUTTER_EASE_OUT_EXPO); g_signal_connect (priv->deceleration_timeline, "new_frame", G_CALLBACK (on_deceleration_new_frame), self); g_signal_connect (priv->deceleration_timeline, "stopped", G_CALLBACK (on_deceleration_stopped), self); clutter_timeline_start (priv->deceleration_timeline); } else { emit_pan_stopped (self, actor); } }