nx_mmdev_gate.nx source
↩ module page · 118 lines · 6887 B
1// nx_mmdev_gate.nx -- GATE for the millimetre surface-deviation ruler (PG4, procgen.plan, 2026-08-24).
2//
3// A ruler is proven by geometry whose true answer is known BEFORE the ruler runs. The fixtures here are
4// NXMSH2 meshes written through the same nx_nxmesh_lib the ruler reads, so the codec round-trip is under
5// test too, in caller-owned mmap -- never a production plane.
6//
7// KNOWN ANSWERS: a unit square (two triangles, 100 mm on a side) against a copy of itself lifted 5.0 mm
8// along its normal. Every vertex of one sits exactly 5.0 mm from the other's surface, so mean, p95 and
9// max must ALL read 50 in tenths of a millimetre. A wrong point-triangle case (edge or corner region
10// taken instead of the face) would read larger; a wrong sign or a dropped axis would read 0 or ~70.
11// That is the anti-vacuity property: the trivial wrong implementations cannot pass it.
12
13import "nx_syscalls.nx"
14import "nx_gate_verdict.nx"
15import "nx_vecmath.nx"
16import "nx_nxmesh_lib.nx"
17import "nx_mmdev_lib.nx"
18
19// 100 mm square in tenths of a millimetre; lift of exactly 5.0 mm.
20const MG_SIDE: i64 = 1000
21const MG_LIFT: i64 = 50
22const MG_LIFT2: i64 = 2500
23// One tenth of a millimetre of slack for the f32 round trip at 0.1 mm resolution -- the codec rounds to
24// nearest, so the worst case is half a unit per coordinate. Printed beside every measurement.
25const MG_SLACK: i64 = 1
26
27// Two triangles: (0,0,0)(S,0,0)(S,S,0) and (0,0,0)(S,S,0)(0,S,0), lifted by `lift` along +z.
28func mg_square(buf: *u8, lift: i64) -> i64 {
29 nm_put_u32(buf, NM_OFF_NLAYERS, 0)
30 nm_put_u32(buf, NM_OFF_NTRIS, 2)
31 let tb: i64 = nm_tri_base(buf)
32 let p: *i64 = sys_mmap(9 * 8) as *i64
33 p[0]=0; p[1]=0; p[2]=lift; p[3]=MG_SIDE; p[4]=0; p[5]=lift; p[6]=MG_SIDE; p[7]=MG_SIDE; p[8]=lift
34 nm_put_tri(buf, tb, 0, p)
35 p[0]=0; p[1]=0; p[2]=lift; p[3]=MG_SIDE; p[4]=MG_SIDE; p[5]=lift; p[6]=0; p[7]=MG_SIDE; p[8]=lift
36 nm_put_tri(buf, tb, 1, p)
37 return 0
38}
39
40func mg_near(a: i64, b: i64) -> i64 { var d: i64 = a - b; if d < 0 { d = 0 - d } if d <= MG_SLACK { return 1 } return 0 }
41
42func main() -> i64 {
43 let c: *i64 = gv_ctr()
44 gv_head("NX-MMDEV-GATE -- millimetre surface deviation ruler (PG4)" as *u8)
45
46 let bytes: i64 = nm_file_bytes(0, 2)
47 let base: *u8 = sys_mmap(bytes + 16)
48 let lifted: *u8 = sys_mmap(bytes + 16)
49 mg_square(base, 0)
50 mg_square(lifted, MG_LIFT)
51
52 // ---- codec round trip through the shared lib ---------------------------------------------------
53 let tb: i64 = nm_tri_base(base)
54 let rx: i64 = nm_coord(base, tb, 0, 1, 0)
55 let rz: i64 = nm_coord(lifted, tb, 0, 0, 2)
56 gv_puts(" codec S->" as *u8); gv_num(rx); gv_puts(" lift->" as *u8); gv_num(rz); gv_puts("\n" as *u8)
57 gv_check("f32-codec-round-trips-the-fixture-within-slack" as *u8, mg_near(rx, MG_SIDE) == 1 && mg_near(rz, MG_LIFT) == 1, c)
58
59 // ---- point-to-triangle on known cases ---------------------------------------------------------
60 // face interior: point above the centre of the first triangle
61 let dface: i64 = md_dist2_pt_tri(600, 300, MG_LIFT, 0,0,0, MG_SIDE,0,0, MG_SIDE,MG_SIDE,0)
62 // vertex region: point beyond corner A along the outward diagonal, at distance sqrt(2)*100 -> 141
63 let dvert: i64 = md_dist2_pt_tri(0-100, 0-100, 0, 0,0,0, MG_SIDE,0,0, MG_SIDE,MG_SIDE,0)
64 // edge region: point 100 below edge AB at its midpoint
65 let dedge: i64 = md_dist2_pt_tri(500, 0-100, 0, 0,0,0, MG_SIDE,0,0, MG_SIDE,MG_SIDE,0)
66 gv_puts(" pt-tri face d2=" as *u8); gv_num(dface); gv_puts(" (expect " as *u8); gv_num(MG_LIFT2)
67 gv_puts(") vertex d2=" as *u8); gv_num(dvert); gv_puts(" (expect 20000) edge d2=" as *u8); gv_num(dedge)
68 gv_puts(" (expect 10000)\n" as *u8)
69 gv_check("point-triangle-face-region-exact" as *u8, dface == MG_LIFT2, c)
70 gv_check("point-triangle-vertex-region-exact" as *u8, dvert == 20000, c)
71 gv_check("point-triangle-edge-region-exact" as *u8, dedge == 10000, c)
72
73 // ---- the ruler: known 5.0 mm answer, both directions ------------------------------------------
74 let ab: *i64 = md_res()
75 let rc1: i64 = md_measure(base, lifted, ab)
76 gv_subjects("tri-verts-measured" as *u8, ab[MD_R_VERTS], c)
77 gv_puts(" base->lifted rc=" as *u8); gv_num(rc1)
78 gv_puts(" mean=" as *u8); gv_num(md_mean(ab)); gv_puts(" p95=" as *u8); gv_num(ab[MD_R_P95])
79 gv_puts(" max=" as *u8); gv_num(ab[MD_R_MAX]); gv_puts(" expect all " as *u8); gv_num(MG_LIFT)
80 gv_puts(" slack=" as *u8); gv_num(MG_SLACK); gv_puts("\n" as *u8)
81 gv_check("known-5mm-lift-reads-5mm-mean-p95-and-max" as *u8,
82 rc1 == 0 && mg_near(md_mean(ab), MG_LIFT) == 1 && mg_near(ab[MD_R_P95], MG_LIFT) == 1 && mg_near(ab[MD_R_MAX], MG_LIFT) == 1, c)
83
84 let ba: *i64 = md_res()
85 let rc2: i64 = md_measure(lifted, base, ba)
86 gv_puts(" lifted->base mean=" as *u8); gv_num(md_mean(ba)); gv_puts("\n" as *u8)
87 gv_check("ruler-is-symmetric-on-a-parallel-offset" as *u8, rc2 == 0 && mg_near(md_mean(ba), md_mean(ab)) == 1, c)
88
89 // ---- self-compare is the non-vacuity proof: a mesh is ZERO from itself ------------------------
90 let self: *i64 = md_res()
91 let rc3: i64 = md_measure(base, base, self)
92 gv_puts(" self mean=" as *u8); gv_num(md_mean(self)); gv_puts(" max=" as *u8); gv_num(self[MD_R_MAX]); gv_puts("\n" as *u8)
93 gv_check("self-compare-reads-exactly-zero" as *u8, rc3 == 0 && md_mean(self) == 0 && self[MD_R_MAX] == 0, c)
94
95 // ---- neg-controls ------------------------------------------------------------------------------
96 // A refusal, not garbage: a subject over the derived extent bound. Build one triangle 3 m long.
97 let huge: *u8 = sys_mmap(nm_file_bytes(0, 1) + 16)
98 nm_put_u32(huge, NM_OFF_NLAYERS, 0)
99 nm_put_u32(huge, NM_OFF_NTRIS, 1)
100 let p: *i64 = sys_mmap(9 * 8) as *i64
101 p[0]=0; p[1]=0; p[2]=0; p[3]=30000; p[4]=0; p[5]=0; p[6]=0; p[7]=100; p[8]=0
102 nm_put_tri(huge, nm_tri_base(huge), 0, p)
103 let rr: *i64 = md_res()
104 let rc4: i64 = md_measure(base, huge, rr)
105 gv_puts(" neg-control 3m subject rc=" as *u8); gv_num(rc4); gv_puts(" (1 = REFUSED extent)\n" as *u8)
106 gv_check("neg-control-over-extent-subject-is-refused-by-name-not-measured" as *u8, rc4 == 1, c)
107
108 let empty: *u8 = sys_mmap(NM_HDR + 16)
109 nm_put_u32(empty, NM_OFF_NLAYERS, 0)
110 nm_put_u32(empty, NM_OFF_NTRIS, 0)
111 let re: *i64 = md_res()
112 let rc5: i64 = md_measure(base, empty, re)
113 gv_puts(" neg-control empty reference rc=" as *u8); gv_num(rc5); gv_puts(" (3 = empty)\n" as *u8)
114 gv_check("neg-control-empty-reference-refuses-rather-than-reporting-zero-deviation" as *u8, rc5 == 3, c)
115
116 return gv_verdict("nx_mmdev_gate" as *u8, c,
117 "Deviation in tenths of a millimetre against geometry with a known answer; the codec, the point-triangle regions, both directions, self-zero and two refusals each print their value." as *u8)
118}