ec3b1a7b90
Instead of using our own homegrown alpha functions, we should use the easing functions also shared by other animation frameworks, like jQuery and Tween, in the interests of code portability. The easing functions have been defined by Robert Penner and are divided into three categories: In Out InOut Each category has a particular curve: Quadratic Cubic Quartic Quintic Sinusoidal Exponential Circular In addition, there are "physical" curves: Elastic Back (overshooting cubic) Bounce (exponentially decaying parabolic) Finally, the Linear curve is also provided as a reference. The functions are private, and are meant to be used only through their logical id as provided by the AnimationMode enumeration. The tests should be updated as well to match the new easing functions.
1201 lines
33 KiB
C
1201 lines
33 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
|
* Jorn Baayen <jorn@openedhand.com>
|
|
* Emmanuele Bassi <ebassi@openedhand.com>
|
|
* Tomas Frydrych <tf@openedhand.com>
|
|
*
|
|
* Copyright (C) 2006, 2007, 2008 OpenedHand
|
|
* Copyright (C) 2009 Intel Corp.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* 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, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-alpha
|
|
* @short_description: A class for calculating an alpha value as a function
|
|
* of time.
|
|
*
|
|
* #ClutterAlpha is a class for calculating an floating point value
|
|
* dependent only on the position of a #ClutterTimeline.
|
|
*
|
|
* A #ClutterAlpha binds a #ClutterTimeline to a progress function which
|
|
* translates the time T into an adimensional factor alpha. The factor can
|
|
* then be used to drive a #ClutterBehaviour, which will translate the
|
|
* alpha value into something meaningful for a #ClutterActor.
|
|
*
|
|
* You should provide a #ClutterTimeline and bind it to the #ClutterAlpha
|
|
* instance using clutter_alpha_set_timeline(). You should also set an
|
|
* "animation mode", either by using the #ClutterAnimatioMode values that
|
|
* Clutter itself provides or by registering custom functions using
|
|
* clutter_alpha_register_func().
|
|
*
|
|
* Instead of a #ClutterAnimationMode you may provide a function returning
|
|
* the alpha value depending on the progress of the timeline, using
|
|
* clutter_alpha_set_func() or clutter_alpha_set_closure(). The alpha
|
|
* function will be executed each time a new frame in the #ClutterTimeline
|
|
* is reached.
|
|
*
|
|
* Since the alpha function is controlled by the timeline instance, you can
|
|
* pause, stop or resume the #ClutterAlpha from calling the alpha function by
|
|
* using the appropriate functions of the #ClutterTimeline object.
|
|
*
|
|
* #ClutterAlpha is used to "drive" a #ClutterBehaviour instance, and it
|
|
* is internally used by the #ClutterAnimation API.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <math.h>
|
|
|
|
#include "clutter-alpha.h"
|
|
#include "clutter-debug.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-main.h"
|
|
#include "clutter-marshal.h"
|
|
#include "clutter-private.h"
|
|
|
|
G_DEFINE_TYPE (ClutterAlpha, clutter_alpha, G_TYPE_INITIALLY_UNOWNED);
|
|
|
|
|
|
struct _ClutterAlphaPrivate
|
|
{
|
|
ClutterTimeline *timeline;
|
|
guint timeline_new_frame_id;
|
|
|
|
gdouble alpha;
|
|
|
|
GClosure *closure;
|
|
|
|
gulong mode;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_TIMELINE,
|
|
PROP_ALPHA,
|
|
PROP_MODE
|
|
};
|
|
|
|
static void
|
|
timeline_new_frame_cb (ClutterTimeline *timeline,
|
|
guint current_frame_num,
|
|
ClutterAlpha *alpha)
|
|
{
|
|
ClutterAlphaPrivate *priv = alpha->priv;
|
|
|
|
/* Update alpha value and notify */
|
|
priv->alpha = clutter_alpha_get_alpha (alpha);
|
|
g_object_notify (G_OBJECT (alpha), "alpha");
|
|
}
|
|
|
|
static void
|
|
clutter_alpha_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterAlpha *alpha;
|
|
ClutterAlphaPrivate *priv;
|
|
|
|
alpha = CLUTTER_ALPHA (object);
|
|
priv = alpha->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_TIMELINE:
|
|
clutter_alpha_set_timeline (alpha, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_MODE:
|
|
clutter_alpha_set_mode (alpha, g_value_get_ulong (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_alpha_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
ClutterAlpha *alpha;
|
|
ClutterAlphaPrivate *priv;
|
|
|
|
alpha = CLUTTER_ALPHA (object);
|
|
priv = alpha->priv;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_TIMELINE:
|
|
g_value_set_object (value, priv->timeline);
|
|
break;
|
|
|
|
case PROP_ALPHA:
|
|
g_value_set_double (value, priv->alpha);
|
|
break;
|
|
|
|
case PROP_MODE:
|
|
g_value_set_ulong (value, priv->mode);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clutter_alpha_finalize (GObject *object)
|
|
{
|
|
ClutterAlphaPrivate *priv = CLUTTER_ALPHA (object)->priv;
|
|
|
|
if (priv->closure)
|
|
g_closure_unref (priv->closure);
|
|
|
|
G_OBJECT_CLASS (clutter_alpha_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
clutter_alpha_dispose (GObject *object)
|
|
{
|
|
ClutterAlpha *self = CLUTTER_ALPHA(object);
|
|
|
|
clutter_alpha_set_timeline (self, NULL);
|
|
|
|
G_OBJECT_CLASS (clutter_alpha_parent_class)->dispose (object);
|
|
}
|
|
|
|
|
|
static void
|
|
clutter_alpha_class_init (ClutterAlphaClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
object_class->set_property = clutter_alpha_set_property;
|
|
object_class->get_property = clutter_alpha_get_property;
|
|
object_class->finalize = clutter_alpha_finalize;
|
|
object_class->dispose = clutter_alpha_dispose;
|
|
|
|
g_type_class_add_private (klass, sizeof (ClutterAlphaPrivate));
|
|
|
|
/**
|
|
* ClutterAlpha:timeline:
|
|
*
|
|
* A #ClutterTimeline instance used to drive the alpha function.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
pspec = g_param_spec_object ("timeline",
|
|
"Timeline",
|
|
"Timeline used by the alpha",
|
|
CLUTTER_TYPE_TIMELINE,
|
|
CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_TIMELINE, pspec);
|
|
|
|
/**
|
|
* ClutterAlpha:alpha:
|
|
*
|
|
* The alpha value as computed by the alpha function. The linear
|
|
* interval is 0.0 to 1.0, but the Alpha allows overshooting by
|
|
* one unit in each direction, so the valid interval is -1.0 to 2.0.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
pspec = g_param_spec_double ("alpha",
|
|
"Alpha value",
|
|
"Alpha value",
|
|
-1.0, 2.0,
|
|
0.0,
|
|
CLUTTER_PARAM_READABLE);
|
|
g_object_class_install_property (object_class, PROP_ALPHA, pspec);
|
|
|
|
/**
|
|
* ClutterAlpha:mode:
|
|
*
|
|
* The progress function logical id - either a value from the
|
|
* #ClutterAnimationMode enumeration or a value returned by
|
|
* clutter_alpha_register_func().
|
|
*
|
|
* If %CLUTTER_CUSTOM_MODE is used then the function set using
|
|
* clutter_alpha_set_closure() or clutter_alpha_set_func()
|
|
* will be used.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_ulong ("mode",
|
|
"Mode",
|
|
"Progress mode",
|
|
0, G_MAXULONG,
|
|
CLUTTER_CUSTOM_MODE,
|
|
G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE);
|
|
g_object_class_install_property (object_class, PROP_MODE, pspec);
|
|
}
|
|
|
|
static void
|
|
clutter_alpha_init (ClutterAlpha *self)
|
|
{
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
|
CLUTTER_TYPE_ALPHA,
|
|
ClutterAlphaPrivate);
|
|
|
|
self->priv->mode = CLUTTER_CUSTOM_MODE;
|
|
self->priv->alpha = 0.0;
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_get_alpha:
|
|
* @alpha: A #ClutterAlpha
|
|
*
|
|
* Query the current alpha value.
|
|
*
|
|
* Return Value: The current alpha value for the alpha
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
gdouble
|
|
clutter_alpha_get_alpha (ClutterAlpha *alpha)
|
|
{
|
|
ClutterAlphaPrivate *priv;
|
|
gdouble retval = 0;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), 0);
|
|
|
|
priv = alpha->priv;
|
|
|
|
if (G_LIKELY (priv->closure))
|
|
{
|
|
GValue params = { 0, };
|
|
GValue result_value = { 0, };
|
|
|
|
g_object_ref (alpha);
|
|
|
|
g_value_init (&result_value, G_TYPE_DOUBLE);
|
|
|
|
g_value_init (¶ms, CLUTTER_TYPE_ALPHA);
|
|
g_value_set_object (¶ms, alpha);
|
|
|
|
g_closure_invoke (priv->closure, &result_value, 1, ¶ms, NULL);
|
|
|
|
retval = g_value_get_double (&result_value);
|
|
|
|
g_value_unset (&result_value);
|
|
g_value_unset (¶ms);
|
|
|
|
g_object_unref (alpha);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* clutter_alpha_set_closure_internal:
|
|
* @alpha: a #ClutterAlpha
|
|
* @closure: a #GClosure
|
|
*
|
|
* Sets the @closure for @alpha. This function does not
|
|
* set the #ClutterAlpha:mode property and does not emit
|
|
* the #GObject::notify signal for it.
|
|
*/
|
|
static inline void
|
|
clutter_alpha_set_closure_internal (ClutterAlpha *alpha,
|
|
GClosure *closure)
|
|
{
|
|
ClutterAlphaPrivate *priv = alpha->priv;
|
|
|
|
if (priv->closure)
|
|
g_closure_unref (priv->closure);
|
|
|
|
/* need to take ownership of the closure before sinking it */
|
|
priv->closure = g_closure_ref (closure);
|
|
g_closure_sink (closure);
|
|
|
|
/* set the marshaller */
|
|
if (G_CLOSURE_NEEDS_MARSHAL (closure))
|
|
{
|
|
GClosureMarshal marshal = clutter_marshal_DOUBLE__VOID;
|
|
|
|
g_closure_set_marshal (closure, marshal);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_set_closure:
|
|
* @alpha: A #ClutterAlpha
|
|
* @closure: A #GClosure
|
|
*
|
|
* Sets the #GClosure used to compute the alpha value at each
|
|
* frame of the #ClutterTimeline bound to @alpha.
|
|
*
|
|
* Since: 0.8
|
|
*/
|
|
void
|
|
clutter_alpha_set_closure (ClutterAlpha *alpha,
|
|
GClosure *closure)
|
|
{
|
|
ClutterAlphaPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
|
|
g_return_if_fail (closure != NULL);
|
|
|
|
priv = alpha->priv;
|
|
|
|
clutter_alpha_set_closure_internal (alpha, closure);
|
|
|
|
priv->mode = CLUTTER_CUSTOM_MODE;
|
|
g_object_notify (G_OBJECT (alpha), "mode");
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_set_func:
|
|
* @alpha: A #ClutterAlpha
|
|
* @func: A #ClutterAlphaFunc
|
|
* @data: user data to be passed to the alpha function, or %NULL
|
|
* @destroy: notify function used when disposing the alpha function
|
|
*
|
|
* Sets the #ClutterAlphaFunc function used to compute
|
|
* the alpha value at each frame of the #ClutterTimeline
|
|
* bound to @alpha.
|
|
*
|
|
* This function will not register @func as a global alpha function.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
void
|
|
clutter_alpha_set_func (ClutterAlpha *alpha,
|
|
ClutterAlphaFunc func,
|
|
gpointer data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
ClutterAlphaPrivate *priv;
|
|
GClosure *closure;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
|
|
g_return_if_fail (func != NULL);
|
|
|
|
priv = alpha->priv;
|
|
|
|
closure = g_cclosure_new (G_CALLBACK (func), data, (GClosureNotify) destroy);
|
|
clutter_alpha_set_closure_internal (alpha, closure);
|
|
|
|
priv->mode = CLUTTER_CUSTOM_MODE;
|
|
g_object_notify (G_OBJECT (alpha), "mode");
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_set_timeline:
|
|
* @alpha: A #ClutterAlpha
|
|
* @timeline: A #ClutterTimeline
|
|
*
|
|
* Binds @alpha to @timeline.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
void
|
|
clutter_alpha_set_timeline (ClutterAlpha *alpha,
|
|
ClutterTimeline *timeline)
|
|
{
|
|
ClutterAlphaPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
|
|
g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline));
|
|
|
|
priv = alpha->priv;
|
|
|
|
if (priv->timeline == timeline)
|
|
return;
|
|
|
|
if (priv->timeline)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (priv->timeline,
|
|
timeline_new_frame_cb,
|
|
alpha);
|
|
|
|
g_object_unref (priv->timeline);
|
|
priv->timeline = NULL;
|
|
}
|
|
|
|
if (timeline)
|
|
{
|
|
priv->timeline = g_object_ref (timeline);
|
|
|
|
g_signal_connect (priv->timeline, "new-frame",
|
|
G_CALLBACK (timeline_new_frame_cb),
|
|
alpha);
|
|
}
|
|
|
|
g_object_notify (G_OBJECT (alpha), "timeline");
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_get_timeline:
|
|
* @alpha: A #ClutterAlpha
|
|
*
|
|
* Gets the #ClutterTimeline bound to @alpha.
|
|
*
|
|
* Return value: a #ClutterTimeline instance
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
ClutterTimeline *
|
|
clutter_alpha_get_timeline (ClutterAlpha *alpha)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), NULL);
|
|
|
|
return alpha->priv->timeline;
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_new:
|
|
*
|
|
* Creates a new #ClutterAlpha instance. You must set a function
|
|
* to compute the alpha value using clutter_alpha_set_func() and
|
|
* bind a #ClutterTimeline object to the #ClutterAlpha instance
|
|
* using clutter_alpha_set_timeline().
|
|
*
|
|
* You should use the newly created #ClutterAlpha instance inside
|
|
* a #ClutterBehaviour object.
|
|
*
|
|
* Return value: the newly created empty #ClutterAlpha instance.
|
|
*
|
|
* Since: 0.2
|
|
*/
|
|
ClutterAlpha *
|
|
clutter_alpha_new (void)
|
|
{
|
|
return g_object_new (CLUTTER_TYPE_ALPHA, NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_new_full:
|
|
* @timeline: #ClutterTimeline timeline
|
|
* @mode: animation mode
|
|
*
|
|
* Creates a new #ClutterAlpha instance and sets the timeline
|
|
* and animation mode.
|
|
*
|
|
* See also clutter_alpha_set_timeline() and clutter_alpha_set_mode().
|
|
*
|
|
* Return Value: the newly created #ClutterAlpha
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterAlpha *
|
|
clutter_alpha_new_full (ClutterTimeline *timeline,
|
|
gulong mode)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
|
|
g_return_val_if_fail (mode != CLUTTER_ANIMATION_LAST, NULL);
|
|
|
|
return g_object_new (CLUTTER_TYPE_ALPHA,
|
|
"timeline", timeline,
|
|
"mode", mode,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_new_with_func:
|
|
* @timeline: a #ClutterTimeline
|
|
* @func: a #ClutterAlphaFunc
|
|
* @data: data to pass to the function, or %NULL
|
|
* @destroy: function to call when removing the alpha function, or %NULL
|
|
*
|
|
* Creates a new #ClutterAlpha instances and sets the timeline
|
|
* and the alpha function.
|
|
*
|
|
* This function will not register @func as a global alpha function.
|
|
*
|
|
* See also clutter_alpha_set_timeline() and clutter_alpha_set_func().
|
|
*
|
|
* Return value: the newly created #ClutterAlpha
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
ClutterAlpha *
|
|
clutter_alpha_new_with_func (ClutterTimeline *timeline,
|
|
ClutterAlphaFunc func,
|
|
gpointer data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
ClutterAlpha *retval;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
|
|
g_return_val_if_fail (func != NULL, NULL);
|
|
|
|
retval = clutter_alpha_new ();
|
|
clutter_alpha_set_timeline (retval, timeline);
|
|
clutter_alpha_set_func (retval, func, data, destroy);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_get_mode:
|
|
* @alpha: a #ClutterAlpha
|
|
*
|
|
* Retrieves the #ClutterAnimatioMode used by @alpha.
|
|
*
|
|
* Return value: the animation mode
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
gulong
|
|
clutter_alpha_get_mode (ClutterAlpha *alpha)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), CLUTTER_CUSTOM_MODE);
|
|
|
|
return alpha->priv->mode;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_linear (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
|
|
return clutter_timeline_get_progress (timeline);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_quad (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble progress = clutter_timeline_get_progress (timeline);
|
|
|
|
return progress * progress;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_quad (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return -1.0 * (t /= d) * (t - 2);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_quad (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
if ((t /= d / 2) < 1)
|
|
return 0.5 * t * t;
|
|
|
|
return -0.5 * ((--t) * (t - 2) - 1);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_cubic (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return (t /= d) * t * t;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_cubic (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return (t = t / d - 1) * t * t + 1;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_cubic (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
if ((t /= d / 2) < 1)
|
|
return 0.5 * t * t * t;
|
|
|
|
return 0.5 * ((t -= 2) * t * t + 2);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_quart (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return (t /= d) * t * t * t;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_quart (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return -0.5 * ((t = t / d - 1) * t * t * t - 1);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_quart (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
if ((t /= d / 2) < 1)
|
|
return 0.5 * t * t * t * t;
|
|
|
|
return -0.5 * ((t -= 2) * t * t * t - 2);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_quint (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return (t /= d) * t * t * t * t;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_quint (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return (t = t / d - 1) * t * t * t * t + 1;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_quint (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
if ((t /= d / 2) < 1)
|
|
return 0.5 * t * t * t * t * t;
|
|
|
|
return 0.5 * ((t -= 2) * t * t * t * t + 2);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_sine (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return -1.0 * cos (t / d * G_PI_2) + 1.0;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_sine (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return sin (t / d * G_PI_2);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_sine (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return -0.5 * (cos (G_PI * t / d) - 1);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_expo (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return (t == 0) ? 0.0 : pow (2, 10 * (t / d - 1));
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_expo (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return (t == d) ? 1.0 : -pow (2, -10 * t / d) + 1;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_expo (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
if (t == 0)
|
|
return 0.0;
|
|
|
|
if (t == d)
|
|
return 1.0;
|
|
|
|
if ((t /= d / 2) < 1)
|
|
return 0.5 * pow (2, 10 * (t - 1));
|
|
|
|
return 0.5 * (-pow (2, -10 * --t) + 2);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_circ (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return -1.0 * (sqrt (1 - (t /= d) * t) - 1);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_circ (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return sqrt (1 - (t = t / d - 1) * t);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_circ (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
if ((t /= d / 2) < 1)
|
|
return -0.5 * (sqrt (1 - t * t) - 1);
|
|
|
|
return 0.5 * (sqrt (1 - (t -= 2) * t) + 1);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_elastic (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
gdouble p = d * .3;
|
|
gdouble s = p / 4;
|
|
|
|
if ((t /= d) == 1)
|
|
return 1.0;
|
|
|
|
return -(pow (2, 10 * (t -= 1)) * sin ((t * d - s) * (2 * G_PI) / p));
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_elastic (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
gdouble p = d * .3;
|
|
gdouble s = p / 4;
|
|
|
|
if ((t /= d) == 1)
|
|
return 1.0;
|
|
|
|
return pow (2, -10 * t) * sin ((t * d - s) * (2 * G_PI) / p) + 1.0;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_elastic (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
gdouble p = d * (.3 * 1.5);
|
|
gdouble s = p / 4;
|
|
|
|
if ((t /= d / 2) == 2)
|
|
return 1.0;
|
|
|
|
if (t < 1)
|
|
return -.5 * (pow (2, 10 * (t -= 1)) * sin ((t * d - s) * (2 * G_PI) / p));
|
|
else
|
|
{
|
|
return pow (2, -10 * (t -= 1))
|
|
* sin ((t * d - s) * (2 * G_PI) / p)
|
|
* .5 + 1.0;
|
|
}
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_back (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return (t /= d) * t * ((1.70158 + 1) * t - 1.70158);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_back (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return (t = t / d - 1) * t * ((1.70158 + 1) * t + 1.70158) + 1;
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_back (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
gdouble s = 1.70158;
|
|
|
|
if ((t /= d / 2) < 1)
|
|
return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
|
|
|
|
return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
|
|
}
|
|
|
|
static gdouble
|
|
ease_out_bounce_internal (gdouble t,
|
|
gdouble d)
|
|
{
|
|
if ((t /= d) < (1 / 2.75))
|
|
return 7.5625 * t * t;
|
|
else if (t < (2 / 2.75))
|
|
return 7.5625 * (t -= (1.5 / 2.75)) * t + .75;
|
|
else if (t < (2.5 / 2.75))
|
|
return 7.5625 * (t -= (2.25 / 2.75)) * t + .9375;
|
|
else
|
|
return 7.5625 * (t -= (2.625 / 2.75)) * t + .984375;
|
|
}
|
|
|
|
static gdouble
|
|
ease_in_bounce_internal (gdouble t,
|
|
gdouble d)
|
|
{
|
|
return 1.0 - ease_out_bounce_internal (d - t, d);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_bounce (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return ease_in_bounce_internal (t, d);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_out_bounce (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
return ease_out_bounce_internal (t, d);
|
|
}
|
|
|
|
static gdouble
|
|
clutter_ease_in_out_bounce (ClutterAlpha *alpha,
|
|
gpointer dummy G_GNUC_UNUSED)
|
|
{
|
|
ClutterTimeline *timeline = alpha->priv->timeline;
|
|
gdouble t = clutter_timeline_get_current_frame (timeline);
|
|
gdouble d = clutter_timeline_get_n_frames (timeline);
|
|
|
|
if (t < d / 2)
|
|
return ease_in_bounce_internal (t * 2, d) * 0.5;
|
|
else
|
|
return ease_out_bounce_internal (t * 2 - d, d) * 0.5 + 1.0 * 0.5;
|
|
}
|
|
|
|
/* static enum/function mapping table for the animation modes
|
|
* we provide internally
|
|
*
|
|
* XXX - keep in sync with ClutterAnimationMode
|
|
*/
|
|
static const struct {
|
|
gulong mode;
|
|
ClutterAlphaFunc func;
|
|
} animation_modes[] = {
|
|
{ CLUTTER_CUSTOM_MODE, NULL },
|
|
|
|
{ CLUTTER_LINEAR, clutter_linear },
|
|
{ CLUTTER_EASE_IN_QUAD, clutter_ease_in_quad },
|
|
{ CLUTTER_EASE_OUT_QUAD, clutter_ease_out_quad },
|
|
{ CLUTTER_EASE_IN_OUT_QUAD, clutter_ease_in_out_quad },
|
|
{ CLUTTER_EASE_IN_CUBIC, clutter_ease_in_cubic },
|
|
{ CLUTTER_EASE_OUT_CUBIC, clutter_ease_out_cubic },
|
|
{ CLUTTER_EASE_IN_OUT_CUBIC, clutter_ease_in_out_cubic },
|
|
{ CLUTTER_EASE_IN_QUART, clutter_ease_in_quart },
|
|
{ CLUTTER_EASE_OUT_QUART, clutter_ease_out_quart },
|
|
{ CLUTTER_EASE_IN_OUT_QUART, clutter_ease_in_out_quart },
|
|
{ CLUTTER_EASE_IN_QUINT, clutter_ease_in_quint },
|
|
{ CLUTTER_EASE_OUT_QUINT, clutter_ease_out_quint },
|
|
{ CLUTTER_EASE_IN_OUT_QUINT, clutter_ease_in_out_quint },
|
|
{ CLUTTER_EASE_IN_SINE, clutter_ease_in_sine },
|
|
{ CLUTTER_EASE_OUT_SINE, clutter_ease_out_sine },
|
|
{ CLUTTER_EASE_IN_OUT_SINE, clutter_ease_in_out_sine },
|
|
{ CLUTTER_EASE_IN_EXPO, clutter_ease_in_expo },
|
|
{ CLUTTER_EASE_OUT_EXPO, clutter_ease_out_expo },
|
|
{ CLUTTER_EASE_IN_OUT_EXPO, clutter_ease_in_out_expo },
|
|
{ CLUTTER_EASE_IN_CIRC, clutter_ease_in_circ },
|
|
{ CLUTTER_EASE_OUT_CIRC, clutter_ease_out_circ },
|
|
{ CLUTTER_EASE_IN_OUT_CIRC, clutter_ease_in_out_circ },
|
|
{ CLUTTER_EASE_IN_ELASTIC, clutter_ease_in_elastic },
|
|
{ CLUTTER_EASE_OUT_ELASTIC, clutter_ease_out_elastic },
|
|
{ CLUTTER_EASE_IN_OUT_ELASTIC, clutter_ease_in_out_elastic },
|
|
{ CLUTTER_EASE_IN_BACK, clutter_ease_in_back },
|
|
{ CLUTTER_EASE_OUT_BACK, clutter_ease_out_back },
|
|
{ CLUTTER_EASE_IN_OUT_BACK, clutter_ease_in_out_back },
|
|
{ CLUTTER_EASE_IN_BOUNCE, clutter_ease_in_bounce },
|
|
{ CLUTTER_EASE_OUT_BOUNCE, clutter_ease_out_bounce },
|
|
{ CLUTTER_EASE_IN_OUT_BOUNCE, clutter_ease_in_out_bounce },
|
|
|
|
{ CLUTTER_ANIMATION_LAST, NULL },
|
|
};
|
|
|
|
typedef struct _AlphaData {
|
|
guint closure_set : 1;
|
|
|
|
ClutterAlphaFunc func;
|
|
gpointer data;
|
|
|
|
GClosure *closure;
|
|
} AlphaData;
|
|
|
|
static GPtrArray *clutter_alphas = NULL;
|
|
|
|
/**
|
|
* clutter_alpha_set_mode:
|
|
* @alpha: a #ClutterAlpha
|
|
* @mode: a #ClutterAnimationMode
|
|
*
|
|
* Sets the progress function of @alpha using the symbolic value
|
|
* of @mode, as taken by the #ClutterAnimationMode enumeration or
|
|
* using the value returned by clutter_alpha_register_func().
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_alpha_set_mode (ClutterAlpha *alpha,
|
|
gulong mode)
|
|
{
|
|
ClutterAlphaPrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_ALPHA (alpha));
|
|
g_return_if_fail (mode != CLUTTER_ANIMATION_LAST);
|
|
|
|
priv = alpha->priv;
|
|
|
|
if (mode == CLUTTER_CUSTOM_MODE)
|
|
{
|
|
priv->mode = mode;
|
|
}
|
|
else if (mode < CLUTTER_ANIMATION_LAST)
|
|
{
|
|
GClosure *closure;
|
|
|
|
/* sanity check to avoid getting an out of sync
|
|
* enum/function mapping
|
|
*/
|
|
g_assert (animation_modes[mode].mode == mode);
|
|
g_assert (animation_modes[mode].func != NULL);
|
|
|
|
closure = g_cclosure_new (G_CALLBACK (animation_modes[mode].func),
|
|
NULL,
|
|
NULL);
|
|
clutter_alpha_set_closure_internal (alpha, closure);
|
|
|
|
priv->mode = mode;
|
|
}
|
|
else if (mode > CLUTTER_ANIMATION_LAST)
|
|
{
|
|
AlphaData *alpha_data = NULL;
|
|
gulong real_index = 0;
|
|
|
|
if (G_UNLIKELY (clutter_alphas == NULL))
|
|
{
|
|
g_warning ("No alpha functions defined for ClutterAlpha to use. "
|
|
"Use clutter_alpha_register_func() to register an "
|
|
"alpha function.");
|
|
return;
|
|
}
|
|
|
|
real_index = mode - CLUTTER_ANIMATION_LAST - 1;
|
|
|
|
alpha_data = g_ptr_array_index (clutter_alphas, real_index);
|
|
if (G_UNLIKELY (alpha_data == NULL))
|
|
{
|
|
g_warning ("No alpha function registered for mode %lu.",
|
|
mode);
|
|
return;
|
|
}
|
|
|
|
if (alpha_data->closure_set)
|
|
clutter_alpha_set_closure (alpha, alpha_data->closure);
|
|
else
|
|
{
|
|
GClosure *closure;
|
|
|
|
closure = g_cclosure_new (G_CALLBACK (alpha_data->func),
|
|
alpha_data->data,
|
|
NULL);
|
|
clutter_alpha_set_closure_internal (alpha, closure);
|
|
}
|
|
|
|
priv->mode = mode;
|
|
}
|
|
else
|
|
g_assert_not_reached ();
|
|
|
|
g_object_notify (G_OBJECT (alpha), "mode");
|
|
}
|
|
|
|
static gulong
|
|
register_alpha_internal (AlphaData *alpha_data)
|
|
{
|
|
if (G_UNLIKELY (clutter_alphas == NULL))
|
|
clutter_alphas = g_ptr_array_new ();
|
|
|
|
g_ptr_array_add (clutter_alphas, alpha_data);
|
|
|
|
return clutter_alphas->len + CLUTTER_ANIMATION_LAST;
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_register_func:
|
|
* @func: a #ClutterAlphaFunc
|
|
* @data: user data to pass to @func, or %NULL
|
|
*
|
|
* Registers a global alpha function and returns its logical id
|
|
* to be used by clutter_alpha_set_mode() or by #ClutterAnimation.
|
|
*
|
|
* The logical id is always greater than %CLUTTER_ANIMATION_LAST.
|
|
*
|
|
* Return value: the logical id of the alpha function
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
gulong
|
|
clutter_alpha_register_func (ClutterAlphaFunc func,
|
|
gpointer data)
|
|
{
|
|
AlphaData *alpha_data;
|
|
|
|
g_return_val_if_fail (func != NULL, 0);
|
|
|
|
alpha_data = g_slice_new (AlphaData);
|
|
alpha_data->closure_set = FALSE;
|
|
alpha_data->func = func;
|
|
alpha_data->data = data;
|
|
|
|
return register_alpha_internal (alpha_data);
|
|
}
|
|
|
|
/**
|
|
* clutter_alpha_register_closure:
|
|
* @closure: a #GClosure
|
|
*
|
|
* #GClosure variant of clutter_alpha_register_func().
|
|
*
|
|
* Registers a global alpha function and returns its logical id
|
|
* to be used by clutter_alpha_set_mode() or by #ClutterAnimation.
|
|
*
|
|
* The logical id is always greater than %CLUTTER_ANIMATION_LAST.
|
|
*
|
|
* Return value: the logical id of the alpha function
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
gulong
|
|
clutter_alpha_register_closure (GClosure *closure)
|
|
{
|
|
AlphaData *alpha_data;
|
|
|
|
g_return_val_if_fail (closure != NULL, 0);
|
|
|
|
alpha_data = g_slice_new (AlphaData);
|
|
alpha_data->closure_set = TRUE;
|
|
alpha_data->closure = closure;
|
|
|
|
return register_alpha_internal (alpha_data);
|
|
}
|