Skip to main content

qualia_client_core/wellfair/
anatomy_dyad.rs

1//! The maternal–fetal **dyad**: a maternal body coupled to a developmental (fetal) body at a gestational
2//! age, positioned at the maternal host structure (the uterus). This is *the consideration made explicit*
3//! — the fetus modelled as part of the female-anatomy picture, not scoped out: two coupled bodies across
4//! space and the developmental `t`-axis (reproductive-continuum plan §2/§3.3).
5//!
6//! The maternal body is the HRA female model; the fetal body is a NIH 3D Carnegie stage
7//! ([`crate::wellfair::fetal_stages`]). Placement here is **illustrative**: the two meshes come from
8//! different sources in different scales, so an anatomically-registered placement needs real-world-scale
9//! metadata on both (a follow-up). What this demonstrates is the coupling + a computed transform that
10//! seats the embryo inside the uterine frame.
11
12use crate::wellfair::fetal_stages::CarnegieStage;
13use serde::{Deserialize, Serialize};
14use wellfare_core::anatomy::AnatomyModel;
15
16/// The dyad: which maternal body, which host structure, and the fetal body's point on the `t`-axis.
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct MaternalFetalDyad {
19    pub maternal_model: AnatomyModel,
20    /// The maternal host structure the fetal body couples to (the uterus).
21    pub host_structure: String,
22    pub carnegie_stage: u8,
23    pub gestational_age_days: u16,
24}
25
26impl MaternalFetalDyad {
27    /// A dyad at a given developmental stage. The maternal side is the female model; the host is the uterus.
28    pub fn at_stage(stage: &CarnegieStage) -> Self {
29        Self {
30            maternal_model: AnatomyModel::Female,
31            host_structure: "uterus".to_string(),
32            carnegie_stage: stage.stage,
33            gestational_age_days: stage.postfertilization_days,
34        }
35    }
36}
37
38/// A computed transform seating a (centroid-origin) fetal mesh within a host (uterine) frame.
39#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
40pub struct DyadPlacement {
41    /// Translate the fetal mesh so its centroid sits at the host centroid.
42    pub translate: [f32; 3],
43    /// Uniform scale so the fetal mesh's largest extent = `fill` × the host's smallest extent.
44    pub scale: f32,
45}
46
47/// Compute an **illustrative** placement of a fetal mesh within a host frame: centre it at the host
48/// centroid, scaled so its largest extent fills `fill` (0..1) of the host's smallest extent (so it sits
49/// inside). Pure. Illustrative only — see the module note on scale registration.
50pub fn place_within(
51    host_min: [f32; 3],
52    host_max: [f32; 3],
53    fetal_min: [f32; 3],
54    fetal_max: [f32; 3],
55    fill: f32,
56) -> DyadPlacement {
57    let mid = |lo: f32, hi: f32| (lo + hi) * 0.5;
58    let translate = [
59        mid(host_min[0], host_max[0]),
60        mid(host_min[1], host_max[1]),
61        mid(host_min[2], host_max[2]),
62    ];
63    let host_min_ext = (host_max[0] - host_min[0])
64        .min(host_max[1] - host_min[1])
65        .min(host_max[2] - host_min[2])
66        .max(1e-6);
67    let fetal_max_ext = (fetal_max[0] - fetal_min[0])
68        .max(fetal_max[1] - fetal_min[1])
69        .max(fetal_max[2] - fetal_min[2])
70        .max(1e-6);
71    DyadPlacement {
72        translate,
73        scale: (fill.clamp(0.0, 1.0) * host_min_ext) / fetal_max_ext,
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn dyad_couples_female_uterus_and_stage_and_placement_fits() {
83        let stage = CarnegieStage {
84            stage: 18,
85            postfertilization_days: 44,
86            nih3d_entry: "3DPX-016952",
87            glb_file_id: 501993,
88        };
89        let dyad = MaternalFetalDyad::at_stage(&stage);
90        assert_eq!(dyad.maternal_model, AnatomyModel::Female);
91        assert_eq!(dyad.host_structure, "uterus");
92        assert_eq!(dyad.carnegie_stage, 18);
93        assert_eq!(dyad.gestational_age_days, 44);
94
95        // Host: a 2×2×2 box centred at (10,0,0). Fetal: a 4-wide box. fill=0.5 → target extent = 1.0;
96        // fetal max-extent = 4 → scale 0.25; centred at the host centroid.
97        let p = place_within(
98            [9.0, -1.0, -1.0],
99            [11.0, 1.0, 1.0],
100            [-2.0, -2.0, -2.0],
101            [2.0, 2.0, 2.0],
102            0.5,
103        );
104        assert_eq!(p.translate, [10.0, 0.0, 0.0]);
105        assert!((p.scale - 0.25).abs() < 1e-6, "scale {}", p.scale);
106    }
107
108    /// Real-asset harness: seat a Carnegie embryo within the actual HRA female uterus (the dyad on real
109    /// data). Live network — ignored by default.
110    #[test]
111    #[ignore = "live network: places a Carnegie embryo within the maternal HRA uterus"]
112    fn place_embryo_in_maternal_uterus_from_real_assets() {
113        use crate::wellfair::ccf_resolver::{discover_ref_organs, fetch_glb, HRA_SPARQL_ENDPOINT};
114        use crate::wellfair::fetal_stages::carnegie_series;
115        use qualia_core_db::render::compile_10d::compile_asset;
116
117        let organs = discover_ref_organs(HRA_SPARQL_ENDPOINT).expect("discover HRA");
118        let uterus = organs
119            .iter()
120            .find(|o| o.filename.contains("uterus"))
121            .expect("uterus in the HRA female set");
122        let u_mesh = compile_asset(
123            &fetch_glb(&uterus.glb_url).expect("fetch uterus"),
124            Some("glb"),
125            "urn:host:uterus",
126            "glb",
127        )
128        .unwrap()
129        .mesh;
130
131        let stage = carnegie_series()
132            .into_iter()
133            .find(|s| s.stage == 18)
134            .unwrap();
135        let f_mesh = compile_asset(
136            &fetch_glb(&stage.glb_url()).expect("fetch embryo"),
137            Some("glb"),
138            "urn:fetal:s18",
139            "glb",
140        )
141        .unwrap()
142        .mesh;
143
144        let dyad = MaternalFetalDyad::at_stage(&stage);
145        let p = place_within(u_mesh.min, u_mesh.max, f_mesh.min, f_mesh.max, 0.6);
146        let ext = |m: &qualia_core_db::render::assets::Mesh| {
147            [
148                m.max[0] - m.min[0],
149                m.max[1] - m.min[1],
150                m.max[2] - m.min[2],
151            ]
152        };
153        eprintln!(
154            "DYAD {:?}: Carnegie {} (~{}d) seated in {} — translate {:?}, scale {:.5} · uterus ext {:?} · embryo ext {:?}",
155            dyad.maternal_model,
156            dyad.carnegie_stage,
157            dyad.gestational_age_days,
158            dyad.host_structure,
159            p.translate,
160            p.scale,
161            ext(&u_mesh),
162            ext(&f_mesh),
163        );
164        assert!(p.scale > 0.0);
165    }
166}