# -*- Mode: Python -*-
# generate fibonacci spirals in svg.
# mathologer: https://youtu.be/_GkxCIW46to
# coding train: https://www.youtube.com/watch?v=KWoJgHFYWxY
# wikipedia: https://en.wikipedia.org/wiki/Phyllotaxis
import math
import svgwrite
def rgb (r,g,b):
return 'rgb(%d,%d,%d)' % (r,g,b)
def gen_color_gradient (s, e, n):
sr, sg, sb = s
er, eg, eb = e
while 1:
for i in range (n):
delta = float(i)/float(n)
yield (
sr + delta * (er - sr),
sg + delta * (eg - sg),
sb + delta * (eb - sb),
)
yield e
def gen_color_rainbow():
# ROYGBIV
colors = [
(0xff,0x00,0x00),
(0xff,0x7f,0x00),
(0xff,0xff,0x00),
(0x00,0xff,0x00),
(0x00,0x00,0xff),
(0x4b,0x00,0x82),
(0x94,0x00,0xd3),
]
while 1:
for c in colors:
yield c
def fibgen():
a, b = 1, 1
while 1:
yield a
a, b = b, a + b
# generate colors in runs, where each run has a length
# controlled by the fibonacci sequence.
def gen_color_fib (gen):
fib = fibgen()
while 1:
color = gen.next()
for i in range (fib.next()):
yield color
# generate gradients of length fib(n) between the colors
# given by `gen`.
def gen_color_fib_gradient (gen):
fib = fibgen()
a = gen.next()
while 1:
b = gen.next()
n = fib.next()
gen1 = gen_color_gradient (a, b, n+2)
for i in range (n):
yield gen1.next()
a = b
# c is the spacing
# a = n * phi
# r = c * sqrt(n)
phi = (1 + math.sqrt(5))/2
rad_phi = (math.pi * 2) / (phi + 1) # 2*pi / phi^2
# tweak for landscape print
w, h = 1294, 970
def laser0():
dots = 6000
c = 6.1
size = 4
phi = (1 + math.sqrt(5))/2
rad_phi = (math.pi * 2) / (phi + 1) # 2*pi / phi^2
d = svgwrite.Drawing ('/tmp/fib.svg', (w,h))
a = 0
for n in range (0, dots):
r = c * math.sqrt(n)
x = r * math.cos (a) + w/2
y = r * math.sin (a) + h/2
d.add (d.circle ((x, y), size, fill='black', stroke='black'))
a += rad_phi
d.save()
def laser1():
dots = 10000
c = 3.8
size = 2
phi = (1 + math.sqrt(5))/2
rad_phi = (math.pi * 2) / (phi + 1) # 2*pi / phi^2
d = svgwrite.Drawing ('/tmp/fib.svg', (w,h))
a = 0
for n in range (0, dots):
r = c * math.sqrt(n)
x = r * math.cos (a) + w/2
y = r * math.sin (a) + h/2
d.add (d.circle ((x, y), size, fill='black', stroke='none'))
a += rad_phi
d.save()
def color0():
color_gen = gen_color_gradient ((255,255,0),(255,0,100), dots)
d = svgwrite.Drawing ('/tmp/fib.svg', (w, h))
a = 0
for n in range (0, dots):
c = 15 - (float(n)/float(dots)) * 6
r = c * math.sqrt(n)
x = r * math.cos (a) + (w/2)
y = r * math.sin (a) + (h/2)
size = min(10, 12 - math.sqrt(n)/5)
color = rgb (*color_gen.next())
d.add (d.circle ((x, y), size, fill=color, stroke='black'))
a += rad_phi
d.save()
def color1():
dots = 10000
c = 3.8
size = 2
phi = (1 + math.sqrt(5))/2
rad_phi = (math.pi * 2) / (phi + 1) # 2*pi / phi^2
colors = ['rgb(200,0,0)', 'rgb(0,200,0)', 'rgb(0,0,200)']
d = svgwrite.Drawing ('/tmp/fib.svg', (w,h))
a = 0
for n in range (0, dots):
r = c * math.sqrt(n)
x = r * math.cos (a) + w/2
y = r * math.sin (a) + h/2
color = colors[n%3]
d.add (d.circle ((x, y), size, stroke='none', fill=color))
a += rad_phi
d.save()
def voronoi0():
# https://github.com/jmespadero/pyDelaunay2D
from delaunay2D import Delaunay2D
# tiny details
dots = 3000
c = 6.1
# nice for coloring
dots = 1000
c = 18
phi = (1 + math.sqrt(5))/2
rad_phi = (math.pi * 2) / (phi + 1) # 2*pi / phi^2
print 'voronoi...'
D = Delaunay2D (center=(0,0), radius=5000)
a = 0
for n in range (0, dots):
r = c * math.sqrt(n)
x = r * math.cos (a)
y = r * math.sin (a)
D.addPoint ((x,y))
a += rad_phi
vc, vr = D.exportVoronoiRegions()
#color_gen = gen_color_gradient ((255,255,0),(255,0,100), dots)
#color_gen = gen_color_gradient ((255,255,0),(255,0,100), 5)
#color_gen = gen_color_rainbow()
#color_gen = gen_color_fib (gen_color_gradient ((255,255,0), (255,0,100), 10))
#color_gen = gen_color_fib (gen_color_rainbow())
color_gen = gen_color_fib_gradient (gen_color_rainbow())
print 'drawing...'
d = svgwrite.Drawing ('/tmp/voronoi.svg', (w, h))
# draw each region into svg
cx, cy = w/2, h/2
for rid, region in vr.iteritems():
points0 = [vc[p] for p in region]
points1 = [(x+cx, y+cy) for (x,y) in points0]
color = rgb (*color_gen.next())
d.add (d.polygon (points1, fill=color, stroke='black'))
d.save()
#color0()
#color1()
#laser0()
#laser1()
voronoi0()