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 
Views