nx_pressure_deformation.nx source
↩ module page · 144 lines · 5621 B
1// nx_pressure_deformation.nx -- skin-on-skin contact tissue compression check.
2//
3// Mannequin failure marker from image #45: hand cupping breast but the
4// breast tissue doesn't displace where the hand presses against it.
5// Real bodies show measurable compression at contact zones; mannequins
6// pass through each other or show rigid edges.
7//
8// Math:
9// Given primary region bbox (breast) + contact region bbox (where
10// arm/hand crosses), the EXPECTED skin-mask area inside the contact
11// region is the primary region's local density × contact area.
12// Measured area / expected area = compression ratio.
13//
14// < 0.7 ratio → strong compression (tissue displaced) NATURAL
15// 0.7..0.85 → mild compression AMBIGUOUS
16// > 0.85 → little/no compression MANNEQUIN_RIGID
17//
18// Also measure contact-boundary roughness: count skin-mask transitions
19// in horizontal slices through the contact band. Real flesh contacts
20// produce many transitions (wavy compression boundary); mannequins
21// produce a clean straight boundary.
22
23// nx_safety_envelope:
24// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
25// sil_target: SIL1
26// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
27// verdict: NOT_YET_EVALUATED
28
29import "nx_syscalls.nx"
30import "nx_image.nx"
31const NX_MAGIC_1024: i64 = 1024
32
33const NX_PRESS_RES_PRIMARY_AREA: i64 = 0
34const NX_PRESS_RES_CONTACT_AREA: i64 = 1
35const NX_PRESS_RES_EXPECTED: i64 = 2
36const NX_PRESS_RES_COMPRESSION_Q10: i64 = 3 // measured/expected in Q10
37const NX_PRESS_RES_BOUNDARY_TRANS: i64 = 4 // total horizontal transitions
38const NX_PRESS_RES_VERDICT: i64 = 5
39const NX_PRESS_RES_FIELDS: i64 = 6
40
41const NX_PRESS_VERDICT_MANNEQUIN_RIGID: i64 = 0
42const NX_PRESS_VERDICT_AMBIGUOUS: i64 = 1
43const NX_PRESS_VERDICT_NATURAL: i64 = 2
44
45const NX_PRESS_COMPRESS_NATURAL_Q10: i64 = 717 // ≤ 0.70 = natural
46const NX_PRESS_COMPRESS_AMBIG_Q10: i64 = 870 // 0.70..0.85 = ambiguous
47
48// Count skin-mask pixels inside a bbox.
49func nx_press_count_in_bbox(mask: *Image, x0: i64, y0: i64,
50 x1: i64, y1: i64) -> i64 {
51 var n: i64 = 0
52 var y: i64 = y0
53 while y <= y1 {
54 if y >= 0 { if y < mask.height {
55 var x: i64 = x0
56 while x <= x1 {
57 if x >= 0 { if x < mask.width {
58 if nx_image_get(mask, x, y, 0) > 0 { n = n + 1 }
59 } }
60 x = x + 1
61 }
62 } }
63 y = y + 1
64 }
65 return n
66}
67
68// Count horizontal mask transitions (0->1 or 1->0) within bbox.
69func nx_press_count_transitions(mask: *Image, x0: i64, y0: i64,
70 x1: i64, y1: i64) -> i64 {
71 var total: i64 = 0
72 var y: i64 = y0
73 while y <= y1 {
74 if y >= 0 { if y < mask.height {
75 var prev: i64 = 0
76 var x: i64 = x0
77 while x <= x1 {
78 if x >= 0 { if x < mask.width {
79 var cur: i64 = 0
80 if nx_image_get(mask, x, y, 0) > 0 { cur = 1 }
81 if cur != prev { total = total + 1 }
82 prev = cur
83 } }
84 x = x + 1
85 }
86 } }
87 y = y + 1
88 }
89 return total
90}
91
92// Score deformation at contact zone.
93// bbox layout (8 entries):
94// [0..3] primary region (breast) x0,y0,x1,y1
95// [4..7] contact region (overlap) x0,y0,x1,y1
96func nx_pressure_deformation(mask: *Image, bbox: *i64, result: *i64) -> i64 {
97 var i: i64 = 0
98 while i < NX_PRESS_RES_FIELDS { result[i] = 0; i = i + 1 }
99
100 let p_x0: i64 = bbox[0]; let p_y0: i64 = bbox[1]
101 let p_x1: i64 = bbox[2]; let p_y1: i64 = bbox[3]
102 let c_x0: i64 = bbox[4]; let c_y0: i64 = bbox[5]
103 let c_x1: i64 = bbox[6]; let c_y1: i64 = bbox[7]
104
105 let primary_w: i64 = p_x1 - p_x0 + 1
106 let primary_h: i64 = p_y1 - p_y0 + 1
107 let contact_w: i64 = c_x1 - c_x0 + 1
108 let contact_h: i64 = c_y1 - c_y0 + 1
109 if primary_w <= 0 { return 1 }
110 if primary_h <= 0 { return 2 }
111 if contact_w <= 0 { return 3 }
112 if contact_h <= 0 { return 4 }
113
114 let primary_area: i64 = nx_press_count_in_bbox(mask, p_x0, p_y0, p_x1, p_y1)
115 let contact_area: i64 = nx_press_count_in_bbox(mask, c_x0, c_y0, c_x1, c_y1)
116 result[NX_PRESS_RES_PRIMARY_AREA] = primary_area
117 result[NX_PRESS_RES_CONTACT_AREA] = contact_area
118
119 let primary_bbox_area: i64 = primary_w * primary_h
120 if primary_bbox_area == 0 { return 5 }
121 let primary_density_q10: i64 = (primary_area * NX_MAGIC_1024) / primary_bbox_area
122 let contact_bbox_area: i64 = contact_w * contact_h
123 let expected: i64 = (primary_density_q10 * contact_bbox_area) / NX_MAGIC_1024
124 result[NX_PRESS_RES_EXPECTED] = expected
125
126 var compression_q10: i64 = NX_MAGIC_1024
127 if expected > 0 { compression_q10 = (contact_area * NX_MAGIC_1024) / expected }
128 if compression_q10 > NX_MAGIC_1024 { compression_q10 = NX_MAGIC_1024 }
129 result[NX_PRESS_RES_COMPRESSION_Q10] = compression_q10
130
131 let trans: i64 = nx_press_count_transitions(mask, c_x0, c_y0, c_x1, c_y1)
132 result[NX_PRESS_RES_BOUNDARY_TRANS] = trans
133
134 var verdict: i64 = NX_PRESS_VERDICT_MANNEQUIN_RIGID
135 if compression_q10 <= NX_PRESS_COMPRESS_NATURAL_Q10 {
136 verdict = NX_PRESS_VERDICT_NATURAL
137 } else {
138 if compression_q10 <= NX_PRESS_COMPRESS_AMBIG_Q10 {
139 verdict = NX_PRESS_VERDICT_AMBIGUOUS
140 }
141 }
142 result[NX_PRESS_RES_VERDICT] = verdict
143 return 0
144}