Skip to content

Commit dec2f21

Browse files
fix: seat the ladder rungs and stop the iron chamfers reading as wood (#172)
* fix: seat the ladder rungs and stop the iron chamfers reading as wood The rung dowels were as deep as the stile they pass through, so each one erupted through the stile's front and back chamfers as a thin spike in every joint close-up. Narrowing the dowel to 26 mm corrects the proportion; a material or camera change would only have hidden it. Beveling the caps and shoes left every chamfer face in the wood slot. Bevel does not inherit material_index onto the faces it creates, and the piece claimed its metal faces from a pre-bevel snapshot, so each iron fitting rendered as a black block framed in tan. Claim them from the bevel op's own return instead. The previous round removed six iron fittings per ladder without re-fitting the triangle band or the outer AABB, leaving the piece failing its own gates at exit 4. Both constants are re-fitted to the measured result: 2184 tris to 936. Locks all of it in as budgets recomputed from the generated mesh: shell count, rung-to-stile depth clearance, tenon engagement and breakout margin, and material face floors that the pre-bevel snapshot would have failed. --lift-z could never fire, because matrix_world is evaluated data and the object was moved without a view-layer update; --stray-vert and --fat-rungs are new, and all four are proven to fire. Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * docs: require joint-fit budgets and a falsifier per hygiene budget A part exactly as thick as its host passes any "do the parts overlap" check while erupting through the host's chamfer as a spike, which is how the wooden ladder shipped. Name the three minima that catch it, and the frame to measure them in, so the next piece inherits them instead of rediscovering the class. The hygiene budgets added in the last pass had no named falsifier, so nothing proved they could fail. Name one per budget and require the exit code be checked rather than merely non-zero. Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> --------- Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 0083c51 commit dec2f21

8 files changed

Lines changed: 797 additions & 144 deletions

File tree

2.62 KB
Loading

docs/gallery/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,12 +1151,12 @@ <h2><a href="iron-cauldron/">iron-cauldron</a></h2>
11511151
</article>
11521152
<article class="card" data-tags="mesh export showcase">
11531153
<a class="card-media" href="wooden-ladder/" aria-label="wooden-ladder example detail page">
1154-
<img src="assets/wooden-ladder-hero.webp" alt="wooden-ladder — A procedural timber ladder with raked stiles, six rungs, and iron shoes through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather" loading="lazy" decoding="async" />
1154+
<img src="assets/wooden-ladder-hero.webp" alt="wooden-ladder — A procedural timber ladder with raked stiles, six dowel rungs tenoned into the stiles, and iron shoes and top caps through UVs, bake, LOD, collider, and Unity…" loading="lazy" decoding="async" />
11551155
</a>
11561156
<div class="card-body">
11571157
<h2><a href="wooden-ladder/">wooden-ladder</a></h2>
1158-
<p class="teaches">A procedural timber ladder with raked stiles, six rungs, and iron shoes through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.</p>
1159-
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 2184 tris, two materials with 1008 wood and 48 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.474×0.361×1.482 m, LOD ratios in band, convex collider 52 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.</p>
1158+
<p class="teaches">A procedural timber ladder with raked stiles, six dowel rungs tenoned into the stiles, and iron shoes and top caps through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.</p>
1159+
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 936 tris, two materials with 324 wood and 216 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.472×0.365×1.460 m, grounded at zmin 0, mesh hygiene all zero, 12 shells with every rung clearing the stile chamfer by 3.61 mm, convex collider 24 tris, non-empty glTF. Four falsifiers exit 9, 15, 16, and 17 on the LOD, hygiene, grounded, and joint-fit budgets.</p>
11601160
<a class="card-link" href="wooden-ladder/">View example <span aria-hidden="true">&rarr;</span></a>
11611161
</div>
11621162
</article>

docs/gallery/wooden-ladder/index.html

Lines changed: 373 additions & 68 deletions
Large diffs are not rendered by default.

showcase/README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ entry in `showcase/gallery.json`, and a rendered still.
3434
- **Falsifier** breaks one pipeline stage so a **named** budget fails and
3535
the piece exits its documented code. Prove default and falsifier on
3636
4.5.11, 5.1.2, and 5.2.1. `--skip-decimate` is the LOD-ratio
37-
falsifier (exit 9). `--lift-z` is the grounded-zmin falsifier (exit 16).
37+
falsifier (exit 9), `--stray-vert` the mesh-hygiene falsifier (exit
38+
15), `--lift-z` the grounded-zmin falsifier (exit 16), and
39+
`--fat-rungs` (or the piece's equivalent) the joint-fit falsifier
40+
(exit 17). A budget with no falsifier witnesses nothing: prove each
41+
one fails once, and check the exit code, not just non-zero.
3842
- **Hygiene budgets.** Copied combinatorics from
3943
`examples/mesh-hygiene-audit` (do not import the example). Every piece
4044
asserts on the generated mesh: non-manifold edges 0, loose verts 0,
@@ -43,6 +47,17 @@ entry in `showcase/gallery.json`, and a rendered still.
4347
characteristic 2 — that is a single-shell contract. Pairs of parts
4448
meant to touch assert a BVH surface gap below a named epsilon
4549
(vert-vert is the wrong metric for thin straps and collars).
50+
- **Joint-fit budgets.** Parts that interpenetrate on purpose — a tenon
51+
in a mortise, a peg in a hub — assert how deep the overlap goes, not
52+
just that it exists. Split the mesh into shells by edge connectivity,
53+
assert the expected shell count, and for each joint assert three
54+
named minima recomputed from vertex positions: the inserted part stays
55+
clear of the host's chamfer, it seats far enough past the host's near
56+
face, and it stops short of the host's far face. A part exactly as
57+
thick as its host passes a "parts overlap" test while erupting through
58+
the host's chamfer as a spike — that is the class this catches.
59+
Measure in the construction frame (un-rotate by any rake) so a tilted
60+
host does not inflate its own AABB.
4661
- **Material face floors.** Every declared material asserts a named
4762
minimum face count on the finished mesh, recomputed from
4863
`polygon.material_index`. This catches the slot-assignment wipe class:

showcase/gallery.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,8 @@
296296
{
297297
"name": "wooden-ladder",
298298
"dir": "showcase/wooden-ladder",
299-
"teaches": "A procedural timber ladder with raked stiles, six rungs, and iron shoes through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.",
300-
"witnessesFix": "Recomputed: 2184 tris, two materials with 1008 wood and 48 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.474×0.361×1.482 m, LOD ratios in band, convex collider 52 tris, non-empty glTF. --skip-decimate exits 9 on the LOD1 ratio budget.",
299+
"teaches": "A procedural timber ladder with raked stiles, six dowel rungs tenoned into the stiles, and iron shoes and top caps through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.",
300+
"witnessesFix": "Recomputed: 936 tris, two materials with 324 wood and 216 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.472×0.365×1.460 m, grounded at zmin 0, mesh hygiene all zero, 12 shells with every rung clearing the stile chamfer by 3.61 mm, convex collider 24 tris, non-empty glTF. Four falsifiers exit 9, 15, 16, and 17 on the LOD, hygiene, grounded, and joint-fit budgets.",
301301
"hero": "docs/gallery/assets/wooden-ladder-hero.webp",
302302
"preview": "showcase/wooden-ladder/preview.webp",
303303
"tags": [

showcase/wooden-ladder/README.md

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Wooden ladder
22

33
A showcase piece, not an example. Procedural timber ladder (raked
4-
stiles, six rungs, iron shoes and straps) then the shipped pipeline:
5-
unique-cell UVs, Cycles high-to-low normal bake, LOD chain, convex
6-
collider, Unity glTF export.
4+
stiles, six 26 mm dowel rungs tenoned into the stiles, iron shoes and
5+
top caps) then the shipped pipeline: unique-cell UVs, Cycles
6+
high-to-low normal bake, LOD chain, convex collider, Unity glTF export.
77

88
It asserts **budget conformance** of the generated result. It does not
99
witness an API contract. "It rendered without error" is not a check.
@@ -21,14 +21,34 @@ materials, UVs, evaluated LOD, collider, or export file.
2121

2222
| Axis | Declared | Measured (4.5.11 / 5.1.2 / 5.2.1) |
2323
| --- | --- | --- |
24-
| Base triangles | 2100–2280 | 2184 / 2184 / 2184 |
24+
| Base triangles | 900–975 | 936 / 936 / 936 |
2525
| LOD1 ratio | 0.32–0.62 of base | 0.5000 / 0.5000 / 0.5000 |
26-
| LOD2 ratio | 0.10–0.35 of base | 0.2198 / 0.2198 / 0.2198 |
27-
| Materials | exactly 2 distinct, ≥24 wood, ≥24 metal | 2 slots, 1008 wood, 48 metal |
26+
| LOD2 ratio | 0.10–0.35 of base | 0.2179 / 0.2179 / 0.2179 |
27+
| Materials | exactly 2 distinct, ≥280 wood, ≥180 metal | 2 slots, 324 wood, 216 metal |
2828
| UVs | in `0..1`, AABB overlap ≤ 1e-5 | in range, overlap 0 |
29-
| Outer AABB | (0.474, 0.361, 1.482) m ± 0.01 | (0.4740, 0.3612, 1.4823), zmin 0 |
30-
| Collider tris | ≤ 120 | 52 |
31-
| Export | written, size > 0 | 158256 / 158256 / 158240 bytes |
29+
| Outer AABB | (0.472, 0.365, 1.460) m ± 0.01 | (0.4720, 0.3649, 1.4597) |
30+
| Grounded | `abs(zmin)` ≤ 1e-4 | 0.0000 |
31+
| Mesh hygiene | non-manifold, loose v/e, doubles at 1e-5, zero-area, n-gons all 0 | all 0 |
32+
| Parts | exactly 12 shells (2 stiles, 6 rungs, 2 caps, 2 shoes) | 12 (2 / 6) |
33+
| Rung depth clearance | ≥ 0.003 m inside the stile faces | 0.00361 |
34+
| Tenon engagement | ≥ 0.006 m past the stile inner face | 0.01200 |
35+
| Tenon breakout margin | ≥ 0.005 m short of the stile outer face | 0.04600 |
36+
| Collider tris | ≤ 120 | 24 |
37+
| Export | written, size > 0 | 75140 / 75140 / 75128 bytes |
38+
39+
Base triangles fell from **2184 to 936** in the quality pass: four iron
40+
fittings per stile collapsed to one shoe and one top cap, and the
41+
redundant top block came out. No hidden-face removal was involved.
42+
43+
The rung rims are capped and buried in the stile rather than left open.
44+
That costs 144 tris nobody sees, but an open rim is a non-manifold
45+
boundary and the hygiene budget forbids it.
46+
47+
The joint budgets are measured per rung against each stile, from vertex
48+
positions in the un-raked construction frame, and the reported value is
49+
the worst case over all twelve rung ends. `clearance` is how far the
50+
widest dowel stays inside the stile's front and back faces; it must
51+
exceed the 2.5 mm chamfer or the dowel erupts through it as a spike.
3252

3353
DECIMATE COLLAPSE triangle counts are **not** identical across series —
3454
the gate is a ratio band, not an exact count. On this mesh the three
@@ -37,18 +57,31 @@ binaries agreed on LOD ratios. Bake pixels are stochastic; the gate is
3757
uses no RNG. Export byte counts differ by 16 B on 5.2.1 (glTF
3858
serializer), not a gated axis.
3959

40-
`--skip-decimate` skips the LOD DECIMATE stage so LOD1 ratio is 1.0 and
41-
exit 9 fires. That is the named budget the falsifier violates.
60+
Each falsifier violates exactly one named budget, and each was proven to
61+
fire on all three binaries:
62+
63+
| Falsifier | Budget violated | Exit | Measured failure |
64+
| --- | --- | --- | --- |
65+
| `--skip-decimate` | LOD1 ratio band | 9 | ratio 1.0000 |
66+
| `--stray-vert` | Mesh hygiene, loose verts | 15 | `loose_v=1` |
67+
| `--lift-z` | Grounded zmin | 16 | zmin 0.050000 |
68+
| `--fat-rungs` | Rung depth clearance | 17 | clearance −0.00051 |
69+
70+
`--fat-rungs` widens the dowels to the full 34 mm stile depth, which is
71+
the proportion this piece shipped with before the quality pass.
4272

4373
## Run
4474

4575
```bash
4676
blender --background --python wooden_ladder.py --
4777
blender --background --python wooden_ladder.py -- --skip-decimate
78+
blender --background --python wooden_ladder.py -- --stray-vert
79+
blender --background --python wooden_ladder.py -- --lift-z
80+
blender --background --python wooden_ladder.py -- --fat-rungs
4881
blender --background --python wooden_ladder.py -- --output ladder.png
4982
```
5083

51-
Smoke does not pass `--output` or `--skip-decimate`.
84+
Smoke passes no flags: no `--output`, no falsifier.
5285

5386
## Exit codes
5487

@@ -72,3 +105,6 @@ File-local. `9` is a valid check code. `10` is reserved for
72105
| 12 | Bake did not finish or image has no data |
73106
| 13 | Export file missing or empty |
74107
| 14 | `--output` produced no file |
108+
| 15 | Mesh hygiene (`--stray-vert` lands here) |
109+
| 16 | World AABB min Z off the floor (`--lift-z` lands here) |
110+
| 17 | Part count or rung-to-stile joint fit (`--fat-rungs` lands here) |
3.31 KB
Loading

0 commit comments

Comments
 (0)