Particles3D by Dan Gries (Black Version)
@author Dan Gries
www.dangries.com
djg@dangries.com
Prepared for www.flashandmath.com.
Last modified July 22, 2008.
Feel free to use and modify the classes in the package
com.dangries.* to create your own Flash applications under the
following two conditions:
1) If possible, please include a line acknowledging this work and the author
with your application in a way that is visible in your compiled file
or on your html page.
2) If you choose to modify the classes in the package
com.dangries.*, put them into a package different from
the original. In this case too, please acknowledge this work
in a way visible to the user.
本来ならいかのパッケージをimportする必要があるがwonderflでは対応して
いないのでこのソースの一番したにパッケージの中身をぶちまけてます。
import com.dangries.bitmapUtilities.*;
import com.dangries.objects.Particle3D;
import com.dangries.geom3D.*;
import com.dangries.display.*;
/**
* Copyright bradsedito ( http://wonderfl.net/user/bradsedito )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/mus2
*/
/**
* @author Dan Gries
* www.dangries.com
* djg@dangries.com
* Prepared for www.flashandmath.com.
*
* Last modified July 22, 2008.
*
* Feel free to use and modify the classes in the package
* com.dangries.* to create your own Flash applications under the
* following two conditions:
*
* 1) If possible, please include a line acknowledging this work and the author
* with your application in a way that is visible in your compiled file
* or on your html page.
*
* 2) If you choose to modify the classes in the package
* com.dangries.*, put them into a package different from
* the original. In this case too, please acknowledge this work
* in a way visible to the user.
*/
package {
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.events.MouseEvent;
import flash.utils.Timer;
// 本来ならいかのパッケージをimportする必要があるがwonderflでは対応して
// いないのでこのソースの一番したにパッケージの中身をぶちまけてます。
//import com.dangries.bitmapUtilities.*;
//import com.dangries.objects.Particle3D;
//import com.dangries.geom3D.*;
//import com.dangries.display.*;
import net.hires.debug.Stats;
[SWF(width = "500", height = "500", backgroundColor = "0x000000", frameRate = "60")]
public class ParticlesOrbits extends Sprite
{
private var noiseMaker:NoiseParticleMaker;
private var particles:Array/*noiseMaker.particleArray*/;
private var canvas:RotatingParticleBoard;
private var canvasRect:Rectangle;
private var p:Particle3D;
private var i:int;//for文で使いまわす
private var colorTransform:ColorTransform = new ColorTransform(.95, .95, .95, 0.8, 1, 1, 1, -2.5);
private const NUM_DESTINATIONS:Number = 1;
private const NUM_PARTICLES:Number = 3000;
private const NUM_DISTANCE:Number = 20;//6;
public function ParticlesOrbits()
{
Wonderfl.capture_delay( 30 );
canvas = new RotatingParticleBoard(stage.stageWidth, stage.stageHeight, false, 0xFFFFFFFF, 100);
canvas.setDepthDarken(0, -50, 2);
canvas.holder.x = stage.stageWidth / 2 - canvas.width / 2;
canvas.holder.y = stage.stageHeight / 2 - canvas.height / 2;
canvas.currentQ = new Quaternion(Math.cos(Math.PI / 12), Math.sin(Math.PI / 12) / Math.sqrt(2), 0, -Math.sin(Math.PI / 12) / Math.sqrt(2));
canvas.autoQuaternion = new Quaternion(1, 0, 0, 0);
canvas.arcballRad = Math.sqrt(canvas.width * canvas.width + canvas.height * canvas.height) / 2;
canvas.moveLightWithRecess = true;
canvas.recess = NUM_DISTANCE;
canvasRect = new Rectangle(0, 0, canvas.width, canvas.height);
lightDistanceUpdate();
noiseMaker = new NoiseParticleMaker(NUM_PARTICLES);
noiseMaker.addEventListener(NoiseParticleMaker.PARTICLES_CREATED, onCreatedHandler);
noiseMaker.createParticles();
addChild(new Stats());
}
//========================================================================
// パーティクルの準備が出来た?後に実行される処理
//========================================================================
private function onCreatedHandler(e:Event):void {
particles = noiseMaker.particleArray;
noiseMaker.buildDestinationArrays(NUM_DESTINATIONS);
// 3パターン適当に
var flg:Number = Math.random();
if (flg >= 0 && flg < .3 ){
setPositionsRandomCube();
}else if (flg >= .3 && flg < .6) {
setPositionsRandomSphere();
}else if (flg >= .6 && flg < 10) {
setPositionsRandomCylinder();
};
for (i = 0; i < particles.length; i++) {
p = particles[i];
p.x = p.dest[0].x;
p.y = p.dest[0].y;
p.z = p.dest[0].z;
p.setColor(0xFFFFFF/*0xFF7EAAD6*/);
//p.setColor(0xFFFFFF/*0xFF7EAAD6*/);
var vx:Number = p.y * 0.02;
var vy:Number = -p.x * 0.02;
var vz:Number = 0;
p.vel = new Point3D(vx, vy, vz);
}
stage.addChild(canvas.holder);
var timer:Timer = new Timer(0);
timer.addEventListener(TimerEvent.TIMER, loop);
timer.start();
stage.addEventListener(Event.ENTER_FRAME, onEnterFrameHandler);
}
//========================================================================
// 被写界深度によって色を更新してるっぽい
//========================================================================
private function lightDistanceUpdate():void {
var d:Number = Math.pow(0.01 * 33, 0.2);
var d2:Number = 0.5 * Math.pow(Math.cos(d * Math.PI), 3) + 1;
canvas.setDepthDarken(10, 64 - d2 * 200, 1);
}
//========================================================================
// loop
//========================================================================
private function loop(e:TimerEvent):void {
var q:Number = (Math.random() - .5) * 2;
var w:Number = (Math.random() - .5) * 2;
var t:Number = (Math.random() - .5) * 2;
colorTransform = new ColorTransform(q, w, t, 0.7, 0, 0, 0, -2.5);
e.currentTarget.delay = Math.random() * 2000 + 500;
}
//========================================================================
// onEnterFrameHandler
//========================================================================
private function onEnterFrameHandler(e:Event):void {
var scaledAccelFactor:Number = (1.33 + 1.33 * 2);
for (i = 0; i < particles.length; i++) {
p = particles[i];
var dSquared:Number = p.x * p.x + p.y * p.y + p.z * p.z;
var ax:Number = -p.x / (0.000001 + dSquared);
var ay:Number = -p.y / (0.000001 + dSquared);
var az:Number = -p.z / (0.000001 + dSquared);
p.vel.x = (p.vel.x + scaledAccelFactor * ax);
p.vel.y = (p.vel.y + scaledAccelFactor * ay);
p.vel.z = (p.vel.z + scaledAccelFactor * az);
p.x += p.vel.x;
p.y += p.vel.y;
p.z += p.vel.z;
}
canvas.bitmapData.lock();
canvas.bitmapData.colorTransform(canvasRect, colorTransform);
canvas.drawParticles(particles);
canvas.bitmapData.unlock();
}
//========================================================================
// Cube
//========================================================================
private function setPositionsRandomCube():void {
for (i = 0; i < particles.length; i++) {
p = particles[i];
var s1:Number = 1 * Math.floor(Math.random() * 2) - 1;
var s2:Number = 1 * Math.random() - 1;
var s3:Number = 1 * Math.random() - 1;
var sArray:Array/*Number*/ = [s1, s2, s3];
var shift:int = Math.floor(Math.random() * 3);
p.dest[0].x = 75 * sArray[shift];
p.dest[0].y = 1000 * sArray[(shift + 1) % 3];
p.dest[0].z = 15 * sArray[(shift + 2) % 3];
}
}
//========================================================================
// Sphere
//========================================================================
private function setPositionsRandomSphere():void {
for (i = 0; i < particles.length; i++) {
p = particles[i];
var theta:Number = Math.random() * Math.PI * 2;
var phi:Number = Math.acos(2 * Math.random() - 1);
p.dest[0].x = 100 * Math.sin(phi) * Math.sin(theta);
p.dest[0].y = 500 * Math.sin(phi) * Math.sin(theta);
p.dest[0].z = 70 * Math.sin(phi);
}
}
//========================================================================
// Cylinder
//========================================================================
private function setPositionsRandomCylinder():void {
for (i = 0; i < particles.length; i++) {
p = particles[i];
var theta:Number = Math.random() * 2 * Math.PI;
p.dest[0].x = 400 * Math.sin(theta);
p.dest[0].y = 70 * Math.cos(theta);
p.dest[0].z = 70 * (2 * Math.random() - 1);
}
}
}
}
//================================================================================
// これより下はライブラリの中身をそのままぶちまけてます
//
// @author Dan Gries
// www.dangries.com
// djg@dangries.com
// Prepared for www.flashandmath.com.
//
//================================================================================
import flash.display.*;
import flash.events.*;
import flash.events.MouseEvent;
import flash.text.*;
class RotatingParticleBoard extends Bitmap {
public var zBuffer:Array;
public var fLen:Number;
public var projOriginX:Number;
public var projOriginY:Number;
public var recess:Number;
//darkening related variables
private var useDepthDarken:Boolean;
private var A:Number;
private var expRate:Number;
private var maxBrighten:Number;
private var bgColor:Number;
//Vars used in functions
private var f:Number;
private var m:Number;
private var dColor:uint;
private var intX:int;
private var intY:int;
private var p:Particle3D;
private var X2:Number;
private var Y2:Number;
private var Z2:Number;
private var wX:Number;
private var wY:Number;
private var wZ:Number;
private var xX:Number;
private var xY:Number;
private var xZ:Number;
private var yY:Number;
private var yZ:Number;
private var zZ:Number;
public var frame:Shape;
public var holder:Sprite;
private var v1:Point3D;
private var v2:Point3D;
private var lastV:Point3D;
private var rSquare:Number;
private var invR:Number;
private var mouseDragging:Boolean;
private var lastQ:Quaternion;
private var changeQ:Quaternion;
private var requestQ:Quaternion;
private var inside:Boolean;
private var numRadii:Number;
private var stepChangeQ:Quaternion;
private var dV:Point3D;
public var averagingSize:int;
private var averagingArray:Array;
private var qSum:Quaternion;
public var arcballRad:Number;
public var currentQ:Quaternion;
public var autoQuaternion:Quaternion;
public var moveLightWithRecess:Boolean;
public var easingRatio:Number;
private var s:Number;
//Text box can be used to print parameters, etc., for testing purposes.
//public var txtMonitor:TextField;
public function RotatingParticleBoard(w:Number, h:Number, transp:Boolean = true, fillColor:Number = 0x00000000, _fLen:Number = 200) {
var thisData:BitmapData = new BitmapData(w, h, transp, fillColor);
this.bitmapData = thisData;
this.zBuffer = [];
this.useDepthDarken = false; //changed to true in setDepthDarken
this.projOriginX = w/2;
this.projOriginY = h/2;
this.fLen = _fLen;
this.bgColor = fillColor;
this.recess = 0;
this.holder = new Sprite();
this.holder.addChild(this);
this.x = 0;
this.y = 0;
this.arcballRad = 0.5*Math.sqrt(Math.pow(w,2)+Math.pow(h,2));
this.lastQ = new Quaternion();
this.currentQ = new Quaternion(1,0,0,0);
this.autoQuaternion = new Quaternion(1,0,0,0);
this.requestQ = new Quaternion();
this.changeQ = new Quaternion(1,0,0,0);
this.stepChangeQ = new Quaternion();
this.v1 = new Point3D();
this.v2 = new Point3D();
this.dV = new Point3D();
this.mouseDragging = false;
this.easingRatio = 0.4;
this.averagingSize = 8;
this.averagingArray = [];
this.holder.addEventListener(Event.ADDED_TO_STAGE, addListeners);
this.moveLightWithRecess = false;
/*
//FOR TESTING
txtMonitor = new TextField();
txtMonitor.autoSize = TextFieldAutoSize.LEFT;
txtMonitor.x = 0;
txtMonitor.y = h+25;
txtMonitor.defaultTextFormat = new TextFormat("Arial",10,0xFFFFFF);
txtMonitor.text = "testing";
this.holder.addChild(txtMonitor);
*/
}
private function addListeners(evt:Event):void {
this.holder.removeEventListener(Event.ADDED_TO_STAGE, addListeners);
this.holder.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
}
public function makeFrame(thick:Number, c:uint, inputAlpha:Number):void {
this.frame = new Shape();
this.frame.graphics.lineStyle(thick, c, inputAlpha);
this.frame.graphics.drawRect(0,0,this.width,this.height);
this.frame.x = 0;
this.frame.y = 0;
this.holder.addChild(frame);
}
public function setDepthDarken(unityDepth:Number, halfDepth:Number, _maxBrighten:Number):void {
this.useDepthDarken = true;
this.A = Math.pow(2, unityDepth/(halfDepth-unityDepth));
this.expRate = Math.LN2/(unityDepth-halfDepth);
this.maxBrighten = _maxBrighten;
}
public function clearBoard(particles:Array):void {
for (var i:Number = 0; i<=particles.length-1; i++) {
p = particles[i];
if (p.onScreen) {
intX = int(p.projX);
intY = int(p.projY);
zBuffer[intX+this.height*intY] = Number.NEGATIVE_INFINITY;
//could also insert the code below to erase pixels,
//but my method has been to use a blur filter and
//a colorTransform applied to whole bitmap, which
//creates "ghost" which fades out after a few frames.
//this.bitmapData.setPixel32(intX, intY, bgColor);
}
}
}//end of clearBoard
public function drawParticles(particles:Array):void {
clearBoard(particles);
///////////
//viewpoint change by mouse
if (mouseDragging) {
v2.x = (holder.mouseX-projOriginX)/arcballRad;
v2.y = (holder.mouseY-projOriginY)/arcballRad;
rSquare = v2.x*v2.x + v2.y*v2.y;
//for mouse position outside Arcball perimeter:
if (rSquare > 1) {
invR = 1/Math.sqrt(rSquare);
v2.x = v2.x*invR;
v2.y = v2.y*invR;
v2.z = 0;
}
//for mouse position inside Arcball perimeter:
else {
v2.z = Math.sqrt(1 - rSquare);
inside = true;
}
//interpret mouse movement as request for rotation change
requestQ.w = v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
requestQ.x = v1.y*v2.z - v2.y*v1.z;
requestQ.y = -v1.x*v2.z + v2.x*v1.z;
requestQ.z = v1.x*v2.y - v2.x*v1.y;
//smooth movement is accomplished by rotating only 40% towards the
//requested rotation in each frame. After about ten frames,
//the rotation will be within 1% of the requested rotation.
changeQ.w += easingRatio*(requestQ.w - changeQ.w);
changeQ.x += easingRatio*(requestQ.x - changeQ.x);
changeQ.y += easingRatio*(requestQ.y - changeQ.y);
changeQ.z += easingRatio*(requestQ.z - changeQ.z);
//ensure that the change quaternion is a unit quaternion:
s = 1/Math.sqrt(changeQ.w*changeQ.w +changeQ.x*changeQ.x+changeQ.y*changeQ.y+changeQ.z*changeQ.z);
changeQ.w *= s;
changeQ.x *= s;
changeQ.y *= s;
changeQ.z *= s;
//tracking each rotation to determine auto rotation after mouse up:
//determine last movement
stepChangeQ.w = lastV.x*v2.x + lastV.y*v2.y + lastV.z*v2.z;
stepChangeQ.x = lastV.y*v2.z - v2.y*lastV.z;
stepChangeQ.y = -lastV.x*v2.z + v2.x*lastV.z;
stepChangeQ.z = lastV.x*v2.y - v2.x*lastV.y;
//stepChangeQ = stepChangeQ.normalize();
lastV = v2.clone();
averagingArray.push(stepChangeQ);
if (averagingArray.length > averagingSize) {
averagingArray.splice(0,1);
}
//TESTING:
//txtMonitor.text = stepChangeQ.toString(4);
//txtMonitor.appendText(" "+qSum.toString(4));
}
else {
lastQ = currentQ.clone();
changeQ.w = autoQuaternion.w;
changeQ.x = autoQuaternion.x;
changeQ.y = autoQuaternion.y;
changeQ.z = autoQuaternion.z;
}
//find current resultant rotation by quaternion multiplication
//of lastQ and changeQ
currentQ.w = changeQ.w*lastQ.w - changeQ.x*lastQ.x - changeQ.y*lastQ.y - changeQ.z*lastQ.z;
currentQ.x = changeQ.w*lastQ.x + lastQ.w*changeQ.x + changeQ.y*lastQ.z - lastQ.y*changeQ.z;
currentQ.y = changeQ.w*lastQ.y + lastQ.w*changeQ.y - changeQ.x*lastQ.z + lastQ.x*changeQ.z;
currentQ.z = changeQ.w*lastQ.z + lastQ.w*changeQ.z + changeQ.x*lastQ.y - lastQ.x*changeQ.y;
///////////
for (var i:Number = 0; i<=particles.length-1; i++) {
p = particles[i];
//viewpoint-relative coordinates found by quaternion rotation.
//Quaternion has been converted to rotation matrix, using
//common subexpressions for efficiency.
//(Efficient algorithm found on Wikipedia!)
X2 = 2*currentQ.x;
Y2 = 2*currentQ.y;
Z2 = 2*currentQ.z;
wX = currentQ.w*X2;
wY = currentQ.w*Y2;
wZ = currentQ.w*Z2;
xX = currentQ.x*X2;
xY = currentQ.x*Y2;
xZ = currentQ.x*Z2;
yY = currentQ.y*Y2;
yZ = currentQ.y*Z2;
zZ = currentQ.z*Z2;
p.u = (1-(yY+zZ))*p.x + (xY-wZ)*p.y + (xZ+wY)*p.z;
p.v = (xY+wZ)*p.x + (1-(xX+zZ))*p.y + (yZ-wX)*p.z;
p.w = (xZ-wY)*p.x + (yZ+wX)*p.y + (1-(xX+yY))*p.z - recess;
//Projection coordinates
m = fLen/(fLen - p.w);
p.projX = p.u*m + projOriginX;
p.projY = p.v*m + projOriginY;
//test if particle is in viewable region
if ((p.projX > this.width)||(p.projX<0)||(p.projY<0)||(p.projY>this.height)||(p.w>fLen-1)) {
p.onScreen = false;
}
else {
p.onScreen = true;
}
//drawing
intX = int(p.projX);
intY = int(p.projY);
if (p.onScreen) {
if (!(p.w < zBuffer[intX+this.height*intY])) {
if (this.useDepthDarken) {
//position-based darkening - exponential
if (moveLightWithRecess) {
f = A*Math.exp(expRate*(p.w+recess));
}
else {
f = A*Math.exp(expRate*(p.w));
}
if (f>maxBrighten) {
f = maxBrighten;
}
dColor = (0xFF000000) |(Math.min(255,f*p.red) << 16) | (Math.min(255,f*p.green) << 8) | (Math.min(255,f*p.blue));
this.bitmapData.setPixel32(intX, intY, dColor);
}
else {
this.bitmapData.setPixel32(intX, intY, p.color);
}
this.zBuffer[intX+this.height*intY] = p.w;
}
}
}
}//end of drawParticles
private function onDown(evt:MouseEvent):void {
this.holder.removeEventListener(MouseEvent.MOUSE_DOWN, onDown);
this.holder.root.addEventListener(MouseEvent.MOUSE_UP, onUp);
mouseDragging = true;
changeQ = new Quaternion(1,0,0,0);
qSum = new Quaternion(0,0,0,0);
averagingArray = [qSum];
lastQ = currentQ.clone();
v1.x = (holder.mouseX-projOriginX)/arcballRad;
v1.y = (holder.mouseY-projOriginY)/arcballRad;
rSquare = v1.x*v1.x + v1.y*v1.y;
if (rSquare > 1) {
invR = 1/Math.sqrt(rSquare);
v1.x = v1.x*invR;
v1.y = v1.y*invR;
v1.z = 0;
inside = false;
}
else {
v1.z = Math.sqrt(1 - rSquare);
inside = true;
}
lastV = v1.clone();
}
private function onUp(evt:MouseEvent):void {
this.holder.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
this.holder.root.removeEventListener(MouseEvent.MOUSE_UP, onUp);
mouseDragging = false;
if (averagingArray.length>0) {
qSum = new Quaternion(0,0,0,0);
for (var t:Number = 0; t<= averagingArray.length-1; t++) {
qSum = qSum.add(averagingArray[t]);
}
if (qSum.magnitudeSquare() != 0) {
autoQuaternion = qSum.normalize();
}
//else {
//autoQuaternion = new Quaternion(1,0,0,0);
//}
}
//TESTING:
//txtMonitor.text =averagingArray.length.toString();
}
}//end of class definition
import flash.display.*;
class Particle3D {
public var x:Number;
public var y:Number;
public var z:Number;
private var outputPoint:Point3D;
//links, for creating linked lists
public var next:Particle3D;
public var prev:Particle3D;
public var onScreen:Boolean;
//velocity and acceleration vectors
public var vel:Point3D = new Point3D();
public var accel:Point3D = new Point3D();
public var lastX:Number;
public var lastY:Number;
public var lastZ:Number;
//projected coordinates
public var projX:Number;
public var projY:Number;
//coords WRT viewpoint axes
public var u:Number;
public var v:Number;
public var w:Number;
//location in source picture
public var picX:Number;
public var picY:Number;
//destination array
public var dest:Array;
//attributes
public var color:uint;
public var red:Number;
public var green:Number;
public var blue:Number;
public var lum:Number;
public var alpha:Number;
public var initColor:uint;
public var initRed:Number;
public var initGreen:Number;
public var initBlue:Number;
public var initLum:Number;
public var destColor:uint;
public var destRed:Number;
public var destGreen:Number;
public var destBlue:Number;
public var destLum:Number;
public var colorChanging:Boolean;
function Particle3D(thisColor:Number = 0xFFFFFFFF):void {
this.dest = [];
this.color = thisColor;
this.red = getRed(thisColor);
this.green = getGreen(thisColor);
this.blue = getBlue(thisColor);
this.alpha = getAlpha(thisColor);
this.lum = 0.2126*this.red + 0.7152*this.green + 0.0722*this.blue;
this.colorChanging = false;
this.onScreen = true;
}
public function setColor(thisColor:Number):void {
this.color = thisColor;
this.red = getRed(thisColor);
this.green = getGreen(thisColor);
this.blue = getBlue(thisColor);
this.alpha = getAlpha(thisColor);
this.lum = 0.2126*this.red + 0.7152*this.green + 0.0722*this.blue;
}
public function getAlpha(c:Number):Number {
return Number((c >> 24) & 0xFF);
}
public function getRed(c:Number):Number {
return Number((c >> 16) & 0xFF);
}
public function getGreen(c:Number):Number {
return Number((c >> 8) & 0xFF);
}
public function getBlue(c:Number):Number {
return Number(c & 0xFF);
}
}
import flash.display.*;
import flash.events.*;
class NoiseParticleMaker extends EventDispatcher {
public static const PARTICLES_CREATED:String="particlesCreated";
private var _particleArray:Array;
public var numParticles:Number;
public function NoiseParticleMaker(num:Number):void {
this._particleArray = [];
this.numParticles = num;
}
public function createParticles():void {
var c:uint;
var r:Number;
var g:Number;
var b:Number;
for (var i:Number=0; i<=numParticles-1; i++) {
r = Math.random()*255;
g = Math.random()*255;
b = Math.random()*255;
c = uint(0xFF000000 | int(r) << 16 | int(g) << 8 | int(b));
var p:Particle3D = new Particle3D(c);
//add to array
this.particleArray.push(p);
}
dispatchEvent(new Event(NoiseParticleMaker.PARTICLES_CREATED));
}
public function setGlobalAlpha(globalAlpha:Number):void {
var c:uint;
var p:Particle3D;
trace(_particleArray.length);
for (var t:int=0; t<= _particleArray.length - 1; t++) {
p = _particleArray[t];
p.setColor((uint(255*globalAlpha) << 24) | (p.color & 0x00FFFFFF));
}
}
public function buildDestinationArrays(numDestinations:Number):void {
var p:Particle3D;
for (var t:int=0; t<= _particleArray.length - 1; t++) {
p = _particleArray[t];
for (var n:Number = 0; n <= numDestinations-1; n++) {
var thisPoint3D:Point3D = new Point3D();
p.dest.push(thisPoint3D);
}
}
}
public function get particleArray():Array {
return _particleArray;
}
}
class Quaternion {
public var w:Number;
public var x:Number;
public var y:Number;
public var z:Number;
private var qOutput:Quaternion;
public function Quaternion(_w:Number=0,_x:Number=0, _y:Number=0, _z:Number=0) {
this.w = _w;
this.x = _x;
this.y = _y;
this.z = _z;
}
public function clone():Quaternion {
return new Quaternion(this.w, this.x, this.y, this.z);
}
public function add(q:Quaternion):Quaternion {
qOutput = new Quaternion()
qOutput.w = this.w + q.w;
qOutput.x = this.x + q.x;
qOutput.y = this.y + q.y;
qOutput.z = this.z + q.z;
return qOutput;
}
public function subtract(q:Quaternion):Quaternion {
qOutput = new Quaternion()
qOutput.w = this.w - q.w;
qOutput.x = this.x - q.x;
qOutput.y = this.y - q.y;
qOutput.z = this.z - q.z;
return qOutput;
}
public function normalize():Quaternion {
qOutput = new Quaternion()
var mag:Number = Math.sqrt(this.w*this.w + this.x*this.x + this.y*this.y + this.z*this.z);
qOutput.w = this.w/mag;
qOutput.x = this.x/mag;
qOutput.y = this.y/mag;
qOutput.z = this.z/mag;
return qOutput;
}
public function magnitudeSquare():Number {
return this.w*this.w + this.x*this.x + this.y*this.y +this.z*this.z;
}
public function toString(radix:int):String {
return "(" + this.w.toFixed(radix) + ", " + this.x.toFixed(radix) + ", " + this.y.toFixed(radix) + ", " + this.z.toFixed(radix) + ")";
}
}
class Point3D {
public var x:Number;
public var y:Number;
public var z:Number;
private var outputPoint:Point3D;
public function Point3D(x1:Number=0,y1:Number=0,z1:Number=0):void {
this.x = x1;
this.y = y1;
this.z = z1;
}
public function clone():Point3D {
outputPoint = new Point3D();
outputPoint.x = this.x;
outputPoint.y = this.y;
outputPoint.z = this.z;
return outputPoint;
}
}