forked from: Gumowski-Mira Attractor (A)

by heroboy forked from Gumowski-Mira Attractor (A) (diff: 162)
Gumowski-Mira Attractor Pattern A
@see    http://aquioux.net/blog/?p=1345
@author Aquioux(YOSHIDA, Akio)
♥0 | Line 363 | Modified 2011-03-16 22:31:47 | MIT License
play

ActionScript3 source code

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

// forked from Aquioux's Gumowski-Mira Attractor (A)
// forked from Aquioux's Clifford Attractor (A)
package {
    import caurina.transitions.Tweener;
    import flash.display.Sprite;
    import flash.events.Event;
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
    /**
     * Gumowski-Mira Attractor Pattern A
     * @see    http://aquioux.net/blog/?p=1345
     * @author Aquioux(YOSHIDA, Akio)
     */
    public class Main extends Sprite {
        
        private var viewer:Viewer;        // ビューア
        private var buttons:Buttons;    // ボタン
        private var sliders:Sliders;    // スライダー
        
        
        public function Main():void {
            setup();
            addEventListener(Event.ENTER_FRAME, update);
        }
        
        // セットアップ
        private function setup():void {
            // ステージサイズ
            var w:int = stage.stageWidth;
            var h:int = stage.stageHeight;
            
            // アトラクターエンジン初期化
            Attractor.setup();
            //Attractor.paramRandom();
            
            // ビューアの作成
            viewer = new Viewer();
            viewer.setup(w, h);
            addChild(viewer);
            
            // ボタンの作成
            buttons = new Buttons();
            buttons.setup();
            buttons.action = drawByButton;
            buttons.y = h - 20;
            addChild(buttons);
            
            // スライダーの作成
            sliders = new Sliders();
            sliders.setup();
            sliders.action = drawBySlider;
            sliders.reset();
            addChild(sliders);
        }
        
        // アップデート
        private function update(e:Event):void {
            viewer.update(Attractor.update());
        }
        
        // ボタンに起因する描画の実行
        // アトラクタの変数の値をトゥイーン
        private function drawByButton(values:Vector.<Number>):void {
            Tweener.addTween(Attractor, {
                "m":values[0],
                "a":values[1],
                time:1.5,
                transition:"easeOutCubic",
                onStart:viewer.reset,
                onUpdate:sliders.reset
            });
        }

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


//package {
    /**
     * アトラクターエンジン(Gumowski-Mira Attractor)
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Attractor {
        /**
         * アトラクター計算に使用するパラメータ m
         */ 
        static public function get m():Number { return _m; }
        static public function set m(value:Number):void { _m = value; }
        static private var _m:Number;
        /**
         * アトラクター計算に使用するパラメータ a
         */ 
        static public function get a():Number { return _a; }
        static public function set a(value:Number):void { _a = value; }
        static private var _a:Number;
        
        /**
         * アトラクター計算に使用する各パラメータの最小値、最大値、既定値
         */
        static public function get M_MIN():Number     { return 0; }
        static public function get M_MAX():Number     { return 200; }
        static public function get M_DEFAULT():Number { return 0; }
        static public function get A_MIN():Number     { return 0; }
        static public function get A_MAX():Number     { return 200; }
        static public function get A_DEFAULT():Number { return 10; }
        static private const PARAMS:Vector.<Number> = Vector.<Number>([
            -0.9,    0.9,    -0.9,    // m
             0.001,    0.015,     0.008    // a
        ]);
        
        /**
         * パーティクル数
         */
        static public function get numOfParticle():int { return numOfParticle_; }
        static private var numOfParticle_:int = 30000;
        

        // update() の返り値となる Vector
        static private var data_:Vector.<Number>;
        
        // 漸化式用変数
        static private var x_:Number;        // X座標値
        static private var y_:Number;        // Y座標値
        
        
        /**
         * セットアップ
         */
        static public function setup():void {
            // update() の返り値となる Vector
            data_ = new Vector.<Number>(numOfParticle_ * 2, true);
            // 各パラメータの値を初期化
            paramDefault();
        }
        
        /**
         * パラメータ m, a を既定値に戻す
         */
        static public function paramDefault():void {
            _m = M_DEFAULT;
            _a = A_DEFAULT;
        }
        
        /**
         * パラメータ m, a をランダムな値にする
         */
        static public function paramRandom():void {
            _m = Math.random() * (M_MAX - M_MIN) + M_MIN;
            _a = Math.random() * (A_MAX - A_MIN) + A_MIN;
        }
        
        /**
         * アトラクター計算
         * @return    結果の座標を一次元配列で格納した Vector
         */
        static public function update():Vector.<Number> {
            x_ = Math.random() * 10 - 5;
            y_ = Math.random() * 10 - 5;
            if (Math.random() < 0.5 ) x_ = -x_; 
            if (Math.random() < 0.5 ) y_ = -y_; 
            var len:int = numOfParticle_ * 2;
            var r1:Rndm = new Rndm(m);
            var r2:Rndm = new Rndm(a);
            for (var i:int = 0; i < len; i += 2) {
                data_[i]     = r1.random() - 0.5;
                data_[i + 1] = r2.random() - 0.5;
            }
            return data_;
        }
        static private function f(x:Number):Number {
            var x2:Number = x * x;
            return _m * x + (2 * (1 - _m) * x2) / (1 + x2);
        }
    }
//}


//package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.filters.BitmapFilterQuality;
    import flash.filters.BlurFilter;
    import flash.geom.ColorTransform;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    /**
     * ビューア
     * @author Aquioux(YOSHIDA, Akio)
     */
    /*public*/ class Viewer extends Bitmap {
        /**
         * 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 = 0.0;        // 開始位置
        private var add_:Number   = 0.5;        // start_ への増分
        private var renge_:Number = 150 / 360;    // 循環色相の範囲(角度)
        
        // 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座標オフセット
        private const OFFSET_SCALE:int = 200;        // スケールオフセット
        
        
        /**
         * コンストラクタ
         */ 
        public function Viewer() {
        }
        
        /**
         * セットアップ
         * @param    sw    ステージ幅
         * @param    sh    ステージ高
         */
        public function setup(sw:Number, sh:Number):void {
            // ステージサイズ
            sw_ = sw;
            sh_ = sh;
            // BitmapData 関連
            bufferBmd_ = new BitmapData(sw_, sh_, true, 0xFF000000);
            bitmapData = bufferBmd_.clone();
            rect_ = new Rectangle(0, 0, sw_, sh_);
            // 各オフセット
            offsetX_ = sw_ / 2;
            offsetY_ = sh_ / 2;
            // 描画色関連
            reset();
            // 描画色のアルファ値を設定
            CycleRGB.alpha = 0x99;
        }
        
        /**
         * アップデート
         * @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]     * OFFSET_SCALE + offsetX_) >> 0;
                var py:Number = (data[i + 1] * OFFSET_SCALE + 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();
            
            // bitmapData の更新
            bitmapData.lock();
            bitmapData.colorTransform(rect_, _fade);
            bitmapData.applyFilter(bitmapData, rect_, ZERO_POINT, _blur);
            bitmapData.draw(bufferBmd_);
            bitmapData.unlock();
        }
        
        /**
         * 描画色用変数の再設定
         */
        public function reset():void {
            start_ = (Math.random() * 360) >> 0;
            add_   = ((Math.random() * 50 + 50) >> 0) / 100;
            renge_ = (Math.random() * 90 >> 0) + 90;
        }
    }
//}


//package {
    import com.bit101.components.HUISlider;
    import flash.display.Sprite;
    import flash.events.Event;
    /**
     * コントロール用スライダー
     * @author Aquioux(YOSHIDA, Akio)
     */
    /*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 VALUES:Array = ["m:", "a:"];
        
        // スライダー格納配列
        private var sliders:Array = [];

        
        /**
         * コンストラクタ
         */
        public function Sliders() {
        }
        
        /**
         * セットアップ
         */
        public function setup():void {
            var params:Vector.<Number> = Vector.<Number>([
                Attractor.M_MIN, Attractor.M_MAX, Attractor.m,
                Attractor.A_MIN, Attractor.A_MAX, Attractor.a
            ]);
            
            var idx:int = 0;
            for (var y:int = 0; y < VALUES.length; y++) {
                var slider:HUISlider = new HUISlider(this, 25, y * 15, VALUES[idx], handler);
                slider.width = 430;
                slider.labelPrecision = PRECISION;
                slider.tick = TICK;
                slider.setSliderParams(params[idx * 3], params[idx * 3 + 1], params[idx * 3 + 2]);
                sliders[idx] = slider;
                idx++;
            }
        }
        
        /**
         * リセット(Attractor 内の数値に伴ったスライダー値にリセット)
         */
        public function reset():void {
            sliders[0].value = Attractor.m;
            sliders[1].value = Attractor.a;
        }
        
        // ハンドラ
        private function handler(e:Event):void {
            var target:HUISlider = HUISlider(e.target);
            var label:String = target.label;
            var value:Number = target.value;
            
            if (label == VALUES[0]) Attractor.m = value;
            if (label == VALUES[1]) Attractor.a = value;
            _action();
        }
    }
//}


//package {
    import com.bit101.components.PushButton;
    import flash.display.Sprite;
    import flash.events.Event;
    /**
     * コントロール用ボタン
     * @author Aquioux(YOSHIDA, Akio)
     */
    /*public*/ class Buttons extends Sprite {
        /**
         * ボタンアクション(外部で定義した処理)
         */
        public function set action(value:Function):void { _action = value; }
        private var _action:Function;
        

        // 写像の名称と変数の値
        private const VALUES:Array = [
            ["No.1",    Vector.<Number>([-0.9, 0.015])    ],
            ["No.2",    Vector.<Number>([-0.738, 0.001])    ],
            ["No.3",    Vector.<Number>([-0.6, 0.015])    ],
            ["No.4",    Vector.<Number>([-0.1, 0.001])    ],
            ["No.5",    Vector.<Number>([-0.016, 0.001])    ],
            ["No.6",    Vector.<Number>([0.232, 0.015])    ],
            ["No.7",    Vector.<Number>([0.373, 0.002])    ],
            ["No.8",    Vector.<Number>([0.5, 0.001])    ],
            ["No.9",    Vector.<Number>([0.9, 0.001])    ]
        ];

        // 前回押したボタン
        private var prevButton_:PushButton;
        
        
        /**
         * インストラクタ
         */
        public function Buttons() {
        }
        
        /**
         * セットアップ
         */
        public function setup():void {
            // ボタンの作成
            var buttonWidth:int =  52;
            var buttonHeight:int = 20;
            var idx:int = 0;
            for (var x:int = 0; x < 9; x++) {
                var b:PushButton = new PushButton(this, buttonWidth * x, buttonHeight * y, VALUES[idx][0], handler);
                b.name = String(idx);
                idx++;
                b.width  = buttonWidth;
                b.height = buttonHeight;
            }
        }
        
        /**
         * リセット(前回押したため無効になっているボタンを有効にする)
         */
        public function reset():void {
            if (prevButton_) prevButton_.enabled = true;
        }

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

        /**
         * ボタンアクション(内部で完了する処理)
         * 押されたボタンを押せなくし、前回押したボタンを押せるようにする
         * @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 カラーのためのアルファ値
         */
        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 DEGREE90:Number  = PI / 2;    // 90度(弧度法形式)
        private static const DEGREE180:Number = PI;        // 180度(弧度法形式)
        
        /**
         * 角度に応じた RGB を得る
         * @param    angle    HSV のように角度(度数法)を指定
         * @return    色(0xNNNNNN)
         */
        public static function getColor(angle:Number):uint {
            var radian:Number = angle * PI / 180;
            var valR:uint = (Math.cos(radian)             + 1) * 0xFF >> 1;
            var valG:uint = (Math.cos(radian + DEGREE90)  + 1) * 0xFF >> 1;
            var valB:uint = (Math.cos(radian + DEGREE180) + 1) * 0xFF >> 1;
            return valR << 16 | valG << 8 | valB;
        }
        
        /**
         * 角度に応じた RGB を得る(32bit カラー)
         * @param    angle    HSV のように角度(度数法)を指定
         * @return    色(0xNNNNNNNN)
         */
        public static function getColor32(angle:Number):uint {
            return _alpha << 24 | getColor(angle);
        }
    }
//}

import flash.display.BitmapData;
	
	// Provides common random functions using a seeded random system. Can be used through static interface or via instantiation.
	
	class Rndm {
	// static interface:
		// NOTE: for usage information, look at the instance methods below.
	
		protected static var _instance:Rndm;
		public static function get instance():Rndm {
			if (_instance == null) { _instance = new Rndm(); }
			return _instance;
		}
		
		public static function get seed():uint {
			return instance.seed;
		}
		public static function set seed(value:uint):void {
			instance.seed = value;
		}
		
		public static function get pointer():uint {
			return instance.pointer;
		}
		public static function set pointer(value:uint):void {
			instance.pointer = value;
		}
		
		public static function random():Number {
			return instance.random();
		}
		
		public static function float(min:Number,max:Number=NaN):Number {
			return instance.float(min,max);
		}
		
		public static function boolean(chance:Number=0.5):Boolean {
			return instance.boolean(chance);
		}
		
		public static function sign(chance:Number=0.5):int {
			return instance.sign(chance);
		}
		
		public static function bit(chance:Number=0.5):int {
			return instance.bit(chance);
		}
		
		public static function integer(min:Number,max:Number=NaN):int {
			return instance.integer(min,max);
		}
		
		public static function reset():void {
			instance.reset();
		}
		
		
	// constants:
	// private properties:
		protected var _seed:uint=0;
		protected var _pointer:uint=0;
		protected var bmpd:BitmapData;
		protected var seedInvalid:Boolean=true;
	
	// public properties:
		
	// constructor:
		public function Rndm(seed:uint=0) {
			_seed = seed;
			bmpd = new BitmapData(1000,200);
		}
		
	// public getter/setters:
	
		// seed = Math.random()*0xFFFFFF; // sets a random seed
		// seed = 50; // sets a static seed
		public function get seed():uint {
			return _seed;
		}
		public function set seed(value:uint):void {
			if (value != _seed) { seedInvalid = true; _pointer=0; }
			_seed = value;
		}
		
		// trace(Rndm.pointer); // traces the current position in the number series
		// Rndm.pointer = 50; // moves the pointer to the 50th number in the series
		public function get pointer():uint {
			return _pointer;
		}
		public function set pointer(value:uint):void {
			_pointer = value;
		}
	
	// public methods:
		// random(); // returns a number between 0-1 exclusive.
		public function random():Number {
			if (seedInvalid) {
				bmpd.noise(_seed,0,255,1|2|4|8);
				seedInvalid = false;
			}
			_pointer = (_pointer+1)%200000;
			// Flash's numeric precision appears to run to 0.9999999999999999, but we'll drop one digit to be safe:
			return (bmpd.getPixel32(_pointer%1000,_pointer/1000>>0)*0.999999999999998+0.000000000000001)/0xFFFFFFFF;
		}
		
		// float(50); // returns a number between 0-50 exclusive
		// float(20,50); // returns a number between 20-50 exclusive
		public function float(min:Number,max:Number=NaN):Number {
			if (isNaN(max)) { max = min; min=0; }
			return random()*(max-min)+min;
		}
		
		// boolean(); // returns true or false (50% chance of true)
		// boolean(0.8); // returns true or false (80% chance of true)
		public function boolean(chance:Number=0.5):Boolean {
			return (random() < chance);
		}
		
		// sign(); // returns 1 or -1 (50% chance of 1)
		// sign(0.8); // returns 1 or -1 (80% chance of 1)
		public function sign(chance:Number=0.5):int {
			return (random() < chance) ? 1 : -1;
		}
		
		// bit(); // returns 1 or 0 (50% chance of 1)
		// bit(0.8); // returns 1 or 0 (80% chance of 1)
		public function bit(chance:Number=0.5):int {
			return (random() < chance) ? 1 : 0;
		}
		
		// integer(50); // returns an integer between 0-49 inclusive
		// integer(20,50); // returns an integer between 20-49 inclusive
		public function integer(min:Number,max:Number=NaN):int {
			if (isNaN(max)) { max = min; min=0; }
			// Need to use floor instead of bit shift to work properly with negative values:
			return Math.floor(float(min,max));
		}
		
		// reset(); // resets the number series, retaining the same seed
		public function reset():void {
			_pointer = 0;
		}
		
	// private methods:
	}