The Source for Java Technology Collaboration
User: Password:
Register | Login help    

Search

Online Books:
java.net on MarkMail:


Been There, Scene That

Posted by chet on January 4, 2008 at 2:27 PM PST

(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 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 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 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 is working an intro/overview on the subject. Chris Campbell 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. 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.

SceneGraphAnimationPicSm.gif

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, the docs linked on the project site, the demos for that project, and the copious other amounts of information on the library (including demos, chapters in the Filthy Rich Clients 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 on the scene graph site 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 name TimingController 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.

Clip has a handful of factory methods for the common cases:

            create(int duration, double repeatCount, Object target, 
                   String property, T... keyValues)

            create(int duration, double repeatCount, Property property, T... keyValues)

            create(int duration, double repeatCount, TimingTarget target)

            create(int duration, Object target, String property, 
                   T... keyValues)

            create(int duration, Property property, T... keyValues)

            create(int duration, TimingTarget target)
        

(The Property object will be explained later, but think of it as a replacement for the previous PropertySetter object in the Timing Framework).

Like Animator, Clips begin running when the start() method is called (although Clip also has more involved and powerful scheduling capabilities, discussed later):

            // Fade in myObject over a half second
            Clip clip = Clip.create(500, myObject, "opacity", 0f, 1f);
            clip.start();
        

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 class BeanProperty, 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, and SplineInterpolator), and now there is just one class, Interpolators, that provides five factory methods for different types of interpolators:

            public class Interpolators {
            
                static Interpolator getLinearInstance();
                
                static Interpolator getDiscreteInstance();
                
                static Interpolator getSplineInstance(float x1, float y1, float x2, float y2);

                static Interpolator getEasingInstance();
                
                static Interpolator getEasingInstance(float acceleration, float deceleration);
                
            }

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 approach, where there was a list of n KeyTimes, a list of n KeyValues 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:

            static KeyFrames<T> create(Object target, String propertyName, KeyFrame<T>... keyFrames);
            
            static KeyFrames<T> create(Object target, String propertyName, T... keyValues);
            
            static KeyFrames<T> create(Property<T> property, KeyFrame<T>... keyFrames);
            
            static KeyFrames<T> create(Property<T> property, T... keyValues);
            

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 old TimingTrigger. 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 old Animator.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)

Related Topics >> Java Desktop      
Comments
Comments are listed in date ascending order (oldest first)

What is the relationship between the Property and BeanProperty classes in the Scene graph project and the Property and BeanProperty classes in the Beans Binding project? It seems like they are practically identical. Have you guys spoken to Scott Violet and Shannon Hickey about how the binding framework and scene graph timing framework will integrate?

Well, what can I say. It's cold and grey and rainy here in Oregon. Blogging is way more fun than sleeping. :)

So Cheat ,
does this mean we put away the timing Framwork at 1.0 and start using the animation engine in the scene graph in future?

sorry for the typo Chet

Yes, Chet is actually The Cheat of HomeStarRunner fame. Long Live Da Cheat!

psychostud: You've cleverly anticipated part of what I cover in Part 2 - I'll be posting that later today or tomorrow... (no problem about the typo - I've been called worse...) Chet.

thats great chet,
does the scenegraph also have something similar to the animatedTransitions Library or would they work complementing each other ?

Finally or too late?
1. Sun has been pushing 20 years old technology (Swing) for too long. Macromedia has shown the way forward with Flash, Microsoft followed with WPF (aka Silverlight) and now Sun is reimplementing this concept (animated scenegraph) with Java FX. Finally! But this is only the first step in the right direction.

2. Flash is not successful because Flash Player (or Actionscript) is a great technology (they both suck), but because they have a great IDE targeted at designers. Remember, great GUIs are made by designers not developers. Most developers hate designing GUIs and they suck at it. What Java needs is a GUI design tool that bridges the gap between designers and developers (you know what I mean when you yet again start transforming HTML design into XML/XSLT/Velocity/whatever template). A tool that lets us developers easily pick up where designers left off. Microsoft learned that lesson as we can see by their Expression series. Hope Sun has learned that lesson too. Or is it too late?

3. And finally: Sun, I hope you don't think that you can make a breakthrough GUI technology (Web or RIA) without supporting at least an industry-standard video playback, preferably also recording. Codec that is going to rule the next decade is H.264. If we can't create a YouTube rip-off in Java, don't bother developing this technology, because it's not going to make us competitive versus Flash or Silverlight.

psychostud: AnimTrans is complementary to the scene graph animation engine for now, just like it's complementary with the Timing Framework. The "animation engine" is all about providing the fundamental support for timing behavior plus the ability to interpolate values of different types. AnimTrans, however, is about providing a layer on top of that to make animated seques, in particular, much easier. Maybe something like this makes sense to include in the scene graph engine eventually, but for now it's a separate piece of functionality. Chet.