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

Balloon algorithm

Balloon algorithm, it allows you to move the balloon by its string using physics, gravity and more
/**
 * Copyright yoambulante.com ( http://wonderfl.net/user/yoambulante.com )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/tRiO
 */

/********************************************************/
/*  Copyright © YoAmbulante (alex.nino@yoambulante.com) */
/*                                                      */
/*  You may modify or sell this code, you can do        */
/*  whatever you want with it, I don't mind. Just don't */
/*  forget to contribute with a beer thru the website.  */
/*                                                      */
/*  Visit http://www.yoambulante.com to clarify any     */
/*  doubt with this code. Note that donators have       */
/*  priority on information enquiries.                  */
/*                                                      */
/********************************************************/
package {
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.system.LoaderContext;
    import flash.system.SecurityDomain;
    import flash.system.ApplicationDomain;
    import flash.events.IOErrorEvent;
    import flash.events.SecurityErrorEvent;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.text.TextField;
    import flash.net.URLRequest;
    [SWF(width="650", height="600", frameRate="30", backgroundColor="#FFFFFF")]
    public class BalloonMain extends Sprite {        
        private var points:Array;
        private var sticks:Array;
        private var numdots:int;        
        private var prevPoint:Point;
        private var startPoint:Point;
        private var endPoint:Point;
        private var nextPoint:Point;
        private var xloc:Number;
        private var yloc:Number;        
        private var draggable:DraggableDot;
        private var draggers:Array;
        private var balloon:Sprite;
        private var rotation_target:Number;        
        private const numSteps:int = 20;
        private const numStepsFact:int = 1/numSteps;
        private const RAD_DEG:Number = (180 / Math.PI);                
        private var t:TextField;
        function BalloonMain():void {            
            rotation_target = 0;
            stage.align = StageAlign.TOP_LEFT;            
            stage.scaleMode = StageScaleMode.NO_SCALE;
            // entry point            
            points = new Array();
            var i:int;
            for (i=0; i<30; i++){
                points[i] = new VerletPoint(i%2==0?400:405, 500-(i*5));
            }
            numdots = points.length;        
            //points[6].anchor(400, 500);
            prevPoint = new Point();
            startPoint = new Point();
            endPoint = new Point();
            nextPoint = new Point();            
            
            sticks = new Array();
            for (i=0; i<points.length-1; i++){
                sticks.push(new VerletStick(points[i], points[i+1]));
            }            
            //create draggers
            draggers = new Array();
            for (i = 0; i < sticks.length; i++) {
                var sp:DraggableDot = new DraggableDot(VerletStick(sticks[i])._pointA);                
                sp.graphics.beginFill(0x0011FF,0);
                sp.graphics.drawCircle(0, 0, 5);                
                sp.addEventListener(MouseEvent.MOUSE_DOWN, drag);
                addChild(sp);
                draggers[i] = sp;                
            }
            stage.addEventListener(MouseEvent.MOUSE_UP, drop);
            draggable = null;            
            balloon = new Sprite();
            balloon.y = 500;
            addChild(balloon);            
            
            t = new TextField();
            t.text = "Balloon, beta version by Alex Nino (YoAmbulante.com), 28th Oct 2009 - 3:45am";
            t.selectable = false;
            t.width = 350;
            t.textColor = 0xCCCCCC;
            addChild(t);
            
            this.addEventListener(Event.ENTER_FRAME, enterFrame);
            enterFrame(null);
            
            //load external balloon skin asset
            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, attachBalloonSkin);
            loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, emptyEvent);
            loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, emptyEvent);
    
            var context:LoaderContext = new LoaderContext();
            context.applicationDomain = ApplicationDomain.currentDomain;
            context.securityDomain = SecurityDomain.currentDomain;
            
            loader.load(new URLRequest("http://www.yoambulante.com/swf/labs/BalloonSkin.swf"), context);
        }        
        private function attachBalloonSkin(e:Event):void {
            var C:Class = LoaderInfo(e.currentTarget).applicationDomain.getDefinition("BalloonRed") as Class;
            balloon.addChild( new C as Sprite );
        }
        private function emptyEvent(e:Event):void {
            t.text = "error "+e;
        }
        private function drag(e:MouseEvent):void {
            draggable = e.currentTarget as DraggableDot;            
            draggable.startDrag(true);            
            draggable.dragging = true;
            draggable.point.anchored = true;            
        }
        private function drop(e:MouseEvent):void {
            if (draggable == null) return;
            draggable.stopDrag();            
            draggable.dragging = false;
            draggable.point.anchored = false;            
            draggable = null;
        }
        private function enterFrame(e:Event):void {
            if (draggable != null) {
                draggable.point.x = draggable.x;
                draggable.point.y = draggable.y;
            }                
            var total:int = sticks.length - 1;
            var i:int;
            var time:Number = new Date().getTime();
            var stick:VerletStick;
            var moving:Boolean = true;
            for (i=0; i<=total; i++){
                stick = VerletStick(sticks[i]);                
                stick._pointA.y += .4;                
                stick._pointA.update(); //check new position depending on its position.
                if (i == total){
                    stick._pointB.y += .4;
                    if (balloon.y > 80){
                        stick._pointB.y -= draggable?10:14; //apply ballon force
                    } else {
                        stick._pointB.y -= 12; //neutral force                        
                    }
                }
                stick._pointB.update();                
                stick.update();    //stick the two points togueter    i >= dot_index
            }
            var loops:int = 11;
            do {
                for (i = 0; i <= total; i++) {
                    stick = VerletStick(sticks[i]);    
                    stick.update();    //stick the points togueter                        
                }
            } while (--loops > 0);                    
            //update draggers positions.
            for (i = 0; i < sticks.length; i++) {
                DraggableDot(draggers[i]).update();
            }                
            balloon.x = points[numdots - 3].x;
            balloon.y = points[numdots - 3].y;            
            //renders all dots using cardinal splines.
            if (true) {
                graphics.clear();    
                graphics.lineStyle(1,0x999999,1,false,"normal","round","round");
                var n:uint = numdots - 2;
                graphics.moveTo(points[0].x, points[0].y);                
                //update rotation.            
                var dx:Number = balloon.x - points[numdots - 10].x;
                var dy:Number = balloon.y - points[numdots - 10].y;
                var dist:Number = Math.sqrt(dx * dx + dy * dy);        
                balloon.rotation = Math.asin(dx / dist) * RAD_DEG * .5;
                for (i = 2; i < n; i++) {
                    if (i-1 >= 0){
                        prevPoint.x = points[i-1].x;
                        prevPoint.y = points[i-1].y;
                    }                
                    startPoint.x = points[i].x;
                    startPoint.y = points[i].y;                    
                    if (i+1 < numdots){
                        endPoint.x = points[i+1].x;
                        endPoint.y = points[i+1].y;    
                    }
                    if (i+2 < numdots){
                        nextPoint.x = points[i+2].x;
                        nextPoint.y = points[i+2].y;    
                    }            
                    for (var j:int=0; j<=numSteps; j++) {                                        
                        xloc = getCardinalSplinePoint(prevPoint.x, startPoint.x, endPoint.x, nextPoint.x, numStepsFact, j, .5);
                        yloc = getCardinalSplinePoint(prevPoint.y, startPoint.y, endPoint.y, nextPoint.y, numStepsFact, j, .5);                    
                        if ((i == 0) && (j == 0)) {
                            graphics.moveTo(xloc, yloc);
                        } else {
                            graphics.lineTo(xloc, yloc);
                        }
                    }
                }
            }
        }        
    }    
}
function getCardinalSplinePoint(prevVal:Number, startVal:Number, endVal:Number, nextVal:Number, numSteps:Number, curStep:Number, tension:Number):Number {
    var t1:Number = (endVal - prevVal)*tension; 
    var t2:Number = (nextVal - startVal)*tension;                        
    var s:Number = curStep * numSteps; 
    var h1:Number = (2 * Math.pow(s,3)) - (3 * Math.pow(s,2)) + 1; 
    var h2:Number = -(2 * Math.pow(s,3)) + (3 * Math.pow(s,2)); 
    var h3:Number = Math.pow(s,3) - (2 * Math.pow(s,2)) + s; 
    var h4:Number = Math.pow(s,3) - Math.pow(s,2);            
    var value:Number = (h1 * startVal) + (h2 * endVal) + (h3 * t1) + (h4 * t2);            
    return value; 
}
class DraggableDot extends flash.display.Sprite {
    public var point:VerletPoint;            
    public var dragging:Boolean;                    
    public function DraggableDot(p:VerletPoint) {                        
        dragging = false;
        point = p;                        
        update();
    }
    public function update():void {
        if (!dragging){
            x = point.x;
            y = point.y;
        }
    }    
}
class VerletPoint {
    public var x:Number;
    public var y:Number;    
    public var anc_x:Number;
    public var anc_y:Number;
    public var anchored:Boolean;                
    private var _oldX:Number;
    private var _oldY:Number;
    private static const SPEED_FACT:Number = .92;
    function VerletPoint(x:Number, y:Number) {                        
        anchored = false;
        anc_x = 0;
        anc_y = 0;
        setPosition(x, y);
    }
    public function anchor(x:Number, y:Number):void {
        anchored = true;
        anc_x = x;
        anc_y = y;
    }
    public function update():void {
        //move this dot to next position depending on its speed and direction.            
        var tempX:Number = x;
        var tempY:Number = y;
        if (!anchored){
            x += vx; //vel in x (speed)
            y += vy; //vel in y (speed)
        }
        _oldX = tempX;
        _oldY = tempY;
    }
    public function setPosition(x:Number, y:Number):void {
        //setup a point position and reset speed.
        this.x = _oldX = x;
        this.y = _oldY = y;
    }
    public function constrain(left:Number, right:Number, top:Number, bottom:Number):void {
        //ensure that the points are still inside limits (the screen)
        x = Math.max(left, Math.min(right, x));
        y = Math.max(top, Math.min(bottom, y));
    }
    public function get vx():Number    {
        return (x - _oldX) * SPEED_FACT;
    }
    public function get vy():Number {
        return (y - _oldY) * SPEED_FACT;
    }        
}
class VerletStick {
    public var _pointA:VerletPoint;
    public var _pointB:VerletPoint;
    private var _length:Number;        
    function VerletStick(pointA:VerletPoint, pointB:VerletPoint, length:Number = NaN) {
        _pointA = pointA;
        _pointB = pointB;            
        if(isNaN(length)){
            var dx:Number = _pointA.x - _pointB.x;
            var dy:Number = _pointA.y - _pointB.y;
            _length = Math.sqrt(dx * dx + dy * dy);
        } else {
            _length = length;
        }
    }
    public function update(stiff:Boolean = false):void {
        var dx:Number = _pointB.x - _pointA.x;
        var dy:Number = _pointB.y - _pointA.y;                        
        var dist:Number = Math.sqrt(dx * dx + dy * dy);
        var diff:Number = _length - dist;
        var offsetX:Number = (diff * dx / dist) / 2;
        var offsetY:Number = (diff * dy / dist) / 2;        
        _pointA.x -= offsetX;
        _pointA.y -= offsetY;
        _pointB.x += offsetX;
        _pointB.y += offsetY;
    }
}