In [1]:
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib.collections import PatchCollection
%matplotlib inline

In [2]:
def spheres(n, angle):
    from math import atan2, cos, sin, pi, tan
    a = angle*pi/180
    X, Y = [0], [0]
    X.append(X[-1]+2*cos(a)), Y.append(Y[-1]+2*sin(a))
    fy = 1/2 ; fx = fy/tan(a)
    for i in range(2, n):
        fy = fy+1
        a = atan2(fy, fx)
        X.append(X[-1]+2*cos(a)), Y.append(Y[-1]+2*sin(a))
    fig, ax = plt.subplots()
    fig.set_size_inches((8,20))
    p = PatchCollection([Circle((x,-y), 1) for x,y in zip(X,Y)]+[Circle((-x,-y), 1) for x,y in zip(X,Y)])
    ax.add_collection(p)
    ax.set_aspect('equal')
    ax.autoscale_view(True)

In [3]:
spheres(100, 2)



In [4]:
spheres(12, 5)



In [5]:
spheres(100,0.75)



In [6]:
spheres(100,0.2)



In [7]:
spheres(100,0.1)