PixelCanvas tool

by hemingway
much more efficient than my last example of PixelCanvas... each pixel now has a vector that stores a primitive data object type. these objects have a structure of:
{delay:int, color:uint, flag:int}

delay is the number of frames to delay before the final two variables, color & flag, are applied to the pixel that the object was dispatched to (addCommand($cmdObj) will push a command into the vector queue. Each pixel flagged ACTIVE is iterated over within PixelCanvas and the oldest object in each Pixel's command vector has it's delay decremented if it's > 0, else, that object is shift()ed out of the queue and the Pixel this object is from has it's color() and flag() properties updated..

therefore .color() and .flag() properties on each pixel can reliably refer to the current Pixel's drawn color, and defined flag.
♥0 | Line 374 | Modified 2014-05-20 06:29:12 | 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/ydvo
 */

/**
 * 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();
            
            _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;
            
            stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
            stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
        }
        
        private function mouseDownHandler($mouseEvent:MouseEvent) :void
        {
            _mDown = true;
        }
        
        private function mouseUpHandler($mouseEvent:MouseEvent) :void
        {
            _mDown = false;
        }
        
        private function enterFrameHandler($event:Event) :void
        {
            simulatePixels();

            (_mDown) ? addSand() : null;
        }
        
        private function addSand($mX:int=0, $mY:int=0) :void
        {
            var $pixelA:Pixel = _pixelCanvas.getPixelFromPoint(stage.mouseX, stage.mouseY);   
            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];
            
            if (_mDown)
            {
                $pixelA.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelB.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelC.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelD.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelE.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelF.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelG.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelH.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelI.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelJ.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelK.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelL.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                $pixelM.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
            }
        }
        
        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;

            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;
                        
                        if ($nextY < $canvasMax)
                        {
                            if ($rnd == 1)
                            {
                                if (_pixelDrawTable[$nextY][$x].flag != SAND)
                                {
                                    $sandPixel.addCommand({delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE});
                                    $sandPixel = _pixelDrawTable[$nextY][$x];
                                    $sandPixel.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                                }
                                else if ($rightX < $canvasMax && _pixelDrawTable[$nextY][$rightX].flag != SAND)
                                {
                                    $sandPixel.addCommand({delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE});
                                    $sandPixel = _pixelDrawTable[$nextY][$rightX];
                                    $sandPixel.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                                }
                                else if ($leftX > 0 && _pixelDrawTable[$nextY][$leftX].flag != SAND)
                                {
                                    $sandPixel.addCommand({delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE});
                                    $sandPixel = _pixelDrawTable[$nextY][$leftX];
                                    $sandPixel.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                                }
                            }else{
                                if (_pixelDrawTable[$nextY][$x].flag != SAND)
                                {
                                    $sandPixel.addCommand({delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE});
                                    $sandPixel = _pixelDrawTable[$nextY][$x];
                                    $sandPixel.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                                }
                                else if ($leftX > 0 && _pixelDrawTable[$nextY][$leftX].flag != SAND)
                                {
                                    $sandPixel.addCommand({delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE});
                                    $sandPixel = _pixelDrawTable[$nextY][$leftX];
                                    $sandPixel.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                                }
                                else if ($rightX < $canvasMax && _pixelDrawTable[$nextY][$rightX].flag != SAND)
                                {
                                    $sandPixel.addCommand({delay:0, color:PixelCanvas.CANVAS_COLOR, flag:PixelCanvas.PIXEL_ACTIVE});
                                    $sandPixel = _pixelDrawTable[$nextY][$rightX];
                                    $sandPixel.addCommand({delay:0, color:SAND_COLOR, flag:SAND});
                                }
                            }
                        }
                        
                        //($sandPixel.vY > 10) ? $sandPixel.vY = 1 : $sandPixel.vY++;
                    }
                }
            }
        }
    }
}

//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(_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.commands.length > 0)
                {       
                    if ($pixel.state)
                    {
                        if ($pixel.commands[0].delay > 0) 
                        {
                            $pixel.commands[0].delay--;
                        }else{
                            updatePixel($pixel);
                        }
                    }
                }
            }
        }
    }
    
    private function updatePixel($pixel:Pixel) :void
    {
        var $command:Object = $pixel.commands.shift();
            $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 _commands:Vector.<Object>; //{delay:int, color:uint, flag:int}
    
    public function Pixel($x:int, $y:int, $size:int, $command:Object)
    {
        _commands = new Vector.<Object>;
        _commands.push($command);
        _state = true;
        _color = $command.color;
        _flag = $command.flag;
        _size = $size;
        _vY = 1;
        _cY = $y/$size;
        _cX = $x/$size;
        _y = $y;
        _x = $x;
    }
    
    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 commands() :Object
    { return _commands }
    
    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 addCommand($command:Object) :void
    { (_state) ? _commands.push($command) : null }
}

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