oxihuman_morph/
foot_arch_control.rs1#![allow(dead_code)]
3
4use std::f32::consts::PI;
7
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct FootArchConfig {
11 pub arch_height: f32,
12 pub arch_length: f32,
13 pub stiffness: f32,
14}
15
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct FootArchState {
19 pub arch_height: f32,
20 pub arch_length: f32,
21 pub stiffness: f32,
22 pub pronation: f32,
23}
24
25#[allow(dead_code)]
26#[derive(Debug, Clone)]
27pub struct FootArchWeights {
28 pub high_arch: f32,
29 pub flat_foot: f32,
30 pub long_arch: f32,
31 pub stiff: f32,
32 pub pronated: f32,
33}
34
35#[allow(dead_code)]
36pub fn default_foot_arch_config() -> FootArchConfig {
37 FootArchConfig {
38 arch_height: 0.5,
39 arch_length: 0.5,
40 stiffness: 0.5,
41 }
42}
43
44#[allow(dead_code)]
45pub fn new_foot_arch_state() -> FootArchState {
46 FootArchState {
47 arch_height: 0.5,
48 arch_length: 0.5,
49 stiffness: 0.5,
50 pronation: 0.0,
51 }
52}
53
54#[allow(dead_code)]
55pub fn set_foot_arch_height(state: &mut FootArchState, value: f32) {
56 state.arch_height = value.clamp(0.0, 1.0);
57}
58
59#[allow(dead_code)]
60pub fn set_foot_arch_length(state: &mut FootArchState, value: f32) {
61 state.arch_length = value.clamp(0.0, 1.0);
62}
63
64#[allow(dead_code)]
65pub fn set_foot_arch_stiffness(state: &mut FootArchState, value: f32) {
66 state.stiffness = value.clamp(0.0, 1.0);
67}
68
69#[allow(dead_code)]
70pub fn set_foot_pronation(state: &mut FootArchState, value: f32) {
71 state.pronation = value.clamp(-1.0, 1.0);
72}
73
74#[allow(dead_code)]
75pub fn compute_foot_arch_weights(state: &FootArchState, cfg: &FootArchConfig) -> FootArchWeights {
76 let h = state.arch_height * cfg.arch_height;
77 let high_arch = (h * (PI * 0.25).sin()).clamp(0.0, 1.0);
78 let flat_foot = (1.0 - h).clamp(0.0, 1.0);
79 let long_arch = (state.arch_length * cfg.arch_length).clamp(0.0, 1.0);
80 let stiff = (state.stiffness * cfg.stiffness).clamp(0.0, 1.0);
81 let pronated = (state.pronation.abs() * 0.5).clamp(0.0, 1.0);
82 FootArchWeights {
83 high_arch,
84 flat_foot,
85 long_arch,
86 stiff,
87 pronated,
88 }
89}
90
91#[allow(dead_code)]
92pub fn foot_arch_to_json(state: &FootArchState) -> String {
93 format!(
94 r#"{{"arch_height":{},"arch_length":{},"stiffness":{},"pronation":{}}}"#,
95 state.arch_height, state.arch_length, state.stiffness, state.pronation
96 )
97}
98
99#[allow(dead_code)]
100pub fn blend_foot_arches(a: &FootArchState, b: &FootArchState, t: f32) -> FootArchState {
101 let t = t.clamp(0.0, 1.0);
102 FootArchState {
103 arch_height: a.arch_height + (b.arch_height - a.arch_height) * t,
104 arch_length: a.arch_length + (b.arch_length - a.arch_length) * t,
105 stiffness: a.stiffness + (b.stiffness - a.stiffness) * t,
106 pronation: a.pronation + (b.pronation - a.pronation) * t,
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_default_config() {
116 let cfg = default_foot_arch_config();
117 assert!((0.0..=1.0).contains(&cfg.arch_height));
118 }
119
120 #[test]
121 fn test_new_state() {
122 let s = new_foot_arch_state();
123 assert!((s.arch_height - 0.5).abs() < 1e-6);
124 }
125
126 #[test]
127 fn test_set_arch_height_clamp() {
128 let mut s = new_foot_arch_state();
129 set_foot_arch_height(&mut s, 1.5);
130 assert!((s.arch_height - 1.0).abs() < 1e-6);
131 }
132
133 #[test]
134 fn test_set_arch_length() {
135 let mut s = new_foot_arch_state();
136 set_foot_arch_length(&mut s, 0.8);
137 assert!((s.arch_length - 0.8).abs() < 1e-6);
138 }
139
140 #[test]
141 fn test_set_stiffness() {
142 let mut s = new_foot_arch_state();
143 set_foot_arch_stiffness(&mut s, 0.7);
144 assert!((s.stiffness - 0.7).abs() < 1e-6);
145 }
146
147 #[test]
148 fn test_set_pronation() {
149 let mut s = new_foot_arch_state();
150 set_foot_pronation(&mut s, -0.5);
151 assert!((s.pronation - (-0.5)).abs() < 1e-6);
152 }
153
154 #[test]
155 fn test_compute_weights() {
156 let s = new_foot_arch_state();
157 let cfg = default_foot_arch_config();
158 let w = compute_foot_arch_weights(&s, &cfg);
159 assert!((0.0..=1.0).contains(&w.high_arch));
160 assert!((0.0..=1.0).contains(&w.flat_foot));
161 }
162
163 #[test]
164 fn test_to_json() {
165 let s = new_foot_arch_state();
166 let json = foot_arch_to_json(&s);
167 assert!(json.contains("arch_height"));
168 assert!(json.contains("pronation"));
169 }
170
171 #[test]
172 fn test_blend() {
173 let a = new_foot_arch_state();
174 let mut b = new_foot_arch_state();
175 b.arch_height = 1.0;
176 let mid = blend_foot_arches(&a, &b, 0.5);
177 assert!((mid.arch_height - 0.75).abs() < 1e-6);
178 }
179
180 #[test]
181 fn test_blend_identity() {
182 let a = new_foot_arch_state();
183 let r = blend_foot_arches(&a, &a, 0.5);
184 assert!((r.arch_height - a.arch_height).abs() < 1e-6);
185 }
186}