Skip to main content

oxihuman_morph/
styloid_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5/// Styloid process length morph.
6#[derive(Debug, Clone)]
7pub struct StyloidMorph {
8    /// Process length (0.0 = absent, 1.0 = elongated).
9    pub length: f32,
10    /// Angulation deviation from neutral (-1.0 … 1.0).
11    pub angle_offset: f32,
12    /// Tip sharpness (0.0 = blunt, 1.0 = sharp).
13    pub tip_sharpness: f32,
14}
15
16pub fn new_styloid_morph() -> StyloidMorph {
17    StyloidMorph {
18        length: 0.0,
19        angle_offset: 0.0,
20        tip_sharpness: 0.0,
21    }
22}
23
24pub fn sty_set_length(m: &mut StyloidMorph, v: f32) {
25    m.length = v.clamp(0.0, 1.0);
26}
27
28pub fn sty_set_angle_offset(m: &mut StyloidMorph, v: f32) {
29    m.angle_offset = v.clamp(-1.0, 1.0);
30}
31
32pub fn sty_set_tip_sharpness(m: &mut StyloidMorph, v: f32) {
33    m.tip_sharpness = v.clamp(0.0, 1.0);
34}
35
36pub fn sty_overall_weight(m: &StyloidMorph) -> f32 {
37    (m.length + m.angle_offset.abs() + m.tip_sharpness) / 3.0
38}
39
40pub fn sty_blend(a: &StyloidMorph, b: &StyloidMorph, t: f32) -> StyloidMorph {
41    let t = t.clamp(0.0, 1.0);
42    StyloidMorph {
43        length: a.length + (b.length - a.length) * t,
44        angle_offset: a.angle_offset + (b.angle_offset - a.angle_offset) * t,
45        tip_sharpness: a.tip_sharpness + (b.tip_sharpness - a.tip_sharpness) * t,
46    }
47}
48
49pub fn sty_is_neutral(m: &StyloidMorph) -> bool {
50    m.length < 1e-5 && m.angle_offset.abs() < 1e-5 && m.tip_sharpness < 1e-5
51}
52
53pub fn sty_to_json(m: &StyloidMorph) -> String {
54    format!(
55        r#"{{"length":{:.4},"angle_offset":{:.4},"tip_sharpness":{:.4}}}"#,
56        m.length, m.angle_offset, m.tip_sharpness
57    )
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_new_defaults() {
66        /* all zero */
67        let m = new_styloid_morph();
68        assert_eq!(m.length, 0.0);
69        assert_eq!(m.angle_offset, 0.0);
70        assert_eq!(m.tip_sharpness, 0.0);
71    }
72
73    #[test]
74    fn test_set_length() {
75        /* valid length stored */
76        let mut m = new_styloid_morph();
77        sty_set_length(&mut m, 0.5);
78        assert!((m.length - 0.5).abs() < 1e-6);
79    }
80
81    #[test]
82    fn test_length_clamp_high() {
83        /* clamp above 1 */
84        let mut m = new_styloid_morph();
85        sty_set_length(&mut m, 2.0);
86        assert_eq!(m.length, 1.0);
87    }
88
89    #[test]
90    fn test_angle_offset_clamp() {
91        /* clamp below -1 */
92        let mut m = new_styloid_morph();
93        sty_set_angle_offset(&mut m, -5.0);
94        assert_eq!(m.angle_offset, -1.0);
95    }
96
97    #[test]
98    fn test_is_neutral_true() {
99        /* default neutral */
100        assert!(sty_is_neutral(&new_styloid_morph()));
101    }
102
103    #[test]
104    fn test_is_neutral_false() {
105        /* after set length not neutral */
106        let mut m = new_styloid_morph();
107        sty_set_length(&mut m, 0.2);
108        assert!(!sty_is_neutral(&m));
109    }
110
111    #[test]
112    fn test_overall_weight() {
113        /* formula check */
114        let m = StyloidMorph {
115            length: 0.9,
116            angle_offset: 0.0,
117            tip_sharpness: 0.9,
118        };
119        assert!((sty_overall_weight(&m) - 0.6).abs() < 1e-5);
120    }
121
122    #[test]
123    fn test_blend() {
124        /* blend at 1.0 gives b */
125        let a = new_styloid_morph();
126        let b = StyloidMorph {
127            length: 1.0,
128            angle_offset: 0.5,
129            tip_sharpness: 0.8,
130        };
131        let c = sty_blend(&a, &b, 1.0);
132        assert!((c.length - 1.0).abs() < 1e-6);
133    }
134
135    #[test]
136    fn test_to_json() {
137        /* JSON has length key */
138        assert!(sty_to_json(&new_styloid_morph()).contains("length"));
139    }
140
141    #[test]
142    fn test_clone() {
143        /* clone is independent */
144        let m = StyloidMorph {
145            length: 0.3,
146            angle_offset: 0.2,
147            tip_sharpness: 0.1,
148        };
149        let m2 = m.clone();
150        assert_eq!(m.length, m2.length);
151    }
152}