Burning Ship Julia
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/iM75
*/
package {
//import aquioux.display.colorUtil.CycleRGB;
//import aquioux.display.fractal.Buttons;
//import aquioux.display.fractal.Engine;
//import aquioux.display.fractal.InputBehavior;
//import aquioux.display.fractal.Sliders;
//import aquioux.display.fractal.Viewer;
import com.bit101.components.PushButton;
import flash.display.Sprite;
import flash.events.Event;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
/**
* Burning Ship Fractal の描画
* @see http://aquioux.net/blog/?p=3207
* @author YOSHIDA, Akio (Aquioux)
*/
public class Main extends Sprite {
// ビューアサイズ
private const WIDTH:int = 450;
private const HEIGHT:int = 450;
// 各連携クラス
private var calculator_:Julia; // フラクタル計算クラス
private var viewer_:Viewer; // ビューアクラス
private var inputBehavior_:InputBehavior; // ユーザー入力挙動クラス
private var engine_:Engine; // フラクタル描画エンジンクラス
private var sliders_:Sliders; // スライダー
private var complexButtons_:Buttons; // プリセットパラメータボタン(複素数c)
// ボタン構築用データ
// 複素数c
// 注意点:一番最初の値は、calclator の DEFAULT でなければならない
private const PRESET_COMPLEX_DATA:Array = [
["-0.05 + 0.28i", Vector.<Number>([-0.05, 0.28])],
["-0.324 + 0.13i", Vector.<Number>([-0.324, 0.13])],
["-0.2 + 0.275i", Vector.<Number>([-0.2, 0.275])],
["0.4 - 0.57i", Vector.<Number>([0.4, -0.57])],
["0.46 + 0.19i", Vector.<Number>([0.46, 0.19])],
["-0.78 - 1.1i", Vector.<Number>([-0.78, -1.1])]
];
// カラーリング
private const PRESET_COLOR_DATA:Array = [
["mono", Vector.<Number>([0])],
["color", Vector.<Number>([1])]
];
// カラーマップ配列
private var colorMapList:Vector.<Vector.<uint>>;
// コンストラクタ
public function Main() {
// カラーマップ生成
colorMapList = new Vector.<Vector.<uint>>();
// mono
var degree:int = 256;
var step:Number = 0xFF / degree;
var colorMap:Vector.<uint> = new Vector.<uint>(degree, true);
for (var i:int = 0; i < degree; i++) {
var c:uint = step * i;
colorMap[i] = c << 16 | c << 8 | c;
}
colorMapList.push(colorMap);
// color
degree = 90;
step = 360 / (degree + 1);
var start:int = Math.random() * 360 >> 0;
colorMap = new Vector.<uint>(degree, true);
for (i = 0; i < degree; i++) colorMap[i] = CycleRGB.getColor(i * step + start);
colorMapList.push(colorMap);
colorMapList.fixed = true;
// フラクタル計算クラス
calculator_ = new Julia();
calculator_.colorMap = colorMap;
// ビューアクラス
viewer_ = new Viewer(WIDTH, HEIGHT);
viewer_.heightOnceDraw = 5;
addChild(viewer_);
// ユーザー入力挙動クラス
inputBehavior_ = new InputBehavior(viewer_);
// フラクタル描画エンジンクラス
engine_ = new Engine();
engine_.viewer = viewer_;
engine_.controller = inputBehavior_;
engine_.calculator = calculator_;
// スライダー
// スライダー構築用データ
var sliderData:Array = [
[
WIDTH, // 幅
stage.stageHeight - HEIGHT, // 高
calculator_.DEFAULT_RECT.x, // 最小値
calculator_.DEFAULT_RECT.width + calculator_.DEFAULT_RECT.x, // 最大値
calculator_.DEFAULT_REAL_NUMBER // デフォルト値
], // 実数軸
[
stage.stageWidth - WIDTH,
HEIGHT,
calculator_.DEFAULT_RECT.y,
calculator_.DEFAULT_RECT.height + calculator_.DEFAULT_RECT.y,
calculator_.DEFAULT_IMAGINARY_NUMBER
] // 虚数軸
];
sliders_ = new Sliders(sliderData);
addChild(sliders_);
sliders_.realAction = realSliderHandler;
sliders_.imagAction = imagSliderHandler;
// プリセットパラメータボタン
// ボタンパラメータ
var buttonWidth:int = 75;
var buttonHeight:int = 20;
// 複素数c
complexButtons_ = new Buttons(buttonWidth, buttonHeight, WIDTH, PRESET_COMPLEX_DATA);
addChild(complexButtons_);
complexButtons_.action = presetComplexButtonsHandler;
// カラーリング
var colorButtons:Buttons = new Buttons(buttonWidth, buttonHeight, WIDTH, PRESET_COLOR_DATA);
colorButtons.y = buttonHeight;
addChild(colorButtons);
colorButtons.action = presetColorButtonsHandler;
// リセットボタン
// ズーム
var zoomResetButton:PushButton = new PushButton(this, 0, 0, "ZoomReset", zoomResetButtonHandler);
zoomResetButton.width = buttonWidth;
zoomResetButton.height = buttonHeight;
zoomResetButton.y = buttonHeight * 2;
// 複素数c
var complexResetButton:PushButton = new PushButton(this, buttonWidth, 0, "ComplexCReset", complexResetButtonHandler);
complexResetButton.width = buttonWidth;
complexResetButton.height = buttonHeight;
complexResetButton.x = buttonWidth;
complexResetButton.y = buttonHeight * 2;
// 初回の描画
// 複素数c初期化
calculator_.resetComplexC();
// スライダー値更新
sliders_.setRealValue(calculator_.c.real);
sliders_.setImagValue(calculator_.c.imag);
// プリセットパラメータボタンボタン初期選択
complexButtons_.selectedButton(0);
colorButtons.selectedButton(1);
// ズーム初期化
zoomResetButtonHandler(null);
}
// スライダーハンドラ
// 実数軸
private function realSliderHandler(val:Number):void {
complexButtons_.releaseSelectedButton()
calculator_.c.real = val;
engine_.start();
}
// 虚数軸
private function imagSliderHandler(val:Number):void {
complexButtons_.releaseSelectedButton()
calculator_.c.imag = val;
engine_.start();
}
// プリセットパラメータボタンハンドラ
// 複素数c
private function presetComplexButtonsHandler(values:Vector.<Number>):void {
calculator_.c.real = values[0];
calculator_.c.imag = values[1];
sliders_.setRealValue(calculator_.c.real);
sliders_.setImagValue(calculator_.c.imag);
engine_.start();
}
// カラーリング
private function presetColorButtonsHandler(values:Vector.<Number>):void {
calculator_.colorMap = colorMapList[values[0]];
engine_.start();
}
// リセットボタンハンドラ
// ズーム
private function zoomResetButtonHandler(event:Event):void {
engine_.reset();
engine_.start();
}
// 複素数c
private function complexResetButtonHandler(event:Event):void {
complexButtons_.selectedButton(0);
calculator_.resetComplexC();
sliders_.setRealValue(calculator_.c.real);
sliders_.setImagValue(calculator_.c.imag);
engine_.start();
}
}
}
//package {
//import aquioux.display.fractal.ICalculator;
//import aquioux.math.Complex;
import flash.geom.Rectangle;
/**
* Burning Ship Fractal 計算クラス
* base : Julia
* デフォルト時、(-2.0, -2.0) ~ (2.0, 2.0) の領域を計算する
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class Julia implements ICalculator {
/**
* 描画範囲
*/
public function get DEFAULT_RECT():Rectangle { return RECTANGLE; }
private const RECTANGLE:Rectangle = new Rectangle(START_X, START_Y, (END_X - START_X), (END_Y - START_Y));
// 個別の開始座標、終了座標
private const START_X:Number = -2.0; // 開始X座標値
private const START_Y:Number = -2.0; // 開始Y座標値
private const END_X:Number = 2.0; // 終了X座標値
private const END_Y:Number = 2.0; // 終了Y座標値
/**
* 漸化式の c
*/
public function get c():Complex { return _c; }
private var _c:Complex = new Complex(DEFAULT_REAL_NUMBER, DEFAULT_IMAGINARY_NUMBER);
// 複素数 c デフォルト
public const DEFAULT_REAL_NUMBER:Number = -0.05;
public const DEFAULT_IMAGINARY_NUMBER:Number = 0.28;
/**
* 集合に該当する部分の色(一般的には色なし=黒)
*/
public function set color(value:uint):void { _color = value; }
private var _color:uint = 0x000000;
/**
* 発散部分のカラーリングマップ
*/
public function set colorMap(value:Vector.<uint>):void {
_colorMap = value;
degree_ = value.length;
}
private var _colorMap:Vector.<uint>;
// 発散チェックループ回数(_colorMap.length の値)
private var degree_:int;
/**
* 複素数 c をデフォルトに戻す
*/
public function resetComplexC():void {
_c.real = DEFAULT_REAL_NUMBER;
_c.imag = DEFAULT_IMAGINARY_NUMBER;
}
/**
* 指定座標の計算をおこなう
* @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 ← (|Re[z]| + |Im[z]|i)^2 + c
* @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--) {
// 発散の評価(|z| > 2 = |z|^2 > 4)
var zRlSqr:Number = zRl * zRl;
var zImSqr:Number = zIm * zIm;
if (zRlSqr + zImSqr > 4) break;
// バーニングシップ・フラクタル用前処理
if (zRl < 0) zRl = -zRl;
if (zIm < 0) zIm = -zIm;
// 漸化式
var zNextRl:Number = zRlSqr - zImSqr + cRl;
zIm = zRl * zIm * 2 + cIm;
zRl = zNextRl;
}
return i;
// break で脱しなかった(発散しなかった)場合、while を回りきるので i は -1 になる
}
}
//}
//package aquioux.display.fractal {
import flash.geom.Rectangle;
/**
* フラクタル計算クラスの interface
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ interface ICalculator {
function get DEFAULT_RECT():Rectangle; // 描画範囲デフォルト値
function set color(value:uint):void; // 集合該当する部分の色
function set colorMap(value:Vector.<uint>):void; // 発散部分のカラーリングマップ
function calculate(x:Number, y:Number):uint; // 計算部
}
//}
//package aquioux.display.fractal {
import flash.events.Event;
import flash.geom.Rectangle;
/**
* フラクタル描画エンジンクラス
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class Engine {
/**
* フラクタル計算クラス
*/
public function set calculator(value:ICalculator):void { _calculator = value; }
private var _calculator:ICalculator;
/**
* ユーザー入力挙動クラス
*/
public function set controller(value:InputBehavior):void {
_controller = value;
_controller.addEventListener(InputBehavior.MOVING, moveHandler);
_controller.addEventListener(InputBehavior.ZOOMING, zoomHandler);
_controller.addEventListener(Event.CHANGE, start);
}
private var _controller:InputBehavior;
/**
* ビューアクラス
*/
public function set viewer(value:Viewer):void {
_viewer = value;
_viewer.addEventListener(Event.CHANGE, draw);
displayWidth_ = _viewer.width;
displayHeight_ = _viewer.height;
dataLen_ = displayWidth_ * _viewer.drawRect.height;
data_ = new Vector.<uint>(dataLen_, true);
}
private var _viewer:Viewer;
/**
* 描画計算領域を示す Rectangle
*/
public function set rect(value:Rectangle):void {
// 引数保持
_rect = value;
// 複素平面の走査開始座標を設定する
startX_ = _rect.x;
startY_ = _rect.y;
// 複素平面の走査加算値を計算する
stepX_ = _rect.width / (displayWidth_ - 1);
stepY_ = _rect.height / (displayHeight_ - 1);
}
private var _rect:Rectangle;
// ビューア描画用データ
private var data_:Vector.<uint>;
// 描画ピクセル数(data_.length)
private var dataLen_:int;
// ビューアサイズ
private var displayWidth_:int; // 幅
private var displayHeight_:int; // 高
// 複素平面の走査計算開始座標
private var startX_:Number; // 実数座標
private var startY_:Number; // 虚数座標
// 複素平面の走査計算加算値
private var stepX_:Number; // 実数軸
private var stepY_:Number; // 虚数軸
/**
* 描画計算領域のリセット
*/
public function reset():void {
rect = _calculator.DEFAULT_RECT;
}
/**
* 描画開始
* @param event イベント
*/
public function start(event:Event = null):void {
if (event) { // _controller の Event.CHANGE からの呼び出し
var r:Rectangle = _controller.rect.clone();
r.x = r.x * stepX_ + _rect.x;
r.y = r.y * stepY_ + _rect.y;
r.width *= stepX_;
r.height *= stepY_;
rect = r;
}
_viewer.start();
}
// 描画実行
private function draw(event:Event):void {
// 描画部分の計算
var drawOffset:int = _viewer.drawRect.y;
for (var i:int = 0; i < dataLen_; i++) {
var posX:int = i % displayWidth_;
var posY:int = drawOffset + i / displayWidth_ >> 0;
data_[i] = _calculator.calculate(posX * stepX_ + startX_, posY * stepY_ + startY_);
}
_viewer.draw(data_);
}
// 移動
private function moveHandler(e:Event):void {
_viewer.move(_controller.rect);
}
// 拡大・縮小
private function zoomHandler(e:Event):void {
_viewer.zoom(_controller.rect);
}
}
//}
//package aquioux.display.fractal {
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
/**
* ユーザー入力挙動クラス
* マウスドラッグ
* 矩形を描く、マウスボタンを離すとその矩形領域を全体として表示(拡大)
* cntl ボタン+マウスドラッグ
* 表示領域の移動(移動)
* マウスホイール
* マウスカーソル位置を中心に拡大(↑)・縮小(↓)
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class InputBehavior extends EventDispatcher {
/**
* 移動カスタムイベント
*/
public static const MOVING:String = "moving";
/**
* ズームカスタムイベント
*/
public static const ZOOMING:String = "zooming";
/**
* マウス挙動 Rectangle
*/
public function get rect():Rectangle { return _rect; }
private var _rect:Rectangle = new Rectangle();
// 矩形描画、移動に係る変数
private var startX_:Number; // ドラッグ開始X座標
private var startY_:Number; // ドラッグ開始Y座標
private var moveX_:Number; // ドラッグによる移動量(X軸方向)
private var moveY_:Number; // ドラッグによる移動量(Y軸方向)
private var isCtrlDown_:Boolean = false; // cntl キーダウン
// マウス入力挙動対象
private var target_:DisplayObject;
/**
* コンストラクタ
* @param target マウス入力挙動対象
*/
public function InputBehavior(target:DisplayObject) {
// マウス入力挙動対象
target_ = target;
// マウスイベントハンドラ登録
target_.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
target_.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
// キーボードイベントハンドラ登録
target_.stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
target_.stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
// マウスイベントハンドラ
private function mouseDownHandler(event:MouseEvent):void {
// 起点待避
startX_ = target_.mouseX;
startY_ = target_.mouseY;
// マウスイベントハンドラ登録
if (isCtrlDown_) { // 移動
_rect.width = target_.width;
_rect.height = target_.height;
target_.addEventListener(MouseEvent.MOUSE_MOVE, moving); // 移動中
target_.addEventListener(MouseEvent.MOUSE_UP, moved); // 移動終了
} else { // 拡大
target_.addEventListener(MouseEvent.MOUSE_MOVE, zooming); // 拡大中
target_.addEventListener(MouseEvent.MOUSE_UP, zoomed); // 拡大終了
}
}
// マウスイベントハンドラ
// 移動中
private function moving(event:MouseEvent):void {
// 移動量の計算
moveX_ = startX_ - target_.mouseX;
moveY_ = startY_ - target_.mouseY;
// _rect 調整
_rect.x = moveX_;
_rect.y = moveY_;
// イベント送出
dispatchEvent(new Event(MOVING));
}
// 移動終了
private function moved(event:MouseEvent):void {
// マウスイベントハンドラ解除
target_.removeEventListener(MouseEvent.MOUSE_MOVE, moving);
target_.removeEventListener(MouseEvent.MOUSE_UP, moved);
// イベント送出
update();
}
// 拡大中
private function zooming(event:MouseEvent):void {
// 移動量の計算
moveX_ = target_.mouseX - startX_;
moveY_ = target_.mouseY - startY_;
// 選択領域を正方形にする
// 絶対値を求める
var isMinusWidth:Boolean = moveX_ < 0 ? true : false;
var isMinusHeight:Boolean = moveY_ < 0 ? true : false;
if (isMinusWidth) moveX_ *= -1;
if (isMinusHeight) moveY_ *= -1;
// 幅・高のうち大きい方を正方形の辺とする
var edge:Number = moveX_ > moveY_ ? moveX_ : moveY_;
moveX_ = edge;
moveY_ = edge;
// _rect 調整
_rect.x = startX_;
_rect.y = startY_;
_rect.width = moveX_;
_rect.height = moveY_;
if (isMinusWidth) _rect.x -= _rect.width;
if (isMinusHeight) _rect.y -= _rect.height;
// イベント送出
dispatchEvent(new Event(ZOOMING));
}
// 拡大終了
private function zoomed(event:MouseEvent):void {
// マウスイベントハンドラ解除
target_.removeEventListener(MouseEvent.MOUSE_MOVE, zooming);
target_.removeEventListener(MouseEvent.MOUSE_UP, zoomed);
// イベント送出
update();
}
// マウスホイールイベントハンドラ
private function mouseWheelHandler(event:MouseEvent):void {
// マウスホイールの移動量をスケールに変換
var delta:Number = event.delta * 0.5;
delta = (delta < 0) ? -delta : 1 / delta;
// _rect を計算
var nowWidth:Number = target_.width * delta;
var nowHeight:Number = target_.height * delta;
_rect.x = target_.mouseX - nowWidth / 2;
_rect.y = target_.mouseY - nowHeight / 2;
_rect.width = nowWidth;
_rect.height = nowHeight;
// イベント送出
update();
}
// イベント送出(マウスアップ時)
private function update():void {
dispatchEvent(new Event(Event.CHANGE));
}
// キーボードイベントハンドラ
// キーダウン
private function keyDownHandler(event:KeyboardEvent):void {
if (event.ctrlKey) isCtrlDown_ = true;
}
// キーアップ
private function keyUpHandler(event:KeyboardEvent):void {
if (isCtrlDown_) isCtrlDown_ = false;
}
}
//}
//package aquioux.display.fractal {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Graphics;
import flash.display.Shape;
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 {
/**
* 1度の draw で描画する高さ(drawRect.y への加算値)
*/
public function get heightOnceDraw():int { return _heightOnceDraw; }
public function set heightOnceDraw(value:int):void {
_heightOnceDraw = value;
_drawRect.height = value;
}
private var _heightOnceDraw:int = 1;
/**
* キャンバス描画 Rectangle(1度に描画するエリアを示す Rectangle)
*/
public function get drawRect():Rectangle { return _drawRect; } // read only
private var _drawRect:Rectangle;
// キャンバス
private var canvas_:BitmapData;
private var canvasRect_:Rectangle;
// 移動ガイド
private var moveGuideBm_:Bitmap;
private var moveGuideBmd_:BitmapData;
// 移動ガイド表示中か否か
private var isMove_:Boolean = false;
// ズームガイド
private var zoomGuideGraphics_:Graphics;
// 描画のために使いまわす Point
private var destPoint_:Point = new Point();
// 画面を暗くする効果用
private const FADE:ColorTransform = new ColorTransform(0.75, 0.75, 0.75);
/**
* コンストラクタ
* @param w 描画幅
* @param h 描画高
*/
public function Viewer(w:int, h:int) {
// キャンバス生成
canvas_ = new BitmapData(w, h, false, 0x0);
addChild(new Bitmap(canvas_));
canvasRect_ = canvas_.rect;
// キャンバス描画 Rectangle 生成
_drawRect = new Rectangle(0, 0, w, _heightOnceDraw);
// 移動ガイド生成
moveGuideBmd_ = canvas_.clone();
moveGuideBm_ = new Bitmap(moveGuideBmd_);
moveGuideBm_.alpha = 0;
addChild(moveGuideBm_);
// ズームガイド生成
var zoomGuideShape:Shape = new Shape();
zoomGuideShape.blendMode = BlendMode.INVERT;
addChild(zoomGuideShape);
zoomGuideGraphics_ = zoomGuideShape.graphics;
}
/**
* 移動時の挙動
* @param rect 挙動を制御する Rectangle
*/
public function move(rect:Rectangle):void {
if (!isMove_) { // 移動開始時
// キャンバス全体を暗くする
canvas_.colorTransform(canvasRect_, FADE);
// ガイドの生成(キャンバスをそのまま写す)
destPoint_.x = 0;
destPoint_.y = 0;
moveGuideBmd_.copyPixels(canvas_, canvasRect_, destPoint_);
moveGuideBm_.alpha = 0.5;
isMove_ = true;
}
// 移動ガイドの移動
moveGuideBm_.x = -rect.x;
moveGuideBm_.y = -rect.y;
}
/**
* ズーム時の挙動
* @param rect 挙動を制御する Rectangle
*/
public function zoom(rect:Rectangle):void {
// ズーム用枠の表示
zoomGuideGraphics_.clear();
zoomGuideGraphics_.lineStyle(0, 0x0);
zoomGuideGraphics_.drawRect(rect.x, rect.y, rect.width, rect.height);
}
/**
* 描画開始
*/
public function start():void {
if (isMove_) { // 移動から呼び出されたとき
// destPoint を更新
destPoint_.x = moveGuideBm_.x;
destPoint_.y = moveGuideBm_.y;
// 移動ガイドをキャンバスに描画
canvas_.lock();
canvas_.copyPixels(moveGuideBmd_, canvasRect_, destPoint_);
canvas_.unlock();
// 移動ガイドの後処理
moveGuideBm_.alpha = 0;
moveGuideBm_.x = 0;
moveGuideBm_.y = 0;
isMove_ = false;
} else { // ズームから呼び出されたとき
// ズームガイドに表示された枠を消す
zoomGuideGraphics_.clear();
// キャンバスを暗くする
canvas_.colorTransform(canvasRect_, FADE);
}
// キャンバス描画 Rectangle の初期化
_drawRect.y = 0;
// ENTER_FRAME イベント開始
addEventListener(Event.ENTER_FRAME, function():void { dispatchEvent(new Event(Event.CHANGE)); } );
}
/**
* 描画実行
* @param data キャンバスを描くための Vector.<uint>
*/
public function draw(data:Vector.<uint>):void {
// 指定領域の更新
canvas_.lock();
canvas_.setVector(_drawRect, data);
canvas_.unlock();
// キャンバス描画 Rectangle の更新
_drawRect.y += _heightOnceDraw;
// 終了判定
if (_drawRect.y >= canvas_.width) removeEventListener(Event.ENTER_FRAME, arguments.callee);
}
}
//}
//package aquioux.display.fractal {
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 var prevButton_:PushButton;
// ボタンの配列
private var buttonList_:Vector.<PushButton>
// _action への引数を格納する Dictionary
private var valueList_:Dictionary;
/**
* コンストラクタ
* @param buttonWidth ひとつのボタンの幅
* @param buttonHeight ひとつのボタンの高さ
* @param stageWidth ボタンを配置する幅
* @param data ボタンが担う値
*/
public function Buttons(buttonWidth:int, buttonHeight:int, stageWidth:int, data:Array) {
var numOfButtonRow:int = stageWidth / buttonWidth >> 0; // 1行に配置されるボタンの数
var numOfButtonLastRow:int = data.length % numOfButtonRow; // 最後の行に配置されるボタンの数
var numOfButtonCol:int = data.length / numOfButtonRow >> 0; // ボタンが配置されるの列の数
var yLimit:int = (numOfButtonLastRow == 0) ? numOfButtonCol : numOfButtonCol + 1;
var xLimit:int = numOfButtonRow;
var idx:int = 0;
valueList_ = new Dictionary();
buttonList_ = new Vector.<PushButton>();
for (var y:int = 0; y < yLimit; y++) {
if ((numOfButtonLastRow != 0) && (y == numOfButtonCol)) xLimit = numOfButtonLastRow;
for (var x:int = 0; x < xLimit; x++) {
var b:PushButton = new PushButton(this, buttonWidth * x, buttonHeight * y, data[idx][0], handler);
b.width = buttonWidth;
b.height = buttonHeight;
valueList_[b] = data[idx][1];
buttonList_[idx] = b;
idx++;
}
}
}
/**
* 指定のボタンを、外部から押した状態にする
*/
public function selectedButton(idx:int):void {
if (idx < buttonList_.length) {
buttonAction(buttonList_[idx]);
} else {
throw new Error("指定したインデックス値がボタンの数より大きい値です。");
}
}
/**
* 現在選択されているボタンを、外部から解除する
*/
public function releaseSelectedButton():void {
if (prevButton_) prevButton_.enabled = true;
}
// ボタンハンドラ
private function handler(e:Event):void {
buttonAction(PushButton(e.target));
}
// ボタンを押したときの挙動
private function buttonAction(target:PushButton):void {
// 前回押されたボタンを有効にする
releaseSelectedButton();
// 今回押されたボタンを無効にする
target.enabled = false;
prevButton_ = target;
// 外部から指定したボタンアクションを実行する
_action(valueList_[target]);
}
}
//}
//package aquioux.display.fractal {
import com.bit101.components.HSlider;
import com.bit101.components.Label;
import com.bit101.components.VSlider;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.events.MouseEvent;
/**
* 複素数c用スライダー
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class Sliders extends Sprite {
/**
* 実数軸スライダーアクション(外部で定義する)
*/
public function set realAction(value:Function):void { _realAction = value; }
private var _realAction:Function;
/**
* 虚数軸スライダーアクション(外部で定義する)
*/
public function set imagAction(value:Function):void { _imagAction = value; }
private var _imagAction:Function;
// スライダー
private var hSlider_:HSlider; // 横
private var vSlider_:VSlider; // 縦
// ラベル
private var hLabel_:Label; // 横スライダー用
private var vLabel_:Label; // 縦スライダー用
// マウスダウンフラグ
private var isMouseDownH_:Boolean; // 横スライダー用
private var isMouseDownV_:Boolean; // 縦スライダー用
/**
* コンストラクタ
* @param values 各種パラメータ
*/
public function Sliders(values:Array) {
// 横スライダー(複素数cの実数部)
hSlider_ = new HSlider(this, 0, values[1][1]);
hSlider_.width = values[0][0];
hSlider_.height = values[0][1];
hSlider_.tick = 0.001;
hSlider_.setSliderParams(values[0][2], values[0][3], values[0][4]);
// 縦スライダー(複素数cの虚数部)
vSlider_ = new VSlider(this, values[0][0], 0);
vSlider_.width = values[1][0];
vSlider_.height = values[1][1];
vSlider_.tick = 0.001;
vSlider_.setSliderParams(values[1][2], values[1][3], values[1][4]);
// マウスハンドラ
// スライダーに直接ハンドラを設定すると ENTER_FRAME のタイミングで更新がかかり、
// 処理負荷が大きくなるため、マウスアップ時にスライダハンドラを実行させる
hSlider_.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHHandler);
vSlider_.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownVHandler);
hSlider_.addEventListener(MouseEvent.MOUSE_UP, mouseUpHHandler);
vSlider_.addEventListener(MouseEvent.MOUSE_UP, mouseUpVHandler);
// ラベルパラメータ
var labelHeight:int = 15;
var offset:int = 5;
// 横スライダー用ラベル
hLabel_ = new Label(this, 0, hSlider_.y - (labelHeight * 2 + offset));
hLabel_.blendMode = BlendMode.INVERT;
// 縦スライダー用ラベル
vLabel_ = new Label(this, 0, hSlider_.y - (labelHeight + offset));
vLabel_.blendMode = BlendMode.INVERT;
}
/**
* 外部からのスライダー値更新(実数軸)
* @param val 更新値
*/
public function setRealValue(val:Number):void {
hSlider_.value = val;
hlabelUpdate(val);
}
/**
* 外部からのスライダー値更新(虚数軸)
* @param val 更新値
*/
public function setImagValue(val:Number):void {
vSlider_.value = val;
vlabelUpdate(val);
}
// スライダーハンドラ
private function hsliderHandler():void {
var val:Number = hSlider_.value
// ラベル更新
hlabelUpdate(val);
// 外部から指定したスライダーアクションを実行する
_realAction(val);
}
private function vsliderHandler():void {
var val:Number = vSlider_.value
// ラベル更新
vlabelUpdate(val);
// 外部から指定したスライダーアクションを実行する
_imagAction(val);
}
// ラベル書換
private function hlabelUpdate(val:Number):void {
hLabel_.text = "real : " + String(val);
}
private function vlabelUpdate(val:Number):void {
vLabel_.text = "imag : " + String(val);
}
// マウスハンドラ
private function mouseDownHHandler(event:MouseEvent):void {
isMouseDownH_ = true;
isMouseDownV_ = false;
}
private function mouseDownVHandler(event:MouseEvent):void {
isMouseDownV_ = true;
isMouseDownH_ = false;
}
private function mouseUpHHandler(event:MouseEvent):void {
if (isMouseDownH_) hsliderHandler();
isMouseDownH_ = false;
}
private function mouseUpVHandler(event:MouseEvent):void {
if (isMouseDownV_) vsliderHandler();
isMouseDownV_ = false;
}
}
//}
//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);
}
}
//}