Skip to main content

oxihuman_morph/
flat_foot_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Flat foot (pes planus / fallen arch) morph.
6
7/// Flat foot configuration.
8#[derive(Debug, Clone)]
9pub struct FlatFootMorphConfig {
10    pub arch_collapse: f32,
11    pub pronation: f32,
12    pub toe_splay: f32,
13}
14
15impl Default for FlatFootMorphConfig {
16    fn default() -> Self {
17        Self {
18            arch_collapse: 0.0,
19            pronation: 0.0,
20            toe_splay: 0.0,
21        }
22    }
23}
24
25/// Flat foot morph state.
26#[derive(Debug, Clone)]
27pub struct FlatFootMorph {
28    pub config: FlatFootMorphConfig,
29    pub intensity: f32,
30    pub left_foot: bool,
31    pub right_foot: bool,
32}
33
34impl FlatFootMorph {
35    pub fn new() -> Self {
36        Self {
37            config: FlatFootMorphConfig::default(),
38            intensity: 0.0,
39            left_foot: true,
40            right_foot: true,
41        }
42    }
43}
44
45impl Default for FlatFootMorph {
46    fn default() -> Self {
47        Self::new()
48    }
49}
50
51/// Create a new FlatFootMorph.
52pub fn new_flat_foot_morph() -> FlatFootMorph {
53    FlatFootMorph::new()
54}
55
56/// Set arch collapse factor (0.0 = normal, 1.0 = fully flat).
57pub fn flat_foot_set_arch_collapse(morph: &mut FlatFootMorph, v: f32) {
58    morph.config.arch_collapse = v.clamp(0.0, 1.0);
59}
60
61/// Set pronation angle factor.
62pub fn flat_foot_set_pronation(morph: &mut FlatFootMorph, v: f32) {
63    morph.config.pronation = v.clamp(0.0, 1.0);
64}
65
66/// Set toe splay factor.
67pub fn flat_foot_set_toe_splay(morph: &mut FlatFootMorph, v: f32) {
68    morph.config.toe_splay = v.clamp(0.0, 1.0);
69}
70
71/// Compute effective arch height reduction.
72pub fn flat_foot_arch_height(morph: &FlatFootMorph) -> f32 {
73    let base_height = 1.0f32;
74    base_height - morph.intensity * morph.config.arch_collapse
75}
76
77/// Serialize to JSON.
78pub fn flat_foot_to_json(morph: &FlatFootMorph) -> String {
79    format!(
80        r#"{{"intensity":{},"arch_collapse":{},"pronation":{},"toe_splay":{}}}"#,
81        morph.intensity, morph.config.arch_collapse, morph.config.pronation, morph.config.toe_splay,
82    )
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_new() {
91        let m = new_flat_foot_morph();
92        assert!(m.left_foot /* left enabled by default */);
93    }
94
95    #[test]
96    fn test_arch_collapse_clamp() {
97        let mut m = new_flat_foot_morph();
98        flat_foot_set_arch_collapse(&mut m, 2.0);
99        assert!((m.config.arch_collapse - 1.0).abs() < 1e-6 /* clamped */);
100    }
101
102    #[test]
103    fn test_pronation() {
104        let mut m = new_flat_foot_morph();
105        flat_foot_set_pronation(&mut m, 0.6);
106        assert!((m.config.pronation - 0.6).abs() < 1e-6 /* stored */);
107    }
108
109    #[test]
110    fn test_toe_splay() {
111        let mut m = new_flat_foot_morph();
112        flat_foot_set_toe_splay(&mut m, 0.3);
113        assert!((m.config.toe_splay - 0.3).abs() < 1e-6 /* stored */);
114    }
115
116    #[test]
117    fn test_arch_height_normal() {
118        let m = new_flat_foot_morph();
119        assert!((flat_foot_arch_height(&m) - 1.0).abs() < 1e-6 /* full height */);
120    }
121
122    #[test]
123    fn test_arch_height_flat() {
124        let mut m = new_flat_foot_morph();
125        flat_foot_set_arch_collapse(&mut m, 1.0);
126        m.intensity = 1.0;
127        assert!((flat_foot_arch_height(&m) - 0.0).abs() < 1e-6 /* fully flat */);
128    }
129
130    #[test]
131    fn test_json_keys() {
132        let m = new_flat_foot_morph();
133        let j = flat_foot_to_json(&m);
134        assert!(j.contains("arch_collapse") /* key */);
135    }
136
137    #[test]
138    fn test_default_both_feet() {
139        let m = FlatFootMorph::default();
140        assert!(m.right_foot && m.left_foot /* both enabled */);
141    }
142
143    #[test]
144    fn test_clone() {
145        let m = new_flat_foot_morph();
146        let c = m.clone();
147        assert!((c.intensity - m.intensity).abs() < 1e-6 /* clone */);
148    }
149}