逆写像によるジュリア集合の描画
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/wQhK
*/
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")]
/**
* 逆写像によるジュリア集合の描画
* @author Aquioux(Yoshida, Akio)
* @see http://aquioux.net/blog/?p=1874
*/
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 = 18;
// 漸化式 の z
private var Z:Complex = MathComplex.COMPLEX_0;
// 漸化式 の c
private var c_:Complex = new Complex( -0.37, 0.61);
// ビューア
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);
}
}
}
//}
//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));
}
}
//}