code wiki / (root) / nx_emotion_authenticity.nx

nx_emotion_authenticity.nx source

↩ module page · 144 lines · 5585 B

1// nx_emotion_authenticity.nx -- Best Acting axis: micro-expression authenticity. 2// 3// Beyond nx_emotion_congruence (declared emotion vs measured emotion), 4// this checks whether the emotion reads AUTHENTIC vs performed. 5// Real emotions involve coordinated multiple muscle groups: 6// - Duchenne smile: zygomaticus + orbicularis oculi (eye-corner crinkle) 7// - Authentic surprise: brow + jaw + eye widening together 8// - Sad: brow inner-corner up, eyelids droop, lip corners down 9// AI-rendered emotions often only animate ONE muscle group (mouth-only 10// smile, eyes flat) producing dead-face / uncanny effect. 11// 12// 5-axis check: 13// 1. eye_crinkle_q10 Duchenne marker for smile 14// 2. brow_movement_q10 brow follows the emotion 15// 3. lip_curve_q10 mouth shape matches emotion 16// 4. jaw_state_q10 jaw open/closed appropriate 17// 5. gaze_engagement_q10 eyes engaged vs glazed 18// 19// Composite scored against declared emotion → authenticity verdict. 20 21// nx_safety_envelope: 22// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 23// sil_target: SIL1 24// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 25// verdict: NOT_YET_EVALUATED 26 27import "nx_syscalls.nx" 28const NX_MAGIC_1024: i64 = 1024 29 30const NX_EAU_RES_EYE_CRINKLE: i64 = 0 31const NX_EAU_RES_BROW: i64 = 1 32const NX_EAU_RES_LIP: i64 = 2 33const NX_EAU_RES_JAW: i64 = 3 34const NX_EAU_RES_GAZE: i64 = 4 35const NX_EAU_RES_COMPOSITE: i64 = 5 36const NX_EAU_RES_VERDICT: i64 = 6 37const NX_EAU_RES_FAIL_AXIS: i64 = 7 38const NX_EAU_RES_FIELDS: i64 = 8 39 40const NX_EAU_V_DEAD_FACE: i64 = 0 41const NX_EAU_V_PERFORMED: i64 = 1 42const NX_EAU_V_BELIEVABLE: i64 = 2 43const NX_EAU_V_AUTHENTIC: i64 = 3 44 45const NX_EAU_AXIS_EYE_CRINKLE: i64 = 0 46const NX_EAU_AXIS_BROW: i64 = 1 47const NX_EAU_AXIS_LIP: i64 = 2 48const NX_EAU_AXIS_JAW: i64 = 3 49const NX_EAU_AXIS_GAZE: i64 = 4 50 51// Emotion ids (subset matching emotion_congruence taxonomy). 52const NX_EAU_EMO_NEUTRAL: i64 = 0 53const NX_EAU_EMO_HAPPY: i64 = 1 54const NX_EAU_EMO_SAD: i64 = 2 55const NX_EAU_EMO_SURPRISED: i64 = 3 56const NX_EAU_EMO_AROUSED: i64 = 4 57const NX_EAU_EMO_PENSIVE: i64 = 5 58 59// For each declared emotion, return target Q10 values for each axis. 60// out[0]=eye, out[1]=brow, out[2]=lip, out[3]=jaw, out[4]=gaze. 61func nx_eau_targets(emotion: i64, out: *i64) -> i64 { 62 if emotion == NX_EAU_EMO_HAPPY { 63 out[0] = 819; out[1] = 614; out[2] = 870; out[3] = 614; out[4] = 870 64 return 0 65 } 66 if emotion == NX_EAU_EMO_SAD { 67 out[0] = 256; out[1] = 768; out[2] = 870; out[3] = 410; out[4] = 256 68 return 0 69 } 70 if emotion == NX_EAU_EMO_SURPRISED { 71 out[0] = 819; out[1] = 920; out[2] = 614; out[3] = 870; out[4] = 920 72 return 0 73 } 74 if emotion == NX_EAU_EMO_AROUSED { 75 out[0] = 614; out[1] = 410; out[2] = 819; out[3] = 614; out[4] = 870 76 return 0 77 } 78 if emotion == NX_EAU_EMO_PENSIVE { 79 out[0] = 410; out[1] = 410; out[2] = 410; out[3] = 256; out[4] = 410 80 return 0 81 } 82 // Neutral default 83 out[0] = 410; out[1] = 410; out[2] = 410; out[3] = 410; out[4] = 614 84 return 0 85} 86 87// Score authenticity: how close measured axis values are to target for declared emotion. 88func nx_emotion_authenticity(declared_emotion: i64, 89 eye_crinkle_q10: i64, brow_q10: i64, 90 lip_q10: i64, jaw_q10: i64, gaze_q10: i64, 91 result: *i64) -> i64 { 92 var i: i64 = 0 93 while i < NX_EAU_RES_FIELDS { result[i] = 0; i = i + 1 } 94 95 let targets: *i64 = (sys_mmap(5 * 8 + 16)) as *i64 96 nx_eau_targets(declared_emotion, targets) 97 98 // Per-axis score = 1024 - |measured - target| / 1. 99 var d0: i64 = eye_crinkle_q10 - targets[0] 100 var d1: i64 = brow_q10 - targets[1] 101 var d2: i64 = lip_q10 - targets[2] 102 var d3: i64 = jaw_q10 - targets[3] 103 var d4: i64 = gaze_q10 - targets[4] 104 if d0 < 0 { d0 = -d0 } 105 if d1 < 0 { d1 = -d1 } 106 if d2 < 0 { d2 = -d2 } 107 if d3 < 0 { d3 = -d3 } 108 if d4 < 0 { d4 = -d4 } 109 var s0: i64 = NX_MAGIC_1024 - d0 110 var s1: i64 = NX_MAGIC_1024 - d1 111 var s2: i64 = NX_MAGIC_1024 - d2 112 var s3: i64 = NX_MAGIC_1024 - d3 113 var s4: i64 = NX_MAGIC_1024 - d4 114 if s0 < 0 { s0 = 0 } 115 if s1 < 0 { s1 = 0 } 116 if s2 < 0 { s2 = 0 } 117 if s3 < 0 { s3 = 0 } 118 if s4 < 0 { s4 = 0 } 119 120 result[NX_EAU_RES_EYE_CRINKLE] = s0 121 result[NX_EAU_RES_BROW] = s1 122 result[NX_EAU_RES_LIP] = s2 123 result[NX_EAU_RES_JAW] = s3 124 result[NX_EAU_RES_GAZE] = s4 125 126 // Composite: weighted average. Eye crinkle + gaze are heaviest (Duchenne markers). 127 let composite: i64 = (s0 * 25 + s1 * 15 + s2 * 20 + s3 * 15 + s4 * 25) / 100 128 result[NX_EAU_RES_COMPOSITE] = composite 129 130 var verdict: i64 = NX_EAU_V_DEAD_FACE 131 if composite >= 410 { verdict = NX_EAU_V_PERFORMED } 132 if composite >= 614 { verdict = NX_EAU_V_BELIEVABLE } 133 if composite >= 819 { verdict = NX_EAU_V_AUTHENTIC } 134 result[NX_EAU_RES_VERDICT] = verdict 135 136 var min_s: i64 = s0 137 var min_a: i64 = NX_EAU_AXIS_EYE_CRINKLE 138 if s1 < min_s { min_s = s1; min_a = NX_EAU_AXIS_BROW } 139 if s2 < min_s { min_s = s2; min_a = NX_EAU_AXIS_LIP } 140 if s3 < min_s { min_s = s3; min_a = NX_EAU_AXIS_JAW } 141 if s4 < min_s { min_s = s4; min_a = NX_EAU_AXIS_GAZE } 142 result[NX_EAU_RES_FAIL_AXIS] = min_a 143 return 0 144}