ニュートン法によるフラクタル(4) ~3乗以外の冪乗で根による塗り分け~
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/6PdE
*/
// forked from Aquioux's ニュートン法によるフラクタル(2) ~根による塗り分け~
// forked from Aquioux's ニュートン法によるフラクタル(1)
package {
import com.bit101.components.PushButton;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Rectangle;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
/**
* ニュートン法によるフラクタル(4) ~3乗以外の冪乗で根による塗り分け~
* @see http://aquioux.net/blog/?p=2117
* @author Aquioux(Yoshida, Akio)
*/
public class Main extends Sprite {
// 集合でない部分の色
private var colors_:Vector.<uint>;
// 計算クラス
private var newton_:Newton;
// 走査クラス
private var scan_:Scan;
// 表示用 BitmapData
private var bmd_:BitmapData;
private var rect_:Rectangle; // bmd_ 用 Rectangle
// 前回押したボタン
private var prevButton_:PushButton;
public function Main():void {
// ステージサイズ
var sw:int = stage.stageWidth;
var sh:int = stage.stageHeight;
// Newton クラスのセットアップ
newton_ = new Newton();
// Scan クラスのセットアップ
scan_ = new Scan();
scan_.calculator = newton_;
scan_.setup(sw, sh);
rect_ = new Rectangle(0, 0, sw, sh);
// Viewer の作成
bmd_ = new BitmapData(sw, sh, false, 0x0);
addChild(new Bitmap(bmd_));
// ボタンの作成
var h:int = stage.stageHeight;
var buttonWidth:int = 117;
var button0:PushButton = new PushButton(this, 0, h - 20, "z^3 - 1 = 0", button0Handler);
var button1:PushButton = new PushButton(this, buttonWidth, h - 20, "z^4 - 1 = 0", button1Handler);
var button2:PushButton = new PushButton(this, buttonWidth * 2, h - 20, "z^5 - 1 = 0", button2Handler);
var button3:PushButton = new PushButton(this, buttonWidth * 3, h - 20, "z^6 - 1 = 0", button3Handler);
button0.width = buttonWidth;
button1.width = buttonWidth;
button2.width = buttonWidth;
button3.width = buttonWidth;
// 初回状態の表示
button3Handler(null);
button3.selected = true;
changeButton(button3);
}
// 描画
private function draw():void {
bmd_.lock();
bmd_.setVector(rect_, scan_.update());
bmd_.unlock();
}
// ボタンハンドラ
private function button0Handler(e:Event):void {
if (e) changeButton(PushButton(e.target));
newton_.expo = 3;
draw();
}
private function button1Handler(e:Event):void {
if (e) changeButton(PushButton(e.target));
newton_.expo = 4;
draw();
}
private function button2Handler(e:Event):void {
if (e) changeButton(PushButton(e.target));
newton_.expo = 5;
draw();
}
private function button3Handler(e:Event):void {
if (e) changeButton(PushButton(e.target));
newton_.expo = 6;
draw();
}
private function changeButton(currentButton:PushButton):void {
if (prevButton_) prevButton_.enabled = true;
currentButton.enabled = false;
prevButton_ = currentButton;
}
}
}
//package {
import flash.geom.Rectangle;
/**
* ニュートン法によるフラクタル描画クラス
* _scale = 1.0 のとき (-2, -2) ~ (2, 2) の領域を対象に計算する
* @author Aquioux(Yoshida, Akio)
*/
/*public*/ class Newton 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 = -2.0; // X軸最小値
private const MIN_Y:Number = -2.0; // Y軸最小値
private const MAX_X:Number = 2.0; // X軸最大値
private const MAX_Y:Number = 2.0; // Y軸最大値
/**
* どの解にも属さない部分の色(一般的には色なし=黒)
*/
private var _color:uint = 0x000000;
public function set color(value:uint):void { _color = value; }
/**
* 漸化式の z の冪乗の指数
*/
private var _expo:int;
public function set expo(value:int):void {
_expo = value;
solution_.fixed = false;
solution_.length = 0;
// ド・モアブルの定理から 1 の _expo 乗根を求め、配列に格納する
var angle:Number = 360 / _expo;
for (var i:int = 0; i < _expo; i++) {
var t:Number = angle * i * Math.PI / 180;
solution_.push(Math.cos(t));
solution_.push(Math.sin(t));
}
solution_.fixed = true;
degree_ = _expo * 6;
colorRange_ = 0xFF / degree_ >> 0;
}
// 解を一次元形式で格納する配列
private const solution_:Vector.<Number> = new Vector.<Number>();
// 収束確認用
private const LIMIT:Number = 1.0E-8;
// 収束チェックループ回数
private var degree_:int;
// 色調整率
private var colorRange_:int;
/**
* Scan クラスからの走査データを受け、計算をおこなう
* @param x X座標値
* @param y Y座標値
* @return 計算結果
*/
public function calculate(x:Number, y:Number):uint {
return formula(x, y);
}
/**
* 漸化式 z ← z - (z^n - 1) / (n * z^(n-1)) の評価
* @param zRl 複素数 z の実数部
* @param zIm 複素数 z の虚数部
* @return 収束評価値
* @private
*/
private function formula(zRl:Number, zIm:Number):uint {
// 漸化式の計算要素の複素数
var zExpoRl:Number; // z^(n-1) 実数部
var zExpoIm:Number; // z^(n-1) 虚数部
var z1Rl:Number; // z^n - 1 実数部
var z1Im:Number; // z^n - 1 虚数部
var z2Rl:Number; // n * z^(n-1) の実数部
var z2Im:Number; // n * z^(n-1) の虚数部
var z2norm:Number; // |z2|^2
var z3Rl:Number; // (z^n - 1) / (n * z^(n-1)) の実数部
var z3Im:Number; // (z^n - 1) / (n * z^(n-1)) の虚数部
// ド・モアブルの定理
var r1:Number; // 局座標形式複素数の距離(計算前)
var t1:Number; // 局座標形式複素数の偏角(計算前)
var r2:Number; // 局座標形式複素数の距離(計算後)
var t2:Number; // 局座標形式複素数の偏角(計算後)
// 評価値と解との距離
var dstRl:Number; // 評価値と解との距離(実数部)
var dstIm:Number; // 評価値と解との距離(虚数部)
var dst:Number; // 評価値と解との距離
// 色要素
var c:uint; // 色要素
var color:uint; // 色
var i:int = degree_;
while (i--) {
// ド・モアブルの定理による冪乗計算
// 複素数を局座標化
r1 = Math.sqrt(zRl * zRl + zIm * zIm);
t1 = Math.atan2(zIm, zRl);
// z^(n-1)
var expo:int = _expo - 1;
r2 = r1; // 距離
var j:int = expo;
while (--j) r2 *= r1;
t2 = t1 * expo; // 偏角
zExpoRl = Math.cos(t2) * r2;
zExpoIm = Math.sin(t2) * r2;
// z^n - 1
z1Rl = zExpoRl * zRl - zExpoIm * zIm - 1;
z1Im = zExpoRl * zIm + zExpoIm * zRl;
// n * z^(n-1)
z2Rl = zExpoRl * _expo;
z2Im = zExpoIm * _expo;
// (z^n - 1) / (n * z^(n-1))
z2norm = z2Rl * z2Rl + z2Im * z2Im;
z3Rl = (z1Rl * z2Rl + z1Im * z2Im) / z2norm;
z3Im = (z1Im * z2Rl - z1Rl * z2Im) / z2norm;
// z = z - (z^n - 1) / (n * z^(n-1))
zRl -= z3Rl;
zIm -= z3Im;
// 収束評価
c = i * colorRange_;
for (var k:int = 0; k < _expo; k++) {
dstRl = zRl - solution_[k * 2];
dstIm = zIm - solution_[k * 2 + 1];
dst = dstRl * dstRl + dstIm * dstIm;
if (dst < LIMIT) {
switch(k) {
case 0:
color = c << 16 | 0 << 8 | 0;
break;
case 1:
color = 0 << 16 | c << 8 | 0;
break;
case 2:
color = 0 << 16 | 0 << 8 | c;
break;
case 3:
color = 0 << 16 | c << 8 | c;
break;
case 4:
color = c << 16 | 0 << 8 | c;
break;
case 5:
color = c << 16 | c << 8 | 0;
break;
}
return color;
}
}
}
return _color;
}
}
//}
//package {
import flash.geom.Rectangle;
/**
* 計算クラスの interface
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ interface ICalculator {
function get rect():Rectangle;
function calculate(x:Number, y:Number):uint;
}
//}
//package {
import flash.geom.Rectangle;
/**
* 二次元走査クラス
* @author Aquioux(Yoshida, Akio)
*/
/*public*/ class Scan {
/**
* 計算クラス
*/
private var _calculator:ICalculator;
public function set calculator(value:ICalculator):void {
_calculator = value;
rect = _calculator.rect;
}
/**
* 描画領域(_scale = 1.0 における値)
*/
private var _rect:Rectangle;
public function set rect(value:Rectangle):void {
_rect = value;
}
/**
* 表示位置オフセットX座標値
*/
private var _offsetX:Number = 0.0;
public function set offsetX(value:Number):void {
_offsetX = value;
calcValue();
}
/**
* 表示位置オフセットY座標値
*/
private var _offsetY:Number = 0.0;
public function set offsetY(value:Number):void {
_offsetY = value;
calcValue();
}
/**
* スケール値
*/
private var _scale:Number = 1.0;
public function set scale(value:Number):void {
_scale = value;
calcValue();
}
// ----- その他変数 -----
// 計算加算値
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();
}
/**
* 複素数平面を走査し、その値を計算クラスの渡す
* @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 {
stepX_ = (_rect.width / _scale) / displayWidth_;
stepY_ = (_rect.height / _scale) / displayHeight_;
startX_ = (_rect.width * 0.5 + _rect.x + _offsetX) - displayWidth_ * 0.5 * stepX_;
startY_ = (_rect.height * 0.5 + _rect.y + _offsetY) - displayHeight_ * 0.5 * stepY_;
}
}
//}