Random Hair on Surface

This rhino script allows the user to create highly varied, yet controllable, low polygon count “hairs”.  This script operates based on the world coordinate system, growing upwards from a user-selected series of points.

Version 2 allows for the selection of a surface which will be randomly populated with grass blades perpendicular to the surface (about the surface normal)  The user is given the option to specify the range of lengths (min-max), the range of bending per segment, and the base and tip radius. The user can control the number of segments the blade will have, the more segments, the smoother the bend, the greater the polygons. In addition as part of the interest in polygon optimization the user can specify the number of sides on each blade, a 2 sided blade is flat, 3 sided become three dimensional (triangular), 4 rectangular, etc. the higher the number the rounder it is, the higher the polygon count.

Rhino Script

Option Explicit
'Script written by <David Mans>
'Script copyrighted by <Neoarchaic Studio>
'Script version Sunday, July 05, 2009 9:42:52 PM

Call Main()
Sub Main()
	Dim strSurface, arrInputs
	
	strSurface = Rhino.GetObject("Select Surface", 8)
	If isNull(strSurface) Then Exit Sub
	Call reparameterize(strSurface)
	
	arrInputs = Rhino.PropertyListBox(array("Count", "Min Length", "Max Length", "Min Hair Bend", "Max Hair Bend", "Base Radius", "Tip Radius", "Segments", "Sides"), array(1000, 4, 12, 10, 45, 0.1, 0.01, 5, 2))
	If isNull(arrInputs) Then Exit Sub
	
	Dim i, stem(), blades(), sides
	If CInt(arrInputs(7)) < 2 Then
		sides = 2
	Else
		sides = CInt(arrInputs(7))
	End If
	
	Dim arrPlanes
	
	Call Rhino.EnableRedraw(False)
	arrPlanes = randomPtsSrf(strSurface, CInt(arrInputs(0)))
	
	ReDim stem(uBound(arrPlanes)), blades(uBound(arrPlanes))
	For i = 0 To uBound(arrPlanes) Step 1
		stem(i) = segmentedStem(arrPlanes(i), random(CDbl(arrInputs(1)), CDbl(arrInputs(2))), CInt(arrInputs(7)), random(CDbl(arrInputs(3)), CDbl(arrInputs(4))))
		blades(i) = bladesFlat(stem(i), CDbl(arrInputs(5)), CDbl(arrInputs(6)), sides)
	Next
	Call Rhino.EnableRedraw(True)
End Sub
Function randomPtsSrf(strSurface, intCount)
	randomPtsSrf = Null
	Dim i, uDom, vDom, arrOutput()
	ReDim arrOutput(intCount-1)
	uDom = Rhino.SurfaceDomain(strSurface, 0)
	vDom = Rhino.SurfaceDomain(strSurface, 1)
    
	For i = 0 To intCount - 1 Step 1
		arrOutput(i) = Rhino.SurfaceFrame(strSurface, array(random(uDom(0), uDom(1)), random(vDom(0), vDom(1))))
	Next

	randomPtsSrf = arrOutput
End Function
Function bladesFlat(arrPlanes, radB, radT, intSteps)
	bladesFlat = Null
	Dim i, j, k, r, arrOutput
	Dim radStep, rotStep
	Dim arrPoints(),arrFaces(), arrMesh()
	ReDim arrMesh(intSteps-1)
	If radB > radT Then
		radStep = -(radB - radT) / uBound(arrPlanes)
	Else
		radStep = (radT - radB) / uBound(arrPlanes)
	End If
	
	rotStep = 360 / intSteps
	
	For k = 0 To intSteps - 1 Step 1
		r = 0
		For i = 0 To uBound(arrPlanes) Step 1
			ReDim Preserve arrPoints(r)
			arrPoints(r) = Rhino.PointAdd(arrPlanes(i)(0), Rhino.VectorScale(Rhino.VectorUnitize(Rhino.RotatePlane(arrPlanes(i), k * rotStep, arrPlanes(i)(3))(1)), radB + i * radStep))
			r = r + 1
			ReDim Preserve arrPoints(r)
			arrPoints(r) = Rhino.PointAdd(arrPlanes(i)(0), Rhino.VectorScale(Rhino.VectorUnitize(Rhino.RotatePlane(arrPlanes(i), (k + 1) * rotStep, arrPlanes(i)(3))(1)), radB + i * radStep))
			r = r + 1
		
		Next
		r = 0
		ReDim arrFaces(uBound(arrPoints)-2)
		For i = 0 To uBound(arrPoints) - 2 Step 2
			arrFaces(r) = array(i, i + 1, i + 3, i + 3)
			r = r + 1
			arrFaces(r) = array(i, i + 3, i + 2, i + 2)
			r = r + 1
		Next
		If intSteps = 2 Then
			If k = 1 Then
				arrOutput = Rhino.addmesh(arrPoints, arrFaces)
			End If
		Else
			arrMesh(k) = Rhino.addmesh(arrPoints, arrFaces)
		End If
	Next
	If intSteps >< 2 Then
		arrOutput = Rhino.MeshBooleanUnion(arrMesh)
	End If
	bladesFlat = arrOutput
End Function
Function segmentedStem(arrPlane, dblHeight, dblSegments, maxRotation)
	segmentedStem = Null
	Dim i, count
	count = dblSegments - 1
	Dim dblStep, tempLen, dblLen()
	Dim mPlane(), tmpAngle(1), blnWavy
	ReDim dblLen(count), mPlane(count+1)
	
	mPlane(0) = Rhino.RotatePlane(arrPlane, random(0, 360), arrPlane(3))
	blnWavy = random(-1, 1)
	
	dblStep = dblHeight / dblSegments
	For i = 0 To count Step 1
		If i = 0 Then
			tempLen = random(0.5, 1) * dblStep
			dblLen(i) = dblStep - tempLen
		ElseIf i = count Then
			tempLen = dblStep + dblLen(i - 1)
		Else
			tempLen = random(0.5, 1) * (dblStep + dblLen(i - 1))
			dblLen(i) = dblStep + dblLen(i - 1) - tempLen
		End If
		If blnWavy >= 0 Then
			tmpAngle(0) = random(0, maxRotation)
			tmpAngle(1) = random(0, maxRotation)
		Else
			tmpAngle(0) = random(-maxRotation, 0)
			tmpAngle(1) = random(-maxRotation, 0)
		End If
		mPlane(i + 1) = Rhino.RotatePlane(Rhino.RotatePlane(Rhino.moveplane(mPlane(i), Rhino.PointAdd(mPlane(i)(0), Rhino.VectorScale(Rhino.VectorUnitize(mPlane(i)(3)), tempLen))), tmpAngle(0), mPlane(i)(1)), tmpAngle(1), mPlane(i)(2))
	Next
	segmentedStem = mPlane
End Function
Function reparameterize(strObjectID)
	If Rhino.IsCurve(strObjectID) = True Then
		Call rhino.SelectObject(strObjectID)
		Call rhino.Command("reparameterize 0 1", False)
		Call rhino.UnselectAllObjects()
	End If
	If Rhino.IsSurface(strObjectID) = True Then
		Call rhino.SelectObject(strObjectID)
		Call rhino.Command("reparameterize 0 1 0 1", False)
		Call rhino.UnselectAllObjects()
	End If
End Function
Function random(min, max)
	random = Null
	Dim dblValue: dblValue = min + (max - min) * rnd()
	random = dblValue
End Function