PixelCanvas example

by hemingway
PixelCanvas.update is called sequentially after any changes to a Pixel. What this means is that updates to the canvas can occur multiple times per frame, and since we know that each call to PixelCanvas.update provides the Pixel that was changed, we can save time in our update method by only updating the supplied Pixel. Where, conversely, we would iterate through the entire matrix and needlessly update every Pixel, regardless of whether it had actually changed.
♥0 | Line 335 | Modified 2014-05-04 12:32:33 | 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/Pd4D
 */

package //tracasseur.examples
{
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.events.Event;
    import flash.geom.Point;
    //import tracasseur.display.PixelCanvas;
    //import tracasseur.text.DataField;

    [SWF(frameRate=60, width=465, height=465)]
    public class PixelExample extends Sprite
    {
        private var pixels :Vector.<Pixel>;
        private var pixelCanvas :PixelCanvas;
        private var dataField :DataField;

        private var evtMouse :Boolean;
        private var posMouse :Vector.<Point>;
        
        private var sandMatrix :Array;
        private var matrixSize :Point;
        
        private var framePos :int;

        public function PixelExample()
        {
            pixelCanvas = new PixelCanvas();
            dataField = new DataField("test");
            
            pixels = new Vector.<Pixel>;
            evtMouse = false;
            posMouse = new Vector.<Point>();
            framePos = 0;

            addEventListener(Event.ADDED_TO_STAGE, addedToStage);
        }
        
        private function addedToStage($e:*) :void
        {
            removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
            
            addChild(pixelCanvas);
            addChild(dataField);
            
            initSimulation();
                        
            pixelCanvas.addEventListener(MouseEvent.MOUSE_DOWN, pixelCanvas_mouseDown);
            pixelCanvas.addEventListener(MouseEvent.MOUSE_MOVE, pixelCanvas_mouseMove);
            pixelCanvas.addEventListener(MouseEvent.MOUSE_UP, pixelCanvas_mouseUp);
            pixelCanvas.addEventListener(Event.ENTER_FRAME, pixelCanvas_enterFrame);
        }
        
        private function initSimulation() :void
        {
            matrixSize = pixelCanvas.getSize();          
            
            sandMatrix = [];
            
            for (var $x:int = 0; $x < matrixSize.x; $x++)
            {
                sandMatrix[$x] = [];
                
                for (var $y:int = 0; $y < matrixSize.y; $y++)
                {
                    sandMatrix[$x][$y] = 0;
                }
            }
        }
        
        private function pixelCanvas_enterFrame($e:*) :void
        {
            framePos ++;
            
            if (evtMouse)
            {
                pixels[0] = pixelCanvas.getPixel(posMouse[0].x, posMouse[0].y);
               // (posMouse.length > 1) ? 
                pixels[1] = pixelCanvas.getPixel(posMouse[1].x, posMouse[1].y); //: null;
                
                dataField.text = "pixels[0].x: " + pixels[0].x + "\npixels[0].y: " + pixels[0].y + "\n\npixels[1].x: " + pixels[1].x + "\npixels[1].y: " + pixels[1].y; 
                
                if (pixels[0].data == 0)
                {
                    var $m:int = (pixels[1].y - pixels[0].y) / (pixels[1].x - pixels[0].x);
                    var $b:int = (pixels[1].y - ($m * pixels[1].x));
                    var $u:int;
                    var $n:int;
                    
                    sandMatrix[pixels[0].x][pixels[0].y] = 1;
                            
                    pixels[0].data = 1;
                    pixels[0].color = 0xC2B250;
                    
                    if (pixels[0].x > pixels[1].x) 
                    {
                        for (var $c:int = pixels[1].x; $c < pixels[0].x; $c++)
                        {
                            $u = (($m * $c) + $b);
                            
                            var $pixA:Pixel = pixelCanvas.getPixel($c, $u);
                            
                            sandMatrix[$c][$u] = 1;
                            
                            $pixA.data = 1;
                            $pixA.color = 0xC2B250;
                        }

                    }else{
                        for (var $v:int = pixels[0].x; $v < pixels[1].x; $v++)
                        {
                            $n = (($m * $v) + $b);
                            
                            var $pixB:Pixel = pixelCanvas.getPixel($v, $n);
                            
                            sandMatrix[$v][$n] = 1;
                            
                            $pixB.data = 1;
                            $pixB.color = 0xC2B250;
                        }

                    }

                }
            }
            
            var $pixelA :Pixel;
            var $pixelB :Pixel;
            
            for (var $x:int = 0; $x < matrixSize.x; $x++)
            {
                for (var $y:int = 0; $y < matrixSize.y; $y++)
                {
                    if (sandMatrix[$x][$y] == 1 && sandMatrix[$x][$y+1] == 0 && ($y + 1) < (matrixSize.y - 1))
                    {
                        $pixelA = pixelCanvas.getPixel($x, $y);
                        $pixelB = pixelCanvas.getPixel($x, $y+1);
                        
                        sandMatrix[$x][$y] = 0;
                        sandMatrix[$x][$y+1] = 1;
                        
                        $pixelA.data = 0;
                        $pixelA.color = 0xDCDCDC;
                        
                        $pixelB.data = 1;
                        $pixelB.color = 0xC2B250;
                    }
                }
            }
        }

        private function pixelCanvas_mouseDown($e:MouseEvent) :void
        { posMouse.unshift(pixelCanvas.getCanvasPos($e.stageX, $e.stageY)); evtMouse = true }
        private function pixelCanvas_mouseMove($e:MouseEvent) :void
        { posMouse.unshift(pixelCanvas.getCanvasPos($e.stageX, $e.stageY)); (posMouse.length > 2) ? posMouse.pop() : null; }
        private function pixelCanvas_mouseUp($e:*) :void
        { evtMouse = false }
    }
}

import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.events.Event;
 
/**
 * buffer for display & behavior of two-dimensional 'Pixel' matrix
 */
class PixelCanvas extends Sprite
{
    public var framePos :int;
    
    private var canvasWidth :int;
    private var canvasHeight :int;
    private var canvasColor :uint;
    private var canvasClipping :Boolean;
    private var canvas :BitmapData;

    private var pixelMatrix :Array;
    private var pixelWidth :int;
    private var pixelHeight :int;
    private var pixelVolume :Rectangle;
    private var pixelMatrixSize :Point;
    private var pixelMatrixDrawQueue :Array;
    
    public function PixelCanvas($width:int = 465,
                                $height:int = 465,
                                $color:uint = 0xDCDCDC,
                                $pixelWidth:int = 5,
                                //$pixelHeight:int = 5, //height unsupported
                                $clipping:Boolean = true)
    {
        canvasWidth = $width;
        canvasHeight = $height;
        canvasColor = $color;
        canvasClipping = $clipping;
        canvas = new BitmapData(canvasWidth, canvasHeight, false, canvasColor);

        pixelMatrix = [];
        pixelWidth = $pixelWidth;
        pixelHeight = pixelWidth;
        pixelVolume = new Rectangle();
        pixelMatrixSize = new Point(int(canvasWidth / pixelWidth), int(canvasHeight / pixelHeight));
        pixelMatrixDrawQueue = [];
        
        initMatrices();
        
        addEventListener(Event.ADDED_TO_STAGE, addedToStage);
    }
    
    private function initMatrices() :void
    {
        for (var $x:int = 0; $x < pixelMatrixSize.x; $x++)
        {
            pixelMatrixDrawQueue[$x] = [];
            pixelMatrix[$x] = [];
            
        
            for (var $y:int = 0; $y < pixelMatrixSize.y; $y++)
            {
                pixelMatrixDrawQueue[$x][$y] = [];
                pixelMatrix[$x][$y] = new Pixel(this,
                                                $x * pixelWidth,
                                                $y * pixelHeight,
                                                canvasColor,
                                                pixelWidth);
                            
            }
        }
        
        (canvasClipping) ? initClipping() : null;    
    }
    
    private function initClipping() :void
    {
        var $pixel :Pixel;
        
        for (var $x:int = 0; $x < pixelMatrixSize.x; $x++)
        {
            $pixel = getPixel($x, 0);
            $pixel.color = 0;
            $pixel.data = -1;
            
            $pixel = getPixel($x, (pixelMatrixSize.y - 1));
            $pixel.color = 0;
            $pixel.data = -1;
        
            for (var $y:int = 0; $y < pixelMatrixSize.y; $y++)
            {
                $pixel = getPixel(0, $y);
                $pixel.color = 0;
                $pixel.data = -1;
            
                $pixel = getPixel((pixelMatrixSize.x - 1), $y);
                $pixel.color = 0;
                $pixel.data = -1;
            }
        }
    }
    
    private function addedToStage($e:*) :void
    {
        removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
        
        addChild(new Bitmap(canvas));
        
        initCanvas();
        
        addEventListener(Event.ENTER_FRAME, enterFrame);
    }
    
    private function initCanvas() :void
    {
        var $pixel :Pixel;
        
        canvas.lock();
        
        for (var $x:int = 0; $x < pixelMatrixSize.x; $x++)
        {
            for (var $y:int = 0; $y < pixelMatrixSize.y; $y++)
            {
                $pixel = getPixel($x, $y);
                
                pixelVolume = new Rectangle($pixel.x,
                                            $pixel.y,
                                            $pixel.width,
                                            $pixel.width);
                                            
                canvas.fillRect(pixelVolume, $pixel.color);
                
                $pixel.active = true;
            }
        }
        
        canvas.unlock();
    }
    
    /**
     * TODO: if the same pixel was updated within the same frame, 
     *       then store it to be updated next frame.
     *
     * NOTES: called only by the pixel that needs changed,
     *        invoked by the pixel itself after a change has occurred.
     */
    internal function update($pixel:Pixel) :void
    {
        pixelVolume = new Rectangle($pixel.x,
                                    $pixel.y,
                                    $pixel.width,
                                    $pixel.width);
        
        canvas.lock();

        if ($pixel.framePos == framePos)
        {
            if ($pixel.frameCount == 0)
            {
                //$pixel.frameQueue.push($pixel.color);
                //$pixel.frameCount++;
            }else{
                canvas.fillRect(pixelVolume, $pixel.frameQueue[0]);
                
                //$pixel.frameQueue.splice(0, 1);
                //$pixel.frameCount--;
            }
        }else{
            canvas.fillRect(pixelVolume, $pixel.color);
        
            $pixel.framePos = framePos;
        }

        canvas.unlock();
    }
    
    private function enterFrame($e:*) :void
    { framePos++ }

    public function getCanvasPos($x:int, $y:int) :Point
    { return new Point(int($x/pixelWidth), int($y/pixelHeight)) }
    public function getPixel($x:int, $y:int) :Pixel
    { return pixelMatrix[$x][$y] }
    public function getSize() :Point
    { return pixelMatrixSize }
}

/**
 * chassis for definition & incubation of 'Pixel' data- requires 'PixelCanvas'
 *
 * NOTES:
 *   only a pixel's color can be modified,
 *   all other variables are either read-only or used to define a pixel's purpose
 */
class Pixel
{
    private var canvas :PixelCanvas;
    
    protected var _x :int;
    protected var _y :int;
    protected var _width :int;
    //protected var _height :int;
    protected var _color :uint;
    
    public var data :Number;
    public var active :Boolean;   
    
    public var framePos :int;
    public var frameCount :int;
    public var frameQueue :Array;
    
    public function Pixel($par:PixelCanvas,
                          $x:int,
                          $y:int,
                          $color:uint,
                          $width:int,
                          //$height:int,
                          $data:int = 0) :void
    {
        canvas = $par;
        
        _x = $x;
        _y = $y;
        _width = $width;
        //_height = width; //height unsupported
        color = $color;
        
        data = $data;
        active = false;
        
        framePos = 0;
        frameCount = 0;
        frameQueue = [];
    }
    
    public function get x() :int
    { return _x }
    public function get y() :int
    { return _y }
    public function get width() :int
    { return _width }
    //public function get height() :int
    //{ return _height }
    public function get color() :uint
    { return _color }
    
    public function set color($:uint) :void
    { _color = $; (active) ? canvas.update(this) : null }
}

/**
 * extension of 'TextField' for display of dynamic data (read-only)
 */
class DataField extends TextField
{
    protected var _x :Number;
    protected var _y :Number;
    protected var _font :String;
    protected 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, addedToStage);
    }
        
    private function addedToStage($e:*) :void
    {
        removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
        
        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($:String) :void
    { _font = $; setTextFormat(new TextFormat(font, null, 0)) }
    public override function set text($:String) :void
    { super.text = $; setTextFormat(new TextFormat(font, null, 0)) }
}