Skip to main content

oxihuman_morph/
eye_size_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Eye aperture/size morph stub.
6
7/// Which eye to target.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub enum EyeSide {
10    Left,
11    Right,
12    Both,
13}
14
15/// Eye size morph controller.
16#[derive(Debug, Clone)]
17pub struct EyeSizeMorph {
18    pub side: EyeSide,
19    pub aperture: f32,
20    pub width: f32,
21    pub height: f32,
22    pub tilt: f32,
23    pub morph_count: usize,
24    pub enabled: bool,
25}
26
27impl EyeSizeMorph {
28    pub fn new(morph_count: usize) -> Self {
29        EyeSizeMorph {
30            side: EyeSide::Both,
31            aperture: 0.5,
32            width: 0.5,
33            height: 0.5,
34            tilt: 0.0,
35            morph_count,
36            enabled: true,
37        }
38    }
39}
40
41/// Create a new eye size morph controller.
42pub fn new_eye_size_morph(morph_count: usize) -> EyeSizeMorph {
43    EyeSizeMorph::new(morph_count)
44}
45
46/// Set target side.
47pub fn esm_set_side(morph: &mut EyeSizeMorph, side: EyeSide) {
48    morph.side = side;
49}
50
51/// Set eye aperture (openness).
52pub fn esm_set_aperture(morph: &mut EyeSizeMorph, aperture: f32) {
53    morph.aperture = aperture.clamp(0.0, 1.0);
54}
55
56/// Set eye width.
57pub fn esm_set_width(morph: &mut EyeSizeMorph, width: f32) {
58    morph.width = width.clamp(0.0, 1.0);
59}
60
61/// Set eye height.
62pub fn esm_set_height(morph: &mut EyeSizeMorph, height: f32) {
63    morph.height = height.clamp(0.0, 1.0);
64}
65
66/// Set eye tilt angle (normalized).
67pub fn esm_set_tilt(morph: &mut EyeSizeMorph, tilt: f32) {
68    morph.tilt = tilt.clamp(-1.0, 1.0);
69}
70
71/// Evaluate morph weights (stub: aperture × size average).
72pub fn esm_evaluate(morph: &EyeSizeMorph) -> Vec<f32> {
73    /* Stub: combined aperture/width/height average */
74    if !morph.enabled || morph.morph_count == 0 {
75        return vec![];
76    }
77    let w = (morph.aperture + morph.width + morph.height) / 3.0;
78    vec![w.clamp(0.0, 1.0); morph.morph_count]
79}
80
81/// Enable or disable.
82pub fn esm_set_enabled(morph: &mut EyeSizeMorph, enabled: bool) {
83    morph.enabled = enabled;
84}
85
86/// Serialize to JSON-like string.
87pub fn esm_to_json(morph: &EyeSizeMorph) -> String {
88    let side = match morph.side {
89        EyeSide::Left => "left",
90        EyeSide::Right => "right",
91        EyeSide::Both => "both",
92    };
93    format!(
94        r#"{{"side":"{}","aperture":{},"width":{},"height":{},"tilt":{},"enabled":{}}}"#,
95        side, morph.aperture, morph.width, morph.height, morph.tilt, morph.enabled
96    )
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_default_side() {
105        let m = new_eye_size_morph(4);
106        assert_eq!(m.side, EyeSide::Both /* default side must be Both */);
107    }
108
109    #[test]
110    fn test_set_side() {
111        let mut m = new_eye_size_morph(4);
112        esm_set_side(&mut m, EyeSide::Left);
113        assert_eq!(m.side, EyeSide::Left /* side must be set */);
114    }
115
116    #[test]
117    fn test_aperture_clamped() {
118        let mut m = new_eye_size_morph(4);
119        esm_set_aperture(&mut m, 2.0);
120        assert!((m.aperture - 1.0).abs() < 1e-6 /* aperture clamped to 1.0 */);
121    }
122
123    #[test]
124    fn test_width_clamped() {
125        let mut m = new_eye_size_morph(4);
126        esm_set_width(&mut m, -0.5);
127        assert!((m.width).abs() < 1e-6 /* width clamped to 0.0 */);
128    }
129
130    #[test]
131    fn test_height_clamped() {
132        let mut m = new_eye_size_morph(4);
133        esm_set_height(&mut m, 1.5);
134        assert!((m.height - 1.0).abs() < 1e-6 /* height clamped to 1.0 */);
135    }
136
137    #[test]
138    fn test_tilt_clamped() {
139        let mut m = new_eye_size_morph(4);
140        esm_set_tilt(&mut m, 3.0);
141        assert!((m.tilt - 1.0).abs() < 1e-6 /* tilt clamped to 1.0 */);
142    }
143
144    #[test]
145    fn test_evaluate_length() {
146        let m = new_eye_size_morph(5);
147        assert_eq!(
148            esm_evaluate(&m).len(),
149            5 /* output must match morph_count */
150        );
151    }
152
153    #[test]
154    fn test_evaluate_disabled() {
155        let mut m = new_eye_size_morph(4);
156        esm_set_enabled(&mut m, false);
157        assert!(esm_evaluate(&m).is_empty() /* disabled must return empty */);
158    }
159
160    #[test]
161    fn test_to_json_has_side() {
162        let m = new_eye_size_morph(4);
163        let j = esm_to_json(&m);
164        assert!(j.contains("\"side\"") /* JSON must have side */);
165    }
166
167    #[test]
168    fn test_enabled_default() {
169        let m = new_eye_size_morph(4);
170        assert!(m.enabled /* must be enabled by default */);
171    }
172}