Creating Symmetry

Fourier

DigitalArt.FourierModule
Fourier

Fourier constructions for symmetry patterns, following Farris Chapter 4.

General curve function μg

The general parameterised curve is a finite Fourier sum:

μg(t; cs, fs) = Σᵢ cs[i] · exp(im · fs[i] · t)

where cs are complex coefficients and fs are integer frequencies. This is the core building block for all curve construction in the project.

Selective Fourier sums fkm

fkm(t, k, m) sums only those terms where the frequency index n satisfies n ≡ k (mod m). This selects a specific residue class of Fourier modes and is the mechanism Farris uses to enforce wallpaper symmetry groups.

fkm(t, k, m; M, as) = Σ_{n=−M}^{M, n%m==k} as[n+M+1] · exp(im · n · t)
DigitalArt.Fourier.fkmMethod
fkm(t, k, m; M=5, as=ones(2M+1)) -> ComplexF64

Selective Fourier sum retaining only frequencies n where n % m == k.

This construction is central to Farris' approach to wallpaper symmetry groups: by restricting which Fourier modes participate, specific rotational and reflective symmetries are enforced.

fkm(t, k, m) = Σ_{n=−M}^{M} as[n+M+1] · exp(im·n·t)   [where n % m == k]

Arguments:

  • t: real parameter
  • k: residue class (integer)
  • m: modulus (positive integer)
  • M: half-bandwidth; frequencies range over −M:M (default 5)
  • as: coefficient vector of length 2M+1 (default all ones)

Example — 5-fold symmetry, residue class 3:

M = 37
t = range(0, 2π, 1000)
vals = fkm.(t, 3, 5; M=M, as=ones(2M+1))
DigitalArt.Fourier.μgMethod
μg(t; cs, fs) -> ComplexF64

General Fourier curve: sum of rotating complex exponentials.

μg(t) = Σᵢ cs[i] · exp(im · fs[i] · t)

Arguments:

  • t: real parameter (typically in [0, 2π])
  • cs: vector of complex coefficients
  • fs: vector of integer frequencies (same length as cs)

Example:

t = range(0, 2π, 1000)
vals = μg.(t, cs=[1.0, 0.5, im/3], fs=[1, 6, -14])

Symmetry

DigitalArt.SymmetryModule
Symmetry

Rendering functions for rotational symmetry artwork.

Provides drawfunc for basic curve plotting and drawZθfunc4b for the core symmetry construction: drawing n rotated copies of a curve with a probabilistic color sequence.

The symmetry construction

Given a complex-valued curve func(t) and n evenly spaced rotation angles θ ∈ [0, 2π), each rotated copy is exp(im·θ) · func(t). Colors evolve via the mycolor state machine (see SymmetryColors).

The history array returned by drawZθfunc4b records (θ, color, cont) for each rotation, enabling replay or animation.

DigitalArt.Symmetry.drawZθfunc4bMethod
drawZθfunc4b(func, t, ic, cf, cfs; n=8) -> (Figure, history)

Draw n rotationally symmetric copies of a complex-valued curve with evolving colors.

Arguments:

  • func: complex-valued function of a real parameter t
  • t: parameter range (e.g. range(0, 2π, 1000))
  • ic: initial HSLA color
  • cf: color selector function (e.g. mycolor from SymmetryColors)
  • cfs: color configuration array passed to cf
  • n: number of rotations (default 8)

The figure has a black background. Each rotation θ = 2πk/n produces a curve exp(im·θ) · func.(t) drawn in the current color. Color is updated each step via cf(cfs; pc=current_color, cont=cont_flag).

Returns:

  • Figure: the completed Makie figure
  • history: vector of [θ, color, cont] for each rotation step
DigitalArt.Symmetry.drawfuncMethod
drawfunc(func, t; color=nothing) -> Figure

Plot the complex-valued curve func evaluated at parameter values t.

The real and imaginary parts of func.(t) are used as x and y coordinates. If color is nothing, a random RGBA color is chosen.

Returns a Makie Figure.

SymmetryColors

DigitalArt.SymmetryColorsModule
SymmetryColors

Color generation and color state machine for symmetry artwork.

Provides randomised HSLA color generators for common color families, a perturbation function for smooth color variation, and a probabilistic state machine (mycolor) that drives the color evolution across rotational traces.

Color configuration arrays (cfs)

A cfs array is a Vector of (threshold, color_fn) pairs where thresholds are cumulative probabilities summing to 1. The first entry drives the "stay near current color" behaviour; subsequent entries are discrete jumps to new color families. Example:

cfs3 = [(0.80, rred), (0.82, ablack), (0.87, rgreen), (0.95, rblue), (1.0, awhite)]

Preset named colors

chinese_red     = HSLA(11,  0.70, 0.39, 1.0)
cadmium_orange  = HSLA(28,  0.84, 0.55, 1.0)
naples          = HSLA(48,  0.94, 0.67, 1.0)
DigitalArt.SymmetryColors.color_from_colorMethod
color_from_color(c; Δ=[0.1, 0.1, 0.1, 0.1]) -> HSLA

Return a new HSLA color near c by adding uniform random perturbations in [-Δ/2, Δ/2] to each of hue (H), saturation (S), lightness (L), and alpha (A). S, L, and A are clamped to [0, 1].

DigitalArt.SymmetryColors.mycolorMethod
mycolor(cfs; pc=nothing, cont=false) -> (HSLA, Bool)

Probabilistic color selector with continuity tracking.

The state machine has two modes:

  • Continuous (cont=true): with probability cfs[1][1], slightly vary the previous color pc via color_from_color. Returns (new_color, true).
  • Jump (cont=false or random draw exceeds first threshold): select a new color family from the remaining entries in cfs. Returns (new_color, false).

Arguments:

  • cfs: vector of (threshold, color_fn) pairs; thresholds are cumulative probabilities in (0, 1] summing to 1.0. The first entry drives continuation.
  • pc: previous color (used by color_from_color when continuing).
  • cont: whether we are currently in a continuous color run.

Returns a tuple (color, new_cont) where new_cont indicates whether the returned color begins or continues a run.