Phyllotaxis and Fibonacci

Sunflowers and traffic lights

It occurred to me the other day while looking at a set of LED traffic lights that they could be improved quite easily.  The layout of the LEDs in all the ones I've seen seems to favour some radial directions over others and/or result in a higher concentration of bulbs at some radii than at others.

My solution involves placing LEDs a increasing distances from the centre and as each LED is placed rotating by the golden angle.  This ensures that no radial direction is preferred over any other.  In addition to this, setting $r_n = \sqrt{n}$ ensures that each bulb covers a roughly similar area.

By "area covered" I am referring to the portion of space which is closer to that bulb than to any other.  This is the Voronoi cell generated by a point belonging to a set of points.  The picture above shows the Voronoi cells generated by a set of LEDs placed in the manner described, and the code below shows how I obtained it.  I think it would make a pretty stain glass window!

#!/usr/bin/env python2
import matplotlib.colors
import matplotlib.cm
import matplotlib.pyplot as plt 
import matplotlib.patches as patches 
from scipy.spatial import Voronoi
from math import sin, cos, pi
from random import random

# Define the constants found in nature
phi = .5+.5*(5**.5)
theta_golden = 2*pi/(phi**2)

# Find leaf centres which are used as Voronoi cell generators
leaf_centres = []
for i in range(1,400):
    # r should be sqrt(i) so that each cell is roughly same area
    r = i**.5
    #r = i
    theta = theta_golden * i
    x,y = r*cos(theta), r*sin(theta)
    leaf_centres.append((x,y))
    
# Set up the plot parameters
plt.ion()
ax = plt.gca()
ax.set_aspect('equal')
plt.tick_params(axis='x', which='both', bottom=False, top=False, labelbottom=False)
plt.tick_params(axis='y', which='both', left=False, right=False, labelleft=False)
xs = map(lambda (x,y):x, leaf_centres)
ys = map(lambda (x,y):y, leaf_centres)
xlim = (min(xs) * 1.1, max(xs) * 1.1)
ylim = (min(ys) * 1.1, max(ys) * 1.1)
ax.set_xlim(*xlim)
ax.set_ylim(*ylim)

# Calculate the Voronoi cells
v = Voronoi(leaf_centres)

# Plot cells using a random colours from the 'hot_r' heatmap
norm = matplotlib.colors.Normalize(vmin=-.5,vmax=2,clip=True)
cmap = matplotlib.cm.cmap_d['hot_r']

for region in v.regions:

    # Skip cells around the outside which are open and so not polygons
    if -1 in region:
        continue

    # Skip region if it has too few vertices to be a polygon
    if len(region) < 3:
        continue

    # Find vertices of polygon
    region.append(region[0])
    xs = v.vertices[region,0]
    ys = v.vertices[region,1]

    # Skip outside polygon if too far from the centre
    rs = (xs*xs + ys*ys)**.5
    if max(rs) > xlim[1] or max(rs) > ylim[1]:
        continue

    # Plot polygon with black outline and random fill colour
    vs = zip(xs,ys)
    rgb = cmap(norm(random()))
    poly = patches.Polygon(vs, color=rgb) 
    ax.add_patch(poly)
    plt.plot(xs, ys, lw=1, color='black')

# hang around
raw_input('Press any key to close')


The following picture shows what you get if you replace the $r_n = \sqrt{n}$ rule with $r_n = n$.  The cell sizes now get larger as you go further out, but the spiral arms are easier to see.  I'm not sure which I prefer...

 POSTSCRIPT

There's an anecdote about Voronoi cells which I find quite interesting.  Apparently the 19th century statistician John Snow used these to demonstrate that the vector for Cholera was infected drinking water.  He used the set of public water pumps in London as the cell generators and showed that the affected population corresponded to those living or working within certain cell boundaries.  He explained this by saying that a) in general everyone travels to the nearest pump to drink, and b) it was drinking from an infected pump that caused people to contract the disease.

POST-POSTSCRIPT

I wondered what the fixed cell area design would look like if it was painted onto a globe rather than printed onto a flat surface.  The answer is obtained by replacing $r$ in the program with $\psi$, the angle from the pole.  And the rule $r_n = \sqrt{n}$ becomes $\psi_n = cos^ {-1}\left(\frac{N-2n}{N}\right)$ where $N$ is the number of cells wanted.  Projecting the result onto a 2D surface, with the pole centre, and generating Voronoi cells gives you a nice Sun like image:


Comments