code wiki / (root) / nx_areola_pigment.nx

nx_areola_pigment.nx source

↩ module page · 163 lines · 6822 B

1// nx_areola_pigment.nx -- areola pigment vs reproductive-state validator. 2// 3// Image #46: areolas read brown/dark for a young non-pregnant figure. 4// Real-world ranges: 5// NULLIPAROUS_YOUNG (no births, under 25): light pink/tan 6// NULLIPAROUS_ADULT (no births, 25-40): pink/tan, slightly darker 7// POSTPARTUM (had children): meaningfully darker 8// PREGNANT (currently pregnant): darkest, melanin elevated 9// LACTATING (breastfeeding): dark to very dark 10// 11// Math: 12// pigment_intensity = 255 - max(R,G,B) (darkness, 0=white .. 255=black) 13// hue_warmth = R - G (positive = warm brown) 14// Expected ranges per state (pigment_intensity, hue_warmth): 15// NULLIPAROUS_YOUNG pigment 60..130 warmth 25..70 16// NULLIPAROUS_ADULT pigment 90..160 warmth 35..90 17// POSTPARTUM pigment 130..200 warmth 55..115 18// PREGNANT pigment 160..230 warmth 70..140 19// LACTATING pigment 140..220 warmth 60..125 20// 21// Output flat-array: 22// result[0] = mean_R 23// result[1] = mean_G 24// result[2] = mean_B 25// result[3] = pigment_intensity 26// result[4] = hue_warmth 27// result[5] = pigment_score_q10 (1024 = in declared-state range) 28// result[6] = warmth_score_q10 29// result[7] = composite_q10 30// result[8] = verdict (0=TOO_DARK_FOR_STATE, 1=AMBIGUOUS, 2=MATCH, 3=TOO_LIGHT_FOR_STATE) 31 32// nx_safety_envelope: 33// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 34// sil_target: SIL1 35// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 36// verdict: NOT_YET_EVALUATED 37 38import "nx_syscalls.nx" 39import "nx_image.nx" 40const NX_MAGIC_1024: i64 = 1024 41 42const NX_REPRO_UNKNOWN: i64 = 0 43const NX_REPRO_NULLIPAROUS_YOUNG: i64 = 1 44const NX_REPRO_NULLIPAROUS_ADULT: i64 = 2 45const NX_REPRO_POSTPARTUM: i64 = 3 46const NX_REPRO_PREGNANT: i64 = 4 47const NX_REPRO_LACTATING: i64 = 5 48 49const NX_AREOLA_VERDICT_TOO_DARK: i64 = 0 50const NX_AREOLA_VERDICT_AMBIGUOUS: i64 = 1 51const NX_AREOLA_VERDICT_MATCH: i64 = 2 52const NX_AREOLA_VERDICT_TOO_LIGHT: i64 = 3 53 54const NX_AREOLA_RES_MEAN_R: i64 = 0 55const NX_AREOLA_RES_MEAN_G: i64 = 1 56const NX_AREOLA_RES_MEAN_B: i64 = 2 57const NX_AREOLA_RES_PIGMENT: i64 = 3 58const NX_AREOLA_RES_WARMTH: i64 = 4 59const NX_AREOLA_RES_PIG_SCORE: i64 = 5 60const NX_AREOLA_RES_WARM_SCORE: i64 = 6 61const NX_AREOLA_RES_COMPOSITE: i64 = 7 62const NX_AREOLA_RES_VERDICT: i64 = 8 63const NX_AREOLA_RES_FIELDS: i64 = 9 64 65// Per-state pigment range lookup. min/max written to out args. 66func nx_areola_pigment_range(state: i64, out_min: *i64, out_max: *i64) -> i64 { 67 if state == NX_REPRO_NULLIPAROUS_YOUNG { out_min[0] = 60; out_max[0] = 130; return 0 } 68 if state == NX_REPRO_NULLIPAROUS_ADULT { out_min[0] = 90; out_max[0] = 160; return 0 } 69 if state == NX_REPRO_POSTPARTUM { out_min[0] = 130; out_max[0] = 200; return 0 } 70 if state == NX_REPRO_PREGNANT { out_min[0] = 160; out_max[0] = 230; return 0 } 71 if state == NX_REPRO_LACTATING { out_min[0] = 140; out_max[0] = 220; return 0 } 72 out_min[0] = 60; out_max[0] = 230 73 return 0 74} 75 76func nx_areola_warmth_range(state: i64, out_min: *i64, out_max: *i64) -> i64 { 77 if state == NX_REPRO_NULLIPAROUS_YOUNG { out_min[0] = 25; out_max[0] = 70; return 0 } 78 if state == NX_REPRO_NULLIPAROUS_ADULT { out_min[0] = 35; out_max[0] = 90; return 0 } 79 if state == NX_REPRO_POSTPARTUM { out_min[0] = 55; out_max[0] = 115; return 0 } 80 if state == NX_REPRO_PREGNANT { out_min[0] = 70; out_max[0] = 140; return 0 } 81 if state == NX_REPRO_LACTATING { out_min[0] = 60; out_max[0] = 125; return 0 } 82 out_min[0] = 25; out_max[0] = 140 83 return 0 84} 85 86// Linear in-range score with linear falloff outside. 87func nx_areola_score_in_range(v: i64, vmin: i64, vmax: i64, spread: i64) -> i64 { 88 if v >= vmin { if v <= vmax { return NX_MAGIC_1024 } } 89 var dist: i64 = 0 90 if v < vmin { dist = vmin - v } 91 if v > vmax { dist = v - vmax } 92 if dist >= spread { return 0 } 93 return NX_MAGIC_1024 - ((dist * NX_MAGIC_1024) / spread) 94} 95 96// Measure areola pixels. Caller provides RGB image + areola binary mask 97// (255 = areola pixel) + reproductive state + result array. 98func nx_areola_pigment(rgb: *Image, areola_mask: *Image, 99 reproductive_state: i64, result: *i64) -> i64 { 100 var i: i64 = 0 101 while i < NX_AREOLA_RES_FIELDS { result[i] = 0; i = i + 1 } 102 let w: i64 = rgb.width 103 let h: i64 = rgb.height 104 var sum_r: i64 = 0 105 var sum_g: i64 = 0 106 var sum_b: i64 = 0 107 var count: i64 = 0 108 var y: i64 = 0 109 while y < h { 110 var x: i64 = 0 111 while x < w { 112 let m: i64 = nx_image_get(areola_mask, x, y, 0) 113 if m > 0 { 114 sum_r = sum_r + nx_image_get(rgb, x, y, 0) 115 sum_g = sum_g + nx_image_get(rgb, x, y, 1) 116 sum_b = sum_b + nx_image_get(rgb, x, y, 2) 117 count = count + 1 118 } 119 x = x + 1 120 } 121 y = y + 1 122 } 123 if count == 0 { return 1 } 124 let mean_r: i64 = sum_r / count 125 let mean_g: i64 = sum_g / count 126 let mean_b: i64 = sum_b / count 127 result[NX_AREOLA_RES_MEAN_R] = mean_r 128 result[NX_AREOLA_RES_MEAN_G] = mean_g 129 result[NX_AREOLA_RES_MEAN_B] = mean_b 130 131 var max_rgb: i64 = mean_r 132 if mean_g > max_rgb { max_rgb = mean_g } 133 if mean_b > max_rgb { max_rgb = mean_b } 134 let pigment: i64 = 255 - max_rgb 135 let warmth: i64 = mean_r - mean_g 136 result[NX_AREOLA_RES_PIGMENT] = pigment 137 result[NX_AREOLA_RES_WARMTH] = warmth 138 139 let pmin: *i64 = (sys_mmap(8 + 8)) as *i64 140 let pmax: *i64 = (sys_mmap(8 + 8)) as *i64 141 nx_areola_pigment_range(reproductive_state, pmin, pmax) 142 let wmin: *i64 = (sys_mmap(8 + 8)) as *i64 143 let wmax: *i64 = (sys_mmap(8 + 8)) as *i64 144 nx_areola_warmth_range(reproductive_state, wmin, wmax) 145 146 let pig_score: i64 = nx_areola_score_in_range(pigment, pmin[0], pmax[0], 60) 147 let warm_score: i64 = nx_areola_score_in_range(warmth, wmin[0], wmax[0], 30) 148 result[NX_AREOLA_RES_PIG_SCORE] = pig_score 149 result[NX_AREOLA_RES_WARM_SCORE] = warm_score 150 let composite: i64 = (pig_score + warm_score) / 2 151 result[NX_AREOLA_RES_COMPOSITE] = composite 152 153 var verdict: i64 = NX_AREOLA_VERDICT_AMBIGUOUS 154 if composite >= 768 { 155 verdict = NX_AREOLA_VERDICT_MATCH 156 } else { 157 // Out of range — direction tells us if too dark or too light. 158 if pigment > pmax[0] { verdict = NX_AREOLA_VERDICT_TOO_DARK } 159 else { if pigment < pmin[0] { verdict = NX_AREOLA_VERDICT_TOO_LIGHT } } 160 } 161 result[NX_AREOLA_RES_VERDICT] = verdict 162 return 0 163}