Distribution with limited randomness

Hello Newsgroup,

I run into the following problem on a regular basis and haven’t found a way to solve it 100% yet, maybe someone can help:

I would like to be able to randomly distribute a block / object over a 3D surface but with the possibility to specify a max and min value for the distance between the blocks (or their bounding boxes) this way we could avoid or limit the amount of the intersection of objects e.g. to spread trees over a landscape (please see attached picture)

Ideally I would also be able to specify a random z-axis rotation for the blocks and random scale effects with a specified min/ max setting for the instances, but since this already can be achieved by existing scripts e.g. harmony this would be just for convenience…

I would be very thankfull for any help here.

best regards


Hi Andreas,

Native Rhino doesn’t have any randomnizing tools but there’s this free plugin that might help, http://www.food4rhino.com/project/arraycrv-plus?etx.

Hi Andreas- I made a script a while ago that does something like this- you can give it a try, it might be good enough - Unzip, then drag and drop the rvb file onto Rhino to add this alias -


This will then work much like a regular Rhino command.

Sprinkler (2).zip (1.4 KB)


Like this?


I’ve thought about making a little plugin on this idea but wasn’t sure if it would be worth it…

Hi @pascal

I get an error when I try the sprinkler script. Am I doing something wrong or…?

TIA, Jakob


thank you all for your feedback -> great forum !

@pascal: yes, i did a few test with sprinkler, but as I tried to explain in my initial post the results are “too” random for e.g. a forest, the distances between the elements are sometimes too large and even more problematic often too close.

@VaneSteeg: I know curve-plus, but it is for arranging objects on curves, not surfaces or am I wrong?

@nathancoatney: This looks interesting, is it available for download?

actually I already tested a lot of stuff but nothing really does what I need, If anyone would help me with a script / plugin to fulfill the requirements (“even” random distribution with min/max distance, random rotations, vertical alignment of the objects ( like trees on a slope ), user definable scale variations) I would gladly pay for such a tool…



…e.g. for 3D studio there are different highly advanced tools available that I already used
for inspiration : http://www.itoosoft.com/forestpack.php

of course I am not talking about such a mighty program with all its features, but I just would like to show that there is a lot of interest in such tools… and there are really great things you can do with it!



Hi Jakob - that error usually shows up when the alias is present but the script is not loaded- try drag&drop of the unipped rvb file again…


1 Like

Didn’t someone make a plug-in about 10 years ago called Random Bubbles or something. I remember using it, and I think it had some controls but maybe only about size…

That was an old grasshopper script that I would have a difficult time finding the source for now. I remember the issues with it were:

It couldn’t handle enough instances for something like a forest, I think mainly because of the drawing overhead.
At the time I don’t believe Grasshopper could use/manipulate instance objects.

However, I am fairly certain this is something achievable with python. The issue will remain with the drawing of the instances. In the end, if Rhino has to draw millions of things, it will slow down, unless there is some trick I don’t know about. If you are using a renderer (like Maxwell, and I think Vray) that has a lightweight proxy object that uses a standard document object such as a point cloud or bounding box, then it is probably a viable idea.

If there is a small market for such a tool then I wouldn’t mind maintaining it, but I see there are already some free tools that do basically the same thing:


But maybe controlling the spacing and/or collision detection is a differentiator. I will try to spend my Rhino time working on it, as I would like the tool for myself as well, and have been meaning to put something together for a while now. If it goes anywhere I’ll post something.


That is exactly what is missing right now.
As I said I already tried numerous solutions including grow, which is a great tool especially when combined with octane, but it also lacks the min / max distance distribution feature, in addition I think I remember that the objects are distributed in the direction of the surface normal , for e.g. trees there has to be an option to keep the objects oriented vertical…

There is a number of commercial plugins available for e.g. 3d studio, forest pack or scatter are two of them. Their abilities are of course very wide , you can tell them to mix your distribution out of certain percentageges of objects, have a probability to form clusters and so on. But since many of those tasks can already be done in Rhino by other scripts, it is possible for sure to integrate some of this into one solution. I have a lot of ideas for such a program but lack the ability to program it properly. …



Thanks @pascal - worked perfectly!

If anyone is interested, Alex made a very nice contribution in the grasshopper forum:



Hi Andy,

Jarek posted a tool for distributing trees some years before (maybe you can found it at the old Rhino Forum Archive too). Attached two button scripts from this tool. one for placing random points on a surface and one for copy trees to the points.


(Option Explicit
’Script written and copyrighted by Jaroslaw Bieda
'Script version August 19, 2007 6:10:31 PM

Call RandomPointsOnSurf()
Sub RandomPointsOnSurf()

Dim strSurface, strSurfaceShrunk

Dim arrPoints() 
Dim arrCurves, idCurve
Dim dblCrvDist : dblCrvDist=0
Dim arrDomU, arrDomV
Dim t(1), pt, strPoint
Dim a, m, j
Dim idDistTest
Dim intM : intM = 0 'level of point count match
Dim blnSkip : blnSkip = vbFalse 'skipping regenerating points while MinDist=0 and switching strMatch levels

strSurface = Rhino.GetObject("Select surface to generate random points on",8)
If IsNull(strSurface) Then Exit Sub

arrCurves=Rhino.GetObjects("Select edge curves ( ENTER if none )",4)
Dim l : l=1
If Not IsNull(arrCurves) Then l=0

Call Rhino.EnableRedraw(vbFalse) 'shrinking surface to speed up the process
If Rhino.IsSurfaceTrimmed(strSurfaceShrunk) Then Rhino.ShrinkTrimmedSurface(strSurfaceShrunk)
arrDomU = Rhino.SurfaceDomain(strSurfaceShrunk, 0)
arrDomV = Rhino.SurfaceDomain(strSurfaceShrunk, 1)
Call Rhino.DeleteObject(strSurfaceShrunk)
Call Rhino.EnableRedraw(vbTrue)

If Not IsArray(arrDomU) Or Not IsArray(arrDomV) Then Exit Sub

Dim strResult, arrOptions() : ReDim arrOptions(5-l)
Dim intQuantity : intQuantity = 10 'Initial points number
Dim dbDist : dbDist=0
Dim strMatch : strMatch="Disabled"

	arrOptions(1)="PointCount_" & intQuantity
	If dbDist<1 And dbDist>0 Then arrOptions(2)="MinPtDist_between_0_and_1"
	arrOptions(3)="MatchPointCount_" & strMatch
	If blnSkip=False Then	
		ReDim arrPoints(0) : j=0
		Call Rhino.EnableRedraw(vbFalse)
		For a = 1 To intQuantity
			For m = 0 To intM '-loop for iterations for point count match	
					t(0) = arrDomU(0) + (((arrDomU(1) - arrDomU(0))*Rnd)) 
					t(1) = arrDomV(0) + (((arrDomV(1) - arrDomV(0))*Rnd)) 
					pt = Rhino.EvaluateSurface(strSurface, t)
					If Rhino.IsPointOnSurface(strSurface,pt) Then Exit Do
				strPoint = Rhino.AddPoint(pt) 
				'testing for existing points withing given minimum distance		
				If dbDist > 0 Then
					If IsArray(Rhino.SelectedObjects) Then
						For Each idDistTest In Rhino.SelectedObjects	
							If FastDistance(pt,Rhino.PointCoordinates(idDistTest)) < dbDist Then
								Exit For
							End If
					End If
				End If

				'testing distance to curves
				If l=0 And dblCrvDist>0 And Rhino.IsObject(strPoint) Then
					For Each idCurve In arrCurves
						If FastDistance(Rhino.PointCoordinates(strPoint),Rhino.EvaluateCurve(idCurve,Rhino.CurveClosestPoint(idCurve,Rhino.PointCoordinates(strPoint)))) < dblCrvDist Then
							Exit For
						End If
				End If

				If Rhino.IsObject(strPoint) Then
					ReDim Preserve arrPoints(j)
   				 Exit For
				End If
			Call Rhino.SelectObject(strPoint)
			'process percentage and points generated	
			Call Rhino.Prompt("Generating points... "&CStr(Int((100*a)/intQuantity))&"% | Generated " & CStr(UBound(arrPoints)+1)&" points. Press ESC to cancel")
			'refresh every 10%
			If a Mod (intQuantity/10) = 0 Then			
				Call Rhino.EnableRedraw(vbTrue)
				Call Rhino.EnableRedraw(vbFalse)
			End If
		Call Rhino.Print("Generated " & CStr(UBound(Rhino.SelectedObjects)+1)&" points...")	
		Call Rhino.EnableRedraw(vbTrue)
	End If
	strResult = Rhino.GetString("Generate random points on surface","CREATE",arrOptions)
	Select Case Left(UCase(strResult),2)	

		Case "CU"
			dblCrvDist=Rhino.GetReal("Minimum distance from curve?",dblCrvDist,0)
			If IsNull(dblCrvDist) Then dblCrvDist=0
			Call Rhino.DeleteObjects(Rhino.SelectedObjects)
			If dblCrvDist>0 Then blnSkip=False
		Case "CR"
			Exit Do
		Case "PO"
			intQuantity = Rhino.GetInteger("Number of points to generate",intQuantity,1)
			Call Rhino.DeleteObjects(Rhino.SelectedObjects)
		Case "RA"
			Call Rhino.DeleteObjects(Rhino.SelectedObjects)
		Case "MI"
			dbDist=Rhino.GetReal("Minimal point to point distance",dbDist,0)
			Call Rhino.DeleteObjects(Rhino.SelectedObjects)
			If dbDist>0 Then blnSkip = False
		Case "MA"  ' setting the level of point count match while minimal distance is defined
			If dbDist=0 Then blnSkip = vbTrue
			strMatch=Rhino.Getstring("Select point count match attempt level",strMatch,Array("Disabled","Low","Med","High","Max"))
			Select Case Left(UCase(strMatch),2)
				Case "DE"
					intM = 0
				Case "LO"
					intM = 10
				Case "ME"
					intM = 50
				Case "HI"
					intM = 100
				Case "MA"
					intM = 300
				Case Else
			End Select
			If Not blnSkip=True Then Call Rhino.DeleteObjects(Rhino.SelectedObjects)
		Case Else 
			Call Rhino.DeleteObjects(Rhino.SelectedObjects)
			Exit Sub
	End Select
Call Rhino.Command("!_Group")

End Sub

Function FastDistance(byref P1,byref P2)
End Function)

(Option Explicit

Call CopyToPoints()
Sub CopyToPoints
Dim idObjs : idObjs = Rhino.GetObjects(“Objects to copy”, ,True, True, True)
Dim ptFrom : ptFrom = Rhino.GetPoint(“Point to copy from”)
Dim ptTarget : ptTarget = Rhino.GetPointCoordinates(“Points to copy to”, False)

Dim i
For i = 0 To UBound(ptTarget)
	Call Rhino.CopyObjects(idObjs, ptFrom, ptTarget(i))

End Sub)

And Thomas An. was a great script writer. Maybe his good old tool helps you.

Harmony.rar (320.6 KB)

1 Like

Thank you Micha, I always thougth I knew these tools, but just tried the script again and discovered the min. distance option… however when I try this option it it says “error in line 171”

any clue ? does this work for you?


1 Like

Works fine here. Maybe a copy&paste error. Attached the script as txt file.

RandomPoints.txt (5.4 KB)

Thanks, will try tomorrow. …

Hi @vcube,
were you able to run the script with the min.distance option using the definition from the RandomPoints.txt provided from Micha? The link doesn´t work anymore… Could you please reupload it?

… here some old tools, the FiveOClockShadow.rhp is for Rhino 5 and copy random objects to a polysurface.

Points2Surface.txt (5.4 KB)
Copy2Points.txt (406 Bytes)
FiveOClockShadow.rhp (20 KB)