forked from: forked from: 反応拡散系のシミュレーション
Simulation of reaction-diffusion system
Explanations: http://game.g.hatena.ne.jp/Nao_u/20100111
added wanderer : http://wonderfl.net/c/ubO1
/**
* Copyright aobyrne ( http://wonderfl.net/user/aobyrne )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/6YjR
*/
// forked from tmrtks's forked from: 反応拡散系のシミュレーション
// forked from Nao_u's 波紋+環境マップ
// forked from Nao_u's テクスチャ生成実験
//
// 反応拡散系のシミュレーション
//
// クリックで発生
// カーソルキー上下でパラメータ変更
// r でリセット
// i でランダムな初期化
//
// 解説など:http://game.g.hatena.ne.jp/Nao_u/20100111
//
package {
import flash.display.Sprite;
import flash.events.*;
import flash.utils.Timer;
[SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="30")]
public class ReactionDiffusionWanderer extends Sprite {
public function ReactionDiffusionWanderer() {
Main = this;
initialize();
stage.addEventListener(Event.ENTER_FRAME,update);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyCheckDown);
stage.addEventListener(MouseEvent.MOUSE_UP, MouseCheckUp);
stage.addEventListener(MouseEvent.MOUSE_DOWN, MouseCheckDown);
var timer:Timer = new Timer(100);
timer.addEventListener(TimerEvent.TIMER, doTimer);
timer.start();
}
private function doTimer(e:TimerEvent):void
{
var timer:Timer = e.target as Timer;
if (timer.currentCount%200==0)
{
MouseData = MouseData==3?2:3;
}
if (timer.currentCount%401==0)
{
No++;
//ResetCnt++;
//RandomInit++;
MouseData = 3;
setFTDv(No);
}
}
}
}
import flash.display.*;
import flash.events.*
import flash.text.TextField;
import flash.geom.*;
import flash.utils.getTimer;
import flash.ui.Keyboard;
import flash.net.*;
var Main:Sprite;
var SCREEN_W:Number = 465;
var SCREEN_H:Number = 465;
var Text:TextField
var Tex:ProcTex;
var BITMAP_W:int = 465/2;
var BITMAP_H:int = 465/2;
var TexData: BitmapData;
var loaderA:Loader;
var loaderB:Loader;
var bLoad:Boolean = false;
var ClickCnt:int = 0;
var F:Number = 0.030;
var K:Number = 0.085;
var Dv:Number = 0.350;
var wanderer:WanderingTarget = new WanderingTarget;
function initialize():void {
wanderer.position = new Vector3D(465 * 0.5, 465 * 0.5);
wanderer.boundsCentre = new Vector3D(465 * 0.5, 465 * 0.5);
wanderer.boundsRadius = 465 * 0.5;
MouseData = 3;
Tex = new ProcTex( BITMAP_W, BITMAP_H );
Tex.Bmp.x = 0;
Tex.Bmp.y = 0;
Text = new TextField();
Text.text = "----";
Text.autoSize = "left";
Text.textColor = 0xFFFFFF;
Main.addChild(Text);
bLoad = true;
setFTDv(No);
}
var No:int = 0;
var NoMax:int = 9;
var ResetCnt:int = 0;
var RandomInit:int = 0;
function keyCheckDown(event:KeyboardEvent):void {
switch (event.keyCode){
case Keyboard.UP: No++; break;
case Keyboard.DOWN: No--; break;
case 82: ResetCnt++; break;
case 73: RandomInit++; break;
}
if( No < 0 ) No = NoMax-1;
if( No >= NoMax ) No = 0;
setFTDv(No);
}
var Cr:int = 0;
var Cg:int = 0;
var Str:String = "";
function setFTDv( No:int ):void{
switch(No){
case 0:
Str = "標準";
F = 0.030;
K = 0.085;
Dv = 0.350;
Cr = 125;
Cg = 125;
break;
case 1:
Str = "細胞分裂";
F = 0.0095;
K = 0.065;
Dv = 0.300;
Cr = 255;
Cg = 128;
break;
case 2:
Str = "BZ反応風";
F = 0.014;
K = 0.054;
Dv = 0.450;
Cr = 128;
Cg = 128;
break;
case 3:
Str = "たくさんの玉が消滅しながら飛び交う";
F = 0.01;
K = 0.06;
Dv = 0.45;
Cr = 255;
Cg = 0;
break;
case 4:
Str = "変形しながら拡大1";
F = 0.046;
K = 0.107;
Dv = 0.45;
Cr = 120;
Cg = 135;
break;
case 5:
Str = "変形しながら拡大2";
F = 0.03;
K = 0.085;
Dv = 0.55;
Cr = 220;
Cg = 135;
break;
case 6:
Str = "不定形・迷路";
F = 0.04;
K = 0.10;
Dv = 0.45;
Cr = 35;
Cg = 120;
break;
case 7:
Str = "粗い網目";
F = 0.035;
K = 0.09;
Dv = 0.4;
Cr = 100;
Cg = 225;
break;
case 7:
Str = "もっと粗い網目";
F = 0.055;
K = 0.116;
Dv = 0.49;
Cr = 135;
Cg = 155;
break;
case 8:
Str = "小さい迷路";
F = 0.045;
K = 0.105;
Dv = 0.2;
Cr = 105;
Cg = 105;
break;
}
}
// 更新
var mxPrev:int;
var myPrev:int;
var Pow:Number = 1.0;
function update(e :Event):void{
var time:int = getTimer();
Tex.draw();
if(! MouseData )
return;
wanderer.wander();
wanderer.update();
//var mx:int = Main.mouseX * BITMAP_W / SCREEN_W;
var mx:int = wanderer.x * BITMAP_W / SCREEN_W;
//var my:int = Main.mouseY * BITMAP_H / SCREEN_H;
var my:int = wanderer.y * BITMAP_H / SCREEN_H;
Pow += Math.abs(mx - mxPrev) + Math.abs(my - myPrev);
if( Pow > 1.0 ) Pow = 1.0;
Tex.addPower( mx, my, Pow*1 );
Pow *= 0.5;
mxPrev = mx;
myPrev = my;
var endTime:int = getTimer() - time;
// Text.text = " 生成時間:" + endTime + "[ms]";
Text.text = "Type "+ No + ":"+Str+" F="+F+" K="+K+" Dv="+Dv+" md:"+MouseData+" mx:"+mx+" my:"+my;
}
// テクスチャ生成クラス : Class texture generation; Procedural Texture
class ProcTex{
public var BmpData:BitmapData;
public var TmpBmpData:BitmapData;
public var Bmp:Bitmap;
public var Width:int;
public var Height:int;
public var HeightBufU:Array;
public var HeightBufV:Array;
public var HeightIdxDst:int;
public var HeightIdxSrc1:int;
public var BufWidth:int;
public var BufHeight:int;
public function ProcTex( w:int, h:int ){
Width = w;
Height = h;
BmpData = new BitmapData(Width, Height, false, 0xffffff);
Bmp = new Bitmap(BmpData);
Bmp.scaleX = 2.0;
Bmp.scaleY = 2.0;
Main.addChild(Bmp);
// バッファの生成
HeightBufU = new Array;
HeightBufV = new Array;
BufWidth = Width+2;
BufHeight = Height+2;
HeightBufU[0] = new Vector.<Number>;
HeightBufV[0] = new Vector.<Number>;
HeightBufU[1] = new Vector.<Number>;
HeightBufV[1] = new Vector.<Number>;
HeightBufU[2] = new Vector.<Number>;
HeightBufV[2] = new Vector.<Number>;
for( var x:int=-1; x<Width+1; x++ ){
for( var y:int=-1; y<Height+1; y++ ){
var idx:int = (x+1) + (y+1)*BufWidth;
HeightBufU[0].push( 0 );
HeightBufV[0].push( 0 );
HeightBufU[1].push( 0 );
HeightBufV[1].push( 0 );
HeightBufU[2].push( 0 );
HeightBufV[2].push( 0 );
}
}
HeightIdxSrc1=1;
HeightIdxDst=2;
}
public function draw():void{
for( var i:int=0; i<6; i++ ){
HeightIdxSrc1++;
HeightIdxDst++;
if( HeightIdxSrc1 > 2 ) HeightIdxSrc1 = 0;
if( HeightIdxDst > 2 ) HeightIdxDst = 0;
if( ResetCnt > 0){
reset();
ResetCnt = 0;
break;
}
if( RandomInit > 0 ){
random_init();
RandomInit = 0;
ClickCnt = 10;
MouseData = 0;
break;
}
calc();
}
createBmp( HeightIdxDst );
}
// 設置/消去 Erase / installation
public function addPower( px:int, py:int, pow:Number ):void{
if( MouseData & MOUSE_LEFT ){
ClickCnt = 30;
}
var r:int = 10;
if( ClickCnt > 0 ){
r = 6;
ClickCnt--;
if( ClickCnt < 15 ) return;
}
var buf:Vector.<Number> = HeightBufU[HeightIdxDst];
var buf2:Vector.<Number> = HeightBufV[HeightIdxDst];
for( var lx:int=-r; lx<=r; lx++ ){
for( var ly:int=-r; ly<=r; ly++ ){
const d:Number = Math.sqrt(lx*lx+ly*ly);
if (d < r) {
var p:Number = Math.cos(Math.PI/2 * d/r);
var x:int = px + lx;
var y:int = py + ly;
if( x < 0 || x >= Width || y < 0 || y >= Height ) break;
var idx:int = (x+1) + (y+1)*BufWidth;
if( ClickCnt > 0 ){
buf[idx] += 0.01*pow * p;
buf2[idx] += 0.05;
}else{
buf2[idx] = 0.0;
}
}
}
}
}
// 画像の生成 Image creation
public function createBmp( idx:int ):void{
var buf:Vector.<Number> = HeightBufU[HeightIdxDst];
var buf2:Vector.<Number> = HeightBufV[HeightIdxDst];
var col:Number, col2:Number, colU:Number, colL:Number, vecX:Number, vecY:Number;
var idx:int, ix:int, iy:int;
var c:int;
var c2:int;
BmpData.lock();
for( var x:int=0; x<Width; x++ ){
for( var y:int=0; y<Height; y++ ){
idx = (x+1) + (y+1)*BufWidth;
c = (buf[idx]) * 255.0;
c2 = (buf2[idx]) * 455.0;
if( c > 255 ) c = 255;
if( c < 0 ) c = 0;
if( c2 > 255 ) c2 = 255;
if( c2 < 0 ) c2 = 0;
BmpData.setPixel(x, y, c2 + (((c2*Cg)>>8)<<8) + (((c2*Cr)>>8)<<16) );
}
}
BmpData.unlock();
}
// 計算 Calculation
public function calc():void{
var Du:Number = 0.99;
var srcBuf1U:Vector.<Number> = HeightBufU[HeightIdxSrc1];
var srcBuf1V:Vector.<Number> = HeightBufV[HeightIdxSrc1];
var dstBufU:Vector.<Number> = HeightBufU[HeightIdxDst];
var dstBufV:Vector.<Number> = HeightBufV[HeightIdxDst];
var idx:int, idxU:int, idxD:int, idxL:int, idxR:int;
var difU:Number,difV:Number;
var cntU:Number,cntV:Number;
var reaU:Number,reaV:Number, dd:Number;
for( var x:int=0; x<Width; x++ ){
for( var y:int=0; y<Height; y++ ){
idx = (x+1) + (y+1)*BufWidth;
idxU = (x+1) + (y )*BufWidth;
idxD = (x+1) + (y+2)*BufWidth;
idxL = (x ) + (y+1)*BufWidth;
idxR = (x+2) + (y+1)*BufWidth;
cntU = srcBuf1U[idx]
cntV = srcBuf1V[idx]
difU = srcBuf1U[idxU] + srcBuf1U[idxD] + srcBuf1U[idxL] + srcBuf1U[idxR];
difV = srcBuf1V[idxU] + srcBuf1V[idxD] + srcBuf1V[idxL] + srcBuf1V[idxR];
reaU = cntU + Du * (difU * 0.25 - cntU );
reaV = cntV + Dv * (difV * 0.25 - cntV );
dd = reaU * reaV * reaV;
if( dd > reaU ) dd = reaU;
reaU += -dd + F*(1-reaU);
reaV += dd - K*reaV;
if( reaV > 10 ) reaV = 10;
dstBufU[idx] = reaU;
dstBufV[idx] = reaV;
}
}
}
// reset
public function reset():void{
var idx:int, idxU:int, idxD:int, idxL:int, idxR:int;
// for( var HeightIdx:int=1; HeightIdx < 3 ; HeightIdx++){
for( var x:int=0; x<Width; x++ ){
for( var y:int=0; y<Height; y++ ){
var bufU:Vector.<Number> = HeightBufU[HeightIdxDst];
var bufV:Vector.<Number> = HeightBufV[HeightIdxDst];
idx = (x+1) + (y+1)*BufWidth;
bufU[idx] = 0.0;
bufV[idx] = 0.0;
}
}
// }
}
// random init
public function random_init():void{
var v:Number = 1;
var len:int = 10;
var r:int = 10;
var bufU:Vector.<Number> = HeightBufU[HeightIdxDst];
var bufV:Vector.<Number> = HeightBufV[HeightIdxDst];
reset();
// for( var px:int=0; px<Width; px += len ){
// for( var py:int=0; py<Height; py += len ){
for( var i:int = 0; i < 20; i++){
var px:int = Math.random() * Width;
var py:int = Math.random() * Height;
v = Math.random();
// r = Math.random() * 20;
for( var lx:int=-r; lx<=r; lx++ ){
for( var ly:int=-r; ly<=r; ly++ ){
const d:Number = Math.sqrt(lx*lx+ly*ly);
if (d < r && d > 3) {
var p:Number = Math.cos(Math.PI/2 * d/r);
var x:int = px + lx;
var y:int = py + ly;
if( x < 0 || x >= Width || y < 0 || y >= Height ) break;
var idx:int = (x+1) + (y+1)*BufWidth;
bufU[idx] = 0.01; //v * 30 / (d + 1) + 0.5 ;
bufV[idx] = x*x * Math.random();//7 * Math.random() + 1;
}
}
}
// len = Math.random() * 20;
}
// }
}
}
var MOUSE_LEFT:int = 0x01;
var MOUSE_LEFT_TRG:int = 0x02;
var MouseData:int;
function MouseCheckDown(event:MouseEvent):void{
MouseData |= MOUSE_LEFT;
MouseData |= MOUSE_LEFT_TRG;
}
function MouseCheckUp(event:MouseEvent):void{
MouseData &= ~MOUSE_LEFT;
}
function MouseUpdate():void{
MouseData &= ~MOUSE_LEFT_TRG;
}
import flash.display.DisplayObject;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Vector3D;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.objects.DisplayObject3D;
internal class WanderingTarget extends Point
{
/**
* SEE http://blog.soulwire.co.uk/laboratory/flash/as3-flocking-steering-behaviors
* SOUL WIRE
*/
private var _matrix : Matrix3D;
private var _maxForce : Number;
private var _maxSpeed : Number;
private var _distance : Number;
private var _drawScale : Number;
private var _maxForceSQ : Number;
private var _maxSpeedSQ : Number;
private var _velocity : Vector3D;
private var _position : Vector3D;
private var _oldPosition : Vector3D;
private var _acceleration : Vector3D;
private var _steeringForce : Vector3D;
private var _screenCoords : Point;
private var _renderData : DisplayObject;
private var _edgeBehavior : String;
private var _boundsRadius : Number;
private var _boundsCentre : Vector3D = new Vector3D();
private var _radius : Number = 10.0;
private var _wanderTheta : Number = 0.0;
private var _wanderRadius : Number = 16.0;
private var _wanderDistance : Number = 60.0;
private var _wanderStep : Number = 0.25;
private var _lookAtTarget : Boolean = true;
protected var _config : Object = {
minForce:3.0,
maxForce:6.0,
minSpeed:6.0,
maxSpeed:12.0,
minWanderDistance:10.0,
maxWanderDistance:100.0,
minWanderRadius:5.0,
maxWanderRadius:20.0,
minWanderStep:0.1,
maxWanderStep:0.9,
boundsRadius:250,
numBoids:120
};
public function WanderingTarget()
{
maxForce = 4;//random(_config.minForce, _config.maxForce);
maxSpeed = 10//;random(_config.minSpeed, _config.maxSpeed);
_wanderDistance = 25;//random(_config.minWanderDistance, _config.maxWanderDistance);
_wanderRadius = 15;//random(_config.minWanderRadius, _config.maxWanderRadius);
_wanderStep = 5;//random(_config.minWanderStep, _config.maxWanderStep);
super();
reset();
//super(fov, near, far, useCulling, useProjection);
}
/**
* Generates a random wandering force for the Boid.
* The results of this method can be controlled by the
* _wanderDistance, _wanderStep and _wanderRadius parameters
*
* @param multiplier
*
* By multiplying the force generated by this behavior,
* more or less weight can be given to this behavior in
* comparison to other behaviors being calculated by the
* Boid. To increase the weighting of this behavior, use
* a number above 1.0, or to decrease it use a number
* below 1.0
*/
public function wander( multiplier : Number = 1.0 ) : void
{
_wanderTheta += Math.random() * _wanderStep;
if ( Math.random() < 0.5 )
{
_wanderTheta = -_wanderTheta;
}
var pos : Vector3D = _velocity.clone();
//trace(pos)
pos.normalize();
pos.scaleBy(_wanderDistance);
pos.incrementBy(_position);
var offset : Vector3D = new Vector3D();
offset.x = _wanderRadius * Math.cos(_wanderTheta);
offset.y = _wanderRadius * Math.sin(_wanderTheta);
//offset.z = _wanderRadius * Math.tan(_wanderTheta);
// trace(offset)
//trace(_wanderRadius, _wanderTheta, pos, offset)
_steeringForce = steer(pos.add(offset));
if ( multiplier != 1.0 )
{
_steeringForce.scaleBy(multiplier);
}
// trace(_steeringForce)
_acceleration.incrementBy(_steeringForce);
}
private function steer( target : Vector3D, ease : Boolean = false, easeDistance : Number = 100 ) : Vector3D
{
//trace(_steeringForce,target.clone());
_steeringForce = target.clone();
_steeringForce.decrementBy(_position);
//trace(_steeringForce,target.clone());
_distance = _steeringForce.normalize();
//trace('ab', _distance)
if ( _distance > 0.00001 )
{
if ( _distance < easeDistance && ease )
{
_steeringForce.scaleBy(_maxSpeed * ( _distance / easeDistance ));
}
else
{
_steeringForce.scaleBy(_maxSpeed);
}
_steeringForce.decrementBy(_velocity);
if ( _steeringForce.lengthSquared > _maxForceSQ )
{
_steeringForce.normalize();
_steeringForce.scaleBy(_maxForce);
}
}
//trace(_steeringForce)
return _steeringForce;
}
public function update() : void
{
_oldPosition.x = _position.x;
_oldPosition.y = _position.y;
_oldPosition.z = _position.z;
_velocity.incrementBy(_acceleration);
if ( _velocity.lengthSquared > _maxSpeedSQ )
{
_velocity.normalize();
_velocity.scaleBy(_maxSpeed);
}
_position.incrementBy(_velocity);
x = _position.x;
y = _position.y;
//z = _position.z;
_acceleration.x = 0;
_acceleration.y = 0;
_acceleration.z = 0;
if ( isNaN(_boundsRadius) )
{
trace( "isNaN(_boundsRadius) : " + isNaN(_boundsRadius) );
return;
}
if( !_position.equals(_oldPosition) )
{
var distance : Number = Vector3D.distance(_position, _boundsCentre);
if( distance > _boundsRadius + _radius )
{
/**
* Move the boid to the edge of the boundary
* then invert it's velocity and step it
* forward back into the sphere
*/
_position.decrementBy(_boundsCentre);
_position.normalize();
_position.scaleBy(_boundsRadius + _radius);
_velocity.scaleBy(-1);
_position.incrementBy(_velocity);
_position.incrementBy(_boundsCentre);
}
}
}
/**
* Resets the Boid's position, velocity, acceleration and
* current steering force to zero
*/
public function reset() : void
{
_velocity = new Vector3D();
_position = new Vector3D();
_oldPosition = new Vector3D();
_acceleration = new Vector3D();
_steeringForce = new Vector3D();
_screenCoords = new Point();
}
/**
* The maximum force available to the Boid when
* calculating the steering force produced by
* the Boids steering bahaviors
*/
public function get maxForce() : Number
{
return _maxForce;
}
public function set maxForce( value : Number ) : void
{
if ( value < 0 )
{
value = 0;
}
_maxForce = value;
_maxForceSQ = value * value;
}
/**
* The maximum speed the Boid can reach
*/
public function get maxSpeed() : Number
{
return _maxSpeed;
}
public function set maxSpeed( value : Number ) : void
{
if ( value < 0 )
{
value = 0;
}
_maxSpeed = value;
_maxSpeedSQ = value * value;
}
protected function random( min : Number, max : Number = NaN ) : Number
{
if ( isNaN(max) )
{
max = min;
min = 0;
}
return Math.random() * ( max - min ) + min;
}
/**
* The centrepoint of the Boids bounding sphere.
* If the Boid travels futher than boundsRadius
* from this point the specified edge behavior
* will take affect.
*/
public function get boundsCentre() : Vector3D
{
return _boundsCentre;
}
public function set boundsCentre( value : Vector3D ) : void
{
_boundsCentre = value;
}
/**
* The maximum distance which this Boid can
* travel from it's boundsCentre before the
* specified edge behavior takes affect
*/
public function get boundsRadius() : Number
{
return _boundsRadius;
}
public function set boundsRadius( value : Number ) : void
{
_boundsRadius = value;
}
public function get position():Vector3D
{
return _position;
}
public function set position(value:Vector3D):void
{
_position = value;
}
}