In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

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();
        }
    }
}