Been There, Scene That

Part 1 of a two-part article on the animation engine in the Scene Graph project. A moving tale.

(This is Part 1 of a two-part blog. It's been broken in two for no particularly good reason other
than it was getting a bit long for a single entry and I always like a bit of suspense and
tension in my technical articles - don't you?
. Look for Part 2 in the next few days)

You may have already heard about the Scene
Graph project[2] that we released last
month on java.net.

In case you haven't heard about it yet, here's some information about it:

We released the project last month on java.net.

Since the team was pretty busy at the time (I was at
JavaPolis[3] talking about the
project, among other things, and the team was cranking away on the actual
code), we haven't really gotten around to blogging about it yet to tell people
more about the project (hey, the code's out there - does anyone need anything
more?). Fortunately for us,
Josh[4] seems to blog in his sleep, so there's at least been some information
floating
out there in the blogosphere. But maybe now that the library is available, we should actually
talk about it to help everyone understand what it is, how it works, and what
you can do with it.

Hans Muller[5] is working an
intro/overview on the subject. Chris Campbell[6] has a blog in the works on scene graph image effects. And I was tasked with (surprise, surprise) a
discussion of animation.

TimingFramework++

I think that the best way to describe the animation engine in the scene graph
project is that it is like the next major version of the
Timing Framework[7]. Maybe it's because I'm a graphics geek, but I always find
it easier to understand concepts through pictures. So here's a technical diagram
illustrating how the scene graph animation engine relates to the Timing Framework.
It's a bit technical, but hopefully you'll get the point.

The similarlity between the two libraries is not, obviously, a coincidence. We started with the 1.0 version
of the Timing Framework and changed it to suit our needs for the scene graph, where "suit our needs" means that we refactored the API to improve upon various things and added functionality that has so far
been lacking in the Timing Framework.

Rather than explain how the Timing Framework works, I'd encourage you to check
out the project[7], the docs linked on the project site[7], the demos for that project, and
the copious other amounts of information on the library (including demos,
chapters in the Filthy Rich Clients[8]
book, and so on). I'll assume that
anyone reading past here has some passing familiarity with the Timing
Framework.

I'll step through the major categories of differences between the
TimingFramework and this new animation library, to give a sense of what's new
in the scene graph. I'll show some sample code along the way, although I'd encourage you to check out
the Nodes example[9] on thescene graph site[2] to see animation usage in action.

Refactoring: What's Changed?

Clip is the new Animator

After living for way too long with the bureaucratically dull nameTimingController
that came from the original version of the Timing Framework, we finally
renamed that class to the friendlier Animator name that the library enjoys
today in version 1.0. But now it's changed again. That
class, the most fundamental in the whole library, is now called Clip. This
was done to help people that might be familiar with the concepts covered by
that class in other toolkits and environments where the name Clip is common. It's also
closer to what the object is; yes, it animates things, but it's really just a
short, atomic animation which is meant to be strung together with other
animations in an application, much like short clips are edited together to make
an entire movie (or, in the case of the
Fantastic Four sequel, to make a travesty).

Besides, Clip saves 4 characters every time you type it. Pretty cool, huh?
That's like several milliseconds of coding time per day that we've saved you, our
user. All part of the
job, providing service and performance with a smiley.

PropertySetter is now BeanProperty

The old way of having the animation engine automatically set object/property
values was through the PropertySetter class, which was constructed with a given
object and property-name pair. We felt that this was a bit too constrictive in
a world where there might be other ways that one might want to set values on
objects; what if someone has an object without JavaBean-like getter/setters on
it?

So we defined a new interface, Property, that abstracts out the concepts and methods for getting and setting the value of some property.

public interface Property<T> {

public <T> getValue();

public void setValue(T value); }

Then we refactored the old PropertySetter class as the new classBeanProperty,
which is an implementation of that interface that specifically defines
properties in terms of JavaBean objects/name pairs.

public class BeanProperty<T> implements Property<T> {

public BeanProperty(Object object, String propertyName);

public <T> getValue();

public void setValue(T value); }

Also, note that we separated the functionality of setting values on a Property object
from animating those values. These concepts overlapped in the previous
PropertySetter object, which was an implementation of TimingTarget and handled
both animating the value in question as well as setting it on the specified
object. Now, the new KeyFrames object handles animating a property, and the
resulting value is then sent into the appropriate Property object.

Interpolators

One of my favorite refactorings from the TimingFramework was the change that Chris
Campbell made to interpolators. The base interface, Interpolator, is the same:

public interface Interpolator {

public float interpolate(float fraction);

}

The change is that there used to be several implementation classes of
Interpolator (LinearInterpolator, DiscreteInterpolator, andSplineInterpolator), and now there is just one class, Interpolators,
that provides five factory
methods for different types of interpolators:

The linear, discrete, and spline interpolators are virtually the same as
before, in both construction and
operation. But there is now an additional "easing" variety that takes the place
of the setAcceleration()/setDeceleration() behavior in the old Animator class.
This simplifies the use of acceleration/deceleration, and makes it more
consistent with the use of other interpolator functionality.

Also, note that Clip uses an easing interpolator by default (with the default factors
of .2f/.2f), as opposed
to the old linearly-interpolated Animator object. Linear interpolation makes
more sense as a default from an analytical standpoint (it seems less arbitrary
than some particular choice of easing factors), but frankly most of the animations that
you'll create should be non-linearly interpolated, which means that you would either not get the behavior you should if you used the linear
default or (as in all of my animation demos) you'd have to keep writing the same
boilerplate code to set the easing factors to get the behavior right.

KeyFrames

The previous structure of KeyFrames was closely modeled on the
SMIL[10] approach,
where there was a list of nKeyTimes, a list of nKeyValues which corresponded
to the times in KeyTimes, and an optional list of (n-1)Interpolators for the
intervals between the times in KeyTimes. The basic functionality of that system
is unchanged, but it has been reorganized in a way that we think makes more
sense for the API.

Now there is a KeyFrame object which holds a time/value pair as well as an optional Interpolator. The Interpolator is used for the interval between the previous
KeyFrame and this one (which is ignored if this is the first
KeyFrame, since there is no preceding interval). A KeyFrames object in the new system is just a
collection of these individual KeyFrame objects. There is also more latitude now for
creating animations without start/end values. For example, if a KeyFrames
object is defined without a KeyFrame object at time t=0,
then the animation
derives the value at t=0 when the animation is started. Additionally, if there is no KeyFrame at time t=1, then the animation simply holds the value set by the
preceding KeyFrame.

KeyFrames implement the TimingTarget interface, and are used as targets of an
animation to animate a value and then set it on the Property object with
which they were constructed.
Note that KeyFrames objects are created either with an explicit Property object
or with the object/property
pair that is used to construct a BeanProperty object internally:

Evaluator is now Composer

The system in TimingFramework to handle interpolation between values of various
types uses the Evaluator class (or, rather, subclasses of Evaluator). Each Evaluator subclass is defined to take values of a certain type, a fraction
to interpolate between then, and would linearly interpolate between those values.
Chris
abstracted this functionality another level and created the Composer class. It
performs similar functionality, but only requires from its subclasses the
work of breaking down a type into component pieces that are stored in
double values. That is, the Evaluator class internally handles the simple
parameteric calculation that interpolates between two double values. All that
Composer and its subclasses need to do is to marshall values between their
native representation and a representation in a series of doubles.

As in the TimingFramework, most types that you would care about animating are
already handled by the system. But if you do need to animate a variable of a type
that is unknown to the system, then you need to create your own Composer subclass and register it
with the system. It will then be used whenever the system encounters that type
and looks for an appropriate Composer. Instead of putting sample code for a here, I'll just defer to the JavaDocs
and the source code, which do a good job of showing what any new subclass would
need to do in order to work within this system. But assuming you've defined some
custom Composer, MytypeComposer, registration is easy:

Composer.register(new MytypeComposer());

TimingTrigger is replaced by Clip dependency scheduling

One piece of functionality that is completely gone is the oldTimingTrigger.
This class is the mechanism for sequencing animations in the Timing Framework;
you daisy-chain animations by triggering one animation to start when
another ended. But now, the Clip class has this capability built-in, with more
bells and whistles. Instead of going through a Trigger to sequence animations,
you can tell the animations directly that you want to sequence them on each
other. Also, there is more flexibility in how the animations are sequenced; you
can schedule an animation to start on another animation's start or end, and can
do either one with a specified offset value.

addBeginAnimation(Animation anim);

addBeginAnimation(Animation anim, long offset);

addEndAnimation(Animation anim);

addEndAnimation(Animation anim, long offset);

(where Animation is the superclass
of both Clip and the as-yet-undiscussed Timeline class).

So, for example, you can have
clipB start 100 ms after clipA ends with the following call:

clipA.addEndAnimation(clipB, 100);

Minor Changes

There are various minor changes to the system that you will notice as you try it
out. It's not worth going through all of these (because it'd take me a long
time to go over the API to catch all of these changes, for one thing), but they
should be fairly self-explanatory when you encounter them. For example, the oldAnimator.INFINITE constant, which was used, for example, to define unending
animations has become Clip.INDEFINITE. Not a big change, but it seemed more
semantically correct.

(That's it for this entry - check back next week for the gripping conclusion to this discussion, as we go over some of the new bits in the scene graph animation library that are not now part of the Timing Framework)