Skip to main content

oxihuman_morph/
knee_shape_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5pub struct KneeShapeMorph {
6    pub prominence: f32,
7    pub width: f32,
8    pub valgus_angle_deg: f32,
9}
10
11pub fn new_knee_shape_morph() -> KneeShapeMorph {
12    KneeShapeMorph {
13        prominence: 0.3,
14        width: 0.4,
15        valgus_angle_deg: 0.0,
16    }
17}
18
19pub fn knee_set_prominence(m: &mut KneeShapeMorph, v: f32) {
20    m.prominence = v.clamp(0.0, 1.0);
21}
22
23pub fn knee_is_valgus(m: &KneeShapeMorph) -> bool {
24    m.valgus_angle_deg > 5.0
25}
26
27pub fn knee_overall_weight(m: &KneeShapeMorph) -> f32 {
28    (m.prominence + m.width) * 0.5
29}
30
31pub fn knee_blend(a: &KneeShapeMorph, b: &KneeShapeMorph, t: f32) -> KneeShapeMorph {
32    let t = t.clamp(0.0, 1.0);
33    KneeShapeMorph {
34        prominence: a.prominence + (b.prominence - a.prominence) * t,
35        width: a.width + (b.width - a.width) * t,
36        valgus_angle_deg: a.valgus_angle_deg + (b.valgus_angle_deg - a.valgus_angle_deg) * t,
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_new() {
46        /* prominence > 0 */
47        let m = new_knee_shape_morph();
48        assert!(m.prominence > 0.0);
49    }
50
51    #[test]
52    fn test_set_prominence() {
53        /* clamped */
54        let mut m = new_knee_shape_morph();
55        knee_set_prominence(&mut m, 0.9);
56        assert!((m.prominence - 0.9).abs() < 1e-5);
57    }
58
59    #[test]
60    fn test_not_valgus_by_default() {
61        /* default is not valgus */
62        let m = new_knee_shape_morph();
63        assert!(!knee_is_valgus(&m));
64    }
65
66    #[test]
67    fn test_is_valgus() {
68        /* valgus angle > 5 degrees */
69        let m = KneeShapeMorph {
70            prominence: 0.3,
71            width: 0.4,
72            valgus_angle_deg: 10.0,
73        };
74        assert!(knee_is_valgus(&m));
75    }
76
77    #[test]
78    fn test_blend() {
79        /* midpoint */
80        let a = KneeShapeMorph {
81            prominence: 0.0,
82            width: 0.0,
83            valgus_angle_deg: 0.0,
84        };
85        let b = KneeShapeMorph {
86            prominence: 1.0,
87            width: 1.0,
88            valgus_angle_deg: 10.0,
89        };
90        let c = knee_blend(&a, &b, 0.5);
91        assert!((c.prominence - 0.5).abs() < 1e-5);
92        assert!((c.valgus_angle_deg - 5.0).abs() < 1e-4);
93    }
94}