[animation] Implement new easing functions
authorEmmanuele Bassi <ebassi@linux.intel.com>
Tue, 20 Jan 2009 17:57:30 +0000 (17:57 +0000)
committerEmmanuele Bassi <ebassi@linux.intel.com>
Tue, 20 Jan 2009 17:57:30 +0000 (17:57 +0000)
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.

clutter/clutter-alpha.c
clutter/clutter-types.h
tests/interactive/test-animation.c
tests/interactive/test-easing.c

index eeef07d..615af0e 100644 (file)
@@ -315,6 +315,37 @@ clutter_alpha_get_alpha (ClutterAlpha *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
@@ -336,18 +367,7 @@ clutter_alpha_set_closure (ClutterAlpha *alpha,
 
   priv = alpha->priv;
 
-  if (priv->closure)
-    g_closure_unref (priv->closure);
-
-  priv->closure = g_closure_ref (closure);
-  g_closure_sink (closure);
-
-  if (G_CLOSURE_NEEDS_MARSHAL (closure))
-    {
-      GClosureMarshal marshal = clutter_marshal_DOUBLE__VOID;
-
-      g_closure_set_marshal (closure, marshal);
-    }
+  clutter_alpha_set_closure_internal (alpha, closure);
 
   priv->mode = CLUTTER_CUSTOM_MODE;
   g_object_notify (G_OBJECT (alpha), "mode");
@@ -374,13 +394,19 @@ clutter_alpha_set_func (ClutterAlpha    *alpha,
                         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 (alpha, closure);
+  clutter_alpha_set_closure_internal (alpha, closure);
+
+  priv->mode = CLUTTER_CUSTOM_MODE;
+  g_object_notify (G_OBJECT (alpha), "mode");
 }
 
 /**
@@ -548,6 +574,418 @@ clutter_alpha_get_mode (ClutterAlpha *alpha)
   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
  *
@@ -557,7 +995,41 @@ static const struct {
   gulong mode;
   ClutterAlphaFunc func;
 } animation_modes[] = {
-  { CLUTTER_CUSTOM_MODE, NULL },
+  { 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 {
@@ -593,15 +1065,24 @@ clutter_alpha_set_mode (ClutterAlpha *alpha,
 
   priv = alpha->priv;
 
-  if (mode < CLUTTER_ANIMATION_LAST)
+  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);
 
-      if (G_LIKELY (animation_modes[mode].func != NULL))
-        clutter_alpha_set_func (alpha, animation_modes[mode].func, NULL, NULL);
+      closure = g_cclosure_new (G_CALLBACK (animation_modes[mode].func),
+                                NULL,
+                                NULL);
+      clutter_alpha_set_closure_internal (alpha, closure);
 
       priv->mode = mode;
     }
@@ -631,9 +1112,14 @@ clutter_alpha_set_mode (ClutterAlpha *alpha,
       if (alpha_data->closure_set)
         clutter_alpha_set_closure (alpha, alpha_data->closure);
       else
-        clutter_alpha_set_func (alpha, alpha_data->func,
-                                alpha_data->data,
-                                NULL);
+        {
+          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;
     }
@@ -643,6 +1129,17 @@ clutter_alpha_set_mode (ClutterAlpha *alpha,
   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
@@ -670,12 +1167,7 @@ clutter_alpha_register_func (ClutterAlphaFunc func,
   alpha_data->func = func;
   alpha_data->data = 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;
+  return register_alpha_internal (alpha_data);
 }
 
 /**
@@ -696,18 +1188,13 @@ clutter_alpha_register_func (ClutterAlphaFunc func,
 gulong
 clutter_alpha_register_closure (GClosure *closure)
 {
-  AlphaData *data;
+  AlphaData *alpha_data;
 
   g_return_val_if_fail (closure != NULL, 0);
 
-  data = g_slice_new (AlphaData);
-  data->closure_set = TRUE;
-  data->closure = closure;
-
-  if (G_UNLIKELY (clutter_alphas == NULL))
-    clutter_alphas = g_ptr_array_new ();
-
-  g_ptr_array_add (clutter_alphas, data);
+  alpha_data = g_slice_new (AlphaData);
+  alpha_data->closure_set = TRUE;
+  alpha_data->closure = closure;
 
-  return clutter_alphas->len + CLUTTER_ANIMATION_LAST;
+  return register_alpha_internal (alpha_data);
 }
index 2e18569..968a7a5 100644 (file)
@@ -189,39 +189,127 @@ typedef enum {
 /**
  * ClutterAnimationMode:
  * @CLUTTER_CUSTOM_MODE: custom progress function
- * @CLUTTER_LINEAR: linear progress
- * @CLUTTER_SINE_IN: sine-in progress
- * @CLUTTER_SINE_OUT: sine-out progress
- * @CLUTTER_SINE_IN_OUT: sine-in-out progress
- * @CLUTTER_EASE_IN: ease-in progress
- * @CLUTTER_EASE_OUT: ease-out progress
- * @CLUTTER_EASE_IN_OUT: ease-in-out progress
- * @CLUTTER_EXPO_IN: exponential in progress
- * @CLUTTER_EXPO_OUT: exponential out progress
- * @CLUTTER_EXPO_IN_OUT: exponential in-out progress
- * @CLUTTER_SMOOTH_IN_OUT: smoothstep in-out progress
- * @CLUTTER_ANIMATION_LAST: last animation mode
+ * @CLUTTER_LINEAR: linear tweening
+ * @CLUTTER_EASE_IN_QUAD: quadratic tweening
+ * @CLUTTER_EASE_OUT_QUAD: quadratic tweening, inverse of
+ *    %CLUTTER_EASE_IN_QUAD
+ * @CLUTTER_EASE_IN_OUT_QUAD: quadratic tweening, combininig
+ *    %CLUTTER_EASE_IN_QUAD and %CLUTTER_EASE_OUT_QUAD
+ * @CLUTTER_EASE_IN_CUBIC: cubic tweening
+ * @CLUTTER_EASE_OUT_CUBIC: cubic tweening, invers of
+ *    %CLUTTER_EASE_IN_CUBIC
+ * @CLUTTER_EASE_IN_OUT_CUBIC: cubic tweening, combining
+ *    %CLUTTER_EASE_IN_CUBIC and %CLUTTER_EASE_OUT_CUBIC
+ * @CLUTTER_EASE_IN_QUART: quartic tweening
+ * @CLUTTER_EASE_OUT_QUART: quartic tweening, inverse of
+ *    %CLUTTER_EASE_IN_QUART
+ * @CLUTTER_EASE_IN_OUT_QUART: quartic tweening, combining
+ *    %CLUTTER_EASE_IN_QUART and %CLUTTER_EASE_OUT_QUART
+ * @CLUTTER_EASE_IN_QUINT: quintic tweening
+ * @CLUTTER_EASE_OUT_QUINT: quintic tweening, inverse of
+ *    %CLUTTER_EASE_IN_QUINT
+ * @CLUTTER_EASE_IN_OUT_QUINT: fifth power tweening, combining
+ *    %CLUTTER_EASE_IN_QUINT and %CLUTTER_EASE_OUT_QUINT
+ * @CLUTTER_EASE_IN_SINE: sinusoidal tweening
+ * @CLUTTER_EASE_OUT_SINE: sinusoidal tweening, inverse of
+ *    %CLUTTER_EASE_IN_SINE
+ * @CLUTTER_EASE_IN_OUT_SINE: sine wave tweening, combining
+ *    %CLUTTER_EASE_IN_SINE and %CLUTTER_EASE_OUT_SINE
+ * @CLUTTER_EASE_IN_EXPO: exponential tweening
+ * @CLUTTER_EASE_OUT_EXPO: exponential tweening, inverse of
+ *    %CLUTTER_EASE_IN_EXPO
+ * @CLUTTER_EASE_IN_OUT_EXPO: exponential tweening, combining
+ *    %CLUTTER_EASE_IN_EXPO and %CLUTTER_EASE_OUT_EXPO
+ * @CLUTTER_EASE_IN_CIRC: circular tweening
+ * @CLUTTER_EASE_OUT_CIRC: circular tweening, inverse of
+ *    %CLUTTER_EASE_IN_CIRC
+ * @CLUTTER_EASE_IN_OUT_CIRC: circular tweening, combining
+ *    %CLUTTER_EASE_IN_CIRC and %CLUTTER_EASE_OUT_CIRC
+ * @CLUTTER_EASE_IN_ELASTIC: elastic tweening, with offshoot on start
+ * @CLUTTER_EASE_OUT_ELASTIC: elastic tweening, with offshoot on end
+ * @CLUTTER_EASE_IN_OUT_ELASTIC: elastic tweening with offshoot on both ends
+ * @CLUTTER_EASE_IN_BACK: overshooting cubic tweening, with
+ *   backtracking on start
+ * @CLUTTER_EASE_OUT_BACK: overshooting cubic tweening, with
+ *   backtracking on end
+ * @CLUTTER_EASE_IN_OUT_BACK: overshooting cubic tweening, with
+ *   backtracking on both ends
+ * @CLUTTER_EASE_IN_BOUNCE: exponentially decaying parabolic (bounce)
+ *   tweening, with bounce on start
+ * @CLUTTER_EASE_OUT_BOUNCE: exponentially decaying parabolic (bounce)
+ *   tweening, with bounce on end
+ * @CLUTTER_EASE_IN_OUT_BOUNCE: exponentially decaying parabolic (bounce)
+ *   tweening, with bounce on both ends
+ * @CLUTTER_ANIMATION_LAST: last animation mode, used as a guard for
+ *   registered global alpha functions
  *
  * The animation modes used by #ClutterAlpha and #ClutterAnimation. This
- * enumeration can be expanded in later versions of Clutter.
+ * enumeration can be expanded in later versions of Clutter. See the
+ * #ClutterAlpha documentation for a graph of all the animation modes.
+ *
+ * Every global alpha function registered using clutter_alpha_register_func()
+ * or clutter_alpha_register_closure() will have a logical id greater than
+ * %CLUTTER_ANIMATION_LAST.
  *
  * Since: 1.0
  */
 typedef enum {
   CLUTTER_CUSTOM_MODE = 0,
 
+  /* linear */
   CLUTTER_LINEAR,
-  CLUTTER_SINE_IN,
-  CLUTTER_SINE_OUT,
-  CLUTTER_SINE_IN_OUT,
-  CLUTTER_EASE_IN,
-  CLUTTER_EASE_OUT,
-  CLUTTER_EASE_IN_OUT,
-  CLUTTER_EXPO_IN,
-  CLUTTER_EXPO_OUT,
-  CLUTTER_EXPO_IN_OUT,
-  CLUTTER_SMOOTH_IN_OUT,
 
+  /* quadratic */
+  CLUTTER_EASE_IN_QUAD,
+  CLUTTER_EASE_OUT_QUAD,
+  CLUTTER_EASE_IN_OUT_QUAD,
+
+  /* cubic */
+  CLUTTER_EASE_IN_CUBIC,
+  CLUTTER_EASE_OUT_CUBIC,
+  CLUTTER_EASE_IN_OUT_CUBIC,
+
+  /* quartic */
+  CLUTTER_EASE_IN_QUART,
+  CLUTTER_EASE_OUT_QUART,
+  CLUTTER_EASE_IN_OUT_QUART,
+
+  /* quintic */
+  CLUTTER_EASE_IN_QUINT,
+  CLUTTER_EASE_OUT_QUINT,
+  CLUTTER_EASE_IN_OUT_QUINT,
+
+  /* sinusoidal */
+  CLUTTER_EASE_IN_SINE,
+  CLUTTER_EASE_OUT_SINE,
+  CLUTTER_EASE_IN_OUT_SINE,
+
+  /* exponential */
+  CLUTTER_EASE_IN_EXPO,
+  CLUTTER_EASE_OUT_EXPO,
+  CLUTTER_EASE_IN_OUT_EXPO,
+
+  /* circular */
+  CLUTTER_EASE_IN_CIRC,
+  CLUTTER_EASE_OUT_CIRC,
+  CLUTTER_EASE_IN_OUT_CIRC,
+
+  /* elastic */
+  CLUTTER_EASE_IN_ELASTIC,
+  CLUTTER_EASE_OUT_ELASTIC,
+  CLUTTER_EASE_IN_OUT_ELASTIC,
+
+  /* overshooting cubic */
+  CLUTTER_EASE_IN_BACK,
+  CLUTTER_EASE_OUT_BACK,
+  CLUTTER_EASE_IN_OUT_BACK,
+
+  /* exponentially decaying parabolic */
+  CLUTTER_EASE_IN_BOUNCE,
+  CLUTTER_EASE_OUT_BOUNCE,
+  CLUTTER_EASE_IN_OUT_BOUNCE,
+
+  /* guard, before registered alpha functions */
   CLUTTER_ANIMATION_LAST
 } ClutterAnimationMode;
 
index ee0ff73..54cf0fe 100644 (file)
@@ -64,7 +64,7 @@ on_button_press (ClutterActor       *actor,
   vertex.y = CLUTTER_UNITS_FROM_FLOAT ((float) new_height / 2);
 
   animation =
-    clutter_actor_animate (actor, CLUTTER_EASE_IN, 2000,
+    clutter_actor_animate (actor, CLUTTER_EASE_IN_EXPO, 2000,
                            "x", new_x,
                            "y", new_y,
                            "width", new_width,
index 22048ad..d625d6c 100644 (file)
@@ -7,16 +7,36 @@ const struct {
   ClutterAnimationMode mode;
 } easing_modes[] = {
   { "linear", CLUTTER_LINEAR },
-  { "sine-in", CLUTTER_SINE_IN },
-  { "sine-out", CLUTTER_SINE_OUT },
-  { "sine-in-out", CLUTTER_SINE_IN_OUT },
-  { "ease-in", CLUTTER_EASE_IN },
-  { "ease-out", CLUTTER_EASE_OUT },
-  { "ease-in-out", CLUTTER_EASE_IN_OUT },
-  { "expo-in", CLUTTER_EXPO_IN },
-  { "expo-out", CLUTTER_EXPO_OUT },
-  { "expo-in-out", CLUTTER_EXPO_IN_OUT },
-  { "smooth-in-out", CLUTTER_SMOOTH_IN_OUT }
+  { "easeInQuad", CLUTTER_EASE_IN_QUAD },
+  { "easeOutQuad", CLUTTER_EASE_OUT_QUAD },
+  { "easeInOutQuad", CLUTTER_EASE_IN_OUT_QUAD },
+  { "easeInCubic", CLUTTER_EASE_IN_CUBIC },
+  { "easeOutCubic", CLUTTER_EASE_OUT_CUBIC },
+  { "easeInOutCubic", CLUTTER_EASE_IN_OUT_CUBIC },
+  { "easeInQuart", CLUTTER_EASE_IN_QUART },
+  { "easeOutQuart", CLUTTER_EASE_OUT_QUART },
+  { "easeInOutQuart", CLUTTER_EASE_IN_OUT_QUART },
+  { "easeInQuint", CLUTTER_EASE_IN_QUINT },
+  { "easeOutQuint", CLUTTER_EASE_OUT_QUINT },
+  { "easeInOutQuint", CLUTTER_EASE_IN_OUT_QUINT },
+  { "easeInSine", CLUTTER_EASE_IN_SINE },
+  { "easeOutSine", CLUTTER_EASE_OUT_SINE },
+  { "easeInOutSine", CLUTTER_EASE_IN_OUT_SINE },
+  { "easeInExpo", CLUTTER_EASE_IN_EXPO },
+  { "easeOutExpo", CLUTTER_EASE_OUT_EXPO },
+  { "easeInOutExpo", CLUTTER_EASE_IN_OUT_EXPO },
+  { "easeInCirc", CLUTTER_EASE_IN_CIRC },
+  { "easeOutCirc", CLUTTER_EASE_OUT_CIRC },
+  { "easeInOutCirc", CLUTTER_EASE_IN_OUT_CIRC },
+  { "easeInElastic", CLUTTER_EASE_IN_ELASTIC },
+  { "easeOutElastic", CLUTTER_EASE_OUT_ELASTIC },
+  { "easeInOutElastic", CLUTTER_EASE_IN_OUT_ELASTIC },
+  { "easeInBack", CLUTTER_EASE_IN_BACK },
+  { "easeOutBack", CLUTTER_EASE_OUT_BACK },
+  { "easeInOutBack", CLUTTER_EASE_IN_OUT_BACK },
+  { "easeInBounce", CLUTTER_EASE_IN_BOUNCE },
+  { "easeOutBounce", CLUTTER_EASE_OUT_BOUNCE },
+  { "easeInOutBounce", CLUTTER_EASE_IN_OUT_BOUNCE },
 };
 
 static const gint n_easing_modes = G_N_ELEMENTS (easing_modes);
@@ -30,36 +50,48 @@ on_button_press (ClutterActor       *actor,
                  ClutterButtonEvent *event,
                  ClutterActor       *rectangle)
 {
-  ClutterAnimation *animation;
-  ClutterAnimationMode cur_mode;
-  gchar *text;
-  guint stage_width, stage_height;
-  guint label_width, label_height;
-
-  text = g_strdup_printf ("Easing mode: %s (%d of %d)\n",
-                          easing_modes[current_mode].name,
-                          current_mode + 1,
-                          n_easing_modes);
-
-  clutter_text_set_text (CLUTTER_TEXT (easing_mode_label), text);
-  g_free (text);
-
-  clutter_actor_get_size (main_stage, &stage_width, &stage_height);
-  clutter_actor_get_size (easing_mode_label, &label_width, &label_height);
-
-  clutter_actor_set_position (easing_mode_label,
-                              stage_width  - label_width  - 10,
-                              stage_height - label_height - 10);
-
-  cur_mode = easing_modes[current_mode].mode;
-
-  animation =
-    clutter_actor_animate (rectangle, cur_mode, 2000,
-                           "x", event->x,
-                           "y", event->y,
-                           NULL);
-
-  current_mode = (current_mode + 1 < n_easing_modes) ? current_mode + 1 : 0;
+  if (event->button == 3)
+    {
+      gchar *text;
+      guint stage_width, stage_height;
+      guint label_width, label_height;
+
+      current_mode = (current_mode + 1 < n_easing_modes) ? current_mode + 1
+                                                         : 0;
+
+      text = g_strdup_printf ("Easing mode: %s (%d of %d)\n"
+                              "Right click to change the easing mode",
+                              easing_modes[current_mode].name,
+                              current_mode + 1,
+                              n_easing_modes);
+
+      clutter_text_set_text (CLUTTER_TEXT (easing_mode_label), text);
+      g_free (text);
+
+      clutter_actor_get_size (main_stage,
+                              &stage_width,
+                              &stage_height);
+      clutter_actor_get_size (easing_mode_label,
+                              &label_width,
+                              &label_height);
+
+      clutter_actor_set_position (easing_mode_label,
+                                  stage_width  - label_width  - 10,
+                                  stage_height - label_height - 10);
+    }
+  else if (event->button == 1)
+    {
+      ClutterAnimation *animation;
+      ClutterAnimationMode cur_mode;
+
+      cur_mode = easing_modes[current_mode].mode;
+
+      animation =
+        clutter_actor_animate (rectangle, cur_mode, 2000,
+                               "x", event->x,
+                               "y", event->y,
+                               NULL);
+    }
 
   return TRUE;
 }
@@ -92,7 +124,8 @@ test_easing_main (int argc, char *argv[])
                     "button-press-event", G_CALLBACK (on_button_press),
                     rect);
 
-  text = g_strdup_printf ("Easing mode: %s (%d of %d)\n",
+  text = g_strdup_printf ("Easing mode: %s (%d of %d)\n"
+                          "Right click to change the easing mode",
                           easing_modes[current_mode].name,
                           current_mode + 1,
                           n_easing_modes);