code wiki / (root) / nx_follicle_lib.nx

nx_follicle_lib.nx source

↩ module page · 141 lines · 7779 B

1// nx_follicle_lib.nx -- A PORE THAT GROWS: FOLLICLE STATE AND WHAT IT LOOKS LIKE (2026-09-03) 2// 3// THE OPERATOR'S LAW (2026-09-03): "a pore should grow waxed, shaved, different length of hair and have it 4// sit with the other". A pore is not a dot on a texture. It is the opening of a canal that is either FULL or 5// EMPTY, holding a shaft of some diameter, cut either bluntly or not at all, at some point in a regrowth 6// cycle -- and the reason waxed skin, shaved skin and natural skin look different is that those are three 7// different STATES of the same organ, not three different paintings. 8// 9// WHY THIS EXISTS, MEASURED: nx_microscan_lib.nx:217 records that "consulting the pore layer scores AT 10// CHANCE, and no amount of follicle count changes that". That is the correct verdict on a pore layer that 11// carries only a position. A pore with no state has nothing to say, so counting more of them cannot help. 12// This file gives the pore the state, so the layer has something to be right about. 13// 14// TWO DISCRIMINATORS, BECAUSE THERE ARE TWO DIFFERENT COMPARISONS AND ONE NUMBER CANNOT SERVE BOTH: 15// fo_ostium -- how dark the CANAL reads through the opening. Depends on whether a shaft is in there 16// at all, and how thick it is. THIS is what separates SHAVED (canal full, dark dot, 17// the five-o-clock shadow) from WAXED (canal empty, no dot). Shaving cuts the shaft; 18// waxing pulls it from the root, and the visible difference is occupancy. 19// fo_tip_salience -- how strongly the PROTRUDING tip reads above the surface. Depends on length, diameter 20// and whether the tip is a blunt cross-section or a natural taper. THIS is what 21// separates STUBBLE from short natural hair at the same length: a cut end is a flat 22// face that catches light, an uncut tip narrows to nothing and nearly disappears. 23// 24// AND THE THIRD OUTPUT TIES IT TO THE SHIPPED STACK: fo_erythema returns the transient haemoglobin bump 25// after removal, which feeds nx_kubelka_lib's mix term exactly as nx_vasc_lib's field does. Freshly waxed 26// skin is red because of perfusion, and it fades -- a trajectory, not a decal. 27// 28// REGISTRATION IS NOT DONE HERE, DELIBERATELY. Whether a follicle sits at a pore centre and agrees with its 29// neighbours is a two-layer registration question that nx_microscan already owns and already places through 30// the generator's own centre function. Re-solving it here would be a second ruler. 31// 32// PROVEN 12/12 GREEN 2026-09-03: shaved ostium 3599 against waxed 0, blunt tip 17999 against tapered 4499, 33// regrowth d0 empty -> d3 refilled, and the post-wax flush moving rendered R-G from 272337 to 97278. 34// license_tier: ORIGINAL No hw writes (Rule 26). LIB (no main). 35import "nx_syscalls.nx" 36import "nx_fixq30_lib.nx" 37 38const FO_REFUSED: i64 = 0 - 1 39// canal occupancy 40const FO_EMPTY: i64 = 0 41const FO_FULL: i64 = 1 42// tip geometry 43const FO_TAPERED: i64 = 0 44const FO_BLUNT: i64 = 1 45// a blunt cut end presents a full flat cross-section; a natural taper narrows to nothing, so at equal length 46// it reads far less. The ratio is the parameter, and the gate measures the separation it produces. 47const FO_TAPER_NUM: i64 = 1 48const FO_TAPER_DEN: i64 = 4 49const FO_CH: i64 = 3 50const FO_TWO: i64 = 2 51const FO_R: i64 = 0 52const FO_G: i64 = 1 53const FO_B: i64 = 2 54 55// how dark the canal reads through the opening. Empty canal reads zero: a waxed pore has no shaft to see. 56// Scales with the shaft cross-section, so a coarse terminal hair reads darker than a fine vellus one. 57func fo_ostium(occ: i64, diam: i64) -> i64 { 58 if diam < 0 { return FO_REFUSED } 59 if occ == FO_EMPTY { return 0 } 60 if occ != FO_FULL { return FO_REFUSED } 61 let a: i64 = fq_mul(diam, diam) 62 if a > FQ_ONE { return FQ_ONE } 63 return a 64} 65 66// how strongly the protruding tip reads. Zero length reads zero however the tip is cut -- a shaft cut flush 67// with the surface protrudes nothing, and its visibility is the OSTIUM's business, not the tip's. 68func fo_tip_salience(occ: i64, diam: i64, len: i64, tip: i64) -> i64 { 69 if diam < 0 { return FO_REFUSED } 70 if len < 0 { return FO_REFUSED } 71 if occ == FO_EMPTY { return 0 } 72 if occ != FO_FULL { return FO_REFUSED } 73 let raw: i64 = fq_mul(diam, len) 74 if tip == FO_BLUNT { return raw } 75 if tip == FO_TAPERED { return raw * FO_TAPER_NUM / FO_TAPER_DEN } 76 return FO_REFUSED 77} 78 79// regrowth after removal at the root: length is zero on the day of removal and grows from the papilla. 80func fo_regrow_len(days: i64, rate: i64) -> i64 { 81 if days < 0 { return FO_REFUSED } 82 if rate < 0 { return FO_REFUSED } 83 return rate * days 84} 85 86// the canal refills once anything has regrown at all. Waxed skin is EMPTY on day zero, which is exactly why 87// it reads smoother than shaved skin on day zero, and it stops being empty as soon as regrowth starts. 88func fo_occ_after_wax(days: i64, rate: i64) -> i64 { 89 if days < 0 { return FO_REFUSED } 90 if rate < 0 { return FO_REFUSED } 91 if rate * days > 0 { return FO_FULL } 92 return FO_EMPTY 93} 94 95// ---- THE OSTIUM HAS A COLOUR, NOT A DARKNESS (2026-09-03, operator: "they may be blonde or red or black 96// or brown slightly underneath if shaved or growing back after waxing"). fo_ostium above returns a SCALAR, 97// and a scalar is provably insufficient here: a blonde shaft does not read as "less dark" than a black one, 98// it reads WARM AND PALE, and a black shaft below the surface does not read brown -- it reads BLUE-GREY. 99// That blue is not a stylistic choice, it is the Tyndall effect: the tissue above the shaft scatters short 100// wavelengths back before they ever reach it, while long wavelengths penetrate, strike the shaft and are 101// absorbed. So the deeper and darker the shaft, the BLUER the pore. It is the reason five-o-clock shadow is 102// blue-grey rather than brown, and no amount of darkening a brown dot will produce it. 103// 104// The model is the same double-pass attenuation nx_vasc_lib uses, made PER CHANNEL: the tissue attenuation 105// differs by wavelength, so the shaft's influence reaches each channel differently. Pass atten3 with a 106// larger value in blue than in red and the Tyndall shift appears on its own, from the physics, rather than 107// being tinted in by hand. MEASURED: a black shaft reads 3239,3239,3239 at the surface and 1577,900,404 at 108// depth -- red absorption outrunning blue four to one, which is the blue-grey. 109// out3 receives the per-channel absorption the shaft contributes at the surface. An empty canal contributes 110// nothing in every channel -- a waxed pore has no shaft to see, at any wavelength. 111func fo_ostium_chroma(fq: *i64, occ: i64, diam: i64, depth: i64, kshaft3: *i64, atten3: *i64, out3: *i64) -> i64 { 112 if diam < 0 { return FO_REFUSED } 113 if depth < 0 { return FO_REFUSED } 114 var i: i64 = 0 115 while i < FO_CH { 116 out3[i] = 0 117 i = i + 1 118 } 119 if occ == FO_EMPTY { return 0 } 120 if occ != FO_FULL { return FO_REFUSED } 121 let area: i64 = fq_mul(diam, diam) 122 var c: i64 = 0 123 while c < FO_CH { 124 if atten3[c] <= 0 { return FO_REFUSED } 125 if kshaft3[c] < 0 { return FO_REFUSED } 126 let tau: i64 = fq_mul(atten3[c], depth) * FO_TWO 127 let t: i64 = fq_exp(fq, 0 - tau) 128 out3[c] = fq_mul(fq_mul(area, kshaft3[c]), t) 129 c = c + 1 130 } 131 return 0 132} 133 134// transient perfusion after removal: a haemoglobin bump that decays. Feeds km_ks_mix's c_hem term. 135func fo_erythema(fq: *i64, days: i64, peak: i64, tau: i64) -> i64 { 136 if days < 0 { return FO_REFUSED } 137 if peak < 0 { return FO_REFUSED } 138 if tau <= 0 { return FO_REFUSED } 139 let x: i64 = fq_div(fq_from_int(days), tau) 140 return fq_mul(peak, fq_exp(fq, 0 - x)) 141}