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

逆写像によるジュリア集合の描画(4分木)

逆写像によるジュリア集合の描画(4分木)
@author Aquioux(Yoshida, Akio)
@see http://aquioux.net/blog/?p=1884
/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/4c4C
 */

// forked from Aquioux's 逆写像によるジュリア集合の描画
package {
//    import aquioux.geom.Complex;
//    import aquioux.geom.MathComplex;
    import com.bit101.components.HSlider;
    import com.bit101.components.VSlider;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    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")]
    /**
     * 逆写像によるジュリア集合の描画(4分木)
     * @author Aquioux(Yoshida, Akio)
     * @see http://aquioux.net/blog/?p=1884
     */
    public class Main extends Sprite {
        // 描画領域サイズ
        private const WIDTH:int  = 450;
        private const HEIGHT:int = 450;

        // スライダー関連
        private const SLIDER_LONG:int  = WIDTH;
        private const SLIDER_SHORT:int = stage.stageWidth - WIDTH;
        private const SLIDER_VALUE_MIN:Number = -2.0;
        private const SLIDER_VALUE_MAX:Number =  2.0;

        // 写像の世代数
        private const GENERATION:int = 8;

        // 漸化式 の z
        private var Z:Complex = MathComplex.COMPLEX_0;

        // 漸化式 の c
        private var c_:Complex = new Complex( -0.035, 0.795);
        
        // ビューア
        private var viewer_:Viewer;
        // 計算クラス
        private var julia_:Julia;
        // スライダー
        private var hslider_:HSlider;
        private var vslider_:VSlider;
        // 現在の c の値を表示するテキストフィールド
        private var textField1_:TextField;
        private var textField2_:TextField;
        
        // マウスダウン判定フラグ
        private var isDown_:Boolean = true;
        
        
        public function Main():void {
            // ビューアの生成
            viewer_ = new Viewer();
            addChild(viewer_);

            // Julia クラスの生成・セットアップ
            julia_ = new Julia();
            julia_.viewer = viewer_;
            julia_.minX = julia_.minY = SLIDER_VALUE_MIN;
            julia_.maxX = julia_.maxY = SLIDER_VALUE_MAX;
            julia_.setup(WIDTH, HEIGHT);
            
            // スライダーの作成
            // 横スライダー(複素数 c の実数部)
            hslider_ = new HSlider(this, 0, HEIGHT);
            hslider_.width  = SLIDER_LONG;
            hslider_.height = SLIDER_SHORT;
            hslider_.setSliderParams(SLIDER_VALUE_MIN, SLIDER_VALUE_MAX, c_.real);
            hslider_.tick = 0.001;
            // 縦スライダー(複素数 c の虚数部)
            vslider_ = new VSlider(this, WIDTH, 0);
            vslider_.width  = SLIDER_SHORT;
            vslider_.height = SLIDER_LONG;
            vslider_.setSliderParams(SLIDER_VALUE_MIN, SLIDER_VALUE_MAX, c_.imag);
            vslider_.tick = 0.001;
            
            // テキストフィールドの作成
            textField1_ = new TextField();
            textField2_ = new TextField();
            textField1_.autoSize = TextFieldAutoSize.LEFT;
            textField2_.autoSize = TextFieldAutoSize.LEFT;
            var fontSize:int = 12;
            var textFormat:TextFormat = new TextFormat("_typewriter", fontSize, 0x000000);
            textField1_.defaultTextFormat = textFormat;
            textField2_.defaultTextFormat = textFormat;
            textField1_.y = 0;
            textField2_.y = fontSize * 1.25 >> 0;
            textField1_.selectable = false;
            textField2_.selectable = false;
            addChild(textField1_);
            addChild(textField2_);
            
            // マウスハンドラの設定
            // スライダーに直接ハンドラを設定すると ENTER_FRAME のタイミングで更新がかかり、
            // 処理負荷が大きくなるため、マウスアップ時にスライダハンドラを実行させる
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            stage.addEventListener(MouseEvent.MOUSE_UP,   mouseUpHandler);
            stage.addEventListener(Event.MOUSE_LEAVE,     mouseLeaveHandler);
            
            // 初回状態の表示
            mouseUpHandler(null);
        }
        
        
        // 描画
        private function draw():void {
            // プロット開始
            julia_.start(Z, c_, GENERATION);
        }
        

        // マウスハンドラ
        private function mouseDownHandler(e:MouseEvent):void {
            isDown_ = true;
        }
        private function mouseUpHandler(e:MouseEvent):void {
            if (isDown_) {
                hsliderHandler();
                vsliderHandler();
                isDown_ = false;
            }
        }
        private function mouseLeaveHandler(e:Event):void {
            mouseUpHandler(null);
        }
        

        // スライダーハンドラ
        private function hsliderHandler():void {
            var value:Number = hslider_.value;
            textField1_.text = "real : " + String(value);
            c_.real = value;
            draw();
        }
        private function vsliderHandler():void {
            var value:Number = vslider_.value;
            textField2_.text = "imag : " + String(value);
            c_.imag = value;
            draw();
        }
    }
}


//package {
//    import aquioux.geom.Complex;
//    import aquioux.geom.MathComplex;
    import flash.display.BitmapData;
    import flash.geom.Rectangle;
    /**
     * 逆写像によるジュリア集合描画クラス
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class Julia {
        /**
         * 描画領域のX軸における最小値
         */
        public function set minX(value:Number):void {_minX = value;}
        private var _minX:Number = -2.0;
        /**
         * 描画領域のX軸における最大値
         */
        public function set maxX(value:Number):void {_maxX = value;}
        private var _maxX:Number =  2.0;
        /**
         * 描画領域のY軸における最小値
         */
        public function set minY(value:Number):void {_minY = value;}
        private var _minY:Number = -2.0;
        /**
         * 描画領域のY軸における最大値
         */
        public function set maxY(value:Number):void {_maxY = value;}
        private var _maxY:Number =  2.0;

        /**
         * 点をプロットする色
         */
        public function set plotColor(value:uint):void { _plotColor = value; }
        private var _plotColor:uint = 0x00000000;
        /**
         * 地の色
         */
        public function set backgraundColor(value:uint):void { _backgraundColor = value; }
        private var _backgraundColor:uint = 0xFFFFFFFF;
        
        /**
         * ビューア
         */
        private var _viewer:Viewer;
        public function set viewer(value:Viewer):void { _viewer = value; }


        // オフセット値(プロット時使用)
        private var offsetX_:Number;
        private var offsetY_:Number;
        
        // ステップ値(計算時使用)
        private var stepX_:Number;
        private var stepY_:Number;
        
        // ビューアへ渡すデータを格納した Vector
        private var data_:Vector.<uint>;

        // 描画領域
        private var width_:int;        // 幅
        private var height_:int;    // 高
        
        
        /**
         * セットアップ
         * @param    width    描画領域幅
         * @param    hidth    描画領域高
         */
        public function setup(width:int, height:int):void {
            width_  = width;
            height_ = height;
            
            // ビューアの BitmapData 生成
            if (!_viewer) new Error("setup 前にビューアを指定してください。");
            _viewer.setup(width_, height_, false, _backgraundColor);

            // ビューア用 Vector の生成
            data_ = new Vector.<uint>(width_ * height_, true);
            
            // オフセット値計算
            offsetX_ = width_  / 2;
            offsetY_ = height_ / 2;
            
            // ステップ値計算
            var rangeX:Number = (_maxX - _minX) / 2;
            var rangeY:Number = (_maxY - _minY) / 2;
            stepX_ = offsetX_ / rangeX;
            stepY_ = offsetY_ / rangeY;
            
        }
        /**
         * 描画(再帰処理)開始
         * @param    z    漸化式の z
         * @param    c    漸化式の c
         * @param    generation    再帰世代数
         */
        public function start(z:Complex, c:Complex, generation:int):void {
            // ビューア用 Vector 初期化
            var len:int = data_.length;
            for (var i:int = 0; i < len; i++) data_[i] = _backgraundColor;
            // プロット
            plot(z, c, generation);
            // ビューアへ通知
            _viewer.update(data_);
        }
        
        // 再帰処理
        // 漸化式:z ← ±√(z - c) <z ← z^2 + c の逆写像>
        private function plot(z:Complex, c:Complex, generation:int):void {
            if (generation <= 0) {// 再帰終了時
                var x:int = z.real * stepX_ + offsetX_ >> 0;
                var y:int = z.imag * stepY_ + offsetY_ >> 0;
                if ((0 <= x && x < width_) && (0 <= y && y < height_)) {
                    // 描画座標値が描画領域をはみ出していない場合
                    data_[y * width_ + x] = _plotColor;
                }
            } else {        // 再帰続行(2分木再帰)
                z = MathComplex.subtract(z, c);
                z = MathComplex.sqrt(z);
                var rl:Number = z.real;
                var im:Number = z.imag;
                generation--;
                plot(new Complex( rl,  im), c, generation);
                plot(new Complex(-rl, -im), c, generation);
                plot(new Complex(-rl,  im), c, generation);
                plot(new Complex( rl, -im), c, generation);
            }
        }
    }
//}


//package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    /**
     * ビューア
     * @author YOSHIDA, Akio(Aquioux)
     */
    /*public*/ class Viewer extends Bitmap {
        // BitmapData 関連
        private var rect_:Rectangle;        // ColorTransform, Blur 共用

        /**
         * セットアップ
         * @param    w    表示領域幅
         * @param    h    表示領域高
         * @param    transparent    表示領域透明サポート
         * @param    fillColor    表示領域塗りつぶし色
         */
        public function setup(w:Number, h:Number, transparent:Boolean = true, fillColor:uint = 0xFFFFFFFF):void {
            bitmapData = new BitmapData(w, h, transparent, fillColor);
            rect_      = new Rectangle(0, 0, w, h);
        }
        
        /**
         * アップデート
         * @param    data    setVector 用 Vector
         */
        public function update(data:Vector.<uint>):void {
            bitmapData.lock();
            bitmapData.setVector(rect_, data);
            bitmapData.unlock();
        }
    }
//}


//package aquioux.geom {
    /**
     * 複素数
     * @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, im:Number) {
            _rl = rl;
            _im = im;
        }
        
        // 複製
        public function clone():Complex {
            return new Complex(_rl, _im);
        }
        
        public function toString():String {
            return _rl + " + " + _im + "i";
        }
    }
//}


//package aquioux.geom {
    /**
     * 複素数の演算
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ final class MathComplex {

        // 定数
        static public const COMPLEX_1:Complex = new Complex(1.0, 0.0);
        static public const COMPLEX_0:Complex = new Complex(0.0, 0.0);
        static public const PURELY_IMAGINARY:Complex = new Complex(0.0, 1.0);
        

        // 加算
        static public function add(a:Complex, b:Complex):Complex {
            return new Complex(
                a.real + b.real,
                a.imag + b.imag
            );
        }
        // 減算
        static public function subtract(a:Complex, b:Complex):Complex {
            return new Complex(
                a.real - b.real,
                a.imag - b.imag
            );
        }
        // 乗算
        static public function multiply(a:Complex, b:Complex):Complex {
            return new Complex(
                a.real * b.real - a.imag * b.imag,
                a.real * b.imag + a.imag * b.real
            );
        }
        // 除算
        static public function divide(a:Complex, b:Complex):Complex {
            var val:Number = abs2(b);
            return new Complex(
                (a.real * b.real + a.imag * b.imag) / val,
                (a.imag * b.real - a.real * b.imag) / val
            );
        }
        

        // 共役複素数を求める
        static public function conjugate(c:Complex):Complex {
            return new Complex(
                 c.real,
                -c.imag
            );
        }
        
        // 絶対値
        static public function abs(c:Complex):Number {
            return Math.sqrt(abs2(c));
            // |c| = √(c * c~) = √(c.real^2 + c.imag^2)
        }
        // 絶対値の二乗
        static public function abs2(c:Complex):Number {
            return c.real * c.real + c.imag * c.imag;
        }
        

        // スケーリング(第1引数の実数部、虚数部をそれぞれ第2引数倍する)
        static public function scale(c:Complex, n:Number):Complex {
            return new Complex(
                c.real * n,
                c.imag * n
            );
        }
        
        // 整数化(引数の実数部、虚数部それぞれの小数点以下を切り捨てる)
        static public function integer(c:Complex):Complex {
            return new Complex(
                c.real >> 0,
                c.imag >> 0
            );
        }
        

        // べき乗
        static public function pow(c:Complex, n:int):Complex {
            var z:Complex = c.clone();
            while(--n) z = multiply(z, c);
            return z;
        }
        

        // 平方根
        static public function sqrt(c:Complex):Complex {
            var val1:Number;
            if (Math.abs(c.real) < 0.000001) {
                val1 = c.imag * Math.PI / (2 * Math.abs(c.imag));
            } else {
                val1 = Math.atan2(c.imag, c.real);
            }
            val1 /= 2;
            var val2:Number = Math.pow(abs2(c), 1 / 4);
            
            return new Complex(
                Math.cos(val1) * val2,
                Math.sin(val1) * val2
            );
        }


        // 指数関数
        static public function exp(c:Complex):Complex {
            var val:Number = Math.exp(c.real);
            return new Complex(
                Math.cos(c.imag) * val,
                Math.sin(c.imag) * val
            );
        }

        // 三角関数(サイン)
        static public function sin(c:Complex):Complex {
            return new Complex(
                Math.sin(c.real) * (Math.exp(c.imag) + Math.exp(-c.imag)) / 2,
                Math.cos(c.real) * (Math.exp(c.imag) - Math.exp(-c.imag)) / 2
            );
        }
        // 三角関数(コサイン)
        static public function cos(c:Complex):Complex {
            return new Complex(
                 Math.cos(c.real) * (Math.exp(c.imag) + Math.exp(-c.imag)) / 2,
                -Math.sin(c.real) * (Math.exp(c.imag) - Math.exp(-c.imag)) / 2
            );
        }
        // 三角関数(タンジェント)
        static public function tan(c:Complex):Complex {
            return divide(sin(c), cos(c));
        }
    }
//}