nx_vpick_gate.nx
buildroot/runtime/nx_vpick_gate.nx
about
nx_vpick_gate.nx -- the gate for nx_vpick_lib (/compare/dcc DC4: ray-pick and a transform gizmo).
THE CLAIM UNDER TEST is that a click in the sovereign viewport resolves to the RIGHT triangle and a drag
resolves to an UNDOABLE operation. Both halves are asserted on values a wrong implementation cannot
produce, never on a return code alone -- because the three cheapest wrong pickers in the world all
return plausible codes: one that always misses, one that always returns triangle 0, and one that returns
whichever triangle happens to be first in the buffer.
THE FIXTURE CAMERA IS THE SHIPPED CAMERA. VG_W, VG_H, VG_DIST and VG_FOVH below are literally MV_W,
MV_H, DIST and FOVH from nx_meshview_wasm. If the pick maths had drifted from the renderer's convention
the exact-value teeth would fail, because they are the numbers that camera actually produces.
ANTI-VACUITY, named where it lives, because most of these teeth are individually satisfiable by a stub:
* a picker that ALWAYS MISSES passes every miss tooth -- killed by the exact-barycentric positive
control, which pins u, v, w, t and the hit point to arithmetic worked out by hand.
* a picker that ALWAYS RETURNS 0 passes the positive control -- killed by the named-miss tooth.
* a picker that RETURNS THE FIRST HIT IN THE BUFFER passes a single depth test -- killed by running
the SAME two triangles in both buffer orders and requiring DIFFERENT answers (1 then 0). A
first-wins picker answers 0 twice, a last-wins picker answers 1 twice; only genuine depth sorting
answers 1 then 0.
* a degenerate-triangle guard that simply skips everything passes "degenerate is never picked" --
killed by putting the degenerate FIRST and requiring the valid triangle behind it to be found WITH
its correct barycentrics.
* a gizmo that returns ZERO is undoable, replayable and axis-constrained -- killed by comparing its
magnitude against a closed form derived a DIFFERENT way (view half-width at the pivot plane), which
also kills a gizmo that returns the raw pixel delta.
* an edit stack that records nothing has a stable digest -- killed by requiring the digest to CHANGE
and by reading the moved vertex back component by component.
* a BENCH whose loop the compiler deleted times as FREE and reads as a very fast picker -- killed by
accumulating the picked index and requiring the sum to equal the repetition count times what a single
pick returns, a number no deleted loop can produce.
* a LINEARITY verdict taken at ONE triangle count is not a verdict at all, because a quadratic curve
and a linear one are the same number at a single N -- killed by four rungs spanning 64x, and by a
bar derived from the two hypotheses being separated rather than tuned until the result passed.
Every guard in the library is a gv_bite: it must fire on the bad input AND stay silent on the good one.
THE SECOND HALF OF THE RUNG, ADDED 2026-08-25. A pick that is CORRECT is still not an interaction claim
until it carries a COST at a stated triangle count -- dcc.plan's unit line says exactly that, and the
rung's done-rule was half unmet while this file proved only correctness. Section 9 times vp_pick_screen
over procedurally generated grids at four triangle counts, publishes nanoseconds per operation with its
dependencies 5 imports · 0 importers
imports: nx_syscalls.nxnx_gate_verdict.nxnx_vpick_lib.nxnx_frame_budget.nxnx_oracleband_lib.nx
imported by: nobody (leaf or entry point)
structs
| none |
consts
| 54 | const VG_I64: i64 = 8 |
| 55 | const VG_NV: i64 = 9 // 3 triangles worth of vertices: far, near, degenerate |
| 56 | const VG_MESH_WORDS: i64 = 27 // VG_NV * 3 |
| 57 | const VG_NCELLS: i64 = 27 // vp_cells_needed(VG_NV) |
| 58 | const VG_CAP: i64 = 8 // op capacity; no fixture here pushes more than one |
| 61 | const VG_W: i64 = 64 // MV_W |
| 62 | const VG_H: i64 = 64 // MV_H |
| 63 | const VG_YAW0: i64 = 0 |
| 64 | const VG_YAW90: i64 = 90 |
| 65 | const VG_DIST: i64 = 65536 // DIST (4.0 in Q14) |
| 66 | const VG_FOVH: i64 = 30 // FOVH (half field of view, degrees) |
| 67 | const VG_FOV90: i64 = 90 // a half-fov with no forward-facing frustum: cot(90) is 0 |
| 71 | const VG_CX: i64 = 32 // VG_W / 2 |
| 72 | const VG_CY: i64 = 32 // VG_H / 2 |
| 73 | const VG_DPX: i64 = 8 // the drag length used everywhere, in pixels |
| 74 | const VG_BX_R: i64 = 40 // VG_CX + VG_DPX |
| 75 | const VG_BX_L: i64 = 24 // VG_CX - VG_DPX |
| 76 | const VG_BX_BIG: i64 = 48 // VG_CX + 2*VG_DPX |
| 77 | const VG_BY_D: i64 = 40 // VG_CY + VG_DPX (screen y grows DOWNWARD) |
| 78 | const VG_EDGE_X: i64 = 63 // VG_W - 1, the last legal column |
| 79 | const VG_OFF_X: i64 = 64 // VG_W, the first illegal column |
| 87 | const VG_SLACK: i64 = 12 |
| 94 | const VG_SLACK_MEASURED: i64 = 3 |
| 102 | const VG_NOT_PIXELS_MULT: i64 = 100 |
| 106 | const VG_SUM_MUL: i64 = 131 |
| 108 | const VG_BAD_AXIS: i64 = 3 // one past VP_AXIS_Z |
| 109 | const VG_BAD_VERT: i64 = 9 // == VG_NV, the first out-of-range vertex index |
| 116 | const VG_VERT_C: i64 = 3 // i64 components per fixture vertex: x, y, z |
| 117 | const VG_TRI_I: i64 = 3 // i64 vertex indices per fixture triangle: a, b, c |
| 118 | const VG_IX_1TRI: i64 = VG_TRI_I // a one-triangle index buffer |
| 119 | const VG_IX_2TRI: i64 = VG_TRI_I + VG_TRI_I // the two-triangle fixtures (near/far pairs) |
| 129 | const VB_CONF: *u8 = "knowledge/gamefeel_oracle.conf\x00" |
| 130 | const VB_FPS_VR: i64 = 90 // the rate frame_budget_ms_90hz is cited at; identical to TB_FPS_VR |
| 131 | const VB_MS_US: i64 = 1000 // microseconds in a millisecond -- the conf states its rows in ms |
| 148 | const VB_LADDER: i64 = 4 |
| 149 | const VB_G0: i64 = 4 |
| 150 | const VB_G1: i64 = 8 |
| 151 | const VB_G2: i64 = 16 |
| 152 | const VB_G3: i64 = 32 |
| 153 | const VB_GMAX: i64 = 32 // the largest rung; the buffers are sized from it and a tooth checks it |
| 158 | const VB_STEP: i64 = 4 |
| 164 | const VB_PX: i64 = 41 |
| 165 | const VB_PY: i64 = 27 |
| 176 | const VB_RESOLVE_US: i64 = 1000 |
| 177 | const VB_REPS_MIN: i64 = 1000 |
| 178 | const VB_REPS0: i64 = 1024 // first attempt, the smallest power of two at or above VB_REPS_MIN |
| 179 | const VB_REPS_CAP: i64 = 4194304 // refuse to spin forever: ANNOUNCED, never silent (same shape as TB_TICKS_CAP) |
| 185 | const VB_SAMPLES: i64 = 2 |
functions
| 187 | func vg_eq(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 } |
| 188 | func vg_ne(a: i64, b: i64) -> i64 { if a != b { return 1 } return 0 } |
| 189 | func vg_gt(a: i64, b: i64) -> i64 { if a > b { return 1 } return 0 } |
| 190 | func vg_lt(a: i64, b: i64) -> i64 { if a < b { return 1 } return 0 } |
| 191 | func vg_near(a: i64, b: i64, slack: i64) -> i64 |
| 197 | func vg_absd(a: i64, b: i64) -> i64 |
| 202 | func vg_setv(m: *i64, k: i64, x: i64, y: i64, z: i64) -> i64 { m[k*VG_VERT_C+VP_AXIS_X]=x; m[k*VG_VERT_C+VP_AXIS_Y]=y; m[k*VG_VERT_C+VP_AXIS_Z]=z; return 0 } |
| 203 | func vg_seti(p: *i64, k: i64, a: i64, b: i64, c: i64) -> i64 { p[k*VG_TRI_I+0]=a; p[k*VG_TRI_I+1]=b; p[k*VG_TRI_I+2]=c; return 0 } |
| 204 | func vg_setray(r: *i64, ox: i64, oy: i64, oz: i64, dx: i64, dy: i64, dz: i64) -> i64 |
| 209 | func vg_setcam(c: *i64, w: i64, h: i64, yaw: i64, dist: i64, fovh: i64) -> i64 |
| 214 | func vg_setdrag(d: *i64, ax: i64, ay: i64, bx: i64, by: i64) -> i64 |
| 218 | func vg_sum(p: *i64, n: i64) -> i64 |
| 225 | func vg_show(label: *u8, v: i64) -> i64 |
| 248 | func vb_measure(cam: *i64, mesh: *i64, nverts: i64, idx: *i64, ntris: i64, scratch: *i64, pout: *i64, out: *i64) -> i64 |
| 280 | func main(argc: i64, argv: *i64) -> i64 |