Magnet Fractal Type I (Julia)

by Aquioux
ドラッグで矩形を描く→拡大
cntl + ドラッグ→移動
スライダー→複素数 c の値を変更

drag -> draw Rectangle -> zoom up
cntl + drag -> move
slider -> change value Complex Number C
♥0 | Line 536 | Modified 2012-02-28 20:43:40 | MIT License
play

ActionScript3 source code

/**
 * Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/ks6Q
 */

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")]
    /**
     * Magnet Fractal Type I (Julia) の描画
     * @see    http://aquioux.net/blog/?p=2226
     * @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 var sliderValueMin:Number;
        private var sliderValueMax:Number;
        
        // 初期値
        private const REAL_NUMBER:Number      = 0;    // c の実数部
        private const IMAGINARY_NUMBER:Number = 0;    // c の虚数部
        
        // 各連携クラス
        private var fractal_:Magnet1;                // フラクタル計算クラス
        private var engine_:Engine;                    // フラクタル描画エンジンクラス
        private var mouseBehavior_:MouseBehavior;    // マウス操作クラス
        private var viewer_:Viewer;                    // ビューアクラス
        private var buttons_:Buttons;                // プリセットパラメータボタン
        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 colors:Vector.<uint> = new Vector.<uint>();
            var degree:int = 24;
            var step:Number = 360 / (degree + 1);
            for (var i:int = 0; i < degree; ++i) colors[i] = CycleRGB.getColor(i * step);
            colors.reverse();
            colors.fixed = true;

            // フラクタル計算クラス
            fractal_ = new Magnet1();
            fractal_.colors = colors;
            
            // ビューアクラス
            viewer_ = new Viewer(WIDTH, HEIGHT);
            addChild(viewer_);
            
            // マウス操作クラス
            mouseBehavior_ = new MouseBehavior(WIDTH, HEIGHT);
            addChild(mouseBehavior_);
            
            // フラクタル描画エンジンクラス
            engine_ = new Engine();
            engine_.viewer     = viewer_;
            engine_.controller = mouseBehavior_;
            engine_.calculator = fractal_;
            engine_.reset();
            
            // プリセットパラメータボタン
            buttons_ = new Buttons();
            buttons_.action = buttonHandler;
            addChild(buttons_);
            // リセットボタン
            var resetButton:PushButton = new PushButton(this, 0, buttons_.y + buttons_.height, "Reset", resetButtonHandler);
            resetButton.width  = 50;
            resetButton.height = 20;

            // スライダー
            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;
            textFieldReal_.y = HEIGHT - 15 * 2.5;
            addChild(textFieldReal_);
            textFieldImag_ = new TextField();
            textFieldImag_.defaultTextFormat = textFormat;
            textFieldImag_.autoSize = TextFieldAutoSize.LEFT;
            textFieldImag_.selectable = false;
            textFieldImag_.blendMode = BlendMode.INVERT;
            textFieldImag_.y = HEIGHT - 15 * 1.5;
            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(values:Vector.<Number>):void {
            hslider_.value = fractal_.c.real = values[0];
            vslider_.value = fractal_.c.imag = values[1];
            draw();
            textFieldReal_.text = "real : " + String(hslider_.value);
            textFieldImag_.text = "imag : " + String(vslider_.value);
        }
        // リセットボタン
        private function resetButtonHandler(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);
            buttons_.reset();
        }

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


//package {
    //import aquioux.math.Complex;
    import flash.geom.Rectangle;
    /**
     * Magnet Fractal Type I (Julia) 描画クラス
     * @author Aquioux(Yoshida, Akio)
     */
    /*public*/ class Magnet1 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 = -9;        // X軸最小値
        private const MIN_Y:Number = -9;        // Y軸最小値
        private const MAX_X:Number =  9;        // X軸最大値
        private const MAX_Y:Number =  9;        // Y軸最大値

        /**
         * 集合に該当する部分の色(一般的には色なし=黒)
         */
        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; }
        private var _c:Complex = new Complex(0, 0);
        
        
        // 発散チェックループ回数(_colors.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);    // julia
            return (r >= 0) ? _colors[r] : _color;
        }
        /**
         * 漸化式 z ← ((z^2 + c - 1) / (2 * z + c - 2))^2
         * @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--) {
                // 発散の評価
                var zRlSqr:Number = zRl * zRl;
                var zImSqr:Number = zIm * zIm;
                if (zRlSqr + zImSqr > 1024) break;
                var zTRl:Number = zRl - 1;
                if (zTRl * zTRl + zImSqr < 0.001) break;
                
                // 漸化式の実行
                // 分子
                var z1Rl:Number = zRlSqr - zImSqr + cRl - 1;
                var z1Im:Number = 2 * zRl * zIm + cIm;
                
                // 分母
                var z2Rl:Number = 2 * zRl + cRl - 2;
                var z2Im:Number = 2 * zIm + cIm;
                
                // 除算
                var z3Norm:Number = z2Rl * z2Rl + z2Im * z2Im;
                var z3Rl:Number = (z1Rl * z2Rl + z1Im * z2Im) / z3Norm;
                var z3Im:Number = (z1Im * z2Rl - z1Rl * z2Im) / z3Norm;

                // 2乗
                zRl = z3Rl * z3Rl - z3Im * z3Im;
                zIm = 2 * z3Rl * z3Im;
            }
            return i;
            // break で脱しなかった(発散しなかった)場合、while を回りきるので -1 になる
        }
    }
//}


//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 colors(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(MouseBehavior.MOUSE_MOVE, mouseMoving);
            _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.drawRect.x;
            var startY:int = _viewer.drawRect.y;
            var limitX:int = _viewer.drawRect.width  + startX;
            var limitY:int = _viewer.drawRect.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_);
        }

        // cntl + マウスムーブ時
        private function mouseMoving(e:Event):void {
            _viewer.mouseMoving(_controller.rect.x, _controller.rect.y);
        }
    }
//}


//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 {

        /**
         * カスタムイベント(cntl + マウスムーブ)
         */
        public static const MOUSE_MOVE:String = "mouseMoving";

        /**
         * ドラッグで決定された領域を示す Rectangle
         */ 
        public function get rect():Rectangle { return selectedRect_; }
        private var selectedRect_:Rectangle;


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

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

            // Rectangle 生成
            selectedRect_ = new Rectangle();
            
            // stage に登録されたときの処理
            addEventListener(Event.ADDED_TO_STAGE, addedHandler);
        }
        // stage に登録されたときの処理
        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, enableWidth_, enableHeight_);
            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 mouseDownHandler(e:MouseEvent):void {
            // 起点待避
            dragStartX_ = mouseX;
            dragStartY_ = mouseY;

            // マウスイベントハンドラ登録
            if (isCtrlDown_) {
                // 移動
                selectedRect_.width  = enableWidth_;
                selectedRect_.height = enableHeight_;
                stage.addEventListener(MouseEvent.MOUSE_MOVE, movingRectangle);
                stage.addEventListener(MouseEvent.MOUSE_UP,   moveupRectangle);
            } else {
                // 拡大
                stage.addEventListener(MouseEvent.MOUSE_MOVE, zoomingRectangle);
                stage.addEventListener(MouseEvent.MOUSE_UP,   zoomupRectangle);
            }
        }

        // 移動
        // マウスムーブ
        private function movingRectangle(e:MouseEvent):void {
            dragWidth_  = dragStartX_ - mouseX;
            dragHeight_ = dragStartY_ - mouseY;

            // selectedRect_ 調整
            selectedRect_.x = dragWidth_;
            selectedRect_.y = dragHeight_;

            // イベント送出
            mouseMoving();
        }
        // マウスアップ
        private function moveupRectangle(e:MouseEvent):void {
            // マウスイベントハンドラ解除
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, movingRectangle);
            stage.removeEventListener(MouseEvent.MOUSE_UP,   moveupRectangle);

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

        // 拡大
        // マウスムーブ
        private function zoomingRectangle(e:MouseEvent):void {
            // 選択領域のサイズ計算
            dragWidth_  = mouseX - dragStartX_;
            dragHeight_ = mouseY - dragStartY_;

            // 選択領域を正方形にする
            // 絶対値を求める
            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(dragStartX_, dragStartY_, dragWidth_, dragHeight_);
        }
        // マウスアップ
        private function zoomupRectangle(e:MouseEvent):void {
            // マウスイベントハンドラ解除
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, zoomingRectangle);
            stage.removeEventListener(MouseEvent.MOUSE_UP,   zoomupRectangle);
            
            // selectedRect_ 調整
            selectedRect_.x      = dragStartX_;
            selectedRect_.y      = dragStartY_;
            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 mouseWheelHandler(e:MouseEvent):void {
            // マウスホイールの移動量をスケールに変換
            var delta:Number = e.delta * 0.5;
            delta = (delta < 0) ? -delta : 1 / delta;
            
            // selectedRect_ を計算
            var nowWidth:Number  = enableWidth_  * delta;
            var nowHeight:Number = enableHeight_ * delta;
            selectedRect_.x      = mouseX - nowWidth  / 2;
            selectedRect_.y      = mouseY - nowHeight / 2;
            selectedRect_.width  = nowWidth;
            selectedRect_.height = nowHeight;
            
            // イベント送出
            update();
        }

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

        // イベント送出
        // マウスムーブ時
        private function mouseMoving():void {
            dispatchEvent(new Event(MOUSE_MOVE));
        }
        // マウスアップ時
        private function update():void {
            dispatchEvent(new Event(Event.CHANGE));
        }
    }
//}


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


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

        // cntl + マウスムーブ時のガイド表示物
        private var mouseMovingGuideBm_:Bitmap;
        private var mouseMovingGuideBmd_:BitmapData;
        private var isMouseMoving_:Boolean = false;    //     cntl + マウスムーブ中フラグ

        // start() 時、画面を暗くする
        private const FADE:ColorTransform = new ColorTransform(0.5, 0.5, 0.5);
        
        // 1度の draw() で描画する高さ
        private const DRAW_HEIGHT:int = 15;
        
        private const ZERO_POINT:Point = new Point(0, 0);
        private var point_:Point = new Point();
        private var rect_:Rectangle;
        

        /**
         * コンストラクタ
         * @param    w    描画幅
         * @param    h    描画高
         */
        public function Viewer(w:int, h:int) {
            // キャンバス部生成
            bitmapData_ = new BitmapData(w, h, false, 0x0);
            addChild(new Bitmap(bitmapData_));
            
            rect_ = bitmapData_.rect;

            // cntl + マウスムーブ時のガイド表示物
            mouseMovingGuideBmd_ = bitmapData_.clone();
            mouseMovingGuideBm_ = new Bitmap(mouseMovingGuideBmd_);
            mouseMovingGuideBm_.alpha = 0;
            addChild(mouseMovingGuideBm_);
            
            // BitmapData 描画のための Rectangle 生成
            _drawRect = new Rectangle(0, 0, w, DRAW_HEIGHT);
        }

        /**
         * cntl + マウスムーブ時の挙動
         * @param    posX    X軸移動量
         * @param    posY    Y軸移動量
         */
        public function mouseMoving(moveX:int, moveY:int):void {
            // cntl + マウスムーブ開始時の処理
            if (!isMouseMoving_) {
                // キャンバス bitmapData_
                bitmapData_.colorTransform(rect_, FADE);
                // cntl + マウスムーブ時のガイド表示物
                mouseMovingGuideBmd_.copyPixels(bitmapData_, rect_, ZERO_POINT);
                mouseMovingGuideBm_.alpha = 0.5;
                isMouseMoving_ = true;
            }
            
            // cntl + マウスムーブ中の処理
            mouseMovingGuideBm_.x = -moveX;
            mouseMovingGuideBm_.y = -moveY;
        }
        

        /**
         * 描画開始
         */
        public function start():void {
            // cntl + マウスムーブからのアップデートだったとき
            if (isMouseMoving_) {
                // キャンバス bitmapData_
                point_.x = mouseMovingGuideBm_.x;
                point_.y = mouseMovingGuideBm_.y;
                bitmapData_.lock();
                bitmapData_.copyPixels(mouseMovingGuideBmd_, rect_, point_);
                bitmapData_.unlock();
                // cntl + マウスムーブ時のガイド表示物の後処理
                mouseMovingGuideBm_.alpha = 0;
                mouseMovingGuideBm_.x = 0;
                mouseMovingGuideBm_.y = 0;
                isMouseMoving_ = false;
            } else {
                bitmapData_.colorTransform(rect_, FADE);
            }
            
            _drawRect.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(_drawRect, data);
            bitmapData_.unlock();
            // 終了判定
            _drawRect.y += DRAW_HEIGHT;
            if (_drawRect.y >= bitmapData_.width) removeEventListener(Event.ENTER_FRAME, update);
        }
    }
//}


//package {
    import com.bit101.components.PushButton;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.utils.Dictionary;
    /**
     * プリセットパラメータボタン
     * @author YOSHIDA, Akio (Aquioux)
     */
    /*public*/ class Buttons extends Sprite {
        /**
         * ボタンアクション(外部で定義した処理)
         */
        public function set action(value:Function):void { _action = value; }
        private var _action:Function;
        
        
        // エンジンのラベルとプロパティ値のペア
        private const PAIR:Array = [
            ["-1 + 0i",        Vector.<Number>([-1, 0])],
            ["-0.1 + 0i",    Vector.<Number>([-0.1, 0])],
            ["0 + 0i",        Vector.<Number>([0, 0])],
            ["1 + 0i",        Vector.<Number>([1, 0])],
            ["1.2 + 0i",    Vector.<Number>([1.2, 0])],
            ["1.6 + 0i",    Vector.<Number>([1.6, 0])],
            ["2 + 0i",        Vector.<Number>([2, 0])],
            ["2.5 + 0i",    Vector.<Number>([2.5, 0])],
            ["2.9 + 0i",    Vector.<Number>([2.9, 0])],
            ["3 + 0i",        Vector.<Number>([3, 0])],
            ["3.1 + 0i",    Vector.<Number>([3.1, 0])],
            ["4 + 0i",        Vector.<Number>([4, 0])],
        ];
        // ボタンの数
        private const NUM_OF_BUTTON_ROW:int     = 9;    // 1行
        private const NUM_OF_BUTTON_LASTROW:int = PAIR.length % NUM_OF_BUTTON_ROW;    // 最後の行
        private const NUM_OF_BUTTON_COL:int     = PAIR.length / NUM_OF_BUTTON_ROW >> 0;    // 列数
        
        // 前回押したボタン
        private var prevButton_:PushButton;

        // _action への引数を格納する Dictionary
        private var values:Dictionary;
        
        
        /**
         * インストラクタ
         */
        public function Buttons() {
            // ボタンの作成
            var buttonWidth:int  = 50;
            var buttonHeight:int = 20;
            var idx:int = 0;
            var yLimit:int = (NUM_OF_BUTTON_LASTROW == 0) ? NUM_OF_BUTTON_COL : NUM_OF_BUTTON_COL + 1;
            var xLimit:int = NUM_OF_BUTTON_ROW;
            values = new Dictionary();
            for (var y:int = 0; y < yLimit; y++) {
                if ((NUM_OF_BUTTON_LASTROW != 0) && (y == NUM_OF_BUTTON_COL)) xLimit = NUM_OF_BUTTON_LASTROW;
                for (var x:int = 0; x < xLimit; x++) {
                    var b:PushButton = new PushButton(this, buttonWidth * x, buttonHeight * y, PAIR[idx][0], handler);
                    b.width   = buttonWidth;
                    b.height  = buttonHeight;
                    values[b] = PAIR[idx][1];
                    idx++;
                }
            }
        }
        
        /**
         * リセット(前回押されたボタンを有効にする)
         */
        public function reset():void {
            if (prevButton_) prevButton_.enabled = true;
        }


        // ボタンハンドラ
        private function handler(e:Event):void {
            // 前回押されたボタンを有効にする
            reset();
            // 今回押されたボタンを無効にする
            var target:PushButton = PushButton(e.target);
            target.enabled = false;
            prevButton_ = target;
            // ボタンアクション
            _action(values[target]);
        }
    }
//}


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

Forked