Skip to main content

oxihuman_morph/
sacrum_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Sacrum tilt morph — adjusts sacral base angle and width.
6
7/// Sacrum morph configuration.
8#[derive(Debug, Clone)]
9pub struct SacrumMorph {
10    pub tilt: f32,
11    pub width: f32,
12    pub curvature: f32,
13    pub promontory_depth: f32,
14}
15
16impl SacrumMorph {
17    pub fn new() -> Self {
18        Self {
19            tilt: 0.5,
20            width: 0.5,
21            curvature: 0.5,
22            promontory_depth: 0.5,
23        }
24    }
25}
26
27impl Default for SacrumMorph {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33/// Create a new sacrum morph.
34pub fn new_sacrum_morph() -> SacrumMorph {
35    SacrumMorph::new()
36}
37
38/// Set sacral tilt (0 = anterior, 1 = posterior).
39pub fn sac_set_tilt(m: &mut SacrumMorph, v: f32) {
40    m.tilt = v.clamp(0.0, 1.0);
41}
42
43/// Set sacrum width.
44pub fn sac_set_width(m: &mut SacrumMorph, v: f32) {
45    m.width = v.clamp(0.0, 1.0);
46}
47
48/// Set sacral curvature.
49pub fn sac_set_curvature(m: &mut SacrumMorph, v: f32) {
50    m.curvature = v.clamp(0.0, 1.0);
51}
52
53/// Set sacral promontory depth.
54pub fn sac_set_promontory_depth(m: &mut SacrumMorph, v: f32) {
55    m.promontory_depth = v.clamp(0.0, 1.0);
56}
57
58/// Compute pelvic inlet estimate based on tilt and width.
59pub fn sac_pelvic_inlet(m: &SacrumMorph) -> f32 {
60    m.width * (1.0 - (m.tilt - 0.5).abs() * 0.3)
61}
62
63/// Serialize to JSON-like string.
64pub fn sacrum_morph_to_json(m: &SacrumMorph) -> String {
65    format!(
66        r#"{{"tilt":{:.4},"width":{:.4},"curvature":{:.4},"promontory_depth":{:.4}}}"#,
67        m.tilt, m.width, m.curvature, m.promontory_depth
68    )
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_defaults() {
77        let m = new_sacrum_morph();
78        assert!((m.tilt - 0.5).abs() < 1e-6);
79    }
80
81    #[test]
82    fn test_tilt_clamp() {
83        let mut m = new_sacrum_morph();
84        sac_set_tilt(&mut m, -1.0);
85        assert_eq!(m.tilt, 0.0);
86    }
87
88    #[test]
89    fn test_width_set() {
90        let mut m = new_sacrum_morph();
91        sac_set_width(&mut m, 0.7);
92        assert!((m.width - 0.7).abs() < 1e-6);
93    }
94
95    #[test]
96    fn test_curvature_clamp() {
97        let mut m = new_sacrum_morph();
98        sac_set_curvature(&mut m, 3.0);
99        assert_eq!(m.curvature, 1.0);
100    }
101
102    #[test]
103    fn test_promontory_depth() {
104        let mut m = new_sacrum_morph();
105        sac_set_promontory_depth(&mut m, 0.6);
106        assert!((m.promontory_depth - 0.6).abs() < 1e-6);
107    }
108
109    #[test]
110    fn test_pelvic_inlet_range() {
111        let m = new_sacrum_morph();
112        let pi = sac_pelvic_inlet(&m);
113        assert!((0.0..=1.0).contains(&pi));
114    }
115
116    #[test]
117    fn test_json_keys() {
118        let m = new_sacrum_morph();
119        let s = sacrum_morph_to_json(&m);
120        assert!(s.contains("promontory_depth"));
121    }
122
123    #[test]
124    fn test_clone() {
125        let m = new_sacrum_morph();
126        let m2 = m.clone();
127        assert!((m2.width - m.width).abs() < 1e-6);
128    }
129
130    #[test]
131    fn test_pelvic_inlet_wide() {
132        let mut m = new_sacrum_morph();
133        sac_set_width(&mut m, 1.0);
134        sac_set_tilt(&mut m, 0.5); /* neutral tilt = maximum inlet */
135        assert!(sac_pelvic_inlet(&m) > 0.9);
136    }
137}