Skip to main content

oxihuman_morph/
foot_arch_control.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan) / SPDX-License-Identifier: Apache-2.0 / #![allow(dead_code)]
2#![allow(dead_code)]
3
4//! Foot arch morphology controls for plantar arch shape.
5
6use 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}