Clifford Attractor (A)
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/zWmv
*/
package {
import caurina.transitions.Tweener;
import flash.display.Sprite;
import flash.events.Event;
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
/**
* Clifford Attractor Pattern A
* @see http://aquioux.net/blog/?p=1322
* @author Aquioux(YOSHIDA, Akio)
*/
public class Main extends Sprite {
private var viewer:Viewer; // ビューア
private var buttons:Buttons; // ボタン
private var sliders:Sliders; // スライダー
public function Main():void {
setup();
addEventListener(Event.ENTER_FRAME, update);
}
// セットアップ
private function setup():void {
// ステージサイズ
var w:int = stage.stageWidth;
var h:int = stage.stageHeight;
// アトラクターエンジン初期化
Attractor.setup();
// Attractor.paramRandom();
// ビューアの作成
viewer = new Viewer();
viewer.setup(w, h);
addChild(viewer);
// ボタンの作成
buttons = new Buttons();
buttons.setup();
buttons.action = drawByButton;
buttons.y = h - 40;
addChild(buttons);
// スライダーの作成
sliders = new Sliders();
sliders.setup();
sliders.action = drawBySlider;
sliders.reset();
addChild(sliders);
}
// アップデート
private function update(e:Event):void {
viewer.update(Attractor.update());
}
// ボタンに起因する描画の実行
// アトラクタの変数の値をトゥイーン
private function drawByButton(values:Vector.<Number>):void {
Tweener.addTween(Attractor, {
"a":values[0],
"b":values[1],
"c":values[2],
"d":values[3],
time:1.5,
transition:"easeOutCubic",
onStart:viewer.reset,
onUpdate:sliders.reset
});
}
// スライダーに起因する描画の実行
private function drawBySlider():void {
buttons.reset();
}
}
}
//package {
/**
* アトラクターエンジン(Clifford Attractor)
* @author Aquioux(YOSHIDA, Akio)
*/
/*public*/ class Attractor {
/**
* アトラクター計算に使用するパラメータ a
*/
static public function get a():Number { return _a; }
static public function set a(value:Number):void { _a = value; }
static private var _a:Number;
/**
* アトラクター計算に使用するパラメータ b
*/
static public function get b():Number { return _b; }
static public function set b(value:Number):void { _b = value; }
static private var _b:Number;
/**
* アトラクター計算に使用するパラメータ c
*/
static public function get c():Number { return _c; }
static public function set c(value:Number):void { _c = value; }
static private var _c:Number;
/**
* アトラクター計算に使用するパラメータ d
*/
static public function get d():Number { return _d; }
static public function set d(value:Number):void { _d = value; }
static private var _d:Number;
/**
* アトラクター計算に使用する各パラメータの最小値、最大値、既定値
*/
static public function get A_MIN():Number { return PARAMS[0]; }
static public function get A_MAX():Number { return PARAMS[1]; }
static public function get A_DEFAULT():Number { return PARAMS[2]; }
static public function get B_MIN():Number { return PARAMS[3]; }
static public function get B_MAX():Number { return PARAMS[4]; }
static public function get B_DEFAULT():Number { return PARAMS[5]; }
static public function get C_MIN():Number { return PARAMS[6]; }
static public function get C_MAX():Number { return PARAMS[7]; }
static public function get C_DEFAULT():Number { return PARAMS[8]; }
static public function get D_MIN():Number { return PARAMS[9]; }
static public function get D_MAX():Number { return PARAMS[10]; }
static public function get D_DEFAULT():Number { return PARAMS[11]; }
static private const PARAMS:Vector.<Number> = Vector.<Number>([
-3.0, 3.0, -1.4, // a
-3.0, 3.0, 1.6, // b
-2.0, 2.0, -2.0, // c
-2.0, 2.0, 0.7, // d
]);
/**
* パーティクル数
*/
static public function get numOfParticle():int { return numOfParticle_; }
static private var numOfParticle_:int = 25000;
// update() の返り値となる Vector
static private var data_:Vector.<Number>;
// 漸化式用変数
static private var x_:Number; // X座標値
static private var y_:Number; // Y座標値
/**
* セットアップ
*/
static public function setup():void {
// update() の返り値となる Vector
data_ = new Vector.<Number>(numOfParticle_ * 2, true);
// 各パラメータの値を初期化
paramDefault();
}
/**
* パラメータ a, b, c, d を既定値に戻す
*/
static public function paramDefault():void {
_a = A_DEFAULT;
_b = B_DEFAULT;
_c = C_DEFAULT;
_d = D_DEFAULT;
}
/**
* パラメータ a, b, c, d をランダムな値にする
*/
static public function paramRandom():void {
_a = Math.random() * (A_MAX - A_MIN) + A_MIN;
_b = Math.random() * (B_MAX - B_MIN) + B_MIN;
_c = Math.random() * (C_MAX - C_MIN) + C_MIN;
_d = Math.random() * (D_MAX - D_MIN) + D_MIN;
}
/**
* アトラクター計算
* @return 結果の座標を一次元配列で格納した Vector
*/
static public function update():Vector.<Number> {
x_ = Math.random() * 10 - 5;
y_ = Math.random() * 10 - 5;
if (Math.random() < 0.5 ) x_ = -x_;
if (Math.random() < 0.5 ) y_ = -y_;
var len:int = numOfParticle_ * 2;
for (var i:int = 0; i < len; i += 2) {
var xn:Number = Math.sin(_a * y_) + _c * Math.cos(_a * x_);
var yn:Number = Math.sin(_b * x_) + _d * Math.cos(_b * y_);
data_[i] = xn;
data_[i + 1] = yn;
x_ = xn;
y_ = yn;
}
return data_;
}
}
//}
//package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.filters.BitmapFilterQuality;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.geom.Rectangle;
/**
* ビューア
* @author Aquioux(YOSHIDA, Akio)
*/
/*public*/ class Viewer extends Bitmap {
/**
* 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 = 0.0; // 開始位置
private var add_:Number = 0.5; // start_ への増分
private var renge_:Number = 150 / 360; // 循環色相の範囲(角度)
private var dir_:int = 1; // グラデーションの方向
// BitmapData 関連
private var bufferBmd_:BitmapData; // バッファ
private var rect_:Rectangle; // ColorTransform, Blur 共用
private const ZERO_POINT:Point = new Point(0, 0);
// 表示オフセット
private var offsetX_:Number; // X座標オフセット
private var offsetY_:Number; // Y座標オフセット
private const OFFSET_SCALE:int = 50; // スケールオフセット
/**
* コンストラクタ
*/
public function Viewer() {
}
/**
* セットアップ
* @param sw ステージ幅
* @param sh ステージ高
*/
public function setup(sw:Number, sh:Number):void {
// BitmapData 関連
bufferBmd_ = new BitmapData(sw, sh, true, 0xFF000000);
bitmapData = bufferBmd_.clone();
rect_ = new Rectangle(0, 0, sw, sh);
// 各オフセット
offsetX_ = sw / 2;
offsetY_ = sh / 2;
// 描画色関連
reset();
// 描画色のアルファ値を設定
CycleRGB.alpha = 0xCC;
}
/**
* アップデート
* @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] * OFFSET_SCALE + offsetX_) >> 0;
var py:Number = (data[i + 1] * OFFSET_SCALE + offsetY_) >> 0;
var offsetColor:Number = (px + py * dir_) * renge_;
//var offsetColor:Number = (px * height / width - py * width / height) * renge_;
bufferBmd_.setPixel32(px, py, CycleRGB.getColor32(offsetColor + start_));
}
bufferBmd_.unlock();
// bitmapData の更新
bitmapData.lock();
bitmapData.colorTransform(rect_, _fade);
bitmapData.applyFilter(bitmapData, rect_, ZERO_POINT, _blur);
bitmapData.draw(bufferBmd_);
bitmapData.unlock();
}
/**
* 描画色用変数の再設定
*/
public function reset():void {
start_ = (Math.random() * 360) >> 0;
add_ = ((Math.random() * 100 + 10) >> 0) / 100;
renge_ = ((Math.random() * 180 + 30) >> 00) / 360;
dir_ = (Math.random() < 0.5) ? -1 : 1;
dir_ = (Math.random() < 0.3) ? dir_ : 0;
}
}
//}
//package {
import com.bit101.components.HUISlider;
import flash.display.Sprite;
import flash.events.Event;
/**
* コントロール用スライダー
* @author Aquioux(YOSHIDA, Akio)
*/
/*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 VALUES:Array = ["a:", "b:", "c:", "d:"];
// スライダー格納配列
private var sliders:Array = [];
/**
* コンストラクタ
*/
public function Sliders() {
}
/**
* セットアップ
*/
public function setup():void {
var params:Vector.<Number> = Vector.<Number>([
Attractor.A_MIN, Attractor.A_MAX, Attractor.a,
Attractor.B_MIN, Attractor.B_MAX, Attractor.b,
Attractor.C_MIN, Attractor.C_MAX, Attractor.c,
Attractor.D_MIN, Attractor.D_MAX, Attractor.d
]);
var idx:int = 0;
for (var y:int = 0; y < VALUES.length; y++) {
var slider:HUISlider = new HUISlider(this, 25, y * 15, VALUES[idx], handler);
slider.width = 430;
slider.labelPrecision = PRECISION;
slider.tick = TICK;
slider.setSliderParams(params[idx * 3], params[idx * 3 + 1], params[idx * 3 + 2]);
sliders[idx] = slider;
idx++;
}
}
/**
* リセット(Attractor 内の数値に伴ったスライダー値にリセット)
*/
public function reset():void {
sliders[0].value = Attractor.a;
sliders[1].value = Attractor.b;
sliders[2].value = Attractor.c;
sliders[3].value = Attractor.d;
}
// ハンドラ
private function handler(e:Event):void {
var target:HUISlider = HUISlider(e.target);
var label:String = target.label;
var value:Number = target.value;
if (label == VALUES[0]) Attractor.a = value;
if (label == VALUES[1]) Attractor.b = value;
if (label == VALUES[2]) Attractor.c = value;
if (label == VALUES[3]) Attractor.d = value;
_action();
}
}
//}
//package {
import com.bit101.components.PushButton;
import flash.display.Sprite;
import flash.events.Event;
/**
* コントロール用ボタン
* @author Aquioux(YOSHIDA, Akio)
*/
/*public*/ class Buttons extends Sprite {
/**
* ボタンアクション(外部で定義した処理)
*/
public function set action(value:Function):void { _action = value; }
private var _action:Function;
// 写像の名称と複素数の値
private const VALUES:Array = [
["No.1", Vector.<Number>([-2.167, -1.583, -0.609, -1.774]) ],
["No.2", Vector.<Number>([-1.03, -1.743, 1.844, 0.469]) ],
["No.3", Vector.<Number>([-1.5, -3.0, 1.5, 1.5]) ],
["No.4", Vector.<Number>([-1.4, 1.6, 1.0, 0.7]) ],
["No.5", Vector.<Number>([1.7, 1.7, 0.06, 1.2]) ],
["No.6", Vector.<Number>([1.3, 1.7, 0.5, 1.4]) ],
["No.7", Vector.<Number>([0.87, 1.29, -1.07, 1.55]) ],
["No.8", Vector.<Number>([1.7, 1.7, -1.2, 1.2]) ],
["No.9", Vector.<Number>([1.6, -0.6, -1.2, 1.6]) ],
["No.10", Vector.<Number>([1.1, -1.1, 1.0, 1.4]) ],
["No.11", Vector.<Number>([1.4, -1.4, 0.5, 0.8]) ],
["No.12", Vector.<Number>([0.7, -2.0, 1.6, 0.9]) ],
["No.13", Vector.<Number>([0.7, -2.879, 1.6, 0.9]) ],
["No.14", Vector.<Number>([3.0, -3.0, 2.0, -2.0]) ],
["No.15", Vector.<Number>([3.0, -3.0, 2.0, -0.793]) ],
["No.16", Vector.<Number>([3.0, -3.0, 2.0, 0.0]) ],
["No.17", Vector.<Number>([2.0, -2.0, 2.0, -0.135]) ],
["No.18", Vector.<Number>([2.828, -1.461, -0.444, -1.104]) ]
//["No.1", Vector.<Number>([-1.835, -1.743, 1.844, 0.469]) ],
//["No.5", Vector.<Number>([-1.96, 1.73, 1.21, -0.208]) ],
];
// 前回押したボタン
private var prevButton_:PushButton;
/**
* インストラクタ
*/
public function Buttons() {
}
/**
* セットアップ
*/
public function setup():void {
// ボタンの作成
var buttonWidth:int = 52;
var buttonHeight:int = 20;
var idx:int = 0;
for (var y:int = 0; y < 2; y++) {
for (var x:int = 0; x < 9; x++) {
var b:PushButton = new PushButton(this, buttonWidth * x, buttonHeight * y, VALUES[idx][0], handler);
b.name = String(idx);
idx++;
b.width = buttonWidth;
b.height = buttonHeight;
}
}
}
/**
* リセット(前回押したため無効になっているボタンを有効にする)
*/
public function reset():void {
if (prevButton_) prevButton_.enabled = true;
}
// ハンドラ
private function handler(e:Event):void {
var target:PushButton = PushButton(e.target);
var idx:int = int(target.name);
handlerAction(target);
_action(VALUES[idx][1]);
}
/**
* ボタンアクション(内部で完了する処理)
* 押されたボタンを押せなくし、前回押したボタンを押せるようにする
* @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 カラーのためのアルファ値
*/
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 DEGREE90:Number = PI / 2; // 90度(弧度法形式)
private static const DEGREE180:Number = PI; // 180度(弧度法形式)
/**
* 角度に応じた RGB を得る
* @param angle HSV のように角度(度数法)を指定
* @return 色(0xNNNNNN)
*/
public static function getColor(angle:Number):uint {
var radian:Number = angle * PI / 180;
var valR:uint = (Math.cos(radian) + 1) * 0xFF >> 1;
var valG:uint = (Math.cos(radian + DEGREE90) + 1) * 0xFF >> 1;
var valB:uint = (Math.cos(radian + DEGREE180) + 1) * 0xFF >> 1;
return valR << 16 | valG << 8 | valB;
}
/**
* 角度に応じた RGB を得る(32bit カラー)
* @param angle HSV のように角度(度数法)を指定
* @return 色(0xNNNNNNNN)
*/
public static function getColor32(angle:Number):uint {
return _alpha << 24 | getColor(angle);
}
}
//}