Beautifl - Flash Gallery

Thumbnail : ブッダブロ Buddhabrot
ブッダブロ Buddhabrot
Aquioux 2011-02-25 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.filters.BitmapFilterQuality;
    import flash.filters.BlurFilter;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    [SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
    /**
     * ブッダブロの描画
     * @see    jp: http://ja.wikipedia.org/wiki/%E3%83%96%E3%83%83%E3%83%80%E3%83%96%E3%83%AD
     * @see    en: http://en.wikipedia.org/wiki/Buddhabrot
     * @see    http://aquioux.net/blog/?p=1306
     * @author Aquioux(Yoshida, Akio)
     */
    public class Main extends Sprite {
        // 表示用 BitmapData
        private var bmd_:BitmapData;
        private var bufferBmd_:BitmapData;
        
        // bitmapData 用変数・定数
        private var rect_:Rectangle;
        private const ZERO_POINT:Point = new Point();
        private const BLUR:BlurFilter = new BlurFilter(4, 4, BitmapFilterQuality.HIGH);
        private var matrix_:Matrix = new Matrix();
        
        private var cnt_:int = 3600;        // ブッダブロ描画回数
        
        // ブッダブロを描くための複素数 c
        private var rl_:Number;        // 実数部
        private var im_:Number;        // 虚数部

        // プロット色に関する変数・定数
        private var color_:uint;                // プロット色
        private var angle_:Number = 0.0;        // プロット色を決めるための値
        private const STEP:Number = 180 / cnt_;    // プロット色のインクリメントステップ
        
        private var textField_:TextField;    // cnt_ を表示するテキストフィールド
        
        // ステージサイズ
        private var w:int = stage.stageWidth;
        private var h:int = stage.stageHeight;


        public function Main():void {
            // Viewer のバッファ
            bufferBmd_ = new BitmapData(w, h, true, 0x00000000);
            rect_ = bufferBmd_.rect;
            // Viewer の作成
            bmd_  = new BitmapData(w, h, true, 0xFF000000);
            addChild(new Bitmap(bmd_));
            
            matrix_.rotate(Math.PI / 2);
            matrix_.translate(w, 0);
            
            // Buddhabrot クラスのセットアップ
            Buddhabrot.setup(w, h);
            Buddhabrot.scale = 1.5;
            
            // テキストフィールドの作成
            textField_ = new TextField();
            textField_.autoSize = TextFieldAutoSize.LEFT;
            textField_.defaultTextFormat = new TextFormat("_typewriter", 12, 0xFFFFFF);
            addChild(textField_);
            
            angle_ = Math.random() * 360 >> 0;    // 開始プロット色
            
            addEventListener(Event.ENTER_FRAME, update);
        }
        
        private function update(e:Event):void {
            // プロット色の決定
            color_ = CycleRGB.getColor(angle_);
            color_ = 0x66 << 24 | color_;
            angle_ += STEP;

            // 複素数 c をランダムで選ぶ
            rl_ = Math.random() * 2 - 1.5;
            im_ = Math.random() * 2 - 1;
            draw(rl_, im_, color_);
            // プロットの過程をもう一度
            rl_ = Math.random() * 2 - 1.5;
            im_ = Math.random() * 2 - 1;
            draw(rl_, im_, color_);

            // テキストフィールドに残り回数を表示
            textField_.text = String(cnt_);

            // 終了判定
            if (cnt_-- <= 0) {
                textField_.text = "Complete!";
                removeEventListener(Event.ENTER_FRAME, arguments.callee);
            }
        }
        // 描画
        private function draw(val1:Number, val2:Number, color:uint):void {
            // 複素数 c を設定
            Buddhabrot.rl = val1;
            Buddhabrot.im = val2;
            var data:Vector.<int> = Buddhabrot.update();
            if (!data) return;    // 発散していたら終了

            // BitmapData にブッダブロを描画
            var len:uint = data.length / 2;
            bufferBmd_.lock();
            bufferBmd_.fillRect(rect_, 0x00000000);
            for (var i:int = 0; i < len; i++) {
                var idx1:uint = i * 2;
                var idx2:uint = idx1 + 1;
                var x:int  = data[idx1];
                var y1:int = data[idx2];
                var y2:int = h - data[idx2];
                bufferBmd_.setPixel32(x, y1, color);
                bufferBmd_.setPixel32(x, y2, color);
            }
            bufferBmd_.applyFilter(bufferBmd_, rect_, ZERO_POINT, BLUR);
            bufferBmd_.unlock();
            bmd_.lock();
            bmd_.draw(bufferBmd_, matrix_, null, BlendMode.ADD);
            bmd_.unlock();
        }
    }
}


//package {
    /**
     * ブッダブロ描画クラス
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class Buddhabrot {
        /**
         * 複素数 c の実数部
         */
        static public function set rl(value:Number):void { _rl = value; }
        static private var _rl:Number = 0.0;
        /**
         * 複素数 c の虚数部
         */
        static public function set im(value:Number):void { _im = value; }
        static private var _im:Number = 0.0;
        
        /**
         * 発散評価のループ回数
         */
        static public function set degree(value:int):void { _degree = value; }
        static private var _degree:int = 1000;

        /**
         * スケール値
         */
        static public function set scale(value:Number):void {
            _scale = value;
            calcInterval();
        }
        static private var _scale:Number = 1.0;
        

        static private var width_:int;            // ステージ幅
        static private var height_:int;            // ステージ高
        static private var halfWidth_:Number;    // ステージ幅の半分
        static private var halfHeight_:Number;    // ステージ高の半分

        static private var interval_:Number;    // 座標間隔
        
        
        /**
         * 初期化
         * @param    width    ステージ幅
         * @param    height    ステージ高
         */
        static public function setup(width:int, height:int):void {
            // ステージサイズ
            width_  = width;
            height_ = height;
            halfWidth_  = width_ / 2;
            halfHeight_ = height_ / 2;
            
            // 座標間隔を計算する
            calcInterval();
        }
        
        // 座標間隔を計算する
        static private function calcInterval():void {
            var shorter:int = (width_ < height_) ? width_ : height_;
            interval_ = (_scale * shorter) / 4;    // 定数 4 はスケール 1.0 のとき表示領域が -2 ~ 2 であるため
        }

        /**
         * ブッダブロ描画
         * @return    プロットする2次元座標値を1次元配列で格納した Vector
         */
        static public function update():Vector.<int> {
            // 返り値となる Vector
            var data:Vector.<int> = new Vector.<int>(_degree * 2, true);
            
            // z_n
            var zRl:Number = _rl;    // 実数部
            var zIm:Number = _im;    // 虚数部
            // z_n の各要素の二乗
            var zRlSqr:Number;        // 実数部
            var zImSqr:Number;        // 虚数部
            // z_n+1
            var zRlNxt:Number;        // 実数部
            var zImNxt:Number;        // 虚数部
            
            for (var i:int = 0; i < _degree; i++) {
                // 発散の評価
                zRlSqr = zRl * zRl;
                zImSqr = zIm * zIm;
                if (zRlSqr + zImSqr > 4) break;
                
                // 発散していなかった場合、再評価へ向けて z_n の値を更新する
                zRlNxt = zRlSqr - zImSqr + _rl;
                zImNxt = 2 * zRl * zIm + _im;
                zRl = zRlNxt;
                zIm = zImNxt;
                // z_n をプロット座標にして Vecter に待避
                var idx1:uint = i * 2;
                var idx2:uint = idx1 + 1;
                data[idx1] = ((zRlNxt + 0.5) * interval_ + halfWidth_)  >> 0;
                data[idx2] = (zImNxt * interval_ + halfHeight_) >> 0;
            }
            if (i < _degree) data = null;
            return data;
        }
    }
//}


//package aquioux.display.colorUtil {
    /**
     * コサインカーブで色相環的な RGB を計算
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class CycleRGB {
        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;
        }
    }
//}