Skip to main content

oxihuman_morph/
orbital_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Orbital (eye socket) shape morph — rim shape, tilt, and depth control.
6
7/// Orbital morph configuration.
8#[derive(Debug, Clone)]
9pub struct OrbitalMorph {
10    pub width: f32,
11    pub height: f32,
12    pub depth: f32,
13    pub tilt: f32,
14    pub rim_prominence: f32,
15}
16
17impl OrbitalMorph {
18    pub fn new() -> Self {
19        Self {
20            width: 0.5,
21            height: 0.5,
22            depth: 0.5,
23            tilt: 0.0,
24            rim_prominence: 0.5,
25        }
26    }
27}
28
29impl Default for OrbitalMorph {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35/// Create a new orbital morph.
36pub fn new_orbital_morph() -> OrbitalMorph {
37    OrbitalMorph::new()
38}
39
40/// Set orbital width.
41pub fn orb_set_width(m: &mut OrbitalMorph, v: f32) {
42    m.width = v.clamp(0.0, 1.0);
43}
44
45/// Set orbital height.
46pub fn orb_set_height(m: &mut OrbitalMorph, v: f32) {
47    m.height = v.clamp(0.0, 1.0);
48}
49
50/// Set orbital depth (how deep the eye socket is).
51pub fn orb_set_depth(m: &mut OrbitalMorph, v: f32) {
52    m.depth = v.clamp(0.0, 1.0);
53}
54
55/// Set orbital tilt (-1 = medial down, 0 = horizontal, 1 = lateral down).
56pub fn orb_set_tilt(m: &mut OrbitalMorph, v: f32) {
57    m.tilt = v.clamp(-1.0, 1.0);
58}
59
60/// Set supraorbital rim prominence.
61pub fn orb_set_rim_prominence(m: &mut OrbitalMorph, v: f32) {
62    m.rim_prominence = v.clamp(0.0, 1.0);
63}
64
65/// Aperture area estimate (ellipse approximation, normalized).
66pub fn orb_aperture_area(m: &OrbitalMorph) -> f32 {
67    m.width * m.height * std::f32::consts::FRAC_PI_4
68}
69
70/// Serialize to JSON-like string.
71pub fn orbital_morph_to_json(m: &OrbitalMorph) -> String {
72    format!(
73        r#"{{"width":{:.4},"height":{:.4},"depth":{:.4},"tilt":{:.4},"rim_prominence":{:.4}}}"#,
74        m.width, m.height, m.depth, m.tilt, m.rim_prominence
75    )
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_defaults() {
84        let m = new_orbital_morph();
85        assert!((m.width - 0.5).abs() < 1e-6);
86        assert_eq!(m.tilt, 0.0);
87    }
88
89    #[test]
90    fn test_width_clamp() {
91        let mut m = new_orbital_morph();
92        orb_set_width(&mut m, 5.0);
93        assert_eq!(m.width, 1.0);
94    }
95
96    #[test]
97    fn test_height_set() {
98        let mut m = new_orbital_morph();
99        orb_set_height(&mut m, 0.7);
100        assert!((m.height - 0.7).abs() < 1e-6);
101    }
102
103    #[test]
104    fn test_depth_set() {
105        let mut m = new_orbital_morph();
106        orb_set_depth(&mut m, 0.8);
107        assert!((m.depth - 0.8).abs() < 1e-6);
108    }
109
110    #[test]
111    fn test_tilt_clamp() {
112        let mut m = new_orbital_morph();
113        orb_set_tilt(&mut m, 2.0);
114        assert_eq!(m.tilt, 1.0);
115    }
116
117    #[test]
118    fn test_rim_prominence_set() {
119        let mut m = new_orbital_morph();
120        orb_set_rim_prominence(&mut m, 0.9);
121        assert!((m.rim_prominence - 0.9).abs() < 1e-6);
122    }
123
124    #[test]
125    fn test_aperture_area_positive() {
126        let m = new_orbital_morph();
127        assert!(orb_aperture_area(&m) > 0.0);
128    }
129
130    #[test]
131    fn test_json_keys() {
132        let m = new_orbital_morph();
133        let s = orbital_morph_to_json(&m);
134        assert!(s.contains("rim_prominence"));
135    }
136
137    #[test]
138    fn test_clone() {
139        let m = new_orbital_morph();
140        let m2 = m.clone();
141        assert!((m2.depth - m.depth).abs() < 1e-6);
142    }
143}