From plane
to pixel
A flat plane cuts through a block of voxels. Every pixel of that plane asks which cube is closest? and copies that cube's brightness. This page plays the answer out one sample at a time, on the five hand-checked examples in docs/algorithm.md.
The idea, in five steps
Think of the volume as a block of small cubes, each holding one brightness number. A plane from the CSV is a flat sheet placed inside — or running past — that block. The renderer turns the sheet into a frame of one pixel per sample. The whole job is the five steps below.
- 01Size the frame One row per voxel-length step down the legs, one column per step across the base.
- 02Slide the rows down Each row's ends sit on the two legs. No row is wider than the base, and the last row spans it exactly — triangles widen the whole way down; a rectangle keeps every row full width.
- 03Place the pixel centres Pixels sit one step apart, symmetric about the row's centre. The row stays centred as its width changes.
- 04Find the nearest cube Round the sample point to whole x, y, z indices. An exact half goes to the even index.
- 05Copy the value Map the cube's value over its storage type's full range to 0–255. A sample that misses the plane or the volume stays black.
Walk it through
The plane is drawn flat, as if unfolded. Rows run left → right; the legs run top → bottom. On the row being sampled: the dashed verticals are the walls between cubes along the axis the row travels, each placed dot carries the cube its sample lands on, and the pixel being placed shows its original (pre-rounding) value with the cube that value rounds to.
One cell per pixel, filled in reading order. The number is the pixel's gray level, 0–255.
The formulas, with plain names
These are the same formulas as docs/algorithm.md, using the names
the code in Ex0.py uses. Coordinates are voxel indices: no origin,
spacing, or world-space offset is applied.
leg_length(dist(top_left, bottom_left) + dist(top_right, bottom_right)) / 2the two legs agree within EPS = 0.001base_lengthdist(bottom_left, bottom_right)frame_heightmax(1, round(leg_length))one row per voxel-length unitframe_widthmax(1, round(base_length))one column per voxel-length unitrow_fraction(row_index + 1) / frame_heightone leg step taken before each rowleft_edge[row]top_left + row_fraction × (bottom_left − top_left)slides down the left legright_edge[row]top_right + row_fraction × (bottom_right − top_right)slides down the right legrow_centre(left_edge + right_edge) / 2row_widthdist(left_edge, right_edge)row_direction(right_edge − left_edge) / row_widtha one-unit step across the rowoffset_from_centrecolumn_index + 0.5 − frame_width / 2pixel centres, symmetric about the row centresample_pointrow_centre + offset_from_centre × row_directionvoxel_indexrint(sample_point)nearest cube; an exact half goes to the even indexinside_row|offset_from_centre| ≤ row_width / 2plus a tiny tolerance for floating-point errorinside_volume0 ≤ voxel_index < volume.shapea miss stays black; it is never clamped to the edgegray_levelinside ? round(255 × (value − type_min) / (type_max − type_min)) : 0the type's full range, never the frame's own brightest valueThe names used on this page
The CSV letters stay in error messages. Every computed quantity has a plain name instead — the same one the code uses.
| name | meaning | CSV letter(s) |
|---|---|---|
| top_left top_right | The plane's two top corners. For a triangle they are the same point, the apex. | a1, a2 (triangle: a) |
| bottom_left bottom_right | The two bottom corners. The base runs between them. | b, c |
leg_length |
Average of the two leg lengths, top corner to opposite bottom corner. | — |
base_length | Distance between the two bottom corners. | — |
frame_height, frame_width |
The frame's row and column counts, clamped to at least 1. | — |
row_index, row_fraction |
Which row, and how far down the legs it sits. | — |
left_edge, right_edge, row_centre, row_width, row_direction |
The row's two ends, its midpoint, its length, and a one-unit step across it. | — |
column_index, offset_from_centre |
Which pixel in the row, and its signed distance from the row centre. | — |
sample_point, voxel_index |
The pixel's 3D sample point, and that point rounded to whole cube indices. | — |
volume |
The 3D input array volume[x][y][z] — the brief's M. Each value is an integer of one fixed bit width (numpy's dtype). | M[x][y][z] |
From 16-bit values to gray levels
Gray levels only run 0 to 255, so every value is mapped over its storage
type's full span — not over the picture's own brightest value. An 8-, 16- or
32-bit unsigned type maps its whole range (0..255, 0..65535, or
0..4294967295) onto 0..255. A signed type maps its minimum..maximum, so
int16 takes −32768..32767 onto 0..255 and zero lands at
mid-gray.
A dark pixel really is dark in the video. Nothing is stretched to make the frame look brighter, and a sample that misses stays black.
The numbers come from the hand-checked tables in docs/algorithm.md
and the byte-identical goldens in tests/golden/. Every time a walk
finishes, the page recomputes the frame and compares it to the fixture.