Skip to main content

oxihuman_morph/
parietal_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5/// Parietal bone curvature morph.
6#[derive(Debug, Clone)]
7pub struct ParietalMorph {
8    /// Coronal curvature (0.0 = flat, 1.0 = highly curved).
9    pub coronal_curve: f32,
10    /// Sagittal curvature (0.0 = flat, 1.0 = domed).
11    pub sagittal_curve: f32,
12    /// Parietal boss prominence (0.0 = absent, 1.0 = prominent).
13    pub boss: f32,
14}
15
16pub fn new_parietal_morph() -> ParietalMorph {
17    ParietalMorph {
18        coronal_curve: 0.0,
19        sagittal_curve: 0.0,
20        boss: 0.0,
21    }
22}
23
24pub fn par_set_coronal_curve(m: &mut ParietalMorph, v: f32) {
25    m.coronal_curve = v.clamp(0.0, 1.0);
26}
27
28pub fn par_set_sagittal_curve(m: &mut ParietalMorph, v: f32) {
29    m.sagittal_curve = v.clamp(0.0, 1.0);
30}
31
32pub fn par_set_boss(m: &mut ParietalMorph, v: f32) {
33    m.boss = v.clamp(0.0, 1.0);
34}
35
36pub fn par_overall_weight(m: &ParietalMorph) -> f32 {
37    (m.coronal_curve + m.sagittal_curve + m.boss) / 3.0
38}
39
40pub fn par_blend(a: &ParietalMorph, b: &ParietalMorph, t: f32) -> ParietalMorph {
41    let t = t.clamp(0.0, 1.0);
42    ParietalMorph {
43        coronal_curve: a.coronal_curve + (b.coronal_curve - a.coronal_curve) * t,
44        sagittal_curve: a.sagittal_curve + (b.sagittal_curve - a.sagittal_curve) * t,
45        boss: a.boss + (b.boss - a.boss) * t,
46    }
47}
48
49pub fn par_is_neutral(m: &ParietalMorph) -> bool {
50    m.coronal_curve < 1e-5 && m.sagittal_curve < 1e-5 && m.boss < 1e-5
51}
52
53pub fn par_to_json(m: &ParietalMorph) -> String {
54    format!(
55        r#"{{"coronal_curve":{:.4},"sagittal_curve":{:.4},"boss":{:.4}}}"#,
56        m.coronal_curve, m.sagittal_curve, m.boss
57    )
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_defaults() {
66        /* all zero */
67        let m = new_parietal_morph();
68        assert_eq!(m.coronal_curve, 0.0);
69    }
70
71    #[test]
72    fn test_set_coronal() {
73        /* valid value */
74        let mut m = new_parietal_morph();
75        par_set_coronal_curve(&mut m, 0.5);
76        assert!((m.coronal_curve - 0.5).abs() < 1e-6);
77    }
78
79    #[test]
80    fn test_clamp_high() {
81        /* clamp */
82        let mut m = new_parietal_morph();
83        par_set_coronal_curve(&mut m, 2.0);
84        assert_eq!(m.coronal_curve, 1.0);
85    }
86
87    #[test]
88    fn test_clamp_low() {
89        /* clamp low */
90        let mut m = new_parietal_morph();
91        par_set_boss(&mut m, -1.0);
92        assert_eq!(m.boss, 0.0);
93    }
94
95    #[test]
96    fn test_neutral_true() {
97        /* default neutral */
98        assert!(par_is_neutral(&new_parietal_morph()));
99    }
100
101    #[test]
102    fn test_neutral_false() {
103        /* after setting boss */
104        let mut m = new_parietal_morph();
105        par_set_boss(&mut m, 0.1);
106        assert!(!par_is_neutral(&m));
107    }
108
109    #[test]
110    fn test_weight() {
111        /* formula */
112        let m = ParietalMorph {
113            coronal_curve: 0.3,
114            sagittal_curve: 0.6,
115            boss: 0.9,
116        };
117        assert!((par_overall_weight(&m) - 0.6).abs() < 1e-5);
118    }
119
120    #[test]
121    fn test_blend() {
122        /* midpoint */
123        let a = new_parietal_morph();
124        let b = ParietalMorph {
125            coronal_curve: 1.0,
126            sagittal_curve: 0.0,
127            boss: 0.0,
128        };
129        let c = par_blend(&a, &b, 0.5);
130        assert!((c.coronal_curve - 0.5).abs() < 1e-5);
131    }
132
133    #[test]
134    fn test_to_json() {
135        /* JSON has coronal_curve */
136        assert!(par_to_json(&new_parietal_morph()).contains("coronal_curve"));
137    }
138
139    #[test]
140    fn test_clone() {
141        /* clone independent */
142        let m = ParietalMorph {
143            coronal_curve: 0.1,
144            sagittal_curve: 0.2,
145            boss: 0.3,
146        };
147        let m2 = m.clone();
148        assert_eq!(m.boss, m2.boss);
149    }
150}