Phyllotaxis: Draw flowers using mathematics

Michael Taylor

2018/05/19

1 Patterns in nature

The scientist does not study nature because it is useful; he studies it because he delights in it, and he delights in it because it is beautiful (Henri Poincaré)

There are many examples of natural facts that can be described in mathematical terms. Nice examples are the shape of snowflakes, the fractal geometry of romanesco broccoli or how self-similarity rules the growth of plants.

R is a tool for doing serious analysis, but not everything in life is serious. Life is also funny, and R can be used to have fun and to do beautiful things. Its graphical power can be used to produce artistic images like the one that illustrates this section, which is inspired by how plants arrange their leaves. This fact is called phyllotaxis and will serve as the basis of this project.

In this notebook, we are using the ggplot2 package. Apart from having fun, we will learn many important features of it that will be useful not only to do art but also to represent data in real-life problems. Let’s start by loading the library.

# This sets plot images to a nice size.
options(repr.plot.width = 4, repr.plot.height = 4)
knitr::opts_chunk$set(cache = T)
# Loading in the ggplot2 package
library(ggplot2)

2 Warming up: drawing points on a circle

There are many ways to represent data with ggplot2: from simple scatter plots to more complex ones, such as violin plots. The functions that start with geom_ define how the plot is shown. In this notebook, we will only work with geom_point which plots points in two dimensions. We just need a dataset with two variables, let’s call them x and y.

Let’s start by drawing \(50\) points on a circle of radius 1. As every \((x, y)\) point should be in the unit circle, it follows that \(x² + y² = 1\). We can get this using the superfamous Pythagorean trigonometric identity which states that \(sin²(θ) + cos²(θ) = 1\) for any real number \(θ\).

t <- seq(0, 2*pi, length.out = 50)
x <- sin(t)
y <- cos(t)
df <- data.frame(t, x, y)

# Make a scatter plot of points in a circle
p <- ggplot(df, aes(x, y))
# coord_fixed(ratio = 1) ensures that the ranges of axes are equal 
# to the specified ratio by adjusting the plot aspect ratio
p + geom_point() + coord_fixed(ratio = 1)

3 Make it harmonious with the Golden Angle

Plants arrange their leaves in spirals. A spiral is a curve which starts from the origin and moves away from this point as it revolves around it. In the plot above all our points are at the same distance from the origin. A simple way to arrange them in a spiral is to multiply x and y by a factor which increases for each point. We could use t as that factor, as it meets these conditions, but we will do something more harmonious. We will use the Golden Angle:

Golden Angle = \(\pi(3 −\sqrt 5)\)

This number is inspired by the Golden Ratio, one of the most famous numbers in the history of mathematics. Both the Golden Ratio and the Golden Angle appear in unexpected places in nature. Apart of flower petals and plant leaves, you’ll find them in seed heads, pine cones, sunflower seeds, shells, spiral galaxies, hurricanes, etc.

It’s time to spiralize!

• Create the variable points which defines the number of points to draw and set its value to 500.
• Create another variable called angle and set its value to \(\pi(3 −\sqrt 5)\).
• Make a scatter plot.

# Defining the number of points
points  <- 500

# Defining the Golden Angle
angle <- pi*(3-sqrt(5))

t <- (1:points) * angle
x <- sin(t)
y <-cos(t)
df <- data.frame(t, x, y)

# Make a scatter plot of points in a spiral
p <- ggplot(df, aes(x*t, y*t))
p + geom_point() + coord_fixed(ratio = 1)

4 Remove everything unnecessary

Apart from data, a plot includes many other components that define its final appearance. Our previous plot contains:

Art does not get along with most of these elements, so it’s time to move to action.

# Make a scatter plot of points in a spiral
p <- ggplot(df, aes(x*t, y*t))
p + geom_point() +
  theme(aspect.ratio = 1,
       panel.grid = element_blank(),
       axis.ticks = element_blank(),
       text = element_blank(),
       title = element_blank(),
       panel.background = element_rect(fill = "white"))

5 A bit of makeup: size, color and transparency

Our drawing starts to look like a plant, but we can do it much better. By changing color, transparency (also called alpha), and size of the points, the image will become more appealing.

# Make a scatter plot of points in a spiral
p <- ggplot(df, aes(x*t, y*t))
p + geom_point(size=8, alpha=0.5, color="darkgreen") +
  theme(aspect.ratio = 1,
       panel.grid = element_blank(),
       axis.ticks = element_blank(),
       text = element_blank(),
       title = element_blank(),
       panel.background = element_rect(fill = "white"))

6 Play with aesthetics: the dandelion

Until now, all points have the same appearance (size, color, shape, and alpha). Sometimes you will want to make the appearance of the points dependent on a variable in your dataset. Now we will make size variable. We will also change the shape of points. Although we won’t be able to blow on it, the resulting image should remind you of a dandelion.

# Make a scatter plot of points in a spiral
p <- ggplot(df, aes(x*t, y*t))
p + geom_point(aes(size=t),
               alpha=0.5,
               color="black",
               shape=8) +
  theme(aspect.ratio = 1,
       panel.grid = element_blank(),
       axis.ticks = element_blank(),
       text = element_blank(),
       title = element_blank(),
       legend.position = "none",
       panel.background = element_rect(fill = "white"))

7 Put all it together: the sunflower

Plants not only use the Golden Angle to arrange leaves. It is also found in the arrangement of sunflower seeds. We don’t need anything new to draw a sunflower; we just need to combine some of the things we already know.

# Make a scatter plot of points in a spiral
p <- ggplot(df, aes(x*t, y*t))
p + geom_point(aes(size=t),
               alpha=0.5,
               color="yellow",
               shape=17) +
  theme(aspect.ratio = 1,
       panel.grid = element_blank(),
       axis.ticks = element_blank(),
       text = element_blank(),
       title = element_blank(),
       legend.position = "none",
       panel.background = element_rect(fill = "darkmagenta"))

8 What if you modify the angle?

These patterns are very sensitive to the angle between the points that form the spiral; small changes to the angle can generate very different images. Let’s look at an example of that.

# Defining the Golden Angle
angle <- 2.0
points <- 1000
t <- (1:points) * angle
x <- sin(t)
y <-cos(t)
df <- data.frame(t, x, y)

# Make a scatter plot of points in a spiral
p <- ggplot(df, aes(x*t, y*t))
p + geom_point(aes(size=t),
               alpha=0.5,
               color="yellow",
               shape=17) +
  theme(aspect.ratio = 1,
       panel.grid = element_blank(),
       axis.ticks = element_blank(),
       text = element_blank(),
       title = element_blank(),
       legend.position = "none",
       panel.background = element_rect(fill = "darkmagenta"))

9 All together now: imaginary flowers

The techniques you’ve seen so far allows you to create an infinite number of patterns inspired by nature: the only limit is your imagination. But making art has also been a fun excuse to learn to use ggplot. All the tricks we have seen in this notebook are useful when plotting real data too.

The image on the left is a simple variation of the previous flower and is in essence very similar to the first figure in which we plotted 50 points in a circle. I hope you’ve enjoyed the journey between that simple circle and this beautiful flower.

# Defining the number of points
points  <- 2000

# Defining the Golden Angle
angle <- (13 * pi) / 180 #pi * (3 - sqrt(5))
#t <- seq(0, 2*pi, length.out = 50)
t <- (1:points) * angle
x <- sin(t)
y <-cos(t)
df <- data.frame(t, x, y)

# Make a scatter plot of points in a spiral
p <- ggplot(df, aes(x*t, y*t))
p + geom_point(aes(size=t),
               alpha=0.3,
               color="darkmagenta",
               shape=1) +
  theme(aspect.ratio = 1,
       panel.grid = element_blank(),
       axis.ticks = element_blank(),
       text = element_blank(),
       title = element_blank(),
       legend.position = "none",
       panel.background = element_rect(fill = "white"))

sessionInfo()
## R version 3.4.4 (2018-03-15)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 17134)
## 
## Matrix products: default
## 
## locale:
## [1] LC_COLLATE=English_Canada.1252  LC_CTYPE=English_Canada.1252   
## [3] LC_MONETARY=English_Canada.1252 LC_NUMERIC=C                   
## [5] LC_TIME=English_Canada.1252    
## 
## attached base packages:
## [1] methods   stats     graphics  grDevices utils     datasets  base     
## 
## other attached packages:
## [1] ggplot2_2.2.1
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_0.12.16     bookdown_0.7     digest_0.6.15    rprojroot_1.3-2 
##  [5] plyr_1.8.4       grid_3.4.4       gtable_0.2.0     backports_1.1.2 
##  [9] magrittr_1.5     scales_0.5.0     evaluate_0.10.1  pillar_1.2.2    
## [13] blogdown_0.6     rlang_0.2.0      stringi_1.1.7    lazyeval_0.2.1  
## [17] rmarkdown_1.9    tools_3.4.4      stringr_1.3.0    munsell_0.4.3   
## [21] xfun_0.1         yaml_2.1.19      compiler_3.4.4   colorspace_1.3-2
## [25] htmltools_0.3.6  knitr_1.20       tibble_1.4.2