nx_devpaint_lib.nx
buildroot/runtime/nx_devpaint_lib.nx
about
nx_devpaint_lib.nx -- THE DERIVED-SCALE DEVIATION HEATMAP (2026-08-26).
WHY, MEASURED: /exceed/vrm/seed-san.html has been telling visitors that
/exceed/vrm/nishi_seedsan_heatmap.glb is "the INTERACTIVE deviation heatmap (drag-rotate the painted
gap itself)". That file was BYTE-IDENTICAL to nishi_fitted_seedsan.glb (sha256 1ef8ec04..., 9,435,856 B
each): a copy of the unpainted mesh with no deviation on it anywhere. Rule 25 says rewrite it better
rather than filter it out, so this lib is the emitter that makes the claim true.
WHAT IS NEW HERE, versus nx_meshdist's incumbent `paint` verb, which is otherwise a working heatmap:
1. THE SCALE IS DERIVED FROM THE MEASURED DISTRIBUTION, NOT PASSED IN. md_paint takes `band_um` as
an argument and defaults it to 5000 -- a number the caller has to guess, and the published run
guessed 10000. A guessed band is a magic number wearing an argument's clothes: pick it too small
and every triangle saturates magenta, too large and the whole body is green, and BOTH pictures
look authoritative. Here the ramp ceiling is the measured p95 rounded up to the next whole
millimetre, and dp_provenance names that rule so the number travels with the picture.
2. IT MEASURES PER VERTEX, NOT PER CENTROID. md_paint takes the distance from each triangle's
centroid. A centroid can sit on the reference surface while a corner is far off it, so the
incumbent can hide deviation at exactly the sharp features you built a heatmap to find.
3. THE RAMP IS CONTINUOUS, not five hard bands, so a 39 mm and a 41 mm triangle no longer land in
different colours because of where a band edge happened to fall.
COMPOSED, NEVER RE-IMPLEMENTED: the distance itself is nx_mmdev_lib's exact point-to-triangle ruler
(Ericson 5.1.5 + its uniform grid, integer, no sampling). The colour codec is nx_nxmesh_lib's. This
file adds the derivation and the ramp and nothing else -- there is no second distance ruler here.
DECLARED IMPRECISIONS, all three:
(a) NXMSH2 carries ONE colour per TRIANGLE. Three measured corner deviations are therefore reduced
to one colour by MAX -- a triangle is as bad as its worst corner. That is wrong in the direction
of SHOWING deviation rather than hiding it, which is the safe direction for an inspection tool.
Measured on the shipping subject, a triangle spans about 10 mm on a 1.6 m body and renders about
2 px at showcase size, so per-corner interpolation would change little that a viewer can see;
the reduction is a format limit, not a shortcut, and lifting it needs a per-vertex colour source
in nx_mesh2glb, which is named here rather than silently wished for.
(b) Percentiles come from a histogram of MD_HIST_BINS one-tenth-millimetre bins, so they are exact
to the bin. Deviations past the last bin land in it and are counted, so p95 can never be
reported BELOW a deviation that fell off the end.
(c) The scale is per-subject by construction. Two heatmaps are only comparable by reading the
printed bounds, which is exactly why dp_provenance exists and why the CLI prints them.
license_tier: ORIGINAL
dependencies 4 imports · 2 importers
imports: nx_syscalls.nxnx_vecmath.nxnx_nxmesh_lib.nxnx_mmdev_lib.nx
imported by: nx_devpaint.nxnx_devpaint_gate.nx
structs
| none |
consts
| 47 | const DP_R_CORNERS: i64 = 0 // triangle-corners measured: the FULL population, never a sample |
| 48 | const DP_R_TRIS: i64 = 1 |
| 49 | const DP_R_LO: i64 = 2 // ramp floor, ruler units (tenths of a millimetre) |
| 50 | const DP_R_HI: i64 = 3 // ramp ceiling, DERIVED -- see dp_scale |
| 51 | const DP_R_P50: i64 = 4 |
| 52 | const DP_R_P95: i64 = 5 |
| 53 | const DP_R_MAX: i64 = 6 |
| 54 | const DP_R_MEAN: i64 = 7 |
| 55 | const DP_R_SATURATED: i64 = 8 // corners at or above HI: they clamp to the top stop |
| 56 | const DP_R_OVERFLOW: i64 = 9 // corners past the last histogram bin |
| 57 | const DP_R_REFUSED: i64 = 10 // 0 ok, 1 extent bound, 3 empty |
| 58 | const DP_R_N: i64 = 11 |
| 65 | const DP_STOPS: i64 = 5 |
| 66 | const DP_SEGS: i64 = 4 // DERIVED: DP_STOPS - 1 segments between five stops |
| 67 | const DP_PCT_DEN: i64 = 100 |
| 68 | const DP_P50_NUM: i64 = 50 |
| 69 | const DP_P95_NUM: i64 = 95 |
functions
| 71 | func dp_stop_r(i: i64) -> i64 called by 1: dp_ramp |
| 78 | func dp_stop_g(i: i64) -> i64 called by 1: dp_ramp |
| 85 | func dp_stop_b(i: i64) -> i64 called by 1: dp_ramp |
| 96 | func dp_ramp(d: i64, lo: i64, hi: i64, out3: *i64) -> i64 |
| 112 | func dp_res() -> *i64 |
| 120 | func dp_pct(hist: *i64, cnt: i64, num: i64, den: i64) -> i64 called by 1: dp_measure |
| 139 | func dp_measure(ca: *u8, ref: *u8, dev: *i64, res: *i64) -> i64 |
| 189 | func dp_scale(res: *i64) -> i64 |
| 200 | func dp_saturated(dev: *i64, ncorners: i64, hi: i64) -> i64 |
| 211 | func dp_tri_dev(dev: *i64, t: i64) -> i64 |
| 219 | func dp_paint(buf: *u8, tri_base: i64, nt: i64, dev: *i64, lo: i64, hi: i64) -> i64 |