View difference between Paste ID: Xyfxnddb and s3Ym3WK2
SHOW: | | - or go back to the newest paste.
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-
    
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-
    
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-
        
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-
        
79+
       
80
    return (x, y)
81
82-
def printsvg(c, pos):
82+
def getcolor(v,t):
83-
    print '<circle cx="%f" cy="%f" r="%f" fill="white" stroke="black" />' % (zoom * pos[0], zoom * pos[1], zoom * c.radius)
83+
    p = float(v)/float(t)
84
    i = int(p*150)+50
85-
        printsvg(p.circle, (pos[0] + p.pos[0], pos[1] + p.pos[1]))
85+
    return "rgb(%d,%d,%d)" % (i,i,i)
86
 
87
def printsvg(c, pos,m):
88-
c = make(10)
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-
printsvg(c, (c.radius, c.radius))
92+
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>'