PixelCanvas 'Sand' Example

by hemingway
still a lot of work to do but I find a way to improve it everyday. 

there is a very thin memory leak in the sand particle code, but i am not going to address that until I refactor my design. chances are that it will be fixed as consequence of my redesign

PixelCanvas & Pixel are nearly complete, I am just not happy with the partial enterFrame solution for updating pixels..
♥0 | Line 401 | Modified 2014-05-31 13:59:03 | MIT License
play

ActionScript3 source code

/**
 * Copyright hemingway ( http://wonderfl.net/user/hemingway )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/3gDF
 */

/**
 * Now works flawlessly in fullscreen
 */

package
{ 
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import net.hires.debug.*;
    
    [SWF(frameRate=60, width=465, height=465)]
    public class PixelCanvasExample extends Sprite
    {
        private const SAND:int = 2;
      //private const DIRT:int = 3;
      //private const WATER:int = 4;
      
        private const SAND_COLOR:uint = 0xC2B250;
      
        private var _pixels:Vector.<Pixel>;      
        private var _pixelDrawTable:Vector.<Vector.<Pixel>>;
        private var _pixelCanvas:PixelCanvas;
        private var _canvasSize:int;
        private var _sandRate:int;
        private var _sandStep:int;
        private var _mDown:Boolean;
        
        private var _dataField:DataField;
        
        public function PixelCanvasExample() 
        {
            _mDown = false;
            _sandRate = 1;
            _sandStep = _sandRate;
            _pixels = new Vector.<Pixel>;
            _pixelCanvas = new PixelCanvas(465, 3);
            
            _dataField = new DataField;
            
            addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
        }
        
        private function addedToStageHandler(event:Event) :void
        {
            removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);   
            
            addChild(_pixelCanvas);
            addChild(new Stats());
            addChild(_dataField);
            
            _canvasSize = _pixelCanvas.canvasSize;
            _pixelDrawTable = _pixelCanvas.pixelDrawTable;
            
            initSandBox();
            
            stage.addEventListener(Event.ENTER_FRAME, stage_enterFrameHandler);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, stage_mouseDownHandler);
            stage.addEventListener(MouseEvent.MOUSE_UP, stage_mouseUpHandler);
        }
        
        private function stage_mouseDownHandler($mouseEvent:MouseEvent) :void
        {
            _mDown = true;
        }
        
        private function stage_mouseUpHandler($mouseEvent:MouseEvent) :void
        {
            _mDown = false;
        }
        
        private function stage_enterFrameHandler($event:Event) :void
        {
            simulatePixels();

            (_mDown) ? addSand(stage.mouseX, stage.mouseY) : null;
        }
        
        private function initSandBox() :void
        {
            var $pixel:Pixel = _pixelDrawTable[_canvasSize-11][1];
        
            for (var $y:int = (_canvasSize-11); $y<_canvasSize-1; $y++)
            {
                for (var $x:int = 1; $x<_canvasSize; $x++)
                {
                    $pixel.command = {delay:0, color:SAND_COLOR, flag:SAND};
                    $pixel = _pixelDrawTable[$y][$x];
                }
            }
        }
        
        private function addSand($mX:int=0, $mY:int=0) :void
        {
            var $cmd:Object = {delay:0, color:SAND_COLOR, flag:SAND};
            var $pixelA:Pixel = _pixelCanvas.getPixelFromPoint($mX, $mY);   
            var $pixelB:Pixel = _pixelDrawTable[$pixelA.cY][$pixelA.cX-1];
            var $pixelC:Pixel = _pixelDrawTable[$pixelA.cY][$pixelA.cX+1];
            var $pixelD:Pixel = _pixelDrawTable[$pixelA.cY+1][$pixelA.cX];
            var $pixelE:Pixel = _pixelDrawTable[$pixelA.cY-1][$pixelA.cX];
            /*var $pixelF:Pixel = _pixelDrawTable[$pixelA.cY-1][$pixelA.cX-1];
            var $pixelG:Pixel = _pixelDrawTable[$pixelA.cY+1][$pixelA.cX-1];
            var $pixelH:Pixel = _pixelDrawTable[$pixelA.cY-1][$pixelA.cX+1];
            var $pixelI:Pixel = _pixelDrawTable[$pixelA.cY+1][$pixelA.cX+1];
            var $pixelJ:Pixel = _pixelDrawTable[$pixelA.cY][$pixelA.cX-2];
            var $pixelK:Pixel = _pixelDrawTable[$pixelA.cY][$pixelA.cX+2];
            var $pixelL:Pixel = _pixelDrawTable[$pixelA.cY+2][$pixelA.cX];
            var $pixelM:Pixel = _pixelDrawTable[$pixelA.cY-2][$pixelA.cX];*/
            
            $pixelA.command = $cmd;
            $pixelB.command = $cmd;
            $pixelC.command = $cmd;
            $pixelD.command = $cmd;
            $pixelE.command = $cmd;
            /*$pixelF.command = $cmd;
            $pixelG.command = $cmd;
            $pixelH.command = $cmd;
            $pixelI.command = $cmd;
            $pixelJ.command = $cmd;
            $pixelK.command = $cmd;
            $pixelL.command = $cmd;
            $pixelM.command = $cmd;*/
        }
        
        private function simulatePixels() :void
        { 
            var $sandPixel:Pixel;
            var $nextPixel:Pixel;
            var $canvasMax:int;
            var $rightX:int;
            var $leftX:int;
            var $nextY:int;
            var $rnd:int;
            var $tY:int;

            for (var $y:int = 0; $y < _canvasSize; $y++)
            {
                for (var $x:int = 0; $x < _canvasSize; $x++)
                {
                    if (_pixelDrawTable[$y][$x].flag == SAND)
                    {
                        $sandPixel = _pixelDrawTable[$y][$x];
                        $nextPixel = _pixelDrawTable[$y+1][$x];
                        $canvasMax = _canvasSize-1;
                        $rightX = $sandPixel.cX+1;
                        $leftX = $sandPixel.cX-1;
                        $nextY = $sandPixel.cY+$sandPixel.vY;
                        $rnd = Math.ceil(Math.random()*2) as int;
                        $tY = $sandPixel.vY;     
                        
                        if ($nextY < $canvasMax)
                        {
                            if ($rnd == 1)
                            {
                                if (_pixelDrawTable[$nextY][$x].flag != SAND)
                                {
                                    $sandPixel.vY = 1;
                                    $sandPixel.moving = false;
                                    $sandPixel.command = {delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE};
                                    $sandPixel = _pixelDrawTable[$nextY][$x];
                                    $sandPixel.vY = $tY;
                                    $sandPixel.moving = true;
                                    $sandPixel.command = {delay:0, color:SAND_COLOR, flag:SAND};
                                }
                                else if ($rightX < $canvasMax && _pixelDrawTable[$nextY][$rightX].flag != SAND)
                                {
                                    $sandPixel.vY = 1;
                                    $sandPixel.moving = false;
                                    $sandPixel.command = {delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE};
                                    $sandPixel = _pixelDrawTable[$nextY][$rightX];
                                    $sandPixel.vY = $tY;
                                    $sandPixel.moving = true
                                    $sandPixel.command = {delay:0, color:SAND_COLOR, flag:SAND};
                                }
                                else if ($leftX > 0 && _pixelDrawTable[$nextY][$leftX].flag != SAND)
                                {
                                    $sandPixel.vY = 1;
                                    $sandPixel.moving = false;
                                    $sandPixel.command = {delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE};
                                    $sandPixel = _pixelDrawTable[$nextY][$leftX];
                                    $sandPixel.vY = $tY;
                                    $sandPixel.moving = true;
                                    $sandPixel.command = {delay:0, color:SAND_COLOR, flag:SAND};
                                }
                                else
                                {
                                    $sandPixel.moving = false;
                                }
                            }else{
                                if (_pixelDrawTable[$nextY][$x].flag != SAND)
                                {
                                    $sandPixel.vY = 1;
                                    $sandPixel.moving = false;
                                    $sandPixel.command = {delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE};
                                    $sandPixel = _pixelDrawTable[$nextY][$x];
                                    $sandPixel.vY = $tY;
                                    $sandPixel.moving = true;
                                    $sandPixel.command = {delay:0, color:SAND_COLOR, flag:SAND};
                                }
                                else if ($leftX > 0 && _pixelDrawTable[$nextY][$leftX].flag != SAND)
                                {
                                    $sandPixel.vY = 1;
                                    $sandPixel.moving = false;
                                    $sandPixel.command = {delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE};
                                    $sandPixel = _pixelDrawTable[$nextY][$leftX];
                                    $sandPixel.vY = $tY;
                                    $sandPixel.moving = true;
                                    $sandPixel.command = {delay:0, color:SAND_COLOR, flag:SAND};
                                }
                                else if ($rightX < $canvasMax && _pixelDrawTable[$nextY][$rightX].flag != SAND)
                                {
                                    $sandPixel.vY = 1;
                                    $sandPixel.moving = false;
                                    $sandPixel.command = {delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE};
                                    $sandPixel = _pixelDrawTable[$nextY][$rightX];
                                    $sandPixel.vY = $tY;
                                    $sandPixel.moving = true;
                                    $sandPixel.command = {delay:0, color:SAND_COLOR, flag:SAND};
                                }
                                else
                                {
                                    $sandPixel.moving = false;   
                                }
                            }
                        }
                        
                        ($sandPixel.vY > 10) ? ((!$sandPixel.moving) ? ($sandPixel.vY = (($sandPixel.moving = true) as int)+1) : null) : $sandPixel.vY++; //-3, -2, -1 == fire, +0 == plasma, +1 == solid
                    }
                }
            }
        }
    }
}

//tracasseur.display

import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFormat;

class PixelCanvas extends Bitmap
{
    public static const PIXEL_STATIC:int = -1;
    public static const PIXEL_ACTIVE:int = 0;
    
    public static var STATIC_COLOR:uint;
    public static var CANVAS_COLOR:uint;
    
    private var _canvas:BitmapData;
    private var _canvasSize:int;
    private var _canvasBmpSize:int;
    private var _canvasClipping:Boolean;
    private var _pixelSize:int;
    private var _pixelDrawTable:Vector.<Vector.<Pixel>>;
    
    public function PixelCanvas($size:int = 465, $pixelSize:int = 5, $clipping:Boolean = true, $canvasColor:uint = 0xDCDCDC, $staticColor:uint = 0) 
    {
        _pixelDrawTable = new Vector.<Vector.<Pixel>>(); 
        _pixelSize = $pixelSize;
        _canvasClipping = $clipping;
        _canvasBmpSize = $size;
        _canvasSize = $size / $pixelSize;
        _canvas = new BitmapData(_canvasBmpSize, _canvasBmpSize, false, CANVAS_COLOR);
        
        CANVAS_COLOR = $canvasColor;
        STATIC_COLOR = $staticColor;
        
        addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
    }
    
    private function addedToStageHandler($event:Event) :void
    {
        removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);

        initializeCanvas();
        
        //addEventListener(Event.ENTER_FRAME, enterFrameHandler);
    }
    
    private function initializeCanvas() :void
    {
        bitmapData = _canvas;
        
        initializePixels();
    }
    
    private function initializePixels() :void
    {
        var $command:Object;
        var $size:int = _canvasSize-1;
        
        for (var $y:int = 0; $y < _canvasSize; $y++)
        {
            _pixelDrawTable[$y] = new Vector.<Pixel>;
            
            for (var $x:int = 0; $x < _canvasSize; $x++)
            {
                if (_canvasClipping && ($x == 0 || $y == 0 || $x == $size || $y == $size)) 
                     {$command = {delay:0, color:STATIC_COLOR, flag:PIXEL_STATIC}}
                else {$command = {delay:0, color:CANVAS_COLOR, flag:PIXEL_ACTIVE}}
                
                _pixelDrawTable[$y][$x] = new Pixel(this, _pixelSize * $x, _pixelSize * $y, _pixelSize, $command);
            }
        }
    }
    
    private function enterFrameHandler($event:Event) :void
    {
        var $pixel:Pixel;
        
        for (var $y:int = 0; $y < _canvasSize; $y++)
        {
            for (var $x:int = 0; $x < _canvasSize; $x++)
            {
                $pixel = _pixelDrawTable[$y][$x];
            
                if $pixel.state == 
                    updatePixel($pixel);
                }
            }
        }
    }
    
    public function updatePixel($pixel:Pixel) :void
    {
        var $command:Object = $pixel.command;
            $pixel.color = $command.color;
            $pixel.flag = $command.flag;
        var $pixelVolume:Rectangle = new Rectangle($pixel.x, $pixel.y, _pixelSize, _pixelSize);
        
        //_canvas.lock();
        _canvas.fillRect($pixelVolume, $command.color);
        //_canvas.unlock();

        $pixel.state = ($command.flag == PIXEL_STATIC) ? false : true;
    }
    
    public function getPixelFromPoint($x:int, $y:int) :Pixel
    {
        $x = Math.round($x / _pixelSize); 
        $y = Math.round($y / _pixelSize); 
        
        return _pixelDrawTable[$y][$x] 
    }
    
    public function getPixelFromTable($x:int, $y:int) :Pixel
    {
        return _pixelDrawTable[$y][$x]
    }
    
    public function get canvasSize() :int
    { return _canvasSize }
    public function get canvasBmpSize() :int
    { return _canvasBmpSize }
    public function get canvasClipping() :Boolean
    { return true }    
    public function get pixelSize() :int
    { return _pixelSize }
    public function get pixelDrawTable() :Vector.<Vector.<Pixel>>
    { return _pixelDrawTable }
}

class Pixel
{
    private var _x:int;
    private var _y:int;
    private var _cX:int;
    private var _cY:int;
    private var _vY:int;
    private var _size:int;
    private var _flag:int;
    private var _color:uint;
    private var _state:Boolean;
    private var _moving:Boolean;
    private var _command:Object; //{delay:int, color:uint, flag:int}
    private var _parent:PixelCanvas;
    
    public function Pixel($parent:PixelCanvas, $x:int, $y:int, $size:int, $command:Object)
    {
        _parent = $parent;
        _command = $command;
        _moving = false;
        _state = true;
        _color = $command.color;
        _flag = $command.flag;
        _size = $size;
        _vY = 1;
        _cY = $y/$size;
        _cX = $x/$size;
        _y = $y;
        _x = $x;
        
        _parent.updatePixel(this);
    }
    
    public function get x() :int
    { return _x }
    public function get y() :int
    { return _y }
    public function get cX() :int
    { return _cX }
    public function get cY() :int
    { return _cY }
    public function get vY() :int
    { return _vY }
    public function get size() :int
    { return _size }
    public function get flag() :int
    { return _flag }
    public function get color() :uint
    { return _color }
    public function get state() :Boolean
    { return _state }
    public function get moving() :Boolean
    { return _moving }
    public function get command() :Object
    { return _command }
    
    public function set vY($:int) :void
    { _vY = $ }
    public function set flag($flag:int) :void
    { _flag = $flag }
    public function set color($color:uint) :void
    { _color = $color }
    public function set state($state:Boolean) :void
    { _state = $state }
    public function set moving($moving:Boolean) :void
    { _moving = $moving }
    public function set command($command:Object) :void
    { _command = $command; _parent.updatePixel(this); }
}

class PixelCommand
{
    public var delay:int;
    public var color:uint;
    public var flag:int;
}

//tracasseur.utils

class DataField extends TextField
{
    private var _x :Number;
    private var _y :Number;
    private var _font :String;
    private var _data :String;
    
    public function DataField($data:String = "", $x:Number = 4, $y:Number = 2, $font:String = "Consolas")
    {
        _x = $x;
        _y = $y;
        _font = $font;
        _data = $data;
        
        addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
    }
        
    private function addedToStageHandler($event:Event) :void
    {
        removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
        
        multiline = true;
        autoSize = "left";
        selectable = false;
        mouseEnabled = false;
        
        init();
    }
        
    private function init() :void
    {
        x = _x;
        y = _y;
        text = _data;
        
        setTextFormat(new TextFormat(font, null, 0));
    }
        
    public function get font() :String
    { return _font }
    
    public function set font($font:String) :void
    { _font = $font; setTextFormat(new TextFormat(font, null, 0)); }
    public override function set text($data:String) :void
    { super.text = $data; setTextFormat(new TextFormat(font, null, 0)); }
}