nx_curveskel_gate.nx source
↩ module page · 323 lines · 17104 B
1// nx_curveskel_gate.nx -- THE REFEREE FOR SKELETON-FIT: a curve skeleton extracted from a BARE mesh must SPAN the
2// shape's extremities, peel the right number of limbs (a linear volume = two, forking once at its thick centre;
3// a T = three, with a real fork joint), carry a valid single-rooted tree, reflect thickness in its root radius, be
4// byte-deterministic, and REFUSE a volumeless sheet by name. Linear fixtures are axis-aligned square PRISMS built
5// at run time under /tmp/nx_curveskel_gate (the same sealable shape nx_boneheat_gate uses, so the voxel seal is
6// isolated from the skeleton logic). The BRANCHED fixture is a watertight T extracted by nx_csg from the UNION of
7// two box SDFs (a stem and a crossbar) -- a boolean union has no internal walls, which two overlapping closed
8// meshes would have, and marching tetrahedra over the field is watertight by construction. Every solve is
9// IN-PROCESS through nx_curveskel_lib so a mutant of the lib is reached.
10// license_tier: ORIGINAL expect_exit: 0
11import "nx_syscalls.nx"
12import "nx_gate_verdict.nx"
13import "nx_nxa.nx"
14import "nx_curveskel_lib.nx"
15import "nx_sdf.nx"
16import "nx_csg.nx"
17
18const CG_DIR: *u8 = "/tmp/nx_curveskel_gate"
19const CG_MODE_DIR: i64 = 493
20const CG_AROUND: i64 = 4
21const CG_LEN: i64 = 6000 // 60 mm prism
22const CG_RINGS: i64 = 61
23const CG_R_THIN: i64 = 300 // 3 mm half-width
24const CG_R_FAT: i64 = 700 // 7 mm half-width
25const CG_VERT_CAP: i64 = 2048
26const CG_TRI_CAP: i64 = 8192
27const CG_ROOT: i64 = 0 - 1
28const CG_SPAN_LO: i64 = 1200 // 20% of CG_LEN: a tip below this is near the z=0 end
29const CG_SPAN_HI: i64 = 4800 // 80% of CG_LEN: a tip above this is near the z=CG_LEN end
30// the T: nx_csg and nx_mesh speak Q14-mm (one millimetre = 16384); the NXA speaks 0.01 mm (one millimetre = 100)
31const CG_MM_Q14: i64 = 16384
32const CG_NXA_PER_MM: i64 = 100
33const CG_TEE_STEM_HX: i64 = 3 // mm half-extents: stem 6 mm square, 30 mm tall
34const CG_TEE_STEM_HZ: i64 = 15
35const CG_TEE_STEM_CZ: i64 = 15
36const CG_TEE_BAR_HX: i64 = 15 // crossbar 30 mm wide, 6 mm tall, sitting on the stem
37const CG_TEE_BAR_HZ: i64 = 3
38const CG_TEE_BAR_CZ: i64 = 33
39const CG_TEE_LO_X: i64 = 0 - 16
40const CG_TEE_LO_Y: i64 = 0 - 4
41const CG_TEE_LO_Z: i64 = 0 - 1
42const CG_TEE_HI_X: i64 = 16
43const CG_TEE_HI_Y: i64 = 4
44const CG_TEE_HI_Z: i64 = 37
45const CG_TEE_RES: i64 = 40 // marching-tet grid: under 1 mm per cell on every axis of the 32 x 8 x 38 mm box
46const CG_TEE_SPAN_X: i64 = 1200 // 12 mm in NXA units: a joint past this on each side has reached the crossbar tips
47const CG_TEE_FOOT_Z: i64 = 300 // 3 mm: a joint below this has reached the stem foot
48const CG_TEE_LIMBS: i64 = 3
49
50func cg_ring_xy(k: i64, r: i64, out: *i64) -> i64 {
51 if k == 0 { out[0] = r; out[1] = 0 - r }
52 if k == 1 { out[0] = r; out[1] = r }
53 if k == 2 { out[0] = 0 - r; out[1] = r }
54 if k == 3 { out[0] = 0 - r; out[1] = 0 - r }
55 return 0
56}
57func cg_tri(tris: *i64, st: *i64, a: i64, b: i64, c: i64) -> i64 {
58 let t: i64 = st[1]
59 tris[1 + t * 3] = a; tris[1 + t * 3 + 1] = b; tris[1 + t * 3 + 2] = c
60 st[1] = t + 1
61 return 0
62}
63// axis-aligned square prism of half-width r along z, capped -> a sealable linear volume
64func cg_prism(vert: *i64, tris: *i64, st: *i64, r: i64, z0: i64, z1: i64, rings: i64) -> i64 {
65 let v0: i64 = st[0]
66 let xy: *i64 = sys_mmap(16) as *i64
67 var rr: i64 = 0
68 while rr < rings {
69 var k: i64 = 0
70 while k < CG_AROUND {
71 cg_ring_xy(k, r, xy)
72 let idx: i64 = v0 + rr * CG_AROUND + k
73 vert[1 + idx * 3] = xy[0]
74 vert[1 + idx * 3 + 1] = xy[1]
75 vert[1 + idx * 3 + 2] = z0 + (z1 - z0) * rr / (rings - 1)
76 k = k + 1
77 }
78 rr = rr + 1
79 }
80 st[0] = v0 + rings * CG_AROUND
81 rr = 0
82 while rr < rings - 1 {
83 var k: i64 = 0
84 while k < CG_AROUND {
85 let a: i64 = v0 + rr * CG_AROUND + k
86 let b: i64 = v0 + rr * CG_AROUND + (k + 1) % CG_AROUND
87 let c: i64 = v0 + (rr + 1) * CG_AROUND + (k + 1) % CG_AROUND
88 let d: i64 = v0 + (rr + 1) * CG_AROUND + k
89 cg_tri(tris, st, a, b, c)
90 cg_tri(tris, st, a, c, d)
91 k = k + 1
92 }
93 rr = rr + 1
94 }
95 let cb: i64 = st[0]
96 vert[1 + cb * 3] = 0; vert[1 + cb * 3 + 1] = 0; vert[1 + cb * 3 + 2] = z0
97 let ct: i64 = st[0] + 1
98 vert[1 + ct * 3] = 0; vert[1 + ct * 3 + 1] = 0; vert[1 + ct * 3 + 2] = z1
99 st[0] = st[0] + 2
100 var k: i64 = 0
101 while k < CG_AROUND {
102 cg_tri(tris, st, cb, v0 + (k + 1) % CG_AROUND, v0 + k)
103 let top: i64 = v0 + (rings - 1) * CG_AROUND
104 cg_tri(tris, st, ct, top + k, top + (k + 1) % CG_AROUND)
105 k = k + 1
106 }
107 return st[0] - v0
108}
109// a flat quad: 4 verts, 2 triangles, NO volume -- the neg-control (no medial axis)
110func cg_sheet(vert: *i64, tris: *i64, st: *i64) -> i64 {
111 vert[1] = 0; vert[2] = 0; vert[3] = 0
112 vert[4] = CG_LEN; vert[5] = 0; vert[6] = 0
113 vert[7] = CG_LEN; vert[8] = CG_LEN; vert[9] = 0
114 vert[10] = 0; vert[11] = CG_LEN; vert[12] = 0
115 st[0] = 4
116 st[1] = 0
117 cg_tri(tris, st, 0, 1, 2)
118 cg_tri(tris, st, 0, 2, 3)
119 return 4
120}
121func cg_write(path: *u8, vert: *i64, tris: *i64, st: *i64) -> i64 {
122 vert[0] = st[0]; tris[0] = st[1]
123 let tags: *i64 = sys_mmap(64) as *i64
124 let ptrs: *i64 = sys_mmap(64) as *i64
125 let wls: *i64 = sys_mmap(64) as *i64
126 tags[0] = nxa_tag4("VERT" as *u8); ptrs[0] = vert as i64; wls[0] = 1 + st[0] * 3
127 tags[1] = nxa_tag4("TRIS" as *u8); ptrs[1] = tris as i64; wls[1] = 1 + st[1] * 3
128 return bh_nxa_write(path, 2, tags, ptrs, wls)
129}
130// the watertight T: UNION of a stem box and a crossbar box through nx_csg (marching tetrahedra over the SDF), the
131// Q14-mm mesh converted to NXA 0.01 mm units and written as VERT + TRIS. Returns bytes written; out[0]=nv out[1]=nt
132func cg_tee(path: *u8, out: *i64) -> i64 {
133 let stem: *NxSdfPrim = nx_sdf_make(NX_SDF_BOX, 0, 0, CG_TEE_STEM_CZ * CG_MM_Q14, CG_TEE_STEM_HX * CG_MM_Q14, CG_TEE_STEM_HX * CG_MM_Q14, CG_TEE_STEM_HZ * CG_MM_Q14)
134 let bar: *NxSdfPrim = nx_sdf_make(NX_SDF_BOX, 0, 0, CG_TEE_BAR_CZ * CG_MM_Q14, CG_TEE_BAR_HX * CG_MM_Q14, CG_TEE_STEM_HX * CG_MM_Q14, CG_TEE_BAR_HZ * CG_MM_Q14)
135 let m: *NxMesh = nx_csg_extract(stem, bar, NX_CSG_UNION, CG_TEE_LO_X * CG_MM_Q14, CG_TEE_LO_Y * CG_MM_Q14, CG_TEE_LO_Z * CG_MM_Q14, CG_TEE_HI_X * CG_MM_Q14, CG_TEE_HI_Y * CG_MM_Q14, CG_TEE_HI_Z * CG_MM_Q14, CG_TEE_RES, 0)
136 let nv: i64 = m.n_verts as i64
137 let nt: i64 = m.n_tris as i64
138 out[0] = nv; out[1] = nt
139 if nv < 1 { return 0 - 1 }
140 if nt < 1 { return 0 - 1 }
141 let vert: *i64 = sys_mmap((1 + nv * 3) * 8 + 64) as *i64
142 let tris: *i64 = sys_mmap((1 + nt * 3) * 8 + 64) as *i64
143 vert[0] = nv; tris[0] = nt
144 var i: i64 = 0
145 while i < nv {
146 vert[1 + i * 3] = m.verts[i * 4] * CG_NXA_PER_MM / CG_MM_Q14
147 vert[1 + i * 3 + 1] = m.verts[i * 4 + 1] * CG_NXA_PER_MM / CG_MM_Q14
148 vert[1 + i * 3 + 2] = m.verts[i * 4 + 2] * CG_NXA_PER_MM / CG_MM_Q14
149 i = i + 1
150 }
151 var t: i64 = 0
152 while t < nt {
153 tris[1 + t * 3] = m.indices[t * 3]; tris[1 + t * 3 + 1] = m.indices[t * 3 + 1]; tris[1 + t * 3 + 2] = m.indices[t * 3 + 2]
154 t = t + 1
155 }
156 let tags: *i64 = sys_mmap(64) as *i64
157 let ptrs: *i64 = sys_mmap(64) as *i64
158 let wls: *i64 = sys_mmap(64) as *i64
159 tags[0] = nxa_tag4("VERT" as *u8); ptrs[0] = vert as i64; wls[0] = 1 + nv * 3
160 tags[1] = nxa_tag4("TRIS" as *u8); ptrs[1] = tris as i64; wls[1] = 1 + nt * 3
161 return bh_nxa_write(path, 2, tags, ptrs, wls)
162}
163// open an output NXA, return base pointer; lp[0] = length
164func cg_open(path: *u8, lp: *i64) -> i64 {
165 let b: *u8 = sys_read_file(path, lp)
166 if (b as i64) == 0 { return 0 }
167 return b as i64
168}
169// SKEL joint count in an output NXA, or -1
170func cg_nj(b: *u8, flen: i64) -> i64 {
171 let o: i64 = nxa_find(b, flen, nxa_tag4("SKEL" as *u8))
172 if o < 0 { return 0 - 1 }
173 let w: *i64 = b as *i64
174 return w[o]
175}
176func cg_joint_word(b: *u8, flen: i64, j: i64, word: i64) -> i64 {
177 let o: i64 = nxa_find(b, flen, nxa_tag4("SKEL" as *u8))
178 let w: *i64 = b as *i64
179 return w[o + 1 + j * CS_JOINT_WORDS + word]
180}
181func cg_files_equal(pa: *u8, pb: *u8) -> i64 {
182 let la: *i64 = sys_mmap(16) as *i64
183 let lb: *i64 = sys_mmap(16) as *i64
184 let a: *u8 = sys_read_file(pa, la)
185 let b: *u8 = sys_read_file(pb, lb)
186 if (a as i64) == 0 { return 0 }
187 if (b as i64) == 0 { return 0 }
188 if la[0] != lb[0] { return 0 }
189 var i: i64 = 0
190 while i < la[0] { if a[i] != b[i] { return 0 } i = i + 1 }
191 return 1
192}
193
194func main() -> i64 {
195 let ctr: *i64 = gv_ctr()
196 gv_head("nx_curveskel_gate -- a curve skeleton from a bare mesh must span the extremities, peel two limbs from a linear volume and three from a T with a fork joint, carry a valid single-rooted tree, reflect thickness, be deterministic and refuse a volumeless sheet" as *u8)
197 sys_mkdir(CG_DIR, CG_MODE_DIR)
198 let vert: *i64 = sys_mmap((1 + CG_VERT_CAP * 3) * 8) as *i64
199 let tris: *i64 = sys_mmap((1 + CG_TRI_CAP * 3) * 8) as *i64
200 let st: *i64 = sys_mmap(16) as *i64
201 let rep: *i64 = sys_mmap(CR_WORDS * 8) as *i64
202 let lp: *i64 = sys_mmap(16) as *i64
203
204 // ---- fixture A: a thin straight prism ----
205 st[0] = 0; st[1] = 0
206 cg_prism(vert, tris, st, CG_R_THIN, 0, CG_LEN, CG_RINGS)
207 let pa: *u8 = "/tmp/nx_curveskel_gate/thin.nxa"
208 let pao: *u8 = "/tmp/nx_curveskel_gate/thin_sk.nxa"
209 let pao2: *u8 = "/tmp/nx_curveskel_gate/thin_sk2.nxa"
210 let wrote: i64 = cg_write(pa, vert, tris, st)
211 gv_check("T1 fixture-reached-the-condition: a 60 mm straight prism was written as an NXA with VERT and TRIS and no SKEL" as *u8, ((wrote > 0) as i64) * ((st[0] == CG_RINGS * CG_AROUND + 2) as i64), ctr)
212 var k: i64 = 0
213 while k < CR_WORDS { rep[k] = 0; k = k + 1 }
214 let rcA: i64 = cs_run(pa, pao, rep)
215 cs_report(rep, pa, pao)
216 let rootThin: i64 = rep[CR_ROOT_R]
217 gv_check("T2 the solve exits 0, seals the prism (interior>0) and writes a SKEL section read back by an independent reader with the same joint count" as *u8, ((rcA == CS_EXIT_OK) as i64) * ((rep[CR_INTERIOR] > 0) as i64), ctr)
218 let bA: i64 = cg_open(pao, lp)
219 let fA: i64 = lp[0]
220 let njA: i64 = cg_nj(bA as *u8, fA)
221 gv_check("T2b the SKEL read back by nxa_find matches the reported joint count and is at least a root plus two tips" as *u8, ((njA == rep[CR_JOINTS]) as i64) * ((njA >= 3) as i64), ctr)
222 gv_check("T3 anti-vacuity LIMB COUNT: a linear volume peels EXACTLY two limbs (both ends) -- a centroid-blob impl would report zero and a single-axis impl one" as *u8, (rep[CR_LIMBS] == 2) as i64, ctr)
223 gv_check("T4 a linear volume forks ONCE at its thick centre: exactly one branch joint (the root carries both limbs), never a limb count masquerading as many forks" as *u8, (rep[CR_BRANCHES] == 1) as i64, ctr)
224 var minz: i64 = CS_TRACE_INF
225 var maxz: i64 = 0 - CS_TRACE_INF
226 var roots: i64 = 0
227 var badpar: i64 = 0
228 var j: i64 = 0
229 while j < njA {
230 let tz: i64 = cg_joint_word(bA as *u8, fA, j, 3)
231 if tz < minz { minz = tz }
232 if tz > maxz { maxz = tz }
233 let par: i64 = cg_joint_word(bA as *u8, fA, j, 0)
234 if par == CG_ROOT { roots = roots + 1 } else { if par < 0 { badpar = badpar + 1 } if par >= njA { badpar = badpar + 1 } }
235 j = j + 1
236 }
237 gv_check("T5 the skeleton SPANS the shape: a joint sits near the z=0 end (below 20% of length) and near the z=60mm end (above 80%) -- it is not a blob at the centroid" as *u8, ((minz < CG_SPAN_LO) as i64) * ((maxz > CG_SPAN_HI) as i64), ctr)
238 gv_check("T6 the tree is valid: EXACTLY one root joint (parent -1) and every other parent is an in-range index (no dangling parent)" as *u8, ((roots == 1) as i64) * ((badpar == 0) as i64), ctr)
239 k = 0
240 while k < CR_WORDS { rep[k] = 0; k = k + 1 }
241 cs_run(pa, pao2, rep)
242 gv_check("T7 two extractions of the same asset are BYTE-IDENTICAL (integer geodesic peeling, deterministic scan order)" as *u8, cg_files_equal(pao, pao2), ctr)
243
244 // ---- fixture B: a FAT straight prism -- root radius must reflect thickness ----
245 st[0] = 0; st[1] = 0
246 cg_prism(vert, tris, st, CG_R_FAT, 0, CG_LEN, CG_RINGS)
247 let pb: *u8 = "/tmp/nx_curveskel_gate/fat.nxa"
248 let pbo: *u8 = "/tmp/nx_curveskel_gate/fat_sk.nxa"
249 cg_write(pb, vert, tris, st)
250 k = 0
251 while k < CR_WORDS { rep[k] = 0; k = k + 1 }
252 let rcB: i64 = cs_run(pb, pbo, rep)
253 cs_report(rep, pb, pbo)
254 let rootFat: i64 = rep[CR_ROOT_R]
255 gv_check("T8 the medial field is REAL, not constant: a 7 mm-wide prism reports a strictly larger root radius than the 3 mm-wide one, and still peels two limbs" as *u8, ((rcB == CS_EXIT_OK) as i64) * ((rootFat > rootThin) as i64) * ((rep[CR_LIMBS] == 2) as i64), ctr)
256
257 // ---- neg-control: a flat sheet has no interior volume -> REFUSE by name ----
258 st[0] = 0; st[1] = 0
259 cg_sheet(vert, tris, st)
260 let pc: *u8 = "/tmp/nx_curveskel_gate/sheet.nxa"
261 let pco: *u8 = "/tmp/nx_curveskel_gate/sheet_sk.nxa"
262 cg_write(pc, vert, tris, st)
263 k = 0
264 while k < CR_WORDS { rep[k] = 0; k = k + 1 }
265 let rcC: i64 = cs_run(pc, pco, rep)
266 gv_check("T9 neg-control-refuses-volumeless-sheet: a flat quad with no interior is refused BY NAME with exit 3, never handed a fabricated skeleton" as *u8, ((rcC == CS_EXIT_REFUSE) as i64) * ((rep[CR_SEALED] == 0) as i64), ctr)
267 gv_check("T10 neg-control DISCRIMINATION: the prism yields a real skeleton (rcA exit 0, joints at least 3) while the sheet is refused (rcC exit 3) -- volume and non-volume are not the same measurement" as *u8, ((rcA == CS_EXIT_OK) as i64) * ((njA >= 3) as i64) * ((rcC == CS_EXIT_REFUSE) as i64), ctr)
268
269 // ---- fixture D: the watertight T (stem UNION crossbar via nx_csg) -- three tips, one fork ----
270 let pd: *u8 = "/tmp/nx_curveskel_gate/tee.nxa"
271 let pdo: *u8 = "/tmp/nx_curveskel_gate/tee_sk.nxa"
272 let tee: *i64 = sys_mmap(16) as *i64
273 let wroteT: i64 = cg_tee(pd, tee)
274 gv_check("T11 fixture-reached-the-condition: nx_csg extracted a watertight T from the union of two box SDFs and it was written as an NXA with VERT and TRIS" as *u8, ((wroteT > 0) as i64) * ((tee[0] > 0) as i64) * ((tee[1] > 0) as i64), ctr)
275 k = 0
276 while k < CR_WORDS { rep[k] = 0; k = k + 1 }
277 let rcD: i64 = cs_run(pd, pdo, rep)
278 cs_report(rep, pd, pdo)
279 let teeLimbs: i64 = rep[CR_LIMBS]
280 let teeBranches: i64 = rep[CR_BRANCHES]
281 let teeJoints: i64 = rep[CR_JOINTS]
282 let teeInterior: i64 = rep[CR_INTERIOR]
283 let teeTrunc: i64 = rep[CR_TRUNC]
284 gv_check("T12 the T seals and solves: exit 0, interior cells exist, no safety cap reached" as *u8, ((rcD == CS_EXIT_OK) as i64) * ((teeInterior > 0) as i64) * ((teeTrunc == 0) as i64), ctr)
285 gv_check("T13 anti-vacuity BRANCH TOPOLOGY: the T peels EXACTLY three limbs (stem foot, left tip, right tip) -- a bar-only impl reports two and an over-peeler more" as *u8, (teeLimbs == CG_TEE_LIMBS) as i64, ctr)
286 gv_check("T14 a real FORK joint exists: at least one joint carries two or more children where the crossbar meets the stem" as *u8, (teeBranches >= 1) as i64, ctr)
287 let bD: i64 = cg_open(pdo, lp)
288 let fD: i64 = lp[0]
289 let njD: i64 = cg_nj(bD as *u8, fD)
290 var minx: i64 = CS_TRACE_INF
291 var maxx: i64 = 0 - CS_TRACE_INF
292 var minzT: i64 = CS_TRACE_INF
293 j = 0
294 while j < njD {
295 let tx: i64 = cg_joint_word(bD as *u8, fD, j, 1)
296 let tz: i64 = cg_joint_word(bD as *u8, fD, j, 3)
297 if tx < minx { minx = tx }
298 if tx > maxx { maxx = tx }
299 if tz < minzT { minzT = tz }
300 j = j + 1
301 }
302 gv_check("T15 the T skeleton REACHES all three arms: a joint beyond 12 mm on the left, one beyond 12 mm on the right, and one below 3 mm at the stem foot" as *u8, ((minx < 0 - CG_TEE_SPAN_X) as i64) * ((maxx > CG_TEE_SPAN_X) as i64) * ((minzT < CG_TEE_FOOT_Z) as i64), ctr)
303
304 gv_values_head()
305 gv_kv("thin_joints" as *u8, njA)
306 gv_kv("thin_limbs" as *u8, 2)
307 gv_kv("thin_root_radius" as *u8, rootThin)
308 gv_kv("fat_root_radius" as *u8, rootFat)
309 gv_kv("thin_min_joint_z" as *u8, minz)
310 gv_kv("thin_max_joint_z" as *u8, maxz)
311 gv_kv("thin_roots" as *u8, roots)
312 gv_kv("thin_bad_parents" as *u8, badpar)
313 gv_kv("tee_verts" as *u8, tee[0])
314 gv_kv("tee_tris" as *u8, tee[1])
315 gv_kv("tee_interior" as *u8, teeInterior)
316 gv_kv("tee_limbs" as *u8, teeLimbs)
317 gv_kv("tee_branches" as *u8, teeBranches)
318 gv_kv("tee_joints" as *u8, teeJoints)
319 gv_kv("tee_min_joint_x" as *u8, minx)
320 gv_kv("tee_max_joint_x" as *u8, maxx)
321 gv_kv("tee_min_joint_z" as *u8, minzT)
322 return gv_verdict("nx_curveskel_gate" as *u8, ctr, "curve-skeleton skeleton-fit: spans the extremities, peels two limbs from a bar and three from a T with a fork, valid single-rooted tree, thickness-scaled root, deterministic, refuses a volumeless sheet" as *u8)
323}