Skip to main content

oxihuman_morph/
columella_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
6pub struct ColumellaMorph {
7    pub inclination: f32,
8    pub width: f32,
9    pub hanging: f32,
10}
11
12pub fn new_columella_morph() -> ColumellaMorph {
13    ColumellaMorph {
14        inclination: 0.0,
15        width: 0.0,
16        hanging: 0.0,
17    }
18}
19
20pub fn columella_set_inclination(m: &mut ColumellaMorph, v: f32) {
21    m.inclination = v.clamp(0.0, 1.0);
22}
23
24pub fn columella_set_width(m: &mut ColumellaMorph, v: f32) {
25    m.width = v.clamp(0.0, 1.0);
26}
27
28pub fn columella_overall_weight(m: &ColumellaMorph) -> f32 {
29    (m.inclination + m.width + m.hanging) / 3.0
30}
31
32pub fn columella_blend(a: &ColumellaMorph, b: &ColumellaMorph, t: f32) -> ColumellaMorph {
33    let t = t.clamp(0.0, 1.0);
34    ColumellaMorph {
35        inclination: a.inclination + (b.inclination - a.inclination) * t,
36        width: a.width + (b.width - a.width) * t,
37        hanging: a.hanging + (b.hanging - a.hanging) * t,
38    }
39}
40
41pub fn columella_is_hanging(m: &ColumellaMorph) -> bool {
42    m.hanging > 0.5
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn test_new_defaults() {
51        /* all zero */
52        let m = new_columella_morph();
53        assert_eq!(m.inclination, 0.0);
54    }
55
56    #[test]
57    fn test_set_inclination() {
58        /* stores valid value */
59        let mut m = new_columella_morph();
60        columella_set_inclination(&mut m, 0.6);
61        assert!((m.inclination - 0.6).abs() < 1e-6);
62    }
63
64    #[test]
65    fn test_set_inclination_clamp() {
66        /* clamps high */
67        let mut m = new_columella_morph();
68        columella_set_inclination(&mut m, 2.0);
69        assert_eq!(m.inclination, 1.0);
70    }
71
72    #[test]
73    fn test_set_width() {
74        /* valid width stored */
75        let mut m = new_columella_morph();
76        columella_set_width(&mut m, 0.3);
77        assert!((m.width - 0.3).abs() < 1e-6);
78    }
79
80    #[test]
81    fn test_overall_weight() {
82        /* average */
83        let m = ColumellaMorph {
84            inclination: 0.3,
85            width: 0.6,
86            hanging: 0.9,
87        };
88        assert!((columella_overall_weight(&m) - 0.6).abs() < 1e-5);
89    }
90
91    #[test]
92    fn test_is_hanging_false() {
93        /* default not hanging */
94        let m = new_columella_morph();
95        assert!(!columella_is_hanging(&m));
96    }
97
98    #[test]
99    fn test_is_hanging_true() {
100        /* above threshold */
101        let m = ColumellaMorph {
102            inclination: 0.0,
103            width: 0.0,
104            hanging: 0.8,
105        };
106        assert!(columella_is_hanging(&m));
107    }
108
109    #[test]
110    fn test_blend() {
111        /* blend at 0.5 */
112        let a = ColumellaMorph {
113            inclination: 0.0,
114            width: 0.0,
115            hanging: 0.0,
116        };
117        let b = ColumellaMorph {
118            inclination: 1.0,
119            width: 1.0,
120            hanging: 1.0,
121        };
122        let c = columella_blend(&a, &b, 0.5);
123        assert!((c.hanging - 0.5).abs() < 1e-6);
124    }
125
126    #[test]
127    fn test_clone() {
128        /* clone independent */
129        let m = ColumellaMorph {
130            inclination: 0.2,
131            width: 0.4,
132            hanging: 0.6,
133        };
134        let m2 = m.clone();
135        assert_eq!(m.hanging, m2.hanging);
136    }
137}