Lorenz Attractor Aquioux forked:0favorite:10lines:370license : MIT License modified : 2011-11-01 21:53:15 Embed Tweet package { import caurina.transitions.Tweener; import flash.display.Graphics; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")] /** * Lorenz Attractor * @see http://aquioux.net/blog/?p=2064 * @author YOSHIDA, Akio(Aquioux) */ public class Main extends Sprite { private var viewer_:Viewer; // ビューア private var buttons_:Buttons; // ボタン private var sliders_:Sliders; // スライダー private var mouseStage_:Sprite; // マウスドラッグで動かすため // マウスをダウンしているか否か private var isMouseDown_:Boolean = false; // MOUSW_MOVE による移動量 private var moveX_:Number = 0; private var moveY_:Number = 0; // 前回の MOUSW_MOVE 時のマウス座標 private var prevMouseX_:Number = 0; private var prevMouseY_:Number = 0; // 摩擦係数 private var friction:Number = 0.98; public function Main():void { setup(); addEventListener(Event.ENTER_FRAME, update); viewer_.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); viewer_.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); viewer_.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler); } // セットアップ private function setup():void { // ステージサイズ var w:int = stage.stageWidth; var h:int = stage.stageHeight; // エンジンの初期化 Engine.setup(); // プロジェクションクラス初期化 Projection.setup(Engine.data); // ビューアの作成 viewer_ = new Viewer(w, h); viewer_.buttonMode = true; addChild(viewer_); // ボタンの作成 buttons_ = new Buttons(); buttons_.action = drawByButton; buttons_.y = h - buttons_.height; addChild(buttons_); // スライダーの作成 sliders_ = new Sliders(); sliders_.action = drawBySlider; sliders_.reset(); addChild(sliders_); } // アップデート private function update(e:Event):void { if (isMouseDown_) { moveX_ = mouseX - prevMouseX_; moveY_ = mouseY - prevMouseY_; prevMouseX_ = mouseX; prevMouseY_ = mouseY; } else { moveX_ *= friction; moveY_ *= friction; } viewer_.update(Projection.update(moveX_, moveY_)); } // マウスハンドラ private function mouseDownHandler(e:MouseEvent):void { isMouseDown_ = true; } private function mouseUpHandler(e:MouseEvent):void { isMouseDown_ = false; } private function mouseMoveHandler(e:MouseEvent):void { if (!isMouseDown_) { prevMouseX_ = mouseX; prevMouseY_ = mouseY; } } // ボタンに起因する描画の実行 // エンジンの変数の値をトゥイーン private function drawByButton(values:Vector.<Number>):void { Tweener.addTween(Engine, { "p":values[0], "r":values[1], "b":values[2], time:1.5, transition:"easeOutCubic", onStart:viewer_.reset, onUpdate:sliders_.reset }); } // スライダーに起因する描画の実行 private function drawBySlider():void { buttons_.reset(); } } } //package { /** * エンジン(Lorenz Attractor) * @author YOSHIDA, Akio(Aquioux) */ /*public*/ class Engine { /** * 計算に使用するパラメータ p */ static public function get p():Number { return _p; } static public function set p(value:Number):void { _p = value; update(); // 再計算 } static private var _p:Number; /** * 計算に使用するパラメータ r */ static public function get r():Number { return _r; } static public function set r(value:Number):void { _r = value; update(); // 再計算 } static private var _r:Number; /** * 計算に使用するパラメータ b */ static public function get b():Number { return _b; } static public function set b(value:Number):void { _b = value; update(); // 再計算 } static private var _b:Number; /** * 計算に使用する各パラメータの最小値、最大値、既定値 */ static public function get P_MIN():Number { return PARAMS[0]; } static public function get P_MAX():Number { return PARAMS[1]; } static public function get P_DEFAULT():Number { return PARAMS[2]; } static public function get R_MIN():Number { return PARAMS[3]; } static public function get R_MAX():Number { return PARAMS[4]; } static public function get R_DEFAULT():Number { return PARAMS[5]; } static public function get B_MIN():Number { return PARAMS[6]; } static public function get B_MAX():Number { return PARAMS[7]; } static public function get B_DEFAULT():Number { return PARAMS[8]; } static private const PARAMS:Vector.<Number> = Vector.<Number>([ 0.0, 100, 10, // p 0.0, 100, 28, // r 0.0, 10, 8 / 3 // b ]); static private const DT:Number = 0.005; /** * 座標データ Vector */ static public function get data():Vector.<Number> { return _data; } static private var _data:Vector.<Number>; /** * パーティクル数 */ static public function get numOfParticle():int { return numOfParticle_; } static private var numOfParticle_:int = 15000; // 漸化式用変数 static private var x_:Number; // X座標値 static private var y_:Number; // Y座標値 static private var z_:Number; // Y座標値 // スケール static private const SCALE:Number = 0.05 / DT; /** * セットアップ */ static public function setup():void { _data = new Vector.<Number>(numOfParticle_ * 3, true); paramDefault(); } /** * 計算 * @return 結果の座標を一次元配列で格納した Vector */ static public function update():void { x_ = Math.random() * 20 - 10; y_ = Math.random() * 20 - 10; z_ = Math.random() * 20 - 10; var len:int = numOfParticle_ * 3; for (var i:int = 0; i < len; i += 3) { var xn:Number = (_p * (y_ - x_)) * DT; var yn:Number = (x_ * (_r - z_) - y_) * DT; var zn:Number = (x_ * y_ - _b * z_) * DT; x_ += xn; y_ += yn; z_ += zn; _data[i] = x_ * SCALE; _data[i + 1] = y_ * SCALE; _data[i + 2] = z_ * SCALE; } } /** * パラメータを既定値に戻す */ static public function paramDefault():void { _p = P_DEFAULT; _r = R_DEFAULT; _b = B_DEFAULT; update(); // 再計算 } /** * パラメータをランダムな値にする */ static public function paramRandom():void { _p = Math.random() * (P_MAX - P_MIN) + P_MIN; _r = Math.random() * (R_MAX - R_MIN) + R_MIN; _b = Math.random() * (B_MAX - B_MIN) + B_MIN; update(); // 再計算 } } //} //package { import flash.geom.Matrix3D; import flash.geom.PerspectiveProjection; import flash.geom.Utils3D; import flash.geom.Vector3D; /** * 三次元座標を二次元座標に投射する * @author YOSHIDA, Akio(Aquioux) */ /*public*/ class Projection { /** * X座標オフセット値 */ static public function get offsetX():Number { return _offsetX; } static public function set offsetX(value:Number):void { _offsetX = value; } static private var _offsetX:Number = 0; /** * Y座標オフセット値 */ static public function get offsetY():Number { return _offsetY; } static public function set offsetY(value:Number):void { _offsetY = value; } static private var _offsetY:Number = 0; /** * Z座標オフセット値 */ static public function get offsetZ():Number { return _offsetZ; } static public function set offsetZ(value:Number):void { _offsetZ = value; } static private var _offsetZ:Number = 500; // 座標 Vecotr static private var verts_:Vector.<Number>; // 三次元座標 static private var projectedVerts_:Vector.<Number>; // 二次元投射後 static private var uvts_:Vector.<Number>; // uvts // パースペクティブ・プロジェクション static private var projection_:PerspectiveProjection; static private var projectionMatrix3D_:Matrix3D; // 回転計算用マトリクス static private var matrix_:Matrix3D; // 移動量保持 static private var vx_:Number = 0; static private var vy_:Number = 0; /** * セットアップ * @param data 三次元座標データ */ static public function setup(verts:Vector.<Number>):void { // 座標 Vecotr verts_ = verts; var n:uint = verts_.length; projectedVerts_ = new Vector.<Number>(n * 2 / 3, true); uvts_ = new Vector.<Number>(n, true); // 使っていない // パースペクティブ・プロジェクション projection_ = new PerspectiveProjection(); projectionMatrix3D_ = projection_.toMatrix3D(); // 回転計算用マトリクス matrix_ = new Matrix3D(); } /** * アップデート * @param moveX 移動量(X軸方向) * @param moveY 移動量(Y軸方向) * @return 三次元座標を投射した二次元座標データ */ static public function update(moveX:Number, moveY:Number):Vector.<Number> { // 外部からの移動量 moveX、moveY を内部の移動量変数 vx_、vy_ に加算 vx_ += moveX; vy_ -= moveY; // マトリクス計算 matrix_.identity(); matrix_.appendRotation(vy_, Vector3D.X_AXIS); matrix_.appendRotation(vx_, Vector3D.Y_AXIS); matrix_.appendTranslation(_offsetX, _offsetY, _offsetZ); matrix_.append(projectionMatrix3D_); // 座標データに回転を適用 Utils3D.projectVectors(matrix_, verts_, projectedVerts_, uvts_); return projectedVerts_; } } //} //package { //import aquioux.display.colorUtil.CycleRGB; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.filters.BitmapFilterQuality; import flash.filters.BlurFilter; import flash.geom.ColorTransform; import flash.geom.Point; import flash.geom.Rectangle; /** * ビューア * @author YOSHIDA, Akio(Aquioux) */ /*public*/ class Viewer extends Sprite { /** * ColorTransform によるフェードアウトのための定義 */ public function set fade(value:ColorTransform):void { _fade = value; } private var _fade:ColorTransform = new ColorTransform(0.95, 0.95, 0.95); /** * Blur filter によるフェードアウトのための定義 */ public function set blur(value:BlurFilter):void { _blur = value; } private var _blur:BlurFilter = new BlurFilter(8, 8, BitmapFilterQuality.HIGH); // 描画色用の変数 private var start_:Number; // 開始位置 private var add_:Number; // start_ への増分 private var renge_:Number; // 循環色相の範囲(角度) // BitmapData 関連 private var bmd_:BitmapData; // 表示 BitmapData private var bufferBmd_:BitmapData; // バッファ private var rect_:Rectangle; // ColorTransform, Blur 共用 private const ZERO_POINT:Point = new Point(0, 0); // 表示オフセット private var sw_:int; // ステージ幅 private var sh_:int; // ステージ高 private var offsetX_:Number; // X座標オフセット private var offsetY_:Number; // Y座標オフセット /** * コンストラクタ * @param sw ステージ幅 * @param sh ステージ高 */ public function Viewer(sw:int, sh:int) { // ステージサイズ sw_ = sw; sh_ = sh; // BitmapData 関連 bufferBmd_ = new BitmapData(sw, sh, true, 0xFF000000); bmd_ = bufferBmd_.clone(); rect_ = new Rectangle(0, 0, sw, sh); addChild(new Bitmap(bmd_)); // 各オフセット offsetX_ = sw / 2; offsetY_ = sh / 2; // 描画色関連 CycleRGB.alpha = 0xCC; start_ = Math.random() * 360 >> 0; reset(); } /** * アップデート * @param data 描画座標データ */ public function update(data:Vector.<Number>):void { // bufferBmd_ の更新 bufferBmd_.lock(); bufferBmd_.fillRect(bufferBmd_.rect, 0x00000000); var len:uint = data.length; start_ += add_; for (var i:int = 0; i < len; i += 2) { var px:Number = (data[i] + offsetX_) >> 0; var py:Number = (data[i + 1] + offsetY_) >> 0; var offsetColorX:Number = (px > offsetX_) ? px - sw_ : px; var offsetColorY:Number = (py > offsetY_) ? py - sh_ : py; var offsetColor:Number = offsetColorX * offsetColorY / renge_; if (offsetColor < 0) offsetColor = -offsetColor; bufferBmd_.setPixel32(px, py, CycleRGB.getColor32(offsetColor + start_)); } bufferBmd_.unlock(); // bmd_ の更新 bmd_.lock(); bmd_.colorTransform(rect_, _fade); bmd_.applyFilter(bmd_, rect_, ZERO_POINT, _blur); bmd_.draw(bufferBmd_); bmd_.unlock(); } /** * 描画色用変数の再設定 */ public function reset():void { add_ = ((Math.random() * 50 >> 0) + 50) / 100; renge_ = ((Math.random() * 150 >> 0) + 120); } } //} //package { import com.bit101.components.HUISlider; import com.bit101.components.Slider; import flash.display.Sprite; import flash.events.Event; import flash.utils.Dictionary; /** * コントロール用スライダー * @author YOSHIDA, Akio(Aquioux) */ /*public*/ class Sliders extends Sprite { /** * スライダーアクション(外部で定義した処理) */ public function set action(value:Function):void { _action = value; } private var _action:Function; // スライダーの値を小数第何位まで有効にするか private const PRECISION:int = 3; private const TICK:Number = 1 / Math.pow(10, PRECISION); // スライダーに対応させるエンジン・プロパティ private const PROPS:Array = ["p", "r", "b"]; private const DELIMITER:String = ":"; // スライダー格納配列 private var sliders:Dictionary; /** * コンストラクタ */ public function Sliders() { var params:Vector.<Number> = Vector.<Number>([ Engine.P_MIN, Engine.P_MAX, Engine.p, Engine.R_MIN, Engine.R_MAX, Engine.r, Engine.B_MIN, Engine.B_MAX, Engine.b ]); sliders = new Dictionary(); for (var i:int = 0; i < PROPS.length; i++) { var prop:String = PROPS[i]; var slider:HUISlider = new HUISlider(this, 25, i * 15, prop + DELIMITER, handler); slider.width = 430; slider.labelPrecision = PRECISION; slider.tick = TICK; slider.setSliderParams(params[i * 3], params[i * 3 + 1], params[i * 3 + 2]); sliders[prop] = slider; } } /** * リセット(Engine 内の数値に伴ったスライダー値にリセット) */ public function reset():void { for each (var prop:String in PROPS) sliders[prop].value = Engine[prop]; } // ハンドラ private function handler(e:Event):void { var target:HUISlider = HUISlider(e.target); var prop:String = target.label.split(DELIMITER)[0]; Engine[prop] = target.value; _action(); } } //} //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 = [ ["Default", Vector.<Number>([10, 28, 8 / 3])], ["No.1", Vector.<Number>([7.4, 28, 5])], ["No.2", Vector.<Number>([4.6, 50, 4.555])], ["No.3", Vector.<Number>([6, 27, 5])], ["No.4", Vector.<Number>([7.5, 28, 4.8])], ["No.5", Vector.<Number>([4.195, 50, 0])], ["No.6", Vector.<Number>([0.345, 75, 0])], ["No.7", Vector.<Number>([80, 38.5, 1.5])], ["No.8", Vector.<Number>([0.575, 100, 0.215])] ]; private var values:Dictionary; // 前回押したボタン private var prevButton_:PushButton; /** * インストラクタ */ public function Buttons() { // ボタンの作成 var buttonWidth:int = 52; var buttonHeight:int = 20; var idx:int = 0; values = new Dictionary(); for (var x:int = 0; x < PAIR.length; 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 { var target:PushButton = PushButton(e.target); handlerAction(target); _action(values[target]); } /** * ボタンアクション(内部で完了する処理) * 押されたボタンを押せなくし、前回押したボタンを押せるようにする * @param target 押されたボタン * @private */ private function handlerAction(target:PushButton):void { reset(); target.enabled = false; prevButton_ = 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); } } //} Code Fullscreen Preview Fullscreen qwertyui atakanckilic.. ProjectNya bradsedito Dan0 kleinschmidt.. snowsunny novita001 gaziya hidrodixtion.. : awesomemath awesome chaos math カオス ストレンジアトラクタ target Dictionary mouseY mouseX Vector alpha BlurFilter clone addChild buttonMode split Rectangle Math.cos Math.pow Tweener.addTween Event width Math.PI height time