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

Lorenz Attractor

/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/mAN4
 */

package {
    import caurina.transitions.Tweener;
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
    /**
     * Lorenz Attractor
     * @see http://aquioux.net/blog/?p=2064
     * @author YOSHIDA, Akio(Aquioux)
     */
    public class Main extends Sprite {
        
        private var viewer_:Viewer;        // ビューア
        private var buttons_:Buttons;    // ボタン
        private var sliders_:Sliders;    // スライダー

        private var mouseStage_:Sprite;    // マウスドラッグで動かすため
        
        // マウスをダウンしているか否か
        private var isMouseDown_:Boolean = false;
        
        // MOUSW_MOVE による移動量
        private var moveX_:Number = 0;
        private var moveY_:Number = 0;
        // 前回の MOUSW_MOVE 時のマウス座標
        private var prevMouseX_:Number = 0;
        private var prevMouseY_:Number = 0;
        // 摩擦係数
        private var friction:Number = 0.98;
        
        
        public function Main():void {
            setup();
            addEventListener(Event.ENTER_FRAME, update);
            viewer_.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            viewer_.addEventListener(MouseEvent.MOUSE_UP,   mouseUpHandler);
            viewer_.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
        }
        
        // セットアップ
        private function setup():void {
            // ステージサイズ
            var w:int = stage.stageWidth;
            var h:int = stage.stageHeight;
            
            // エンジンの初期化
            Engine.setup();
            // プロジェクションクラス初期化
            Projection.setup(Engine.data);
            
            // ビューアの作成
            viewer_ = new Viewer(w, h);
            viewer_.buttonMode = true;
            addChild(viewer_);
            
            // ボタンの作成
            buttons_ = new Buttons();
            buttons_.action = drawByButton;
            buttons_.y = h - buttons_.height;
            addChild(buttons_);
            
            // スライダーの作成
            sliders_ = new Sliders();
            sliders_.action = drawBySlider;
            sliders_.reset();
            addChild(sliders_);
        }
        
        // アップデート
        private function update(e:Event):void {
            if (isMouseDown_) {
                moveX_ = mouseX - prevMouseX_;
                moveY_ = mouseY - prevMouseY_;
                prevMouseX_ = mouseX;
                prevMouseY_ = mouseY;
            } else {
                moveX_ *= friction;
                moveY_ *= friction;
            }
            viewer_.update(Projection.update(moveX_, moveY_));
        }

        // マウスハンドラ
        private function mouseDownHandler(e:MouseEvent):void {
            isMouseDown_ = true;
        }
        private function mouseUpHandler(e:MouseEvent):void {
            isMouseDown_ = false;
        }
        private function mouseMoveHandler(e:MouseEvent):void {
            if (!isMouseDown_) {
                prevMouseX_ = mouseX;
                prevMouseY_ = mouseY;
            }
        }
        
        // ボタンに起因する描画の実行
        // エンジンの変数の値をトゥイーン
        private function drawByButton(values:Vector.<Number>):void {
            Tweener.addTween(Engine, {
                "p":values[0],
                "r":values[1],
                "b":values[2],
                time:1.5,
                transition:"easeOutCubic",
                onStart:viewer_.reset,
                onUpdate:sliders_.reset
            });
        }

        // スライダーに起因する描画の実行
        private function drawBySlider():void {
            buttons_.reset();
        }
    }
}


//package {
    /**
     * エンジン(Lorenz Attractor)
     * @author YOSHIDA, Akio(Aquioux)
     */
    /*public*/ class Engine {
        /**
         * 計算に使用するパラメータ p
         */
        static public function get p():Number { return _p; }
        static public function set p(value:Number):void {
            _p = value;
            update();    // 再計算
        }
        static private var _p:Number;
        /**
         * 計算に使用するパラメータ r
         */
        static public function get r():Number { return _r; }
        static public function set r(value:Number):void {
            _r = value;
            update();    // 再計算
        }
        static private var _r:Number;
        /**
         * 計算に使用するパラメータ b
         */
        static public function get b():Number { return _b; }
        static public function set b(value:Number):void {
            _b = value;
            update();    // 再計算
        }
        static private var _b:Number;
        
        /**
         * 計算に使用する各パラメータの最小値、最大値、既定値
         */
        static public function get P_MIN():Number      { return PARAMS[0]; }
        static public function get P_MAX():Number      { return PARAMS[1]; }
        static public function get P_DEFAULT():Number { return PARAMS[2]; }
        static public function get R_MIN():Number      { return PARAMS[3]; }
        static public function get R_MAX():Number      { return PARAMS[4]; }
        static public function get R_DEFAULT():Number { return PARAMS[5]; }
        static public function get B_MIN():Number      { return PARAMS[6]; }
        static public function get B_MAX():Number      { return PARAMS[7]; }
        static public function get B_DEFAULT():Number { return PARAMS[8]; }
        static private const PARAMS:Vector.<Number> = Vector.<Number>([
            0.0, 100, 10,    // p
            0.0, 100, 28,    // r
            0.0, 10, 8 / 3    // b
        ]);

        static private const DT:Number = 0.005;
        
        /**
         * 座標データ Vector
         */ 
        static public function get data():Vector.<Number> { return _data; }
        static private var _data:Vector.<Number>;
        
        /**
         * パーティクル数
         */
        static public function get numOfParticle():int { return numOfParticle_; }
        static private var numOfParticle_:int = 15000;
        
        // 漸化式用変数
        static private var x_:Number;        // X座標値
        static private var y_:Number;        // Y座標値
        static private var z_:Number;        // Y座標値
        
        // スケール
        static private const SCALE:Number = 0.05 / DT;
        
        
        /**
         * セットアップ
         */
        static public function setup():void {
            _data = new Vector.<Number>(numOfParticle_ * 3, true);
            paramDefault();
        }
        
        /**
         * 計算
         * @return    結果の座標を一次元配列で格納した Vector
         */
        static public function update():void {
            x_ = Math.random() * 20 - 10;
            y_ = Math.random() * 20 - 10;
            z_ = Math.random() * 20 - 10;
            var len:int = numOfParticle_ * 3;
            for (var i:int = 0; i < len; i += 3) {
                var xn:Number = (_p * (y_ - x_)) * DT;
                var yn:Number = (x_ * (_r - z_) - y_) * DT;
                var zn:Number = (x_ * y_ - _b * z_) * DT;
                x_ += xn;
                y_ += yn;
                z_ += zn;
                _data[i]     = x_ * SCALE;
                _data[i + 1] = y_ * SCALE;
                _data[i + 2] = z_ * SCALE;
            }
        }
        
        /**
         * パラメータを既定値に戻す
         */
        static public function paramDefault():void {
            _p = P_DEFAULT;
            _r = R_DEFAULT;
            _b = B_DEFAULT;
            update();    // 再計算
        }
        
        /**
         * パラメータをランダムな値にする
         */
        static public function paramRandom():void {
            _p = Math.random() * (P_MAX - P_MIN) + P_MIN;
            _r = Math.random() * (R_MAX - R_MIN) + R_MIN;
            _b = Math.random() * (B_MAX - B_MIN) + B_MIN;
            update();    // 再計算
        }
    }
//}


//package  {
    import flash.geom.Matrix3D;
    import flash.geom.PerspectiveProjection;
    import flash.geom.Utils3D;
    import flash.geom.Vector3D;
    /**
     * 三次元座標を二次元座標に投射する
     * @author YOSHIDA, Akio(Aquioux)
     */
    /*public*/ class Projection {
        /**
         * X座標オフセット値
         */
        static public function get offsetX():Number { return _offsetX; }
        static public function set offsetX(value:Number):void { _offsetX = value; }
        static private var _offsetX:Number = 0;
        /**
         * Y座標オフセット値
         */
        static public function get offsetY():Number { return _offsetY; }
        static public function set offsetY(value:Number):void { _offsetY = value; }
        static private var _offsetY:Number = 0;
        /**
         * Z座標オフセット値
         */
        static public function get offsetZ():Number { return _offsetZ; }
        static public function set offsetZ(value:Number):void { _offsetZ = value; }
        static private var _offsetZ:Number = 500;
        
        // 座標 Vecotr
        static private var verts_:Vector.<Number>;            // 三次元座標
        static private var projectedVerts_:Vector.<Number>;    // 二次元投射後
        static private var uvts_:Vector.<Number>;            // uvts
        // パースペクティブ・プロジェクション
        static private var projection_:PerspectiveProjection;
        static private var projectionMatrix3D_:Matrix3D;
        // 回転計算用マトリクス
        static private var matrix_:Matrix3D;
        
        // 移動量保持
        static private var vx_:Number = 0;
        static private var vy_:Number = 0;


        /**
         * セットアップ
         * @param    data    三次元座標データ
         */
        static public function setup(verts:Vector.<Number>):void {
            // 座標 Vecotr
            verts_ = verts;
            var n:uint = verts_.length;
            projectedVerts_ = new Vector.<Number>(n * 2 / 3, true);
            uvts_           = new Vector.<Number>(n, true);    // 使っていない
            
            // パースペクティブ・プロジェクション
            projection_ = new PerspectiveProjection();
            projectionMatrix3D_ = projection_.toMatrix3D();
            
            // 回転計算用マトリクス
            matrix_ = new Matrix3D();
        }


        /**
         * アップデート
         * @param    moveX    移動量(X軸方向)
         * @param    moveY    移動量(Y軸方向)
         * @return    三次元座標を投射した二次元座標データ
         */
        static public function update(moveX:Number, moveY:Number):Vector.<Number> {
            // 外部からの移動量 moveX、moveY を内部の移動量変数 vx_、vy_ に加算
            vx_ += moveX;
            vy_ -= moveY;
            
            // マトリクス計算
            matrix_.identity();
            matrix_.appendRotation(vy_, Vector3D.X_AXIS);
            matrix_.appendRotation(vx_, Vector3D.Y_AXIS);
            matrix_.appendTranslation(_offsetX, _offsetY, _offsetZ);
            matrix_.append(projectionMatrix3D_);

            // 座標データに回転を適用
            Utils3D.projectVectors(matrix_, verts_, projectedVerts_, uvts_);
            
            return projectedVerts_;
        }
    }
//}


//package {
    //import aquioux.display.colorUtil.CycleRGB;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.filters.BitmapFilterQuality;
    import flash.filters.BlurFilter;
    import flash.geom.ColorTransform;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    /**
     * ビューア
     * @author YOSHIDA, Akio(Aquioux)
     */
    /*public*/ class Viewer extends Sprite {
        /**
         * ColorTransform によるフェードアウトのための定義
         */
        public function set fade(value:ColorTransform):void { _fade = value; }
        private var _fade:ColorTransform = new ColorTransform(0.95, 0.95, 0.95);
        
        /**
         * Blur filter によるフェードアウトのための定義
         */
        public function set blur(value:BlurFilter):void { _blur = value; }
        private var _blur:BlurFilter = new BlurFilter(8, 8, BitmapFilterQuality.HIGH);
        
        // 描画色用の変数
        private var start_:Number;                // 開始位置
        private var add_:Number;                // start_ への増分
        private var renge_:Number;                // 循環色相の範囲(角度)
        
        // BitmapData 関連
        private var bmd_:BitmapData;            // 表示 BitmapData
        private var bufferBmd_:BitmapData;        // バッファ
        private var rect_:Rectangle;            // ColorTransform, Blur 共用
        private const ZERO_POINT:Point = new Point(0, 0);
        
        // 表示オフセット
        private var sw_:int;                    // ステージ幅
        private var sh_:int;                    // ステージ高
        private var offsetX_:Number;            // X座標オフセット
        private var offsetY_:Number;            // Y座標オフセット
        
        
        /**
         * コンストラクタ
         * @param    sw    ステージ幅
         * @param    sh    ステージ高
         */
        public function Viewer(sw:int, sh:int) {
            // ステージサイズ
            sw_ = sw;
            sh_ = sh;

            // BitmapData 関連
            bufferBmd_ = new BitmapData(sw, sh, true, 0xFF000000);
            bmd_  = bufferBmd_.clone();
            rect_ = new Rectangle(0, 0, sw, sh);
            addChild(new Bitmap(bmd_));
            
            // 各オフセット
            offsetX_ = sw / 2;
            offsetY_ = sh / 2;
            
            // 描画色関連
            CycleRGB.alpha = 0xCC;
            start_ = Math.random() * 360 >> 0;
            reset();
        }
        
        /**
         * アップデート
         * @param    data    描画座標データ
         */
        public function update(data:Vector.<Number>):void {
            // bufferBmd_ の更新
            bufferBmd_.lock();
            bufferBmd_.fillRect(bufferBmd_.rect, 0x00000000);
            var len:uint = data.length;
            start_ += add_;
            for (var i:int = 0; i < len; i += 2) {
                var px:Number = (data[i]     + offsetX_) >> 0;
                var py:Number = (data[i + 1] + offsetY_) >> 0;
                var offsetColorX:Number = (px > offsetX_) ? px - sw_ : px;
                var offsetColorY:Number = (py > offsetY_) ? py - sh_ : py;
                var offsetColor:Number = offsetColorX * offsetColorY / renge_;
                if (offsetColor < 0) offsetColor = -offsetColor;
                bufferBmd_.setPixel32(px, py, CycleRGB.getColor32(offsetColor + start_));
            }
            bufferBmd_.unlock();
            
            // bmd_ の更新
            bmd_.lock();
            bmd_.colorTransform(rect_, _fade);
            bmd_.applyFilter(bmd_, rect_, ZERO_POINT, _blur);
            bmd_.draw(bufferBmd_);
            bmd_.unlock();
        }
        
        /**
         * 描画色用変数の再設定
         */
        public function reset():void {
            add_   = ((Math.random() * 50 >> 0) + 50) / 100;
            renge_ = ((Math.random() * 150 >> 0) + 120);
        }
    }
//}


//package {
    import com.bit101.components.HUISlider;
    import com.bit101.components.Slider;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.utils.Dictionary;
    /**
     * コントロール用スライダー
     * @author YOSHIDA, Akio(Aquioux)
     */
    /*public*/ class Sliders extends Sprite {
        /**
         * スライダーアクション(外部で定義した処理)
         */
        public function set action(value:Function):void { _action = value; }
        private var _action:Function;
        
        // スライダーの値を小数第何位まで有効にするか
        private const PRECISION:int = 3;
        private const TICK:Number = 1 / Math.pow(10, PRECISION);
        
        // スライダーに対応させるエンジン・プロパティ
        private const PROPS:Array = ["p", "r", "b"];
        private const DELIMITER:String = ":";
        
        // スライダー格納配列
        private var sliders:Dictionary;
        

        /**
         * コンストラクタ
         */
        public function Sliders() {
            var params:Vector.<Number> = Vector.<Number>([
                Engine.P_MIN, Engine.P_MAX, Engine.p,
                Engine.R_MIN, Engine.R_MAX, Engine.r,
                Engine.B_MIN, Engine.B_MAX, Engine.b
            ]);
            
            sliders = new Dictionary();
            for (var i:int = 0; i < PROPS.length; i++) {
                var prop:String = PROPS[i];
                var slider:HUISlider = new HUISlider(this, 25, i * 15, prop + DELIMITER, handler);
                slider.width = 430;
                slider.labelPrecision = PRECISION;
                slider.tick = TICK;
                slider.setSliderParams(params[i * 3], params[i * 3 + 1], params[i * 3 + 2]);
                sliders[prop] = slider;
            }
        }
        
        /**
         * リセット(Engine 内の数値に伴ったスライダー値にリセット)
         */
        public function reset():void {
            for each (var prop:String in PROPS) sliders[prop].value = Engine[prop];
        }
        
        // ハンドラ
        private function handler(e:Event):void {
            var target:HUISlider = HUISlider(e.target);
            var prop:String = target.label.split(DELIMITER)[0];
            
            Engine[prop] = target.value;
            _action();
        }
    }
//}


//package {
    import com.bit101.components.PushButton;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.utils.Dictionary;
    /**
     * コントロール用ボタン
     * @author YOSHIDA, Akio(Aquioux)
     */
    /*public*/ class Buttons extends Sprite {
        /**
         * ボタンアクション(外部で定義した処理)
         */
        public function set action(value:Function):void { _action = value; }
        private var _action:Function;
        
        // エンジンのラベルとプロパティ値のペア
        private const PAIR:Array = [
            ["Default",    Vector.<Number>([10, 28, 8 / 3])],
            ["No.1",    Vector.<Number>([7.4, 28, 5])],
            ["No.2",    Vector.<Number>([4.6, 50, 4.555])],
            ["No.3",    Vector.<Number>([6, 27, 5])],
            ["No.4",    Vector.<Number>([7.5, 28, 4.8])],
            ["No.5",    Vector.<Number>([4.195, 50, 0])],
            ["No.6",    Vector.<Number>([0.345, 75, 0])],
            ["No.7",    Vector.<Number>([80, 38.5, 1.5])],
            ["No.8",    Vector.<Number>([0.575, 100, 0.215])]
        ];
        private var values:Dictionary;

        // 前回押したボタン
        private var prevButton_:PushButton;
        
        /**
         * インストラクタ
         */
        public function Buttons() {
            // ボタンの作成
            var buttonWidth:int =  52;
            var buttonHeight:int = 20;
            var idx:int = 0;

            values = new Dictionary();
            for (var x:int = 0; x < PAIR.length; x++) {
                var b:PushButton = new PushButton(this, buttonWidth * x, buttonHeight * y, PAIR[idx][0], handler);
                b.width  = buttonWidth;
                b.height = buttonHeight;
                values[b] = PAIR[idx][1];
                idx++;
            }
        }
        
        /**
         * リセット(前回押したため無効になっているボタンを有効にする)
         */
        public function reset():void {
            if (prevButton_) prevButton_.enabled = true;
        }

        // ハンドラ
        private function handler(e:Event):void {
            var target:PushButton = PushButton(e.target);
            handlerAction(target);
            _action(values[target]);
        }

        /**
         * ボタンアクション(内部で完了する処理)
         * 押されたボタンを押せなくし、前回押したボタンを押せるようにする
         * @param    target    押されたボタン
         * @private
         */
        private function handlerAction(target:PushButton):void {
            reset();
            target.enabled = false;
            prevButton_ = target;
        }
    }
//}


//package aquioux.display.colorUtil {
    /**
     * コサインカーブで色相環的な RGB を計算
     * @author Aquioux(YOSHIDA, Akio)
     */
    /*public*/ class CycleRGB {
        /**
         * 32bit カラーのためのアルファ値(0~255)
         */
        static public function get alpha():uint { return _alpha; }
        static public function set alpha(value:uint):void {
            _alpha = (value > 0xFF) ? 0xFF : value;
        }
        private static var _alpha:uint = 0xFF;
    
        private static const PI:Number = Math.PI;        // 円周率
        private static const DEGREE120:Number  = PI * 2 / 3;    // 120度(弧度法形式)
        
        /**
         * 角度に応じた RGB を得る
         * @param    angle    HSV のように角度(度数法)を指定
         * @return    色(0xNNNNNN)
         */
        public static function getColor(angle:Number):uint {
            var radian:Number = angle * PI / 180;
            var r:uint = (Math.cos(radian)             + 1) * 0xFF >> 1;
            var g:uint = (Math.cos(radian + DEGREE120) + 1) * 0xFF >> 1;
            var b:uint = (Math.cos(radian - DEGREE120) + 1) * 0xFF >> 1;
            return r << 16 | g << 8 | b;
        }
        
        /**
         * 角度に応じた RGB を得る(32bit カラー)
         * @param    angle    HSV のように角度(度数法)を指定
         * @return    色(0xNNNNNNNN)
         */
        public static function getColor32(angle:Number):uint {
            return _alpha << 24 | getColor(angle);
        }
    }
//}