A square lattice and a triangular one, overlaid, with every point joined to whatever is closest.
Two lattices, one rule
Two point lattices are generated over the same region: one square, one triangular
(a square lattice with its first basis vector rotated thirty degrees).
(new Vector2(SquareSize, 0), new Vector2(0, SquareSize), …),
(Vec2.Rotate(new Vector2(TriangleSize, 0), MathF.PI / 6f), new Vector2(0, TriangleSize), …)
They are then thrown into one pile, and the drawing rule is applied without regard to
which lattice a point came from: join every point to whatever is nearest it.
A sixth of the points — which, because the two lattices are generated one after the
other, is the square lattice on its own. Every point finds its four orthogonal
neighbours at exactly the same distance, and the drawing is a grid.
A third. Still one lattice, still regular.
Where the moiré comes from
Half the points, and the triangular lattice has begun to arrive. Now the rule has a
choice to make at every point, and the answer varies from place to place — not because
the rule varies, but because the two lattices drift in and out of phase with each other
across the field.
Where they nearly coincide, a point’s nearest neighbour is its counterpart from the
other lattice and the join is very short. Where they are half a cell out of phase, the
nearest neighbour is a point from the point’s own lattice and the join is long. In
between there is a band where the two are almost equidistant, and the connection
pattern changes abruptly.
Those bands are the rosettes. Nothing draws them; they are the loci where the
nearest-neighbour relation switches, and their spacing is the beat frequency between
the two lattice pitches.
Three quarters.
Nearest, or nearly nearest
The rule is not strictly “the single closest point”. It is a threshold:
float threshold = candidates[pos].distSq * DistanceThreshold;
limit = 0;
while (limit < candidates.Count && candidates[limit].distSq <= threshold) limit++;
Sort the candidates by distance, take the one at DistancePosition, multiply its
distance by DistanceThreshold, and connect to everything inside that.
At a threshold of 1.2 a point connects to everything within twenty per cent of its
nearest neighbour’s distance. That tolerance is what makes the drawing dense enough to
read: strict nearest-neighbour would give one edge per point and a sparse graph, while
the tolerance produces four edges where four neighbours tie and one where they do not —
so the number of edges also varies with the phase, and reinforces the rosettes.
The search itself is done through a spatial hash of cells twice the lattice pitch, so
only nine cells are examined per point rather than all forty thousand.
The full field.
Six renderings
| Rendering | Square | Triangle | Square rotation | Ground | Ink | Character |
|---|
| Cyan Moiré | 13 | 10 | 0° | black | cyan | Reference — four rings across |
| Amber Rings | 12 | 11 | 0° | near-black | amber | Close pitches; one wide ring |
| Diagonal Ice | 13 | 10 | 15° | near-black | ice blue | Tilted square; elongated rosettes |
| Silver Weave | 8 | 6 | 0° | near-black | silver | Fine scale; eight tight rings |
| Cream Field | 16 | 13 | 0° | cream | near-black | Inverted ground; sparse, architectural |
| Gallery Cyan | 13 | 10 | 0° | gallery wall | cyan | Lead work in a walnut frame and warm mat |
Lattice pitch sets the ring scale: closer pitches mean a longer beat wavelength and fewer,
wider rings. Rotating the square lattice tilts the whole interference pattern without
changing the rule. Small pitches pack the canvas with many fine rings. Inverting the
ground from near-black to cream makes the same nearest-neighbour rule read as dense ink
on paper rather than luminous lines in darkness.
Gallery Cyan is the single presentation mode: it places the unchanged lead rendering
inside a warm mat and dark frame, with enough surrounding wall to read as an object rather
than another pattern variant.
Determinism
Nothing is seeded and nothing is random. Both lattices are generated by integer
combinations of their basis vectors, and the nearest-neighbour search is exact
arithmetic over a deterministic spatial hash.