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

ニュートン法によるフラクタル(5) ~漸化式の一般化~

Get Adobe Flash player
by Aquioux 17 Nov 2011
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/qAXU
 */

// forked from Aquioux's ニュートン法によるフラクタル(2) ~根による塗り分け~
// forked from Aquioux's ニュートン法によるフラクタル(1)
package {
    //import aquioux.math.Complex;
    import com.bit101.components.PushButton;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Rectangle;
    [SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
    /**
     * ニュートン法によるフラクタル(5) ~漸化式の一般化~
     * @see   http://aquioux.net/blog/?p=2123 
     * @author Aquioux(Yoshida, Akio)
     */
    public class Main extends Sprite {
        // 計算クラス
        private var newton_:Newton;
        // 走査クラス
        private var scan_:Scan;
        // 表示用 BitmapData
        private var bmd_:BitmapData;
        private var rect_:Rectangle;    // bmd_ 用 Rectangle
        
        private var prevButton_:PushButton;
        

        public function Main():void {
            // ステージサイズ
            var sw:int  = stage.stageWidth;
            var sh:int = stage.stageHeight;

            // Newton クラスのセットアップ
            newton_ = new Newton();

            // Scan クラスのセットアップ
            scan_ = new Scan();
            scan_.calculator = newton_;
            scan_.setup(sw, sh);
            rect_ = new Rectangle(0, 0, sw, sh);
            
            // Viewer の作成
            bmd_ = new BitmapData(sw, sh, false, 0x0);
            addChild(new Bitmap(bmd_));
            
            // ボタンの作成
            var buttonWidth:int = 93;
            var button0:PushButton = new PushButton(this, 0,               sh - 20, "1.0 + 0.0 i",   button0Handler);
            var button1:PushButton = new PushButton(this, buttonWidth,     sh - 20, "1.5 + 0.0 i",   button1Handler);
            var button2:PushButton = new PushButton(this, buttonWidth * 2, sh - 20, "1.0 + 0.5 i",   button2Handler);
            var button3:PushButton = new PushButton(this, buttonWidth * 3, sh - 20, "1.0 + 0.75 i",  button3Handler);
            var button4:PushButton = new PushButton(this, buttonWidth * 4, sh - 20, "1.0 - 0.5 i",   button4Handler);
            button0.width = buttonWidth;
            button1.width = buttonWidth;
            button2.width = buttonWidth;
            button3.width = buttonWidth;
            button4.width = buttonWidth;
            
            // 初回状態の表示
            button2Handler(null);
            button2.selected = true;
            changeButton(button2);
        }
        
        // 描画
        private function draw():void {
            bmd_.lock();
            bmd_.setVector(rect_, scan_.update());
            bmd_.unlock();
        }
        // ボタンハンドラ
        private function button0Handler(e:Event):void {
            if (e) changeButton(PushButton(e.target));
            newton_.a = new Complex(1.0, 0.0);
            draw();
        }
        private function button1Handler(e:Event):void {
            if (e) changeButton(PushButton(e.target));
            newton_.a = new Complex(1.5, 0.0);
            draw();
        }
        private function button2Handler(e:Event):void {
            if (e) changeButton(PushButton(e.target));
            newton_.a = new Complex(1.0, 0.5);
            draw();
        }
        private function button3Handler(e:Event):void {
            if (e) changeButton(PushButton(e.target));
            newton_.a = new Complex(1.0, 0.75);
            draw();
        }
        private function button4Handler(e:Event):void {
            if (e) changeButton(PushButton(e.target));
            newton_.a = new Complex(1.0, -0.5);
            draw();
        }
        private function changeButton(currentButton:PushButton):void {
            if (prevButton_) prevButton_.enabled = true;
            currentButton.enabled = false;
            prevButton_ = currentButton;
        }
    }
}


//package {
    //import aquioux.math.Complex;
    import flash.geom.Rectangle;
    /**
     * ニュートン法によるフラクタル描画クラス
     * _scale = 1.0 のとき (-2, -2) ~ (2, 2) の領域を対象に計算する
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class Newton 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; }
        
        /**
         * 漸化式の a
         */
        private var _a:Complex = new Complex(1, 0);
        public function set a(value:Complex):void { _a = value; }
        

        // z^3 - 1 = 0 の解
        private const R1:Complex = new Complex(1, 0);
        private const R2:Complex = new Complex(-0.5,  Math.sin(2 * Math.PI / 3));
        private const R3:Complex = new Complex(-0.5, -Math.sin(2 * Math.PI / 3));
        
        // 収束確認用
        private const LIMIT:Number = 1.0E-8;

        // 収束チェックループ回数
        private const DEGREE:int = 0x33;
        // 色調整率
        private const COLOR_RANGE:int = 0xFF / DEGREE >> 0;


        /**
         * Scan クラスからの走査データを受け、計算をおこなう
         * @param    x    X座標値
         * @param    y    Y座標値
         * @return    計算結果
         */
        public function calculate(x:Number, y:Number):uint {
            return formula(x, y);
        }
        /**
         * 漸化式 z ← z - (z^3 - 1) / (3 * z^2) の評価
         * @param    zRl    複素数 z の実数部
         * @param    zIm    複素数 z の虚数部
         * @return    収束評価値
         * @private
         */
        private function formula(zRl:Number, zIm:Number):int {
            // 漸化式の計算要素の複素数
            var zRlSqr:Number;        // 実数部の2乗
            var zImSqr:Number;        // 虚数部の2乗
            var z1Rl:Number;        // z^3 - 1 の実数部
            var z1Im:Number;        // z^3 - 1 の虚数部
            var z2Rl:Number;        // 3 * z^2 の実数部
            var z2Im:Number;        // 3 * z^2 の虚数部
            var z2norm:Number;        // |z2|^2
            var z3Rl:Number;        // (z^3 - 1) / (3 * z^2) の実数部
            var z3Im:Number;        // (z^3 - 1) / (3 * z^2) の虚数部
            var z4Rl:Number;        // a * (z^3 - 1) / (3 * z^2) の実数部
            var z4Im:Number;        // a * (z^3 - 1) / (3 * z^2) の虚数部
            // 評価値と解との距離
            var dstRl:Number;        // 評価値と解との距離(実数部)
            var dstIm:Number;        // 評価値と解との距離(虚数部)
            var dst:Number;            // 評価値と解との距離
            // 色要素
            var c:uint;
            
            var i:int = DEGREE;
            while (i--) {;
                // z^2
                zRlSqr = zRl * zRl - zIm * zIm;
                zImSqr = 2 * zRl * zIm;
                // z^3 - 1
                z1Rl = zRlSqr * zRl - zImSqr * zIm - 1;
                z1Im = zRlSqr * zIm + zImSqr * zRl;
                
                // 3 * z^2
                z2Rl = zRlSqr * 3;
                z2Im = zImSqr * 3;
                
                // (z^3 - 1) / (3 * z^2)
                z2norm = z2Rl * z2Rl + z2Im * z2Im;
                z3Rl = (z1Rl * z2Rl + z1Im * z2Im) / z2norm;
                z3Im = (z1Im * z2Rl - z1Rl * z2Im) / z2norm;
                
                // a * (z^3 - 1) / (3 * z^2)
                z4Rl = _a.real * z3Rl - _a.imag * z3Im;
                z4Im = _a.real * z3Im + _a.imag * z3Rl;
                
                // z = z - a * (z^3 - 1) / (3 * z^2)
                zRl -= z4Rl;
                zIm -= z4Im;
                
                // 収束評価
                c = i * COLOR_RANGE;
                // 解1
                dstRl = zRl - R1.real;
                dstIm = zIm - R1.imag;
                dst   = dstRl * dstRl + dstIm * dstIm;
                if (dst < LIMIT) return c << 16 | 0 << 8 | 0;
                // 解2
                dstRl = zRl - R2.real;
                dstIm = zIm - R2.imag;
                dst   = dstRl * dstRl + dstIm * dstIm;
                if (dst < LIMIT) return 0 << 16 | c << 8 | 0;
                // 解3
                dstRl = zRl - R3.real;
                dstIm = zIm - R3.imag;
                dst   = dstRl * dstRl + dstIm * dstIm;
                if (dst < LIMIT) return 0 << 16 | 0 << 8 | c;
            }
            return _color;
        }
    }
//}


//package {
    import flash.geom.Rectangle;
    /**
     * 計算クラスの interface
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ interface ICalculator {
        function get rect():Rectangle;
        function calculate(x:Number, y:Number):uint;
    }
//}


//package {
    import flash.geom.Rectangle;
    /**
     * 二次元走査クラス
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class Scan {
        /**
         * 計算クラス
         */
        private var _calculator:ICalculator;
        public function set calculator(value:ICalculator):void {
            _calculator = value;
            rect = _calculator.rect;
        }
        
        /**
         * 描画領域(_scale = 1.0 における値)
         */
        private var _rect:Rectangle;
        public function set rect(value:Rectangle):void {
            _rect = value;
        }

        /**
         * 表示位置オフセットX座標値
         */
        private var _offsetX:Number = 0.0;
        public function set offsetX(value:Number):void {
            _offsetX = value;
            calcValue();
        }
        /**
         * 表示位置オフセットY座標値
         */
        private var _offsetY:Number = 0.0;
        public function set offsetY(value:Number):void {
            _offsetY = value;
            calcValue();
        }
        
        /**
         * スケール値
         */
        private var _scale:Number = 1.0;
        public function set scale(value:Number):void {
            _scale = value;
            calcValue();
        }
        

        // ----- その他変数 -----
        // 計算加算値
        private var stepX_:Number;            // X軸
        private var stepY_:Number;            // Y軸
        // 走査開始座標
        private var startX_:Number;            // X座標
        private var startY_:Number;            // Y座標
        // 表示領域
        private var displayWidth_:int;        // 幅
        private var displayHeight_:int;        // 高
        
        // ビューアへ渡すデータ
        private var data_:Vector.<uint>;
        // data_ のインデックス
        private var idx_:int;
        
        
        /**
         * 初期化
         * @param    width    表示幅
         * @param    height    表示高
         */
        public function setup(width:int, height:int):void {
            // 表示サイズ
            displayWidth_  = width;
            displayHeight_ = height;
            
            // data_ の生成
            data_ = new Vector.<uint>(width * height, true);

            // 複素数平面走査用の各変数を計算する
            calcValue();
        }

        /**
         * 複素数平面を走査し、その値を計算クラスの渡す
         * @return    計算クラスから返ってきた値を格納した Vector
         */
        public function update():Vector.<uint> {
            idx_ = 0;
            for (var y:int = 0; y < displayHeight_; y++) {
                for (var x:int = 0; x < displayWidth_; x++) {
                    data_[idx_++] = _calculator.calculate(startX_ + x * stepX_, startY_ + y * stepY_);
                }
            }
            return data_;
        }
        
        // 複素数平面走査用の変数を計算する
        private function calcValue():void {
            stepX_  = (_rect.width  / _scale) / displayWidth_;
            stepY_  = (_rect.height / _scale) / displayHeight_;
            startX_ = (_rect.width  * 0.5 + _rect.x + _offsetX) - displayWidth_  * 0.5 * stepX_;
            startY_ = (_rect.height * 0.5 + _rect.y + _offsetY) - displayHeight_ * 0.5 * stepY_;
        }
    }
//}


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