Skip to main content

oxihuman_morph/
tibia_fibula_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Tibia/fibula proportions morph — lower leg bone geometry control.
6
7/// Tibia/fibula morph configuration.
8#[derive(Debug, Clone)]
9pub struct TibiaFibulaMorph {
10    pub length: f32,
11    pub tibial_torsion: f32,
12    pub fibula_offset: f32,
13    pub malleolus_width: f32,
14}
15
16impl TibiaFibulaMorph {
17    pub fn new() -> Self {
18        Self {
19            length: 0.5,
20            tibial_torsion: 0.5,
21            fibula_offset: 0.5,
22            malleolus_width: 0.5,
23        }
24    }
25}
26
27impl Default for TibiaFibulaMorph {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33/// Create a new tibia/fibula morph.
34pub fn new_tibia_fibula_morph() -> TibiaFibulaMorph {
35    TibiaFibulaMorph::new()
36}
37
38/// Set lower leg length.
39pub fn tf_set_length(m: &mut TibiaFibulaMorph, v: f32) {
40    m.length = v.clamp(0.0, 1.0);
41}
42
43/// Set tibial torsion (0 = internal, 0.5 = neutral, 1 = external).
44pub fn tf_set_tibial_torsion(m: &mut TibiaFibulaMorph, v: f32) {
45    m.tibial_torsion = v.clamp(0.0, 1.0);
46}
47
48/// Set fibula lateral offset.
49pub fn tf_set_fibula_offset(m: &mut TibiaFibulaMorph, v: f32) {
50    m.fibula_offset = v.clamp(0.0, 1.0);
51}
52
53/// Set bimalleolar width.
54pub fn tf_set_malleolus_width(m: &mut TibiaFibulaMorph, v: f32) {
55    m.malleolus_width = v.clamp(0.0, 1.0);
56}
57
58/// Foot progression angle heuristic from tibial torsion.
59pub fn tf_foot_progression(m: &TibiaFibulaMorph) -> f32 {
60    (m.tibial_torsion - 0.5) * 2.0 /* -1 = toe-in, 1 = toe-out */
61}
62
63/// Serialize to JSON-like string.
64pub fn tibia_fibula_morph_to_json(m: &TibiaFibulaMorph) -> String {
65    format!(
66        r#"{{"length":{:.4},"tibial_torsion":{:.4},"fibula_offset":{:.4},"malleolus_width":{:.4}}}"#,
67        m.length, m.tibial_torsion, m.fibula_offset, m.malleolus_width
68    )
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn test_defaults() {
77        let m = new_tibia_fibula_morph();
78        assert!((m.length - 0.5).abs() < 1e-6);
79        assert!((m.tibial_torsion - 0.5).abs() < 1e-6);
80    }
81
82    #[test]
83    fn test_length_clamp() {
84        let mut m = new_tibia_fibula_morph();
85        tf_set_length(&mut m, 3.0);
86        assert_eq!(m.length, 1.0);
87    }
88
89    #[test]
90    fn test_torsion_set() {
91        let mut m = new_tibia_fibula_morph();
92        tf_set_tibial_torsion(&mut m, 0.8);
93        assert!((m.tibial_torsion - 0.8).abs() < 1e-6);
94    }
95
96    #[test]
97    fn test_fibula_offset_set() {
98        let mut m = new_tibia_fibula_morph();
99        tf_set_fibula_offset(&mut m, 0.6);
100        assert!((m.fibula_offset - 0.6).abs() < 1e-6);
101    }
102
103    #[test]
104    fn test_malleolus_clamp() {
105        let mut m = new_tibia_fibula_morph();
106        tf_set_malleolus_width(&mut m, 2.0);
107        assert_eq!(m.malleolus_width, 1.0);
108    }
109
110    #[test]
111    fn test_foot_progression_neutral() {
112        let m = new_tibia_fibula_morph();
113        assert!(tf_foot_progression(&m).abs() < 1e-5); /* neutral = 0 */
114    }
115
116    #[test]
117    fn test_foot_progression_toe_out() {
118        let mut m = new_tibia_fibula_morph();
119        tf_set_tibial_torsion(&mut m, 1.0);
120        assert!(tf_foot_progression(&m) > 0.0); /* external = toe-out */
121    }
122
123    #[test]
124    fn test_json_keys() {
125        let m = new_tibia_fibula_morph();
126        let s = tibia_fibula_morph_to_json(&m);
127        assert!(s.contains("tibial_torsion"));
128    }
129
130    #[test]
131    fn test_clone() {
132        let m = new_tibia_fibula_morph();
133        let m2 = m.clone();
134        assert!((m2.malleolus_width - m.malleolus_width).abs() < 1e-6);
135    }
136}