RhinoScript morphodynamic

From KokkugiaWiki

this tutorial covers the basics of a morphodynamic design strategy based on attractors. the first script demonstrates how a field of geometry can undergo simple transformations at a local level based on the proximity of a field of attractors. the second script looks at how more complex parametric transformations can be developed.


attractor tools scale

this script is used for scaling up or down fields of objects. however it is easy to modify the script to do more interesting things than scaling. in its current format it generates things like the following: Image:AttractorToolsScale.png


download this rhino file which contains the base geometry to operate on.


the pseudo-code for the script looks something like the following:

  'define the variables  
	
  ' user defined input

  ' loop through each object and get the closest attractor

    ' get object center point
    
    ' loop through attractors to find the distance to the closest attractor

	' get distance from the object center point to the attractor

	' is it closer then keep track of the distance and the attractor
    
    ' if the object is within the threshold then operate on it


Option Explicit
' script written by roland snooks | 2007 | www.kokkugia.com

Call attractorToolsScale

Sub attractorToolsScale
  
	Dim i, j, arrObjects, arrAttract, thresholdDist, scaleFactor, arrBBox, arrCntrPt, counter, arrAttPt 
	Dim dblAttDistTest, dblClosestAttDist, dblClosestAtt, adjAmount
  
	' input
	arrObjects = Rhino.GetObjects("select objects")
	arrAttract = Rhino.GetObjects("select point attractors", 0)
	thresholdDist = Rhino.GetReal("distance threshold", 10, 0)
	scaleFactor = Rhino.GetReal("scale factor", 1, 0)
  
	' loop through each object and get the closest attractor
	For i = 0 To UBound(arrObjects)
      
		' get object bounding box centerpoint
		arrBBox = Rhino.BoundingBox(arrObjects(i))
		arrCntrPt = array((((arrBBox(2)(0)) + (arrBBox(0)(0))) / 2), (((arrBBox(2)(1)) + (arrBBox(0)(1))) / 2), (((arrBBox(0)(2)) + (arrBBox(4)(2))) / 2))
 
		' loop through attractors to find the distance to the closest
		counter = 0
		For j = 0 To UBound(arrAttract)

			arrAttPt = Rhino.PointCoordinates(arrAttract(j))
    
			' get distance
			dblAttDistTest = Rhino.Distance(arrAttPt, arrCntrPt)
    
			' is it closer
			If counter < 1 Then
				dblClosestAttDist = dblAttDistTest
				dblClosestAtt = counter
			Else
				If dblAttDistTest < thresholdDist Then
					If dblAttDistTest < dblClosestAttDist Then 
						dblClosestAttDist = dblAttDistTest
						dblClosestAtt = counter
					End If
				End If  
			End If
			counter = counter + 1

		Next 
    
		' if the object is within the threshold then operate on it
		If dblClosestAttDist < thresholdDist Then
      
			' caculate the adjustment amount
                        ' decrease infinite amount
			adjAmount = 1 - ((thresholdDist - dblClosestAttDist)/thresholdDist)   
                        ' increase by factor of up to 100% x scaleFactor                  
			'adjAmount = ((thresholdDist - dblClosestAttDist)/thresholdDist) * scaleFactor + 1      
   
			' based on attractor proximity do something to the object (eg scale)
      
			' scale
			Rhino.ScaleObject arrObjects(i), arrCntrPt, array(adjAmount,adjAmount,adjAmount)

		End If
    
	Next  

End Sub


in addition to scaling the objects we could also modify their color. this is best done with a separate function:

Function at_ChangeColor(obj,tDist,aDist,sFactor)
	Dim objColor, newColor
	' caculate color
	newColor = 255 - ((1 - ((tDist - aDist)/tDist)) * 255) 
	' change color
	objColor = Rhino.ObjectColor(obj, newColor)

End Function	  

this function would need to be called in the same part of the script where we call the scale method.

		at_ChangeColor arrObjects(i),thresholdDist,dblClosestAttDist,scaleFactor 


to use curves instead of points as attractors you could replace the following line of code:

  arrAttPt = Rhino.PointCoordinates(arrAttract(j))

this bit of code is effectively getting the parameter of the closest point on the attractor curve and then returning the coordinates of that point.

  dblParam = Rhino.CurveClosestPoint(arrAttract(j), arrCntrPt)
  arrAttPt = Rhino.EvaluateCurve(arrAttract(j), dblParam)


attractor tools parametric

the following function is an example of how a parametric component can be instantiated on a surface using a predefined polyline substrate. this function can be called from the attractorTools script, instead of calling the scale method, consequently it instantiates a parametric component using the attractor information as the input to the component.

download this rhino file which contains the base geometry to operate on.

the following line needs to be called at the end of the outer loop:

at_Component arrObjects(i), srf, adjAmount

the parametric component uses simple vector math to essentially scale the polyline and project it along the surface normal a certain distance before surfacing

the result.

Option Explicit
' script written by roland snooks | 2007 | www.kokkugia.com

Call attractorToolsScale

Sub attractorToolsScale
  
	Dim i, j, arrObjects, arrAttract, thresholdDist, scaleFactor, arrBBox, arrCntrPt, counter, arrAttPt 
	Dim dblAttDistTest, dblClosestAttDist, dblClosestAtt, adjAmount, srf
  
	' input
	arrObjects = Rhino.GetObjects("select polygon tesselations", 4)
	srf = Rhino.GetObject("select base surface", 16)
	arrAttract = Rhino.GetObjects("select point attractors", 1)
	thresholdDist = Rhino.GetReal("distance threshold", 5, 0)
	scaleFactor = Rhino.GetReal("scale factor", 2, 0)
  
	' loop through each object and get the closest attractor
	For i = 0 To UBound(arrObjects)
      
		' get object bounding box centerpoint
		arrBBox = Rhino.BoundingBox(arrObjects(i))
		arrCntrPt = array((((arrBBox(2)(0)) + (arrBBox(0)(0))) / 2), (((arrBBox(2)(1)) + (arrBBox(0)(1))) / 2), (((arrBBox(0)(2)) + (arrBBox(4)(2))) / 2))
 
		' loop through attractors to find the distance to the closest
		counter = 0
		For j = 0 To UBound(arrAttract)

			arrAttPt = Rhino.PointCoordinates(arrAttract(j))
    
			' get distance
			dblAttDistTest = Rhino.Distance(arrAttPt, arrCntrPt)
    
			' is it closer
			If counter < 1 Then
				dblClosestAttDist = dblAttDistTest
				dblClosestAtt = counter
			Else
				If dblAttDistTest < thresholdDist Then
					If dblAttDistTest < dblClosestAttDist Then 
						dblClosestAttDist = dblAttDistTest
						dblClosestAtt = counter
					End If
				End If  
			End If
			counter = counter + 1

		Next 
    
		' if the object is within the threshold then operate on it
		If dblClosestAttDist < thresholdDist Then
      
			' caculate the adjustment amount
			adjAmount = 1 - ((thresholdDist - dblClosestAttDist)/thresholdDist)                     ' decrease infinite amount
			'adjAmount = ((thresholdDist - dblClosestAttDist)/thresholdDist) * scaleFactor + 1      ' increase by factor of up to 100% x scaleFactor
   
			' based on attractor proximity do something to the object (eg scale)
      
			' scale
			'Rhino.ScaleObject arrObjects(i), arrCntrPt, array(adjAmount,adjAmount,adjAmount) ' scale
      		
			' instantiate parametric component
			at_Component arrObjects(i), srf, adjAmount
			
			' change color
			at_ChangeColor arrObjects(i),thresholdDist,dblClosestAttDist,scaleFactor 
      
		End If
    
	Next  

End Sub



Function at_Component(obj, srf, adjAmount)
	
	Dim pts, centerPt, i, x, y, z, newPts(), srfNormal, closestPt, arrVertices(), arrFaceVertices(), ptCount, ptVec, scaleVec
	
	' get the obj's points
	pts = Rhino.PolylineVertices(obj)
    
	' calculate the centerPoint of the pts
	ptCount = UBound(pts)
	For i = 0 To ptCount
		x = x + pts(i)(0)
		y = y + pts(i)(1)
		z = z + pts(i)(2)
	Next
	
	centerPt = Array(x/(ptCount+1), y/(ptCount+1), z/(ptCount+1))

	ReDim newPts(0)
	
	' offset points to the inside
	For i = 0 To ptCount
		' calculate vector from pt to centerPt
		ptVec = Rhino.VectorCreate(centerPt, pts(i))
		
		' scale that vector based on adjAmount
		scaleVec = Rhino.VectorScale(ptVec, adjAmount*5)
		
		' make a new pt and add it to an array
		ReDim Preserve newPts(i)
		newPts(i) = Rhino.VectorAdd(pts(i), scaleVec)

	Next
	
	' get pt on srf closest to pt
	closestPt = Rhino.SurfaceClosestPoint(srf, centerPt)

	' get normal
	srfNormal = Rhino.SurfaceNormal(srf, closestPt)
	
	' move points normal based on adjAmount
	For i = 0 To ptCount
		' move each point by a factor of the normal
		newPts(i) = Rhino.VectorAdd(newPts(i), Rhino.VectorScale(srfNormal, adjAmount) )
	Next
	
	Dim arrVertTemp, arrFaceTemp
	' loop through all pts to make mesh
	For i = 0 To ptCount
		
		If i = ptCount Then
			ReDim Preserve arrVertices(i)
			arrVertices(i) = array(pts(i), newPts(i), newPts(0), pts(0))	

		Else
			ReDim Preserve arrVertices(i)
			arrVertices(i) = array(pts(i), newPts(i), newPts(i+1), pts(i+1))

		End If
		
		Rhino.AddPolyline arrVertices(i)
		Rhino.AddSrfPt arrVertices(i)
		
		ReDim Preserve arrFaceVertices(i)
		arrFaceVertices(i) = array(0,1,2,3,3)

	Next
	
	'Rhino.AddMesh arrVertices, arrFaceVertices
	
End Function

Views
Personal tools