Skip to main content

oxihuman_morph/
pigeon_toe_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Pigeon toe (in-toeing) foot rotation morph.
6
7/// Pigeon toe configuration.
8#[derive(Debug, Clone)]
9pub struct PigeonToeMorphConfig {
10    pub intoeing_angle: f32,
11    pub femoral_anteversion: f32,
12    pub metatarsus_adductus: f32,
13}
14
15impl Default for PigeonToeMorphConfig {
16    fn default() -> Self {
17        Self {
18            intoeing_angle: 0.0,
19            femoral_anteversion: 0.0,
20            metatarsus_adductus: 0.0,
21        }
22    }
23}
24
25/// Pigeon toe morph state.
26#[derive(Debug, Clone)]
27pub struct PigeonToeMorph {
28    pub config: PigeonToeMorphConfig,
29    pub intensity: f32,
30    pub enabled: bool,
31}
32
33impl PigeonToeMorph {
34    pub fn new() -> Self {
35        Self {
36            config: PigeonToeMorphConfig::default(),
37            intensity: 0.0,
38            enabled: true,
39        }
40    }
41}
42
43impl Default for PigeonToeMorph {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49/// Create a new PigeonToeMorph.
50pub fn new_pigeon_toe_morph() -> PigeonToeMorph {
51    PigeonToeMorph::new()
52}
53
54/// Set inward toe angle factor (0.0–1.0).
55pub fn pigeon_toe_set_intoeing(morph: &mut PigeonToeMorph, v: f32) {
56    morph.config.intoeing_angle = v.clamp(0.0, 1.0);
57}
58
59/// Set femoral anteversion component.
60pub fn pigeon_toe_set_femoral(morph: &mut PigeonToeMorph, v: f32) {
61    morph.config.femoral_anteversion = v.clamp(0.0, 1.0);
62}
63
64/// Set metatarsus adductus component.
65pub fn pigeon_toe_set_metatarsus(morph: &mut PigeonToeMorph, v: f32) {
66    morph.config.metatarsus_adductus = v.clamp(0.0, 1.0);
67}
68
69/// Compute total inward rotation in normalized units.
70pub fn pigeon_toe_total_rotation(morph: &PigeonToeMorph) -> f32 {
71    morph.intensity
72        * (morph.config.intoeing_angle
73            + morph.config.femoral_anteversion * 0.5
74            + morph.config.metatarsus_adductus * 0.3)
75}
76
77/// Serialize to JSON.
78pub fn pigeon_toe_to_json(morph: &PigeonToeMorph) -> String {
79    format!(
80        r#"{{"intensity":{},"intoeing_angle":{},"femoral_anteversion":{},"metatarsus_adductus":{}}}"#,
81        morph.intensity,
82        morph.config.intoeing_angle,
83        morph.config.femoral_anteversion,
84        morph.config.metatarsus_adductus,
85    )
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn test_new() {
94        let m = new_pigeon_toe_morph();
95        assert!((m.config.intoeing_angle - 0.0).abs() < 1e-6 /* default */);
96    }
97
98    #[test]
99    fn test_intoeing_clamp() {
100        let mut m = new_pigeon_toe_morph();
101        pigeon_toe_set_intoeing(&mut m, 5.0);
102        assert!((m.config.intoeing_angle - 1.0).abs() < 1e-6 /* clamped */);
103    }
104
105    #[test]
106    fn test_femoral() {
107        let mut m = new_pigeon_toe_morph();
108        pigeon_toe_set_femoral(&mut m, 0.4);
109        assert!((m.config.femoral_anteversion - 0.4).abs() < 1e-6 /* stored */);
110    }
111
112    #[test]
113    fn test_metatarsus() {
114        let mut m = new_pigeon_toe_morph();
115        pigeon_toe_set_metatarsus(&mut m, 0.2);
116        assert!((m.config.metatarsus_adductus - 0.2).abs() < 1e-6 /* stored */);
117    }
118
119    #[test]
120    fn test_total_rotation_zero() {
121        let m = new_pigeon_toe_morph();
122        assert!((pigeon_toe_total_rotation(&m) - 0.0).abs() < 1e-6 /* zero */);
123    }
124
125    #[test]
126    fn test_total_rotation_nonzero() {
127        let mut m = new_pigeon_toe_morph();
128        pigeon_toe_set_intoeing(&mut m, 1.0);
129        m.intensity = 1.0;
130        assert!(pigeon_toe_total_rotation(&m) > 0.0 /* positive */);
131    }
132
133    #[test]
134    fn test_json_key() {
135        let m = new_pigeon_toe_morph();
136        let j = pigeon_toe_to_json(&m);
137        assert!(j.contains("intoeing_angle") /* key */);
138    }
139
140    #[test]
141    fn test_default_enabled() {
142        let m = PigeonToeMorph::default();
143        assert!(m.enabled /* enabled */);
144    }
145
146    #[test]
147    fn test_clone() {
148        let m = new_pigeon_toe_morph();
149        let c = m.clone();
150        assert!((c.intensity - m.intensity).abs() < 1e-6 /* equal */);
151    }
152}