In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Quadratic Bezier Spline Tracer

Click anywhere to spawn a tracker of variable speed that moves along the predetermined path. Inspired by Ironhidegames Kingdom Rush
/**
 * Copyright dimumurray ( http://wonderfl.net/user/dimumurray )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/2Z2r
 */

package {
    import flash.text.AntiAliasType;
    import flash.text.TextFormat;
    import flash.text.TextField;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Sprite;
    public class PathTest extends Sprite {
        private var tracerQueue:Vector.<PathTracer> = new Vector.<PathTracer>();
        private var pathSegments:Vector.<QuadBezierSegment> = new Vector.<QuadBezierSegment>();
        private var container:Sprite;
        private var textField:TextField;
        
        private var path:Vector.<Number> = new <Number>[
            176, 112,
            80, 96,
            96, 192,
            112, 288,
            240, 240,
            368, 192,
            432, 256,
            496, 320,
            448, 368,
            400, 416,
            320, 384,
            240, 352,
            192, 368,
            144, 384,
            160, 448,
            176, 512,
            304, 496,
            432, 480,
            496, 496,
            560, 512,
            544, 592
        ];
        
        public function PathTest() {
            stage.align = StageAlign.TOP_LEFT;
            
            var format:TextFormat = new TextFormat();
            format.size = 24;
            format.font = "Trebuchet MS";
            
            textField = new TextField();
            textField.autoSize = "left";
            textField.antiAliasType = AntiAliasType.ADVANCED;
            textField.defaultTextFormat = format;
            textField.htmlText = "CLICK ANYWHERE";
            
            container = addChild(new Sprite()) as Sprite;
            container.scaleY = container.scaleX = .75;
            addChild(textField);
            init();
        }
        
        private function init():void {
            buildPathSegments();
            drawTrack();
            addEventListener(Event.ENTER_FRAME, update);
            stage.addEventListener( MouseEvent.CLICK, spawnTracker );
        }
        
        private function buildPathSegments():void {
            var segment:QuadBezierSegment;
            
            for ( var i:int = 0; i < (path.length - 3); i += 4 ) {
                segment = new QuadBezierSegment(path[i], path[i+1], path[i+4], path[i+5], path[i+2], path[i+3]);
                pathSegments.push(segment);
            }
        }
        
        private function drawTrack():void {
            container.graphics.moveTo(path[0], path[1]);
            var commands:Vector.<int> = new <int>[1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3];
            container.graphics.lineStyle(4, 0xF88011);
            container.graphics.drawPath(commands, path);
        }
        
        private function spawnTracker(e:MouseEvent):void {
            var tracker:Tracker = new Tracker();
            var pathTracer:PathTracer = new PathTracer(tracker, pathSegments, (0.5 + Math.random() * 5));
            
            container.addChild(tracker);
            tracerQueue.push(pathTracer);
        }
        
        private function update(e:Event):void {
            var tracer:PathTracer;
            for each (tracer in tracerQueue) {
                tracer.interpolate();
            }
        }
    }
}
import flash.display.Shape;

import flash.display.DisplayObject;
import flash.geom.Point;

class QuadBezierSegment {
    static private function getArcLength(_ax:Number, _ay:Number, _bx:Number, _by:Number, _cx:Number, _cy:Number):Number {
        var ax:Number = _ax - 2*_cx + _bx;
        var ay:Number = _ay - 2*_cy + _by;
        var bx:Number = 2*(_cx - _ax);
        var by:Number = 2*(_cy - _ay);
        
        var a:Number = 4*(ax*ax + ay*ay);
        var b:Number = 4*(ax*bx + ay*by);
        var c:Number = bx*bx + by*by;
        
        var abc:Number = 2*Math.sqrt(a+b+c);
        var a2:Number  = Math.sqrt(a);
        var a32:Number = 2*a*a2;
        var c2:Number  = 2*Math.sqrt(c);
        var ba:Number  = b/a2;
        
        return (a32*abc + a2*b*(abc-c2) + (4*c*a-b*b)*Math.log((2*a2+ba+abc)/(ba+c2)))/(4*a32);
    }

    private var a:Point;         // start anchor point
    private var b:Point;         // end anchor point
    private var c:Point;         // control point
   
    private var length:Number;   // arc length of the curve
    
    private var ac:Point;       // x and y components of the vector AC
    private var cb:Point;       // x and y components of the vector CB
    
    private var startAngle:Number;
    private var angularDisplacement:Number;
    
    /**
     * Quadratic Bezier Segment.
     *
     * @param ax x-component of the start anchor point.
     * @param ay y-component of the start anchor point.
     * @param bx x-component of the end anchor point.
     * @param by y-component of the end anchor point.
     * @param cx x-component of the control point.
     * @param cy y-component of the control point.
     */
    public function QuadBezierSegment(ax:Number, ay:Number, bx:Number, by:Number, cx:Number, cy:Number) {
        a = new Point(ax, ay);
        b = new Point(bx, by);
        c = new Point(cx, cy);
        
        ac = c.subtract(a);
        cb = b.subtract(c);
        
        startAngle = Math.atan2(ac.y, ac.x);
        
        angularDisplacement = Math.acos( ( ac.x * cb.x + ac.y * cb.y ) / ( ac.length * cb.length ) );
        angularDisplacement *= ((ac.x * cb.y - ac.y * cb.x) < 0) ? -1 : 1;
        
        length = getArcLength(a.x, a.y, b.x, b.y, c.x, c.y);
    }
    
    /**
     * Interpolate using DeCasteljau's Algorithm
     */
    public function interpolateObject(target:DisplayObject, t:Number):void {
        var d:Point = Point.interpolate(c, a, t);
        var e:Point = Point.interpolate(b, c, t);
        
        var f:Point = Point.interpolate(e, d, t);
        
        var g:Point = e.subtract(d);
        var angle:Number = Math.atan2(g.y, g.x) * (180/Math.PI);
        
        target.x = f.x;
        target.y = f.y;
        target.rotation = angle;
        // target.rotation = (startAngle + angularDisplacement * t) * (180/Math.PI); 
    }
    
    public function getLength():Number {
        return length;
    }

}

class PathTracer {
    private var step:Number;
    private var accum:Number = 0;
    
    private var segmentIndex:int = 0;
    private var numSegments:int;
    
    private var target:DisplayObject;
    
    private var path:Vector.<QuadBezierSegment>;
    
    private var segment:QuadBezierSegment;
    
    private var arcLength:Number;
    
    private var t:Number;
    
    public function PathTracer(target:DisplayObject, path:Vector.<QuadBezierSegment>, speed:Number) {
        step = speed || 0;
        this.target = target;
        this.path = path;
        
        numSegments = path.length;
        
        segment = path[segmentIndex];
        segment.interpolateObject(target, 0);
        
        arcLength = segment.getLength();
    }
    
    public function interpolate():void {
        accum += step;
        t = accum / arcLength;
        
        if ( t > 1 ) {
            segmentIndex = (segmentIndex + 1) % numSegments;
            segment = path[segmentIndex];
            
            accum -= arcLength;
            arcLength = segment.getLength();
            t = accum / arcLength;
        }
        
        segment.interpolateObject(target, t);
    }
}

class Tracker extends Shape {
    public function Tracker() {
        graphics.beginFill(0x0066CC);
        graphics.drawRect(-8, -8, 16, 16);
        graphics.endFill();
        
        graphics.lineStyle(2, 0xF88011);
        graphics.moveTo(0, -32);
        graphics.lineTo(0, -16);
        graphics.moveTo(0, 16);
        graphics.lineTo(0, 32);
    }
}