Siral Twist 3D
ref. http://www.flashandmath.com/flashcs4/flower/
/**
* Copyright FLASHMAFIA ( http://wonderfl.net/user/FLASHMAFIA )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/1VCV
*/
package {
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.utils.getTimer;
[SWF(width = '512', height = '512')]
public class FlowerTwist extends Sprite//
{
private const TWIST : Number = 1 / 50;
private const ZFACTOR1 : Number = 3.0;
private const ZFACTOR2 : Number = 0.004;
private const F : Number = 1 / 2500;
private const CLIP : Number = 0.3;
private const PI : Number = Math.PI;
/* */
private var atomizer : PictureAtomizer;
private var ppp : Vector.<Particle3D>;
private var blur : BlurFilter;
private var darken : ColorTransform;
private var board : RotatingParticleBoard;
private var spin : Number;
function FlowerTwist()//
{
stage.stageFocusRect = mouseEnabled = tabEnabled = tabChildren = false;
stage.scaleMode = "noScale";
stage.align = "TL";
stage.quality = "low";
stage.frameRate = 64;
opaqueBackground = 0x808080;
board = new RotatingParticleBoard(512, 512, false, 0x0, 160);
//board.opaqueBackground = 0x808080;
board.setDepthDarken(-40, -150, 1.25);
board.currentQ = new Quaternion(Math.cos(PI / 12), Math.sin(PI / 12) / Math.sqrt(2), 0.0, -Math.sin(PI / 12) / Math.sqrt(2));
board.autoQuaternion = new Quaternion(Math.cos(PI / 400), Math.sin(PI / 400) / Math.sqrt(2), 0.0, Math.sin(PI / 400) / Math.sqrt(2));
board.arcballRad = 210.0;
addChild(board.holder);
blur = new BlurFilter(4.0, 4.0, 1);
darken = new ColorTransform(1.0, 1.0, 1.0, 0.85);
var xbmd : BitmapData = new BitmapData(128, 128);
xbmd.draw(failPic(xbmd.width, xbmd.height));
atomizer = new PictureAtomizer(1.25, 2, (xbmd.width >> 1), (xbmd.height >> 1), 0.0, 0.0);
atomizer.atomize(xbmd);
ppp = atomizer.ppp;
atomizer.setPositionsByPic(0, 0, 0);
spin = 0.0;
addEventListener(Event.ENTER_FRAME, oef);
}
private function failPic(w : int, h : int) : Shape//
{
var out : Shape = new Shape();
out.graphics.lineStyle(16, 0xFF0000, 1.0, false, 'none', 'none');
out.graphics.beginFill(0xC0C0C0);
out.graphics.drawRect(0, 0, w, h);
out.graphics.endFill();
out.graphics.moveTo(2, 2);
out.graphics.lineTo(w - 2, h - 2);
out.graphics.moveTo(w - 2, 2);
out.graphics.lineTo(2, h - 2);
return out;
}
private function oef(e : Event) : void//
{
var t : Number = getTimer() * F;
var oscParam : Number = 0.5 * ((1 - CLIP) + (1 + CLIP) * (0.25 - Math.cos(t) - 0.25 * Math.cos(2 * t)));
if (oscParam < 0.0) oscParam = 0.0;
var o1 : Number = 0.5 + (0.7 - 0.5) * oscParam;
var o2 : Number = 0.3 + (0.1 - 0.3) * oscParam;
var o3 : Number = 0.5 + (0.75 - 0.5) * oscParam;
var spinSpeed : Number = PI / 64 * (1 - 2 * o1);
spin = (spin + spinSpeed) % (PI * 2);
board.recess = (-0.5 + o2) * 400 + 100;
var offset : Number = 225 * o3;
for each (var p : Particle3D in ppp)//
{
p.z = oscParam * (ZFACTOR1 * (p.bx + p.by) + ZFACTOR2 * (p.lum * p.lum - 100)) - offset;
var angle : Number = (p.z + offset) * TWIST + spin;
var cos : Number = Math.cos(angle);
var sin : Number = Math.sin(angle);
p.x = p.bx * cos + p.by * sin;
p.y = -p.bx * sin + p.by * cos;
}
/* DRAW */
board.bitmapData.lock();
board.bitmapData.applyFilter(board.bitmapData, board.bitmapData.rect, board.bitmapData.rect.topLeft, blur);
board.bitmapData.colorTransform(board.bitmapData.rect, darken);
board.drawParticles(ppp);
board.bitmapData.unlock();
}
}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
internal class PictureAtomizer//
{
public var ppp : Vector.<Particle3D>;
public var sampling : Number;
public var spread : Number;
public var originX : int;
public var originY : int;
public var fuzzX : Number = 0.0;
public var fuzzY : Number = 0.0;
function PictureAtomizer(sampling : Number = 1.0, spread : Number = 1.0, originX : int = 0, originY : int = 0, fuzzX : Number = 0.0, fuzzY : Number = 0.0)//
{
this.sampling = sampling;
this.spread = spread;
this.originX = originX;
this.originY = originY;
this.fuzzX = fuzzX;
this.fuzzY = fuzzY;
ppp = new Vector.<Particle3D>();
}
public function atomize(bmd : BitmapData) : void //
{
var px : int;
while (px < bmd.width)//
{
px += sampling;
var py : int = 0;
while (py < bmd.height)//
{
py += sampling;
var rx : int = px + ((fuzzX * sampling * (-1.0 + 2.0 * Math.random())) >> 0);
var ry : int = py + ((fuzzY * sampling * (-1.0 + 2.0 * Math.random())) >> 0);
var c : uint = bmd.getPixel32(rx, ry);
var p : Particle3D = new Particle3D(c);
p.bx = spread * (rx - originX);
p.by = spread * (ry - originY);
ppp.push(p);
}
}
ppp.fixed = true;
}
public function setPositionsByPic(ox : Number, oy : Number, z0 : Number) : void {
var p : Particle3D;
var n : uint = ppp.length;
while (n-- != 0) {
p = ppp[n];
p.x = p.bx + ox;
p.y = p.by + oy;
p.z = z0;
}
}
}
internal class P3D {
public var x : Number = 0.0;
public var y : Number = 0.0;
public var z : Number = 0.0;
function P3D() {
}
public function clone() : P3D {
var p : P3D = new P3D();
p.x = x;
p.y = y;
p.z = z;
return p;
}
}
internal class Particle3D//
{
public var x : Number;
public var y : Number;
public var z : Number;
public var u : Number;
public var v : Number;
public var w : Number;
public var x2d : Number;
public var y2d : Number;
public var bx : Number;
public var by : Number;
public var c : uint;
public var r : uint;
public var g : uint;
public var b : uint;
public var lum : Number;
public var onScreen : Boolean;
function Particle3D(c : uint = 0xFFFFFFFF, x : Number = 0.0, y : Number = 0.0, z : Number = 0.0)//
{
setColor(c);
this.x = x;
this.y = y;
this.z = z;
onScreen = true;
}
public function setColor(c : uint) : void//
{
c = c;
r = (c >> 16) & 0xFF;
g = (c >> 8) & 0xFF;
b = c & 0xFF;
lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
public function clone() : Particle3D {
return new Particle3D(c, x, y, z);
}
}
internal class Quaternion {
public var w : Number;
public var x : Number;
public var y : Number;
public var z : Number;
/* */
private var qout : Quaternion;
function Quaternion(w : Number = 0.0, x : Number = 0.0, y : Number = 0.0, z : Number = 0.0) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
}
public function add(q : Quaternion) : Quaternion {
qout = new Quaternion();
qout.w = w + q.w;
qout.x = x + q.x;
qout.y = y + q.y;
qout.z = z + q.z;
return qout;
}
public function normalize() : Quaternion {
qout = new Quaternion();
var mag : Number = Math.sqrt(w * w + x * x + y * y + z * z);
qout.w = w / mag;
qout.x = x / mag;
qout.y = y / mag;
qout.z = z / mag;
return qout;
}
public function magnitudeSquare() : Number {
return w * w + x * x + y * y + z * z;
}
public function clone() : Quaternion {
return new Quaternion(w, x, y, z);
}
}
internal class RotatingParticleBoard extends Bitmap//
{
private const easingRatio : Number = 0.4;
/* */
public var arcballRad : Number;
public var currentQ : Quaternion;
public var autoQuaternion : Quaternion;
public var recess : Number;
public var holder : Sprite;
/* */
private var zBuffer : Vector.<Number>;
private var fLen : Number;
private var projOriginX : Number;
private var projOriginY : Number;
private var averagingSize : Number;
private var moveLightWithRecess : Boolean;
private var useDepthDarken : Boolean;
private var A : Number;
private var expRate : Number;
private var maxBrighten : Number;
private var v1 : P3D;
private var v2 : P3D;
private var lastV : P3D;
private var invR : Number;
private var dragging : Boolean;
private var lastQ : Quaternion;
private var changeQ : Quaternion;
private var requestQ : Quaternion;
private var inside : Boolean;
private var stepChangeQ : Quaternion;
private var averagingArray : Array;
private var qSum : Quaternion;
function RotatingParticleBoard(width : int, height : int, transp : Boolean = true, fillColor : uint = 0x0, fLen : Number = 200.0)//
{
bitmapData = new BitmapData(width, height, transp, fillColor);
zBuffer = new Vector.<Number>(height * width);
useDepthDarken = false;
projOriginX = width >> 1;
projOriginY = height >> 1;
this.fLen = fLen;
recess = 0.0;
holder = new Sprite();
holder.addEventListener(Event.ADDED_TO_STAGE, onStage);
holder.addChild(this);
arcballRad = 0.5 * Math.sqrt(width * width + height * height);
lastQ = new Quaternion();
currentQ = new Quaternion(1, 0, 0, 0);
autoQuaternion = new Quaternion(1, 0, 0, 0);
requestQ = new Quaternion();
changeQ = new Quaternion(1, 0, 0, 0);
stepChangeQ = new Quaternion();
v1 = new P3D();
v2 = new P3D();
dragging = false;
averagingSize = 8;
averagingArray = [];
moveLightWithRecess = false;
}
private function onStage(e : Event) : void//
{
if (e != null) holder.removeEventListener(e.type, onStage);
holder.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
holder.addEventListener(MouseEvent.MOUSE_UP, onUp);
}
public function setDepthDarken(unityDepth : Number, halfDepth : Number, maxBrighten : Number) : void//
{
useDepthDarken = true;
A = Math.pow(2, unityDepth / (halfDepth - unityDepth));
expRate = Math.LN2 / (unityDepth - halfDepth);
this.maxBrighten = maxBrighten;
}
private function clearBoard(particles : Vector.<Particle3D>) : void//
{
for (var i : Number = 0; i <= particles.length - 1; i++)//
{
var p : Particle3D = particles[i];
if (p.onScreen)//
{
var x2d : int = (p.x2d) >> 0;
var y2d : int = (p.y2d) >> 0;
zBuffer[x2d + height * y2d] = Number.NEGATIVE_INFINITY;
}
}
}
public function drawParticles(particles : Vector.<Particle3D>) : void//
{
clearBoard(particles);
if (dragging)//
{
v2.x = (holder.mouseX - projOriginX) / arcballRad;
v2.y = (holder.mouseY - projOriginY) / arcballRad;
var rSquare : Number = v2.x * v2.x + v2.y * v2.y;
if (rSquare > 1)//
{
invR = 1 / Math.sqrt(rSquare);
v2.x = v2.x * invR;
v2.y = v2.y * invR;
v2.z = 0;
}//
else//
{
v2.z = Math.sqrt(1 - rSquare);
inside = true;
}
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;
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);
var s : Number = 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;
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;
lastV = v2.clone();
averagingArray.push(stepChangeQ);
if (averagingArray.length > averagingSize) averagingArray.splice(0, 1);
}
else
{
lastQ = currentQ.clone();
changeQ.w = autoQuaternion.w;
changeQ.x = autoQuaternion.x;
changeQ.y = autoQuaternion.y;
changeQ.z = autoQuaternion.z;
}
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 : uint = 0; i <= particles.length - 1; i++)//
{
var p : Particle3D = particles[i];
// XXX
var x2 : Number = 2 * currentQ.x;
var y2 : Number = 2 * currentQ.y;
var z2 : Number = 2 * currentQ.z;
var wx : Number = currentQ.w * x2;
var wy : Number = currentQ.w * y2;
var wz : Number = currentQ.w * z2;
var xX : Number = currentQ.x * x2;
var xY : Number = currentQ.x * y2;
var xz : Number = currentQ.x * z2;
var yy : Number = currentQ.y * y2;
var yz : Number = currentQ.y * z2;
var zz : Number = 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;
var m : Number = fLen / (fLen - p.w);
p.x2d = p.u * m + projOriginX;
p.y2d = p.v * m + projOriginY;
p.onScreen = !((p.x2d > width) || (p.x2d < 0) || (p.y2d < 0) || (p.y2d > height) || (p.w > fLen - 1));
var x2d : int = p.x2d >> 0;
var y2d : int = p.y2d >> 0;
if (p.onScreen)//
{
if (!(p.w < zBuffer[x2d + height * y2d]))//
{
if (useDepthDarken)//
{
var f : Number = (moveLightWithRecess) ? A * Math.exp(expRate * (p.w + recess)) : A * Math.exp(expRate * (p.w));
if (f > maxBrighten) f = maxBrighten;
if (p.r > 0xFF) p.r = 0xFF;
if (p.g > 0xFF) p.g = 0xFF;
if (p.b > 0xFF) p.b = 0xFF;
var dColor : uint = (0xFF000000) | ((f * p.r) << 16) | ((f * p.g) << 8) | (255, f * p.b);
bitmapData.setPixel32(x2d, y2d, dColor);
}//
else//
{
bitmapData.setPixel32(x2d, y2d, p.c);
}
zBuffer[x2d + height * y2d] = p.w;
}
}
}
}
private function onDown(e : MouseEvent) : void//
{
dragging = 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;
var rSquare : Number = 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(e : MouseEvent) : void//
{
holder.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
holder.root.removeEventListener(MouseEvent.MOUSE_UP, onUp);
dragging = false;
if (averagingArray.length > 0)//
{
qSum = new Quaternion();
for (var t : Number = 0; t <= averagingArray.length - 1; t++)//
{
qSum = qSum.add(averagingArray[t]);
}
if (qSum.magnitudeSquare() != 0) autoQuaternion = qSum.normalize();
}
}
}