Math and (Quasi) Physics in Action Script 3

Verlet Structure

A simple demonstration of Verlet structure based on AdvancED ActionScript 3.0 Animation by Keith Peters

Main.as

´╗┐package {
  import flash.display.*;
  import flash.events.*;
  import flash.geom.Rectangle;
  
  public class Main extends Sprite{
    private var points:Vector.<VerletPoint>;
    private var sticks:Vector.<VerletStick>;
    private var rect:Rectangle;
    private var acc:uint = 1;
    private var len:uint = 15;
    
    public function Main() {
      points = new Vector.<VerletPoint>;
      sticks = new Vector.<VerletStick>;
      rect = new Rectangle(0,0,stage.stageWidth,stage.stageHeight);
      
      for (var i:uint = 0; i < len; i ++) { 
        if (i >= 1) {
          points.push(new VerletPoint(Math.random() * 100 - 50 + points[i - 1].x, Math.random() * 100 - 50 + points[i - 1].y));
          points[i].constrain(rect);                       
          sticks.push(new VerletStick(points[i], points[i - 1]));
        } else {
          points.push(new VerletPoint(Math.random() * stage.stageWidth, Math.random() * stage.stageHeight));
        }
        if (Math.random() < 0.5 && i >= 2) {
          sticks.push(new VerletStick(points[i], points[Math.floor(Math.random() * (i - 2))]));
        }
        
        points[i].x += Math.random() * 7 - 4;
        points[i].y += Math.random() * 7 - 4;
      }
      addEventListener(Event.ENTER_FRAME, h_enterFrame);
    }
    
    private function h_enterFrame(evt:Event):void {
      var i:uint, j:uint;
      for (i = 0; i < points.length; i ++) { 
        points[i].update();
      }
      for (i = 0; i < points.length; i ++) { 
        for (j = 0; j < acc; j ++) {
          points[i].constrain(rect);
        }
      }
      for (i = 0; i < sticks.length; i ++) { 
        for (j = 0; j < acc; j ++) {
          sticks[i].update();
        }
      }
      
      graphics.clear();
      for (i = 0; i < points.length; i ++) { 
        points[i].render(graphics);
      }
      
      for (i = 0; i < sticks.length; i ++) { 
        sticks[i].render(graphics);
      }
    }
    
  }
}

VerletPoint.as

´╗┐package {
  import flash.display.Graphics;
  import flash.geom.Rectangle;
  public class VerletPoint {
    public var x:Number;
    public var y:Number;
    private var ox:Number;
    private var oy:Number;

    public function VerletPoint(x:Number, y:Number) {
      setPos(x, y);
    }
    public function update():void {
      var tx:Number=x;
      var ty:Number=y;
      x+=vx;
      y+=vy;
      ox=tx;
      oy=ty;
    }
    public function setPos(x:Number, y:Number):void {
      this.x = ox = x;
      this.y = oy = y;
    }
    public function constrain(rect:Rectangle):void {
      x = Math.max(rect.left, Math.min(rect.right, x));
      y = Math.max(rect.top, Math.min(rect.bottom, y));
    }
    public function set vx(n:Number):void {
      ox = x - n;
    }
    public function set vy(n:Number):void {
      oy = y - n;
    }
    public function get vx():Number {
      return x - ox;
    }
    public function get vy():Number {
      return y - oy;
    }
    public function render(g:Graphics):void {
      g.beginFill(0xFF6600, 0.5);
      g.drawCircle(x, y, 3);
      g.endFill();
    }
  }
}

VerletStick.as

´╗┐package {
  import flash.display.Graphics;
  public class VerletStick {
    private var pa:VerletPoint;
    private var pb:VerletPoint;
    private var len:Number;
    
    public function VerletStick(pa:VerletPoint, pb:VerletPoint, len:Number = -1) {
      this.pa = pa;
      this.pb = pb;
      if (len == -1) {
        var dx:Number = pa.x - pb.x;
        var dy:Number = pa.y - pb.y;
        this.len = Math.sqrt(dx * dx + dy * dy)
      } else {
        this.len = len;
      }
    }
    
    public function update():void {
      var dx:Number = pb.x - pa.x;
      var dy:Number = pb.y - pa.y;
      var d:Number = Math.sqrt(dx * dx + dy * dy);
      var diff:Number = len - d;
      var offsetX:Number = (diff * dx / d) / 2;
      var offsetY:Number = (diff * dy / d) / 2; 
      pa.x -= offsetX;
      pa.y -= offsetY;
      pb.x += offsetX;
      pb.y += offsetY;
    }
    public function render(g:Graphics):void {
      g.lineStyle(0,0xFF6600,0.5);
      g.moveTo(pa.x, pa.y);
      g.lineTo(pb.x, pb.y);
    }
  }
}