# -*- 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()