Skip to main content

oxihuman_morph/
tooth_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5pub struct ToothMorph {
6    pub size: f32,
7    pub protrusion: f32,
8    pub spacing: f32,
9    pub rotation: f32,
10}
11
12pub fn new_tooth_morph() -> ToothMorph {
13    ToothMorph {
14        size: 0.0,
15        protrusion: 0.0,
16        spacing: 0.0,
17        rotation: 0.0,
18    }
19}
20
21pub fn tooth_set_size(m: &mut ToothMorph, v: f32) {
22    m.size = v.clamp(0.0, 1.0);
23}
24
25pub fn tooth_is_prominent(m: &ToothMorph) -> bool {
26    m.protrusion > 0.5
27}
28
29pub fn tooth_overall_weight(m: &ToothMorph) -> f32 {
30    (m.size.abs() + m.protrusion.abs() + m.spacing.abs() + m.rotation.abs()) * 0.25
31}
32
33pub fn tooth_blend(a: &ToothMorph, b: &ToothMorph, t: f32) -> ToothMorph {
34    let t = t.clamp(0.0, 1.0);
35    ToothMorph {
36        size: a.size + (b.size - a.size) * t,
37        protrusion: a.protrusion + (b.protrusion - a.protrusion) * t,
38        spacing: a.spacing + (b.spacing - a.spacing) * t,
39        rotation: a.rotation + (b.rotation - a.rotation) * t,
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn test_new_tooth_morph() {
49        /* default should have all zeros */
50        let m = new_tooth_morph();
51        assert_eq!(m.size, 0.0);
52        assert_eq!(m.protrusion, 0.0);
53    }
54
55    #[test]
56    fn test_tooth_set_size() {
57        /* set size clamps to 0..=1 */
58        let mut m = new_tooth_morph();
59        tooth_set_size(&mut m, 0.7);
60        assert!((m.size - 0.7).abs() < 1e-6);
61    }
62
63    #[test]
64    fn test_tooth_is_prominent() {
65        /* protrusion > 0.5 means prominent */
66        let mut m = new_tooth_morph();
67        m.protrusion = 0.6;
68        assert!(tooth_is_prominent(&m));
69        m.protrusion = 0.3;
70        assert!(!tooth_is_prominent(&m));
71    }
72
73    #[test]
74    fn test_tooth_overall_weight() {
75        /* all fields zero -> weight zero */
76        let m = new_tooth_morph();
77        assert_eq!(tooth_overall_weight(&m), 0.0);
78    }
79
80    #[test]
81    fn test_tooth_blend() {
82        /* blend at t=0 returns a, at t=1 returns b */
83        let a = ToothMorph {
84            size: 0.0,
85            protrusion: 0.0,
86            spacing: 0.0,
87            rotation: 0.0,
88        };
89        let b = ToothMorph {
90            size: 1.0,
91            protrusion: 1.0,
92            spacing: 1.0,
93            rotation: 1.0,
94        };
95        let r = tooth_blend(&a, &b, 0.5);
96        assert!((r.size - 0.5).abs() < 1e-6);
97    }
98}