code wiki / (root) / nx_curveskel_lib.nx

nx_curveskel_lib.nx

buildroot/runtime/nx_curveskel_lib.nx

23720 B476 linesdepth 3pulls 6 transitivereach 11 importersview sourcekind library
docsdependenciesstructsconstsfunctions

about

nx_curveskel_lib.nx -- SOVEREIGN SKELETON-FIT: extract a curve skeleton (an armature) from a BARE unrigged mesh. WHY (2026-09-05). nx_boneheat ships the SKIN half of auto-rig, and its accept rule needs a skeleton to weight against. Blender's Rigify only assembles a CONTROL rig from a meta-rig a human PLACES by hand -- its own manual says it does not attach to a mesh -- and UniRig/RigNet predict a skeleton with a LEARNED model over a training set. Neither is sovereign-from-the-first-byte for an arbitrary mesh. This is the classical route done right: a deterministic integer CURVE SKELETON by geodesic farthest-point peeling, needing no checkpoint and no data. 1. VOXELISE + SEAL the mesh volume -- COMPOSED from nx_boneheat_lib (bh_grid_build/bh_raster/bh_flood), the SAME voxeliser the skin solve uses, so the two halves cannot disagree about the volume. Interior 0 (a sheet with no volume) is REFUSED by name. 2. MEDIAL RADIUS field: multi-source Dijkstra seeded from EVERY surface cell over the domain -> each interior cell's distance to the nearest surface = its local half-thickness. The ROOT is the thickest interior cell. 3. PEEL limbs: seed the current skeleton set, Dijkstra, take the geodesically FARTHEST interior cell as a limb tip, and gradient-descend the geodesic field from the tip back to the skeleton -- that path IS a bone chain. Repeat until the farthest remaining point is closer than the body's own half-thickness (a derived stop, not a magic number: a bump thinner than the torso is not a limb). This is the shortest-path curve-skeleton family (Verroust-Lazarus class), integer and deterministic. 4. SAMPLE joints along each chain spaced by the LOCAL medial radius (thick regions sparse, thin regions dense -- derived from the field), parent-chained, the first joint of a limb parented to the nearest existing joint so branches (a T or a cross) form real fork joints. Emit a SKEL section; nx_boneheat skins it. Safety caps CS_MAX_JOINTS / CS_MAX_LIMBS are NAMED bounds on output size and ANNOUNCE truncation; they are not tuning. UNITS: VERT is 0.01 mm model units (NXA v1); joint positions are cell centres in the same units. license_tier: ORIGINAL

dependencies 3 imports · 6 importers

nx_syscalls.nx nx_nxa.nx nx_boneheat_lib.nx nx_curveskel_lib.nx nx_autorig_mesh.nx nx_autorig_pose_gate.nx nx_curveskel.nx nx_curveskel_gate.nx nx_rigbench_lib.nx nx_rigbench_lib_t21.nx

imports: nx_syscalls.nxnx_nxa.nxnx_boneheat_lib.nx

imported by: nx_autorig_mesh.nxnx_autorig_pose_gate.nxnx_curveskel.nxnx_curveskel_gate.nxnx_rigbench_lib.nxnx_rigbench_lib_t21.nx

structs

none

consts

28const CS_MAX_JOINTS: i64 = 256 // NAMED safety cap on emitted joints; truncation ANNOUNCES, never silent
29const CS_MAX_LIMBS: i64 = 32 // NAMED safety cap on peeled limbs; truncation ANNOUNCES
30const CS_JOINT_WORDS: i64 = 8 // SKEL row: parent tx ty tz qx qy qz qw (shared with nx_boneheat_lib)
31const CS_ROOTPARENT: i64 = 0 - 1
32const CS_TRACE_INF: i64 = 1152921504606846976
33const CS_EXIT_OK: i64 = 0
34const CS_EXIT_USAGE: i64 = 2
35const CS_EXIT_REFUSE: i64 = 3
37const CR_VERTS: i64 = 0
38const CR_TRIS: i64 = 1
39const CR_CELLS: i64 = 2
40const CR_INTERIOR: i64 = 3
41const CR_SEALED: i64 = 4
42const CR_ROOT_R: i64 = 5
43const CR_LIMBS: i64 = 6
44const CR_JOINTS: i64 = 7
45const CR_BRANCHES: i64 = 8
46const CR_MAXPATH: i64 = 9
47const CR_BYTES: i64 = 10
48const CR_TRUNC: i64 = 11
49const CR_NX: i64 = 12
50const CR_NY: i64 = 13
51const CR_NZ: i64 = 14
52const CR_CELL: i64 = 15
53const CR_ROOTCELL: i64 = 16
54const CR_WALLS: i64 = 17 // internal-wall cells bh_flood stripped to INTERIOR before the medial field was built
55const CR_INSIG: i64 = 18 // tips claimed WITHOUT a joint: they reached no farther than the medial radius where they attach
56const CR_PEELS: i64 = 19 // peel iterations (limbs + insignificant tips); CS_MAX_PEELS bounds it and truncation ANNOUNCES
57const CS_MAX_PEELS: i64 = 512 // NAMED safety cap on peel iterations (one Dijkstra over the domain each); announced, never tuning
58const CR_WORDS: i64 = 24

functions

60func cs_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
61func cs_num(v: i64) -> i64
called by 1: cs_kv calls 2: sys_mmapsys_write
74func cs_kv(k: *u8, v: i64) -> i64 { cs_puts(k); cs_num(v); return 0 }
called by 1: cs_report calls 2: cs_putscs_num
76func cs_cell_center(g: *i64, c: i64, axis: i64) -> i64
87func cs_seed_surface(g: *i64) -> i64
called by 1: cs_run_cells calls 1: bh_seed
98func cs_copy_dist(g: *i64, dst: *i64) -> i64
called by 1: cs_run_cells
105func cs_argmax_interior(g: *i64, f: *i64) -> i64
called by 1: cs_run_cells
120func cs_seed_skel(g: *i64, skel: *u8) -> i64
called by 1: cs_run_cells calls 2: bh_is_domainbh_seed
127func cs_trace(g: *i64, d: *i64, dsurf: *i64, start: i64, path: *i64, cap: i64) -> i64
170func cs_nearest_joint(g: *i64, joints: *i64, nj: i64, c: i64) -> i64
called by 1: cs_run_cells calls 1: cs_cell_center
195func cs_local_radius(g: *i64, dsurf: *i64, c: i64) -> i64
229func cs_claim_tube(g: *i64, skel: *u8, dsurf: *i64, path: *i64, plen: i64) -> i64
266func cs_root_cell(g: *i64, dsurf: *i64) -> i64
called by 1: cs_run_cells
297func cs_run(inpath: *u8, outpath: *u8, rep: *i64) -> i64
called by 4: amesh_runmainmainmain calls 1: cs_run_cells
302func cs_run_cells(inpath: *u8, outpath: *u8, rep: *i64, cells_override: i64) -> i64
464func cs_report(rep: *i64, inpath: *u8, outpath: *u8) -> i64
called by 4: mainmainmainmain calls 2: cs_putscs_kv