computational craft

Fractals (flora): Recursion, Substitution, L-systems

posterldiagram

simple recursion

Recursion is the “[d]efining a program in such a way that it may call itself, so that use of the program may occur again and again during its execution.” (Arbib)
Here we create a function called add wiggle, that creates a series of lines as a wiggle from a single straight line input. The function can then be called on to replace the straight line sub-components of the wiggly line etc. etc. “This could go on forever” (processing error, presumably Ben Fry and Casey Reas).
koch

koch curve

Option Explicit
'Script written for Pratt Advanced Computational Geometry seminar 2008
'Ezio Blasetti and Dave Pigram

Call Main()
Sub Main()
    Dim strObject : strObject = Rhino.GetObject("give me a line to make wiggles out of", 4)
    If IsNull(strObject) Then Exit Sub 
    Dim dblMinLength : dblMinLength = Rhino.GetReal("input the minimum length of the line", 0.1,0.0001)
    If IsNull(dblMinLength) Then Exit Sub
    Call addWiggle (strObject, dblMinLength)       
End Sub

Function addWiggle (strLine,dblMinLength)
    addWiggle = Null
    If Rhino.CurveLength(strline) < dblMinLength Then Exit Function
    'divide the line to 4 segments
    Dim arrPoints : arrpoints = Rhino.DivideCurve(strLine,4)   
    'draw line from point 2 to 3
    Dim strConstructLine : strConstructLine = Rhino.AddLine(arrPoints(2),arrPoints(3))
    'rotate 90 degrees to the previous line
    Call Rhino.RotateObject(strConstructLine,arrPoints(2),90,,VbFalse) 
    'get the end point of the construction curve
    Dim arrEndPoint : arrEndPoint = Rhino.CurveEndPoint(strConstructLine)
    'draw the new lines
    Dim arrnewLines(3)
    arrNewLines(0) = Rhino.AddLine(arrPoints(0),arrPoints(1))
    arrNewLines(1) = Rhino.AddLine(arrPoints(1),arrEndPoint)
    arrNewLines(2) = Rhino.AddLine(arrEndPoint,arrPoints(3))
    arrNewLines(3) = Rhino.AddLine(arrPoints(3),arrPoints(4))
    'clean up
    Rhino.DeleteObject strLine
    rhino.DeleteObject strConstructLine
    'feed the new lines one of the time into the function
    'loop through the new lines
    Dim i, intCounter
    For i = 0 To Ubound(arrNewLines)
        Call addWiggle(arrnewLines(i),dblMinLength)
        intCounter = intcounter +1
        If intCounter = dblMinLength Then Exit Function
    Next
    'return the strings of all new lines
    addwiggle = arrNewLines
End Function

postertree

recursive tree from rhinoscript 101

Option Explicit

'This script features an implementation of the recursive growth algorithm.
'A main subroutine was added (this one is missing from the primer, because
'it is very boring) to cater for the frontend.
'Comments have been added to this subroutine to explain the more exotic calls

'We initialize a set of variables that hold 'sticky' growth properties
'Since we declare these variables OUTSIDE of any procedure block, they will
'remain active after the script completes. This means the settings are remembered
'between script runs but they will be erased whenever Rhino exits.
'They all start out being uninitialized (not assigned any value)

Dim prop_MinTwigCount
Dim prop_MaxTwigCount
Dim prop_MaxGenerations
Dim prop_MaxTwigLength
Dim prop_LengthMutation
Dim prop_MaxTwigAngle
Dim prop_AngleMutation

Call PlantGenerator()
Sub PlantGenerator()
    'Ask for a start point of the plant.
    Dim ptRoot : ptRoot = Rhino.GetPoint("Root point for plant")
    If IsNull(ptRoot) Then Exit Sub
    'Check all global variables for initialization and assign default
    'values if they have not yet been set. IsEmpty() can be used to
    'detect uninitialized variables (as opposed to IsNull() which is
    'used to detect faulty return values)
    If IsEmpty(prop_MinTwigCount)   Then prop_MinTwigCount = 1
    If IsEmpty(prop_MaxTwigCount)   Then prop_MaxTwigCount = 8
    If IsEmpty(prop_MaxGenerations) Then prop_MaxGenerations = 5
    If IsEmpty(prop_MaxTwigLength)  Then prop_MaxTwigLength = 10.0
    If IsEmpty(prop_LengthMutation) Then prop_LengthMutation = 0.75
    If IsEmpty(prop_MaxTwigAngle)   Then prop_MaxTwigAngle = 30.0
    If IsEmpty(prop_AngleMutation)  Then prop_AngleMutation = 0.85
    'We'll be using a temporary variable to collect data from the user.
    'We do this to prevent exposing the global variables to the frontend of Rhino
    Dim local_Value
    'Now collect all growth properties using a series of GetInteger and GetReal methods
    'This could be done a lot cleaner using an iterated Option getter, but that's a lot
    'more code. See chapter 8 on UI methods.
    'Some of these values have limits on their value. See the RhinoScript help
    'for information about the arguments.
    local_Value = Rhino.GetInteger("Minimum twig count", prop_MinTwigCount)
    If IsNull(local_Value) Then Exit Sub
    prop_MinTwigCount = local_Value
    local_Value = Rhino.GetInteger("Maximum twig count", prop_MaxTwigCount, 1)
    If IsNull(local_Value) Then Exit Sub
    prop_MaxTwigCount = local_Value
    local_Value = Rhino.GetInteger("Maximum branch generations", prop_MaxGenerations, 1, 1000)
    If IsNull(local_Value) Then Exit Sub
    prop_MaxGenerations = local_Value
    local_Value = Rhino.GetReal("Maximum twig length", prop_MaxTwigLength, 0.01)
    If IsNull(local_Value) Then Exit Sub
    prop_MaxTwigLength = local_Value
    local_Value = Rhino.GetReal("Twig length mutation", prop_LengthMutation, 0.01)
    If IsNull(local_Value) Then Exit Sub
    prop_LengthMutation = local_Value
    local_Value = Rhino.GetReal("Maximum twig angle", prop_MaxTwigAngle, 0.0, 90.0)
    If IsNull(local_Value) Then Exit Sub
    prop_MaxTwigAngle = local_Value
    local_Value = Rhino.GetReal("Twig angle mutation", prop_AngleMutation, 0.01)
    If IsNull(local_Value) Then Exit Sub
    prop_AngleMutation = local_Value
    'Assuming we made it this far (I.e. the user has not pressed Escape yet), randomize the
    'vbScript engine. It's a good idea to use the Randomize() function to make sure that random
    'sequences are not repeated.
    Call Randomize()
    'Turning of redraw will drastically speed up the tree generation. However, it's quite fun to watch
    'so you might consider switching it off.
    Call Rhino.EnableRedraw(False)
    'Collect all tree-growth variables into a single array.
    Dim GrowthProps
    GrowthProps = Array(prop_MinTwigCount, prop_MaxTwigCount, prop_MaxGenerations, _
prop_MaxTwigLength, prop_LengthMutation, prop_MaxTwigAngle, prop_AngleMutation)
    'Call the recursive function the first time. It will call itself from now on.
    'We also need to tell this first function that is has generation 1
    'We're assuming the growth direction at the root is vertical,
    'so we have to supply a (0,0,1) direction vector.
    Call RecursiveGrowth(ptRoot, Array(0,0,1), GrowthProps, 1)
    'After all recursion has completed, be sure to enable the redraw again.
    Call Rhino.EnableRedraw(True)
End Sub

'---------------------------------------------------------------------------------------
'-------------------------the code below appears in the primer--------------------------
'---------------------------------------------------------------------------------------
Sub RecursiveGrowth(ByVal ptStart, ByVal vecDir, ByVal Props(), ByVal Generation)
    If Generation > Props(2) Then Exit Sub
    Dim ptGrow, vecGrow, newTwig
    'Copy and mutate the growth-properties
    Dim newProps : newProps = Props
    newProps(3) = Props(3) * Props(4)
    newProps(5) = Props(5) * Props(6)
    If newProps(5) > 90 Then newProps(5) = 90
    'Determine the number of twigs (could be less than zero)
    Dim N, maxN
    maxN = CInt(Props(0) + Rnd() * (Props(1) - Props(0)))
    For N = 1 To maxN
        ptGrow = RandomPointInCone(ptStart, vecDir, 0.25*Props(3), Props(3), Props(5))
        newTwig = AddArcDir(ptStart, ptGrow, vecDir)
        If Not IsNull(newTwig) Then
            vecGrow = Rhino.CurveTangent(newTwig, Rhino.CurveDomain(newTwig)(1))
            Call RecursiveGrowth(ptGrow, vecGrow, newProps, Generation+1)
        End If
    Next
End Sub



Function RandomPointInCone(ByVal Origin, ByVal Direction, ByVal MinDistance, ByVal MaxDistance, ByVal MaxAngle)
    Dim vecTwig
    vecTwig = Rhino.VectorUnitize(Direction)
    vecTwig = Rhino.VectorScale(vecTwig, MinDistance + Rnd() * (MaxDistance-MinDistance))
    Dim MutationPlane
    MutationPlane = Rhino.PlaneFromNormal(Array(0,0,0), vecTwig)
    vecTwig = Rhino.VectorRotate(vecTwig, Rnd() * maxAngle, MutationPlane(1))
    vecTwig = Rhino.VectorRotate(vecTwig, Rnd() * 360, Direction)
    RandomPointInCone = Rhino.PointAdd(Origin, vecTwig)
End Function

Function AddArcDir(ByVal ptStart, ByVal ptEnd, ByVal vecDir)
    AddArcDir = Null
    Dim vecBase : vecBase = Rhino.PointSubtract(ptEnd, ptStart)
    If Rhino.VectorLength(vecBase) = 0.0 Then Exit Function
    If Rhino.IsVectorParallelTo(vecBase, vecDir) Then
        AddArcDir = Rhino.AddLine(ptStart, ptEnd)
        Exit Function
    End If
    vecBase = Rhino.VectorUnitize(vecBase)
    vecDir = Rhino.VectorUnitize(vecDir)
    Dim vecBisector : vecBisector = Rhino.VectorAdd(vecDir, vecBase)
    vecBisector = Rhino.VectorUnitize(vecBisector)
    Dim dotProd : dotProd = Rhino.VectorDotProduct(vecBisector, vecDir)
    Dim midLength : midLength = (0.5 * Rhino.Distance(ptStart, ptEnd)) / dotProd
    vecBisector = Rhino.VectorScale(vecBisector, midLength)
    AddArcDir = Rhino.AddArc3Pt(ptStart, ptEnd, Rhino.PointAdd(ptStart, vecBisector))
End Function

l-systems

posterldots

lsystem definition from wikipedia

An L-system or Lindenmayer system is a parallel rewriting system, namely a variant of a formal grammar (a set of rules and symbols), most famously used to model the growth processes of plant development, but also able to model the morphology of a variety of organisms.[1] L-systems can also be used to generate self-similar fractals such as iterated function systems. L-systems were introduced and developed in 1968 by the Hungarian theoretical biologist and botanist from the University of Utrecht, Aristid Lindenmayer (1925–1989).

simple lsystem data structure

Option Explicit

Call Main()
Sub Main()
    'set initial state
    Dim strGeneration : strGeneration = "A"
    Call makeDots(strGeneration, -1)
    'loop through generations
    Dim i
    For i = 0 To 15
        'print the current contents of the generation
        Rhino.print "generation " & i & ": " & strGeneration
        'enact the substitution rules
        strGeneration = ApplyRules(strGeneration)
        Call makeDots(strGeneration, i)
    Next
    Rhino.print "generation " & i & ": " & strGeneration
End Sub

Function ApplyRules(strGen)
   
    strGen      = Replace(strGen, "C_A", "0")
    strGen      = Replace(strGen, "A", "1")
    strGen      = Replace(strGen, "B", "2")
    strGen      = Replace(strGen, "C", "3")
   
    strGen      = Replace(strGen, "0", "C")
    strGen      = Replace(strGen, "1", "B")
    strGen      = Replace(strGen, "2", "C")
    strGen      = Replace(strGen, "3", "A_B")

    ApplyRules  = strGen
End Function

Sub makeDots(strText, intCurrentGen)
    Dim i, arrTokens
    arrTokens = Rhino.Strtok(strText,"_")
    For i = 0 To Ubound(arrTokens)
        Call Rhino.AddTextDot   (arrTokens(i), array(i,intCurrentGen))
    Next       
End Sub

posterlcubes

simple lsystem data driving cubes

Option Explicit

Call Main()
Sub Main()
    'set initial state
    Dim strGeneration : strGeneration = "A"
    Call makeDots(strGeneration, -1)
    'loop through generations
    Dim i
    For i = 0 To 15
        'print the current contents of the generation
        Rhino.print "generation " & i & ": " & strGeneration
        'enact the substitution rules
        strGeneration = ApplyRules(strGeneration)
        Call makeDots(strGeneration, i)
    Next
    Rhino.print "generation " & i & ": " & strGeneration
End Sub

Function ApplyRules(strGen)
    strGen      = Replace(strGen, "C_A", "0")
    strGen      = Replace(strGen, "A", "1")
    strGen      = Replace(strGen, "B", "2")
    strGen      = Replace(strGen, "C", "3")
   
    strGen      = Replace(strGen, "0", "C")
    strGen      = Replace(strGen, "1", "B")
    strGen      = Replace(strGen, "2", "C")
    strGen      = Replace(strGen, "3", "A_B")
    ApplyRules  = strGen
End Function

Sub makeDots(strText, intCurrentGen)
    Dim i, arrTokens
    arrTokens = Rhino.Strtok(strText,"_")
    For i = 0 To Ubound(arrTokens)
        If arrTokens(i)="A" Then
            Rhino.AddBox(array(array(i,intCurrentGen,0),array(i+1,intCurrentGen,0),array(i+1,intCurrentGen+1,0),array(i,intCurrentGen+1,0),_
                array(i,intCurrentGen,1),array(i+1,intCurrentGen,1),array(i+1,intCurrentGen+1,1),array(i,intCurrentGen+1,1)))
        ElseIf arrTokens(i)="B" Then
            Rhino.AddBox(array(array(i,intCurrentGen,0),array(i+1,intCurrentGen,0),array(i+1,intCurrentGen+1,0),array(i,intCurrentGen+1,0),_
                array(i,intCurrentGen,2),array(i+1,intCurrentGen,2),array(i+1,intCurrentGen+1,2),array(i,intCurrentGen+1,2)))
        ElseIf arrTokens(i)="C" Then
            Rhino.AddBox(array(array(i,intCurrentGen,0),array(i+1,intCurrentGen,0),array(i+1,intCurrentGen+1,0),array(i,intCurrentGen+1,0),_
                array(i,intCurrentGen,3),array(i+1,intCurrentGen,3),array(i+1,intCurrentGen+1,3),array(i,intCurrentGen+1,3)))
        End If
    Next      
End Sub

lsystem diagram with interface for rule input

download compiled plugin with installation instuctions

Option Explicit
'Script written by Ezio Blasetti
'Some custom functions belong to earlier script by Dave Pigram
'Script version Sunday, February 08, 2009 8:36:45 PM

Call Setup()
Sub Setup()
    Dim intNumberRules          : intNumberRules        = Rhino.GetInteger("Input the number of rules", 4, 2)
    Dim intGenerations          : intGenerations        = Rhino.GetInteger("how many generations", 10, 3)
    Dim dblDistance             : dblDistance           = Rhino.GetReal("total width of the diagram",10)
    Dim strGeneration           : strGeneration         = Rhino.StringBox("use CAPS for letters and space for delimiters","A B", "Initial Generation Input")
    Dim arrRuleStrings          : arrRuleStrings        = getRules(intNumberRules)
    Dim arrblnDrawTree          : arrblnDrawTree        = Rhino.GetBoolean("add the tree diagram", array("drawTree","No","Yes"),array(True))
    Dim arrStartStrings         : arrStartStrings       = arrRuleStrings(0)
    Dim arrSubstituteStrings    : arrSubstituteStrings  = arrRuleStrings(1)
    Dim arrTokens               : arrTokens             = Rhino.Strtok(strGeneration)
    Dim minX                    : minX                  = 0
    Dim maxX                    : maxX                  = dblDistance
    Dim dblSpacing              : dblSpacing            = (maxX-minX)/(Ubound(arrTokens)+1)
    Dim arrCharacters           : arrCharacters         = CullDuplicateStrFrom3Arrays(arrTokens, arrStartStrings, arrSubstituteStrings)
    'Dim arrCharacters          : arrCharacters         = array("A","B","C","D")
    Call Rhino.AddLayer("0")
    Call Rhino.EnableRedraw(vbFalse)
    Dim i, j
    For i=0 To Ubound(arrTokens)
        maxX = minX + dblSpacing
        Dim strPlane : strPlane = Rhino.AddSrfPt (array(array(minX,0,0),array(maxX,0,0),array(maxX,-1,0),array(minX,-1,0)))
        'Dim strPlane : strPlane    = Rhino.AddSrfPt (array(array(i,0,0),array(i+1,0,0),array(i+1,-1,0),array(i,-1,0)))
        minX = maxX
        Call Rhino.ObjectName(strPlane, Ucase(arrTokens(i)))
        Call Rhino.ObjectLayer(strPlane, "0")
        For j=0 To Ubound(arrCharacters)
            If arrTokens(i)=arrCharacters(j) Then
                Dim intColorValue : intColorValue = j*(255/Ubound(arrCharacters))
                Call Rhino.ObjectColor(strPlane, RGB(intColorValue+30,0,intColorValue+30))
            End If
        Next
    Next
    Call Rhino.EnableRedraw(vbTrue)
    For i=1 To intGenerations
        Call Rhino.EnableRedraw(vbFalse)
        Call Main(i, arrStartStrings, arrSubstituteStrings, arrCharacters, arrblnDrawTree(0))
        Call Rhino.EnableRedraw(vbTrue)
    Next
    Call cleanUpLayers(arrCharacters, intGenerations)
    Rhino.Print "thanks for playing with me..."
End Sub

Sub Main(intGen, arrStartStrings, arrSubstituteStrings, arrCharacters, blnDrawTree)

    Dim arrParents : arrParents = Rhino.ObjectsByLayer(CStr(intGen-1))
    arrParents = reverseArrayOrder(arrParents)
    Call Rhino.AddLayer(intGen)
    If blnDrawTree Then
        Call Rhino.AddLayer("TreeLines")
    End If
    Dim i
    For i=0 To Ubound(arrParents)
       
        Dim strParent     : strParent     = arrParents(i)
        Dim strParentName : strParentName = Rhino.ObjectName   (strParent)
        Dim arrParentPts  : arrParentPts  = Rhino.SurfacePoints(strParent)
        Dim k : k=0
        Do While k <= Ubound(arrStartStrings) 'Ubound(arrStartStrings) is equal to intNumberRules -1 ie loop through each rule     
            Dim arrRuleTokens : arrRuleTokens = Rhino.Strtok(arrStartStrings(k))               
            'skip the check if there are not enough letters left in the generation to match the current start string
            If Not i + Ubound(arrRuleTokens) > Ubound(arrParents) Then
                'reset the variable that holds the string to be compared to the current startString
                Dim strToCompare : strToCompare = Null                 
                'create a single string that is the same length as the current rule start string with underscores seperating each letter.
                Dim j
                For j = 0 To Ubound(arrRuleTokens) 
                    strToCompare        =   addString2String(strToCompare, Rhino.ObjectName(arrParents(i+j))," ")
                Next ' end ruleTokens loop             
                'compare to the rule and if it matches, make the substitution
                'print "k is: " & k
                If arrStartStrings(k)   =   strToCompare Then                      
                   
                    Dim arrTokens         : arrTokens          = Rhino.Strtok(arrSubstituteStrings(k))
                    Dim arrParentUboundPt : arrParentUboundPt  = Rhino.SurfacePoints(arrParents(i+Ubound(arrRuleTokens)))
                    Dim minX              : minX               = arrParentPts(0)(0)
                    Dim maxX              : maxX               = arrParentUboundPt(2)(0)
                    Dim dblSpacing        : dblSpacing         = (maxX-minX)/(Ubound(arrTokens)+1)
                    Dim e
                    For e=0 To Ubound(arrTokens)
                        'print "minX is:" & minX & " maxX is :" & maxX
                        maxX = minX + dblSpacing
                        'add the substitution string To the Next generation
                        Dim strPlane : strPlane = Rhino.AddSrfPt (array(array(minX,-intGen-1,0),array(maxX,-intGen-1,0),array(maxX,-intGen,0),array(minX,-intGen,0)))          
                        If blnDrawTree Then
                            Dim p
                            For p=0 To Ubound(arrRuleTokens)
                                Dim strLine : strLine = rhino.addline(Rhino.SurfaceAreaCentroid(arrParents(i+p))(0),Rhino.SurfaceAreaCentroid(strPlane)(0))
                                Call Rhino.ObjectLayer(strLine,"TreeLines")
                            Next
                        End If
                        minX = maxX
                        'print arrTokens(e)
                        Call Rhino.ObjectName(strPlane, Ucase(arrTokens(e)))
                        Call Rhino.ObjectLayer(strPlane, intGen)
                        Dim l
                        For l=0 To Ubound(arrCharacters)
                            If arrTokens(e)=arrCharacters(l) Then
                                Dim intColorValue : intColorValue = l*(255/Ubound(arrCharacters))
                                Call Rhino.ObjectColor(strPlane, RGB(intColorValue+30,0,intColorValue+30))
                            End If
                        Next
                    Next
                   
                    'finish this k-loop
                    k = Ubound(arrStartStrings)    
                    'skip ahead to the next unused letter in this generation, before checking against the first start string
                    i = i + Ubound(arrRuleTokens)
                End If 
            End If 
            'progress counter
            k = k + 1
        Loop 'arrStartStrings loop     
    Next
End Sub



Function CullDuplicateStrFrom3Arrays(arr0, arr1, arr2)
    CullDuplicateStrFrom3Arrays = Null
    If Not IsArray(arr0) Then Exit Function
    If Not IsArray(arr1) Then Exit Function
    If Not IsArray(arr2) Then Exit Function
   
    'arr0 = Rhino.CullDuplicateStrings(arr0)
    'arr1 = Rhino.CullDuplicateStrings(arr1)
    'arr2 = Rhino.CullDuplicateStrings(arr2)

    ReDim arrDifferentChars(0)
    Dim arrTokens, i, j, k, bln, blnTemp
    Dim str
    For i=0 To Ubound(arr0)
        str = arr0(i)
        If i=0 Then
            arrDifferentChars(0) = str
        End If
        arrTokens = Rhino.Strtok(str)
        For j=0 To Ubound(arrTokens)
            bln = VbFalse
            For k=0 To Ubound(arrDifferentChars)
                If arrTokens(j) = arrDifferentChars(k) Then
                    blnTemp = vbTrue
                Else
                    blnTemp = vbFalse
                End If
                bln = bln Or blnTemp
            Next
            If bln = vbFalse Then
                ReDim Preserve arrDifferentChars(Ubound(arrDifferentChars)+1)
                arrDifferentChars(Ubound(arrDifferentChars)) = arrTokens(j)
            End If
        Next
    Next
    For i=0 To Ubound(arr1)
        str = arr1(i)
        arrTokens = Rhino.Strtok(str)
        For j=0 To Ubound(arrTokens)
            bln = VbFalse
            For k=0 To Ubound(arrDifferentChars)
                If arrTokens(j) = arrDifferentChars(k) Then
                    blnTemp = vbTrue
                Else
                    blnTemp = vbFalse
                End If
                bln = bln Or blnTemp
            Next
            If bln = vbFalse Then
                ReDim Preserve arrDifferentChars(Ubound(arrDifferentChars)+1)
                arrDifferentChars(Ubound(arrDifferentChars)) = arrTokens(j)
            End If
        Next
    Next
    For i=0 To Ubound(arr2)
        str = arr2(i)
        arrTokens = Rhino.Strtok(str)
        For j=0 To Ubound(arrTokens)
            bln = VbFalse
            For k=0 To Ubound(arrDifferentChars)
                If arrTokens(j) = arrDifferentChars(k) Then
                    blnTemp = vbTrue
                Else
                    blnTemp = vbFalse
                End If
                bln = bln Or blnTemp
            Next
            If bln = vbFalse Then
                ReDim Preserve arrDifferentChars(Ubound(arrDifferentChars)+1)
                arrDifferentChars(Ubound(arrDifferentChars)) = arrTokens(j)
            End If
        Next
    Next
    CullDuplicateStrFrom3Arrays = arrDifferentChars
End Function

Function addString2String(strOriginalString, strStringToAdd, strSpacer)

    addString2String = Null
    Dim strNew 
    If IsNull(strOriginalString) Then
        strNew       = strStringToAdd
    Else
        strNew       = strOriginalString & strSpacer & strStringToAdd  
    End If 
    addString2String = strNew
   
End Function

Function reverseArrayOrder(arr)
    reverseArrayOrder = Null
    If Not IsArray(arr) Then Exit Function
    Dim arrReturn : arrReturn = arr
    Dim i
    Dim count : count = Ubound(arr)
    For i=0 To Ubound(arr)
        arrReturn(i) = arr(count)
        count= count - 1
    Next
    reverseArrayOrder = arrReturn
End Function

Function getRules(intNumberRules)

    getRules    = Null
   
    Dim i, arrRulePrompts(), arrDefaultStarts(), arrDefaultSubs(), arrStartStrings, arrSubstituteStrings
   
    ReDim arrRulePrompts    (intNumberRules - 1)
    ReDim arrDefaultStarts  (intNumberRules - 1)
    ReDim arrDefaultSubs    (intNumberRules - 1)
   
    'create arrays of strings to populate the Property List Boxes
    For i = 0 To intNumberRules - 1
   
        arrRulePrompts  (i) =   "Rule " & i & " Start String:"
       
        'get the previously used rules from a file
        arrDefaultStarts(i) =   Rhino.GetSettings(Rhino.InstallFolder & "Lsystem.ini", "L-System", "DefaultStarts_" & i)
        arrDefaultSubs  (i) =   Rhino.GetSettings(Rhino.InstallFolder & "Lsystem.ini", "L-System", "DefaultSubs_" & i)
       
        If IsNull(arrDefaultStarts  (i)) Then arrDefaultStarts  (i) = "X_X..."
        If IsNull(arrDefaultSubs    (i)) Then arrDefaultSubs    (i) = "X_X..."
       
    Next
   
    'prompt for the user to input the rules in two phases:  first the strings that will be replaced - this will be in heirarchial order ie first rule will be enacted first
    'longer strings (clauses) should be entered first.
    'In the second phase the leters that will be substituted For the starting strings are entered underscores ("_") must seperate Each letter
    arrStartStrings         =   Rhino.PropertyListBox (arrRulePrompts,  arrDefaultStarts, "use CAPS for letters and space for delimiters", "L-System Rules: Start Strings")
    arrSubstituteStrings    =   Rhino.PropertyListBox (arrStartStrings, arrDefaultSubs, "use CAPS for letters and space for delimiters", "L-System Rules: Substitute Strings")

    For i = 0 To intNumberRules - 1
        'write these rules to a file so that you don't have to re-enter them next time
        Rhino.SaveSettings Rhino.InstallFolder & "Lsystem.ini", "L-System", "DefaultStarts_" & i, arrStartStrings(i)
        Rhino.SaveSettings Rhino.InstallFolder & "Lsystem.ini", "L-System", "DefaultSubs_" & i, arrSubstituteStrings(i)
    Next
   
    getRules    =   array(arrStartStrings,arrSubstituteStrings)
   
End Function

Sub cleanUpLayers(arrCharacters, intGenerations)
    Dim strCharacter
    Call Rhino.EnableRedraw (vbFalse)
    For Each strCharacter In arrCharacters
        Call Rhino.AddLayer(strCharacter)
        Dim arrObjects : arrObjects = Rhino.ObjectsByName(Ucase(strCharacter))
        If Not IsNull(arrObjects) Then
            Dim strObject
            For Each strObject In arrObjects
                Call Rhino.ObjectLayer(strObject, strCharacter)
            Next
        End If
    Next
    Dim i
    For i=0 To intGenerations
        Rhino.DeleteLayer(i)
    Next
    Call Rhino.EnableRedraw (vbTrue)
End Sub