Tiny L-System

by smirnov48
Tiny LSystem Demo

F - forward with drawing
b - forward without drawing
+ - increase angle
- - decrease angle
[ - save state in stack
] - return to previous state
♥0 | Line 179 | Modified 2013-09-01 00:52:48 | MIT License
play

ActionScript3 source code

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

/**
 * Tiny LSystem Demo
 *
 * F - forward with drawing
 * b - forward without drawing
 * + - increase angle
 * - - decrease angle
 * [ - save state in stack
 * ] - return to previous state
 */
package {

import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;

public class TinyLSystemDemo extends Sprite {
    private var view:int = 0;
    private var tinyLSystem:TinyLSystem;

    public function TinyLSystemDemo() {
        var timer:Timer = new Timer(4000);
        timer.addEventListener(TimerEvent.TIMER, onTimer);
        timer.start();
        onTimer(null);
    }

    private function onTimer(event:TimerEvent):void {
        if (tinyLSystem) {
            removeChild(tinyLSystem);
            tinyLSystem = null;
        }
        tinyLSystem = new TinyLSystem();
        switch (view++) {
            case 0:
                /* Koch Snowflake */
                tinyLSystem.setLength(0.4);
                tinyLSystem.setAngle(Math.PI/3);
                tinyLSystem.setAxiom("F++F++F");
                tinyLSystem.setRule("F", "F-F++F-F");
                tinyLSystem.setIterations(5);
            break;
            case 1:
                /* Island */
                tinyLSystem.setLength(2);
                tinyLSystem.setAxiom("F+F+F+F");
                tinyLSystem.setRule("F", "F+F-F-FFF+F+F-F");
                tinyLSystem.setIterations(3);
            break;
            case 2:
                /* Chain */
                tinyLSystem.setLength(2);
                tinyLSystem.setAxiom("F+F+F+F");
                tinyLSystem.setRule("F", "F+b-F-FFF+F+b-F");
                tinyLSystem.setRule("b", "bbb");
                tinyLSystem.setIterations(3);
            break;
            case 3:
                /* Dragon Curve */
                tinyLSystem.setLength(4);
                tinyLSystem.setAxiom("FX");
                tinyLSystem.setRule("X", "X+YF+");
                tinyLSystem.setRule("Y", "-FX-Y");
                tinyLSystem.setIterations(12);
            break;
            case 4:
                /* Snowflake */
                tinyLSystem.setLength(1);
                tinyLSystem.setAngle(Math.PI/3);
                tinyLSystem.setAxiom("[F]+[F]+[F]+[F]+[F]+[F]");
                tinyLSystem.setRule("F", "F[+FF][-FF]FF[+F][-F]FF");
                tinyLSystem.setIterations(3);
                view = 0;
            break;
        }
        addChild(tinyLSystem);
        tinyLSystem.draw();
        tinyLSystem.x = stage.stageWidth/2;
        tinyLSystem.y = stage.stageHeight/2;
    }
}
}

import flash.display.Sprite;

internal class TinyLSystem extends Sprite {
    private var length:Number = 2;
    private var angle:Number = Math.PI/2;
    private var iterations:int = 5;
    private var axiom:String = 'F';
    private var rules:Array = new Array();
    private var sequence:String;

    public function setLength(len:Number):void {
        length = len;
    }

    public function setAngle(a:Number):void {
        angle = a;
    }

    public function setAxiom(str:String):void {
        axiom = str;
    }

    public function setRule(token:String, replacement:String):void {
        rules.push({token:token, replacement:replacement});
    }

    public function setIterations(i:int):void {
        iterations = i;
    }

    public function draw():void {
        computeResultSequence();
        var turtle:Turtle = new Turtle(this);
        turtle.setStepLength(length);
        turtle.setRotationAngle(angle);
        for (var i:int = 0; i < sequence.length; i++) {
            var command:String = sequence.charAt(i);
            turtle.processCommand(command);
        }
    }

    private function computeResultSequence():void {
        sequence = axiom;
        for (var i:int = 0; i < iterations; i++) {
            var temp:String = '';
            for (var k:int = 0; k < sequence.length; k++) {
                var token:String = sequence.charAt(k);
                var wasInRules = false;
                for (var j:int = 0; j < rules.length; j++) {
                    if (rules[j].token == token) {
                        temp += rules[j].replacement;
                        wasInRules = true;
                        break;
                    }
                }
                if (!wasInRules) {
                    temp += token;
                }
            }
            sequence = temp;
        }
    }
}

internal class Turtle {
    public var stack:Array = new Array();
    private var length:Number = 2;
    private var angle:Number = Math.PI/2;
    private var canvas:Sprite;
    private var curAngle:Number = 0;
    private var x:Number = 0;
    private var y:Number = 0;

    public function Turtle (canvas:Sprite) {
        this.canvas = canvas;
        canvas.graphics.lineStyle(0,0x000000);
        canvas.graphics.moveTo(x,y);
    }

    public function setStepLength(length:Number):void {
        this.length = length;
    }

    public function setRotationAngle(angle:Number):void {
        this.angle = angle;
    }

    public function processCommand(command:String):void {
        switch (command) {
            case 'F':
                x += Math.cos(curAngle) * length;
                y += Math.sin(curAngle) * length;
                canvas.graphics.lineTo(x,y);
            break;
            case 'b':
                x += Math.cos(curAngle) * length;
                y += Math.sin(curAngle) * length;
                canvas.graphics.moveTo(x,y);
            break;
            case '+':
                curAngle += angle;
            break;
            case '-':
                curAngle -= angle;
            break;
            case '[':
                stack.push(new State(x,y,curAngle));
            break;
            case ']':
                var prev:State = stack.pop() as State;
                x = prev.x;
                y = prev.y;
                canvas.graphics.moveTo(x,y);
                curAngle = prev.angle;
            break;
            default :
                /* Nothing */
        }
    }
}

internal class State {
    public var x:Number;
    public var y:Number;
    public var angle:Number;
    public function State(x:Number, y:Number, angle:Number){
        this.x = x;
        this.y = y;
        this.angle = angle;

    }
}

Forked