<chapter id="clutter-animations">
  <chapterinfo>
    <author>
      <firstname>Matthew</firstname>
      <surname>Allum</surname>
      <affiliation>
        <address>
          <email>mallum@openedhand.com</email>
        </address>
      </affiliation>
    </author>

    <author>
      <firstname>Emmanuele</firstname>
      <surname>Bassi</surname>
      <affiliation>
        <address>
          <email>ebassi@linux.intel.com</email>
        </address>
      </affiliation>
    </author>
  </chapterinfo>

  <title>Creating Animations with Clutter</title>

  <para>With Clutter using hardware accelration for graphics rendering,
  complex and fast animations are possible. This chapter describes basic
  techniques and the utilities Clutter provides in aiding animation
  creation.</para>

  <section id="clutter-animation-basic">
    <title>Basic Animations</title>

    <para>The most basic way to create animations with Clutter is via the use
    of g_timeout_add(). This enables a callback function to be called at a
    defined interval. The callback function can then modify actors visual
    properties as to produce an animation.</para>

    <example id="clutter-timeout-example">
      <title>Simple timeout example</title>
      <para>Implement a rotating actor using 360 "frames"</para>
      <programlisting>
struct RotationClosure {
  ClutterActor *actor;

  gdouble final_angle;
  gdouble current_angle;
};

static gboolean
rotate_actor (gpointer data)
{
  struct RotationClosure *clos = data;

  clutter_actor_set_rotation (clos-&gt;actor, clos-&gt;current_angle, 0, 0, 0);

  /* add one degree */
  clos-&gt;current_angle += 1.0

  if (clos-&gt;current_angle == clos-&gt;final_angle)
    return FALSE;

  return TRUE;
}

static void
rotate_actor_cleanup (gpointer data)
{
  struct RotationClosure *clos = data;

  g_object_unref (clos-&gt;actor);
  g_free (clos);
}

...
  struct RotationClosure *clos = NULL;

  clos = g_new (struct RotationClosure, 1);
  clos-&gt;actor = g_object_ref (an_actor);
  clos-&gt;final_angle = 360.0;
  clos-&gt;current_angle = 0;

  g_timeout_add_full (1000 / 360, /* 360 updates in one second */
                      rotate_actor,
                      clos,
                      rotate_actor_cleanup);
      </programlisting>
    </example>

    <note>
      <title>Priorities</title>
      <para>%G_PRIORITY_DEFAULT should always be used as the timeouts priority
      (in case of g_timeout_add_full()) as not to intefere with Clutter's
      scheduling of repaints and input event handling.</para>
    </note>
  </section>

  <section id="clutter-animation-timelines">
    <title>Timelines</title>

    <para>Using g_timeout_add() to control an animation is complicated
    and does not work in concert with the rest of the operations Clutter
    must perform for each redraw cycle.</para>

    <para>For this reason, Clutter provides #ClutterTimeline, a class that
    allows scheduling animations with a definite duration. Timelines are
    advanced during the redraw cycle so that animations are ready to be
    performed at the right time. This also means that animations will not
    affect the event processing; it also means that if the animation is too
    complex it will be called with a longer delay, thus not blocking the
    whole UI.</para>

    <para>A Timeline is created with:</para>

    <programlisting>
clutter_timeline_new (duration_in_milliseconds);
    </programlisting>

    <para>The duration of the timeline then be modifed via the
    #ClutterTimeline:duration property or by using
    clutter_timeline_set_duration().</para>

    <para>A timeline is started via clutter_timeline_start() and its
    playback further manipulated by the clutter_timeline_pause(),
    clutter_timeline_stop(), clutter_timeline_rewind() and
    clutter_timeline_skip() functions.</para>

    <para>By attaching a handler to the timeline's
    #ClutterTimeline::new-frame signal a timeline can then be used to
    drive an animation by altering an actor's visual properties. The
    callback looks like:</para>

    <programlisting>
void
on_new_frame (ClutterTimeline *timeline,
              gint             elapsed_msecs,
              gpointer         user_data)
{
}
    </programlisting>

    <para>The <literal>elapsed_msecs</literal> parameter is set to the amount
    of time elapsed since the beginning of the timeline, and its value is
    always between 0 and the #ClutterTimeline:duration value.</para>

    <para>The function clutter_timeline_get_progress() can also be used to
    get a normalised value of the timeline's current position between 0 and
    1.</para>

    <para>Timelines can also be played in reverse by setting the direction
    using clutter_timeline_set_direction(), and can also have a one-time
    delay set before they begin playing by using the function
    clutter_timeline_set_delay().</para>

    <para>Timelines can also control a pyshical simulation; the
    clutter_timeline_get_delta() function allows retrieving the number of
    milliseconds elapsed since the previous callback to ensure the physics
    engine to be able to take the actual time elapsed between iterations
    into account.</para>

    <example id="clutter-timeline-example">
      <title>Using a Timeline to drive an animation</title>
      <para>Rewrite the example above with a #ClutterTimeline instead of
      using g_timeout_add()</para>
      <programlisting>
#include &lt;clutter/clutter.h&gt;

static void
on_new_frame (ClutterTimeline *timeline,
	      gint             elapsed_msecs,
	      ClutterActor    *actor)
{
  gdouble angle = 360 * clutter_timeline_get_progress (timeline);

  clutter_actor_set_rotation (actor, CLUTTER_Z_AXIS,
                              angle,
                              clutter_actor_get_width (actor) / 2,
			      clutter_actor_get_height (actor) / 2,
                              0);
}

  ...

  ClutterTimeline *timeline;

  timeline = clutter_timeline_new (1000); /* one second */
  clutter_timeline_set_loop (timeline, TRUE);
  g_signal_connect (timeline, "new-frame", G_CALLBACK (on_new_frame), actor);
  clutter_timeline_start (timeline);
      </programlisting>
    </example>

    <note><para>Multiple timelines can be sequenced in order by using a
    #ClutterScore. See the #ClutterScore documentation for more details on
    using this.</para></note>
  </section>

  <section id="clutter-animation-behaviours">
    <title>Behaviours</title>
    <para>With a large application containing many animations, the use of
    just timelines can become unwieldy and difficult to manage, with much
    code duplication in the #ClutterTimeline::new-frame handlers that can
    require over-complex code changes for minor animation modifications. To
    ease these problems the #ClutterAlpha and #ClutterBehaviour classes were
    created.</para>

    <para>#ClutterAlpha and #ClutterBehaviour attempt to generalise the
    #ClutterTimeline::new-frame function by defining common actions (or
    behaviours) that can be quickly modified, applied to multiple actors or
    mixed on a single actor.</para>

    <para>A #ClutterAlpha is a 'function of time' (and does not refer to the
    alpha channel of a RGBA color). It is created by referencing a source
    timeline and an "easing mode" whichproduces a value between -1.0 and 2.0
    depending on the progress of the timeline. Clutter provides various
    easing modes, as described by the #ClutterAnimationMode enumeration. It is
    also possible to register new animation modes using the function
    clutter_alpha_register_func() or to provide a custom #ClutterAlphaFunc for
    a specific #ClutterAlpha instance.</para>

    <para>A #ClutterBehaviour is created with a #ClutterAlpha and a set of
    parameters for whatever the behaviour modifies in an actor. The value of
    a #ClutterAlpha during the animation is then mapped to a value for the
    behaviour parameters and then applied on the actors referenced by the
    #ClutterBehaviour. With the #ClutterAlpha's underlying timeline playing
    the produced value will change and the behaviour will animate an
    actor.</para>

    <para>A #ClutterBehaviour is effectively 'driven' by a supplied
    #ClutterAlpha and when then applied to an actor it will modify a visual
    property or feature of the actor dependant on the Alpha's value. For
    example, a path-based behaviour applied to an actor will alter its
    position along a #ClutterPath, depending on the current alpha value over
    time. The actual progress of the motion will depend on the chosen "easing
    mode".</para>

    <para> Multiple behaviours can of course be applied to an actor as well
    as a single behaviour being applied to multiple actors. The separation
    of timelines, alphas and behaviours allows for a single timeline to drive
    many behaviours each potentially using different alpha functions.
    Behaviour parameters can also be changed on the fly.</para>

    <para>
      <figure id="behaviour-path-alpha">
        <title>Effects of alpha functions on a path</title>
        <graphic fileref="path-alpha-func.png" format="PNG"/>
        <blockquote>The actors position between the path's end points
        directly correlates to the #ClutterAlpha's current alpha value
        driving the behaviour. With the #ClutterAlpha's animation mode
        set to %CLUTTER_LINEAR the actor will follow the path at a constant
        velocity, but when changing to  %CLUTTER_EASE_IN_OUT_SINE the actor
        initially accelerates before quickly decelerating.</blockquote>
      </figure>
    </para>

    <para>The behaviours included in Clutter are:</para>

    <para>
      <variablelist>
        <varlistentry>
          <term>#ClutterBehaviourDepth</term>
          <listitem><simpara>Changes the depth of actors</simpara></listitem>
        </varlistentry>
        <varlistentry>
          <term>#ClutterBehaviourEllipse</term>
          <listitem><simpara>Moves actors along an elliptical path</simpara></listitem>
        </varlistentry>
        <varlistentry>
          <term>#ClutterBehaviourOpacity</term>
          <listitem><simpara>Changes the opacity of actors</simpara></listitem>
        </varlistentry>
        <varlistentry>
          <term>#ClutterBehaviourPath</term>
          <listitem><simpara>Moves actors along a path</simpara></listitem>
        </varlistentry>
        <varlistentry>
          <term>#ClutterBehaviourRotate</term>
          <listitem><simpara>Rotates actors along an axis</simpara></listitem>
        </varlistentry>
        <varlistentry>
          <term>#ClutterBehaviourScale</term>
          <listitem><simpara>Changes the scaling factors of actors</simpara></listitem>
        </varlistentry>
      </variablelist>
    </para>

    <example id="clutter-behaviour-example">
      <title>Using a #ClutterBehaviour</title>
      <para>The following example demonstrates an ellipse behaviour in
      action.</para>
      <programlisting>
#include &lt;clutter/clutter.h&gt;

int
main (int argc, char *argv[])
{
  ClutterTimeline  *timeline;
  ClutterBehaviour *behave;
  ClutterAlpha     *alpha;
  ClutterActor     *stage, *actor;

  if (clutter_init (&amp;argc, &amp;argv) != CLUTTER_INIT_SUCCESS)
    return 1;

  stage = clutter_stage_get_default ();

  actor = clutter_texture_new_from_file ("ohpowers.png", NULL);
  clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);

  /* set up the animation to be 4 seconds long */
  timeline = clutter_timeline_new (4000);
  clutter_timeline_set_loop (timeline, TRUE);

  /* set up a sinusoidal easing mode to power the behaviour; the
   * alpha will take a reference on the timeline so we can safely
   * release the reference we hold
   */
  alpha = clutter_alpha_new_full (timeline, CLUTTER_EASE_IN_OUT_SINE);
  g_object_unref (timeline);

  /* the behaviour will own the alpha by sinking its floating
   * reference (if needed)
   */
  behave = clutter_behaviour_ellipse_new (alpha, 
					  200,               /* center x */
					  200,               /* center y */
					  400,               /* width */
					  300,               /* height */
					  CLUTTER_ROTATE_CW, /* direction */
					  0.0,               /* initial angle */
					  360.0);            /* final angle */

  clutter_behaviour_apply (behave, actor);

  clutter_actor_show_all (stage);

  clutter_timeline_start (timeline);

  clutter_main();

  /* clean up; behaviours are top-level objects */
  g_object_unref (behave);

  return 0;
}
      </programlisting>
    </example>

    <note><para>The parameters of a #ClutterBehaviour can be changed whilst
    a animation is running.</para></note>

    <para>There can be many #ClutterAlpha's attached to a single timeline.
    There can be many behaviours for a #ClutterAlpha. There can be many
    behaviours applied to an actor. A #ClutterScore can be used to chain
    many behaviours together.</para>

    <warning><para>Combining behaviours that effect the same actor properties
    (i.e two separate paths) will cause unexpected results. The values will
    not be merged in any way with only the last applied behaviour taking
    precedence.</para></warning>

    <note><para>Tips for implementing a new behaviour can be found <link
      linkend="creating-your-own-behaviours">here</link>.</para></note>

  </section>

  <section id="clutter-animation-implicit">
    <title>Implicit Animations</title>

    <para>Using behaviours for simple animations of a single actor may
    be too complicated, in terms of memory management and bookkeeping
    of the object instances. For this reason, Clutter also provides a
    simple animation API for implicit animations using properties of
    an actor: clutter_actor_animate().</para>

    <para>The clutter_actor_animate() family of functions will create
    and use an implicit #ClutterAnimation instance, which will then
    handle the animation of one or more #ClutterActor properties between
    a range of values.</para>

    <example id="clutter-actor-animate-example">
      <title>Using clutter_actor_animate()</title>
      <para>The following example demonstrates how to use the
      clutter_actor_animate() method to tween an actor between the current
      position and a new set of coordinates. The animation takes 200
      milliseconds to complete and uses a linear progression.</para>
      <programlisting>
  clutter_actor_animate (actor, CLUTTER_LINEAR, 200,
                         "x", 200.0f,
                         "y", 200.0f,
                         NULL);
      </programlisting>
    </example>

    <para>The clutter_actor_animate() method returns a #ClutterAnimation
    instance that can be used to start, stop and modify the animation
    while it's running. The #ClutterAnimation::completed signal will
    be emitted when the animation has been completed.</para>

    <warning><para>When the animation is complete it will be automatically
    unreferenced, and disposed if nothing else is holding a reference
    on it.</para></warning>

    <example id="clutter-actor-animate-multi-example">
      <title>Animating inside an event handler</title>
      <para>The following example demonstrates how to animate an actor
      inside the signal handler for a button press event. If the user
      presses the button on a new position while the animation is running,
      the animation will be restarted with the new final values
      updated.</para>
      <programlisting>
  static gboolean
  on_button_press (ClutterActor *actor,
                   ClutterEvent *event,
                   gpointer      user_data)
  {
    gfloat event_x, event_y;

    clutter_event_get_coords (event, &amp;event_x, &amp;event_y);
    clutter_actor_animate (actor, CLUTTER_EASE_SINE_OUT, 500,
                           "x", event_x,
                           "y", event_y,
                           NULL);
    return TRUE;
  }
      </programlisting>
    </example>

    <para>Calling clutter_actor_animate() multiple times on an
    actor which is being animated will cause the animation to be updated
    with the new values.</para>

    <para>If you need to chain up multiple animations created using
    clutter_actor_animate() you should connect to the
    #ClutterAnimation::completed signal using g_signal_connect_after()
    to have the guarantee that the current #ClutterAnimation has been
    detached from the actor. The documentation for clutter_actor_animate()
    has further examples.</para>

  </section>

  <section id="clutter-animation-conclusion">
    <title>Conclusion</title>
    <para>Clutter provides a number of utility classes to aid animations
    and complex animations can be produced by combining the various features
    provided.</para>

    <para>Of course animations can becreated outside of the Clutter animation
    framework, as the framework is not expected to cover every kind of
    possible animation scenario.</para>

    <para>The animation functionality in Clutter is primarily suited to
    building animations with a set or finite running time - i.e transitions
    between states. For animations involving variable input (such as
    touchscreen handling) physical simulations may be more suited.</para>
  </section>

</chapter>
