Skip to main content

oxihuman_morph/
eye_spacing_control.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan) / SPDX-License-Identifier: Apache-2.0 / #![allow(dead_code)]
2#![allow(dead_code)]
3
4//! Eye spacing morphology controls for inter-ocular distance.
5
6use 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}