Skip to main content

qualia_client_core/wellfair/api/
anatomy.rs

1//! 3D anatomy asset cache + physiological state
2
3use super::*;
4
5impl WebizenHostApi {
6    // --- 3D Anatomy asset cache (S5.8 — user-triggered real-mesh acquisition) -------------------
7    //
8    // The person triggers a download of the CCF/HRA reference-organ GLB set from the live SPARQL
9    // endpoint; the host fetches + compiles each to a sealed `.10d` and caches both under
10    // `{storage_root}/assets/ccf/{model}/`. Subsequent runs load the cached `.10d` directly — no
11    // re-download. The cache is the person's own, generated on demand.
12
13    /// Whether the body assets for a model are cached + complete (manifest exists + every referenced
14    /// `.10d` is on disk). `model` is `"male"` / `"female"` (case-insensitive).
15    pub fn body_assets_status(
16        &self,
17        model: &str,
18    ) -> Result<super::super::anatomy_assets::BodyAssetsStatus, String> {
19        let m = parse_anatomy_model(model)?;
20        Ok(super::super::anatomy_assets::status(&self.storage_root, m))
21    }
22
23    /// The cached organ keys for a model (empty if not cached).
24    pub fn cached_organ_keys(&self, model: &str) -> Result<Vec<String>, String> {
25        let m = parse_anatomy_model(model)?;
26        Ok(super::super::anatomy_assets::cached_organ_keys(
27            &self.storage_root,
28            m,
29        ))
30    }
31
32    /// Load a cached `.10d` for one organ. Returns the raw container bytes (for the browser portal's
33    /// `load_10d_colored`).
34    pub fn load_cached_organ_10d(&self, model: &str, organ_key: &str) -> Result<Vec<u8>, String> {
35        let m = parse_anatomy_model(model)?;
36        super::super::anatomy_assets::load_cached_10d(&self.storage_root, m, organ_key)
37    }
38
39    /// The per-organ dual-modality percepts for the cached organ set — so the browser portal knows what
40    /// colour to paint each organ (σ → RGBA via `paint_organs`). Returns `(painted, unmapped)`.
41    pub fn cached_body_organ_percepts(
42        &self,
43        model: &str,
44    ) -> Result<(Vec<super::super::anatomy_view::OrganPercept>, Vec<String>), String> {
45        let m = parse_anatomy_model(model)?;
46        let organ_keys = super::super::anatomy_assets::cached_organ_keys(&self.storage_root, m);
47        if organ_keys.is_empty() {
48            return Ok((Vec::new(), Vec::new()));
49        }
50        let report = self.compute_anatomy_view("person", 2)?;
51        let key_refs: Vec<&str> = organ_keys.iter().map(|s| s.as_str()).collect();
52        let (painted, unmapped) = report.paint_organs(&key_refs);
53        let fit = self.body_fit();
54        let painted = painted
55            .into_iter()
56            .filter(|p| !fit.hides(&p.organ_key))
57            .collect();
58        Ok((painted, unmapped))
59    }
60
61    /// The person's declared constitution, or an empty one if they have not authored it.
62    pub fn get_body_constitution(&self) -> wellfare_core::anatomy::BodyConstitution {
63        super::super::body_constitution::load(&self.storage_root).unwrap_or_default()
64    }
65
66    pub fn body_constitution_is_declared(&self) -> bool {
67        super::super::body_constitution::load(&self.storage_root).is_some()
68    }
69
70    pub fn set_body_constitution(
71        &self,
72        body: &wellfare_core::anatomy::BodyConstitution,
73    ) -> Result<(), String> {
74        super::super::body_constitution::save(&self.storage_root, body)
75    }
76
77    pub fn reset_body_constitution(&self) -> Result<(), String> {
78        super::super::body_constitution::clear(&self.storage_root)
79    }
80
81    /// View transform for the current constitution + physiological state.
82    pub fn body_fit(&self) -> wellfare_core::anatomy::BodyFit {
83        let constitution = self.get_body_constitution();
84        let phys = self.get_physiological_state();
85        super::super::body_constitution::fit_for(&constitution, &phys)
86    }
87
88    /// Clear the cache for a model (idempotent). The person can re-acquire later.
89    pub fn clear_body_cache(&self, model: &str) -> Result<(), String> {
90        let m = parse_anatomy_model(model)?;
91        super::super::anatomy_assets::clear_cache(&self.storage_root, m)
92    }
93
94    /// The accumulative, traceable **score-card** + investigable hypotheses over the person's own records —
95    /// the reading they can act on. Forum-internum / `Sanctuary`-class selfhood content; a set of
96    /// **hypotheses** and pathway-starts, never a diagnosis, never a rating. The card is computed at the
97    /// person's **declared physiological state** (their point on the reproductive continuum), or
98    /// [`PhysiologicalState::Baseline`] if they have not declared one.
99    pub fn compute_scorecard(
100        &self,
101        convergence_threshold: usize,
102    ) -> Result<super::super::anatomy_view::WellbeingScorecardReport, String> {
103        let conditions = self.list_journal_by_kind("condition", 256)?;
104        let medications = self.list_journal_by_kind("medication", 256)?;
105        let diet = self.list_journal_by_kind("diet", 256)?;
106        // Read the person through **their own** weight model — their authorship of how they're read — falling
107        // back to the seed *suggestion* only if they have not authored one.
108        let weights = self.get_weight_model();
109        // Read the person at **their declared physiological state** — their own statement of where they are
110        // on the reproductive continuum — falling back to Baseline if they have not declared one.
111        let state = self.get_physiological_state();
112        Ok(
113            super::super::anatomy_view::build_scorecard_report_from_journal_with_weights(
114                &conditions,
115                &medications,
116                &diet,
117                convergence_threshold,
118                &weights,
119                state,
120            ),
121        )
122    }
123
124    /// The person's own score-card **weight model** — the interpretive lens the card uses — or the seed
125    /// *suggestion* if they have not authored one. Theirs to see, edit, or reset; the software offers a
126    /// starting point, it does not *define* how they are read.
127    pub fn get_weight_model(&self) -> wellfare_core::anatomy::WeightModel {
128        super::super::scorecard_prefs::load(&self.storage_root)
129            .unwrap_or_else(wellfare_core::anatomy::seed_weight_model)
130    }
131
132    /// The seed **suggestion** on its own — so a UI can show "this is the starting point; here's yours" and
133    /// let the person compare / adopt / edit.
134    pub fn seed_weight_model(&self) -> wellfare_core::anatomy::WeightModel {
135        wellfare_core::anatomy::seed_weight_model()
136    }
137
138    /// Whether the person has **authored their own** model (vs. still using the seed suggestion).
139    pub fn weight_model_is_authored(&self) -> bool {
140        super::super::scorecard_prefs::load(&self.storage_root).is_some()
141    }
142
143    /// **Set the person's own** weight model — their authorship of how the score-card reads them.
144    pub fn set_weight_model(
145        &self,
146        model: &wellfare_core::anatomy::WeightModel,
147    ) -> Result<(), String> {
148        super::super::scorecard_prefs::save(&self.storage_root, model)
149    }
150
151    /// **Reset** to the seed suggestion (clears the person's authored model — a choice, always reversible by
152    /// re-authoring).
153    pub fn reset_weight_model(&self) -> Result<(), String> {
154        super::super::scorecard_prefs::clear(&self.storage_root)
155    }
156
157    // --- Physiological state (P6 — the reproductive-continuum declaration) -----------------------
158    //
159    // The person's own statement of where they are on the reproductive continuum — their inward knowledge
160    // of their own body. Forum-internum / Sanctuary-class. The score-card is computed at this state so it
161    // reads them at their current life stage, not a neutral baseline.
162
163    /// The person's **declared** physiological state, or [`PhysiologicalState::Baseline`] if they have not
164    /// declared one. Their own statement; the software never assumes.
165    pub fn get_physiological_state(&self) -> wellfare_core::anatomy::PhysiologicalState {
166        super::super::physiology_prefs::load(&self.storage_root)
167            .unwrap_or(wellfare_core::anatomy::PhysiologicalState::Baseline)
168    }
169
170    /// Whether the person has **declared** their physiological state (vs. still at the implicit baseline).
171    pub fn physiological_state_is_declared(&self) -> bool {
172        super::super::physiology_prefs::load(&self.storage_root).is_some()
173    }
174
175    /// **Set** the person's declared physiological state — their own statement of where they are on the
176    /// reproductive continuum. Forum-internum / Sanctuary-class.
177    pub fn set_physiological_state(
178        &self,
179        state: &wellfare_core::anatomy::PhysiologicalState,
180    ) -> Result<(), String> {
181        super::super::physiology_prefs::save(&self.storage_root, state)
182    }
183
184    /// **Clear** the declared state — revert to the implicit [`PhysiologicalState::Baseline`]. Idempotent.
185    pub fn reset_physiological_state(&self) -> Result<(), String> {
186        super::super::physiology_prefs::clear(&self.storage_root)
187    }
188}