← Just Build It — the deck · evidence document
02 — ProjectionCheck: the fidelity instrument
How much of the approved 2D design intent does a generated mesh preserve? Fully deterministic — registration and silhouette metrics are pure geometry. No VLM anywhere in the loop. The number triages; the designer decides; this report never vetoes.
Implementation: tools/projection-check/projection_check.py (CPython, numpy + PIL
only — parses the GLB itself, no 3D library). First run: 2026-08-25, ~1 s.
Position among the instruments
| Exploration Harness | ProjectionCheck | |
|---|---|---|
| Measures | breadth — where a system stops opening useful directions | fidelity — how much approved intent survives a representation change |
| Method | blind deck, designers sort | deterministic geometry comparison |
| Runs | per study, with designers | per mesh, in seconds, unattended |
ProjectionCheck runs before the human proportion gate as triage: it shows the designer where the mesh drifted from the views they approved. Approve/reject remains a human judgment.
The three layers (as implemented, V0)
1 — Registration
- Parse the GLB scene graph (node transforms applied), gather per-part vertices.
- Normalize: longest axis → 300 mm, min corner → origin. Axis roles derived, not assumed: length = largest extent; height = Y (glTF up) unless Y is length; recorded in the report.
- Per view: uniform scale on the view's u-axis (content-bbox width), align content bboxes left + bottom (the ground line is the stable datum).
- Mirror ambiguity is resolved, not assumed: both flips are scored, the better IoU wins, and the choice is recorded. In the first run this automatically distinguished lateral from medial (side → flip, right → no flip).
2 — Silhouette metrics (per view)
- IoU — overall silhouette agreement.
- Hausdorff (mm) — worst contour deviation, symmetric, on subsampled contours.
- Height ratio — mesh content height / view content height after u-normalization. A proportion drift number: 1.056 means "5.6 % taller than approved".
- Band IoU (u-quartiles) — agreement per length-quartile. This is the localization: it says where the drift lives (rear vs mid vs toe), which is what makes the report a work order rather than a score.
Rendering: vertex-splat rasterization (900 px across u) + morphological closing — no rasterizer dependency, deterministic, silhouette-accurate to a few px. View masks: background-threshold from corner color, speck-removal, then close-before-flood hole filling (a white midsole touching a white background otherwise opens a leak channel into the interior).
3 — Region layer (V0: mesh-side only)
Heel cluster (parts with centroid norm-u < 0.15) reported with centroids and sizes. The 2D counterpart comparison requires a labeled view manifest (F04-style) for the approved views, which this run does not have. Flagged in the report, not faked. This is the strongest argument for carrying the 2D layer manifest into 3D generation.
First run — sheet6 shoe (Tripo v3.1 multiview, task c4d4cbf3)
Input: shoe.tripo-segmented.ref.glb (16 parts, 992k vertices) vs the four
approved views in output/sheet6-case/. Full report:
work/projection-check/report.json; overlays overlay-{side,right,front,back}.png
(grey = view only, orange = mesh only, green = agreement).
| View | IoU | Hausdorff | Height ratio | Band IoU (u-quartiles) | Flip |
|---|---|---|---|---|---|
| side (lateral) | 0.819 | 60.3 mm | 0.971 | .83 / .96 / .83 / .70 | yes |
| right (medial) | 0.900 | 30.3 mm | 0.995 | .85 / .96 / .90 / .84 | no |
| front | 0.880 | 20.2 mm | 1.056 | .85 / .91 / .92 / .81 | yes |
| back | 0.873 | 45.1 mm | 1.000 | .80 / .94 / .93 / .78 | no |
What the numbers already say (read as a work order, per 00-overview §14.9 logic):
- The mesh preserves the medial side better than the lateral (0.900 vs 0.819) — and the lateral drift concentrates in one quartile (0.70). The lateral view is the identity-carrying view for this design; that asymmetry is exactly what a designer needs pointed at before the gate.
- Front height ratio 1.056 — the mesh reads ~6 % taller than the approved front. A proportion drift a still render would hide.
- Hausdorff worst-case 60 mm sits at silhouette extremities (collar/tab) — consistent with where Tripo hallucinates freely because top/bottom views never feed generation (four slots only).
Honesty block
- Thresholds are unset. What IoU is "acceptable" must come from accumulated designer gate decisions, not be invented by this tool. V0 reports; it does not grade.
- Silhouette-level only: interior design lines (panel breaks, midsole grooves) are invisible to these metrics. Region layer + a labeled manifest is the path to interior fidelity.
- The approved views are generated images assumed quasi-orthographic; any perspective in them biases the comparison. Recorded, not corrected, in V0.
- Vertex-splat + closing thickens silhouettes by ~2–3 px; both sides of the comparison pay it, IoU bias is second-order.
Second run — three backends, one input set (F08 method comparison)
Same protocol on the three meshes reconstructed from the F08 six-view set
(work/comfy-runs/f08-method-comparison/), compared against that set's own
views. This turns the deck's "same six views in, three boundaries out" claim
into numbers — and each backend breaks differently:
| Backend | side IoU | right IoU | front IoU | back IoU | Signature failure |
|---|---|---|---|---|---|
| Tripo 3.1 | 0.800 | 0.794 | 0.442 | 0.508 | front/back first-quartile ≈ 0 — one lateral extremity missing; height ratio 0.66 front: collar volume lost |
| Hunyuan3D 2 | 0.154 | 0.161 | 0.415 | 0.449 | floating slab debris (visible in preview) distorts normalization; side family collapses |
| Rodin Gen-2 | 0.892 | 0.895 | 0.725 | 0.644 | best silhouettes — but front height ratio 1.26–1.40: the profile inflates in the width family |
Instrument findings from this run, recorded as work orders on the tool itself:
- The F08
front.pngcarries a second shoe's outsole strip in frame — the content-bbox registration was garbage until a largest-connected-component rule was added to the view mask. View-sheet hygiene is part of the protocol now. - Hunyuan's number conflates silhouette drift with debris: separating "main body fidelity" from "debris fraction" needs face connectivity (indices), which V0 does not read. Until then its row is a boundary statement about the whole output, not the shoe within it.
- Cross-set comparability: the F08 design ≠ the sheet6 design; compare backends within one input set, never absolute numbers across sets.
Next
- Face-connectivity main-body filter → separate debris fraction from body fidelity (unlocks a fair Hunyuan number).
- Labeled view manifest → region layer completion (per-part drift).
- Threshold learning: log gate decisions beside reports until "reject below X" emerges from practice.