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

forked from: faster Tree - ContextFreeArtAS3

do the same as what nutsu has done using frocessing
but without using frocessing
somebody please optimize this code!
source code: http://github.com/mash/ContextFreeArt-AS3
This software is a port of ContextFree.js to Actionscript3.
Context Free Art is http://www.contextfreeart.org/index.html
ContextFree.js is http://code.google.com/p/contextfree/
some hints about implementation for improvement
ContextFree is a combination of resized primitive shapes:
Circle, Square, Triangle
in this implementation, every primitive shapes are 
"children" of "ContextFreeArt", 
which makes it slow to "addChild" and render
after there's already plenty of children.
I thought it would be faster if we draw each primitive when needed,
but bitmaps quality drops when resized
誰か最適化してちょうだい!
ContextFreeはprimitiveな円と四角と三角形の組み合わせです
今の実装では、ContextFreeにどんどんaddChildしていくので
大量にaddChildした後にだんだんパフォーマンスが落ちていきます..
必要な時にbitmapdata.drawしていけばよいのでしょうが
リサイズ時に画質劣化が激しいので厳しいのでは,,(今ココ
/**
 * Copyright h_sakurai ( http://wonderfl.net/user/h_sakurai )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/s3Ex
 */

// forked from mash's faster Tree - ContextFreeArtAS3
// do the same as what nutsu has done using frocessing
// but without using frocessing

// somebody please optimize this code!

// source code: http://github.com/mash/ContextFreeArt-AS3
// This software is a port of ContextFree.js to Actionscript3.
// Context Free Art is http://www.contextfreeart.org/index.html
// ContextFree.js is http://code.google.com/p/contextfree/

// some hints about implementation for improvement

// ContextFree is a combination of resized primitive shapes:
// Circle, Square, Triangle
// in this implementation, every primitive shapes are 
// "children" of "ContextFreeArt", 
// which makes it slow to "addChild" and render
// after there's already plenty of children.

// I thought it would be faster if we draw each primitive when needed,
// but bitmaps quality drops when resized

// 誰か最適化してちょうだい!

// ContextFreeはprimitiveな円と四角と三角形の組み合わせです
// 今の実装では、ContextFreeにどんどんaddChildしていくので
// 大量にaddChildした後にだんだんパフォーマンスが落ちていきます..

// 必要な時にbitmapdata.drawしていけばよいのでしょうが
// リサイズ時に画質劣化が激しいので厳しいのでは,,(今ココ

package {
    import flash.display.*;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;

    public class Tree extends Sprite{

        public function Tree(){
            var cfdg :String = <><![CDATA[
startshape TREE

rule TREE {
    CIRCLE { s .5 1.2 }
    TREE { s .95 y .3 r 3 h 1 saturation 0.1 b 0.02 }
}
rule TREE .2 { 
    TREE { flip 90 } }
rule TREE .2 { 
    TREE { r 10 } 
    TREE { r -30 s .8} }
                                    ]]></>;

            start( cfdg );
        }
        var art :ContextFreeArt
        private function start( cfdg :String ) :void {
            var art :ContextFreeArt = new ContextFreeArt( cfdg, 465, 465 );
            addChild( art );

            stage.addEventListener( MouseEvent.CLICK, function(ev:MouseEvent) :void {
                art.tick();
            });
        }
    }
}

function log(...args) :void {}

//package jp.maaash {
    import flash.display.Sprite;
    ////////import jp.maaash.contextfreeart.Tokenizer;
    //import jp.maaash.contextfreeart.Compiler;
    //import jp.maaash.contextfreeart.Renderer;

    //public class ContextFreeArt extends Sprite {
class ContextFreeArt extends Sprite {
        private var renderer :Renderer;

        public function ContextFreeArt( cfdg_text :String, width :Number = 640, height :Number = 480 ) {
            var t :Tokenizer = new Tokenizer;
            var tokens :Array = t.tokenize( cfdg_text );

            var c :Compiler = new Compiler;
            var compiled :Object = c.compile( tokens );

            logger("compiled: ",compiled);

            renderer = new Renderer( width, height );
            renderer.clearQueue();
            renderer.render( compiled, this );
        }

        public function tick() :void {
            renderer.tick();
        }

        private function logger(... args) :void {
            if ( 1 ) {
                return; 
            }
            log.apply(null, (new Array("[ContextFreeArt]", this)).concat(args));
        }
    }
//}


//package jp.maaash.contextfreeart {
    //import jp.maaash.contextfreeart.state.*;
    import flash.utils.getDefinitionByName;

    //public class Compiler{
class Compiler{
        private const keywords :Array = [ "startshape", "rule", "background" ];
        public var compiled :Object = {};
        public var state :IState;

        private var curKey :String;
        private var curValues :Array;
        private var obj :Object;

        public function Compiler(){
        }

        public function compile( tokens :Array ) :Object {
            state = new General;

            while ( tokens.length > 0 ) {
                var token :String = tokens.shift();
                var nextState :Array = state.eat( token, this );

                //logger("[compile]token: "+token+" nextState: "+nextState);

                if ( nextState ) {
                    next( nextState );
                }
            }
            return compiled;
        }

        private function next( state_and_args :Array ) :void {
            var className :String = state_and_args.shift();

            // uppercase the 1st char
            className = className.substr(0,1).toUpperCase() + className.substr(1);
            switch( className ) {
                case "Startshape":
                    state = new Startshape;
                    break;
                case "General":
                    state = new General;
                    break;
                case "Background":
                    state = new Background;
                    break;
                case "Rule":
                    state = new Rule;
                    break;
                case "RuleWeight":
                    state = new RuleWeight( state_and_args );
                    break;
                case "RuleDraw":
                    state = new RuleDraw( state_and_args );
                    break;
                case "ShapeAdjustment":
                    state = new ShapeAdjustment( state_and_args );
                    break;
                default:
                    throw('unknown className: '+className);
            }
        }

        private function logger(... args) :void {
            if ( 1 ) { 
                return; 
            }
            log.apply(null, (new Array("[compiler]", this)).concat(args));
        }
    }
//}


//package jp.maaash.contextfreeart {

    //public class Tokenizer{
class Tokenizer{
        private var input :String;
        private const stopChars :Array = [" ", "{", "}", "\n", "\r", "\t"];

        public function Tokenizer() {
        }

        // TODO: String comments
        // TODO: Handle ordered arguments (i.e., square brakets)
        // TODO: Handle the | operator
        public function tokenize( _input :String ) :Array {
            input = _input;

            // To make it easier to parse, we pad the brackets with spaces.
            input = input.replace( /([{}])/g, " $1");
  
            var tokens :Array = new Array;
  
            var head :Object = { lastPos: 0 };
            while( 1 ) {
                head = tokenizeNext( head.lastPos );
  
                if ( head == null ) { break; }
  
                if ( head.token ) {
                    tokens.push( head.token );
                }
  
            }

            logger("[tokenize]tokens: ",tokens);
  
            return tokens;
        }

        private function tokenizeNext( pos :Number ) :Object {
            var stops :Array = new Array;

            var len :int = stopChars.length;
            for ( var i:int=0; i<len; i++ ) {
                var stopChar :String = stopChars[ i ];
                var foundPos :int    = input.indexOf( stopChar, pos );
                if ( foundPos != -1 ) {
                    stops.push( foundPos + 1 );
                }
            }

            if ( stops.length == 0 ) { return null; }
  
            var stopPos :Number = Math.min.apply( null, stops );
  
            var token :String   = input.substr(pos, stopPos-pos);
  
            // Remove whitespace characters as they can't be
            // tokens. Brackets can be tokens, so those don't
            // get removed.
            token = token.replace( /[ \n\r\t]/, "" );
  
            return { token: token, lastPos: stopPos }
        }

        private function logger(... args) :void {
            if ( 1 ) {
                return; 
            }
            log.apply(null, (new Array("[Tokenizer]", this)).concat(args));
        }
    }
//}


//package jp.maaash.contextfreeart {
    import flash.display.DisplayObjectContainer;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.Graphics;
    import flash.events.TimerEvent;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.utils.Timer;

    //public class Renderer{
class Renderer{
        private var width  :Number = 640;
        private var height :Number = 480;
        private var globalScale :Number = 300;

        private var centeringScale  :Number = 1;
        private var centeringMatrix :Matrix = new Matrix;

        private var queue :Array;
        private var compiled :Object;
        private var container :Sprite;
        private var background :Sprite;
        private var isRendering :Boolean = false;
        private var maxThreads :int = 1000;
        private var tickTimer :Timer;
        private var globalMatrix :Matrix;

        public function Renderer( _width :Number = 0, _height :Number = 0 ){
            if ( _width  ) { width  = _width;  }
            if ( _height ) { height = _height; }

            logger("w: "+width+" h: "+height);
        }

        public function render( _compiled :Object, _container :DisplayObjectContainer ) :void {
            compiled   = _compiled;
            background = new Sprite;
            _container.addChild( background );
            container  = new Sprite;
            _container.addChild( container );

            if ( ! queue ) { queue = new Array; }

            globalMatrix = new Matrix;

            drawBackground();
            draw();

            tickTimer = new Timer( 30 );
            tickTimer.addEventListener( TimerEvent.TIMER, tick );
            tickTimer.start();
        }

        public function tick( e :TimerEvent = null ) :void {

            //while ( queue.length > 0 ) {
            if ( queue.length > 0 ) {
                isRendering = true;

                var concurrent :int = Math.min( queue.length - 1, maxThreads );

                for ( var i :int=0; i <= concurrent; i++ ) {
                    var args :Array = queue.shift();
                    drawRule.apply( null, args );
                }
                center();
            }

        }

        private function center() :void {

            var rect :Rectangle = container.getRect( container );

            // resize
            centeringScale    = Math.min( width / rect.width, height / rect.height ) * 0.9;
            centeringMatrix.a = centeringMatrix.d = centeringScale;

            // centering
            centeringMatrix.tx         = width /2 - (rect.left + rect.right ) / 2 * centeringScale;
            centeringMatrix.ty         = height/2 - (rect.top  + rect.bottom) / 2 * centeringScale;

            container.transform.matrix = centeringMatrix;

            //logger("[center]mtx,rect,container: ",centeringMatrix,rect,container);
        }

        private function draw() :void {
            var ruleName :String = compiled.startshape;
            var foregroundColor :Color = new Color;

            drawRule( ruleName, new Matrix, foregroundColor );
        }

        private function drawRule( ruleName :String, mtx :Matrix, color :Color, priority :Number = 0 ) :void {
            //logger("[drawRule]ruleName: "+ruleName+" mtx: ",mtx);

            // When things get too small, we can stop rendering.
            // Too small, in this case, means less than half a pixel.
            if( Math.abs( mtx.a ) * globalScale * centeringScale < 0.5 && Math.abs( mtx.b ) * globalScale * centeringScale < 0.5 ){
                //logger("[drawRule]return");
                return;
            }

            var shape :Object = chooseShape( ruleName );
            drawShape( shape, mtx, color, priority );
        }

        private function chooseShape( ruleName :String ) :Object {
            // Choose which rule to go with...
            //logger("[chooseShape]ruleName: "+ruleName);

            var choices :Array = compiled[ ruleName ];
            if ( ! choices ) { throw("no rule found for "+ruleName); }

            var sum :Number = 0;
            for( var i :int=0; i<choices.length; i++) {
                sum += choices[i].weight;
            }

            var shape :Object;
            var r :Number = Math.random() * sum;
            sum = 0;
            for( i=0; i <= choices.length-1; i++) {
                sum += choices[i].weight;
                if( r <= sum ){
                    shape = choices[i];
                    break;
                }
            }
            if ( ! shape ) { throw("chooseShape failed, rule: "+ruleName+" invalid"); }

            return shape;
        }

        private function drawShape( shape :Object, mtx :Matrix, color :Color, priority :Number = 0 ) :void {

            //logger("[drawShape]shape: ",shape, mtx );

            var len :int = shape.draw.length;
            for ( var i :int = 0; i < len; i++ ) {
                var adj :Adjustment = shape.draw[ i ];

                var localTransform :Matrix = mtx.clone();
                localTransform             = adjustTransform( adj, localTransform );
                var localColor :Color      = adjustColor( adj, color );

                var localMatrix :Matrix = globalMatrix.clone();
                globalMatrix.concat( localTransform );

                switch( adj.name ){
                    case "CIRCLE":
                        drawCIRCLE( globalMatrix, localColor );
                        break;
                        
                    case "SQUARE":
                        drawSQUARE( localTransform, localColor );
                        break;
                        
                    case "TRIANGLE":
                        drawTRIANGLE( localTransform, localColor );
                        break;
                        
                    default:
                        var args :Array = [ adj.name, localTransform, localColor ];
                          
                        if( priority == 1 ){ queue.unshift( args ); }
                        else{ queue.push( args ); }
                        
                        break;
                }

                globalMatrix = localMatrix;

            }

        }

        private const halfScale :Number = globalScale * 0.5;
        private const _P:Number = 0.7071067811865476;    //Math.cos( Math.PI / 4 )
        private const _T:Number = 0.41421356237309503;   //Math.tan( Math.PI / 8 )
        private function drawCIRCLE( mx :Matrix, color :Color ) :void {
            var g :Graphics = container.graphics;
            g.beginFill.apply( null, colorToRgba(color) );

            moveTo(  g, mx, + halfScale, 0 );
            curveTo( g, mx, + halfScale     , + halfScale * _T, + halfScale * _P, + halfScale * _P );
            curveTo( g, mx, + halfScale * _T, + halfScale     , 0               , + halfScale );
            curveTo( g, mx, - halfScale * _T, + halfScale     , - halfScale * _P, + halfScale * _P );
            curveTo( g, mx, - halfScale     , + halfScale * _T, - halfScale     , 0 );
            curveTo( g, mx, - halfScale     , - halfScale * _T, - halfScale * _P, - halfScale * _P );
            curveTo( g, mx, - halfScale * _T, - halfScale     , 0               , - halfScale );
            curveTo( g, mx, + halfScale * _T, - halfScale     , + halfScale * _P, - halfScale * _P );
            curveTo( g, mx, + halfScale     , - halfScale * _T, + halfScale     , 0 );

            g.endFill();
        }

        private function drawSQUARE( mx :Matrix, color :Color ) :void {
            var g :Graphics = container.graphics;
            g.beginFill.apply( null, colorToRgba(color) );

            moveTo( g, mx, - halfScale, - halfScale );
            lineTo( g, mx, + halfScale, - halfScale );
            lineTo( g, mx, + halfScale, + halfScale );
            lineTo( g, mx, - halfScale, + halfScale );
            lineTo( g, mx, - halfScale, - halfScale );

            g.endFill();
        }

        private const triangley :Number = Math.sqrt(3) * globalScale / 6;
        private function drawTRIANGLE( mx :Matrix, color :Color ) :void {
            var g :Graphics = container.graphics;
            g.beginFill.apply( null, colorToRgba(color) );

            moveTo( g, mx, - halfScale, triangley );
            lineTo( g, mx, + halfScale, triangley );
            lineTo( g, mx, 0,          -triangley * 2 );
            lineTo( g, mx, - halfScale, triangley );

            g.endFill();
        }

        private function moveTo( g :Graphics, mx :Matrix, x :Number, y :Number ) :void {
            g.moveTo( x * mx.a + y * mx.c + mx.tx,
                      x * mx.b + y * mx.d + mx.ty );
        }

        private function lineTo( g :Graphics, mx :Matrix, x :Number, y :Number ) :void {
            g.lineTo( x * mx.a + y * mx.c + mx.tx,
                      x * mx.b + y * mx.d + mx.ty );
        }

        private function curveTo( g :Graphics, mx :Matrix, cx:Number, cy:Number, x:Number, y:Number ):void{
            g.curveTo( cx * mx.a + cy * mx.c + mx.tx,
                       cx * mx.b + cy * mx.d + mx.ty,
                       x * mx.a + y * mx.c + mx.tx,
                       x * mx.b + y * mx.d + mx.ty );
        }

        private function drawBackground() :void {
            if ( compiled.background ) {
                var colorAdj :Adjustment = compiled.background;
                var backgroundColor :Color = new Color;
                backgroundColor.b = 1; // { h:0, s:0, b:1, a:1 };

                var color :Color = adjustColor( colorAdj, backgroundColor );
                var color_alpha :Array = colorToRgba( color );

                logger("[drawBackground]color: ",color, backgroundColor,color_alpha);

                var bg :Shape = new Shape;
                bg.graphics.beginFill( color_alpha[0], color_alpha[1] );
                bg.graphics.drawRect( 0, 0, width, height );
                background.addChild( bg );
            }
        }

        // order: move rotate scale
        private function adjustTransform( adjs :Adjustment, base :Matrix ) :Matrix {

            //logger("[adjustTransform][0]adjs: ",adjs," base: ",base);

            var mtx :Matrix = new Matrix;

            // Flip around a line through the origin;
            if ( adjs.flipDefined ){
                var flip :Number = adjs.flip;
                // Flip 0 means to flip along the X axis. Flip 90 means to flip along the Y axis.
                // That's why the flip vector (vX, vY) is Pi/2 radians further along than expected. 
                var vX :Number   = Math.cos( -2*Math.PI * flip / 360 );
                var vY :Number   = Math.sin( -2*Math.PI * flip / 360 );
                var norm :Number = 1/(vX*vX + vY*vY);
                //var flip :Matrix = new Matrix((vX*vX-vY*vY)/norm, 2*vX*vY/norm, 2*vX*vY/norm, (vY*vY-vX*vX)/norm, 0, 0);
                mtx.a = (vX*vX-vY*vY)/norm;
                mtx.b = 2*vX*vY/norm;
                mtx.c = 2*vX*vY/norm;
                mtx.d = (vY*vY-vX*vX)/norm;
            }

            // Scaling
            var sizeX :Number = adjs.sizeX;
            var sizeY :Number = adjs.sizeY;
            if ( sizeX || sizeY ) {
                mtx.scale( sizeX, sizeY );
            }

            // Rotation
            var r :Number = adjs.rotate;
            if ( r != 0 ) {
                mtx.rotate( - Math.PI * r / 180 );
            }

            // Tranalsation
            var x :Number =  adjs.x;
            var y :Number = -adjs.y;
            if ( x != 0 || y != 0 ) {
                var point :Point = new Point( x * globalScale, y * globalScale );
                mtx.translate( point.x, point.y );
            }

            mtx.concat( base );

            //logger("[adjustTransform][9]mtx: ",mtx);
            
            return mtx;
        }

        private function colorToRgba( color :Color ) :Array {
            return hsl2rgb( color.h, color.s, color.b, color.a );
        }

        // hue, saturation, brightness, alpha
        // hue: [0,360) default 0
        // saturation: [0,1] default 0
        // brightness: [0,1] default 1
        // alpha: [0,1] default 1
        private function hsl2rgb(h :Number, s :Number, l :Number, a :Number) :Array {

            if (h == 360){ h = 0;}

            //
            // based on C code from http://astronomy.swin.edu.au/~pbourke/colour/hsl/
            //

            while (h < 0){ h += 360; }
            while (h > 360){ h -= 360; }
            var r :Number, g :Number, b :Number;
            if (h < 120){
                r = (120 - h) / 60;
                g = h / 60;
                b = 0;
            }else if (h < 240){
                r = 0;
                g = (240 - h) / 60;
                b = (h - 120) / 60;
            }else{
                r = (h - 240) / 60;
                g = 0;
                b = (360 - h) / 60;
            }

            r = Math.min(r, 1);
            g = Math.min(g, 1);
            b = Math.min(b, 1);

            r = 2 * s * r + (1 - s);
            g = 2 * s * g + (1 - s);
            b = 2 * s * b + (1 - s);

            if (l < 0.5){
                r = l * r;
                g = l * g;
                b = l * b;
            }else{
                r = (1 - l) * r + 2 * l - 1;
                g = (1 - l) * g + 2 * l - 1;
                b = (1 - l) * b + 2 * l - 1;
            }

            r = Math.ceil(r * 255);
            g = Math.ceil(g * 255);
            b = Math.ceil(b * 255);

            // Putting a semicolon at the end of an rgba definition
            // causes it to not work.
            //return "rgba(" + r + ", " + g + ", " + b + ", " + a + ")";

            // <uint>,<Number> to do: graphics.beginFill.apply( null, color.split(',') )
            return [ (r * 256*256 + g * 256 + b), a ];
        }

        // hsba to hsba
        private function adjustColor( adjs :Adjustment, color :Color ) :Color {
            // See http://www.contextfreeart.org/mediawiki/index.php/Shape_adjustments
            var newColor :Color = new Color;
            newColor.h = color.h;
            newColor.s = color.s;
            newColor.b = color.b;
            newColor.a = color.a;

            // Add num to the drawing hue value, modulo 360 
            newColor.h += adjs.hue;
            newColor.h %= 360;

            // If adj<0 then change the drawing [blah] adj% toward 0.
            // If adj>0 then change the drawing [blah] adj% toward 1. 
            if ( adjs.saturation != 0 ) {
                if( adjs.saturation > 0 ){
                    newColor.s += adjs.saturation * (1-color.s);
                } else {
                    newColor.s += adjs.saturation * color.s;
                }
            }
            if ( adjs.brightness != 0 ) {
                if( adjs.brightness > 0 ){
                    newColor.b += adjs.brightness * (1-color.b);
                } else {
                    newColor.b += adjs.brightness * color.b;
                }
            }
            if ( adjs.alpha != 0 ) {
                if( adjs.alpha > 0 ){
                    newColor.a += adjs.alpha * (1-color.a);
                } else {
                    newColor.a += adjs.alpha * color.a;
                }
            }
            
            return newColor;
        }

        public function clearQueue() :void {
            queue = new Array;
        }

        private function logger(... args) :void {
            if ( 1 ) {
                return; 
            }
            log.apply(null, (new Array("[Renderer]", this)).concat(args));
        }
    }
//}


//package jp.maaash.contextfreeart {

    //public class Color {
class Color {
        public var h :Number = 0;
        public var s :Number = 0;
        public var b :Number = 0;
        public var a :Number = 1;

        public function Color(){
        }
    }
//}


//package jp.maaash.contextfreeart {

    //public class Adjustment{
class Adjustment{
        public var name :String;
        public var flipDefined :Boolean = false;
        public var flip :Number;
        public var sizeX :Number = 1;
        public var sizeY :Number = 1;
        public var rotate :Number = 0;
        public var x :Number = 0;
        public var y :Number = 0;
        public var hue :Number = 0;
        public var saturation :Number = 0;
        public var brightness :Number = 0;
        public var alpha :Number = 0;

        public function Adjustment() {
        }
        public function fill( obj :Object ) :void {
            for( var key :String in obj ){
                switch( key ) {
                    case "f":
                    case "flip":
                        flipDefined = true;
                        flip = obj[key];
                        break;
                    case "s":
                    case "size":
                        var size :* = obj[key];
                        if ( typeof(size) == "number" ) { size = [size,size]; }
                        sizeX = size[0];
                        sizeY = size[1];
                        break;
                    case "r":
                        rotate     = obj[key];
                        break;
                    case "h":
                        hue        = obj[key];
                        break;
                    case "sat":
                        saturation = obj[key];
                        break;
                    case "b":
                        brightness = obj[key];
                        break;
                    case "a":
                        alpha      = obj[key];
                        break;
                    case "rotate":
                    case "x":
                    case "y":
                    case "hue":
                    case "saturation":
                    case "brightness":
                    case "alpha":
                        this[key] = obj[key];
                        break;
                    default:
                        throw("unsupported adjustment: "+key);
                }
            }
            
        }

    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class Rule implements IState {
class Rule implements IState {

        public function Rule(){

        }
        public function eat( token :String, compiler :Compiler ) :Array {
            var ruleName :String = token;

            // Create a blank rule if it doesn't aleady exist
            if ( ! compiler.compiled[ ruleName ] ) {
                compiler.compiled[ ruleName ] = [];
            }
            return [ "ruleWeight", ruleName ];
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class RuleDraw implements IState {
class RuleDraw implements IState {
        private var weight :Number = 1;
        private var ruleName :String;

        public function RuleDraw( args :Array ) {
            ruleName = args[0];
        }
        public function eat( token :String, compiler :Compiler ) :Array {
            if( token == "}" ){
                return [ "general" ];
            }
        
            return [ "shapeAdjustment", token, ruleName ];
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class Startshape implements IState {
class Startshape implements IState {

        public function Startshape() {

        }
        public function eat( token :String, compiler :Compiler ) :Array {
            // uppercase the 1st char
            compiler.compiled[ "startshape" ] = token;
            return [ "general" ];
        }
    }
//}



//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class AbstractArgument implements IState {
class AbstractArgument implements IState {
        protected var curKey :String = null;
        protected var curValues :Array = [];
        protected var obj :Object = {};
        protected var compiler :Compiler;

        public function AbstractArgument(){

        }
        public function eat( token :String, _compiler :Compiler ) :Array {
            compiler = _compiler;

            switch ( token ) {
                case "}":
                    flushKey();
                    return onDone( obj );
                case "{":
                    return null;
            }

            // If it's a keyword name...
            if( token.match(/[a-z_]+/i) ) {
                flushKey();
                curKey = token;
                curValues = [];
            }
            // Otherwise it's a value (and hence a number)
            else {
                curValues.push( parseFloat(token) );
            }

            return null;
        }

        protected function onDone( obj :Object ) :Array { return null; } // abstract

        protected function flushKey() :void {
            if ( curKey ) {
                // If there is only one value for the key, we don't need to wrap
                // it in an array.
                if ( curValues.length == 1 ) {
                    obj[ curKey ] = curValues[0];
                }
                else {
                    obj[ curKey ] = curValues;
                }
            }
        }

    }
//}



//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;
    //import jp.maaash.contextfreeart.Adjustment;

    //public class Background extends AbstractArgument {
class Background extends AbstractArgument {

        public function Background() {
        }

        override protected function onDone( obj :Object ) :Array {
            var adj :Adjustment = new Adjustment;
            adj.fill( obj );
            compiler.compiled[ "background" ] = adj;
            compiler = null;
            return [ "general" ];
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;
    //import jp.maaash.contextfreeart.Adjustment;

    //public class ShapeAdjustment extends AbstractArgument {
class ShapeAdjustment extends AbstractArgument {
        private var name :String;
        private var ruleName :String;

        public function ShapeAdjustment( args :Array ) {
            name     = args[0];
            ruleName = args[1];
        }

        override protected function onDone( obj :Object ) :Array {
            trace(this + ".onDone(obj :Object ) : " + obj );
            var shape :Adjustment = new Adjustment();
            shape.name = name;
            shape.fill( obj );
            
            // We are always adding to the lastest rule we've created.
            var last :int = compiler.compiled[ ruleName ].length - 1;
            compiler.compiled[ ruleName ][ last ].draw.push( shape )

            compiler = null;
            return [ "ruleDraw", ruleName ];
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public interface IState {
interface IState {

        function eat( token :String, compiler :Compiler ) :Array;
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class RuleWeight implements IState {
class RuleWeight implements IState {
        private var weight :Number = 1;
        private var ruleName :String;

        public function RuleWeight( args :Array ) {
            ruleName = args[0];
        }
        public function eat( token :String, compiler :Compiler ) :Array {
            if ( token != "{" ) {
                weight = parseFloat( token );
                return null;
            }
            else {
                // "{"
                compiler.compiled[ ruleName ].push({ weight: weight, draw: [] });
                return [ "ruleDraw", ruleName ];
            }
        }
    }
//}

//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class General implements IState {
class General implements IState {

        public function General(){

        }
        public function eat( token :String, compiler :Compiler ) :Array {
            return [ token ];
        }
    }
//}