RhinoScript substitution and l-systems
From KokkugiaWiki
this is a simple example to introduce you to recursion and rule based substitution systems.
when using a substitution system where you need to check what type something is so that it can be substituted with something else, it is easiest to use the Select...Case conditional statement rather than many nested if....else statements. In this example iVariable is what is being checked and in the first example if iVariable is equal to 0 then the DoSomething() function is run.
Select Case iVariable
Case 0
DoSomething()
Case 1
DoSomethingElse()
Case 2
DoSomethingTotallyDifferent()
Case 30, 45, 60, 90, 180
SurpriseMe()
Case Else
DoWhateverIsNormal()
End Select
2d branching l-system
L-System is an abrreviated term for a Lyndenmayer-System, created by Aristid Lyndenmayer. The example below utilises a substitution system to generate an l-system type of branching behavior. for more examples on Lyndenmayer's work see rhinoScript dol-systems
Option Explicit '------------------------------------------------------------------------------ ' 2D L-System branching algorithm using object data ' Author: Robert Stuart-Smith | 2008 | www.kokkugia.com Call Main() Sub Main() Dim strObj, intGenerations, n, i,j,k Dim arrObj, arrObjNew, strObjNew Dim strBehavior, arrBehavior intGenerations = 25 ReDim arrObj(0) 'create starting object arrObj(0) = Rhino.AddLine (Array(0,0,0), Array(0,10, 0)) Rhino.SetObjectData arrObj(0) ,"behavior", "type","L" Rhino.EnableRedraw False 'loop generations For i = 0 To intGenerations ReDim arrObjNew(0) n =0 ' loop for last objects For j = 0 To Ubound(arrObj) 'check current object behavior/s arrBehavior = Rhino.Strtok ((Rhino.GetObjectData (arrObj(j),"behavior", "type" )), ",") For k = 0 To Ubound(arrBehavior) ' do something based on behavior/s of object Select Case arrBehavior(k) Case "L" 'call function to generate geometry strObjNew = addCurve(arrObj(j),0.8,30.0) 'store a behavior on the new object Rhino.SetObjectData strObjNew ,"behavior", "type","R" 'dynamically resize array to store new object temporarily ReDim Preserve arrObjNew(n) arrObjNew(n) = strObjNew n = n + 1 Case "R" strObjNew = addCurve(arrObj(j),0.8,-30.0) Rhino.SetObjectData strObjNew ,"behavior", "type","S" ReDim Preserve arrObjNew(n) arrObjNew(n) = strObjNew n = n + 1 Case "S" strObjNew = addCurve(arrObj(j),0.8,0.0) Rhino.SetObjectData strObjNew ,"behavior", "type","L,R" ReDim Preserve arrObjNew(n) arrObjNew(n) = strObjNew n = n + 1 End Select Next Next ' update list of last objects ReDim arrObj(Ubound(arrObjNew)) arrObj = arrObjNew Next Rhino.EnableRedraw True End Sub Function addCurve(strObj,dblScale,dblAngle) Dim arrPt1, arrPt2, arrVec1, strObjNew arrPt1 = Rhino.CurveStartPoint(strObj) arrPt2 = Rhino.CurveEndPoint(strObj) arrVec1 = Rhino.VectorCreate(arrPt2,arrPt1) arrVec1 = Rhino.VectorScale(arrVec1, dblScale) arrVec1 = Rhino.VectorRotate(arrVec1,dblAngle,Array(0,0,1)) arrVec1 = Rhino.VectorAdd(arrVec1,arrPt2) strObjNew = Rhino.AddLine(arrPt2, arrVec1) addCurve = strObjNew End Function
3d aggregation substitution system
the following example is a simple substitution system which can aggregate a chosen object.
for the next two examples, download this rhino file which has some geometry to aggregate.
Option Explicit
Public currentArray
Public arrType
Public arrPosition
Public objCount
Public currentCount
ReDim arrType(0)
arrType(0) = "a"
ReDim arrPosition(0)
arrPosition(0) = array(0,0,0)
ReDim currentArray(0)
currentArray(0) = 0
Call subAggregation()
Sub subAggregation()
Dim obj, startPos, startType, gens, objType, pos, i, j, newPos
Dim nextArray()
' user input
obj = Rhino.GetObject("choose an object to aggregate")
startPos = Rhino.GetPoint("select a starting location")
startType = Rhino.GetString("what is the starting type", "a")
gens = Rhino.GetReal("how many generations", 10)
objCount = 0
currentCount = 0
ReDim nextArray(0)
nextArray(0) = 0
' add the first piece to the array
makeObject startPos, startPos, startType, startPos, obj
' loop generations
For i = 0 To gens
' for each object test type
currentCount = 0
For j = 0 To UBound(nextArray)
objType = arrType(nextArray(j))
pos = arrPosition(nextArray(j))
Select Case objType
Case "a"
' a goes to b
' calculate new position
newPos = array(pos(0)+1, pos(1), pos(2)-1)
' call makeObject
makeObject pos, newPos, "b", startPos, obj
Case "b"
' b goes to a + c
newPos = array(pos(0)+1, pos(1), pos(2))
makeObject pos, newPos, "a", startPos, obj
newPos = array(pos(0), pos(1)+1, pos(2))
makeObject pos, newPos, "c", startPos, obj
Case "c"
' c goes to a
newPos = array(pos(0), pos(1), pos(2)+1)
makeObject pos, newPos, "a", startPos, obj
End Select
Next
' loop to replace nextArray with currentArray
ReDim nextArray(0)
For j = 0 To UBound(currentArray)
ReDim Preserve nextArray(j)
nextArray(j) = currentArray(j)
Next
Next
End Sub
Function makeObject(pos, newPos, newType, startPos, obj)
' populate the arrays with info
ReDim Preserve arrType(objCount)
arrType(objCount) = newType
ReDim Preserve arrPosition(objCount)
arrPosition(objCount) = newPos
ReDim Preserve currentArray(currentCount)
currentArray(currentCount) = objCount
objCount = objCount +1
currentCount = currentCount +1
' make the object
Rhino.AddLine pos, newPos
Rhino.AddTextDot newType, newPos
Rhino.CopyObject obj, startPos, newPos
End Function
3d aggregation substitution system with multiple object types
the following is a slightly more complex version of the previous example which assigns a different piece to each type
Option Explicit
Public currentArray
Public arrType
Public arrPosition
Public objCount
Public currentCount
ReDim arrType(0)
arrType(0) = "a"
ReDim arrPosition(0)
arrPosition(0) = array(0,0,0)
ReDim currentArray(0)
currentArray(0) = 0
Call subAggregation()
Sub subAggregation()
Dim obj, startPos, startType, gens, objType, pos, i, j, newPos
Dim nextArray()
Dim objA, objB, objC, objD, objE, objF, pointA, pointB, pointC, pointD, pointE, pointF
' user input
objA = Rhino.GetObject("select object a")
pointA = Rhino.GetPoint("select start point for object a")
objB = Rhino.GetObject("select object B")
pointB = Rhino.GetPoint("select start point for object B")
objC = Rhino.GetObject("select object C")
pointC = Rhino.GetPoint("select start point for object C")
objD = Rhino.GetObject("select object D")
pointD = Rhino.GetPoint("select start point for object D")
objE = Rhino.GetObject("select object E")
pointE = Rhino.GetPoint("select start point for object E")
objF = Rhino.GetObject("select object F")
pointF = Rhino.GetPoint("select start point for object F")
startPos = Rhino.GetPoint("select a starting location")
startType = Rhino.GetString("what is the starting type", "a")
gens = Rhino.GetReal("how many generations", 10)
objCount = 0
currentCount = 0
ReDim nextArray(0)
nextArray(0) = 0
' add the first piece to the array
makeObj startPos, startPos, startType, objA, pointA
' loop generations
For i = 0 To gens
' for each object test type
currentCount = 0
For j = 0 To UBound(nextArray)
objType = arrType(nextArray(j))
pos = arrPosition(nextArray(j))
Select Case objType
Case "a"
newPos = array(pos(0), pos(1)-2, pos(2))
makeObj pos, newPos, "b", objB, pointB
newPos = array(pos(0)-4, pos(1), pos(2))
makeObj pos, newPos, "c", objC, pointC
Case "b"
newPos = array(pos(0) + 1, pos(1)+5, pos(2))
makeObj pos, newPos, "f", objF, pointF
newPos = array(pos(0), pos(1)+6, pos(2)-1)
makeObj pos, newPos, "c", objC, pointC
Case "c"
newPos = array(pos(0), pos(1)-1, pos(2)+4)
makeObj pos, newPos, "d", objD, pointD
Case "d"
newPos = array(pos(0)+3, pos(1), pos(2))
makeObj pos, newPos, "e", objE, pointE
Case "e"
newPos = array(pos(0)+1, pos(1)-3, pos(2)+1)
makeObj pos, newPos, "a", objA, pointA
Case "f"
newPos = array(pos(0), pos(1), pos(2)-5)
makeObj pos, newPos, "d", objD, pointD
End Select
Next
' loop to replace nextArray with currentArray
ReDim nextArray(0)
For j = 0 To UBound(currentArray)
ReDim Preserve nextArray(j)
nextArray(j) = currentArray(j)
Next
Next
End Sub
Function makeObj(pos, newPos, newType, obj, startPos)
' populate the arrays with info
ReDim Preserve arrType(objCount)
arrType(objCount) = newType
ReDim Preserve arrPosition(objCount)
arrPosition(objCount) = newPos
ReDim Preserve currentArray(currentCount)
currentArray(currentCount) = objCount
objCount = objCount +1
currentCount = currentCount +1
' make the object
Rhino.AddLine pos, newPos
Rhino.AddTextDot newType, newPos
Rhino.CopyObject obj, startPos, newPos
End Function
a few different rule sets
Case "a" newPos = array(pos(0), pos(1)-2, pos(2)) makeObj pos, newPos, "b", objB, pointB Case "b" newPos = array(pos(0) + 1, pos(1)+5, pos(2)) makeObj pos, newPos, "f", objF, pointF newPos = array(pos(0), pos(1)+6, pos(2)-1) makeObj pos, newPos, "c", objC, pointC Case "c" newPos = array(pos(0), pos(1)-1, pos(2)+4) makeObj pos, newPos, "d", objD, pointD newPos = array(pos(0)+1, pos(1)-3, pos(2)+1) makeObj pos, newPos, "a", objA, pointA Case "d" newPos = array(pos(0)+3, pos(1), pos(2)) makeObj pos, newPos, "e", objE, pointE Case "e" newPos = array(pos(0)+1, pos(1)-3, pos(2)+1) makeObj pos, newPos, "a", objA, pointA Case "f" newPos = array(pos(0), pos(1), pos(2)-5) makeObj pos, newPos, "d", objD, pointD
another rule set - both of these end up back tracking and creating many duplicates
Case "a" newPos = array(pos(0), pos(1)-2, pos(2)) makeObj pos, newPos, "b", objB, pointB newPos = array(pos(0)-4, pos(1), pos(2)) makeObj pos, newPos, "c", objC, pointC Case "b" newPos = array(pos(0) + 1, pos(1)+5, pos(2)) makeObj pos, newPos, "f", objF, pointF newPos = array(pos(0), pos(1)+6, pos(2)-1) makeObj pos, newPos, "c", objC, pointC Case "c" newPos = array(pos(0), pos(1)-1, pos(2)+4) makeObj pos, newPos, "d", objD, pointD Case "d" newPos = array(pos(0)+3, pos(1), pos(2)) makeObj pos, newPos, "e", objE, pointE Case "e" newPos = array(pos(0)+1, pos(1)-3, pos(2)+1) makeObj pos, newPos, "a", objA, pointA Case "f" newPos = array(pos(0), pos(1), pos(2)-5) makeObj pos, newPos, "d", objD, pointD
