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

蜘蛛のフラクタル(ジュリア集合)

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

package {
//    import aquioux.display.colorUtil.CycleRGB;
//    import aquioux.geom.Complex;
    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.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    [SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
    /**
     * 蜘蛛のジュリア集合の描画
     * @see    http://aquioux.net/blog/?p=1890
     * @author Aquioux(Yoshida, Akio)
     */
    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 = Julia.MIN_X;
        private const SLIDER_VALUE_MAX:Number = Julia.MAX_X;
        
        // 初期値
        private const REAL_NUMBER:Number      = 0.0;    // c の実数部
        private const IMAGINARY_NUMBER:Number = 0.0;    // c の虚数部

        // ジュリア集合でない部分の色
        private var colors_:Vector.<uint>;
        
        // 表示用 BitmapData
        private var bmd_:BitmapData;
        // 走査クラス
        private var scan_:Scan;
        // 計算クラス
        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 {
            // カラーセット
            colors_ = new Vector.<uint>();
            var degree:int = 45;
            var step:Number = 360 / degree;
            for (var i:int = 0; i < degree; i++) colors_[i] = CycleRGB.getColor(i * step + 180);
            colors_.reverse();
            colors_.fixed = true;
            
            // Viewer の作成
            bmd_ = new BitmapData(WIDTH, HEIGHT, false, 0x0);
            addChild(new Bitmap(bmd_));
            
            // スライダーの作成
            // 横スライダー(複素数 c の実数部)
            hslider_ = new HSlider(this, 0, HEIGHT);
            hslider_.width  = SLIDER_LONG;
            hslider_.height = SLIDER_SHORT;
            hslider_.setSliderParams(SLIDER_VALUE_MIN, SLIDER_VALUE_MAX, REAL_NUMBER);
            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, IMAGINARY_NUMBER);
            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;
            addChild(textField1_);
            addChild(textField2_);
            
            // マウスハンドラの設定
            // スライダーに直接ハンドラを設定すると ENTER_FRAME のタイミングで更新がかかり、
            // 処理負荷が大きくなるため、マウスアップ時にスライダハンドラを実行させる
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
            stage.addEventListener(Event.MOUSE_LEAVE,   mouseLeaveHandler);
            
            // Julia クラスのセットアップ
            julia_ = new Julia();
            julia_.colors = colors_;

            // Scan クラスのセットアップ
            scan_ = new Scan();
            scan_.minX = Julia.MIN_X;
            scan_.maxX = Julia.MAX_X;
            scan_.minY = Julia.MIN_Y;
            scan_.maxY = Julia.MAX_Y;
            scan_.calculator = julia_;
            scan_.setup(WIDTH, HEIGHT);
            
            // 初回状態の表示
            mouseUpHandler(null);
        }
        
        
        // 描画
        private function draw():void {
            bmd_.lock();
            bmd_.setVector(bmd_.rect, scan_.update());
            bmd_.unlock();
        }
        

        // マウスハンドラ
        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);
            julia_.c.real = value;
            draw();
        }
        private function vsliderHandler():void {
            var value:Number = vslider_.value;
            textField2_.text = "imag : " + String(value);
            julia_.c.imag = value;
            draw();
        }
    }
}


//package {
    /**
     * 二次元走査クラス
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class Scan {
        /**
         * 計算クラス
         */
        private var _calculator:ICalculator;
        public function set calculator(value:ICalculator):void { _calculator = value; }
        
        // ----- 描画領域範囲 -----
        /**
         * 描画領域のX軸最小値(_scale = 1.0 における値)
         */
        private var _minX:Number;
        public function set minX(value:Number):void {
            _minX = value;
            calcWidth();
        }
        /**
         * 描画領域のX軸最大値(_scale = 1.0 における値)
         */
        private var _maxX:Number;
        public function set maxX(value:Number):void {
            _maxX = value;
            calcWidth();
        }
        /**
         * 描画領域のY軸最小値(_scale = 1.0 における値)
         */
        private var _minY:Number;
        public function set minY(value:Number):void {
            _minY = value;
            calcHeight();
        }
        /**
         * 描画領域のY軸最大値(_scale = 1.0 における値)
         */
        private var _maxY:Number;
        public function set maxY(value:Number):void {
            _maxY = value;
            calcHeight();
        }
        
        /**
         * スケール値
         */
        public function set scale(value:Number):void {
            _scale = value;
            calcValue();
        }
        private var _scale:Number = 1.0;

        /**
         * 表示位置オフセットX座標値
         */
        public function set offsetX(value:Number):void {
            _offsetX = value;
            calcStartX();
        }
        private var _offsetX:Number = 0.0;
        /**
         * 表示位置オフセットY座標値
         */
        public function set offsetY(value:Number):void {
            _offsetY = value;
            calcStartY();
        }
        private var _offsetY:Number = 0.0;
        

        // ----- その他変数 -----
        // 計算領域
        private var calculateWidth_:Number;    // 幅
        private var calculateHeight_:Number;// 高
        // 計算領域の中心
        private var centerX_:Number;        // X座標
        private var centerY_:Number;        // Y座標
        // 計算加算値
        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();
            
            // 計算クラスのセットアップ
            if (!_calculator) new Error("setup 前に計算クラスを指定してください。");
            _calculator.setup();
        }

        /**
         * 複素数平面を走査し、その値を計算クラスの渡す
         * @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 {
            // 走査領域を計算する
            calcWidth();
            calcHeight();
            // 複素数平面走査のインクリメントステップを計算する
            calcStep();
            // 複素数平面の走査開始座標を計算する
            calcStartX();    // 実数座標
            calcStartY();    // 虚数座標
        }
        // _minX, _maxX から走査範囲幅を計算
        private function calcWidth():void {
            calculateWidth_ = _maxX - _minX;
            centerX_ = _minX + calculateWidth_ / 2;
        }
        // _minY, _maxY から走査範囲高を計算
        private function calcHeight():void {
            calculateHeight_ = _maxY - _minY;
            centerY_ = _minY + calculateHeight_ / 2;
        }
        // 複素数平面走査の計算加算値を計算する
        private function calcStep():void {
            stepX_ = calculateWidth_  / (_scale * (displayWidth_  - 1));
            stepY_ = calculateHeight_ / (_scale * (displayHeight_ - 1));
        }
        // 複素数平面の走査開始座標を計算する(実数部座標)
        private function calcStartX():void {
            startX_ = centerX_ - calculateWidth_ * 0.5 / _scale + _offsetX;
        }
        // 複素数平面の走査開始座標を計算する(虚数部座標)
        private function calcStartY():void {
            startY_ = centerY_ - calculateHeight_ * 0.5 / _scale - _offsetY;
        }
    }
//}


//package {
//    import aquioux.geom.Complex;
//    import aquioux.geom.MathComplex;
    /**
     * 蜘蛛のジュリア集合描画クラス
     * _scale = 1.0 のとき (-2, -2) ~ (2, 2) の領域を対象に計算する
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class Julia implements ICalculator {
        // ----- 描画領域範囲 -----
        /**
         * 描画領域のX軸最小値
         */
        static public const MIN_X:Number = -2.0;
        /**
         * 描画領域のY軸最小値
         */
        static public const MIN_Y:Number = -2.0;
        /**
         * 描画領域のX軸最大値
         */
        static public const MAX_X:Number =  2.0;
        /**
         * 描画領域のY軸最大値
         */
        static public const MAX_Y:Number =  2.0;

        // ----- 色 -----
        /**
         * ジュリア集合に該当する部分の色(一般的には色なし=黒)
         */
        public function set color(value:uint):void { _color = value; }
        private var _color:uint = 0x000000;
        /**
         * 発散部分の色階調
         */
        public function set colors(value:Vector.<uint>):void {
            _colors = value;
            degree_ = value.length;
        }
        private var _colors:Vector.<uint>;

        // ----- 外部から与えられる複素数 -----
        /**
         * 漸化式の c の実数
         */
        public function get c():Complex { return _c; }
        //public function set c(value:Complex):void { _c = value; }
        private var _c:Complex = MathComplex.COMPLEX_0;


        // 発散チェックループ回数(_colors.length の値)
        private var degree_:int;


        /**
         * 初期化
         */
        public function setup():void {
            // 発散時の色が外部から指定されていなければ設定する
            if (!_colors) {
                var len:int = 256;
                _colors = new Vector.<uint>(len, true);
                for (var i:int = 0; i < len; i++) _colors[i] = i << 16 | i << 8 | i;
                degree_ = len;
            }
        }


        /**
         * Scan クラスからの走査データを受け、計算をおこなう
         * @param    x    X座標値
         * @param    y    Y座標値
         * @return    計算結果
         */
        public function calculate(x:Number, y:Number):uint {
            var r:int = checkDivergence(x, y, _c);
            return (r >= 0) ? _colors[r] : _color;
        }
        /**
         * 漸化式 z ← z * z + c の評価
         * @param    zRl    複素数 z の実数部
         * @param    zIm    複素数 z の虚数部
         * @param    c    複素数 c
         * @return    発散評価値
         * @private
         */
        private function checkDivergence(zRl:Number, zIm:Number, c:Complex):int {
            // 漸化式の計算要素の複素数
            var zRlSqr:Number;        // 実数部の2乗
            var zImSqr:Number;        // 虚数部の2乗
            // 漸化式の計算結果の複素数
            var zRlNxt:Number;        // 実数部
            var zImNxt:Number;        // 虚数部
            // 漸化式の c
            var cRl:Number = c.real;
            var cIm:Number = c.imag;
            
            var i:int = degree_;
            while (i--) {
                // 発散の評価(|z| > 2 = |z|^2 > 4)
                zRlSqr = zRl * zRl;
                zImSqr = zIm * zIm;
                if (zRlSqr + zImSqr > 4) break;
                
                // 発散していなかった場合、漸化式実行
                zRlNxt = zRlSqr - zImSqr + cRl;
                zImNxt = 2 * zRl * zIm   + cIm;
                zRl = zRlNxt;
                zIm = zImNxt;
                
                // 複素数 c の値を更新(c_n+1 ← c_n / 2 + z_n+1)
                cRl /= 2;
                cIm /= 2;
                cRl += zRl;
                cIm += zIm;
            }
            return i;
            // break で脱しなかった(発散しなかった)場合、while を回りきるので -1 になる
        }
    }
//}


//package {
    /**
     * interface
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ interface ICalculator {
        function setup():void;
        function calculate(x:Number, y:Number):uint;
    }
//}


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


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