I picked up this script that I once made in rhino script and enhanced it with new options and dynamic preview.
I think the usage is quite self explanatory.
There are a few more things I want to implement as you can see in the comments, but I’m also open for others’ suggestions.
"""
This script allows you draw make patterns from multiple curves at once.
It assumes all curves are more or less the same size, and have a reasonable
width and height to begin with.
It will then randomly use the curves to make a pattern.
The pattern is then applied to a boundary curve that must be closed.
It uses getpoint in order to enable dynamic redraw when changing the pattern
settings.
***********************************
* script written by Gijs de Zwart *
* www.studiogijs.nl *
* April, 2016 *
***********************************
implemented:
* user selects 1 or more curves to use as pattern
* user selects boundary to fill (closed curve)
* boundary gets filled with a preview of the endresult
* curves outside the boundary are removed
* curves that intersect with the boundary are removed
* pattern looks at the size of the curve(s) (boundingbox) to get correct spacing
* possibility to enter a margin from the border of the boundary
* possibility to give curve(s) a random scale/ rotation
* possibility to offset each row
to do:
possibility to scale the curve(s) that are used in the boundary
possibility to flip curve(s)
error checking
"""
import rhinoscriptsyntax as rs
import Rhino.Geometry.Curve as Curve
import random as rand
import Rhino
import System.Drawing.Color as Color
import scriptcontext as sc
def RunCommand():
class fillcrv():
def __init__(self,crv):
bbox=crv.GetBoundingBox(True)
self.crv=crv
self.width=bbox.Max.X-bbox.Min.X
self.height=bbox.Max.Y-bbox.Min.Y
self.h_space=self.width
self.v_space=self.height
self.lower=bbox.Min
def getSpace(offset, space):
#returns horizontal and vertical space and row offset (skew)
h_space=0
v_space=0
for crv in curves:
if h_space<crv.h_space:
h_space=crv.h_space
if v_space<crv.v_space:
v_space=crv.v_space
h_space+= h_space*space/100
v_space+= v_space*space/100
h_skew=h_space*offset/100
return [h_space,v_space,h_skew]
def CalcStartCondition(boundary):
#returns width, height and startpoint transformation
#and changes boundary to offset margin if requested
bbox=boundary.GetBoundingBox(True)
cen_boundary=bbox.Center
plane=Rhino.Geometry.Plane.WorldXY
plane.Origin = bbox.Min
b_width=bbox.Max.X-bbox.Min.X
b_height=bbox.Max.Y-bbox.Min.Y
#calculate translation vector from curve to boundary
start=bbox.Min-curves[0].lower
return [b_width, b_height, start, boundary]
def CreatePatternCurves(v_amount, h_amount, v_space, h_space, h_skew, start, boundary):
#creates patterned curves in rectangular grid
trans=start
reset=start[0]
odd=0
i=0
patterncurves=[]
for y in range(0,v_amount):
#reset transformation in x-direction
trans[0]=reset
if odd%2==1:
trans[0]+=h_skew
odd+=1
for x in range(0,h_amount):
i=rand.randint(0,len(curves)-1)
copy=curves[i].crv.Duplicate()
copy.Translate(trans)
cen=copy.GetBoundingBox(True).Center
random = optRandom.CurrentValue
if random and copy!=None:
rnd=rand.randint(90,110)/100
rot_rnd=rand.uniform(-.1,.1)
scale=Rhino.Geometry.Transform.Scale(cen,rnd)
rotation=Rhino.Geometry.Transform.Rotation(rot_rnd, cen)
copy.Transform(scale)
copy.Transform(rotation)
#check if point to copy is inside boundary and margin
margin=optMargin.CurrentValue
if boundary.Contains(cen) == Rhino.Geometry.PointContainment.Inside:
#check if it intersects with the boundarycurve:
#uses intersection tolerance to leave a margin
intersection_tolerance = margin+0.01
overlap_tolerance = 0.0
intersections = Rhino.Geometry.Intersect.Intersection.CurveCurve(
boundary,
copy,
intersection_tolerance,
overlap_tolerance
)
if not intersections:
patterncurves.append(copy)
#increment horizontal transform
trans[0]+=h_space
#increment vertical transform
trans[1]+=v_space
return patterncurves
def fillBoundary(crv,boundary, offset, space, random):
#returns pattern curves within boundary
b_width, b_height, start, boundary = CalcStartCondition(boundary)
#determine horizontal and vertical spacing and skew
h_space,v_space,h_skew = getSpace(offset, space)
# calculate amount of curves needed
h_amount=int(b_width/h_space)+1
v_amount=int(b_height/v_space)+1
#create the pattern
patterncurves=CreatePatternCurves(v_amount, h_amount, v_space, h_space, h_skew, start, boundary)
return patterncurves
def OnDynamicDraw(sender, e):
#try:
random=optRandom.CurrentValue
offset=optOffset.CurrentValue
space=optSpace.CurrentValue
h_space,v_space,h_skew = getSpace(offset, space)
patterncurves = fillBoundary(curves, boundary, offset, space, random)
for curve in patterncurves:
e.Display.DrawCurve(curve, Color.LightCyan, 1)
"""
except Exception as ex:
template = "An exception of type {0} occured. Arguments:\n{1!r}"
message = template.format(type(ex).__name__, ex.args)
print message
return
"""
def addPatternToRhino():
if boolCanOffset==False:
print "couldn't offset curve, used boundary instead"
patterncurves = fillBoundary(curves, boundary, offset, space, random)
for i in range(0, len(patterncurves)):
sc.doc.Objects.AddCurve(patterncurves[i])
sc.doc.Views.Redraw()
boundary = rs.GetObject("select boundarycurve to fill", rs.filter.curve)
if not boundary:
return
boundary = rs.coercecurve(boundary)
crvs=rs.GetObjects("select curves to use as pattern", rs.filter.curve)
if not crvs:
return
if not boundary.IsClosed:
return
curves=[]
for crv in crvs:
crv=rs.coercecurve(crv)
crv=fillcrv(crv)
curves.append(crv)
gp=Rhino.Input.Custom.GetPoint()
gp.SetCommandPrompt("pattern properties")
optMargin=Rhino.Input.Custom.OptionDouble(0,0,100)
gp.AddOptionDouble("margin",optMargin)
optRandom = Rhino.Input.Custom.OptionToggle(True, "Off", "On")
gp.AddOptionToggle("add_randomness", optRandom)
optOffset = Rhino.Input.Custom.OptionInteger(50,0,100)
gp.AddOptionInteger("offset_percentage",optOffset)
optSpace = Rhino.Input.Custom.OptionInteger(50,0,500)
gp.AddOptionInteger("space_percentage",optSpace)
gp.DynamicDraw+=OnDynamicDraw
#start collecting user options
while True:
gp.Get()
if gp.Result()==Rhino.Input.GetResult.Option:
continue
break
random=optRandom.CurrentValue
offset=optOffset.CurrentValue
margin=optMargin.CurrentValue
space=optSpace.CurrentValue
boolCanOffset=True
addPatternToRhino()
if( __name__ == "__main__" ):
RunCommand()