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

Watercolor wanderer forked from: 絵の具まぜまぜ(水彩風お絵かき)

also look http://wonderfl.net/c/8Tjs and other wonderfls from the same author : http://wonderfl.net/user/mtok
package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.filters.BlurFilter;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import com.bit101.components.PushButton
    public class RsakaneWaterColorClient extends Sprite
    {
        private var functions:Vector.<Function>;
        private var rsakaneWaterColor:RsakaneWaterColor;
        private var vehicle:Vehicle;
        private var vehicle2:Vehicle;
        private var vData1:Array;
        private var vData2:Array;
        private var bd:BitmapData;
        private var blurFilter:BlurFilter;
        private var rr:Rectangle;
        private var pointOrigin:Point;
        private var count:int;
        private var bitmap:Bitmap;
        public function RsakaneWaterColorClient() 
        {
            bd = new BitmapData(465, 415, false);
            rr = bd.rect;
            blurFilter = new BlurFilter(1.05, 1.05, 1);
            bitmap = new Bitmap(bd, "auto", true);
            pointOrigin = new Point;
            addChild(bitmap);
            RsakaneWaterColor.bd = bd;
            rsakaneWaterColor = addChild(new RsakaneWaterColor) as RsakaneWaterColor;
            
            var sprite:Sprite = addChild(new Sprite) as Sprite;
            vehicle = new Vehicle(sprite);
            vehicle.edgeBehavior = Vehicle.BOUNCE;
            
            var sprite2:Sprite = addChild(new Sprite) as Sprite;
            vehicle2 = new Vehicle(sprite2);
            vehicle2.edgeBehavior = Vehicle.BOUNCE;
            
            vData1 = [new Point(vehicle.x, vehicle.y)];
            vData2 = [new Point(vehicle2.x, vehicle2.y)];

            addEventListener(Event.ENTER_FRAME, loop);
            new PushButton(this,0,435,'unexpected',doRestart)
        }
        
        private function doRestart(e:Event):void 
        {
            removeEventListener(Event.ENTER_FRAME, loop);
            bd.dispose();
            bd = new BitmapData(465, 415, true);
            bitmap.bitmapData = bd;
            RsakaneWaterColor.bd = bd;
            addEventListener(Event.ENTER_FRAME, loop);
            var pushButton:PushButton = e.target as PushButton;
            pushButton.removeEventListener(MouseEvent.CLICK, doRestart);
            pushButton.parent.removeChild(pushButton);
            
        }
        
        private function loop(e:Event):void 
        {
            vehicle.wander();
            vehicle.update();
            rsakaneWaterColor.drawIt(vehicle.x, vehicle.y, vData1);
            vehicle2.wander();
            vehicle2.update();
            rsakaneWaterColor.drawIt(vehicle2.x, vehicle2.y, vData2, 0xff0000);
            if ( !((count++) % 10) )
            {
                trace("111111111" )
                bd.applyFilter(bd, rr, pointOrigin , blurFilter);
            }
        }
        
    }
}
    import com.bit101.components.*;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import frocessing.color.ColorHSV;
 
    class RsakaneWaterColor extends Sprite
    {
        private const DIFF:Number = 0.02;
        public static var bd:BitmapData;
        private var image:BitmapData;
        private var buffer:Array = new Array();
        private var i:int = 0;
        private var r:int = 0;
        private var g:int = 0;
        private var b:int = 0;
        //private var color:int = 0x00FFFF;
        private var color:int = 0;
        private var strengthSlider:HUISlider;
        private var cslider:HUISlider;
        private var factor:Number;
        private var dd:int;
        private var circle:Sprite;
        private var _brushSize:Number;
        
        public function RsakaneWaterColor()
        {
            addEventListener(Event.ADDED_TO_STAGE, init);
            //Wonderfl.capture_delay( 30 );
            
            circle = new Sprite();
            
            
            brushSize = 10;
            brushSize = 10;
            setBrushSize(brushSize);
            
            var button:PushButton = new PushButton(this, 365, 445, "clear", clear);
            strengthSlider = new HUISlider(this, 180, 445, "strength");
            strengthSlider.minimum = 0.1;
            strengthSlider.maximum = 0.8;
            strengthSlider.value = 0.8;
            
            var cslider:Slider = new Slider();
            cslider.y = 465 - cslider.height - 10;
            cslider.x = 1;
            cslider.addEventListener(Event.CHANGE, function():void { color = cslider.value; } );
            //addChild(cslider);
            var vbox:VBox = new VBox(this, 10,330);
            factor = 0.15;
            var hUISlider:HUISlider = new HUISlider(vbox, 10, 10, 'dt', onhhhfh);
            hUISlider.minimum = factor - 0.14;
            hUISlider.maximum = factor + 0.5;
            hUISlider.value = factor;
            
            dd = 1;
            var hUISlider2:HUISlider = new HUISlider(vbox, 10, 30, 'dx', onDD);
            hUISlider2.minimum = 0;
            hUISlider2.maximum = 30;
            hUISlider2.value = dd;
            
            var hUISlider3:HUISlider = new HUISlider(vbox, 10, 30, 'brush', onBrush);
            hUISlider3.minimum = 1;
            hUISlider3.maximum = 30;
            hUISlider3.value = brushSize;

        }
        
        private function onBrush(e:Event):void 
        {
            brushSize = HUISlider(e.target).value;
            setBrushSize(brushSize);
        }
        
        private function onDD(e:Event):void 
        {
            dd = HUISlider(e.target).value;
        }
        
        private function onhhhfh(e:Event):void 
        {
            var hUISlider:HUISlider = HUISlider(e.target);
            factor = hUISlider.value;
        }
        
        private function init(e:Event):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            clear();
            
            stage.quality = "best";
            //stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            //stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            
            
        }
        
        public function clear(event:Event = null):void
        {
            bd.fillRect(bd.rect, 0xFFFFFF);
        }
        
        private function onMouseDown(event:MouseEvent):void
        {
            doPencilLow(mouseX, mouseY);
            stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        }
        
        
        
        private function onMouseUp(event:MouseEvent):void
        {
            doPencilRise();
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
        }
        
        private function onMouseMove(event:MouseEvent):void
        {
            //var mx:int = mouseX;
            //var my:int = mouseY;
            
            drawIt(mouseX,mouseY,buffer);
        }
        
        private function setBrushSize(size:Number):void 
        {
            circle.graphics.clear();
            circle.graphics.beginFill(0x0);
            circle.graphics.drawCircle(size, size, size);
            circle.graphics.endFill();

            image = new BitmapData(size*2, size*2, true, 0x0);
            
            image.draw(circle);
        }
        
        public function doPencilLow(xx:Number, yy:Number):void 
        {
            buffer.push(new Point(xx, yy));
        }
        
        public function doPencilRise():void 
        {
            buffer = new Array();
            i = 0;
        }
        
        public function drawIt(mx:int, my:int, bufferParam:Array, colorParam:uint=0 ):void 
        {
            mx = Math.max(0, mx);
            my = Math.max(0, my);
            mx = Math.min(bd.width, mx);
            my = Math.min(bd.height, my);
            
            bufferParam.push(new Point(mx, my));
            if (bufferParam.length < 4) return;
            bufferParam.shift();
            var prev:Point = bufferParam[bufferParam.length-3];
            var next:Point = bufferParam[bufferParam.length-2];
            next.x = (prev.x + bufferParam[bufferParam.length-1].x) / 2;
            next.y = (prev.y + bufferParam[bufferParam.length-1].y) / 2;
            
            var dx:Number = next.x - prev.x;
            var dy:Number = next.y - prev.y;
            //var s:Number = 0.3 / Math.sqrt(dx * dx + dy * dy) / 2;
            var s:Number = factor / Math.sqrt(dx * dx + dy * dy)
            var colorA:ColorHSV = new ColorHSV();
            var colorB:ColorHSV = new ColorHSV();
            var colorC:ColorHSV = new ColorHSV();
            var colorD:ColorHSV = new ColorHSV();
            
            var colorE:ColorHSV = new ColorHSV();
            colorE.value = colorParam;
            
            for (var t:Number = 0.0; t < 1.0; t += s)
            {    
                var tx:Number = prev.x + dx * t;
                var ty:Number = prev.y + dy * t;
                
                colorA.value = (tx - dd < 0) ?             0xFFFFFF : bd.getPixel(tx - dd, ty);
                colorB.value = (bd.width <= tx + dd) ?     0xFFFFFF : bd.getPixel(tx + dd, ty);
                colorC.value = (ty - dd < 0) ?             0xFFFFFF : bd.getPixel(tx, ty - dd);
                colorD.value = (bd.height <= ty + dd) ? 0xFFFFFF : bd.getPixel(tx, ty + dd);
                
                r = r + ((colorA.r + colorB.r + colorC.r + colorD.r) / 4 - r) * (1.0 - strengthSlider.value);
                g = g + ((colorA.g + colorB.g + colorC.g + colorD.g) / 4 - g) * (1.0 - strengthSlider.value);
                b = b + ((colorA.b + colorB.b + colorC.b + colorD.b) / 4 - b) * (1.0 - strengthSlider.value);
                
                r = r + (colorE.r - r) * DIFF;
                g = g + (colorE.g - g) * DIFF;
                b = b + (colorE.b - b) * DIFF;
                
                colorA.r = r;
                colorA.g = g;
                colorA.b = b;
                
                image.threshold(image, image.rect, new Point(), "!=", 0x0, colorA.value32, 0xFF000000);
                bd.copyPixels(image, image.rect, new Point(prev.x + dx * t, prev.y + dy * t));
            }
            i++;
        }
        
        public function get brushSize():Number 
        {
            return _brushSize;
        }
        
        public function set brushSize(value:Number):void 
        {
            _brushSize = value;
        }
    }


class Slider extends Sprite
{
    private var bar:Sprite;
    private var bd:BitmapData;
    public var value:Number = 0x00FF00;
    
    public function Slider()
    {
        var color:ColorHSV = new ColorHSV();
        bd = new BitmapData(200, 30, false, 0xFF0000);
        addChild(new Bitmap(bd));
        
        for (var x:int = 0; x < bd.width; x++)
        {
            color.h = 360 * (x / bd.width);
            bd.fillRect(new Rectangle(x, 0, 1, bd.height), color.value);
        }
        
        bar = new Sprite();
        bar.graphics.beginFill(0x0);
        bar.graphics.moveTo(0, 30);
        bar.graphics.lineTo(-10, 40);
        bar.graphics.lineTo(10, 40);
        bar.graphics.endFill();
        addChild(bar);
        
        bar.x = 100;
        onEnterFrame();
        
        this.addEventListener(MouseEvent.CLICK, function():void { bar.x = mouseX; onEnterFrame(); } );
        bar.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
    }
    
    private function onMouseDown(event:MouseEvent):void
    {
        bar.startDrag(false, new Rectangle(0, 0, bd.width, 0));
        
        var stop:Function = function():void { bar.stopDrag(); removeEventListener(Event.ENTER_FRAME, onEnterFrame); };
        stage.addEventListener(Event.MOUSE_LEAVE, stop);
        stage.addEventListener(MouseEvent.MOUSE_UP, stop);
        
        addEventListener(Event.ENTER_FRAME, onEnterFrame);
    }
    
    private function onEnterFrame(event:Event = null):void
    {
        value = bd.getPixel(bar.x, 0);
        this.dispatchEvent(new Event(Event.CHANGE));
    }
}
class Vector2D {
    private var _x:Number;
    private var _y:Number;
    public function Vector2D(x:Number = 0, y:Number = 0) {
        _x = x;
        _y = y;
    }
    
    /**
     * draw 
     */
    public function draw(graphics:Graphics, color:uint):void {
        graphics.lineStyle(0, color);
        graphics.moveTo(0, 0);
        graphics.lineTo(_x, _y);
    }
    public function get x():Number { return _x; }
    public function set x(value:Number):void 
    {
        _x = value;
    }
    
    public function get y():Number { return _y; }
    public function set y(value:Number):void 
    {
        _y = value;
    }
    
    /**
     * 複製する
     * @return
     */
    public function clone():Vector2D {
        return new Vector2D(_x, _y);
    }
    /**
     * ベクトルをゼロに
     * @return
     */
    public function zero():Vector2D {
        _x = _y = 0;
        return this;
    }
    
    /**
     * ベクトルがゼロか?
     * @return
     */
    public function isZero():Boolean {
        return _x == 0 && _y == 0;
    }
    
    /**
     * ベクトルの大きさを指定したサイズに
     */
    public function set length(value:Number):void {
        var a:Number = angle;
        _x = Math.cos(a) * value;
        _y = Math.sin(a) * value;
    }
    
    /**
     * ベクトルの長さ
     */
    public function get length():Number {
        return Math.sqrt(lengthSQ);
    }
    
    /**
     * ベクトルの長さの2乗
     */
    public function get lengthSQ():Number {
        return _x * _x + _y * _y;
    }
    
    public function get angle():Number {
        return  Math.atan2(_y, _x);
    }
    
    public function set angle(value:Number):void 
    {
        var len:Number = length;
        _x = Math.cos(value) * len;
        _y = Math.sin(value) * len;
    }
    
    
    /**
     * ベクトルを正規化する
     * ベクトルが0の場合、結果を(1,0)とする
     * @return
     */
    public function normalize():Vector2D {
        if (length == 0) {
            _x = 1;
        }else {
            var len:Number = length;        
            _x /= len;
            _y /= len;
            
        }
        return this;
    }
    /**
     * ベクトルの大きさをmaxまでにカットする。
     * @param    max
     * @return
     */
    public function truncate(max:Number):Vector2D {
        var len:Number = length;
        if (len > max) {
            length = max;
        }
        return this;
    }
    /**
     * ベクトルの向きを逆に
     * @return
     */
    public function reverse():Vector2D {
        _x = -_x;
        _y = -_y;
        return this;
    }
    /**
     * ベクトルが正規化されているか?
     * @return
     */
    public function isNormalized():Boolean {
        return length == 1.0;
    }
    
    /**
     * ベクトル Vとの内積を求める
     * @param    v
     * @return
     */
    public function dotProduct(v:Vector2D):Number {
        return _x * v._x + _y * v._y;
    }
    
    /**
     * 内積からベクトルのなす角を求める -PI/2 ~ PI/2
     * @param    v1
     * @param    v2
     * @return
     */
    public static function angleBetween(v1:Vector2D, v2:Vector2D):Number {
        if (!v1.isNormalized()) v1 = v1.clone().normalize();
        if (!v2.isNormalized()) v2 = v2.clone().normalize();
        
        return Math.acos(v1.dotProduct(v2));
    }
    /**
     * ベクトルvが右にあるか左にあるか、
     * @param    v
     * @return
     */
    public function sign(v:Vector2D):int {
        return this.perp.dotProduct(v) < 0 ? -1 : 1;
    }
    
    /**
     * 直交するベクトル
     */
    public function get perp():Vector2D {
        return new Vector2D( -y, x);
    }
    
    public function distance(v:Vector2D):Number {
        return Math.sqrt(distanceSQ(v));
    }
    public function distanceSQ(v:Vector2D):Number {
        var dx:Number = v._x - _x;
        var dy:Number = v._y - _y;
        return dx * dx + dy * dy;
    }
    
    public function add(v:Vector2D):Vector2D {
        return new Vector2D(_x + v._x, _y + v._y);
    }
    public function subtract(v:Vector2D):Vector2D {
        return new Vector2D(_x - v._x, _y - v._y);
    }
    
    public function multiply(value:Number):Vector2D {
        return new Vector2D(_x * value, _y * value);
    }
    
    public function divide(value:Number):Vector2D{
        if (value == 0) {}//後で考える
        return new Vector2D(_x / value, _y / value);
    }
    
    public function equals(v:Vector2D):Boolean {
        return _x == v._x && _y == v._y;
    }
    
    public function toString():String {
        return "[Vector2D( x:" + _x + ", y:" + _y + ", )]";
    }
}

class Vehicle  {
    protected var _mass:Number = 1.0;
    protected var _maxSpeed:Number = 10;
    protected var _position:Vector2D;
    protected var _velocity:Vector2D;
    
    private var _edgeBehavior:Function;
    
    public static const WRAP:String = "wrap";
    public static const BOUNCE:String = "bounce";
    
    private var _maxForce:Number = 1;
    private var _steeringForce:Vector2D;
    private var _arrivalThreshold:Number = 100;
    private var _wanderAngle:Number = 0;
    private var _wanderDistance:Number = 3;
    private var _wanderRadius:Number = 10;
    private var _wanderRange:Number = 3;

    private var _avoidDistance:Number = 100;
    private var _avoidBuffer:Number = 10;

    private var _pathIndex:int = 0;
    private var _pathThreshold:Number = 20;
    private var _tooCloseDist:Number = 30;
    private var _inSightDist:Number = 200;
    private var sprite:Sprite;
    public function get x():Number { return sprite.x; }
    public function get y():Number { return sprite.y; }
    public function Vehicle(sprite:Sprite=null) {
        this.sprite = sprite;
        _steeringForce = new Vector2D();

        _position = new Vector2D();
        _velocity = new Vector2D();
        _edgeBehavior = wrap;
        draw();
        
    }
    
    protected function draw():void
    {
        if (!sprite) 
        {
            return;
        }
        var g:Graphics = sprite.graphics;
        g.clear();
        g.lineStyle(0);
        g.moveTo(10, 0);
        g.lineTo( -10, 5);
        g.lineTo( -10, -5);
        g.lineTo(10, 0);
    }
    public function update():void {
        _steeringForce.truncate(maxForce);
        _steeringForce = _steeringForce.divide(_mass);
        _velocity = _velocity.add(_steeringForce);
        
        _velocity.truncate(_maxSpeed);
        
        _steeringForce.x = _steeringForce.y = 0;
        _position = _position.add(_velocity);
        
        _edgeBehavior();
        x = position.x;
        y = position.y;
        
        sprite.rotation = _velocity.angle * 180 / Math.PI;
    }
    /**
     * 跳ね返る
     */
    private function bounce():void {
        if (sprite.stage != null) {
            var w:Number = sprite.stage.stageWidth;
            var h:Number = sprite.stage.stageHeight;
            
            if (position.x > w) {
                position.x = w;
                _velocity.x *= -1;
            }else if(position.x < 0){
                position.x = 0;
                _velocity.x *= -1;
            }
            
            if (position.y > h) {
                position.y = h;
                _velocity.y *= -1;
            }else if (position.y < 0) {
                position.y = 0
                _velocity.y *= -1;
            }
        }
    }
    /**
     * 反対側に移動する。
     */
    private function wrap():void {
        if (sprite.stage != null) {
            var w:Number = sprite.stage.stageWidth;
            var h:Number = sprite.stage.stageHeight;
            
            if (position.x > w) position.x = 0;
            if (position.x < 0) position.x = w;
            if ( position.y > h) position.y = 0;
            if ( position.y < 0) position.y = h;
            
        }
    }
    public function get edgeBehavior():String{
        if ( _edgeBehavior == bounce) return Vehicle.BOUNCE;
        if (_edgeBehavior == wrap) return Vehicle.WRAP;
        return "";
    }
    public function set edgeBehavior(value:String):void{
        switch(value) {
        case Vehicle.BOUNCE:
            _edgeBehavior = bounce;
            break;
        case Vehicle.WRAP:
        default:
            _edgeBehavior = wrap;
            break;
        }        
    }
    public function get mass():Number { return _mass; }
    public function set mass(value:Number):void 
    {
        _mass = value;
    }
    
    public function get maxSpeed():Number { return _maxSpeed; }
    public function set maxSpeed(value:Number):void 
    {
        _maxSpeed = value;
    }
    
    public function get position():Vector2D { return _position; }
    public function set position(value:Vector2D):void 
    {
        _position = value;
        x = _position.x;
        y = _position.y;
    }
    
    public function get velocity():Vector2D { return _velocity; }
    public function set velocity(value:Vector2D):void 
    {
        _velocity = value;
    }
    
    public function set x(value:Number):void 
    {
        sprite.x = value;
        _position.x = value;
    }
    
    public function set y(value:Number):void 
    {
        sprite.y = value;
        _position.y = value;
    }
    
    public function get maxForce():Number { return _maxForce; }
    
    public function set maxForce(value:Number):void 
    {
        _maxForce = value;
    }
    
    public function get pathIndex():int { return _pathIndex; }
    
    public function set pathIndex(value:int):void 
    {
        _pathIndex = value;
    }
    
    public function get pathThreshold():Number { return _pathThreshold; }
    
    public function set pathThreshold(value:Number):void 
    {
        _pathThreshold = value;
    }
    
    public function get inSightDist():Number { return _inSightDist; }
    public function set inSightDist(value:Number):void 
    {
        _inSightDist = value;
    }
    
    public function get tooCloseDist():Number { return _tooCloseDist; }
    public function set tooCloseDist(value:Number):void 
    {
        _tooCloseDist = value;
    }
    
    /**
     * Seek behavior
     * @param    target
     */
    public function seek(target:Vector2D):void {
        var desiredVelocity:Vector2D = target.subtract(_position);
        desiredVelocity.normalize();
        desiredVelocity = desiredVelocity.multiply(_maxSpeed);
        var force:Vector2D = desiredVelocity.subtract(_velocity);
        
        _steeringForce = _steeringForce.add(force);
    }
    
    public function flee(target:Vector2D):void {
        var desiredVelocity:Vector2D = target.subtract(_position);
        desiredVelocity.normalize();
        desiredVelocity = desiredVelocity.multiply(_maxSpeed);
        var force:Vector2D = desiredVelocity.subtract(_velocity);
        
        _steeringForce = _steeringForce.subtract(force);        
    }
    public function arrive(target:Vector2D):void {
        var desiredVelocity:Vector2D = target.subtract(_position);
        desiredVelocity.normalize();
        
        
        var dist:Number = _position.distance(target);
        if (dist > _arrivalThreshold) {
            desiredVelocity = desiredVelocity.multiply(_maxSpeed);
        }else {
            //_arrivalThresholdまで近づいたらスピードダウン
            desiredVelocity = desiredVelocity.multiply(_maxSpeed * dist / _arrivalThreshold);
        }
        var force:Vector2D = desiredVelocity.subtract(_velocity);
        _steeringForce = _steeringForce.add(force);
    }
    
    public function pursue(target:Vehicle):void {
        //現在位置からターゲットまでかかる時間
        var lookAheadTime:Number = position.distance(target.position) / _maxSpeed;
        
        var predictedTarget:Vector2D = target.position.add(target.velocity.multiply(lookAheadTime));
        seek(predictedTarget);
    }
    
    public function evade(target:Vehicle):void {
        //現在位置からターゲットまでかかる時間
        var lookAheadTime:Number = position.distance(target.position) / _maxSpeed;
        
        var predictedTarget:Vector2D = target.position.subtract(target.velocity.multiply(lookAheadTime));
        flee(predictedTarget);
    }
    public function wander():void {
        //進行方向から_wanderDistance進んだ位置をcenter
        var center:Vector2D = velocity.clone().normalize().multiply(_wanderDistance);
        
        //centerからのずれ
        var offset:Vector2D = new Vector2D(0,0);
        offset.length = _wanderRadius;
        offset.angle = _wanderAngle;
        _wanderAngle += Math.random() * _wanderRange - _wanderRange * 0.5;
        
        var force:Vector2D = center.add(offset);
        _steeringForce = _steeringForce.add(force);
    }
    
    public function avoid(circles:Array):void {
        var i:int;
        var len:int = circles.length;
        var c:Circle;
        var heading:Vector2D;
        var feeler:Vector2D;
        var projection:Vector2D;
        var dist:Number;
        var force:Vector2D;
        
        for (i = 0; i < len; i++) {
            heading = _velocity.clone().normalize();
            c = circles[i] as Circle;
            
            var difference:Vector2D = c.position.subtract(_position);
            var dotProd:Number = difference.dotProduct(heading);
            
            //進行方向前方にCircleがあるか?
            if (dotProd > 0) {
                feeler = heading.multiply(_avoidDistance);
                projection = heading.multiply(dotProd);
                dist = projection.subtract(difference).length;
                
                //衝突するか?
                if (dist < c.radius + _avoidBuffer && projection.length < feeler.length) {
                    force = heading.multiply(_maxSpeed);
                    force.angle += difference.sign(_velocity) * Math.PI / 2;
                    
                    force = force.multiply(1.0 - projection.length / feeler.length);
                    
                    _steeringForce  = _steeringForce.add(force);
                    
                    //スピードを落とす
                    _velocity = _velocity.multiply(projection.length / feeler.length);
                }
                
            }
        }
    }
    public function followPath(path:Array, loop:Boolean = false):void {
        var wayPoint:Vector2D = path[_pathIndex] as Vector2D;
        if (wayPoint == null) return;
        
        if (_position.distance(wayPoint) < _pathThreshold) {
            if (_pathIndex >= path.length - 1) {
                if (loop) {
                    _pathIndex = 0;
                }
            }else {
                _pathIndex++;
            }
        }
        if (_pathIndex >= path.length - 1 && !loop) {
            arrive(wayPoint);
        }else {
            seek(wayPoint);
        }
    }
    
    /**
     * flocking
     * @param    vehicles
     */
    public function flock(vehicles:Array):void {
        var averageVelocity:Vector2D = _velocity.clone();
        var averagePosition:Vector2D = new Vector2D();
        var inSightCount:int = 0;
        
        var len:int = vehicles.length;
        var v:Vehicle;
        
        /*
         * inSightの範囲にある他のVehicleの平均位置と速度を求める。
         * 近づきすぎている場合は、fleeする。
         */
        for (var i:int = 0; i < len; i++) {
            v = vehicles[i] as Vehicle;
            if (v != this && inSight(v)) {
                averageVelocity = averageVelocity.add(v.velocity);
                averagePosition = averagePosition.add(v.position);
                
                if (tooClose(v)) {
                    flee(v.position);
                }
                
                inSightCount++;
            }
        }
        if (inSightCount > 0) {
            averagePosition = averagePosition.divide(inSightCount);
            averageVelocity = averageVelocity.divide(inSightCount);
            
            //求めた平均位置に向かって移動する。
            seek(averagePosition);
            
            //速度を求めた平均速度へと変化させる方向に力を加える
            _steeringForce.add(averageVelocity.subtract(_velocity));
        }
    }
    public function tooClose(vehicle:Vehicle):Boolean {
        return _position.distance(vehicle.position) < _tooCloseDist;
    }
    public function inSight(vehicle:Vehicle):Boolean {
        if (_position.distance(vehicle.position) > _inSightDist) {
            return false;
        }
        var heading:Vector2D = _velocity.clone().normalize();
        var difference:Vector2D = vehicle.position.subtract(_position);
        var dotProd:Number = difference.dotProduct(heading);
        if (dotProd < 0) return false;
        return true;
    }
    
}
class Circle extends Sprite {
    private var _radius:Number;
    private var _color:uint;
    public function Circle(radius:Number, color:uint = 0x000000) {
        _radius = radius;
        _color = color;
        graphics.lineStyle(0, _color);
        graphics.drawCircle(0, 0, _radius);
    }
    
    public function get radius():Number { return _radius; }
    public function set radius(value:Number):void 
    {
        _radius = value;
    }
    public function get position():Vector2D {
        return new Vector2D(x, y);
    }
}