forked from: forked from: forked from: 流体パーティクル(解読用)
mashed:
http://wonderfl.net/c/ubO1
and
Forked (that is http://wonderfl.net/c/jHkD )
// forked from simplychaos's forked from: forked from: 流体パーティクル(解読用)
// forked from tepe's forked from: 流体パーティクル(解読用)
// forked from clockmaker's 流体パーティクル(解読用)
// forked from miniapp's 流体パーティクル
package {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import net.hires.debug.Stats;
[SWF(backgroundColor=0x000000, width=465, height=465, frameRate=60)]
public class FluidLine extends Sprite {
public function FluidLine() {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private var pmouseX:Number;
private var pmouseY:Number;
private var canvasWidth:int = 365;
private var canvasHeight:int = 365;
private var mousePressed:Boolean;
private var resolution:int = 15;//グリッドサイズ
private var penSize:int = 40;
//グリッド マトリクス
private var numCols:int = canvasWidth / resolution;//x方向
private var numRows:int = canvasHeight / resolution;//y方向
//パーティクル総数
private var numParticles:int = 15000;
//グリッドデータ
private var gridDatasVectors:Vector.<Vector.<GridData>> = new Vector.<Vector.<GridData>>();
//パーティクルデータ
private var particles:Vector.<Particle> = new Vector.<Particle>(numParticles, true);
private var pcount:int = 0;
private var arrows:Array = [];
//----init----
private function init(e:Event = null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
opaqueBackground = 0x0;
wanderingTarget = new WanderingTarget;
wanderingTarget.position = new Vector3D(canvasHeight * 0.5,canvasHeight * 0.5);
wanderingTarget.boundsCentre = new Vector3D(canvasHeight*0.5, canvasWidth*0.5, 0);
wanderingTarget.boundsRadius = canvasHeight*0.5;
//マウスイベント
//stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
//stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
mousePressed = true;
stage.quality = StageQuality.LOW;
//addChild(new Stats());
//パーティクル生成
for (var i:int = 0; i < numParticles; i++) {
particles[i] = new Particle(Math.random() * canvasWidth, Math.random() * canvasHeight);
}
//グリッド生成
for (var col:int=0; col < numCols; ++col) {
gridDatasVectors[col] = new Vector.<GridData>(numRows, true);
arrows[col] = [];
for (var row:int = 0; row < numRows; ++row) {
var gridData:GridData = new GridData(col * resolution, row * resolution, resolution);
gridData.col = col;
gridData.row = row;
gridDatasVectors[col][row] = gridData;
var arrow:Arrow = new Arrow();
addChild(arrow);
arrows[col][row] = arrow;
}
}
//グリッド描画
var gridView:Shape = new Shape();
gridView.graphics.beginFill(0x808080);
addChild(gridView);
for (col = 0; col <= numCols; ++col) {
gridView.graphics.drawRect(0, col * resolution, canvasWidth, 1);
}
for (row = 0; row <= numRows; ++row) {
gridView.graphics.drawRect(row * resolution, 0, 1, canvasHeight);
}
//隣接するグリッドをセットしていく。
for (col = 0; col < numCols; ++col) { //col:X方向
for (row = 0; row < numRows; ++row) { //row:Y方向
gridData = gridDatasVectors[col][row];
if (row > 0) {
var up:GridData = gridDatasVectors[col][row - 1];//上
gridData.up = up;
up.low = gridData;//下
}
if (col > 0) {
var left:GridData = gridDatasVectors[col - 1][row];//左
gridData.left = left;
left.right = gridData;//右
}
if (row > 0 && col > 0) {
var upperLeft:GridData = gridDatasVectors[col - 1][row - 1];
gridData.upperLeft = upperLeft;
upperLeft.lowerRight = gridData;
}
if (row > 0 && col < numCols - 1) {
var upperRight:GridData = gridDatasVectors[col + 1][row - 1];
gridData.upperRight = upperRight;
upperRight.lowerLeft = gridData;
}
}
}
gridDatasVectors.fixed = true;
addEventListener(Event.ENTER_FRAME, draw);//フレーム処理
}
//----init----
//--------
private function mouseDownHandler(e:Event):void {
mousePressed = true;
}
private function mouseUpHandler(e:Event):void {
mousePressed = false;
}
//--------
//----draw----
private function draw(e:Event):void {
wanderingTarget.wander();
wanderingTarget.update();
var mouseXvel:Number = wanderingTarget.x - pmouseX;
var mouseYvel:Number = wanderingTarget.y - pmouseY;
for each(var gridDatas:Vector.<GridData> in gridDatasVectors) {
for each(var gridData:GridData in gridDatas) {
if (mousePressed) {//マウスドラッグ操作
updateGridDataVelocity(gridData, mouseXvel, mouseYvel, penSize);
}
updatePressure(gridData);
}
}
graphics.clear();
graphics.lineStyle(1, 0xFFFFFF);
updateParticle();
for each(gridDatas in gridDatasVectors) {
for each(gridData in gridDatas) {
apdateVelocity(gridData);
}
}
pmouseX = wanderingTarget.x;
pmouseY = wanderingTarget.y;
}
//----draw----
//----updateParticle----
public function updateParticle():void {
for each(var p:Particle in particles) {
if (p.x >= 0 && p.x < canvasWidth && p.y >= 0 && p.y < canvasHeight) {
var col:int = int(p.x / resolution);//自身が属しているgridDataを見つける
var row:int = int(p.y / resolution);
if (col > numCols - 1) col = numCols - 1;
if (row > numRows - 1) row = numRows - 1;
var gridData:GridData = gridDatasVectors[col][row];
var ax:Number = (p.x % resolution) / resolution;
var ay:Number = (p.y % resolution) / resolution;
p.xvel += (1 - ax) * gridData.xvel * 0.05;
p.yvel += (1 - ay) * gridData.yvel * 0.05;
p.xvel += ax * gridData.right.xvel * 0.05;
p.yvel += ax * gridData.right.yvel * 0.05;
p.xvel += ay * gridData.low.xvel * 0.05;
p.yvel += ay * gridData.low.yvel * 0.05;
p.x += p.xvel;
p.y += p.yvel;
var dx:Number = p.px - p.x;
var dy:Number = p.py - p.y;
var dist:Number = Math.sqrt(dx * dx + dy * dy);
var limit:Number = Math.random() * 0.5;
if (dist > limit) {
graphics.moveTo(p.x, p.y);
graphics.lineTo(p.px, p.py);
}
else {
graphics.moveTo(p.x, p.y);
graphics.lineTo(p.x + limit, p.y + limit);
}
p.px = p.x;
p.py = p.y;
}
else {
p.x = p.px = Math.random() * canvasWidth;
p.y = p.py = Math.random() * canvasHeight;
p.xvel = 0;
p.yvel = 0;
}
p.xvel *= 0.5;
p.yvel *= 0.5;
}
}
//----updateParticle----
//----updateGridDataVelocity----
/**
* マウスドラッグの処理
* @param gridData
* @param mvelX
* @param mvelY
* @param penSize
*/
public function updateGridDataVelocity(gridData:GridData,//更新するグリッド
mvelX:int, mvelY:int,//マウス移動量
penSize:Number):void {//ペン
var dx:Number = gridData.x - wanderingTarget.x;
var dy:Number = gridData.y - wanderingTarget.y;
var dist:Number = Math.sqrt(dy * dy + dx * dx);
if (dist < penSize) {
if (dist < 4) {
dist = penSize;
}
//マウスに近いほど力が強くなるように。
var power:Number = penSize / dist;
gridData.xvel += mvelX * power;
gridData.yvel += mvelY * power;
}
}
//----updateGridDataVelocity----
//----updatePressure----
private var n1:Number = 0.1;//周囲のグリッドへの影響
private var n2:Number = 0.1;//減衰量
private var wanderingTarget:WanderingTarget;
public function updatePressure(gridData:GridData):void {
var pressureX:Number = (
gridData.upperLeft.xvel * n1 //左上
+ gridData.left.xvel //左
+ gridData.lowerLeft.xvel * n1 //左下
- gridData.upperRight.xvel * n1 //右上
- gridData.right.xvel //右
- gridData.lowerRight.xvel * n1 //右下
);
var pressureY:Number = (
gridData.upperLeft.yvel * n1 //左上
+ gridData.up.yvel //上
+ gridData.upperRight.yvel * n1 //右上
- gridData.lowerLeft.yvel * n1 //左下
- gridData.low.yvel //下
- gridData.lowerRight.yvel * n1 //右下
);
gridData.pressure = (pressureX + pressureY) * n2;
//ベクトル表示
var arrow:Arrow = arrows[gridData.col][gridData.row];
arrow.x = gridData.x + resolution / 2 >> 0;
arrow.y = gridData.y + resolution / 2 >> 0;
arrow.rotation = Math.atan2(gridData.yvel, gridData.xvel) * 180 / Math.PI;
arrow.scaleX = arrow.scaleY = Math.sqrt(gridData.yvel * gridData.yvel + gridData.xvel * gridData.xvel) / 100
}
//----updatePressure----
//----apdateVelocity----
public function apdateVelocity(gridData:GridData):void {
gridData.xvel += (
gridData.upperLeft.pressure * 0.5 //左上
+ gridData.left.pressure //左
+ gridData.lowerLeft.pressure * 0.5 //左下
- gridData.upperRight.pressure * 0.5 //右上
- gridData.right.pressure //右
- gridData.lowerRight.pressure * 0.5 //右下
) * 0.25;
gridData.yvel += (
gridData.upperLeft.pressure * 0.5 //左上
+ gridData.up.pressure //上
+ gridData.upperRight.pressure * 0.5 //右上
- gridData.lowerLeft.pressure * 0.5 //左下
- gridData.low.pressure //下
- gridData.lowerRight.pressure * 0.5 //右下
) * 0.25;
gridData.xvel *= 0.99;
gridData.yvel *= 0.99;
}
//----apdateVelocity----
}
}//---Main---
//----------------------------
// >>> GridData
import flash.geom.Rectangle;
class BaseGridData {
public var col:int = 0;
public var row:int = 0;
public var x:int = 0;
public var y:int = 0;
public var xvel:Number = 0;
public var yvel:Number = 0;
public var pressure:Number = 0;
public var color:Number = 0;
public var rgb:uint;
public var rectangle:Rectangle;
}
//----------------------------
class GridData extends BaseGridData{
public function GridData(x:int, y:int, resolution:Number) {
this.x = x;
this.y = y;
rectangle = new Rectangle(x, y, resolution, resolution)
}
//すべてのグリッドが8方向に隣接したグリッドを持つわけではないので、
//空のデータをセットしておく。
public var upperLeft:BaseGridData = new NullGridData();//左上
public var up:BaseGridData = new NullGridData();//上
public var upperRight:BaseGridData = new NullGridData();//右上
public var left:BaseGridData = new NullGridData();//左
public var right:BaseGridData = new NullGridData();//右
public var lowerLeft:BaseGridData = new NullGridData();//左下
public var low:BaseGridData = new NullGridData();//下
public var lowerRight:BaseGridData = new NullGridData();//右下
}
//----------------------------
// >>> GridData
class NullGridData extends BaseGridData{
}
//----------------------------
class Particle {
public function Particle(x:Number, y:Number) {
this.x = px = x;
this.y = py = y;
}
public var x:Number;
public var y:Number;
public var px:Number;
public var py:Number;
public var xvel:Number = 0;
public var yvel:Number = 0;
}
//----------------------------
import flash.display.*;
class Arrow extends Sprite
{
function Arrow() {
var s:Shape = new Shape();
s.graphics.beginFill(0xFF0000, 1);
s.graphics.moveTo(2, 4);
s.graphics.lineTo(8, 4);
s.graphics.lineTo(8, 0);
s.graphics.lineTo(20, 7);
s.graphics.lineTo(8, 14);
s.graphics.lineTo(8, 10);
s.graphics.lineTo(2, 10);
s.graphics.lineTo(2, 4);
s.x = -10;
s.y = -7;
addChild(s);
}
}
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 = 2;//random(_config.minForce, _config.maxForce);
maxSpeed = 7;//;random(_config.minSpeed, _config.maxSpeed);
_wanderDistance = 15;//random(_config.minWanderDistance, _config.maxWanderDistance);
_wanderRadius = 5;//random(_config.minWanderRadius, _config.maxWanderRadius);
_wanderStep = 0.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;
}
}