Every tile carries the same rings; only the one line that crosses them is decided by chance.
What is fixed and what is not
A Truchet tile is a motif that is not rotationally symmetric, laid down at random
orientation so the motifs join across edges. This one has two parts, and only one of
them is a Truchet tile:
for (int i = 1; i <= rings; i++)
canvas.DrawCircle(cx, cy, radius * i / rings, paint);
switch (variant)
{
case 0: canvas.DrawLine(cx - radius, cy, cx + radius, cy, paint); break;
case 1: canvas.DrawLine(cx, cy - radius, cx, cy + radius, paint); break;
case 2: canvas.DrawLine(cx - radius, cy - radius, cx + radius, cy + radius, paint); break;
case 3: canvas.DrawLine(cx - radius, cy + radius, cx + radius, cy - radius, paint); break;
}
The rings are identical in every tile of a given grid. The single line is one of
four — horizontal, vertical, or either diagonal — drawn from random.Next(4). That
one integer per tile is the entire random content of the piece.
Everything else the variants do is deliberate: how many rings, how they are inked,
how the rows sit, how long and heavy the lines are, what colors the two parts carry.
None of it is random. The table in Seven treatments lists the
moves each variant makes.
One ring per tile, at a third of the tile radius. The grid is regular and will stay
regular; this part of the drawing never varies.
All three rings. The outermost is exactly the inscribed circle of its tile, so
neighbouring rings are tangent and the grid reads as a lattice of touching discs.
In the default treatment the rings are inked lighter than the lines that will cross
them, so the two parts of the tile stay readable once the drawing is complete.
Four orientations, no rotations
The four cases are not one motif rotated. Look at what they cross: the horizontal
and vertical lines run edge to edge through the middle of a side; the diagonals run
corner to corner and are √2 times as long, but the code draws them to the same
radius in each axis, so they extend past the outer ring by a factor of √2 and
poke into the corners of the tile.
The consequence at the grid level is that horizontal and vertical lines meet their
neighbours’ lines exactly at the shared edge midpoint, and diagonals meet at the
shared corner. Two different joining behaviours from the same four-way draw, and
both are visible once the whole grid is filled.
One variant, Drizzle, skips the switch: it draws every line at a random angle
instead, so the lines stop joining across tiles and the piece reads as loose strokes
falling over the grid rather than a network.
The first third of the tiles have their lines, in the row-major order the grid is
walked. This is the drawing order, not a spatial selection — which is why the
boundary between decided and undecided tiles is a horizontal edge.
Two thirds. The choices are independent per tile: there is no rule that makes a
horizontal line likelier next to another horizontal one, and any run you find is
coincidence.
Every tile. Where two neighbours happen to choose lines that meet at their shared
edge, the eye reads a single longer line crossing two tiles. Nothing joined them;
they were drawn separately, in different iterations, and only their endpoints agree.
The grid size is derived
There is no grid-count parameter. The count falls out of the tile size:
int gridSize = Width / TileSize;
Integer division, so a tile size that does not divide the width leaves a strip of
bare background. None of the square-grid variants do this: their tile sizes of 32,
80, 100 and 200 all divide 800 exactly, into grids of 25×25, 10×10, 8×8 and 4×4.
Honeycomb is the exception, and it has two margins worth looking at. Its rows are
spaced at the hex height — 87 pixels, 100 × √3 / 2 rounded — so the discs in
neighbouring rows touch the same way they do inside a row. Nine such rows carry
discs across 796 of the 800 pixels and leave a four-pixel strip at the bottom; the
odd rows are inset by half a tile on both sides, which is what the packing
requires. Those are honest remainders, the same kind the square grids avoid by
sizing their tiles to divide the canvas.
Seven treatments
The seven renderings are one process under seven configurations. Each moves more
than its colors; no two differ by palette alone.
| Rendering | What it moves | Tile | Grid |
|---|
| Sumi (default) | paper, two inks: light rings, heavy lines | 100 | 8×8 |
| Eclipse | rings filled solid, lines cut in the paper color | 100 | 8×8 |
| Honeycomb | rows staggered and packed hexagonally | 100 | 9 rows of 8, odd rows 7 |
| Drizzle | two rings, lines at any angle, overhanging their tile | 80 | 10×10 |
| Weave | the smallest tiles, two thread colors alternating | 32 | 25×25 |
| Phosphor | green on near-black, dense | 80 | 10×10 |
| Monolith | the largest tiles, the heaviest strokes | 200 | 4×4 |
Each has its own seed, so the tile choices differ entirely — these are seven
independent draws of the same process, not one arrangement in seven colors.
Sumi is the default because it shows the split the piece is built on: the rings are
one fixed substrate, drawn in one ink, and the lines are the random content, drawn
in another. Eclipse inverts the same tile. The other five stretch the geometry —
the lattice, the direction, the scale, the density — which is the range the piece
has.
Museum presentation
The gallery’s final plate is the piece framed: Sumi, matted in paper and hung
in an ink-dark frame on a warm gallery wall. The frame is a presentation
layer, not a treatment — it wraps a finished artwork, scales it into a mat
opening, and hangs the stack on the wall, so the wall becomes the deliberate
surrounding space. It sees the artwork only through ISkiaArt, which means it
can hang any of the treatments without touching their drawing code. There is
one framed plate, not seven, because the presentation is a mode of the piece
rather than an eighth rendering of the process.
Determinism
One Random is created per draw, seeded from the preset, and consumed once per tile
in row-major order; variants with continuous angles consume one angle per tile in
place of the four-way pick. The rings consume nothing. The generator is
System.Random, which the project’s determinism policy flags as not guaranteed
stable across .NET versions.