nx_xport_lib.nx
buildroot/runtime/nx_xport_lib.nx
about
nx_xport_lib.nx -- DC12: ONE AUTHORED ASSET INTO FOUR CONSUMERS, MEASURED BY ONE RULER.
WHY THIS EXISTS: the /compare/dcc board claims a single authored asset serves storefront, game, VR and
clinical use. Today that is a PLAN. Nothing measures it, so a consumer could silently receive a
decimated, re-scaled or re-wound mesh and no instrument in the estate would say so. This lib turns the
claim into a number per consumer.
THE RULER IS md_measure AND THERE IS NOT A SECOND ONE. nx_mmdev_lib already measures exact
point-to-triangle deviation in tenths of a millimetre and is gate-proven to read a known 5.0 mm lift as
5.0 mm and a self-compare as zero. A new deviation metric on this board would be a duplicate ruler, i.e.
a defect. Everything below COMPOSES it. The exporters are composed too -- write_glb and write_obj are
called, never re-implemented -- so what is measured is the artifact the real storefront lane ships.
WHAT IS MEASURED, PER CONSUMER: the artifact is written, then the geometry is RECOVERED BACK OUT OF THE
ARTIFACT BYTES (not from an in-memory copy that never crossed the format) and measured against the
source in BOTH directions. Deviation alone is not sufficient and this lib says so in code: a mesh
mirrored about its own centre has ZERO point-to-triangle deviation and is still wrong, so orientation
(the sign of the signed volume) and scale (per-axis AABB extent) are separate axes, not decoration.
THE BOUND IS DERIVED, NEVER PICKED. Every consumer holds coordinates in its own integer unit. A round
trip through a unit of U integer steps per metre, from the ruler's NM_UNIT_PER_M steps per metre, spends
at most half a step in each direction, and NOTHING AT ALL when U is an integer multiple of the ruler's
unit (the forward map is a multiplication and the inverse an exact division). The glTF path adds a
second, computed term: write_glb encodes each integer coordinate with gl_i2f32, which TRUNCATES the
mantissa, so the loss is the float32 ulp AT THIS ASSET'S OWN COORDINATE MAGNITUDE -- computed from the
mesh, zero while the magnitude fits the 24-bit significand, and rising by itself if it ever does not.
ABSTENTION IS NOT A SCORE OF ZERO. A consumer that cannot be measured reports UNREACHED with a named
reason. A consumer that silently reads zero deviation is indistinguishable from one that was never
measured, which is the whole failure this rung exists to prevent.
DECLARED IMPRECISIONS, so no reader takes them as exact:
(1) The volume-magnitude tolerance uses the AABB as a proxy for surface area. A convoluted mesh has
more area per unit volume than its box, so on such a mesh that tolerance is OPTIMISTIC. It is a
SUPPORTING axis only; the load-bearing orientation test is the volume SIGN, which needs no
tolerance and cannot be optimistic.
(2) The signed volume is the enclosed volume only for a CLOSED mesh. For an open mesh it is still a
valid like-for-like shape statistic, because source and recovered are evaluated with the SAME
origin (the source AABB minimum), but it is not a volume.
(3) md_measure's own declared imprecisions carry through: its mean is valence-weighted and its p95 is
dependencies 8 imports · 1 importers
imports: nx_syscalls.nxnx_vecmath.nxnx_nxmesh_lib.nxnx_mmdev_lib.nxnx_nxa.nxnx_gltf_export.nxnx_obj_export.nxnx_ge_str_lib.nx
imported by: nx_xport_gate.nx
structs
| none |
consts
| 69 | const XP_GL_UNIT_PER_M: i64 = 1024 |
| 72 | const XP_NXA_UNIT_PER_M: i64 = 100000 |
| 74 | const XP_MODE_0644: i64 = 420 |
| 75 | const XP_PATH_CAP: i64 = 512 |
| 76 | const XP_PERMIL: i64 = 1000 |
| 79 | const XP_I64_SAFE: i64 = 4611686018427387904 |
| 82 | const XP_GLB_MAGIC: i64 = 1179937895 // "glTF" little-endian |
| 83 | const XP_GLB_VER: i64 = 2 |
| 84 | const XP_GLB_JSONLEN_OFF: i64 = 12 |
| 85 | const XP_GLB_JSON_OFF: i64 = 20 // 12 byte header + 8 byte chunk header |
| 86 | const XP_GLB_CHUNKHDR: i64 = 8 |
| 87 | const XP_GLB_VEC3_BYTES: i64 = 12 |
| 88 | const XP_GLB_U32_BYTES: i64 = 4 |
| 90 | const XP_GLB_IDX_START_MUL: i64 = 24 |
| 94 | const XP_GLB_SCALE_LIT: *u8 = "\"scale\":[0.0009765625,0.0009765625,0.0009765625]" |
| 95 | const XP_GLB_COUNT_KEY: *u8 = "\"count\":" |
| 98 | const XP_NXA_SECTIONS: i64 = 2 // VERT + TRIS |
| 99 | const XP_NXA_TOC_BYTE: i64 = 32 |
| 100 | const XP_NXA_TOC_ROW_WORDS: i64 = 4 // tag, byte offset, word length, payload check |
| 105 | const XP_HASH_A: i64 = 73856093 |
| 106 | const XP_HASH_B: i64 = 19349663 |
| 107 | const XP_HASH_C: i64 = 83492791 |
| 109 | const XP_HASH_LOAD_INV: i64 = 2 |
| 112 | const XP_VOL_PERMIL_FLOOR: i64 = 1 |
| 115 | const XP_C_STORE_GLB: i64 = 0 |
| 116 | const XP_C_STORE_OBJ: i64 = 1 |
| 117 | const XP_C_GAME_NXA: i64 = 2 |
| 118 | const XP_C_VR_FRAME: i64 = 3 |
| 119 | const XP_C_CLINICAL: i64 = 4 |
| 120 | const XP_C_N: i64 = 5 |
| 123 | const XP_R_REACHED: i64 = 0 // 1 iff an artifact was produced for this consumer |
| 124 | const XP_R_WHY: i64 = 1 // 0 when the verdict is measured, else the reason it is not |
| 125 | const XP_R_BYTES: i64 = 2 |
| 126 | const XP_R_TRIS_SRC: i64 = 3 |
| 127 | const XP_R_TRIS_REC: i64 = 4 |
| 128 | const XP_R_MEAN_AB: i64 = 5 // source vertices -> recovered surface |
| 129 | const XP_R_P95_AB: i64 = 6 |
| 130 | const XP_R_MAX_AB: i64 = 7 |
| 131 | const XP_R_MEAN_BA: i64 = 8 // recovered vertices -> source surface |
| 132 | const XP_R_P95_BA: i64 = 9 |
| 133 | const XP_R_MAX_BA: i64 = 10 |
| 134 | const XP_R_VOLSIGN_OK: i64 = 11 |
| 135 | const XP_R_VOL_PERMIL: i64 = 12 |
| 136 | const XP_R_VOL_PERMIL_BOUND: i64 = 13 // -1 = UNMEASURED (degenerate AABB), never a silent zero |
| 137 | const XP_R_EXT_DELTA: i64 = 14 |
| 138 | const XP_R_EXT_BOUND: i64 = 15 |
| 139 | const XP_R_BOUND: i64 = 16 // the derived 3D deviation bound, in tenths of a millimetre |
| 140 | const XP_R_AXIS: i64 = 17 // per-axis bound = quantisation term + float32 term |
| 141 | const XP_R_AXISQ: i64 = 18 |
| 142 | const XP_R_AXISF: i64 = 19 |
| 143 | const XP_R_VERTS: i64 = 20 |
| 144 | const XP_R_PASS: i64 = 21 |
| 145 | const XP_R_N: i64 = 22 |
| 148 | const XP_WHY_OK: i64 = 0 |
| 149 | const XP_WHY_NO_MESH_PARAM: i64 = 1 // the consumer's entry point takes no mesh at all |
| 150 | const XP_WHY_RASTER_ONLY: i64 = 2 // the consumer's artifact is an image: geometry is not in it |
| 151 | const XP_WHY_ARTIFACT_MISSING: i64 = 3 |
| 152 | const XP_WHY_ARTIFACT_MALFORMED: i64 = 4 |
| 153 | const XP_WHY_SCALE_UNDECLARED: i64 = 5 // refused rather than mis-scaled |
| 154 | const XP_WHY_RULER_REFUSED: i64 = 6 // md_measure itself declined; its code is in the ruler's res |
| 155 | const XP_WHY_COUNT_MISMATCH: i64 = 7 |
| 157 | const XP_F_GLB: *u8 = "xport.glb" |
| 158 | const XP_F_OBJ: *u8 = "xport.obj" |
| 159 | const XP_F_NXA: *u8 = "xport.nxa" |
| 160 | const XP_F_MSH: *u8 = "xport.nxmsh2" |
| 169 | const XP_VR_SRC: *u8 = "buildroot/runtime/_hdl_build/nx_vrframe.nx" |
| 170 | const XP_VR_SIG: *u8 = "func vf_build(distort: i64, outpath: *u8) -> i64" |
| 171 | const XP_VR_RASTER: *u8 = "write_png(combo" |
functions
| 173 | func xp_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 174 | func xp_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } |
| 176 | func xp_res() -> *i64 |
| 182 | func xp_rs(res: *i64, c: i64, slot: i64, v: i64) -> i64 { res[c*XP_R_N + slot] = v; return 0 } |
| 183 | func xp_rg(res: *i64, c: i64, slot: i64) -> i64 { return res[c*XP_R_N + slot] } |
| 185 | func xp_name(c: i64) -> *u8 |
| 193 | func xp_file_of(c: i64) -> *u8 |
| 201 | func xp_unit_of(c: i64) -> i64 |
| 211 | func xp_uses_f32(c: i64) -> i64 |
| 218 | func xp_q(v: i64, num: i64, den: i64) -> i64 |
| 235 | func xp_axis_bound(dst_per_m: i64) -> i64 |
| 246 | func xp_f32_axis_term(maxabs_dst: i64, dst_per_m: i64) -> i64 |
| 262 | func xp_bound3d(axis_nm: i64) -> i64 |
| 269 | func xp_aabb(m: *u8, out6: *i64) -> i64 |
| 293 | func xp_extent(bb: *i64) -> i64 |
| 300 | func xp_maxabs(m: *u8) -> i64 |
| 316 | func xp_ext_delta(a: *u8, b: *u8) -> i64 |
| 334 | func xp_vol_div(m: *u8, bb: *i64) -> i64 |
| 348 | func xp_vol6(m: *u8, org: *i64, div: i64) -> i64 |
| 378 | func xp_vol_permil_bound(bb: *i64, bound3d: i64) -> i64 |
| 395 | func xp_weld(m: *u8, dst_per_m: i64, vbuf: *i64, fbuf: *i64) -> i64 |
| 446 | func xp_soup_from_indexed(vbuf: *i64, fbuf: *i64, nv: i64, nf: i64, src_per_m: i64, out: *u8) -> i64 |
| 472 | func xp_path(dir: *u8, name: *u8, out: *u8) -> i64 |
| 483 | func xp_write_bytes(b: *u8, n: i64, path: *u8) -> i64 |
| 493 | func xp_nxa_write(vbuf: *i64, fbuf: *i64, nv: i64, nf: i64, path: *u8) -> i64 |
| 523 | func xp_atoi(b: *u8, o: i64, n: i64) -> i64 |
| 541 | func xp_rec_glb(b: *u8, n: i64, out: *i64) -> *u8 |
| 588 | func xp_line_end(b: *u8, ls: i64, n: i64) -> i64 |
| 600 | func xp_next_int(b: *u8, cur: *i64, end: i64, ok: *i64) -> i64 |
| 630 | func xp_skip_tok(b: *u8, cur: *i64, end: i64) -> i64 |
| 642 | func xp_rec_obj(b: *u8, n: i64, out: *i64) -> *u8 |
| 705 | func xp_rec_nxa(b: *u8, n: i64, out: *i64) -> *u8 |
| 728 | func xp_rec_msh(b: *u8, n: i64, out: *i64) -> *u8 |
| 740 | func xp_recover(c: i64, b: *u8, n: i64, out: *i64) -> *u8 |
| 755 | func xp_vr_still_unreached(out2: *i64) -> i64 |
| 772 | func xp_emit_all(m: *u8, dir: *u8, res: *i64) -> i64 |
| 826 | func xp_consumers(m: *u8, dir: *u8, res: *i64) -> i64 |