nx_anatomy_placement.nx source
↩ module page · 147 lines · 6895 B
1// nx_anatomy_placement.nx -- out-of-place anatomy verification.
2//
3// Image #56 failure class: nipple rendered at a location that doesn't
4// match where the breast tissue actually projects. The nipple has to
5// be ON the breast mound, near the projection peak, slightly above
6// midline horizontally. Same problem for areola-on-nipple, navel-on-
7// torso-midline, pubic-mound-on-pelvis, etc.
8//
9// The substrate measures the DISTANCE between an anatomical feature's
10// observed centroid and its CANONICAL ZONE (computed from the parent
11// region's bbox + canon ratios). Outside the zone = OUT_OF_PLACE.
12//
13// Canon zones (per Loomis figure-drawing canon):
14// nipple on breast: centered horizontally on breast,
15// vertically at 0.55-0.70 of breast height from top
16// areola on nipple: concentric with nipple centroid
17// navel on torso: horizontal midline, vertical 0.50-0.60 from top of torso
18// pubic mound: horizontal midline, vertical 0.85-0.95 from top of torso
19//
20// Sealed verdict ladder:
21// OUT_OF_PLACE distance > 50% of parent dimension
22// SLIGHTLY_OFF 25-50%
23// CORRECT < 25% from expected zone
24
25// nx_safety_envelope:
26// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
27// sil_target: SIL1
28// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
29// verdict: NOT_YET_EVALUATED
30
31import "nx_syscalls.nx"
32const NX_MAGIC_1024: i64 = 1024
33
34const NX_PLACE_RES_DIST_X_Q10: i64 = 0 // horizontal off-target Q10 (1024 = parent width)
35const NX_PLACE_RES_DIST_Y_Q10: i64 = 1
36const NX_PLACE_RES_DIST_TOTAL_Q10: i64 = 2 // euclidean approx (max(dx, dy))
37const NX_PLACE_RES_VERDICT: i64 = 3
38const NX_PLACE_RES_FIELDS: i64 = 4
39
40const NX_PLACE_OUT_OF_PLACE: i64 = 0
41const NX_PLACE_SLIGHTLY_OFF: i64 = 1
42const NX_PLACE_CORRECT: i64 = 2
43
44// Canon zones (Q10 fractions of parent bbox).
45const NX_PLACE_NIPPLE_X_CENTER: i64 = 512 // 0.50 = horizontal center
46const NX_PLACE_NIPPLE_X_TOL: i64 = 102 // ±10% horizontally
47const NX_PLACE_NIPPLE_Y_MIN: i64 = 563 // 0.55 from top
48const NX_PLACE_NIPPLE_Y_MAX: i64 = 717 // 0.70 from top
49
50const NX_PLACE_NAVEL_X_CENTER: i64 = 512
51const NX_PLACE_NAVEL_X_TOL: i64 = 51 // ±5% horizontally (tighter)
52const NX_PLACE_NAVEL_Y_MIN: i64 = 512
53const NX_PLACE_NAVEL_Y_MAX: i64 = 614
54
55const NX_PLACE_PUBIC_X_CENTER: i64 = 512
56const NX_PLACE_PUBIC_X_TOL: i64 = 51
57const NX_PLACE_PUBIC_Y_MIN: i64 = 870
58const NX_PLACE_PUBIC_Y_MAX: i64 = 973
59
60// Generic placement scorer.
61// parent_x0..parent_y1 — bbox of parent region (e.g. breast)
62// feature_cx, feature_cy — observed centroid of feature (e.g. nipple)
63// expected_x_center_q10 — target X position as Q10 fraction of parent width
64// expected_x_tol_q10 — horizontal tolerance Q10
65// expected_y_min_q10 — vertical lower bound Q10 of parent height
66// expected_y_max_q10 — vertical upper bound Q10
67// result — pre-allocated NX_PLACE_RES_FIELDS i64 array
68func nx_anatomy_placement_score(parent_x0: i64, parent_y0: i64,
69 parent_x1: i64, parent_y1: i64,
70 feature_cx: i64, feature_cy: i64,
71 expected_x_center_q10: i64,
72 expected_x_tol_q10: i64,
73 expected_y_min_q10: i64,
74 expected_y_max_q10: i64,
75 result: *i64) -> i64 {
76 var i: i64 = 0
77 while i < NX_PLACE_RES_FIELDS { result[i] = 0; i = i + 1 }
78
79 let parent_w: i64 = parent_x1 - parent_x0
80 let parent_h: i64 = parent_y1 - parent_y0
81 if parent_w <= 0 { return 1 }
82 if parent_h <= 0 { return 2 }
83
84 // Convert observed centroid to Q10 fraction of parent bbox.
85 let feat_x_q10: i64 = ((feature_cx - parent_x0) * NX_MAGIC_1024) / parent_w
86 let feat_y_q10: i64 = ((feature_cy - parent_y0) * NX_MAGIC_1024) / parent_h
87
88 // Horizontal distance from expected center.
89 var dx: i64 = feat_x_q10 - expected_x_center_q10
90 if dx < 0 { dx = -dx }
91 var dx_off: i64 = 0
92 if dx > expected_x_tol_q10 { dx_off = dx - expected_x_tol_q10 }
93 result[NX_PLACE_RES_DIST_X_Q10] = dx_off
94
95 // Vertical distance from expected band.
96 var dy_off: i64 = 0
97 if feat_y_q10 < expected_y_min_q10 { dy_off = expected_y_min_q10 - feat_y_q10 }
98 if feat_y_q10 > expected_y_max_q10 { dy_off = feat_y_q10 - expected_y_max_q10 }
99 result[NX_PLACE_RES_DIST_Y_Q10] = dy_off
100
101 // Total = max for L∞ distance (cheap proxy for euclidean).
102 var total: i64 = dx_off
103 if dy_off > total { total = dy_off }
104 result[NX_PLACE_RES_DIST_TOTAL_Q10] = total
105
106 var verdict: i64 = NX_PLACE_CORRECT
107 if total > 256 { verdict = NX_PLACE_SLIGHTLY_OFF }
108 if total > 512 { verdict = NX_PLACE_OUT_OF_PLACE }
109 result[NX_PLACE_RES_VERDICT] = verdict
110 return 0
111}
112
113// Convenience wrapper: nipple on breast.
114func nx_nipple_on_breast_check(breast_x0: i64, breast_y0: i64,
115 breast_x1: i64, breast_y1: i64,
116 nipple_cx: i64, nipple_cy: i64,
117 result: *i64) -> i64 {
118 return nx_anatomy_placement_score(breast_x0, breast_y0, breast_x1, breast_y1,
119 nipple_cx, nipple_cy,
120 NX_PLACE_NIPPLE_X_CENTER, NX_PLACE_NIPPLE_X_TOL,
121 NX_PLACE_NIPPLE_Y_MIN, NX_PLACE_NIPPLE_Y_MAX,
122 result)
123}
124
125// Convenience wrapper: navel on torso.
126func nx_navel_on_torso_check(torso_x0: i64, torso_y0: i64,
127 torso_x1: i64, torso_y1: i64,
128 navel_cx: i64, navel_cy: i64,
129 result: *i64) -> i64 {
130 return nx_anatomy_placement_score(torso_x0, torso_y0, torso_x1, torso_y1,
131 navel_cx, navel_cy,
132 NX_PLACE_NAVEL_X_CENTER, NX_PLACE_NAVEL_X_TOL,
133 NX_PLACE_NAVEL_Y_MIN, NX_PLACE_NAVEL_Y_MAX,
134 result)
135}
136
137// Convenience wrapper: pubic mound on torso lower-band.
138func nx_pubic_on_torso_check(torso_x0: i64, torso_y0: i64,
139 torso_x1: i64, torso_y1: i64,
140 pubic_cx: i64, pubic_cy: i64,
141 result: *i64) -> i64 {
142 return nx_anatomy_placement_score(torso_x0, torso_y0, torso_x1, torso_y1,
143 pubic_cx, pubic_cy,
144 NX_PLACE_PUBIC_X_CENTER, NX_PLACE_PUBIC_X_TOL,
145 NX_PLACE_PUBIC_Y_MIN, NX_PLACE_PUBIC_Y_MAX,
146 result)
147}