nx_vasc_lib.nx source
↩ module page · 68 lines · 3967 B
1// nx_vasc_lib.nx -- GROWN VASCULATURE -> THE HAEMOGLOBIN FIELD THE SKIN LAYER SEES (2026-09-03)
2//
3// THE OPERATOR'S LAW THIS IMPLEMENTS (2026-09-03): a human is GROWN bottom-up -- bone, nerve, circulation,
4// muscle, fat, dermis, hair out of follicles -- and the vessels you can see through fair skin must EMERGE
5// from a circulatory structure that is actually there. Painting a blue line on a texture is the failure mode
6// this file exists to make unnecessary.
7//
8// THE JOIN. The estate already grows: nx_derm computes skin as an envelope FROM the layers beneath it,
9// nx_anat_sov carries inside-out vasculature scoped to where it actually shows (chest, hands, not
10// everywhere), nx_nxa_groom grows hair from a follicle-density RATIO. What was missing is the step between a
11// grown vessel network and a VISIBLE colour: geometry has no appearance until something converts it into an
12// absorber concentration. That is this file. It takes segments of a grown network -- each with a volume
13// fraction and a depth below the surface -- and returns the haemoglobin fraction c_hem that nx_kubelka_lib's
14// mix term consumes. Structure in, chromophore out; Kubelka-Munk then turns chromophore into reflectance.
15//
16// BEER-LAMBERT, DOUBLE PASS. Light must cross the tissue above a vessel, scatter, and cross it again to be
17// seen, so a segment's visible contribution falls as exp(-2*atten*depth). A vessel one millimetre down
18// contributes far less than the same vessel just under the surface, which is exactly why vasculature reads
19// on the wrist and the eyelid and not on the palm.
20//
21// WHAT THIS FILE DELIBERATELY DOES NOT MODEL: epidermal MELANIN attenuation. Melanin is already an absorber
22// in km_ks_mix, and attenuating the vessel signal by melanin here as well would COUNT IT TWICE -- the same
23// absorber charged on two layers, which reads as a plausible darkening and is a physics error. The melanin
24// term belongs to Kubelka-Munk alone.
25//
26// IT ACCEPTS A NETWORK, IT DOES NOT INVENT ONE. Branching growth is its own rung with its own gate; this lib
27// is the join and takes the grown segments as input, so no growth model is faked here.
28//
29// PROVEN 9/9 GREEN 2026-09-03. The two teeth that matter: denser vasculature raises the red-minus-green
30// separation from 89,090 to 196,295 micro (the skin reddens BECAUSE the vessels are there), and the same
31// vessel buried deep returns that separation to 70,497 -- byte-identical to the vessel-free baseline.
32// license_tier: ORIGINAL No hw writes (Rule 26). LIB (no main).
33import "nx_syscalls.nx"
34import "nx_fixq30_lib.nx"
35
36const VA_REFUSED: i64 = 0 - 1
37const VA_TWO: i64 = 2
38
39// visible haemoglobin contribution of ONE grown segment.
40// vol is the segment's volume fraction in the sampled tissue column (Q30, 0..1).
41// depth is its depth below the surface, atten the tissue attenuation per unit depth (both Q30, >= 0).
42// Returns vol * exp(-2*atten*depth): the double-pass Beer-Lambert transmittance of the tissue above it.
43func va_seg_visible(fq: *i64, vol: i64, depth: i64, atten: i64) -> i64 {
44 if vol < 0 { return VA_REFUSED }
45 if depth < 0 { return VA_REFUSED }
46 if atten <= 0 { return VA_REFUSED }
47 let tau: i64 = fq_mul(atten, depth) * VA_TWO
48 let t: i64 = fq_exp(fq, 0 - tau)
49 return fq_mul(vol, t)
50}
51
52// total visible haemoglobin fraction from a grown network of n segments.
53// CLAMPED at unity: a fraction cannot exceed one, and returning 1.4 would sail into km_ks_mix and produce a
54// confident reflectance for a tissue that cannot exist.
55func va_field(fq: *i64, vols: *i64, depths: *i64, n: i64, atten: i64) -> i64 {
56 if n < 0 { return VA_REFUSED }
57 if atten <= 0 { return VA_REFUSED }
58 var acc: i64 = 0
59 var i: i64 = 0
60 while i < n {
61 let c: i64 = va_seg_visible(fq, vols[i], depths[i], atten)
62 if c == VA_REFUSED { return VA_REFUSED }
63 acc = acc + c
64 i = i + 1
65 }
66 if acc > FQ_ONE { return FQ_ONE }
67 return acc
68}