Grape Animation Library
http://code.google.com/p/grape-as3/
Grape is the animation library I introduced last post. Its goal is to offer a modular means to animate along parametrized paths.
Here’s the updated demo:
Architecture
Animations have 2 essential component parts: a path and a curve. Paths define position and curves define progression. Both paths and curves are stateless, meaning state is managed by the Animation type and the engine, Grape. This also means paths and curves are freely swappable without a need to clone, etc.
Grape’s Animation type by itself doesn’t assign values to your objects as you’re probably accustomed to with most popular tweening engines. Instead, it simply manages the state of the animation. Its state is a Vector of type Number. This allows the user to reference this state without necessarily tying it to a DisplayObject for instance. When you do want to bind the state of the animation to a DisplayObject, you’ll use a Binding, which simply applies the animation’s state to the appropriate object. The average user probably won’t ever touch a Binding directly, but it’s worth noting.
API
The engine is a singleton called Grape. It has creation methods for the 2 most likely animation scenarios, create2DAnimation and createPropertyAnimation. The latter should be fairly identical to how popular engines work. create2DAnimation will ideally be the typical use-case.
//create the path to animate along var spiral:Path2D = new SpiralPath2D( new Point( 100, 100 ), 200, 0 ); //view is your DisplayObject and 2000 is the duration in milliseconds. Grape.getInstance.create2DAnimation( view, spiral, 2000 );
And that’s it, your DisplayObject will tween in a spiral with original radius of 200 and end radius of 0 for 2 seconds. You can also set the start time (defaults to instantaneous start), the curve (defaults to linear), whether the animation loops (defaults to false), if the animation is to be played in reverse (defaults to false), and if the animation should reverse on loop (false).
The method returns the animation instance, in the case that you need a reference or would like to handle some of its events:
var anim:Animation = Grape.getInstance.create2DAnimation( view, spiral, 2000 ); //add listener for animation complete event anim.addEventListener( Event.COMPLETE, onAnimationComplete ); function onAnimationComplete( event:Event ) : void { //animation done! }
You can also listen for an event everytime a looping animation loops and each tick of the engine:
//add listener for animation loop event anim.addEventListener( Animation.LOOP, onAnimationLoop ); //listen for Grape's tick Grape.getInstance().addEventListener( Grape.TICK, onGrapeTick );
Paths
LinearPath2D, CirclePath2D, SpiralPath2D, SineWavePath2D
These are all pretty self explanatory. Linear moves along a line segment. Circle moves around a circle’s edge; you can set the beginning and end radian. Spiral is the same as Circle but also interpolates radius. SineWavePath2D extends LinearPath2D, and so the wave deviates from the line segment.
ComplexPath2D
ComplexPath2D allows you to tween across multiple paths. You supply it a Vector of paths and a Vector of starting cues, specifically the percentage through the tween at which you want the corresponding path to begin.
//create the paths that will be added to the complex path var spiral:Path2D = new SpiralPath2D( new Point( 100, 100 ), 200, 0 ); var linear1:LinearPath2D = new LinearPath2D( new Point( 100, 100 ), new Point( 200, 200 ) ); var linear2:LinearPath2D = new LinearPath2D( new Point( 200, 200 ), new Point( 200, 100 ) ); var linear3:LinearPath2D = new LinearPath2D( new Point( 200, 100 ), new Point( 100, 100 ) ); var paths:Vector.<Path> = Vector.<Path>( [ spiral, linear1, linear2, linear3 ] ); //spiral and linear1 will take 25% of the animation's duration, linear2 will take 10% //and linear3 will take 40% var cues:Vector.<Number> = Vector.<Number>( [ 0, 0.25, 0.5, 0.6 ] ); //create the complex path var complex:ComplexPath2D = new ComplexPath2D( paths, cues );
Tweening along a twisty train track for example is something that just wouldn’t be possible with any other engine, and forget about trying to apply one cohesive interpolation across the whole path. Complex paths make these stupid easy. And because complex paths are paths, you can nest them inside of each other to create quickly compound, complex animations!
QuadraticBezierPath2D, CubicBezierPath2D
Quadratic Bezier curves are what you’re familiar with from Graphics. Cubic Beziers just offer one extra control point.
var bez:CubicBezierPath2D = new CubicBezierPath2D( new Point( 20, 20 ), new Point( 400, 200 ), new Point( 400, 600 ), new Point( 600, 600 ) );
Bezier’s take one other argument, the Boolean reparametrize. Beziers are like stapling saltwater taffy to the table and lifting it at the center. A really small bit of the curve in terms of its parametrization is left at the staples, while a lot is in your hand near the control points, even though the opposite may be true spatially. What does this mean? It means that even with a linear curve, it’s not likely you can keep constant velocity along the curve. So this Boolean flags whether to reparametrize the curve by arc length. This is a somewhat expensive process and so it defaults to false. Here’s an example of this, the black tween has been reparametrized, the red has not.
LinearSplinePath2D, CosineSplinePath2D, HermiteSplinePath2D, CatmullRomSplinePath2D
Each of the splines takes a Number Vector (x,y pairs) and interpolates between them in their own special way. Linear connects each point linearly, Cosine steps over part of a wavelength to give a (OK, not too nice) curve through each point. Hermite also takes a vector of tangents to give the most amount of control. You can read about them on wikipedia. Catmull-Rom Splines are a subset of Hermite curves. They offer less control, but do not require the user to specify the tangents. Here’s an image of the different interpolations. Top to bottom it’s Linear, Cosine and Catmull-Rom:

This is the code that created the image above:
var p1:Vector.<Number> = Vector.<Number>( [ 20, 20, 100, 100, 120, 50, 300, 300, 400, 50, 500, 200 ] ); var p2:Vector.<Number> = Vector.<Number>( [ 20, 120, 100, 200, 120, 150, 300, 400, 400, 150, 500, 300 ] ); var p3:Vector.<Number> = Vector.<Number>( [ 20, 220, 100, 300, 120, 250, 300, 500, 400, 250, 500, 400 ] ); var path:Path2D = new LinearSplinePath2D( p1 ); path.render( graphics ); path = new CosineSplinePath2D( p2 ); path.render( graphics ); path = new CatmullRomSplinePath2D( p3 ); path.render( graphics );
These have great applicability to dynamically generated paths and lots of other game-related functions.
PhysicsPath2D
This should maybe be called projectile instead of physics. You supply initial position, starting velocity and acceleration vectors and how many seconds the entire tween represents.
Debug Rendering
Each path is renderable as a helpful measure. Because of the way paths work, it was easy to handle all paths in one render method, keeping file size down. You can see the spline API above, just pass a graphics instance to draw into.
Alright, I’m excited to see what you make of this and what sorts of cool things you can make! It’s hosted on Google Code under the MIT license. I’ll try to get it commented soon enough, send me any bugs or feature requests.


Hey man, I’ma give this a shot right now. I was just about to do some refactoring on this project; why not stick a new tweening engine in as well? What could possibly go wrong?
Thanks for your work.
what is the advantage over good old tweening?
@makc: that the path is part of the tween! there’s really no way to do most of the examples in the demo with any other tweening engine. if you’re just tweening one value, there is no advantage, although I bet mine is faster…
cool, drew, very cool. multiple simultaneous paths.. and i like that it doesn’t inject the values directly to the objects. that’s the way to go, i agree; i can imagine some applications right away. ;^)
thanks, gabo!
TweenLite also has a Bezier curve tween plugin, although I’ve never used it.
http://www.greensock.com/as/docs/tween/com/greensock/plugins/BezierPlugin.html
Still, this looks really good. I think I’m going to play around with it some.
Yeah, I think that’s the closest any engine has to doing what this is intended for. I really dislike it though. I’m a stickler for type and that’s one big generic mess IMHO! Also, you SHOULDN’T ever need any higher degree bezier than cubic. The overhead of dynamically expanding the polynomial to solve it makes it not worth it. Plus the more control points the more clumped the curve becomes. But I’m rambling! Let me know what you think of it and if you have any suggestions. Thanks!
Hello drew, thanks for great work. I tried your grape alpha version, but I keep getting error on publishing. Is this going to work with CS3??
Looks like when I import grape class, lines where contains give error.
Am I doing anything wrong?
Thanks!!
Sorry drew, at my last post, i typed TAG bracket, but somehow brackets are gone. I wanted to ask you “lines where contains tag bracket give error.
Sorry for mess!!!
Thanks
it’s flash 10! so, i don’t think you’ll be able to compile it as is with cs3. the use of Vectors is the only thing pushing it that way though, so you could replace every Vector with an Array. sorry!
unfortunately there’s no documentation in the svn and no samples with source code for further investigation.