The piece the kit had been assembled around without having. The basket, the
vegetables, the sack, the crate and the coin pouch all belong on one, and a
market with none of it is a row of props on a floor. It is also the only thing
here with a roof.
The awning SAGS, and that is the whole model. A flat sheet on four posts is a
table with a lid; cloth slung between four points hangs, and the dip is what
says it is cloth before the colour does. It costs nothing:
`dishedSheetGeometry` bends its cross section by `curve`, and one turn about X
maps that bend onto the world's vertical, so the sag is the helper's own
parameter rather than anything hand-built. Per level, because the reference's
front edge dips further than its back — cloth pulled over a ridge and left
loose at the eaves does not hang symmetrically.
Three faults, each of them a thing I had drawn correctly and built wrong:
- The posts were one height, and the cloth is not one height. It is a
parabola across its length and deeper at the front than the back, so a
single post reached some corners and not others: the awning came away as
its own floating piece 67 mm above the post heads, held up in the drawing
and by nothing in the geometry. Each post is now sized by the sheet's OWN
arithmetic — same parabola, same per-level sag, same quarter turn, same
tilt — rather than by a second version of it that can drift.
- The legs ran up to the rail's top face and ended level with it. Two upward
faces in the plane y = 0.745, at all four corners. A leg is housed in a
rail, not flush with it.
- The posts stopped at the rail's underside, which put their feet in ITS
plane — the same fault one storey down. They now run past it and finish as
a stub below, which is the reference's detail as well as the fix.
And one thing the render corrected rather than the checks: the posts were
inboard, so the counter's boards had posts driven through them a hand's
breadth from each end. That is not how a stall is built and not what the
reference shows — the posts stand at the ends and the top lies between them.
The sag came down from 0.17 of the depth to 0.10 for the same reason: at 0.17
the cloth dropped a tenth of its own span and read as a hammock.
38 models, 32 700 triangles, all checks pass, 0 floating cases, GLB round-trip
clean, Conformant, documentation in step — including the showcase arithmetic,
which every new model moves.
The kit had an anvil, a forge and four edged tools, and nothing that put an
edge on any of them. This is the machine that did, and the only piece of
village equipment here with a working part in it — so it turns: crank it and it
runs for three revolutions over five and a half seconds, which is a heavy stone
on a wooden bearing rather than a flywheel.
The wheel is `limestone` and that was measured, not assumed: the reference
reads hue 32.5, saturation 0.196, lightness 0.511 against the trough's 36.6,
0.161, 0.500. A sandstone wheel and a weathered trough are the same rock as far
as this palette is concerned, and neither is the grey `stone` of a dressed
wall. The frame is `oak` taken well down in value — the reference's timber is
0.25 against the palette's 0.40 — but it stays oak, because a third wood key
for one model would be describing the weather rather than the material.
Three faults, all found by the checks and all of them things I had reasoned my
way past:
- The axle and its washers were built, merged into a local array, and then
never returned. The wheel hung on nothing and the crank floated beside the
machine at 0.60 m. An array you forget to use is not a type error.
- The axle sat 48 mm ABOVE the rails and touched them nowhere. The reference
answers this itself and I had left the answer out: there are bearing
blocks bolted on top of each rail with the axle running through them.
- The low stretcher ran down the centre line from end to end and touched
nothing on the way, because at that height the legs have splayed out to
z = 0.21 and it was at z = 0. It tied the trestles together in the drawing
and not in the geometry. There are now two rails, one down each side, and
where the legs are at that height is worked out from the strut's own two
ends so it stays true when the splay is changed.
The last two shared a symptom — the support check reporting two components,
one on the ground and one containing everything above it — and finding them
took a detour worth writing down. Sampling the frame's VERTICES to see where
the legs were at rail height found no legs at all, because a tapered box has
corners only at its ends. That is the third time in this project the same
mistake has been made with the same consequence: a measurement that returns a
confident number about nothing. Slicing the triangles instead put the leg's
cross section exactly where the arithmetic said it was, which is what proved
the gap was somewhere else.
What is NOT here, said plainly: the reference has a treadle and a linkage down
to a foot board. This has a hand crank instead. Both are period; the treadle is
three more moving parts to serve an animation the crank already carries.
37 models, 32 316 triangles, all checks pass, 0 floating cases at 224 voxels as
well as at 112, GLB round-trip clean, Conformant, documentation in step.
The kit could water nobody. It had a well, a bucket and a cauldron, and all
three are things you carry water IN; a trough is the thing a village keeps
water in, standing in the street outside the smithy.
Two additions to the core, both earned by measurement rather than by wanting
them:
- `limestone`, a palette key. `stone` was measured off dressed and rubble
masonry and is grey, because a wall is quarried and laid face out. One
block left in the open for a century reads hue 36 and saturation 0.16
against the wall's 44 and 0.05 — warmer, three times more saturated,
lighter. Both are right and neither replaces the other. The rendered block
now matches its reference to within a fifth of a degree of hue and 0.004
of lightness.
- `water`, a slot, and it is far less green than instinct says. Sampled
inside the reference's basin the film reads hue 39.5 — which is the hue of
the stone UNDERNEATH it, not a colour of its own. It darkens and slightly
saturates whatever it lies on and does that through the material's
opacity. Painted the pond green I was about to reach for, the composite
would have missed the reference by a hundred degrees.
Making it read as CARVED rather than sawn took three goes, and the two failures
are opposite errors worth keeping:
1. Each wall in two tiers, to give `roughenGeometry` more corners to move.
The tiers' corners sit at different positions, the hash moved them by
different amounts, and the render showed a lid on a base with a hard seam
right round it.
2. One box per wall, roughened harder to compensate. A box has eight corners
and every face is two triangles, so moving those corners folded each wall
along its own diagonal. Not stone: creased cardboard.
Both were trying to get subdivision out of the wrong thing. The walls are
`dishedSheetGeometry`, whose own docstring says the trough wants it: one
continuous body with levels along its length, so the rim undulates because
each level carries its own height, the wall thickens and thins because each
carries its own thickness, and there is no join for a seam to open in.
The z-fight checker earned its keep twice on the same plane, `0,-1,0 | 0`.
Four walls standing on the ground all have a face down there, and where the
corners overlap — which is where they are MEANT to overlap — two of those
faces share it. Lifting the basin floor only moved the pairing from
floor-against-wall to wall-against-wall. The answer was to stop there being
four things down there at all: a trough is solid below the water line, so it
is one block from the ground to the basin with the walls sunk into it, by
different amounts, because two buried faces in one plane are still two faces
in one plane.
Last, two defaults that the render corrected rather than the reference. The
walls at 0.27 of the width shut the basin so far that a three-quarter view
showed only its inner face, against the reference's opening of about three
fifths — now 0.21. And the water at 0.55 of the basin's depth sits below the
near rim and the trough reads as dry from every angle anyone will look at it
from; the reference stands a couple of centimetres under the rim, which is
0.85.
36 models, 31 748 triangles, fourteen material slots, all checks pass, 0
floating cases, GLB round-trip clean, Conformant, documentation in step.
Reported twice. The first time I planed the through-tenons from 42 mm proud to
3 and called it done, which answered the tenons rather than the question — the
tenons were never it. Measured again now: nothing stands more than 3.9 mm above
the seat at any slider setting, and the thing that was catching was on the
LEGS.
The leg board widens on the way down; that is the splay. The two feet, cut out
of its bottom edge by the arch, were built at `footWidth` — the board's width
at the FLOOR. But a foot starts at the top of the arch, a third of the way up,
where the board has not widened that far yet. So every foot stood proud of the
board it hangs from, on both sides, as a hard shoulder at shin height: 14 mm by
default, 20 mm on a wide bench, 32 mm at full splay. Setting the splay to zero
made it vanish, which is what finally named the cause.
The leg's outer edge is a straight line from `footWidth / 2` at the floor to
`legWidth / 2` under the seat, and a foot's outer face has to lie ON that line
rather than run vertically up from its own base. A tapered box narrows about
its centre, so taking twice the taper off the top width moves the outer face in
by exactly the taper — and moves the inner face out by the same, narrowing the
arch as it rises, which is also what a splayed leg does. 14.2 mm becomes 0.2,
32.3 becomes 0.4, and 0.2 mm is the sampling resolution rather than a step.
`scripts/probe-bench.ts` is how it was found and it took two wrong versions to
get there, both failing the same way — by measuring something and reporting a
number that was not the answer:
- The first sampled vertices into height bands. A tapered box has corners
only at its top and bottom, so the bands between them came out empty, were
skipped, and it reported a confident 0.0 mm step for a leg with a 17 mm
shoulder on it. It now slices the actual triangles, which gives a width at
any height whether a corner is there or not.
- The second scanned the full height of the leg part and found its biggest
drop at 86% — exactly where the board ends and only the tenon carries on
into the seat. That is what a tenon IS. A real fault of 14 mm was sitting
underneath a 66 mm reading that was not a fault at all, so the scan now
stops at the seat's underside.
Both media images regenerated, since the bench is in each of them.
The image both READMEs open with was a contact sheet whose FIRST cell — top
left, the one everybody's eye lands on — was the `kit` entry: all thirty-five
models arranged in one scene, squeezed into a 300 px cell, an illegible smear.
It is not a model and it does not belong in a grid of models. Rendered on its
own it is by far the better picture anyway, so it is now the picture, and the
contact sheet is a second image beneath it.
Rendering it on its own is also what showed the arrangement had quietly gone
wrong. The row width was a fixed 5.6 m, which was right for a kit of tankards
and barrels and became wrong the moment anything bigger than a row went in: the
oak is 8.6 m across and the mill 6.6, so each took a row to itself while the
small props packed four and five deep. The kit had stretched into a long thin
diagonal, and since a camera fits a bounding SPHERE, the sphere around a
diagonal line is enormous next to the things inside it — the kit occupied about
a quarter of its own picture and the rest was empty floor.
So the row width is measured from the models now rather than fixed: the square
root of the total footprint, which packs rows about as deep as they are wide
whatever the kit happens to contain, floored at the widest single model since
no row can be narrower than the thing standing in it. Swept for the factor:
1.15 gave 15.1 x 21.7 m, 1.45 gave 19.2 x 13.3 — the square is between them,
and 1.24 lands at 16.4 x 14.8, aspect 1.11, filling the framed disc better than
any of them.
It reads as a village yard now: mill and oak at the back, forge and cart in the
middle, the small props at the front. Which is what the entry is FOR — every
model on one ground plane at one scale is the only place a difference in scale
or tone has nowhere to hide.
The first thing in this kit that grew rather than got made, and the first one
built entirely against reference photographs from the start rather than after
three rounds of guessing.
Two references, generated with the same Codex tool the rest of the kit now
uses: one oak in full leaf and the same tree bare. Both of them contradicted
what I was about to build, and the default lowpoly tree -- a tall pole with a
ball on it -- turns out to be wrong in every measurement that matters.
crown width / tree height 1.32 to 1.43. An open-grown oak is half again
as WIDE as it is tall. Nothing else in this kit
is wider than it is tall.
widest at 24% of the height. Not halfway up: a quarter,
just above the ground, because the lowest limbs
are the longest and leave almost horizontally.
bole runs to 72% of the height as one continuous stem. It
does not fork low into equal leaders, which is
what the leafy photograph alone suggested and
the winter one disproved.
bole width / height 0.130 at 2% of the height, 0.073 at 10%. It
nearly halves in the bottom tenth and then
barely tapers at all for a quarter of the tree.
The taper table in the model is those rows, read off the photograph one at a
time. The limbs SWEEP -- out at a shallow angle, turning up at the end -- which
comes free from bending each limb along its own axis before rotating it into
place, and which cost a correction I had not expected: bending a limb swings
its whole chord up by half the arc, so every limb was arriving 0.21 rad high
and the crown's widest point had climbed to 38%. Aiming low by exactly what the
bend will lift is the fix, and it is a constant.
Measuring is the whole story of this one. `scripts/probe-tree.ts` prints the
four reference numbers off the built geometry the same way they came off the
photographs, plus one a photograph cannot give: how many separate pieces the
crown is in. A crown is one piece. This one was four, then one, then two, then
one again, and the flood fill was right every time my eye was vague.
Four things it caught that looking would not have:
- Forks throwing DOWNWARD. The rise of a fork is sin(splay) * sin(spin), so
the sign of the PRODUCT decides it. I constrained the spin and alternated
the splay, which left every second fork pointing at the floor. The
measurement said so plainly while the reasoning said the opposite.
- The crown torn in two, and not where I kept looking. Locating the loose
piece -- height 10-64%, bearing 228-313, reaching 5.7 m where the main mass
stopped at 4.5 -- ended four rounds of widening clumps and evening out
bearings, none of which were the gap. The gap was the MIDDLE: pushing the
leaves outboard to open the space under the crown had taken the inside out
with it, and nine lobes hung off a bare hub. The reference's crown is full
from the bole outward; the clear space is underneath.
- Occlusion, exonerated. Turning it off entirely changed the clipping by
nothing at all, which took two minutes and saved a redesign.
- My own metric, wrong twice. It counted every dark pixel in the crown's box
and 87% of what it found was the cast shadow, so it read 10.8% clipping on
a crown that was barely clipping. And it measured the reference by row
width but our own geometry by twice its radius, which is a different
quantity on anything that is not symmetric.
Colour is measured too, and the measurement contradicted me again: I was about
to bake a top-to-bottom gradient into the crown, and sampled in three bands the
reference reads 0.362, 0.336, 0.324 -- no gradient at all. What is there is
local, a spread of 0.45 in lightness within one band. Foliage varies leaf to
leaf, not top to bottom. Chasing that 0.45 with tint, mottle and occlusion all
at once put 2.2% of the crown through the floor into pure black against the
reference's 0.19%, which is the straw trap for the third time: a statistic taken
off fine detail cannot be added on top of the machinery that already
reproduces it.
The bark is measured off the WINTER photograph after the summer one gave a
colour 28 degrees too green -- a trunk under a full crown is lit through the
leaves. Low saturation made it worse than the hue did: a nearly neutral colour
takes its hue from its light, and this renderer's blue-grey sky dragged the
rendered bole to hue 119 on a tree whose bark is brown.
Finished: hue 79.6 against 77.9, saturation 0.386 against 0.381, lightness
0.386 against 0.373, 0.00% clipped. Crown 1.325 wide for its height, in one
piece, hanging from 8% where the reference hangs from 11%.
The honest limits. What fills a real winter silhouette is thousands of twigs
and no budget here reaches them, so `leafiness: 0` is a bare tree of LIMBS --
a good dead or winter tree, not a photograph of one. And the crown still
carries more of its width high than the reference does: widest at 50% against
24%, on a reference profile that is nearly flat from 24 to 44 so the peak is
softer than the number sounds.
2310 triangles, inside the kit's 2500 budget, which cost a side off every clump
and a level off every fork -- but not one clump, because the count is what
holds the crown together. 35 models, 31 404 triangles, thirteen material slots,
all checks pass, 0 floating cases, GLB round-trip clean, Conformant.
Opening the viewer found in ten seconds what the contact sheet had hidden
through two rounds of refinement: the bellows was cantilevered off a single
post driven through its middle, with four fifths of a door-sized leather bag
hanging in open air. The 300px thumbnail is too small to see what is under a
thing. A viewport you can orbit is not.
The support check passed it, and was right to by its own lights. The post
reaches the floor and the nose touches the hearth wall, so the assembly IS
connected to the ground. Connected is not the same as looking like it could
hold itself up, and looking like it can is the entire rule. The check
answers a question about topology; the rule is about weight.
So the bellows gets the bench it always had in life: two bearers let into the
forge wall at one end -- let INTO, a third of the way through, because a
bearer that stops at the wall face is a bearer resting on nothing -- carried
on legs at the other, with a rail across their tail. Six plain boxes, no
rotations at all, which after three two-rotation mistakes in this one model
was the point. The post moves out beyond the tail where it stops spearing
the bag.
Two more things came out of the same look.
The lever ran the wrong way -- from the post out into empty air, away from
everything. The end a smith pulls is the end he can reach standing at the
fire, so it now falls back over the bellows towards the hearth. And it had
nothing to pull: a rocker with no connection to the top board is a stick
resting on a post. There is now an iron link rod through the board, which is
also the only thing tying the two halves of the assembly together to the eye.
And the leather. The last round widened it so it would stop hiding between
the boards, but width and length were the same parameter, so it grew a third
longer in the same breath and the bag ended up running well past the boards
it is nailed to. Leather cannot outrun its boards -- they are what it is
fastened to. The two scales are now separate and the hide is wider (1.18 to
1.33) and shorter (0.82 to 0.9), which is the real thing and also spreads
the five end caps further apart than the old scheme managed, since they are
now staggered on purpose rather than as a by-product of the width.
+72 triangles, six boxes. 34 models, 29 094 triangles, all checks pass,
0 floating cases, GLB round-trip clean, Conformant, build clean.
Both are now at the kit's standard, so the catalogue table and the contact
sheet go with them this time.
Five things, each named from looking at a render beside its reference.
The fire bed was a smooth disc of the darkest colour in the palette, which
reads as a hole cut in the hearth with a few sparks in it. A fire bed is
BROKEN -- irregular lumps of charcoal with the glow coming up between them --
and vertex colour cannot fake that, so the lumps are modelled. They are
five-sided prisms at eight triangles each, and they are the difference
between a forge and a stone box with a black lid. Which ones glow is
decided by where they sit: the middle of a working fire is where the air
arrives.
The bellows' leather was cut NARROWER than the boards it lies between, so
it hid in the gap and the whole thing read as a stack of loose planks with
daylight through it. A bellows is a bag under pressure and its widest point
is the leather, not the wood.
The hearth's walls are slabs -- that is the saving that pays for the kerb
being real blocks -- and a slab of one colour stays a slab however much it
is mottled. Three string courses to a face, twelve boxes in all, say
COURSED without laying two hundred stones, and they stand proud because a
rib level with its wall is invisible.
The shield's paint was pastel at 0.42 saturation against a reference whose
red and blue are pigment ground in oil. And its planks are rectangles cut
to the chord of a circle, so their ends step in and out around the edge --
unavoidable, since a plank is straight and the outline is not. That is
exactly what a rim is for: a wide band of hide folded over the ends. At
0.055 of the radius it was too narrow to cover them and the silhouette came
out serrated.
Last, the cream cross between the quarters. Without it the quarters meet
edge to edge and the eye sees two colours rather than a device.
34 models, 29 022 triangles, all checks pass, 0 floating cases, GLB
round-trip clean, Conformant, build clean.
Eight kinds mixed by two sliders: how many of them appear, and which one
leads. Turnip, cabbage, onion, leek, parsnip, carrot, tomato, potato.
The first six are what a medieval European kitchen had and the last two are
New World crops that only reach Europe after 1492 -- they are in because a
kit is more useful than it is a museum, and the carrot is orange rather
than the purple a medieval one would have been. The kinds are ordered so
that the period-correct set costs no thought: `lead: 0` with `kinds: 6`
gives exactly those six and never reaches the two after them.
Two parts, `bulbs` and `roots`, because those are the two ways a vegetable
rests -- on its base or on its side -- and a consumer wanting one should not
have to take the other.
Four faults on the way in, and every one of them was about placement rather
than shape.
The header claimed each vegetable is authored with its lowest point at the
origin, and I asserted that instead of measuring it: a leek's blades lean
four times its own radius away from the axis, and a cabbage's loose leaves
hang below its base. Whichever vegetable dug in deepest then defined the
model's floor and made every other one look as though it were hovering.
Reading the bounding box and setting each one down by what it actually
measures makes the claim true by construction rather than by an argument
that has to be right about eight shapes.
The leaves were flung off their vegetables outright. They were built
already in place and then rotated, and rotation turns a body about the
ORIGIN, so each one moved away by its own distance times the sine of the
angle. Origin, rotate, place -- which is the order the hoe's neck already
has a comment about.
Tightening the heap so it read as a pile rather than a display then made
the bulbs collide, twice. Standing vegetables were never turned about their
own axis, so every turnip presented its facets at the same phase and
neighbours met in parallel faces. And where the turnip's cream body meets
its purple shoulder -- two lathes, because `colourTop` interpolates across a
whole profile and bled the purple down to the root -- both pieces were
capping an end that is buried inside the vegetable. Two turnips touching
met on those hidden discs. They are not generated any more, which is the
same saving `bandGeometry` makes on the inside of a hoop.
32 models, 26 939 triangles, all checks pass, 0 floating cases, GLB
round-trip clean, Conformant, build clean.
Two models that each fill a hole the catalogue could name.
The kit had a torch and a lantern and no HEARTH, which is the wrong way
round: a cooking fire is what everyone in a medieval scene is facing and
the only light in most of them. The cauldron hangs from a tripod over a
ring of hearth stones, with embers that `setLit` puts out. Its belly is
wider than its mouth, it has three short feet because it stands in the
ashes as often as it hangs over them, and it hangs by a bail rather than by
its rim.
And there was a `cart-wheel` with nothing to put it on -- while every
container in the catalogue, crates and sacks and barrels and baskets, is a
thing you would MOVE. The handcart's axle sits aft of the bed's centre so a
load leans on the puller rather than away from him, and its shafts leave
the bed at an angle and rest on the ground, which is what lets one person
lift them to waist height without the load shifting.
The cauldron found a fault in the mill on its way in. Both build their
splayed legs the same way -- lean about X, swing about Y, translate out
along the radius -- and the lean was POSITIVE, which carries the top the
same way the translate does. The two add, so the head ends up at the full
outboard radius and the foot at the axis: the tripod stood on its apex and
the mill's quarter-bars stood on their heads, splayed over the cross-trees
and gathered at the post, which is precisely backwards for a brace whose
job is to carry a load out to the ground. Nothing had caught it in the
mill, because a bar joins post to cross-tree either way round and the
bounding box is identical; in the cauldron it separated the tripod from the
ground and the support check finally had something to say.
Lowering the pot to where it could actually cook then broke its chain, and
for a reason this session has seen before: the link radius was derived by
dividing the drop by the link COUNT while the links are laid out over
count-1 gaps, so each link came out a little smaller than half its own gap
and the chain came apart as soon as the drop grew. The tavern sign's chain
had the same slip.
31 models, 25 221 triangles, all checks pass, 0 floating cases, GLB
round-trip clean, Conformant, build clean.
A village well: a drystone kerb, a timber frame over it, a windlass with
its collars and crank, and a rope and bucket that lower down the shaft.
`setDepth` and `setWinding` drive the bucket; its part origin is the
roller's axis, so winding is a translation and costs nothing per frame.
The kit had no masonry at all, which for a medieval catalogue is a hole
rather than an omission -- a well, a trough, a millstone, a boundary wall
and a hearth are all stone before they are anything else. `stone` is the
twelfth slot. Its colour sits close to weathered oak on purpose: what
separates the two at a glance is not hue but that stone scatters light
completely flat, and roughness is not something vertex colour can carry,
which is exactly the argument the slot table makes for keeping iron and
steel apart.
Four faults on the way in, and three of them are ones this session has seen
before in other models.
The blocks were written footprint-first as [wall, arc]. `rotateY` carries a
footprint's +Z out along the radius and its +X round the circumference, so
every block sat across the ring rather than along it -- each one sticking a
block's length out of a wall a block's width thick. The kerb came out as
rubble thrown at a circle.
The rope started at the roller's axis, which put its whole surface inside
the barrel's hollow interior touching nothing. Then it started in the eye
of the wound coil, which is also a hole. It has to begin within the coil's
material, between 1.0 and 1.34 of the roller radius, so that it crosses the
coil's outer face on the way down.
The actions set `anchor.position.y = 0`, which destroys what `origin` had
just done and dropped the whole rope and bucket by the height of the
roller. Everything the actions touch is now an offset from where the origin
put the anchor.
And the masonry was over the triangle budget at 2 784, almost 1 900 of
which were chamfers on forty-four blocks -- edges the roughen pass softens
anyway, and stone is the one material here where a sharp arris is not
wrong. Plain tapered boxes bring the model to 1 376.
Two things about the wall itself, both from looking at it. Varying each
block by a tenth in arc and thickness put every stone at its own radius and
its own width; a dry wall is irregular the way a hand-cut stone is
irregular, which is a few percent. And blocks cut to exactly their share of
the arc cannot close: a flat face on a curve leaves a wedge of daylight at
every joint, so they are cut long and bite into each other -- safe here
because each is rotated to its own angle and no two adjoining faces are
parallel.
29 models, 22 043 triangles, all checks pass, 0 floating cases, GLB
round-trip clean, Conformant, build clean.
The mill and the sign both changed shape after the table was last generated,
so it was claiming 20 523 triangles against 20 667 and drawing the sign the
wrong way round.
The largest thing in the kit, and the one whose name is its whole design:
the entire body -- millstones, gearing, miller and all -- is balanced on one
massive vertical post and turns bodily on it, so the sails can be pointed
into whatever wind there is. The miller does that by walking the tail
ladder round, which is why the ladder is not a way up but a lever, and why
it reaches so far behind the mill.
Four parts, in the order the thing is built: `trestle` (two cross-trees,
the post standing on them, four quarter-bars), `body` (the boarded buck and
its pitched roof), `sails` (a windshaft leaving the front gable, with its
own origin so `setTurning` spins it alone), and `ladder`. `setTurning`,
`setAngle` and `update` drive the sails at about ten revolutions a minute
-- a working mill turns slowly, not in the blur people draw.
The sails are where the triangles went, and deliberately. A sail is not a
blade, it is a LATTICE the miller dresses with canvas according to the
wind. Drawn solid the silhouette is wrong in the one place everyone looks,
so the bars are modelled and the budget is taken from the buck's boarding,
which reads perfectly well as vertex colour. 1 516 triangles.
Four faults on the way in, all found by measuring rather than by looking:
The roof pitches were built `slope * 2` long and tilted by the complement
of the roof angle, which made two panels 2.79 m across a building 1.80 m
wide, standing up like the covers of an open book. Each pitch covers half a
roof, and the tilt is measured from the horizontal because
`chamferedBoxGeometry` takes an X-Z footprint.
The rails of the tail ladder were tilted the wrong way round. `run` is
positive -- the head sits at a larger z than the foot -- so rotating by
`-lean` laid the rails along the opposite diagonal to the one the rungs
were placed on. They crossed near the middle and diverged towards the ends,
and the bounding box looked correct the whole time because the translation
put the ladder where it belonged and only its internals were wrong. The
support audit found it as loose rungs, and found it only at the small end
of the height slider, where the voxel grid is fine enough not to bridge the
gap: the joint was never real at any size.
Fixing the ladder's foot, which sat 0.10 m below the cross-trees so the
mill hovered above whatever it was placed on, then displaced the rails
perpendicular to their own axis by almost exactly a rung's half-depth --
correcting the rails alone was worse than not correcting at all. Rails and
rungs now share one lift.
And the lowest sail tip came within 0.27 m of the ground, which reads as a
mill about to plough its own field.
28 models, 20 523 triangles, all checks pass, 0 floating cases, GLB
round-trip clean, Conformant at 30 items, build clean.
The registry README's model table was generated once and had gone stale in
exactly the way its own note warns about: it claimed 15 425 triangles
against the current 19 007, listed a barrel at 806 triangles that now has
rivets and six hoops at 1 690, and described "the barrel's 13 staves" when
there are 17. Regenerated from the models themselves.
Both READMEs now open with a contact sheet of the whole kit. It lives in a
tracked `media/` directory rather than in `renders/`, which is ignored
because it is rebuilt output -- a README cannot reference a file that is not
in the repository. The registry's copy points at the raw GitHub URL, since
the npm package ships only dist, README and LICENSE and a relative path
would resolve to nothing there.
The root README gains a Showcase section -- the 60 s default, why it is the
default rather than 30 s, and what makes the variation visible -- and the
support check as a fifth note among the checks worth explaining. It is the
most distinctive thing in the suite and the only one that catches a whole
class of fault the others are blind to by construction.
Also: the check count in that section said ~500 and the suite now runs 818.