flash on 2010-4-2
----------------------------------------------------------------
2010年4月2日
元ネタは 「非線形離散力学系の不変集合」
http://wonderfl.net/code/50faf4e208072086db5d28509cfbfdc53ec1c716
です. これを matacat さんが fork した
http://wonderfl.net/code/28316e37eaa162048e9bfbe0a211a46a449c1728
での改良を取り入れて, こちらでも改良版をつくりました
ただし fork ではなくスクラッチから書きなおしています
matacatさん, ありがとうございます♪
〈変更点〉
1: 画面のリサイズに反応して縮尺を再設定し自動的に再スタートする
2: 乱数をやめ, 写像のヤコビアンの固有ベクトルの方向を狙って出発点をとる
3: 前半で青軌道, 後半で赤軌道が派手に変化するように固有ベクトルの符号を決めた
固有ベクトルの計算をしないといけないので写像の変更は少し厄介です. ですから
今回はもう, 写像を (drawOrbitメソッドに) ハードコーディングしちゃいました
matacat さんの描画ルーチン改良は効果バツグンですが, コードに対するわたくしの
理解がいまいち及ばないので, 今回は残念ながらマージを見おくります
----------------------------------------------------------------
/**
* Copyright tenasaku ( http://wonderfl.net/user/tenasaku )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/duut
*/
/* ----------------------------------------------------------------
2010年4月2日
元ネタは 「非線形離散力学系の不変集合」
http://wonderfl.net/code/50faf4e208072086db5d28509cfbfdc53ec1c716
です. これを matacat さんが fork した
http://wonderfl.net/code/28316e37eaa162048e9bfbe0a211a46a449c1728
での改良を取り入れて, こちらでも改良版をつくりました
ただし fork ではなくスクラッチから書きなおしています
matacatさん, ありがとうございます♪
〈変更点〉
1: 画面のリサイズに反応して縮尺を再設定し自動的に再スタートする
2: 乱数をやめ, 写像のヤコビアンの固有ベクトルの方向を狙って出発点をとる
3: 前半で青軌道, 後半で赤軌道が派手に変化するように固有ベクトルの符号を決めた
固有ベクトルの計算をしないといけないので写像の変更は少し厄介です. ですから
今回はもう, 写像を (drawOrbitメソッドに) ハードコーディングしちゃいました
matacat さんの描画ルーチン改良は効果バツグンですが, コードに対するわたくしの
理解がいまいち及ばないので, 今回は残念ながらマージを見おくります
----------------------------------------------------------------
*/
package {
import flash.display.*;
import flash.events.*;
import flash.text.*;
public class Main extends Sprite {
// 反復計算のパラメータ
private const EPSILON:Number = 1.0/64; // 出発点をとる原点の近傍のサイズ
private const ITERATION:int = 200; // 各点の軌道をどこまで追うか
private const LASTFRAME:int = 8000; // 出発点の個数 (順軌道, 逆軌道それぞれ)
// 論理画面のパラメータ
private const LOGICAL_WINDOW_HEIGHT:Number = 6;
private const LOGICAL_WINDOW_TOP:Number = 1.2;
private const LOGICAL_WINDOW_RIGHT:Number = 2.4;
// ヤコビアンの二つの固有値
private const LAMBDA1:Number = 2+Math.sqrt(5);
private const LAMBDA2:Number = 2-Math.sqrt(5);
// ヤコビアンの二本の固有ベクトル
private const E1X:Number = 1/Math.sqrt(1+LAMBDA2*LAMBDA2);
private const E1Y:Number = LAMBDA2*E1X;
private const E2X:Number = -1/Math.sqrt(1+LAMBDA1*LAMBDA1);
private const E2Y:Number = LAMBDA1*E2X;
private var screen:Bitmap;
private var frameCount:Number;
private var monitor:TextField = new TextField();
private var resolution:Number;
private var cX:Number,cY:Number;
// 論理座標を指定してピクセルに色をつける
private function plot(p:Number,q:Number,cc:uint):void {
var X:Number = cX + resolution*p;
var Y:Number = cY - resolution*q;
if ((X>=0)&&(X<stage.stageWidth)&&(Y>=0)&&(Y<stage.stageHeight)) {
screen.bitmapData.setPixel(X,Y,cc);
}
}
// ウィンドウの論理座標上の縦サイズと右上端の論理座標から
// 論理座標原点の物理座標と換算比を算出する
private function setScreenDimension():void {
resolution = stage.stageHeight/LOGICAL_WINDOW_HEIGHT;
cY = LOGICAL_WINDOW_TOP*resolution;
cX = stage.stageWidth - LOGICAL_WINDOW_RIGHT*resolution;
}
// 出発点を指定し、写像の順方向または逆方向の軌道に点を打つ
// 引数 direction > 0 なら 順方向、そうでなければ逆方向
private function drawOrbit(initialX:Number,initialY:Number,direction:int):void {
var i:int;
var X:Number = initialX;
var Y:Number = initialY;
screen.bitmapData.lock();
if (direction > 0) { // 順方向の軌道
for ( i = 0 ; i < ITERATION ; ++i ) {
plot(X,Y,0xff0000);
var X1:Number = 4*X*(1-X) - Y;
Y = -X;
X = X1;
}
} else { // 逆方向の軌道
for ( i = 0 ; i < ITERATION ; ++i ) {
plot(X,Y,0x0000ff);
var oldX:Number = X;
X = -Y;
Y = -4*Y*(1+Y)-oldX;
}
}
screen.bitmapData.unlock();
}
// ステージがサイズ変更されたら再出発
private function onResize(e:Event):void {
stage.removeEventListener(Event.ENTER_FRAME, monitorFade);
stage.removeEventListener(Event.ENTER_FRAME, atEveryFrame);
screen.bitmapData.dispose();
screen.bitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
setScreenDimension();
monitorInit();
startAnimation();
}
// 各フレームごとに、赤軌道と青軌道の出発点をひとつずつ選び軌道を描画させる
private function atEveryFrame(e:Event):void {
var t:Number = (frameCount/LASTFRAME*2 - 1.0)*EPSILON
if ( t < EPSILON ) {
monitor.text = "frame "+String(frameCount)+" of "+String(LASTFRAME);
// 両固有値の固有ベクトル方向に出発点を選ぶ
drawOrbit(E1X*t,E1Y*t,1);
drawOrbit(E2X*t,E2Y*t,-1);
++frameCount;
} else { // 描画完了. 停止処理
monitor.text = "Done!";
stage.frameRate = 24; // 普通の速さにする
stage.removeEventListener(Event.ENTER_FRAME, atEveryFrame);
// 次の処理 (モニタのフェードアウト) へ引きつぐ
stage.addEventListener(Event.ENTER_FRAME, monitorFade);
}
}
// フレームカウンタをリセット, フレームレート設定, アニメーション開始
private function startAnimation():void {
frameCount = 0;
stage.frameRate = 120;
stage.addEventListener(Event.ENTER_FRAME, atEveryFrame);
}
// モニターをフェードアウトさせてアニメーション終了
private function monitorFade(e:Event):void {
if ( monitor.alpha > 0.01 ) {
monitor.alpha *= 0.99;
} else {
stage.removeEventListener(Event.ENTER_FRAME, monitorFade);
}
}
// モニターを(再)初期化
private function monitorInit():void {
var tfmt:TextFormat = new TextFormat();
with (monitor) {
with (tfmt) {
font = null;
size = 16;
color = 0x000090;
leftMargin = 2;
rightMargin = 2;
}
monitor.autoSize = TextFieldAutoSize.LEFT;
defaultTextFormat = tfmt;
text = " Started.";
background = true;
backgroundColor = 0xffffff;
alpha = 0.7;
// width = 160;
// height = 60;
}
monitor.y = stage.stageHeight-monitor.height;
}
private function initialize(e:Event):void {
this.removeEventListener(Event.ADDED_TO_STAGE, initialize);
// 画面の初期設定
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
screen = new Bitmap(new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000));
this.addChild(screen);
setScreenDimension();
monitorInit();
this.addChild(monitor);
stage.addEventListener(Event.RESIZE, onResize);
// アニメーション開始
startAnimation();
}
// The Main constructor simply calles initialize() function.
public function Main():void {
if ( stage != null ) {
initialize(null);
} else {
this.addEventListener(Event.ADDED_TO_STAGE, initialize);
}
}
} // end of class Main
} // end of package