Hexagonal Truchet tiles laid row by row, with every mark a clipped hatch line.
Truchet on six edges
A Truchet tile carries a motif that is not rotationally symmetric. Lay many down at
random orientations and the motifs join across the edges into paths no single tile
contains. Sébastien Truchet described this in 1704 for squares split along a
diagonal.
On hexagons the arithmetic changes: six edges rather than four, joins meeting at three
directions rather than two, and the result reads as woven ribbon instead of as maze.
The grid is a plain offset lattice, and every other row is pushed half a cell across:
float hx = gx * 3f + (gy % 2 == 0 ? 1.5f : 0f);
float hy = gy * H0;
where H0 is √3/2 — the half-height of a unit hexagon, and the only irrational
number the layout needs.
A sixth of the walk. Tiles arrive in the order the index runs, which is row by row,
and each one chooses its motif at the moment it is reached.
A third. Where two neighbours’ arcs meet at a shared edge, the eye reads one
continuous ribbon crossing both. Nothing joined them.
Every mark is a hatch line
There is no fill in this piece. A “filled” region is a set of parallel lines, clipped
to the shape, and the shapes are clipped against each other in the order they are
added — so a motif drawn later cuts the hatching of one drawn earlier.
That is hidden-line removal, which is what a pen plotter needs and a rasteriser does
not. The visible consequence is that overlaps read as cut and stacked paper rather
than as ink over ink, and tone comes only from line spacing.
Half.
Three quarters. The grid extends past the frame — s is computed as
100 / Scale + 10, ten cells more than the canvas needs — and a tile whose centre
falls outside the clip returns immediately:
if (MathF.Abs(Scale * x) > 100f + Scale || MathF.Abs(Scale * y) > 100f + Scale) return;
So the walk keeps visiting cells that will never be drawn, which is why the last
quarter of the progression adds less than the first quarter did.
The full grid.
Five presets
| Preset | Scale | Line width | Seed | Ink | Hatching |
|---|
| Cyan Weave | 4 | 0.35 | 173802 | cyan on near-black | — |
| Gold Plate | 9 | 0.46 | 173804 | gold on warm near-black | — |
| Orange Mesh | 6 | 0.30 | 173803 | orange on near-black | ✓ |
| Ruby Bold | 7 | 0.44 | 173806 | crimson on near-black | — |
| White Lace | 3 | 0.22 | 173805 | ivory on near-black | ✓ |
Scale sets the cell size and therefore how many cells fit the frame; LineWidth
sets how wide a ribbon is relative to its cell, which decides whether the weave reads
as line or as mass. Each preset has its own seed, so the five are independent
draws rather than one arrangement recoloured. Presets with LineWidth below 0.35
fill each cell’s empty area with diagonal hatching lines.
Museum frame
The Framed gallery plate presents Cyan Weave the way a gallery would — on a
wall, behind a mat, inside a frame. The treatment is MuseumFrame from
SkiaSharp.Extensions, which wraps any piece without knowing anything about
it: the artwork is contain-fitted into the opening and clipped at the mat
edge, and the wall, mat, frame, rule and drop shadow are drawn around it in
proportion to the plate.
The plate keeps the piece’s warm off-white mat (#F4F0EA) and the 1000 × 1000
canvas the framed sample always used. No tile parameters change; the same
seed, scale, and line width produce the same weave — the museum presentation
simply gives it room.
Determinism
One Random is created per draw, seeded from the preset, and consumed per cell in
walk order — including for cells that are then clipped away, which is what keeps the
sequence stable regardless of where the frame falls. The generator is
System.Random, which the project’s determinism policy flags as not guaranteed stable
across .NET versions.