Beautifl - Flash Gallery

Thumbnail : Barnsley Fractal J1
Barnsley Fractal J1
Aquioux 2012-02-16 MIT License

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

package {
    //import aquioux.display.colorUtil.CycleRGB;
    import com.bit101.components.HSlider;
    import com.bit101.components.PushButton;
    import com.bit101.components.VSlider;
    import flash.display.BlendMode;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
    /**
     * Barnsley Fractal J1 の描画
     * @see http://aquioux.net/blog/?p=2195
     * @author Aquioux(Yoshida, Akio)
     */
    public class Main extends Sprite {
        // ステージサイズ
        private const WIDTH:int  = 450;
        private const HEIGHT:int = 450;
        
        // 複素数 c 初期値
        private const REAL_NUMBER:Number      = 0.6;    // c の実数部
        private const IMAGINARY_NUMBER:Number = 1.1;    // c の虚数部
        
        // スライダー関連
        private const SLIDER_LONG:int  = WIDTH;
        private const SLIDER_SHORT:int = stage.stageWidth - WIDTH;
        private var sliderValueMin:Number;
        private var sliderValueMax:Number;
        
        // 連携クラス
        private var fractal_:Julia1;                // フラクタル計算クラス
        private var engine_:Engine;                    // フラクタル描画エンジンクラス
        private var mouseBehavior_:MouseBehavior;    // マウス操作クラス
        private var viewer_:Viewer;                    // ビューアクラス
        private var hslider_:HSlider;                // 水平スライダー
        private var vslider_:VSlider;                // 垂直スライダー
        private var textFieldReal_:TextField;        // 水平スライダーの値表示
        private var textFieldImag_:TextField;        // 水平スライダーの値表示
        
        // マウスダウンフラグ
        private var isDown_:Boolean = false;
        

        // コンストラクタ
        public function Main() {
            // フラクタルカラーマップ
            var colorMap:Vector.<uint> = new Vector.<uint>();
            var degree:int = 45;
            var step:Number = 360 / (degree + 1);
            for (var i:int = 0; i < degree; i++) colorMap[i] = CycleRGB.getColor(i * step + 180);
            colorMap.reverse();
            colorMap.fixed = true;

            // フラクタル計算クラス
            fractal_ = new Julia1();
            fractal_.colorMap = colorMap;
            
            // ビューアクラス
            viewer_ = new Viewer(WIDTH, HEIGHT);
            addChild(viewer_);
            
            // マウス操作クラス
            mouseBehavior_ = new MouseBehavior();
            addChild(mouseBehavior_);
            
            // フラクタル描画エンジンクラス
            engine_ = new Engine();
            engine_.viewer     = viewer_;
            engine_.controller = mouseBehavior_;
            engine_.calculator = fractal_;
            engine_.reset();
            
            // リセットボタン
            var button:PushButton = new PushButton(this, 0, 0, "Reset", buttonHandler);
            button.width = 50;
            button.x = WIDTH - button.width;
            
            // スライダー
            sliderValueMin = fractal_.rect.x;
            sliderValueMax = fractal_.rect.width + fractal_.rect.x;
            // 横スライダー(複素数 c の実数部)
            hslider_ = new HSlider(this, 0, HEIGHT);
            hslider_.width  = SLIDER_LONG;
            hslider_.height = SLIDER_SHORT;
            hslider_.setSliderParams(sliderValueMin, sliderValueMax, REAL_NUMBER);
            hslider_.tick = 0.001;
            // 縦スライダー(複素数 c の虚数部)
            vslider_ = new VSlider(this, WIDTH, 0);
            vslider_.width  = SLIDER_SHORT;
            vslider_.height = SLIDER_LONG;
            vslider_.setSliderParams(sliderValueMin, sliderValueMax, IMAGINARY_NUMBER);
            vslider_.tick = 0.001;
            
            // スライダーの値表示
            var textFormat:TextFormat = new TextFormat("_typewriter", 12, 0x0);
            textFieldReal_ = new TextField();
            textFieldReal_.defaultTextFormat = textFormat;
            textFieldReal_.autoSize = TextFieldAutoSize.LEFT;
            textFieldReal_.selectable = false;
            textFieldReal_.blendMode = BlendMode.INVERT;
            addChild(textFieldReal_);
            textFieldImag_ = new TextField();
            textFieldImag_.defaultTextFormat = textFormat;
            textFieldImag_.autoSize = TextFieldAutoSize.LEFT;
            textFieldImag_.selectable = false;
            textFieldImag_.blendMode = BlendMode.INVERT;
            textFieldImag_.y = 15;
            addChild(textFieldImag_);
            
            // マウスハンドラの設定
            // スライダーに直接ハンドラを設定すると ENTER_FRAME のタイミングで更新がかかり、
            // 処理負荷が大きくなるため、マウスアップ時にスライダハンドラを実行させる
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            stage.addEventListener(MouseEvent.MOUSE_UP,   mouseUpHandler);

            // 初回の描画
            fractal_.c.real = hslider_.value;
            fractal_.c.imag = vslider_.value;
            draw();
            textFieldReal_.text = "real : " + String(hslider_.value);
            textFieldImag_.text = "imag : " + String(vslider_.value);
        }

        // 描画
        private function draw():void {
            viewer_.start();
        }

        // マウスハンドラ
        // マウスダウン
        private function mouseDownHandler(e:MouseEvent):void {
            isDown_ = ((mouseX > WIDTH) || (mouseY > HEIGHT)) ? true : false;
        }
        // マウスアップ
        private function mouseUpHandler(e:MouseEvent):void {
            if (isDown_) {
                hsliderHandler();
                vsliderHandler();
                isDown_ = false;
            }
        }
        
        // リセットボタンハンドラ
        private function buttonHandler(e:Event):void {
            hslider_.value = fractal_.c.real = REAL_NUMBER;
            vslider_.value = fractal_.c.imag = IMAGINARY_NUMBER;
            engine_.reset();
            textFieldReal_.text = "real : " + String(hslider_.value);
            textFieldImag_.text = "imag : " + String(vslider_.value);
        }

        // スライダーハンドラ
        // 横スライダー
        private function hsliderHandler():void {
            var value:Number = hslider_.value;
            fractal_.c.real = value;
            draw();
            textFieldReal_.text = "real : " + String(value);
        }
        // 縦スライダー
        private function vsliderHandler():void {
            var value:Number = vslider_.value;
            fractal_.c.imag = value;
            draw();
            textFieldImag_.text = "imag : " + String(value);
        }
    }
}


//package {
    //import aquioux.math.Complex;
    import flash.geom.Rectangle;
    /**
     * Barnsley Fractal J1 描画クラス
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class Julia1 implements ICalculator {
        /**
         * 描画範囲
         */
        public function get rect():Rectangle { return RECTANGLE; }
        private const RECTANGLE:Rectangle = new Rectangle(MIN_X, MIN_Y, (MAX_X - MIN_X), (MAX_Y - MIN_Y));
        // 個別の開始座標、終了座標
        private const MIN_X:Number = -2.0;        // X軸最小値
        private const MIN_Y:Number = -2.0;        // Y軸最小値
        private const MAX_X:Number =  2.0;        // X軸最大値
        private const MAX_Y:Number =  2.0;        // Y軸最大値

        /**
         * 集合に該当する部分の色(一般的には色なし=黒)
         */
        private var _color:uint = 0x000000;
        public function set color(value:uint):void { _color = value; }
        /**
         * 発散部分のカラーマップ
         */
        private var _colorMap:Vector.<uint>;
        public function set colorMap(value:Vector.<uint>):void {
            _colorMap = value;
            degree_ = value.length;
        }
        
        /**
         * 漸化式の c
         */
        public function get c():Complex { return _c; }
        private var _c:Complex = new Complex(0.6, 1.1);
        
        
        // 発散チェックループ回数(_colorMap.length の値)
        private var degree_:int;
        
        
        /**
         * Scan クラスからの走査データを受け、計算をおこなう
         * @param    x    X座標値
         * @param    y    Y座標値
         * @return    計算結果
         */
        public function calculate(x:Number, y:Number):uint {
            var r:int = formula(x, y, _c.real, _c.imag);
            return (r >= 0) ? _colorMap[r] : _color;
        }
        /**
         * 漸化式 z ← c(z - sign(Re[z])) ただし sign(0) = 1
         * @param    zRl    複素数 z の実数部
         * @param    zIm    複素数 z の虚数部
         * @param    cRl    複素数 c の実数部
         * @param    cIm    複素数 c の虚数部
         * @return    発散評価値
         * @private
         */
        private function formula(zRl:Number, zIm:Number, cRl:Number, cIm:Number):int {
            var i:int = degree_;
            while (i--) {
                // 発散の評価
                if (zRl * zRl + zIm * zIm > 4) break;
                
                // 漸化式実行
                zRl += (zRl >= 0) ? -1 : 1;
                var zRlNxt:Number = zRl * cRl - zIm * cIm;
                zIm = zRl * cIm + zIm * cRl;
                zRl = zRlNxt;
            }
            return i;
        }
    }
//}


//package {
    import flash.geom.Rectangle;
    /**
     * フラクタル計算クラスの interface
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ interface ICalculator {
        function get rect():Rectangle;
        function set color(value:uint):void;
        function set colorMap(value:Vector.<uint>):void;
        function calculate(x:Number, y:Number):uint;
    }
//}



//package {
    import flash.events.Event;
    import flash.geom.Rectangle;
    /**
     * フラクタル描画エンジンクラス
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class Engine {
        /**
         * フラクタル計算クラス
         */
        public function set calculator(value:ICalculator):void { _calculator = value; }
        private var _calculator:ICalculator;
        
        /**
         * マウス操作クラス
         */
        public function set controller(value:MouseBehavior):void {
            _controller = value;
            _controller.addEventListener(Event.CHANGE, update);
        }
        private var _controller:MouseBehavior;
        
        /**
         * ビューアクラス
         */
        public function set viewer(value:Viewer):void {
            _viewer = value;
            _viewer.addEventListener(Event.CHANGE, draw);
            displayWidth_  = _viewer.width;
            displayHeight_ = _viewer.height;
        }
        private var _viewer:Viewer;
        
        /**
         * 描画計算領域を示す Rectangle
         */
        public function set rect(value:Rectangle):void {
            // 今後使用するので引数を保持する
            _rect = value;

            // 複素平面の走査開始座標を計算する
            startX_ = _rect.x;
            startY_ = _rect.y;

            // 複素平面の中心座標を計算する
            centerX_ = startX_ + _rect.width  * 0.5;
            centerY_ = startY_ + _rect.height * 0.5;

            // 複素平面の走査加算値を計算する
            stepX_ = _rect.width  / (displayWidth_  - 1);
            stepY_ = _rect.height / (displayHeight_ - 1);
        }
        private var _rect:Rectangle;
        

        // ビューアに描画するデータ
        private var data_:Vector.<uint> = new Vector.<uint>();
        
        // 表示領域サイズ
        private var displayWidth_:int;            // 幅
        private var displayHeight_:int;            // 高

        // 複素平面の走査開始座標
        private var startX_:Number;            // 実数座標
        private var startY_:Number;            // 虚数座標

        // 複素平面の中心座標
        private var centerX_:Number;            // 実数座標
        private var centerY_:Number;            // 虚数座標

        // 複素平面の走査加算値
        private var stepX_:Number;            // 実数軸
        private var stepY_:Number;            // 虚数軸

        
        /**
         * 描画計算領域のリセット、再描画
         */
        public function reset():void {
            rect = _calculator.rect;    // 領域のリセット
            _viewer.start();        // 再描画
        }
        
        /**
         * 描画開始
         * _controller からの通知、および外部からの呼び出しで起動する処理
         */
        public function update(e:Event):void {
            if (e) {
                // MouseBehavior からの rectangle を受ける
                var t:Rectangle = _controller.rect.clone();
                t.x = t.x * stepX_ + _rect.x;
                t.y = t.y * stepY_ + _rect.y;
                t.width  *= stepX_;
                t.height *= stepY_;
                rect = t;    // 内部に保持している _rect を更新
            }
            // _viewer への描画開始指示
            _viewer.start();
        }


        // _viewer の求めに応じて、指定範囲の描画 Vector を渡す
        // _viewer からの通知で起動する処理
        private function draw(e:Event):void {
            var startX:int = _viewer.rect.x;
            var startY:int = _viewer.rect.y;
            var limitX:int = _viewer.rect.width  + startX;
            var limitY:int = _viewer.rect.height + startY;
            var idx:int  = 0;
            data_.fixed  = false;
            data_.length = 0;
            for (var y:int = startY; y < limitY; y++) {
                for (var x:int = startX; x < limitX; x++) {
                    data_[idx++] = _calculator.calculate(x * stepX_ + startX_, y * stepY_ + startY_);
                }
            }
            data_.fixed = true;
            _viewer.draw(data_);
        }
    }
//}


//package  {
    import flash.display.BlendMode;
    import flash.display.Graphics;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    /**
     * 描画領域選択クラス
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class MouseBehavior extends Sprite {
        /**
         * ドラッグで決定された領域を示す Rectangle
         */ 
        public function get rect():Rectangle { return selectedRect_; }
        private var selectedRect_:Rectangle;


        // 選択領域描画用
        private var g_:Graphics;
        
        // 矩形描画、移動に係る変数
        private var dragX_:Number;            // ドラッグ開始X座標
        private var dragY_:Number;            // ドラッグ開始Y座標
        private var dragWidth_:Number;        // ドラッグ幅
        private var dragHeight_:Number;        // ドラッグ高
        private var isCtrlDown_:Boolean = false;    // Shift キーダウン
        

        /**
         * コンストラクタ
         * @param    target    描画エンジン
         */
        public function MouseBehavior() {
            // 初期化
            g_ = this.graphics;
            blendMode = BlendMode.INVERT;

            // Rectangle 生成
            selectedRect_ = new Rectangle();
            
            addEventListener(Event.ADDED_TO_STAGE, addedHandler);
        }
        // イベント登録
        private function addedHandler(e:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE, addedHandler);

            // マウスイベント有効領域を指定する
            var base:Shape = new Shape();
            var g:Graphics = base.graphics;
            g.beginFill(0x0, 0);
            g.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
            g.endFill();
            addChild(base);
            
            // キーボードイベントハンドラ登録
            stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
            stage.addEventListener(KeyboardEvent.KEY_UP,   keyUpHandler);
            
            // マウスイベントハンドラ登録
            addEventListener(MouseEvent.MOUSE_DOWN,  mouseDownHandler);
            addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
        }

        
        // キーボードイベントハンドラ
        // キーダウン
        private function keyDownHandler(e:KeyboardEvent):void {
            if (e.ctrlKey) isCtrlDown_ = true;
        }
        // キーアップ
        private function keyUpHandler(e:KeyboardEvent):void {
            if (isCtrlDown_) isCtrlDown_ = false;
        }

        // マウスイベントハンドラ
        // マウスダウン
        private function mouseDownHandler(e:MouseEvent):void {
            // 起点待避
            dragX_ = mouseX;
            dragY_ = mouseY;

            // マウスイベントハンドラ登録
            if (isCtrlDown_) {
                // 矩形移動
                stage.addEventListener(MouseEvent.MOUSE_MOVE, movingRectangle);
                stage.addEventListener(MouseEvent.MOUSE_UP,   moveupRectangle);
            } else {
                // 矩形描画
                stage.addEventListener(MouseEvent.MOUSE_MOVE, drawingNewRectangle);
                stage.addEventListener(MouseEvent.MOUSE_UP,   drawupNewRectangle);
            }
        }
        
        // マウスムーブ(矩形描画)
        private function drawingNewRectangle(e:MouseEvent):void {
            // 選択領域のサイズ計算
            dragWidth_  = mouseX - dragX_;
            dragHeight_ = mouseY - dragY_;

            // 選択領域を正方形にする
            // 絶対値を求める
            var isMinusWidth:Boolean  = dragWidth_  < 0 ? true : false;
            var isMinusHeight:Boolean = dragHeight_ < 0 ? true : false;
            if (isMinusWidth)  dragWidth_  *= -1;
            if (isMinusHeight) dragHeight_ *= -1;
            // 幅・高のうち大きい方を正方形の辺とする
            var edge:Number = dragWidth_ > dragHeight_ ? dragWidth_ : dragHeight_;
            // 元の値がマイナスだったものはマイナスにする
            dragWidth_  = isMinusWidth  ? -edge : edge;
            dragHeight_ = isMinusHeight ? -edge : edge;

            // 選択領域の表示
            g_.clear();
            g_.lineStyle(0, 0x0);
            g_.drawRect(dragX_, dragY_, dragWidth_, dragHeight_);
        }
        // マウスアップ(矩形描画)
        private function drawupNewRectangle(e:MouseEvent):void {
            // マウスイベントハンドラ解除
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, drawingNewRectangle);
            stage.removeEventListener(MouseEvent.MOUSE_UP,   drawupNewRectangle);
            
            // selectedRect_ 調整
            selectedRect_.x      = dragX_;
            selectedRect_.y      = dragY_;
            selectedRect_.width  = dragWidth_;
            selectedRect_.height = dragHeight_
            // サイズをプラスの値に調整
            if (dragWidth_ < 0) {
                selectedRect_.x += selectedRect_.width;
                selectedRect_.width *= -1;
            }
            if (dragHeight_ < 0) {
                selectedRect_.y += selectedRect_.height;
                selectedRect_.height *= -1;
            }
            
            // 選択領域消去
            g_.clear();

            // イベント送出
            update();
        }

        // マウスムーブ(矩形移動)
        private function movingRectangle(e:MouseEvent):void {
            dragWidth_  = dragX_ - mouseX;
            dragHeight_ = dragY_ - mouseY;
        }
        // マウスアップ(矩形移動)
        private function moveupRectangle(e:MouseEvent):void {
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, movingRectangle);
            stage.removeEventListener(MouseEvent.MOUSE_UP,   moveupRectangle);

            // selectedRect_ 調整
            selectedRect_.x = dragWidth_;
            selectedRect_.y = dragHeight_;
            selectedRect_.width  = stage.stageWidth;
            selectedRect_.height = stage.stageHeight;

            // イベント送出
            update();
        }

        // マウスホイール
        private function mouseWheelHandler(e:MouseEvent):void {
            // マウスホイールの移動量をスケールに変換
            var delta:Number = e.delta * 0.5;
            delta = (delta < 0) ? -delta : 1 / delta;
            
            // selectedRect_ を計算
            var nowWidth:Number  = stage.stageWidth  * delta;
            var nowHeight:Number = stage.stageHeight * delta;
            selectedRect_.x      = mouseX - nowWidth  / 2;
            selectedRect_.y      = mouseY - nowHeight / 2;
            selectedRect_.width  = nowWidth;
            selectedRect_.height = nowHeight;
            
            // イベント送出
            update();
        }
        
        // イベント送出
        private function update():void {
            dispatchEvent(new Event(Event.CHANGE));
        }
    }
//}


//package  {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.ColorTransform;
    import flash.geom.Rectangle;
    /**
     * ...
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Viewer extends Sprite {
        /**
         * BitmapData 描画のための Rectangle
         */
        public function get rect():Rectangle { return _rect; }
        private var _rect:Rectangle;


        // キャンバスとしての BitmapData
        private var bitmapData_:BitmapData;

        // start() 時、画面を暗くする
        private const FADE:ColorTransform = new ColorTransform(0.95, 0.95, 0.95);
        
        // 1度の draw() で描画する高さ
        private const DRAW_HEIGHT:int = 10;
        
        /**
         * コンストラクタ
         * @param    w    描画幅
         * @param    h    描画高
         */
        public function Viewer(w:int, h:int) {
            // キャンバス部生成
            bitmapData_ = new BitmapData(w, h, false, 0x0);
            addChild(new Bitmap(bitmapData_));
            
            // BitmapData 描画のための Rectangle 生成
            _rect = new Rectangle(0, 0, w, DRAW_HEIGHT);
        }
        
        /**
         * 描画開始
         */
        public function start():void {
            bitmapData_.colorTransform(bitmapData_.rect, FADE);
            _rect.y = 0;
            addEventListener(Event.ENTER_FRAME, update);
        }
        // イベント送出
        private function update(e:Event):void {
            dispatchEvent(new Event(Event.CHANGE));
        }

        /**
         * 描画実行
         */
        public function draw(data:Vector.<uint>):void {
            // BitmapData の更新
            bitmapData_.lock();
            bitmapData_.setVector(_rect, data);
            bitmapData_.unlock();
            // 終了判定
            _rect.y += DRAW_HEIGHT;
            if (_rect.y >= bitmapData_.width) removeEventListener(Event.ENTER_FRAME, update);
        }
    }
//}


//package aquioux.math {
    /**
     * 複素数
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ final class Complex {
        // 実数部
        public function get real():Number { return _rl; }
        public function set real(value:Number):void { _rl = value; }
        private var _rl:Number;
        // 虚数部
        public function get imag():Number { return _im; }
        public function set imag(value:Number):void { _im = value; }
        private var _im:Number;
        

        // コンストラクタ
        public function Complex(rl:Number = 0, im:Number = 0) {
            _rl = rl;
            _im = im;
        }
        
        // 複製
        public function clone():Complex {
            return new Complex(_rl, _im);
        }
        
        public function toString():String {
            return _rl + " + " + _im + "i";
        }
    }
//}


//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);
        }
    }
//}