The script below allows you to make an array of curves or poly(surfaces) linearly. Where ArrayLinear will make an array where you set the distance between items, this script will distribute the arrayed objects over the distance you pick. Made this mainly for learning purposes, but might be useful for some. Should work on Windows and Mac.
"""
This script allows you array curves, surfaces and polysurfaces, from object
boundingbox center to a picked point, where the new objects will get distributed
between center and picked point. You will see a dynamic preview and can
change the amount while the preview updates.
***********************************
* script written by Gijs de Zwart *
* www.studiogijs.nl *
* April, 2016 *
***********************************
"""
import Rhino
import rhinoscriptsyntax as rs
from System.Drawing import Color
import scriptcontext as sc
def ArrayBetween():
def OnDynamicDraw(sender, e):
pts=[]
count=optInt.CurrentValue
line = Rhino.Geometry.Line(center, e.CurrentPoint)
curve=line.ToNurbsCurve()
params=curve.DivideByCount(count-1,True)
for param in params:
pts.append(line.PointAt(param))
vec = e.CurrentPoint - center
length = vec.Length
dist=length/(count-1)
vec.Unitize()
for i in range(1,count):
translate = vec * i * dist
xf = Rhino.Geometry.Transform.Translation(translate)
newobj=obj.Duplicate()
newobj.Transform(xf)
if obj.ObjectType==Rhino.DocObjects.ObjectType.Curve:
e.Display.DrawCurve(newobj, Color.LightCyan, 2)
if obj.ObjectType==Rhino.DocObjects.ObjectType.Brep:
e.Display.DrawBrepWires(newobj, Color.LightCyan)
e.Display.DrawLine(line, Color.Blue, 2)
def getPoint():
while True:
result = gp.Get()
if result == Rhino.Input.GetResult.Point:
count=optInt.CurrentValue
line = Rhino.Geometry.Line(center, gp.Point())
curve=line.ToNurbsCurve()
params=curve.DivideByCount(count-1,True)
pts=[]
for param in params:
pts.append(line.PointAt(param))
vec = gp.Point() - center
length = vec.Length
dist=length/(count-1)
vec.Unitize()
for i in range(1,count):
translate = vec * i * dist
xf = Rhino.Geometry.Transform.Translation(translate)
newobj=obj.Duplicate()
newobj.Transform(xf)
if obj.ObjectType==Rhino.DocObjects.ObjectType.Curve:
sc.doc.Objects.AddCurve(newobj)
if obj.ObjectType==Rhino.DocObjects.ObjectType.Brep:
sc.doc.Objects.AddBrep(newobj)
sc.doc.Views.Redraw()
elif result == Rhino.Input.GetResult.Option:
#go back to point selection mode
getPoint()
break
docobj = rs.GetObject("select object to array", 28)
if rs.IsCurve(docobj):
obj=rs.coercecurve(docobj)
if rs.IsBrep(docobj):
obj=rs.coercebrep(docobj)
if not obj: return
plane=Rhino.Geometry.Plane.WorldXY
bb=obj.GetBoundingBox(plane)
if bb==None:
print "can't calculate boundingbox"
return
center = bb.Center
minimum = bb.Min
center[2]=minimum[2]
gp=Rhino.Input.Custom.GetPoint()
gp.SetCommandPrompt("point to array to / distance")
optInt=Rhino.Input.Custom.OptionInteger(3,3,999)
gp.AddOptionInteger("Count",optInt)
gp.SetBasePoint(center, False)
gp.DynamicDraw += OnDynamicDraw
getPoint()
if( __name__ == "__main__" ):
ArrayBetween()
attached is an updated version: now you can change basepoint
Still assumes working in world xy plane:
"""
This script allows you array curves, surfaces and polysurfaces, from object
boundingbox center or corners to a picked point, where the new objects will get
distributed between center or corner and picked point. You will see a dynamic preview
and can change the amount while the preview updates.
***********************************
* script written by Gijs de Zwart *
* www.studiogijs.nl *
* April, 2016 *
***********************************
"""
import Rhino
import rhinoscriptsyntax as rs
from System.Drawing import Color
import scriptcontext as sc
def ArrayBetween():
def OnDynamicDraw(sender, e):
i=optBasePoint[0]
pts=[]
count=optInt.CurrentValue
if i==0:#center
basept.X = center.X
basept.Y = center.Y
if i==1:#lowerleft
basept.X = center.X-width/2
basept.Y = center.Y-depth/2
if i==2:#upperleft
basept.X = center.X-width/2
basept.Y = center.Y+depth/2
if i==3:#lowerright
basept.X = center.X+width/2
basept.Y = center.Y-depth/2
if i==4:#upperright
basept.X = center.X+width/2
basept.Y = center.Y+depth/2
vec = e.CurrentPoint - basept
gp.SetBasePoint(basept, False)
line = Rhino.Geometry.Line(basept, e.CurrentPoint)
curve=line.ToNurbsCurve()
params=curve.DivideByCount(count-1,True)
for param in params:
pts.append(line.PointAt(param))
length = vec.Length
dist=length/(count-1)
vec.Unitize()
for i in range(1,count):
translate = vec * i * dist
xf = Rhino.Geometry.Transform.Translation(translate)
newobj=obj.Duplicate()
newobj.Transform(xf)
if obj.ObjectType==Rhino.DocObjects.ObjectType.Curve:
e.Display.DrawCurve(newobj, Color.LightCyan, 2)
if obj.ObjectType==Rhino.DocObjects.ObjectType.Brep:
e.Display.DrawBrepWires(newobj, Color.LightCyan)
e.Display.DrawLine(line, Color.Blue, 2)
def getPoint():
while True:
result = gp.Get()
if result == Rhino.Input.GetResult.Point:
count=optInt.CurrentValue
line = Rhino.Geometry.Line(center, gp.Point())
curve=line.ToNurbsCurve()
params=curve.DivideByCount(count-1,True)
pts=[]
for param in params:
pts.append(line.PointAt(param))
vec = gp.Point() - basept
length = vec.Length
dist=length/(count-1)
vec.Unitize()
for i in range(1,count):
translate = vec * i * dist
xf = Rhino.Geometry.Transform.Translation(translate)
newobj=obj.Duplicate()
newobj.Transform(xf)
if obj.ObjectType==Rhino.DocObjects.ObjectType.Curve:
sc.doc.Objects.AddCurve(newobj)
if obj.ObjectType==Rhino.DocObjects.ObjectType.Brep:
sc.doc.Objects.AddBrep(newobj)
sc.doc.Views.Redraw()
elif result == Rhino.Input.GetResult.Option:
optionindex = gp.Option().CurrentListOptionIndex
optBasePoint[0]=optionindex
getPoint()
break
docobj = rs.GetObject("select object to array", 28)
if rs.IsCurve(docobj):
obj=rs.coercecurve(docobj)
if rs.IsBrep(docobj):
obj=rs.coercebrep(docobj)
if not obj: return
plane=Rhino.Geometry.Plane.WorldXY
bb=obj.GetBoundingBox(plane)
if bb==None:
print "can't calculate boundingbox"
return
center = bb.Center
basept = bb.Center
minimum = bb.Min
maximum = bb.Max
center.Z=minimum.Z
width = maximum.X-minimum.X
depth = maximum.Y-minimum.Y
gp=Rhino.Input.Custom.GetPoint()
gp.SetCommandPrompt("point to array to / distance")
optInt=Rhino.Input.Custom.OptionInteger(3,3,999)
gp.AddOptionInteger("Count",optInt)
basepoints=["center", "lower_left","upper_left","lower_right","upper_right"]
gp.AddOptionList("basepoint", basepoints, 0)
optBasePoint=[0]#equal to index 0 of basepoints option
gp.DynamicDraw += OnDynamicDraw
getPoint()
if( __name__ == "__main__" ):
ArrayBetween()
easiest way is to make a button for it (you can use the attached icon) and make a reference to the script on your harddrive. For example, if you save your scripts in c:\scripts\ Then you can place the following in the button: noecho -RunPythonScript "c:\scripts\arraybetween.py"
With this simple script, no. I could improve it though, but it’s not high on my priority list. I made the script long time ago to learn working with display conduits.
So since Rhino still doesn’t have this option in its array toolbar, I had to go searching on this forum.
Weirdly, “distribute” asks me to select three objects, but I just want to array one, so has the command changed in the last 6 years?
Thank you for making the script, it works great! But why did you choose a few hard-coded basepoints instead of just letting the user pick their own start and end points?
I think what Dale was referring to at the time was the fact that you can ArrayLinear your objects. Move the last one in the correct place and then distribute the other objects equally in between the first and the last. That’s how I usually solve this problem.
Distribute doesn’t create new geometry, it just spaces objects an equal distance apart.
That’s funny. So after 6 years, something this simple still requires a user made script? Well, at least users can do scripts to fill gaps in functionality.
Well, sure but consider this instead: The built-in ArrayLinear asks you for a “first reference point” and a “second reference point” and interactively draws a preview while you are selecting the second one. That’s actually great UX and a good target to make it intuitive for new script users (but I understand that it was outside of the scope of the excercise).