<chapter id="clutter-animations">
  <chapterinfo>
    <author>
      <firstname>Matthew</firstname>
      <surname>Allum</surname>
      <affiliation>
        <address>
          <email>mallum@openedhand.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">
  <para>
  Simple Rotation...
  </para>
  <programlisting>
struct RotationClosure {
  ClutterActor *actor;
  ClutterFixed final_angle;
  ClutterFixed current_angle;
};

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

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

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

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

  return TRUE;
}

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

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

...
  RotationClosure *clos = NULL;

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

  g_timeout_add_full (1000 / 360, /* fps to interval in milliseconds */
                      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>
  #ClutterTimeline<!-- -->s abstract a set period of time with a set frame
  rate at which to call a provided callback.
  </para>
  
  <para>
  #ClutterTimeline<!-- -->s also extend the timeout sources functionality
  further by:
  </para>
  
  <orderedlist>
    <listitem><para>Having a set duration (in milliseconds) and a set
    'frame rate' - that is, the rate at which the callback is
    called</para></listitem>
    <listitem><para>Passing current progress information to the
    callback</para></listitem>
    <listitem><para>Handling 'dropped frames' and guarenteeing the set
    duration by skipping over frames if the callback cannot keep up with
    the set frame rate</para></listitem>
    <listitem><para>Querying the number of milliseconds elapsed between
    the current and previous callback.</para></listitem>
    <listitem><para>Allowing the timeline to be modified on the fly as
    well as being stopped, started, looped, rewound and
    reversed</para></listitem>
    <listitem><para>Using a #ClutterTimeoutPool to more efficiently
    schedule multiple timeout sources without incurring in potential
    starvation of the main loop slices</para></listitem>
  </orderedlist>

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

  <programlisting>
clutter_timeline_new (n_frames, frames_per_seconds); 
  </programlisting>

  <para>
  Taking a number of frames and a frames per second, or by;
  </para>

  <programlisting>
clutter_timeline_new_for_duration (msecs);
  </programlisting>

  <para>
  Which takes the duration of the timeline in milliseconds with a
  default frame rate (See clutter_get_default_frame_rate()).
  </para>

  <para>
 
  The speed, duration and number of frames of the timeline then be
  modifed via the objects properties and API calls. The timeline can
  be made to loop by setting its "loop" property to %TRUE.

  </para>
  <para>

  The timelines 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() calls.

  </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 in this callback. The callback looks like:

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

  The <literal>frame_num</literal> parameter is set to the timeline's
  current frame number (which is between 1 and the "num-frames" property).
  This value can be used to compute the state of a particular animation
  that is dependant on the frame numer. The clutter_timeline_get_progress()
  function 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 clutter_timeline_set_delay().

  </para>
  <para>

  Timelines can also control a pyshical simulation; the
  clutter_timeline_get_delta() function allows retrieving the number of
  frames and 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">
  <para>
  The following example demonstrates rotating an actor with a timeline.
  </para>
  <programlisting>
#include &lt;clutter/clutter.h&gt;

void
on_new_frame (ClutterTimeline *timeline, 
	      gint             frame_num, 
	      gpointer         data)
{
  ClutterActor *actor = CLUTTER_ACTOR(data);

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

int
main (int argc, char *argv[])
{
  ClutterTimeline *timeline;

  ClutterActor    *stage, *actor;
  GdkPixbuf       *pixbuf;

  clutter_init (&amp;argc, &amp;argv);

  stage = clutter_stage_get_default ();

  pixbuf = gdk_pixbuf_new_from_file ("an-image.png", NULL);

  actor  = clutter_texture_new_from_pixbuf (pixbuf);

  clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);

  clutter_actor_set_position (actor, 100, 100);

  timeline = clutter_timeline_new_for (360, 60); /* one degree per frame */
  clutter_timeline_set_loop (timeline, TRUE);

  g_signal_connect (timeline, "new-frame", G_CALLBACK (on_new_frame), actor);

  clutter_actor_show_all (stage);

  clutter_timeline_start (timeline);

  clutter_main();

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

  <note><para>
  Multiple timelines can be sequenced in order by means of the
  #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 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
  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 simply a 'function of time' (not a pixel alpha!). It
  is created by referencing a source timeline and a function which
  produces a value between 0 and %CLUTTER_ALPHA_MAX dependant on the
  timeline position. Various prebuilt alpha functions are included
  with Clutter these include

  </para>
  <para>

    <variablelist>
      <varlistentry>
        <term>%CLUTTER_ALPHA_RAMP_INC</term>
        <listitem><simpara>Increasing ramp function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_RAMP_DEC</term>
        <listitem><simpara>Decreasing ramp function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_RAMP</term>
        <listitem><simpara>Full ramp function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_SINE_INC</term>
        <listitem><simpara>Increasing sine function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_SINE_DEC</term>
        <listitem><simpara>Decreasing sine function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_SINE_HALF</term>
        <listitem><simpara>Half sine function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_SINE</term>
        <listitem><simpara>Full sine function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_SQUARE</term>
        <listitem><simpara>Square waveform ("step") function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_SMOOTHSTEP_INC</term>
        <listitem><simpara>Increasing smooth transition step
        function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_SMOOTHSTEP_DEC</term>
        <listitem><simpara>Decreasing smooth transition step
        function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_EXP_INC</term>
        <listitem><simpara>Increasing exponential function</simpara></listitem>
      </varlistentry>
      <varlistentry>
        <term>%CLUTTER_ALPHA_EXP_DEC</term>
        <listitem><simpara>Decreasing exponential function</simpara></listitem>
      </varlistentry>
    </variablelist>
  </para>
  <para>

  A Behaviour is created with a #ClutterAlpha and a set of limits for
  whatever the behaviour modifies in an actor. The current #ClutterAlpha
  value is then mapped to a value between these limits and this value
  set on any applied actors. With the #ClutterAlpha's underlying
  timeline playing the produced value will change and the behaviour
  will animate the 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 the path dependant on the current alpha value over time. The
  actual motion will depend on the chosen #ClutterAlphaFunc - a
  #CLUTTER_ALPHA_RAMP_INC making it to move at constant speed along the
  path, a #CLUTTER_ALPHA_SINE making it alternate from one end of the
  path to the other with non constant speed.

  </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 function set to %CLUTTER_ALPHA_RAMP_INC the actor
    will follow the path at a constant velocity, but when changing to
    %CLUTTER_ALPHA_SINE_INC the actor initially accelerates before quickly
    decelerating.
    </blockquote>
  </figure>

  </para>
  <para>

  The behaviours included in Clutter are

  </para>
  <para>

  <variablelist>
    <varlistentry>
      <term>#ClutterBehaviourBspline</term>
      <listitem><simpara>Moves actors along a B-spline path</simpara></listitem>
    </varlistentry>
    <varlistentry>
      <term>#ClutterBehaviourDepth</term>
      <listitem><simpara>Changes the depth of actors</simpara></listitem>
    </varlistentry>
    <varlistentry>
      <term>#ClutterBehaviourEllipse</term>
      <listitem><simpara>Moves actors along an ellipsis</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">
  <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;
  GdkPixbuf        *pixbuf;

  clutter_init (&amp;argc, &amp;argv);

  stage = clutter_stage_get_default ();

  pixbuf = gdk_pixbuf_new_from_file ("ohpowers.png", NULL);

  actor  = clutter_texture_new_from_pixbuf (pixbuf);

  clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);

  timeline = clutter_timeline_new_for_duration (4000); /* milliseconds */
  clutter_timeline_set_loop (timeline, TRUE);

  /* Set an alpha func to power the behaviour */
  alpha = clutter_alpha_new_full (timeline,
                                  CLUTTER_ALPHA_SINE,
                                  NULL, NULL);

  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 */
  g_object_unref (behave);
  g_object_unref (timeline);

  return 0;
}

  </programlisting>
  </example>

  <note>Behaviour parameters can be changed whilst a animation is running</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 behaviour
  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>

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

  </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">
  <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 speed.
  </para>
  <programlisting>
    clutter_actor_animate (actor, CLUTTER_LINEAR, 200
                           "x", 200,
                           "y", 200,
                           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>

  <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>

  <example id="clutter-actor-animate-multi-example">
  <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,
                     ClutterButtonEvent *event,
                     gpointer            user_data)
    {
      clutter_actor_animate (actor, CLUTTER_SINE_IN_OUT, 500,
                             "x", event-&gt;x,
                             "y", event-&gt;y,
                             NULL);
      return TRUE;
    }
  </programlisting>
  </example>

  </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
  and the like. For animations involving variable input (such as touchscreen
  handling) physical simulations may be more suited.
  
  </para> 

  </section>
</chapter>