Advertisement
Guest User

Untitled

a guest
Mar 25th, 2014
643
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import math
  2.  
  3. class Circle:
  4.     def __init__(self, radius):
  5.         self.radius = radius
  6.         self.contents = []  # [Placement]
  7.  
  8.     def add(self, circle, pos):
  9.         self.contents.append(Placement(circle, pos))
  10.  
  11. class Placement:
  12.     def __init__(self, circle, pos):
  13.         self.circle = circle
  14.         self.pos = pos
  15.  
  16. # make 0 and 1
  17. cache = {0: Circle(.5), 1: Circle(1.)}
  18. cache[1].add(cache[0], (-.5, 0))
  19.  
  20. def make(n):
  21.     if n in cache:
  22.         return cache[n]
  23.  
  24.     a = make(n - 1)
  25.     b = make(n - 2)
  26.     z = Circle(a.radius + b.radius)
  27.     z.add(a, (-b.radius, 0))
  28.     z.add(b, (a.radius, 0))
  29.  
  30.     if n - 3 >= 0:
  31.         c = make(n - 3)
  32.         p = getnestpos(a, b, c)
  33.         z.add(c, p)
  34.  
  35.         nest(z, n - 4, b, a)
  36.    
  37.     cache[n] = z
  38.     return z
  39.  
  40. def nest(z, n, a, b, recurse=True):
  41.     if n >= 0:
  42.         ap = bp = None
  43.         for q in z.contents:
  44.             if q.circle is a:
  45.                 ap = q
  46.             if q.circle is b:
  47.                 bp = q
  48.         assert ap and bp, 'failed to find ap and bp'
  49.  
  50.         c = make(n)
  51.         p = getnestpos(a, b, c, ap, bp)
  52.         z.add(c, p)
  53.  
  54.         if recurse:
  55.             nest(z, n - 1, a, c, recurse=False)
  56.             nest(z, n - 2, c, b)
  57.  
  58. def getnestpos(a, b, c, ap=None, bp=None):
  59.     # use law of cosines to get angle between a->b and a->c
  60.     p = a.radius ** 2 + a.radius * b.radius + a.radius * c.radius
  61.     angle = math.acos((p - b.radius * c.radius) / (p + b.radius * c.radius))
  62.     # find the vector a->c
  63.     x = (a.radius + c.radius) * math.cos(angle) - b.radius
  64.     y = (a.radius + c.radius) * math.sin(angle)
  65.    
  66.     if ap and bp:
  67.         # place the vector correctly
  68.         vx = bp.pos[0] - ap.pos[0]
  69.         vy = bp.pos[1] - ap.pos[1]
  70.         angle = math.atan2(vy, vx)
  71.         vmag = math.hypot(vx, vy)
  72.         vx -= a.radius * vx / vmag
  73.         vy -= a.radius * vy / vmag
  74.        
  75.         x, y = (
  76.             ap.pos[0] + vx + x * math.cos(angle) - y * math.sin(angle),
  77.             ap.pos[1] + vy + x * math.sin(angle) + y * math.cos(angle)
  78.         )
  79.        
  80.     return (x, y)
  81.  
  82. def getcolor(v,t):
  83.     p = float(v)/float(t)
  84.     i = int(p*150)+50
  85.     return "rgb(%d,%d,%d)" % (i,i,i)
  86.  
  87. def printsvg(c, pos,m):
  88.     print '<circle cx="%f" cy="%f" r="%f" fill="%s" />' % (zoom * pos[0], zoom * pos[1], zoom * c.radius, getcolor(len(c.contents),m))
  89.     for p in c.contents:
  90.         printsvg(p.circle, (pos[0] + p.pos[0], pos[1] + p.pos[1]),m)
  91.  
  92. zoom = 5
  93. total = 15
  94. c = make(total)
  95.  
  96. size = zoom * c.radius * 2
  97. print '<svg xmlns="http://www.w3.org/2000/svg" width="%f" height="%f">' % (size, size)
  98. printsvg(c, (c.radius, c.radius),total)
  99. print '</svg>'
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement