oxihuman_morph/
eye_spacing_control.rs1#![allow(dead_code)]
3
4use std::f32::consts::PI;
7
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct EyeSpacingConfig {
11 pub distance: f32,
12 pub depth: f32,
13 pub vertical_offset: f32,
14}
15
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct EyeSpacingState {
19 pub distance: f32,
20 pub depth: f32,
21 pub vertical_offset: f32,
22 pub convergence: f32,
23}
24
25#[allow(dead_code)]
26#[derive(Debug, Clone)]
27pub struct EyeSpacingWeights {
28 pub wide_set: f32,
29 pub close_set: f32,
30 pub deep_set: f32,
31 pub shallow: f32,
32 pub converged: f32,
33}
34
35#[allow(dead_code)]
36pub fn default_eye_spacing_config() -> EyeSpacingConfig {
37 EyeSpacingConfig {
38 distance: 0.5,
39 depth: 0.5,
40 vertical_offset: 0.5,
41 }
42}
43
44#[allow(dead_code)]
45pub fn new_eye_spacing_state() -> EyeSpacingState {
46 EyeSpacingState {
47 distance: 0.5,
48 depth: 0.5,
49 vertical_offset: 0.5,
50 convergence: 0.0,
51 }
52}
53
54#[allow(dead_code)]
55pub fn set_eye_distance(state: &mut EyeSpacingState, value: f32) {
56 state.distance = value.clamp(0.0, 1.0);
57}
58
59#[allow(dead_code)]
60pub fn set_eye_depth(state: &mut EyeSpacingState, value: f32) {
61 state.depth = value.clamp(0.0, 1.0);
62}
63
64#[allow(dead_code)]
65pub fn set_eye_vertical_offset(state: &mut EyeSpacingState, value: f32) {
66 state.vertical_offset = value.clamp(0.0, 1.0);
67}
68
69#[allow(dead_code)]
70pub fn set_eye_convergence(state: &mut EyeSpacingState, value: f32) {
71 state.convergence = value.clamp(0.0, 1.0);
72}
73
74#[allow(dead_code)]
75pub fn compute_eye_spacing_weights(
76 state: &EyeSpacingState,
77 cfg: &EyeSpacingConfig,
78) -> EyeSpacingWeights {
79 let d = state.distance * cfg.distance;
80 let wide_set = (d * (PI * 0.25).sin()).clamp(0.0, 1.0);
81 let close_set = (1.0 - d).clamp(0.0, 1.0);
82 let dp = state.depth * cfg.depth;
83 let deep_set = dp.clamp(0.0, 1.0);
84 let shallow = (1.0 - dp).clamp(0.0, 1.0);
85 let converged = state.convergence.clamp(0.0, 1.0);
86 EyeSpacingWeights {
87 wide_set,
88 close_set,
89 deep_set,
90 shallow,
91 converged,
92 }
93}
94
95#[allow(dead_code)]
96pub fn eye_spacing_to_json(state: &EyeSpacingState) -> String {
97 format!(
98 r#"{{"distance":{},"depth":{},"vertical_offset":{},"convergence":{}}}"#,
99 state.distance, state.depth, state.vertical_offset, state.convergence
100 )
101}
102
103#[allow(dead_code)]
104pub fn blend_eye_spacings(a: &EyeSpacingState, b: &EyeSpacingState, t: f32) -> EyeSpacingState {
105 let t = t.clamp(0.0, 1.0);
106 EyeSpacingState {
107 distance: a.distance + (b.distance - a.distance) * t,
108 depth: a.depth + (b.depth - a.depth) * t,
109 vertical_offset: a.vertical_offset + (b.vertical_offset - a.vertical_offset) * t,
110 convergence: a.convergence + (b.convergence - a.convergence) * t,
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_default_config() {
120 let cfg = default_eye_spacing_config();
121 assert!((0.0..=1.0).contains(&cfg.distance));
122 }
123
124 #[test]
125 fn test_new_state() {
126 let s = new_eye_spacing_state();
127 assert!((s.distance - 0.5).abs() < 1e-6);
128 }
129
130 #[test]
131 fn test_set_distance_clamp() {
132 let mut s = new_eye_spacing_state();
133 set_eye_distance(&mut s, 1.5);
134 assert!((s.distance - 1.0).abs() < 1e-6);
135 }
136
137 #[test]
138 fn test_set_depth() {
139 let mut s = new_eye_spacing_state();
140 set_eye_depth(&mut s, 0.8);
141 assert!((s.depth - 0.8).abs() < 1e-6);
142 }
143
144 #[test]
145 fn test_set_vertical_offset() {
146 let mut s = new_eye_spacing_state();
147 set_eye_vertical_offset(&mut s, 0.7);
148 assert!((s.vertical_offset - 0.7).abs() < 1e-6);
149 }
150
151 #[test]
152 fn test_set_convergence() {
153 let mut s = new_eye_spacing_state();
154 set_eye_convergence(&mut s, 0.6);
155 assert!((s.convergence - 0.6).abs() < 1e-6);
156 }
157
158 #[test]
159 fn test_compute_weights() {
160 let s = new_eye_spacing_state();
161 let cfg = default_eye_spacing_config();
162 let w = compute_eye_spacing_weights(&s, &cfg);
163 assert!((0.0..=1.0).contains(&w.wide_set));
164 assert!((0.0..=1.0).contains(&w.deep_set));
165 }
166
167 #[test]
168 fn test_to_json() {
169 let s = new_eye_spacing_state();
170 let json = eye_spacing_to_json(&s);
171 assert!(json.contains("distance"));
172 assert!(json.contains("convergence"));
173 }
174
175 #[test]
176 fn test_blend() {
177 let a = new_eye_spacing_state();
178 let mut b = new_eye_spacing_state();
179 b.distance = 1.0;
180 let mid = blend_eye_spacings(&a, &b, 0.5);
181 assert!((mid.distance - 0.75).abs() < 1e-6);
182 }
183
184 #[test]
185 fn test_blend_identity() {
186 let a = new_eye_spacing_state();
187 let r = blend_eye_spacings(&a, &a, 0.5);
188 assert!((r.distance - a.distance).abs() < 1e-6);
189 }
190}