Going Round in Circles
Introduction
Let us start with an investigation into rolling circles! Circles have been with us since our childhood toys and to our older (and more silly!) aspirations for wheels (ahem!). Let us understand their mathematics and see what we can make with them.
Inspiration
What is a Parametric Equation?
How was this curve created? The equation for the curve is given by a pair of parametric equations, one for \(x\) and one for \(y\):
\[ \begin{eqnarray} x &= cos(t) + cos(6t)/2 + sin(14t)/3\\ y &= sin(t) + sin(6t)/2 + cos(14t)/3 \end{eqnarray} \tag{1}\]
This form is especially suited for a computational depiction of the curve, since we can have the parameter \(t\) go from \(0~ ->\infty\) and let the \(x\) and \(y\) be computed and plotted. All right, whatever…but what does this have to do with circles?? For that we need to turn to the famous Euler formula relating complex vectors and circles.
How about the Euler Formula?
What is the equation of a circle? Most likely you will say:
\[ \begin{eqnarray} x^2 + y^2 &= 1\\ or ~ perhaps\\ (𝑥 − ℎ)^2 + (𝑦 − 𝑘)^2 &= 𝑅^2\\ \end{eqnarray} \tag{2}\]
for a circle with center \(C(h,k)\) and radius R.
As Frank Farris says, this is fine, but it represents a static view of a circle, which is not the simplest way to direct the drawing of one. The simplest way to instruct a machine to draw a circle uses a parametric form discussed above, also known as a vector-valued function:
\[ \gamma(t) = (cos(t), sin(t)) \] for the unit circle and
\[ 𝛾(𝑡) = (h + 𝑅 cos(𝑡), k + 𝑅 sin(𝑡)) \] for a more general one.
Now, if we were to use complex numbers as our notation, then the function for our circle becomes:
\[ \begin{eqnarray} \gamma(t) &=& (cos(t), sin(t))\\ &=& e^{it} \end{eqnarray} \tag{3}\]
where of course, \(i = \sqrt{-1}\).
This is the famous Euler Formula that connects complex numbers with trigonometry.
The Mystery Curve
Using this formula, our parametric function \(\mu(t)\) for our mystery curve becomes a family of three circles, of different sizes and rotating at different speeds:
\[ \begin{eqnarray} x &= cos(t) + cos(6t)/2 + sin(14t)/3\\ y &= sin(t) + sin(6t)/2 + cos(14t)/3\\ \end{eqnarray} \]
and
\[ \mu(t) = {\large{\color{hotpink}{1}} * {e^{\color{Blue}{\Large\pmb{1it}}}}} + {\large{\color{hotpink}{\frac{1}{2}}} * {e^{\color{Blue}{\Large{\pmb{6it}}}}}} + {\large{\color{hotpink}{\frac{i}{3}}} * {e^{\color{Blue}{\Large{\pmb{-14it}}}}}} \tag{4}\]
which gives us three rotating vectors with amplitudes given by {1, 1/2, 1/3} and with rotation speeds in the ratio {1 : 6: -14}. The first two rotate counter-clockwise; the third vector rotates in the clockwise direction since we have a negative coefficient for \(t\)). The tips of these rotating vectors course trace out the individual circles. We can picture the pattern as the vector sum of the vectors, or as three circles where each subsequent circle rotates and rolls on the circumference of the earlier one, like meshed gears.
How do we go from the parametric form in Equation 1 to the complex exponential form in Equation 4? The first two terms are direct combinings of the respective cos
and sin
terms into exponentials; the third term may need a bit of understanding.
Here the \(sin\) and \(cos\) terms are “interchanged” between x and y, so we need multiply by \(i\) (rotate by \(\pi/2\)) to swap them, which means that the third circle starts from a 90 degree angle compared to the other two. Multiplying by \(i\) however makes the \(sin\) term negative, so we need to negate t as well, since \(-sin(-t) = sin(t)\). This means that the third exponential rotates in the opposite direction compared to the first two. See the expansion / explanation in the margin. We discuss this more in the following.
\[ \begin{eqnarray} \frac{i}{3}*e^{-i14t} &=& \frac{i}{3} \Big\{cos(-14t) + i(sin(-14t) \Big\}\\ &=& \frac{i}{3} \Big\{cos(14t) - i*sin(14t)\Big\}\\ &=& \frac{1}{3} \Big\{i*cos(14t) + sin(14t)\Big\}\\ &=& \frac{1}{3} \Big\{sin(14t) + i*cos(14t)\Big\}\\ \end{eqnarray} \]
which are respectively the desired x and y parametric functions for the third term.
Sigh.
Amplitude | Rotation | Example | Operation in Parametric Equation | What does it mean, really? |
---|---|---|---|---|
Real | Positive/CCW | \(2*e^{3it}\) | \(x = 2*cos(3t)\); \(y=2*sin(3t)\) | Vector starts from x-axis, goes CCW |
Real | Negative/CW | \(2*e^{-3it}\) | \(x = 2*cos(3t)\); \(y=-2*sin(3t)\) | Vector starts from x-axis, goes CW |
Complex | Positive/CCW | \(2*i*e^{3it}\) | \(x = -2*sin(3t)\); \(y=2*cos(3t)\) | Vector starts at \(\pi/2\), goes CCW |
Complex | Negative/CW | \(2*i*e^{-3it}\) | \(x = 2*sin(3t)\); \(y=2*cos(3t)\) | Vector starts at \(\pi/2\), goes CW |
Do think of what might happen when the amplitude has an overall negative sign, like \(-2*e^{-3it}\) or \(-2i*e^{-3it}\)! (Gasp!! Swoon…). Just flip the vector on its head and rotate the same way as stated.
Plotting with Complex Exponentials in R
We can use the rules in the above table to directly plot using complex vectors in R:
Show the Code
Rotational Symmetry
We notice a pattern in Figure 1, our inspiration example: the shape has a five-fold symmetry: If we rotate the entire figure by \(\frac{2\pi}{5}\), it will overlap exactly with the original. Further, we suspect that the curve has 5 “pieces”, that repeat every \(\frac{2\pi}{5}\). If we chop up the parametric variable \(t\) into 5 sections, we might obtain each individual piece, rotated by that angle. What properties does the generating function Equation 4 have that causes this symmetry?
Following the development in Frank Farris’ book, let us record our ideas/suspicions of symmetry as:
\[ \begin{eqnarray} \mu(t) &= \mu(t + \color{Blue}{\Large\pmb{2\pi/5}})\\ &= e^{\color{Blue}{\huge\pmb{2\pi *i/5}}} * \mu(t) \end{eqnarray} \tag{5}\]
Does this work out? Let’s see:
\[ \begin{eqnarray} \mu(t + 2\pi/5) &=& \Big\{ e^{i(t + 2\pi/5)} + \frac{1}{2}*e^{i6(t + 2\pi/5)} + \frac{1}{3}*i*e^{-i14(t + 2\pi/5)}\Big\}\\ &=& \Big\{e^{2\pi i/5} *e^{it} + \frac{1}{2}*e^{12\pi i/5} *e^{i6t} + \frac{1}{3}*e^{-28\pi i/5} *e^{-i14t}\Big\}\\ &=& \Big\{e^{2\pi i/5}*e^{it} + \frac{1}{2}*e^{(10+2)\pi i/5}*e^{i6t} + \frac{1}{3}*e^{(-30 +2)\pi i/5} *e^{-i14t}\Big\}\\ &=& e^{2\pi i/5}* \Big\{ e^{it} + \frac{1}{2}*e^{i6t} + \frac{1}{3}*i*e^{-i14t}\Big\}\\ \end{eqnarray} \tag{6}\]
So if we shift time by \(t = 2\pi/5\), we get the same pattern rotated by \(2\pi/5\) radians. Because the frequencies 1, 6, and −14 are all congruent to 1 modulo 5, shifting time by \(2\pi/5\) causes the equation to add on a complex rotation term of \(e^{2\pi*i/5}\).
Time shifts are Angle Shifts. And our mystery curve hence meets the symmetry condition in Equation 5.
The Symmetry Condition Theorem
Suppose that \(m\) and \(k\) are integers and that all the frequency numbers \(n_j\), \(j={1..M}\) in the finite sum:
\[ f(t) = \sum_{i=1}^M(a_1*e^{in_1t} + a_2*e^{in_2t}...+ a_M*e^{in_Mt}) \] have \(n_j = k(mod~𝑚)\).
Then, for any choice of the complex coefficients \(a_j\), \(f(t)\) satisfies the symmetry condition:
\[ f(t + \frac{2\pi}{m}) = e{\frac{2k*\pi*i}{m}} * f(t)\\ for~ all~ t \]
What a mouthful! What does that mean?
If we take a set of \(M\) integer frequencies, such that they have the same remainder \(k\) when divided by another integer \(m\), then these frequencies when attached to rotating circles will give us \(m\)-fold symmetry. E.g: M = 5, m = 7, k = 1 implies the frequencies are -14+1, -7+1, 1, 7+1, 14+1.
Question: How do we handle \(n_j\), \(j={1..M}\) being complex, at least some of them?
Look back at the table Table 1.
Consider a \(term = i/3 * e^{i6t}\). We can expand this as:
\[ \begin{eqnarray} term &=& i/3 * e^{i6t}\\ &=& i/3 * \big[cos(6t) + i*sin(6t)\big]\\ &=& -1/3*sin(6t) + i/3*cos(t)\\ \end{eqnarray} \]
We can view this as a rotation by \(\pi/2\) in the counter-clockwise direction. Other angles will contribute to rotations of the coefficients in the same way. Complex Coefficients will alter the nature of the pattern of course, but not the symmetry!
Question: What happens when \(k\) is a factor of \(m\)? E.g: \(k=3\) and \(m=9\): what happens? Find out!
Design Principles for Rotational Symmetry
How do we capture all of the above in a set of design principles for symmetric rotation-based patterns? The design parameters for us are:
- Number of frequencies / rolling circles: M
- The Frequency values for each rolling circle: \(n_j\), \(j={1..M}\)
- The (complex) Amplitudes \(a_j\), \(j={1..M}\)
Larger values of \(M\) give a more fine grain structure to the pattern, especially when combined with diminishing amplitudes of \(a_j\), an idea that we will encounter again in Making Noise.
Let us randomly create an equation, using the following parameters:
- M = 5 (Number of rotating circles)
- m = 7 (Prime Modulus) i.e. Order of Symmetry
- k = 2 (The remainder of \(n*m~mod~k\)) i.e. Type of Symmetry
Here is the plot of the frequency components:
Show the Code
# Set graph theme
theme_set(new = theme_custom())
#
set.seed(42)
M <- 5 # Number of circles
m <- 7 # Prime Modulus
k <- 2 # Type of Symmetry
tibble(
index = seq(-floor((M - 1) / 2), floor((M - 1) / 2), 1),
prime_multiple = m * index,
remainder = rep(k, length(index)),
frequency = prime_multiple + remainder
) %>%
mutate(amplitude = if_else(frequency == k, 1, k / frequency)) %>%
# scaling amplitudes
# mutate(y0 = rep(0, length(index)),
# z0 = rep(0, length(index))) %>%
dplyr::select(prime_multiple, frequency, amplitude) -> circles
circles
Show the Code
##
circles %>%
gf_hline(yintercept = 0, colour = "grey") %>%
gf_segment(rep(0, length(prime_multiple)) + rep(0, length(prime_multiple)) ~ prime_multiple + frequency,
arrow = arrow(
angle = 20,
length = unit(0.15, "inches"),
ends = "last", type = "open"
)
) %>%
gf_segment(
rep(0, length(prime_multiple)) + amplitude ~
frequency + frequency,
data = circles, linewidth = 2,
arrow = arrow(
angle = 30,
length = unit(0.1, "inches"),
ends = "last", type = "open"
)
) %>%
gf_point(rep(0, length(prime_multiple)) ~ prime_multiple,
colour = "red", size = 3
) %>%
gf_point(rep(0, length(prime_multiple)) ~ frequency,
xlab = "Frequency Component",
ylab = "Amplitude", data = circles
) %>%
gf_labs(
title = "Rotating Vectors Frquencies and Amplitudes",
subtitle = "Negative Frequency components rotate counterclockwise", caption = "Red Dots: Prime Modulus Multiples"
) %>%
gf_refine(annotate(
x = circles$frequency + 1.75,
y = circles$amplitude,
geom = "text",
label = as.character(round(circles$amplitude, 4), nsmall = 3), size = 3.5
)) %>%
gf_refine(scale_x_continuous(breaks = c(-28, -21, -14, -7, -5, 0, 2, 7, 9, 14, 16))) %>%
gf_theme(theme_custom())
The function for this curve would be:
\[ \begin{multline} \mu(t) = {\large{\color{hotpink}{1}} * {e^{\color{Blue}{\large{\pmb{2it}}}}}}+ \\ +{\large{\color{hotpink}{0.2222}} * {e^{\color{Blue}{\large{\pmb{9it}}}}}} + {\large{\color{hotpink}{0.125}} * {e^{\color{Blue}{\large{\pmb{16it}}}}}} \\\ - {\large{\color{hotpink}{0.4}} * {e^{\color{Blue}{\large{\pmb{-5it}}}}}} -{\large{\color{hotpink}{0.1667}} * {e^{\color{Blue}{\large{\pmb{-12it}}}}}} \\\ \end{multline} \tag{7}\]
Let us now plot this:
Show the Code
# remainder = 2 from 7
# frequencies: 2, 7+2, 14+2, -7+2, -14+2
f_mystery2 <- function(x) {
1.0 * (exp((0 + 2i) * x) +
0.2222 * exp((0 + 9i) * x) +
0.125 * exp((0 + 16i) * x) -
0.4 * exp((0 - 5i) * x) -
0.1667 * exp((0 - 12i) * x))
}
data_mystery_2 <- tibble(t, pattern = f_mystery2(t))
data_mystery_2 %>%
gf_point(Im(pattern) ~ Re(pattern), title = "Complex Exponential Rendering
") %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
###
data2 <- tibble::tibble(
t = seq(0, 2 * pi, 0.001),
x = cos(2 * t) + 0.2222 * cos(9 * t) + 0.125 * cos(16 * t) - 0.4 * cos(5 * t) - 0.1667 * cos(12 * t),
y = sin(2 * t) + 0.2222 * sin(9 * t) + 0.125 * sin(16 * t) + 0.4 * sin(5 * t) + 0.1667 * sin(12 * t)
)
data2 %>%
gf_point(y ~ x, title = "Parametric Equation Rendering") %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
There, we have designed a pattern with seven-fold rotational symmetry. Can you make this in p5.js
? Can you try for other orders and types of symmetry?
Mirror Symmetry
The coordinate system defines a positive increase in angle as the counterclockwise direction. So an increase in the parameter \(t\), increases the angle for each frequency component in that direction, if their coefficient is positive, and the other way of their coefficient is negative. So far so good.
Consider a small modification to our original Figure 1:
Show the Code
## Original Mystery Curve
# ## remainder = +1 from 5
# ## frequencies 1, 5+1, -15+1
data1 <- tibble::tibble(
t = seq(0, 2 * pi, 0.001),
x = cos(t) + cos(6 * t) / 2 + sin(14 * t) / 3,
y = sin(t) + sin(6 * t) / 2 + cos(14 * t) / 3
)
# Mystery Curve
data1 %>%
gf_point(y ~ x) %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
# Derived from Mystery
# Remainder = 2 from 5
# Frequencies: 2, 5+2, -15+2
# Coeffs: 1,1,1
data3 <- tibble::tibble(
t = seq(0, 2 * pi, 0.001),
x = cos(t) + cos(6 * t) + cos(14 * t),
y = sin(t) + sin(6 * t) + sin(14 * t)
)
data3 %>%
gf_point(y ~ x) %>%
gf_refine(coord_equal()) %>%
gf_hline(yintercept = 0) %>%
gf_theme(theme_void())
It should be immediately clear that the second pattern above is the same above and below the horizontal line; it exhibits horizontal mirror symmetry, \(f(-t) = f(t)\).
Under what conditions would a pattern be symmetric about an arbitrarily-tilted mirror, a mirror at angle \(\alpha\) say?
From Farris:
When every coefficient is a real multiple of \(e\^{i\alpha}\), the curve satisfies \(f(-t) = e^{2\alpha*i}f(t)\).
The right-hand side is the correct expression for reflection across the line through the origin inclined at angle \(\alpha\)(Check!!). If one wants curves with slanted mirrors, simply finds a curve symmetric about the x-axis satisfying \(f(-t) = f(t)\), and tilts it by \(\alpha\).
Fun Extras to Try
It would be cool to simply develop the equations for any pattern in complex notation as in Equation 4 and throw that into code, without the tedious conversions into sines and cosines. Can we try that?
Here is an example in R:
Show the Code
t <- seq(0, 2 * pi, by = 0.001)
x <- t
## NOTE: need the minus sign here inside the exponential!!
## AND Absolutely need the "1" here before the solitary "i"!!
## Need to figure these out
f1 <- function(x) {
(exp(-(0 + 1i) * x) +
0.25 * exp(-(0 + 6i) * x) +
0.2 * exp(-(0 + 11i) * x))
}
plot(f1(x), asp = 1)
##
f2 <- function(x) {
(exp(-(0 + 2i) * x) +
0.2222 * exp(-(0 + 9i) * x) +
0.125 * exp(-(0 + 16i) * x) -
0.4 * exp(-(0 - 5i) * x) -
0.1667 * exp(-(0 - 12i) * x))
}
plot(f2(x), asp = 1)
## Plotting with Exponential Functions
f3 <- function(x) {
(exp(-(0 + 1i) * x) +
0.5 * exp(-(0 + 6i) * x) +
1 / 3 * exp(-(0 - 14i) * x)
)
}
plot(f3(x), asp = 1)
##
f4 <- function(x) {
(exp(-(0 + 1i) * x) +
0.5 * exp(-(0 + 6i) * x) +
1 / 3 * exp(-(0 + 14i) * x)
)
}
plot(f4(x), asp = 1)
##
# ## remainder = +2 from 5
# ## frequencies 0+2, 5+2, -15+2
# ## Coefficients 1, 1, 1
f5 <- function(x) {
(exp(-(0 + 2i) * x) +
1.0 * exp(-(0 + 7i) * x) +
1.0 * exp(-(0 + 13i) * x)
)
}
plot(f5(x), asp = 1)
##
# ## remainder = +2 from 5
# ## frequencies 0+2, 5+2, -15+2
# ## Coefficients 1, -1/2, -i/3 ( Note!!!)
f6 <- function(x) {
(exp(-(0 + 2i) * x) +
-0.5 * exp(-(0 + 7i) * x) +
-1 / 3 * exp(pi / 2 * 1i) * exp(-(0 + 13i) * x)
)
}
plot(f6(x), asp = 1)
Show the Code
# t <- seq(0, 2 * pi, by = 0.001) # Already computed
data1 <- tibble(t, pattern = f1(t))
data2 <- tibble(t, pattern = f2(t))
data3 <- tibble(t, pattern = f3(t))
data4 <- tibble(t, pattern = f4(t))
data5 <- tibble(t, pattern = f5(t))
data6 <- tibble(t, pattern = f6(t))
data1 %>%
gf_point(Im(pattern) ~ Re(pattern)) %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
data2 %>%
gf_point(Im(pattern) ~ Re(pattern)) %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
data3 %>%
gf_point(Im(pattern) ~ Re(pattern)) %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
data4 %>%
gf_point(Im(pattern) ~ Re(pattern)) %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
Comparing Exponential and Trigonometric Functions
Just for practice, let us once more be clear between the complex exponential notation, and the parametric trigonometric functions.
Show the Code
# ## remainder = +2 from 5
# ## frequencies 0+2, 5+2, -15+2
# ## Coefficients 1, 1, 1
f7 <- function(x) {
(exp(-(0 + 2i) * x) + exp(-(0 + 7i) * x) + exp(-(0 + 13i) * x))
}
### Parametric Coordinates Tibble
data7a <- tibble::tibble(
t = seq(0, 2 * pi, 0.001),
x = cos((2) * t) + cos(7 * t) + cos(13 * t),
y = sin(2 * t) + sin(7 * t) + sin(13 * t)
)
### Complex Exponential Tibble
data7b <- tibble(t, pattern = f7(t))
### Plots
plot(f7(x),
asp = 1, cex = 0.2,
main = "Base R: Exponential Function Plot"
)
###
data7a %>%
gf_point(y ~ x,
title = "ggFormula: Trigonometric Function Plot"
) %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
###
data7b %>%
gf_point(Im(pattern) ~ Re(pattern),
title = "ggFormula: Exponential Function Plot"
) %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
Show the Code
# ## remainder = +2 from 5
# ## frequencies 0+2, 5+2, -15+2
# ## Coefficients 1, -1/2, -i/3 ( Note!!!)
f8 <- function(x) {
(exp(-(0 + 2i) * x) - 0.5 * exp(-(0 + 7i) * x) +
1i / 3 * exp((0 + 13i) * x))
}
data8a <- tibble::tibble(
t = seq(0, 2 * pi, 0.001),
x = cos(2 * t) - 0.5 * cos(7 * t) +
0.3 * cos(-13 * t + pi / 2),
y = sin(2 * t) - 0.5 * sin(7 * t) +
0.3 * sin(-13 * t + pi / 2)
)
data8b <- tibble(t, pattern = f8(t))
###
plot(f8(x),
asp = 1, cex = 0.2,
main = "Base R: Exponential Function Plot"
)
###
data8a %>%
gf_point(y ~ x, title = "ggFormula: Trigonometric Function Plot") %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
###
data8b %>%
gf_point(Im(pattern) ~ Re(pattern),
title = "ggFormula: Exponential Function Plot"
) %>%
gf_refine(coord_equal()) %>%
gf_theme(theme_void())
Wait, But Why?
Sums of Complex exponentials are very common in mathematics and show up in many places: here, with symmetry, with Fourier Series, with Sound synthesis and Analysis.
Conclusion
We have seen the close relationship between complex rotating exponentials and their trigonometric decompositions, embodied in the Euler Formula.
We also saw how multiple such exponentials can be used to combine using complex weighting to create symmetric patterns.
And how symmetry depends upon the frequencies of the exponentials having a very specific relationship using modulo arithmetic.
Your Turn
Can you reverse engineer these curves, in R or in p5.js?
References
Frank Farris. Creating Symmetry: The Artful Mathematics of Wallpaper Patterns. Princeton University Press (2 June 2015).
Gorilla Sun Blog. https://www.gorillasun.de/blog/parametric-functions-and-particles/
CrateCode: Complex Generative Art with p5.js. https://cratecode.com/info/p5js-generative-art-complex-functions
Gorilla Sun Blog. https://www.gorillasun.de/blog/parametric-functions-and-particles/
Brent Yorgey.(2015). The Math Less Travelled Blog. Random Cylic Curves. https://mathlesstraveled.wordpress.com/2015/06/04/random-cyclic-curves-5/
University of New South wales. Exponential Sums Page. https://www.unsw.edu.au/science/our-schools/maths/our-school/spotlight-on-our-people/history-school/glimpses-mathematics-and-statistics/exponential-sums
John Myles White. Complex Numbers in R. https://www.johnmyleswhite.com/notebook/2009/12/18/using-complex-numbers-in-r/
R Package Citations
Citation
@online{2024,
author = {},
title = {\textless Iconify-Icon Icon=“ph:circles-Three-Fill”
Width=“1.2em”
Height=“1.2em”\textgreater\textless/Iconify-Icon\textgreater{}
\textless Iconify-Icon Icon=“gravity-Ui:function” Width=“1.2em”
Height=“1.2em”\textgreater\textless/Iconify-Icon\textgreater{}
{Going} {Round} in {Circles}},
date = {2024-05-02},
url = {https://av-quarto.netlify.app/content/courses/MathModelsDesign/Modules/2-Geometry/10-Circles/},
langid = {en}
}