Bill Gosper's Smoking Clover

by tai2
c.f. http://en.wikipedia.org/wiki/Smoking_clover This code is ported from http://sourceforge.net/projects/smokingclover/
♥0 | Line 248 | Modified 2013-05-11 10:17:19 | GPLv3 License
play

ActionScript3 source code

/**
 * Copyright tai2 ( http://wonderfl.net/user/tai2 )
 * GNU General Public License, v3 ( http://www.gnu.org/licenses/quick-guide-gplv3.html )
 * Downloaded from: http://wonderfl.net/c/kzMH
 */

package {
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.events.Event;
    
    public class SmokingClover extends Sprite {
        private const N_COLORS:int = 256;
        private const RADIUS:int = 5432;
        private const MINVEL:Number = .003;
        private const MAXVEL:Number = .15;

        private var canvas:BitmapData;
        private var canvasBitmap:Bitmap;
        private var colors:Array;
        private var counts:Array;
        private var cstates:Array;
        
        public function SmokingClover()
        {
            var i:int;

            Wonderfl.capture_delay(20);

            canvas = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
            canvasBitmap = new Bitmap(canvas);            
            addChild(canvasBitmap); 
            
            addEventListener(Event.ENTER_FRAME, enterFrame);

            colors = new Array(N_COLORS);
            for (i = 0; i < colors.length; i++) {
                colors[i] = {r:0.0, g:0.0, b:0.0};
                var c:Object = colors[i];
            }

            counts = new Array(stage.stageWidth * stage.stageHeight);
            for (i = 0; i < counts.length; i++) {
                counts[i] = 0;
            }

            cstates = new Array(3);
            for (i = 0; i < cstates.length; i++) {
                cstates[i] = {cur:0, vel:rnd(MINVEL, MAXVEL)};
            }

            redraw_lines();
            redraw_dup();
        }
        
        private function enterFrame(event:Event) : void
        {
            next_color(1);

            canvas.lock();
            var x:int;
            var y:int;
            for (y = 0; y < stage.stageHeight; y++) {
                for (x = 0; x < stage.stageWidth; x++) {
                    var c:Object = colors[get_count(x, y) % 256];
                    var r:int = 255 * c.r;
                    var g:int = 255 * c.g;
                    var b:int = 255 * c.b;
                    canvas.setPixel(x, y, (r<<16) | (g<<8) | (b<<0));
                }
            }
            canvas.unlock();            
        }

        private function rotate_colors(steps:int) : void
        {
            if (steps >= N_COLORS) {
                return;
            }

            var tail:Array = colors.splice(steps, colors.length - steps);
            colors = tail.concat(colors);
        }

        private function next_color(steps:int) : void
        {
            for (var step:int = 0; step < steps; step++) {
                for (var i:int; i < cstates.length; i++) {
                    cstates[i].cur += cstates[i].vel;
                    if (cstates[i].cur > 1) {
                        cstates[i].cur = 1;
                        cstates[i].vel = rnd(-MAXVEL, -MINVEL);
                    }
                    if (cstates[i].cur < 0) {
                        cstates[i].cur = 0;
                        cstates[i].vel = rnd(MINVEL, MAXVEL);
                    }
                }

                rotate_colors(1);

                colors[N_COLORS - 1].r = cstates[0].cur;
                colors[N_COLORS - 1].g = cstates[1].cur;
                colors[N_COLORS - 1].b = cstates[2].cur;
            }
        }

        private function redraw_lines() : void
        {
            var maxX:int = stage.stageWidth - 1;
            var maxY:int = stage.stageHeight - 1;
            var midX:int = maxX / 2;
            var midY:int = maxY / 2;
            var line_x:int = RADIUS;
            var line_y:int = 0;
            var line_f:int = 0;

            for (;;) {
                if (line_f > line_x) {
                    line_x--;
                    line_f = line_f - line_x - (line_x - 1);
                }

                clipLine(
                        midX, midY,
                        line_x + midX, line_y + midY,
                        0, 0,
                        maxX, maxY);

                line_f = line_f + line_y + line_y + 1;
                line_y++;
                if (line_y >= line_x) {
                    break;
                }
            }
        }

        private function redraw_dup() : void
        {
            var maxX:int = stage.stageWidth - 1;
            var maxY:int = stage.stageHeight - 1;
            var midX:int = maxX / 2;
            var midY:int = maxY / 2;
            var dup_x:int = midX;

            for (;;) {
                var nsteps:int = 4;
                for (var step:int = 0; step < nsteps; step++) {

                    /* set vals on diagonal to 2*v-1 */
                    if (dup_x - midX + midY <= maxY) {
                        put_count(dup_x, dup_x - midX + midY,
                                Math.max(0, 2 * get_count(dup_x, dup_x - midX + midY) - 1));
                    }

                    /* now do a column from horizontal, down to diag */
                    var yy:int = Math.min(maxY, dup_x - midX + midY);
                    for (var y:int = midY; y <= yy; y++) {
                        var val:int = get_count(dup_x, y);
                        var x1:int = dup_x;
                        var y1:int = y;
                        for (var i:int = 0; i < 4; i++) {
                            if ((y1 < maxY) && (y1 > 0)) {
                                put_count(midX + midX - x1, y1, val);
                                put_count(x1, y1, val);
                            }
                            var o:int = x1;
                            x1 = midX + midY - y1;
                            y1 = midY + o - midX;
                        }
                    }

                    dup_x++;
                    if (dup_x >= maxX) {
                        break;
                    }
                }

                if (dup_x >= maxX) {
                    break;
                }
            }
        }

        private function clipLine(x0:int, y0:int, xn:int, yn:int, xe:int, ye:int, xf:int, yf:int) : void
        {
            var dx:int = Math.abs(xn - x0);
            var dy:int = Math.abs(yn - y0);

            if (xn > x0) {            /* moving right */
                if (yn >= y0) {            /* moving up */
                    if (dx > dy)        /* below diagonal */
                        line(0, x0, y0, dx, dy, xe, ye, xf, yf);
                    else
                        line(1, y0, x0, dy, dx, ye, xe, yf, xf);
                } else {
                    if (dx > dy)
                        line(7, x0, -y0, dx, dy, xe, -yf, xf, -ye);
                    else
                        line(6, -y0, x0, dy, dx, -yf, xe, -ye, xf);
                }
            } else {
                if (yn >= y0) {
                    if (dx > dy)
                        line(3, -x0, y0, dx, dy, -xf, ye, -xe, yf);
                    else
                        line(2, y0, -x0, dy, dx, ye, -xf, yf, -xe);
                } else {
                    if (dx > dy)
                        line(4, -x0, -y0, dx, dy, -xf, -yf, -xe, -ye);
                    else
                        line(5, -y0, -x0, dy, dx, -yf, -xf, -ye, -xe);
                }
            }
        }

        private function line(fun:int, x0:int, y0:int, dx:int, dy:int, xe:int, ye:int, xf:int, yf:int) : void
        {
            var x:int = Math.max(x0,  xe,
                        (dy == 0) ? xe : x0 + ceil(dx * (((ye - y0)<<1) - 1), (dy << 1)));
            var num:int = dx + 2 * dy * (x - x0);
            var lx:int = Math.min(xf,
                    (dy == 0) ? xf : x0 + ceil(dx * (((yf - y0)<<1) - 1), (dy << 1)));
            var xx:int = Math.min(lx, x0 + (dx>>1));
            var y:int = y0 + floor(num, (dx<<1));
            var f:int = (floor(num, (dx<<1)) - dx) >> 1;

            for (var x00:int = x; x00 < xx; x00++, f += dy) {
                if (f + f > dx) {
                    f -= dx;
                    y++;
                }
                switch(fun) {
                case 0:    plot( x00,    y); break;
                case 1:    plot(   y,  x00); break;
                case 2:    plot(  -y,  x00); break;
                case 3:    plot(-x00,    y); break;
                case 4:    plot(-x00,   -y); break;
                case 5:    plot(  -y, -x00); break;
                case 6:    plot(   y, -x00); break;
                case 7:    plot( x00,   -y); break;
                }
            }

            for (var x11:int = x00; x11 < lx; x11++, f += dy) {
                if (f + f > dx) {
                    f -= dx;
                    y++;
                }
                switch(fun) {
                case 0:    plot( x11,    y); break;
                case 1:    plot(   y,  x11); break;
                case 2:    plot(  -y,  x11); break;
                case 3:    plot(-x11,    y); break;
                case 4:    plot(-x11,   -y); break;
                case 5:    plot(  -y, -x11); break;
                case 6:    plot(   y, -x11); break;
                case 7:    plot( x11,   -y); break;
                }
            }
        }

        private function get_count(x:int, y:int) : int
        {
            return counts[stage.stageWidth * y + x];
        }

        private function put_count(x:int, y:int, val:int) : void
        {
            counts[stage.stageWidth * y + x] = val;
        }

        private function plot(x:int, y:int) : void
        {
            counts[stage.stageWidth * y + x]++;
        }

        private function ceil(a:int, b:int) : int
        {
            return (a + b - 1) / b;
        }

        private function floor(a:int, b:int) : int
        {
            return ceil(a - b, b);
        }

        private function rnd(lo:Number, hi:Number) : Number
        {
            return Math.random() * (hi - lo) + lo;
        }

    }
}