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

FloodFilledDynamicShadows = Optimized

As usual, lots of tweaks, optimizations ect.
Get Adobe Flash player
by FLASHMAFIA 08 Apr 2014
/**
 * Copyright FLASHMAFIA ( http://wonderfl.net/user/FLASHMAFIA )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/xEo7
 */

package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;

    [SWF(width = '512', height = '512')]
    //
    //
    // Shadow-casting vertex finding algorithm reference:
    // http://board.flashkit.com/board/showthread.php?t=798017
    //
    // Scanline flood algorithm reference:
    // http://lodev.org/cgtutor/floodfill.html#Scanline_Floodfill_Algorithm_With_Stack
    //
    // Optimization : Paul Ollivier @ FLASHMAFIA 2014
    // http://wonderfl.net/user/FLASHMAFIA/codes
    //
    //
    public class FloodShadowB01 extends Sprite//
    {
        private const SW : Number = 512;
        private const SH : Number = 512;
        //
        private const NUM_OBSTACLES : uint = 256;
        private const OBSTACLE_SIZE_MIN : Number = 2;
        private const OBSTACLE_SIZE_MAX : Number = 16;
        //
        private var _obstacles : Vector.<Polygon>;
        private var _dst : BitmapData;
        //
        private var _stackX : Vector.<Number> = new Vector.<Number>(512);
        private var _stackY : Vector.<Number> = new Vector.<Number>(512);
        private var _stackSize : int = 0;
        private var _fcnt : int;
        private var _th : Number = 0.0;
        private var _tx : int;
        private var _ty : int;
        private var _x : int;
        private var _y : int;

        function FloodShadowB01()//
        {
            if (stage) onStage(null);
            else addEventListener('addedToStage', onStage, false, 0, true);
        }

        final private function onStage(e : Event) : void//
        {
            if (e) removeEventListener(e.type, onStage);

            stage.scaleMode = 'noScale';
            stage.align = 'TL';
            stage.quality = 'low';
            stage.frameRate = 64;
            stage.stageFocusRect = mouseEnabled = mouseChildren = tabEnabled = tabChildren = false;
            opaqueBackground = stage.color = 0xFFFFFF;

            _obstacles = new Vector.<Polygon>();

            for (var i : int = 0; i < NUM_OBSTACLES; ++i)//
            {
                var obstacle : Polygon = new Polygon();

                var oa : Number = 6.283185307179586 * Math.random();
                var os : Number = OBSTACLE_SIZE_MIN + ((OBSTACLE_SIZE_MAX - OBSTACLE_SIZE_MIN) * Math.random());
                var px : Number = SW * Math.random();
                var oy : Number = SH * Math.random();

                obstacle.v0 = new Vertex(px + os * Math.cos(oa), oy + os * Math.sin(oa));

                oa += 1.570796326794897;

                obstacle.v1 = new Vertex(px + os * Math.cos(oa), oy + os * Math.sin(oa));

                oa += 1.570796326794897;

                obstacle.v2 = new Vertex(px + os * Math.cos(oa), oy + os * Math.sin(oa));

                oa += 1.570796326794897;

                obstacle.v3 = new Vertex(px + os * Math.cos(oa), oy + os * Math.sin(oa));

                oa += 1.570796326794897;

                obstacle.v0.next = obstacle.v1;
                obstacle.v1.prev = obstacle.v0;

                obstacle.v1.next = obstacle.v2;
                obstacle.v2.prev = obstacle.v1;

                obstacle.v2.next = obstacle.v3;
                obstacle.v3.prev = obstacle.v2;

                obstacle.v3.next = obstacle.v0;
                obstacle.v0.prev = obstacle.v3;

                _obstacles.push(obstacle);
            }

            addChild(new Bitmap(_dst = new BitmapData(SW, SH, false)));

            addEventListener(Event.ENTER_FRAME, oef);
        }

        final private function oef(e : Event) : void//
        {
            _fcnt++;

            _dst.lock();

            _dst.fillRect(_dst.rect, 0x0);

            // ---------------------------------------------------------------------------------------- UI

            if ((_fcnt & 31) == 1)//
            {
                _tx = SW * Math.random();
                _ty = SH * Math.random();
            }

            _x += (_tx - _x) * 0.044;
            _y += (_ty - _y) * 0.044;

            var mx : int = _x;
            var my : int = _y;

            // ---------------------------------------------------------------------------------------- RENDER LIGHT

            // light circle bounds

            _th += 0.022;

            var lightRadius : int = 222 + (88 * Math.sin(_th));

            var cx : int = mx;
            var cy : int = my;

            var error : int = -lightRadius;
            var px : int = lightRadius;
            var py : int = 0;

            while (px >= py)//
            {
                _dst.setPixel(cx + px, cy + py, 0x333333);

                if (px) _dst.setPixel(cx - px, cy + py, 0x333333);
                if (py) _dst.setPixel(cx + px, cy - py, 0x333333);
                if (px && py) _dst.setPixel(cx - px, cy - py, 0x333333);

                if (px != py)//
                {
                    _dst.setPixel(cx + py, cy + px, 0x333333);

                    if (py) _dst.setPixel(cx - py, cy + px, 0x333333);
                    if (px) _dst.setPixel(cx + py, cy - px, 0x333333);
                    if (py && px) _dst.setPixel(cx - py, cy - px, 0x333333);
                }

                error += py;
                ++py;
                error += py;

                if (error >= 0)//
                {
                    --px;
                    error -= px;
                    error -= px;
                }
            }

            for (var i : uint = 0; i < NUM_OBSTACLES; ++i)//
            {
                var obstacle : Polygon = _obstacles[i];

                dawBoxLine(_dst, obstacle.v0.x, obstacle.v0.y, obstacle.v1.x, obstacle.v1.y);
                dawBoxLine(_dst, obstacle.v1.x, obstacle.v1.y, obstacle.v2.x, obstacle.v2.y);
                dawBoxLine(_dst, obstacle.v2.x, obstacle.v2.y, obstacle.v3.x, obstacle.v3.y);
                dawBoxLine(_dst, obstacle.v3.x, obstacle.v3.y, obstacle.v0.x, obstacle.v0.y);

                processVertice(obstacle.v0, mx, my, lightRadius);
                processVertice(obstacle.v1, mx, my, lightRadius);
                processVertice(obstacle.v2, mx, my, lightRadius);
                processVertice(obstacle.v3, mx, my, lightRadius);
            }

            // ---------------------------------------------------------------------------------------- RENDER LIGHT : FLOOD FILL

            // non-recursive scanline flood fill algorithm + light color calculation

            cx = mx;
            cy = my;

            _stackSize = 0;

            var c0 : uint = _dst.getPixel(mx, my);
            if (c0 == 0x808080) return;

            var w : int = _dst.width - 1;
            var h : int = _dst.height;

            // pushStack(mx, my);

            _stackX[_stackSize] = mx;
            _stackY[_stackSize] = my;

            ++_stackSize;

            //

            while (_stackSize > 0)//
            {
                // pop stack

                --_stackSize;

                //

                mx = _stackX[_stackSize];
                my = _stackY[_stackSize];

                while ((my) && (_dst.getPixel(mx, my) == c0))//
                {
                    --my;
                }

                ++my;

                var spanLeft : int = 0;
                var spanRight : int = 0;

                while ((my < h) && (_dst.getPixel(mx, my) == c0))//
                {
                    _dst.setPixel(mx, my, 0xFFFFFF);

                    // compute channel

                    var dx : int = mx - cx;
                    var dy : int = my - cy;

                    // light ray distance

                    var c : uint = ((1 - ((dx * dx + dy * dy) / (lightRadius * lightRadius)))) * 0xFF;

                    _dst.setPixel(mx, my, (0xFF << 24) | (c << 16) | (c << 8) | c);

                    var c1 : uint = _dst.getPixel(mx - 1, my);

                    if ((!spanLeft) && (mx) && (c1 == c0))//
                    {
                        _stackX[_stackSize] = mx - 1;
                        _stackY[_stackSize] = my;

                        ++_stackSize;

                        //

                        spanLeft = 1;
                    }// 
                    else if ((spanLeft) && (mx) && (c1 != c0))//
                    {
                        spanLeft = 0;
                    }

                    c1 = _dst.getPixel(mx + 1, my);

                    if ((!spanRight) && (mx < w) && (c1 == c0))//
                    {
                        _stackX[_stackSize] = mx + 1;
                        _stackY[_stackSize] = my;

                        ++_stackSize;

                        //

                        spanRight = 1;
                    }// 
                    else if ((spanRight) && (mx < w) && (c1 != c0))//
                    {
                        spanRight = 0;
                    }

                    ++my;
                }
            }

            // ---------------------------------------------------------------------------

            _dst.unlock();
        }

        final private function processVertice(vertex : Vertex, mx : int, my : int, lightRadius : int) : void//
        {
            // get 3 adjacent vertices

            var vprev : Vertex = vertex.prev;
            var vnext : Vertex = vertex.next;

            var dx0 : int = vertex.x - mx;
            var dy0 : int = vertex.y - my;

            // vertex is casting shadow <=> v0 cross v1 and v0 cross v2 has the same sign
            
            // lo-q atan2

            if (((dx0 * (vprev.y - my)) - (dy0 * (vprev.x - mx))) * ((dx0 * (vnext.y - my)) - (dy0 * (vnext.x - mx))) >= 0)//
            {
                if (dy0 >= 0) {
                    if (dx0 >= 0.0) {
                        if (dy0 < 0.0) var atan2_dy0dx0 : Number = -(0.7853981633974483 - (0.7853981633974483 * ((dx0 - dy0) / (dx0 + dy0))));
                        else atan2_dy0dx0 = 0.7853981633974483 - (0.7853981633974483 * ((dx0 - dy0) / (dx0 + dy0)));
                    } else {
                        if (dy0 < 0.0) atan2_dy0dx0 = -(2.356194490192345 - (0.7853981633974483 * ((dx0 + dy0) / (dy0 - dx0))));
                        else atan2_dy0dx0 = 2.356194490192345 - (0.7853981633974483 * ((dx0 + dy0) / (dy0 - dx0)));
                    }
                } else {
                    if (dx0 >= 0.0) {
                        if (dy0 < 0.0) atan2_dy0dx0 = -(0.7853981633974483 - (0.7853981633974483 * ((dx0 + dy0) / (dx0 - dy0))));
                        else atan2_dy0dx0 = 0.7853981633974483 - (0.7853981633974483 * ((dx0 + dy0) / (dx0 - dy0)));
                    } else {
                        if (dy0 < 0.0) atan2_dy0dx0 = -(2.356194490192345 - (0.7853981633974483 * ((dx0 - dy0) / (-dy0 - dx0))));
                        else atan2_dy0dx0 = 2.356194490192345 - (0.7853981633974483 * ((dx0 - dy0) / (-dy0 - dx0)));
                    }
                }

                // bresenham line

                var x0 : int = vertex.x;
                var y0 : int = vertex.y;

                var x1 : int = vertex.x + (lightRadius * Math.cos(atan2_dy0dx0));
                var y1 : int = vertex.y + (lightRadius * Math.sin(atan2_dy0dx0));

                var dx : int = x1 - x0;
                var dy : int = y1 - y0;

                var sx : int = (dx >= 0) ? 1 : -1;
                var sy : int = (dy >= 0) ? 1 :  -1;

                if (dx < 0) dx = -dx;
                if (dy < 0) dy = -dy;

                var err : int = dx - dy;

                while (1)//
                {
                    _dst.setPixel(x0, y0, 0x202020);

                    if ((x0 == x1) && (y0 == y1)) break;

                    var e2 : int = err << 1;

                    if (e2 > -dy)//
                    {
                        err -= dy;
                        x0 += sx;
                    }

                    if (e2 < dx)//
                    {
                        err += dx;
                        y0 += sy;
                    }
                }
            }
        }

        private function dawBoxLine(dst : BitmapData, x0 : int, y0 : int, x1 : int, py1 : int) : void//
        {
            var dx : int = x1 - x0;
            var dy : int = py1 - y0;

            if (dx >= 0) var sx : int = 1;
            else sx = -1;

            if (dy >= 0) var sy : int = 1;
            else sy = -1;

            if (dx < 0) dx = -dx;
            if (dy < 0) dy = -dy;

            var err : int = dx - dy;

            while (1)//
            {
                dst.setPixel(x0, y0, 0x404040);

                if ((x0 == x1) && (y0 == py1)) break;

                var e2 : int = err << 1;

                if (e2 > -dy)//
                {
                    err -= dy;
                    x0 += sx;
                }
                if (e2 < dx)//
                {
                    err += dx;
                    y0 += sy;
                }
            }
        }
    }
}

internal class Vertex//
{
    internal var prev : Vertex;
    internal var next : Vertex;
    internal var x : int;
    internal var y : int;

    function Vertex(x : Number, y : Number)//
    {
        this.x = x;
        this.y = y;
    }
}

internal class Polygon//
{
    internal var v0 : Vertex;
    internal var v1 : Vertex;
    internal var v2 : Vertex;
    internal var v3 : Vertex;
}