Commit graph

6 commits

Author SHA1 Message Date
CRT
9b4ce16c59 Bring the anvil's horn back to its reference proportion, rebuild the book's metal furniture so no two overlapping faces share a plane, and release 0.2.0. 2026-09-01 03:00:37 +07:00
CRT
909bfa0f9f Land the mill's ladder, and stop the table calling six models static
Two things, both found by pulling on the handcart's thread.

THE LADDER. I said three models had parts floating a few millimetres. One did.
The other two readings were my own measurement error and worth writing down,
because it is the same error as the vertex-sampling ones: I measured each part
against the model's BOUNDING BOX minimum, and a part deliberately bedded into
the ground drags that datum below the floor, so every correctly placed part
beside it appears to float by exactly the bedding depth. The forge's base
stones settle 6.8 mm and the well's drystone kerb 9.3 mm, both on purpose, and
the bellows and the well frame sit exactly on the floor their own source
declares. Nothing wrong with either.

The post mill's tail ladder was real: 14.5 mm clear. It already carried two
analytic corrections for the same corner, both correct about what they
describe, and neither accounting for the chamfer — the corner they compute the
drop of has been cut off, so the residual was very nearly the chamfer itself.
Rather than add a third derivation to a spot that has now been got wrong three
times, the assembled ladder is measured and put on the floor. A bounding box
cannot be wrong about this the way arithmetic can, and it stays right when the
chamfer or the rail section changes.

`restsOn` now covers the mill's trestle and ladder too, and its doc comment
carries the bedding caveat, so the next person to reach for it knows the datum
is the bounding box and knows what that costs.

THE ANIMATED COLUMN. `catalog-table.ts` decided it with `built.action`, which
is the VIEWER's hand-registered button and exists for five models. So the
generated table published the grindstone, the stone well, the post mill, the
cauldron, the forge and the handcart as static — six of the eleven models that
carry actions, including three of the most animated things in the kit.

The obvious replacement is worse. `core/kit.ts` wraps update as
`update: (dt) => options.update?.(dt, runtime)`, so it is a function on every
model whether or not that model does anything with it, and switching to it
marked all thirty-seven animated. One flag too narrow, its replacement too
broad.

So the generator now runs each model — a second of frames, then its action if
it has one — and asks whether anything moved. That catches what moves by
itself. What moves ON DEMAND it cannot catch, because the motion is behind an
interface the catalogue never exposed, so `Entry` now carries `actionNames`
from the model's own actions. Eleven models, and both documents list them.

Worth keeping: this table is generated precisely so it cannot go stale, it says
so in its own prose, and it was wrong for months. Generated is not the same as
correct — it only moves the mistake into the generator, where nobody looks.

Also in the npm README, which is the page npmjs.com renders: `vibe3d init` is
now the first line rather than unmentioned, `@types/three` is called out (three
ships no declarations, so without it the installed source does not typecheck),
the `@/` alias and `allowImportingTsExtensions` have a Project setup block, and
the one relative link is gone — npm resolves those against the repository, and
this one pointed at a directory the package deliberately does not ship.
`homepage` now points at the live site instead of a repository that is private.
2026-08-27 16:36:24 +07:00
CRT
9d027fcd06 Add the cauldron and the handcart
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.
2026-08-23 14:57:31 +07:00
CRT
1d4950445f Sweep the mill's sails clear of it, and give the ladder a door to reach
Two faults, both reported from looking at the model.

The sails passed through the buck. A tilted shaft spreads its sails through
depth -- at 0.14 rad a 3.5 m sail swings 0.49 m fore and aft -- and with the
hub only 0.08 m in front of the gable the whole lower half of the disc was
inside the building. Both halves of the fix are what a real mill does: the
shaft tilts a few degrees rather than eight, just enough to keep the sails
off the boarding and put some of their weight on the thrust bearing, and it
projects far enough out that the sweep happens clear of the wall. Far
enough and no further -- the clearance the disc needs is sailLength times
sin(tilt), about 0.26 m, and the first correction overshot to nearly a
metre, which left the sails hanging off the gable with only the shaft
between them.

And the tail ladder climbed to blank boarding. A mill is entered from its
tail, through the one wall the sails never sweep past, and the miller comes
up the same ladder he turns the mill with; a flight of steps ending at a
plain wall was the one thing about this model that could not be true. The
door is boarded proud of the wall with two ledges and a frame, because
there are no booleans in this geometry and a door of applied planks is how
a plank building is actually closed.

28 models, 20 679 triangles, all checks pass, 0 floating cases, build clean.
2026-08-23 13:27:06 +07:00
CRT
2b6e2b4d02 Lay the mill's windshaft along its own axis, and band it
Opening the viewer showed the mill reporting one material while declaring
two. It declared an `iron` slot and used it nowhere: the windshaft's own
comment called it iron-banded and then painted it oak like everything else.
Four sails pull on one shaft from four directions and what holds the whips
to it is iron strapping, so the bands are real and now exist.

Adding them exposed something that had been wrong since the model was
written. `taperedBoxGeometry` takes its third argument as a Y height, so
the shaft -- written with no rotation and only a z offset -- was a short
vertical post standing inside the buck rather than a shaft projecting from
the front gable. Buried in the body it survived every render unseen. The
bands, built the same way, came out coaxial with it, and the z-fight check
reported that immediately.

Two smaller things settled on the way.

The bands first went into `trestle`, where the support check found them
hanging at the model's origin: they are authored around the hub, so they
only make sense in the frame of the part whose origin is the hub. They
belong to `sails` anyway, since the shaft turns with them -- in a real mill
the sails are keyed to it.

And the four sails converged on the hub in perfect symmetry with their
faces meeting there. The fix is how the sails are actually carried: TWO
stocks pass through the shaft at right angles, one behind the other, each
carrying a sail at both ends. Four timbers cannot occupy one crossing, and
the check was right to say so.

Worth recording how the shaft was found, because reasoning about it failed
three times. Rebuilding the four sails alone, in every combination of whip,
rail and bars, gave zero overlaps -- which ruled the sails out and left only
what else was in that part.

28 models, 20 547 triangles, all checks pass, 0 floating cases, GLB
round-trip clean, Conformant, build clean.
2026-08-23 13:15:12 +07:00
CRT
d159acfd97 Add the post mill
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.
2026-08-23 13:01:33 +07:00