Fourier Curves
The core idea
Every curve in this project is built from rotating complex numbers. A single rotating complex exponential traces a circle:
exp(im · f · t) for t ∈ [0, 2π]where f is an integer that controls how many times the point goes around the circle per period. Adding several of these together with different magnitudes and frequencies produces a more complex path — the same idea behind a Fourier series.
μg — the general curve
μg(t; cs, fs) = Σᵢ cs[i] · exp(im · fs[i] · t)Parameters:
| Parameter | Type | Description |
|---|---|---|
t | Real | Curve parameter, typically range(0, 2π, n) |
cs | Vector{Complex} | Coefficients — one per frequency term |
fs | Vector{Real} | Frequencies — integers, but any real is accepted |
The return value is a complex number whose real part is x and imaginary part is y on the canvas.
What each parameter controls
cs — coefficients
Each coefficient cs[i] is a complex number with a magnitude and a phase:
- Magnitude
|cs[i]|controls how large a contribution that frequency makes. Larger magnitude → that term pulls the curve further from the origin. - Phase
angle(cs[i])rotates that term's starting position. In practice most examples use real positive values, but complex coefficients shift where each circular component starts, changing the overall curve shape without changing its "size".
fs — frequencies
Each frequency fs[i] controls how many loops that component makes per period:
fs[i] = 1— one loop (a circle)fs[i] = 2— two loops (the component winds around twice per cycle)fs[i] = -1— one loop in the opposite direction- Large
|fs[i]|— tight, high-frequency wiggles added to the curve
Combining a low frequency (the overall shape) with higher frequencies (fine detail) is how intricate curves are built. The classic example from Farris:
cs = [1.0, 0.5, im/3]
fs = [1, 6, -14]This produces a curve with a large circular component (f=1), a medium six-fold component (f=6), and a fine 14-fold component in reverse (f=-14).
Quick experiments
Change the shape by swapping frequencies:
# Simple figure-eight-like shape
μg(t, cs=[1.0, 0.5], fs=[1, -1])
# More petals by adding a higher frequency
μg(t, cs=[1.0, 0.3, 0.15], fs=[1, 5, 10])fkm — the selective Fourier sum
fkm(t, k, m; M=5, as=ones(2M+1))fkm is a restricted version of μg that only uses frequencies n where n mod m == k. This is the construction Farris uses in Chapter 4 to enforce specific wallpaper symmetry groups.
What the parameters do
| Parameter | Default | Description |
|---|---|---|
k | — | Residue class. Which remainder to keep: selects frequencies where n % m == k |
m | — | Modulus. The "period" of the selection rule |
M | 5 | Half-bandwidth. Frequencies run from −M to +M; 2M+1 total possible terms |
as | ones(2M+1) | Weights. One coefficient per frequency index from −M to +M |
How k and m create symmetry
The selection rule n % m == k keeps only frequencies that are congruent to k modulo m. This enforces m-fold rotational symmetry in the resulting artwork. The curve itself may not look symmetric, but when you draw m rotational copies (as drawZθfunc4b does), the copies will tile perfectly.
Some useful combinations:
m | k | Effect |
|---|---|---|
| 3 | 1 | 3-fold symmetry (frequencies 1, 4, 7, −2, −5, …) |
| 5 | 2 | 5-fold symmetry |
| 6 | 1 | 6-fold symmetry |
| 4 | 1 | 4-fold symmetry |
Setting k=0 selects frequencies 0, ±m, ±2m, … which tends to produce flowers with petals on the axes.
M — the bandwidth
M controls how many terms are available. With M=5 there are 11 possible frequencies (−5 to +5); with M=37 there are 75. A larger M allows finer detail in the curve at the cost of computation time. For artwork purposes, M between 10 and 50 gives good results.
as — per-frequency weights
as is a vector of length 2M+1. The index n+M+1 maps frequency n to its weight. Frequencies that don't satisfy the residue rule are ignored regardless of their weight.
- All ones (default): equal weight to all selected frequencies — produces a relatively "busy" curve.
- Cosine taper (
as[i] = cos(π*(i-M-1)/(2M))): gives less weight to extreme frequencies, producing smoother curves. - Random (
as = randn(2M+1)): each call gives a different curve shape with the same symmetry order.
Example
using DigitalArt
M = 20
t = range(0, 2π, 1000)
# 5-fold rotational symmetry, residue class 2
vals = fkm.(t, 2, 5; M=M, as=ones(2M+1))Connecting curves to the canvas
Both μg and fkm return complex numbers. To draw them:
xs = real.(vals)
ys = imag.(vals)
lines!(ax, xs, ys)The canvas coordinates run roughly in the range set by the largest coefficient. If the curve looks too small, scale the coefficients:
cs_scaled = 2.0 .* cs # double the size