Cubic Spline Generation in AS3 – Catmull Rom Curves


  
While mathematically complex ( at least compared to all the other tutorials on this site ), curve generation techniques such as these catmull rom splines are absolutely critical in the design of games, movies, and media. This specific flash tutorial on spline curve generation only covers 2D creations, but can easily be mapped to a 3D environment, which I will show in another, later tutorial. There is so much power in the knowledge of how to create these splines; in fact, the Pen tool itself in Adobe flash uses similar spline curve creation on it’s back-end implementation (Bezier, not catmull). Here’s a list of some of the practical applications for curve creation using catmull rom splines.

  • General Path interpolation (both 2D and 3D curve mapping)
  • Most blockbuster animated movies ( Pixar, Dreamworks, etc.. ) use catmull rom splines for camera path animations
  • Surface clamping, ex: map a roller-coaster to a track, car to a sloped road, …
  • Combined with a frenet frame, we can get interpolation across a position AND orientation ( something like a full roller coaster simulation, where the camera follows orientation of the track too ).
  • 3D graphics rendering, still-frame and animated
  • Design of a car body; engineers use splines to model and create the smooth shape of a commercial car’s body (Honda, Ford, Toyota cars, etc..)

I hope you can see this has a wide application of uses. So, with that, here’s our source code and demo!

Place the flash file somewhere on the hard drive, and then run “catmull.fla” using CTRL+ENTER inside the Flash IDE.

Here’s the final flash catmull rom dynamic curve example, for your enjoyment:

Click and drag the white circles to observe the dynamic curves in flash, they will move and bend at the user's command!

So lets start development on this curve generating flash tutorial!

  1. Create a new flash document, standard size, 550 pixels by 400 pixels, actionscript 3 (AS3). Set the background color of the stage to a dark color ( mine is 0×003333 ).
  2. Head up to the top, Insert -> New Symbol, Movieclip, name it “ball“, and check the “Export for Actionscript” box. Make sure the class name is “Ball” with a capital B.  Once inside, draw a simple circle to represent your markers that are going to be used for the drag and drop anchors in flash. Make sure they are centered about the orientation cross!
  3. Now that we’ve got the ball in place, all we need to do is write up some quality actionscript 3 code ( about 120 lines! ) But before we do this in Flash, let’s get a quick explanation on how catmull rom actually works..

Using a series of control points, we can procedurally generate spline segments between the points.  As seen in the picture on the right, we use 4 control points and a desired t along the curve to create a new point. We can do this for many different values between t = 0 and t  = 1.  In this flash tutorial, we build 200 segments between each pair of control points, by starting t = 0, and incrementing each time by 0.005 in a for loop, up to t = 1.  The easiest way to ensure we have four control points at all times is to loop back from the last to first control point at the end. Sometimes, developers will either fabricate additional control points that are not visible, or use a “Blended Parabolas” technique to tie off the ends of the interpolation path.

The equation we are using to find sub spline points between P1 and P2 is this:

Where y is the result vector, t is the parameter (between 0 and 1) and the x’s are the grid vectors. In other words, we have 4 points, x1…x4 and we’re trying to interpolate smoothly between the middle two, x2 and x3. At t=0, the above equation evaluates to y = x2, and at t=1, it evaluates to x3. Intermediate values give the points in-between we are trying to find.

With that, let’s get to the actionscript 3 development for this spline curve flash tutorial!

Flash initialization and the Catmull Rom actionscript 3 function:

var lineDrawing:MovieClip;

var Spline_Points:Array = new Array();
var Spline_Lengths:Array = new Array();

var Interpolation_Points:Array = new Array(new Point(100,100),new Point(50,200),new Point(450,250),new Point(100,300));

//CATMULL ROM FUNCTIONALITY
function calculate_catmull(t:Number, p0:Number, p1:Number, p2:Number, p3:Number):Array{
    var t2:Number = t*t;
    var t3:Number = t2 * t;
    // P(t)
    var firstn:Number = (0.5 *( (2 * p1) + (-p0 + p2) * t +(2*p0 - 5*p1 + 4*p2 - p3) * t2 +(-p0 + 3*p1- 3*p2 + p3) * t3));
    // P'(t)
    var secondn:Number = 0.5 * (-p0 + p2) + t * (2*p0 - 5*p1 + 4*p2 - p3) + t2 * 1.5 * (-p0 + 3*p1 - 3*p2 + p3);
    // P''(t)
    var thirdn:Number = (2*p0 - 5*p1 + 4*p2 - p3) + t * 3.0 * (-p0 + 3*p1 - 3*p2 + p3);

    var arr:Array = new Array(firstn, secondn, thirdn);
    return arr;
}
//END CATMULL ROM FUNCTIONALITY

We create an “Interpolation_Points” array, and initialize some points we can to draw. The calculate catmull function does exactly what the mathematical equation above represents. Multiply out the matrix against the control points, get our primary function, the first and second derivatives, and return.

Next piece of the code, Drawing the Markers:

function draw_anchors():void{
    for(var k:Number = 0; k < Interpolation_Points.length; k++){ //loop through all the interpolation points, and render anchors
        var marker:Ball = new Ball();
        marker.x = Interpolation_Points[k].x;
        marker.y = Interpolation_Points[k].y;
        marker.scaleX = 0.5;
        marker.scaleY = 0.5;
        marker.id = k;
        marker.addEventListener(MouseEvent.MOUSE_DOWN,start_dragging);
        marker.addEventListener(MouseEvent.MOUSE_UP,stop_dragging);
        addChild(marker);
    }
}

Pretty simple actionscript? We iterate over the Interpolation_Points array, create a new Ball for each, set it’s position, add visibility by plopping it on the stage, and then attach the drag and drop functions.

Next chunk of actionscript code, Drag and Drop Functionality in Flash:

function start_dragging(event:MouseEvent):void{
    event.target.addEventListener(MouseEvent.MOUSE_MOVE, update_dragging);
    event.target.startDrag();
}
function stop_dragging(event:MouseEvent):void{
    event.target.removeEventListener(MouseEvent.MOUSE_MOVE, update_dragging);
    event.target.stopDrag();
}

function update_dragging(event:MouseEvent):void{
    Interpolation_Points[event.target.id].x = event.target.x;
    Interpolation_Points[event.target.id].y = event.target.y;
    lineDrawing.graphics.clear();
    lineDrawing = new MovieClip();
    Spline_Points = new Array();
    init_track();
}

Once we click down on a marker, add continuous updating so our spline curve in the flash tutorial will update real-time. When our mouse button is raised ( no longer holding down ), remove the continuous updating and remove the drag functionality.

Inside update_dragging, we modify the Interpolation_Points position to match the ball’s, clear the splines onscreen, reset the array, and draw again using the init_track actionscript function, which is up next.

Spline Curve Rendering:

function init_track():void{
   
    for(var k:Number = 0; k < Interpolation_Points.length; k++){ //loop through all the interpolation points, and build splines in between
       
        var point0:Point = Interpolation_Points[k];
        var point1:Point = Interpolation_Points[(k+1) % Interpolation_Points.length];
        var point2:Point = Interpolation_Points[(k+2) % Interpolation_Points.length];
        var point3:Point = Interpolation_Points[(k+3) % Interpolation_Points.length];

        for(var g:Number = 0.0; g < 1.0; g += 0.005){ //for each segment, calculate 200 spine points and store onto vector
           
            var setX:Array = calculate_catmull(g, point0.x, point1.x, point2.x, point3.x);
            var setY:Array = calculate_catmull(g, point0.y, point1.y, point2.y, point3.y);

            // .x/.y/.z labelled incorrectly
            var spline_spot:Point = new Point(setX[0],setY[0]);
            var spline_spot_first:Point = new Point(setX[1],setY[1]);
            var spline_spot_second:Point = new Point(setX[2],setY[2]);
            //end incorrect labelling


            Spline_Points.push(spline_spot);
        }
    }
   
    lineDrawing = new MovieClip();
    this.addChild(lineDrawing);
   
    lineDrawing.graphics.lineStyle(1,0xFFFF00);
    lineDrawing.graphics.moveTo(Spline_Points[0].x,Spline_Points[0].y); ///This is where we start drawing
    for(var iter:Number = 1; iter < Spline_Points.length; iter++){
        lineDrawing.graphics.lineTo(Spline_Points[iter].x, Spline_Points[iter].y);
    }
   
    var filtersArray:Array = new Array();
    var color_array:Array = new Array(0xFF0000, 0x00FF00, 0x0000FF);
    for(var j:int = 0; j < color_array.length; j++){
        var dropShadow:DropShadowFilter = new DropShadowFilter();
        dropShadow.color = color_array[j];
        dropShadow.blurX = 1;
        dropShadow.blurY = 1;
        dropShadow.angle = 0;
        dropShadow.alpha = 1;
        dropShadow.distance = 4;
        filtersArray.push(dropShadow);
    }
    setChildIndex(lineDrawing,0);
   
    lineDrawing.filters = filtersArray;
}

Here’s where the rest of the magic happens in this flash and actionscript tutorial. Loop through all of the interpolation points, grabbing every combination of 4 points in series. For each set of four control points, iterate over t from 0 to 1, for 200 separate sub-sections ( as we explained above with the mathematical equation! ). After calculating the catmull, we then push the new point onto our iteration array, to be drawn after ( spline_spot_first and spline_spot_second aren’t actually used in this tutorial, but they’re the first and second derivatives we would need if we were worried about orientation! ). Once we have an array of all the sub segments of the curve we wanted, it is time to actually draw them!

Dynamically create a new line drawing, and iteratively call moveTo for the line to update, position by position, where our curve is moving to. At the end of that, we add a series of drop shadows to give a 3D effect on the line, which actually looks pretty cool! I’ll explain in detail how dynamic filters work at a later time! We also call setChildIndex(lineDrawing, 0) to push the spline curves down to the bottom, below the markers, so they’re still easy to grab and see by the user.

Only thing after all this code, is actually calling the functions themselves!

draw_anchors();
init_track();

At that, we are done with the flash tutorial and actionscript 3 development! Press Ctrl+Enter to fire it up and see your masterpiece! I plan on adding easing, animation, and eventually 3D maneuverability to catmull rom’s so please stay tuned and be on the lookout for more, great flash cove tutorials here on the site!

-Dave at the Flash Cove (http://flashcove.net)

About Dave

Hey Guys! I'm Dave, an Ohio State CSE alumni who just recently started work in the industry. I'm a flash hobbyist at heart, and love making flash games and tutorials when the time permits. Check out what flash tutorials are available on the site, and don't forget to "Like Us!" on Facebook!