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

Decagon fractal

Script interpreter, generates rhombs from Penrose tiling.
Get Adobe Flash player
by signedvoid 05 Jul 2012
/**
 * Copyright signedvoid ( http://wonderfl.net/user/signedvoid )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/z8pp
 */

// forked from signedvoid's P3 Penrose tiling (scripted)
package
{
    import com.bit101.components.*;
    
    import flash.display.BitmapData;
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.filters.GlowFilter;

    /*
     * 
    Target:
    Generate tilings with two types of rhombs:
    angles 36, 144 degrees and 72, 108 degrees.
    
     - learn how to draw each rhomb by point & angle
     - automate rhombs creation with a script
     - create suggestions of level/figure/point when typing new definition
    TODO:
     - advanced script options, like changing size (done), colors & effects
    
    */
    [SWF(width="465", height="465", backgroundColor="0x3C3C3F")]
    public class PenroseP3Tiling extends Sprite
    {
        protected var rhombs:Vector.<Rhomb>;
        protected var container:Sprite;

        protected var cx:Number = 465 * 0.5; //TODO: move into interpreter class
        protected var cy:Number = 465 * 0.5;
        
        protected var textArea:TextArea;
        protected var message:Label;
        protected var textAreaControl:CheckBox;
        protected var presets:ComboBox;
        protected var scripts:Array =
            [
                "//Star 1\n" +
                "side=25 cx=232 cy=295 //set options - side length and center point\n" +
                "5 T=72 //initial seed - five thick rhombs with angle 72 degrees\n" +
                "//uncomment following lines\n" +
                "//t 0 1 90, t 0 1 -18 // t for thin rhomb, then anchor figure 0, then anchor point 1, then angle\n" +
                "//T 0 0 36"
                ,
                "//Penrose P3\n" +
                "side=15 cx=232 cy=295\n" +
                "5 T=72\n" +
                "t 0 2 126\n" +
                "T 0 0 -72, T 0 0 72, T 0 2 72\n" +
                "T 0 2 -36, t 0 2 54, t 0 2 -126, t 0 2 18, t 0 2 -90\n" +
                "T 0 2 72, T 0 2 -144, T 4 3 -108, T 3 1 36\n" +
                "T 0 0 0, T 0 0 -72, t 0 2 -54, t 1 2 -18\n" +
                "T 2 1 108, T 3 3 -180, t 1 2 54\n" +
                "t 0 2 -18, t 0 2 18, T 0 0 36, T 1 0 -108, T 2 3 36, T 2 3 -108, T 2 3 -36\n" +
                "T 2 0 -36, T 3 0 -36, t 0 2 -90, t 1 2 90, t 6 2 90, t 6 2 -162, T 0 1 0\n" +
                "T 6 2 108, T 6 2 -108, T 6 2 36, T 6 2 -36, t 1 2 -126, t 1 2 -18, T 1 2 -72, t 2 2 54, t 2 2 -54, T 2 2 0, T 4 3 -72, T 5 1 0\n" +
                "T 0 3 72, t 0 3 18, T 7 2 -72, t 7 2 -18, T 5 2 -144, t 5 2 -90, T 9 2 -108, t 8 2 18, t 2 3 54, t 2 3 -54, T 2 3 0, T 6 2 -36, T 6 2 -108, T 9 2 36, T 9 2 -36, T 10 2 36, T 11 2 -108, t 10 2 -18, t 11 2 -54\n" +
                "T10 2 -108, T10 2 108, t9 2 -90, t 8 2 90, t 8 2 54, t9 2 -54, T15 2 0, T15 2 -72, T 16 2 0, T16 2 -72, T 17 3 -36\n" +
                "T 3 3 72, T 2 1 -72, T 2 0 0, T 3 0 0, t 0 1 90, T 0 1 36, T 1 3 -36, t 1 3 18, t 0 1 -18, t 7 2 54, t 6 2 -90, T 10 2 72, T 10 2 -144, t 8 2 -126, t 9 2 18\n"
                ,
                "//Decagon fractal\n" +
                "side=15 cx=232 cy=295\n" +
                "10 t=36\n" +
                "T 0 1 18\n" +
                "T 0 2 -90\n" +
                "t 0 1 108, t 0 1 0\n" +
                "T 0 0 54, T 0 2 -18, T 1 2 126, T 1 2 -126\n" +
                "T 3 2 -18, t 2 0 72, t 2 0 -72\n" +
                "T 1 3 -90, t 0 2 72, t 0 2 -108\n" +
                "t 2 1 72, t 0 1 0, T 1 2 -54, T 1 2 54, T 1 3 -54, T 2 1 18\n" +
                "t 1 1 0, t 1 3 0, t 1 2 0, T 2 1 -54, T 2 2 -54, T 3 3 54, T 3 2 54\n" +
                "T 0 2 126, T 2 2 126, T 2 3 -126, T 2 2 -126\n" +
                "T 2 1 -126, T 3 1 -126, T 1 2 126, T 1 3 126, t 1 0 72, t 1 0 -72, t 3 1 -72, t 1 3 72\n" +
                "T 0 2 -18, T 0 1 -18, T 2 3 18, t 4 3 72, t 4 2 72, t 5 1 -72, t 5 2 -72\n" +
                "T 0 2 -18, T 5 0 90, T 5 1 90, T 3 3 -90, t 4 2 -72, t 3 2 -72, t 5 2 72, t 6 2 72\n" +
                "T 2 3 90, t 4 2 -72, t 4 1 -72, t 7 2 72, t 7 3 72\n" +
                "t 2 0 108, t 2 1 108, t 4 3 -108\n" +
                "t 2 1 -108"
                ,
                "//Decagon fractal 2\n" +
                "side=6 cx=232 cy=295\n" +
                "10 t=36\n" +
                "T 0 1 18\n" +
                "T 0 2 -90\n" +
                "t 0 1 108, t 0 1 0\n" +
                "T 0 0 54, T 0 2 -18, T 1 2 126, T 1 2 -126\n" +
                "T 3 2 -18, t 2 0 72, t 2 0 -72\n" +
                "T 1 3 -90, t 0 2 72, t 0 2 -108\n" +
                "side=12 //next three lines are repeating\n" +
                "t0 1 0, T0 1 54, T0 1 -54\n" +
                "T0 2 126, T0 2 -126, T1 2 18, t 0 2 72, t 0 2 -72\n" +
                "T3 3 -90, t 2 2 -72, t 2 2 108\n" +
                "side=24\n" +
                "t0 1 0, T0 1 54, T0 1 -54\n" +
                "T0 2 126, T0 2 -126, T1 2 18, t 0 2 72, t 0 2 -72\n" +
                "T3 3 -90, t 2 2 -72, t 2 2 108\n" +
                "side=48\n" +
                "t0 1 0, T0 1 54, T0 1 -54\n" +
                "T0 2 126, T0 2 -126, T1 2 18, t 0 2 72, t 0 2 -72\n" +
                "T3 3 -90, t 2 2 -72, t 2 2 108"
            ];
        protected var selectionBitmap:BitmapData;
        
        public function PenroseP3Tiling()
        {
            //init
            rhombs = new Vector.<Rhomb>();
            container = new Sprite();
            addChild(container);
            Rhomb.rhombs = rhombs;
            selectionBitmap = new BitmapData(2, 2, false, 0xFFFFFF);
            selectionBitmap.setPixel(0, 0, 0);
            selectionBitmap.setPixel(1, 1, 0);
            new ThinRhomb().init();
            new ThickRhomb().init();
            
            //gui
            textArea = new TextArea(container, 20, 0, scripts[0]);
            textArea.width = 445;
            textArea.height = 130;
            textArea.addEventListener(Event.CHANGE, onScriptChanged);
            message = new Label(container, 0, 445);
            textAreaControl = new CheckBox(container, 6, 6, "", onTextAreaToggle);
            textAreaControl.selected = true;
            var items:Array = [];
            var firstLineRegex:RegExp = /\/\/(.*)\n|\r/;
            var scriptNo:int = 1;
            for each (var script:String in scripts)
            {
                var lineMatch:Object = firstLineRegex.exec(script);
                if (lineMatch)
                    items.push(lineMatch[1]);
                else
                    items.push("preset #" + scriptNo);
            }
            presets = new ComboBox(container, 20, 20, "", items);
            presets.width = 110;
            presets.rotation = 90;
            presets.addEventListener(Event.SELECT, onPresetChanged);
            presets.selectedIndex = 0;
            
            onScriptChanged(null);
            container.filters = [new GlowFilter(0x0, 0.8, 10, 10, 2, 1, false)];
        }

        protected function onScriptChanged(event:Event):void
        {
            var result:String = parseString(textArea.text);
            message.text = result;
            updateTiling();
        }

        protected function onPresetChanged(event:Event):void
        {
            textArea.text = scripts[presets.selectedIndex];
            onScriptChanged(null);
        }
        
        protected function onTextAreaToggle(event:Event):void
        {
            textArea.visible = textAreaControl.selected;
        }

        protected function parseString(script:String):String
        {
            //state machine stuff
            rhombs.length = 0;
            var angles:Vector.<Number> = new Vector.<Number>();
            var lastLevel:Vector.<Rhomb> = new Vector.<Rhomb>();
            var angle:Number;
            var rhombType:String;
            var rhombAngle:Number;
            var rhomb:Rhomb;
            var lastLevelLength:int = 1;
            var match:Object;
            var newLevelLength:int;
            var round:int, rounds:int;
            
            //parsing stuff
            var lines:Array = script.split(/\n|\r/);
            var stateHeader:Boolean = true;
            var headerRegex:RegExp = /\s*(\d{1,2})\s?(t|T)=(\d{1,3})\s*/g;
            var normalRegex:RegExp = /\s*(t|T)\s?(\d{1,3})\s([0-3])\s([-+]?\d{1,3})\,?\s*/g;
            var unfinishedRegex:RegExp = /\s*(t|T)\s?(\d{1,3})?\s?([0-3])?\s?([-+]?\d{1,3})?\,?\s*/g;
            var lineNo:int = 0;
            var message:String;
            
            LINES : for each (var rawLine:String in lines)
            {
                lineNo++;
                if (!rawLine || !rawLine.length) continue;
                var comment:int = rawLine.indexOf("//");
                if (!comment) continue;
                var line:String = comment > 0 ? rawLine.substring(0, comment) : rawLine;
                trace(line);
                angle = 0;
                if (stateHeader)
                {
                    headerRegex.lastIndex = 0;
                    match = headerRegex.exec(line);
                    if (!match)
                    {
                        message = parseOptionsLine(line);
                        if (message)
                            return "Header invalid: line " + lineNo + ", pos " + headerRegex.lastIndex + ", " + message;
                        continue;
                    }
                    rounds = (int)(match[1]);
                    rhombType = match[2];
                    rhombAngle = (Number)(match[3]);
                    for (round = 0; round < rounds; round++)
                    {
                        rhomb = (rhombType == "t") ? new ThinRhomb() : new ThickRhomb();
                        rhomb.byAxis(cx, cy, angle);
                        angles.push(rhombAngle);
                        angle += rhombAngle;
                        lastLevel.push(rhomb);
                    }
                    stateHeader = false;
                } else
                {
                    if (!angles) return "No header found: line " + lineNo;
                    var thisLevel:Vector.<Rhomb> = new Vector.<Rhomb>();
                    ROUNDS : for (round = 0; round < rounds; round++)
                    {
                        var earlyExit:Boolean = false;
                        normalRegex.lastIndex = 0;
                        newLevelLength = 0;
                        do
                        {
                            unfinishedRegex.lastIndex = normalRegex.lastIndex;
                            match = normalRegex.exec(line);
                            if (!match)
                            {
                                //try unfinished regex
                                match = unfinishedRegex.exec(line);
                                if (!match)
                                {
                                    message = parseOptionsLine(line);
                                    if (message)
                                        return "Line invalid: line " + lineNo + ", pos " + normalRegex.lastIndex + ", " + message;
                                    else
                                        continue LINES;
                                }
                                var prev:Rhomb;
                                if (match[2] == undefined)
                                {
                                    //select entire previous level
                                    for each (prev in lastLevel)
                                    {
                                        prev.Selected = true;
                                    }
                                } else if (match[3] == undefined)
                                {
                                    //select anchor figures
                                    anchorIndex = (int)(match[2]);
                                    if (anchorIndex < lastLevelLength)
                                    {
                                        for (var r:int = 0; r < rounds; r++)
                                        {
                                            prev = lastLevel[r * lastLevelLength + anchorIndex];
                                            prev.Selected = true;
                                        }
                                    }
                                } else
                                {
                                    //select anchor point
                                    anchorIndex = (int)(match[2]);
                                    anchorPoint = (int)(match[3]);
                                    if (anchorIndex < lastLevelLength && anchorPoint <= 3)
                                    {
                                        for (var r1:int = 0; r1 < rounds; r1++)
                                        {
                                            prev = lastLevel[r1 * lastLevelLength + anchorIndex];
                                            prev.PointSelected = true;
                                            prev.SelectedX = prev.x(anchorPoint);
                                            prev.SelectedY = prev.y(anchorPoint);
                                        }
                                    }
                                }
                                earlyExit = true;
                                continue;
                                //return "Unfinished token at " + lineNo;
                            }
                            rhombType = match[1];
                            var anchorIndex:int = (int)(match[2]);
                            var anchorPoint:int = (int)(match[3]);
                            rhombAngle = (Number)(match[4]);
                            rhomb = (rhombType == "t") ? new ThinRhomb() : new ThickRhomb();
                            var lastFigureIndex:int = round * lastLevelLength + anchorIndex;
                            if (lastFigureIndex >= lastLevel.length)
                                return "Anchor figure index out of range: " + lastFigureIndex;
                            var anchorFigure:Rhomb = lastLevel[lastFigureIndex];
                            var anchorX:Number = anchorFigure.x(anchorPoint);
                            var anchorY:Number = anchorFigure.y(anchorPoint);
                            rhomb.byAxis(anchorX, anchorY, angle + rhombAngle);
                            thisLevel.push(rhomb);
                            newLevelLength++;
                        } while (normalRegex.lastIndex < line.length && !earlyExit);
                        angle += angles[round];
                    }
                    lastLevelLength = newLevelLength;
                    lastLevel = thisLevel;
                }
            }
            
            return null;
        }

        protected function parseOptionsLine(line:String):String
        {
            var optionsRegex:RegExp = /\s*([a-z]+)=(\d{1,})\s*/g;
            do
            {
                var match:Object = optionsRegex.exec(line);
                if (!match) return null;
                var name:String = match[1];
                var value:int = (int)(match[2]);
                switch (name)
                {
                    case "side":
                        if (value > 0)
                        {
                            var tcr:ThickRhomb = new ThickRhomb();
                            tcr.Side = value;
                            tcr.init();
                            var tnr:ThinRhomb = new ThinRhomb();
                            tnr.Side = value;
                            tnr.init();
                        }
                        break;
                    case "cx":
                        cx = value;
                        break;
                    case "cy":
                        cy = value;
                        break;
                    default:
                        return "unknown token: " + name;
                }
            } while (optionsRegex.lastIndex < line.length);
            return null;
        }

        protected function updateTiling():void
        {
            var g:Graphics = container.graphics;
            g.clear();
            g.lineStyle(1, 0xFFFFFF);
            g.drawRect(1, 1, 463, 463);
            for each (var rhomb:Rhomb in rhombs)
            {
                g.lineStyle(1);
                if (rhomb.Selected)
                {
                    g.beginBitmapFill(selectionBitmap);
                } else
                {
                    g.beginFill(rhomb.color, 0.5);
                }
                g.moveTo(rhomb.p1x, rhomb.p1y);
                g.lineTo(rhomb.p2x, rhomb.p2y);
                g.lineTo(rhomb.p3x, rhomb.p3y);
                g.lineTo(rhomb.p4x, rhomb.p4y);
                g.endFill();
                if (rhomb.PointSelected)
                {
                    g.lineStyle(1, 0xFFFFFF);
                    g.drawCircle(rhomb.SelectedX, rhomb.SelectedY, 3);
                }
            }
        }
    }
}

import flash.geom.Point;

class Rhomb
{
    public function get AcuteAngle():Number { return 0; }
    public function get Color():uint { return 0; }
    public function get BigDiag():Number { return 0; }
    public function get SmallDiag():Number { return 0; }

    protected static var side:Number = 10;

    public var p1x:Number, p1y:Number;
    public var p2x:Number, p2y:Number;
    public var p3x:Number, p3y:Number;
    public var p4x:Number, p4y:Number;

    public var Selected:Boolean;
    public var PointSelected:Boolean;
    public var SelectedX:Number;
    public var SelectedY:Number;

    public static var rhombs:Vector.<Rhomb>;

    public function get Side():Number
    {
        return side;
    }

    public function set Side(value:Number):void
    {
        side = value;
        init();
    }

    public function Rhomb():void
    {
        if (rhombs) rhombs.push(this);
    }
    
    public function get color():uint
    {
        return Color;
    }

    public function x(index:int):Number
    {
        switch (index)
        {
            case 0: return p1x;
            case 1: return p2x;
            case 2: return p3x;
            case 3: return p4x;
        }
        return NaN;
    }

    public function y(index:int):Number
    {
        switch (index)
        {
            case 0: return p1y;
            case 1: return p2y;
            case 2: return p3y;
            case 3: return p4y;
        }
        return NaN;
    }

    /*         p2
               ^
           .   a2  .
       .               .
 p1 <  a1)           (a1  > p3
       .               .
           .   a2  .
               v
              p4
    */
    
    public function byAxis(i1x:Number, i1y:Number, angle:Number):void
    {
        var aRad:Number = angle * Math.PI / 180;
        p1x = i1x;
        p1y = i1y;
        p2x = p1x + Side * Math.sin(aRad + AcuteAngle * 0.5);
        p2y = p1y + Side * Math.cos(aRad + AcuteAngle * 0.5);
        p4x = p1x + Side * Math.sin(aRad - AcuteAngle * 0.5);
        p4y = p1y + Side * Math.cos(aRad - AcuteAngle * 0.5);
        p3x = p1x + BigDiag * Math.sin(aRad);
        p3y = p1y + BigDiag * Math.cos(aRad);
    }

    public function init():void {}
}

class ThinRhomb extends Rhomb
{
    protected static var bigDiag:Number, smallDiag:Number;
    protected static var acuteAngle:Number = 36 * Math.PI / 180;

    override public function get AcuteAngle():Number
    {
        return acuteAngle;
    }


    override public function get Color():uint
    {
        return 0xB00000;
    }

    override public function get BigDiag():Number
    {
        return bigDiag;
    }
    
    override public function get SmallDiag():Number
    {
        return smallDiag;
    }

    override public function init():void
    {
        bigDiag = side * 2 * Math.cos(AcuteAngle * 0.5);
        smallDiag = side * 2 * Math.sin(AcuteAngle * 0.5);
    }
}

class ThickRhomb extends Rhomb
{
    protected static var bigDiag:Number, smallDiag:Number;
    protected static const acuteAngle:Number = 72 * Math.PI / 180;

    override public function get AcuteAngle():Number
    {
        return acuteAngle;
    }

    override public function get Color():uint
    {
        return 0x00B000;
    }

    override public function get BigDiag():Number
    {
        return bigDiag;
    }

    override public function get SmallDiag():Number
    {
        return smallDiag;
    }
    
    override public function init():void
    {
        bigDiag = side * 2 * Math.cos(AcuteAngle * 0.5);
        smallDiag = side * 2 * Math.sin(AcuteAngle * 0.5);
    }
}