Edit: I re-wrote the script without recursion, it’s simpler…
This works with a triple nested loop, the outer one that does the concentric rings of circles out to max number of levels, the middle one that divides the level circle into the specified number of segments and the inner loop that divides each segment obtained above into the specified number of sub-segments, then adds the circles. Your pattern above works best with divisions like 4 and 6.
You will see there are a couple of custom functions that are called many times. One divides a circle into equal numbers of points and returns the points, the other divides a line into an equal number of segments and returns the division points. There is also a “wrapper” function for the UI and to launch the main script section.
After having done this, I realize that this is still maybe a too-complex answer to your question… Also, I did not use classes in the example. Sorry… FWIW. --Mitch
"""Script to create rings of concentric overlapping circles
Works best with even numbers of divisions like 4 and 6
Works only in WorldXY plane. Script by Mitch Heynick 27.05.15"""
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino, math
def DivideCircleExact(origin,rad,n):
#this function creates "exact" division of points along a circle in XY plane
div_pts=[]
inc=2*math.pi/n
zVec=Rhino.Geometry.Vector3d(0,0,1)
start_pt=Rhino.Geometry.Point3d(origin.X+rad,origin.Y,0)
for i in range(n+1):
xform=Rhino.Geometry.Transform.Rotation(i*inc,zVec,origin)
div_pts.append(xform*start_pt)
return div_pts
def DivideBetweenPoints(s_pt,e_pt,divs):
#divides a line between two points into even segments
#returns (divs+1) division points (including start+end points)
pts=[]
vec=e_pt-s_pt
dist=s_pt.DistanceTo(e_pt)
vec.Unitize()
for i in range(divs):
pts.append(s_pt+(dist/(divs-1)*i*vec))
return pts
def DivideAddCircles(ctr,rad,divs,max):
#This is the actual main script, triple nested loop
#add an initial circle to document at the input point
circ=Rhino.Geometry.Circle(ctr,rad)
sc.doc.Objects.AddCircle(circ)
#"Outer" loop for getting concentric circles
for level in range(1,max):
#get division points along the outer circle
pl_pts=DivideCircleExact(ctr,level*rad,divs)
#create polyline from division points
pl=Rhino.Geometry.Polyline(pl_pts)
#"explode" polyline into line segments
segs=pl.GetSegments()
#"Middle" loop through the segments, divide each into level+1 segments
for seg in segs:
div_pts=DivideBetweenPoints(seg.From,seg.To,level+1)
#"Inner" loop - add circles at the division points
#do not add last circle in each segment to avoid dupes
for j in range(len(div_pts)-1):
circ=Rhino.Geometry.Circle(div_pts[j],rad)
sc.doc.Objects.AddCircle(circ)
def MultipeOverlapCircles():
"""this "wrapper" function gathers initial parameters from user,
then launches the script"""
radius=rs.GetReal("Circle radius?",5,minimum=0)
if not radius: return
divs=rs.GetInteger("Number of circle divisions?",6,minimum=2)
if not divs: return
levels=rs.GetInteger("Number of levels?",5,minimum=2,maximum=20)
if not levels: return
orig=rs.GetPoint("Center point for array?")
if not orig: return
#launch the script
DivideAddCircles(orig,radius,divs,levels)
#redraw the screen
sc.doc.Views.Redraw()
MultipeOverlapCircles()