#!/usr/bin/env python """ PyProcessing animation of a growing sunflower, a.k.a. "spiral phylotaxis." Featuring the "meristem," the blob in the middle that things grow out of. Home page: http://www.tiac.net/~sw/2014/02/Meristem This uses the golden-ratio-angle cheat. In actual plants the pattern is formed by seeds growing into gaps between the earlier seeds, which was demonstrated brilliantly by S. Douady and Y. Couder: http://www.youtube.com/watch?v=9Qy8QnNqB4A Vi Hart has a great series of videos, "Doodling in Math Class: Spirals, Fibonacci and Being a Plant." Part 1: http://www.youtube.com/watch?v=ahXIMUkSXX0 Part 2: http://www.youtube.com/watch?v=lOIP_Z_-0Hs Part 3 gets down to the nitty gritty and a bit of debunking: http://www.youtube.com/watch?v=14-NdQwKz9w In her notes and references, the links are rectangles that appear as the video plays: http://www.youtube.com/watch?v=POKWkuwoihM Hart references the short article "The Mathematical Lives of Plants," by Julie J. Rehmeyer: http://www.math.colostate.edu/~mueller/math155/Lives_of_Plants.pdf Copyright (c) 2014 Steve Witham ess double you at not-this tiac dot net. This code is available under a BSD licence; see the bottom of this file. """ # pyprocessing pulls in a lot of dependencies when it installs, but it is # included in "Enthought Python Distribution Free", a.k.a. "Canopy Express": # https://www.enthought.com/products/epd/free/ from pyprocessing import * # You can change these constants fairly independently: WIDTH = 512 # Width and height of the whole window. FRAMES_PER_SEC = 8 # Smoother motion => more processor load. SEED_DIAM = 16 BIRTHS_PER_SEC = 2.0 N_BUDS = 3 # How many dots in the gray center area. # These are determined by the constants above: RADIUS = WIDTH * .5 CENTER_X, CENTER_Y = (RADIUS, RADIUS) FRAMES_PER_BIRTH = int(FRAMES_PER_SEC / BIRTHS_PER_SEC + .5) BUD_N_FRAMES = N_BUDS * FRAMES_PER_BIRTH; SPEED = SEED_DIAM * SEED_DIAM * .6 / FRAMES_PER_BIRTH; PHI = (sqrt(5) - 1) / 2 # Actually 1/phi or phi - 1, same thing. def setup(): global delay, FRAMES_PER_BIRTH, FRAMES_PER_SEC, T, SEEDS, N frameRate(FRAMES_PER_SEC) size(WIDTH, WIDTH) smooth() # Antialias. T = 0 SEEDS = [] N = 0 def draw(): global T, X, Y, FRAMES_PER_BIRTH, WIDTH, SPEED, SEED_DIAM, SEEDS, N global BUD_N_FRAMES if T % FRAMES_PER_BIRTH == 0: # Start a new bud. age = -int(BUD_N_FRAMES) angle = N * PHI * PI * 2 h, v = cos(angle), sin(angle) SEEDS.append( (age, h, v) ) N += 1 background(color(255, 255, 255)) # Draw the "meristem," i.e. fuzzy blob in the middle. noStroke() for i in range(SEED_DIAM, 0, -1): frac = float(i) / SEED_DIAM fill(int(255 - 64 + 64 * frac)) d = SEED_DIAM * 2 + i ellipse(CENTER_X, CENTER_Y, d, d) fill(255 - 64) ellipse(CENTER_X, CENTER_Y, 2 * SEED_DIAM, 2 * SEED_DIAM) new_seeds = [] noStroke() for age, h, v in SEEDS: # "maturation" is when age == 0. r_at_maturation = SEED_DIAM if age < 0: # Before maturation, act like a "bud". # The bud's movement away from the center follows an exponential # curve matched to the "adult" curve and slope at "maturation." a = .5 * SPEED / r_at_maturation ** 2 b = log(r_at_maturation) r = exp(a * age + b) # The diameter is proportional to the distance from the center, # finally matching the full diameter at maturation (when age == 0). diam = SEED_DIAM * r / r_at_maturation # The color fades from light gray to black over the budding time. fill((255 - 64) - (255 - 64) * (age + BUD_N_FRAMES) / BUD_N_FRAMES) else: # Act like an adult. (r == r_at_maturation) when age == 0. r = sqrt(age * SPEED + r_at_maturation ** 2) diam = SEED_DIAM fill(0) # Black. if r > RADIUS: # Off the edge, seed is not drawn or added to tne new_seeds list. continue x, y = r * h, r * v if r > RADIUS * .75: # Seeds fade to white at the point where they disappear. fill(255 * (r / RADIUS * 4 - 3)) ellipse(CENTER_X + x, CENTER_Y + y, diam, diam) new_seeds.append( (age + 1, h, v) ) SEEDS = new_seeds T += 1 run() """ meristem.py Copyright (c) 2014 Steve Witham All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """