code wiki / (root) / nx_mannequin_index.nx

nx_mannequin_index.nx source

↩ module page · 144 lines · 5309 B

1// nx_mannequin_index.nx -- composite "is this a living person or a mannequin" verdict. 2// 3// Aggregates the per-axis detector scores into a single Q10 composite + 4// sealed verdict. Designed to grow as new mannequin-failure detectors 5// land (skin micro-texture, pressure deformation, joint articulation, 6// lash transition); today scores subsurface scatter + specular falloff 7// + the existing nx_skin_texture PLASTIC_SHEEN signal. 8// 9// Composite formula (each axis 0..1024, weighted average): 10// composite = (sss_score * w_sss + spec_score * w_spec + texture_score * w_tex) / sum_w 11// where higher composite = more LIVING_PERSON, lower = more MANNEQUIN. 12// 13// Sealed verdict: 14// OBVIOUS_PLASTIC composite < 256 15// MANNEQUIN_FEEL 256..512 16// SLIGHTLY_OFF 512..768 17// LIVING_PERSON >= 768 18// 19// Output flat-array: 20// result[0] = composite_q10 21// result[1] = verdict 22// result[2] = sss_score_q10 23// result[3] = spec_score_q10 24// result[4] = texture_score_q10 25// result[5] = n_axes_contributing 26 27// nx_safety_envelope: 28// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 29// sil_target: SIL1 30// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 31// verdict: NOT_YET_EVALUATED 32 33import "nx_syscalls.nx" 34import "nx_subsurface_warmth.nx" 35import "nx_specular_falloff.nx" 36 37const NX_MANN_RES_COMPOSITE: i64 = 0 38const NX_MANN_RES_VERDICT: i64 = 1 39const NX_MANN_RES_SSS: i64 = 2 40const NX_MANN_RES_SPEC: i64 = 3 41const NX_MANN_RES_TEXTURE: i64 = 4 42const NX_MANN_RES_AXES: i64 = 5 43const NX_MANN_RES_FIELDS: i64 = 6 44 45const NX_MANN_OBVIOUS_PLASTIC: i64 = 0 46const NX_MANN_MANNEQUIN_FEEL: i64 = 1 47const NX_MANN_SLIGHTLY_OFF: i64 = 2 48const NX_MANN_LIVING_PERSON: i64 = 3 49 50// Axis weights (relative, will be normalized). 51const NX_MANN_W_SSS: i64 = 3 // subsurface scatter is high signal 52const NX_MANN_W_SPEC: i64 = 2 // specular falloff 53const NX_MANN_W_TEXTURE: i64 = 2 // skin micro-texture (from nx_skin_texture) 54 55// Aggregate per-axis scores into mannequin verdict. 56// Caller passes per-axis Q10 scores (use -1 to mark "not measured"). 57func nx_mannequin_score(sss_score_q10: i64, 58 spec_score_q10: i64, 59 texture_score_q10: i64, 60 result: *i64) -> i64 { 61 var i: i64 = 0 62 while i < NX_MANN_RES_FIELDS { result[i] = 0; i = i + 1 } 63 result[NX_MANN_RES_SSS] = sss_score_q10 64 result[NX_MANN_RES_SPEC] = spec_score_q10 65 result[NX_MANN_RES_TEXTURE] = texture_score_q10 66 67 var sum_score: i64 = 0 68 var sum_w: i64 = 0 69 var axes: i64 = 0 70 if sss_score_q10 >= 0 { 71 sum_score = sum_score + sss_score_q10 * NX_MANN_W_SSS 72 sum_w = sum_w + NX_MANN_W_SSS 73 axes = axes + 1 74 } 75 if spec_score_q10 >= 0 { 76 sum_score = sum_score + spec_score_q10 * NX_MANN_W_SPEC 77 sum_w = sum_w + NX_MANN_W_SPEC 78 axes = axes + 1 79 } 80 if texture_score_q10 >= 0 { 81 sum_score = sum_score + texture_score_q10 * NX_MANN_W_TEXTURE 82 sum_w = sum_w + NX_MANN_W_TEXTURE 83 axes = axes + 1 84 } 85 result[NX_MANN_RES_AXES] = axes 86 87 if sum_w == 0 { 88 result[NX_MANN_RES_COMPOSITE] = 0 89 result[NX_MANN_RES_VERDICT] = NX_MANN_OBVIOUS_PLASTIC 90 return 0 91 } 92 let composite: i64 = sum_score / sum_w 93 result[NX_MANN_RES_COMPOSITE] = composite 94 95 var verdict: i64 = NX_MANN_OBVIOUS_PLASTIC 96 if composite >= 768 { verdict = NX_MANN_LIVING_PERSON } 97 else { 98 if composite >= 512 { verdict = NX_MANN_SLIGHTLY_OFF } 99 else { 100 if composite >= 256 { verdict = NX_MANN_MANNEQUIN_FEEL } 101 } 102 } 103 result[NX_MANN_RES_VERDICT] = verdict 104 return 0 105} 106 107// Corrective phrases that push the generator toward a living-person render. 108// Returns a count; phrases written into the out-array as ints (caller looks 109// them up by id since we don't have first-class string arrays). 110const NX_MANN_PHRASE_NATURAL_SKIN_TEXTURE: i64 = 1 111const NX_MANN_PHRASE_VISIBLE_PORES: i64 = 2 112const NX_MANN_PHRASE_SSS_WARMTH: i64 = 3 113const NX_MANN_PHRASE_SOFT_SPECULAR: i64 = 4 114const NX_MANN_PHRASE_PRESSURE_DEFORMATION: i64 = 5 115const NX_MANN_PHRASE_TENDON_SHADOWS: i64 = 6 116 117func nx_mannequin_corrective_phrases(verdict: i64, sss_score: i64, 118 spec_score: i64, texture_score: i64, 119 phrase_ids: *i64) -> i64 { 120 var n: i64 = 0 121 if verdict == NX_MANN_LIVING_PERSON { return 0 } 122 123 if texture_score < 512 { 124 phrase_ids[n] = NX_MANN_PHRASE_NATURAL_SKIN_TEXTURE 125 n = n + 1 126 phrase_ids[n] = NX_MANN_PHRASE_VISIBLE_PORES 127 n = n + 1 128 } 129 if sss_score < 512 { 130 phrase_ids[n] = NX_MANN_PHRASE_SSS_WARMTH 131 n = n + 1 132 } 133 if spec_score < 512 { 134 phrase_ids[n] = NX_MANN_PHRASE_SOFT_SPECULAR 135 n = n + 1 136 } 137 // Pressure + tendon detectors not yet shipped — emit phrases anyway as 138 // preventive hints (they don't conflict with anything in the registry). 139 phrase_ids[n] = NX_MANN_PHRASE_PRESSURE_DEFORMATION 140 n = n + 1 141 phrase_ids[n] = NX_MANN_PHRASE_TENDON_SHADOWS 142 n = n + 1 143 return n 144}