Skip to main content

oxihuman_morph/
nose_root_morph.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! Nose root width morph — controls the nasion width and radix depth.
6
7/// Nose root morph configuration.
8#[derive(Debug, Clone)]
9pub struct NoseRootMorph {
10    pub width: f32,
11    pub depth: f32,
12    pub height: f32,
13    pub bridge_continuity: f32,
14    pub intercanthal_ratio: f32,
15}
16
17impl NoseRootMorph {
18    pub fn new() -> Self {
19        Self {
20            width: 0.5,
21            depth: 0.5,
22            height: 0.5,
23            bridge_continuity: 0.5,
24            intercanthal_ratio: 0.5,
25        }
26    }
27}
28
29impl Default for NoseRootMorph {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35/// Create a new nose root morph.
36pub fn new_nose_root_morph() -> NoseRootMorph {
37    NoseRootMorph::new()
38}
39
40/// Set root width (0 = narrow, 1 = wide).
41pub fn nroot_set_width(m: &mut NoseRootMorph, v: f32) {
42    m.width = v.clamp(0.0, 1.0);
43}
44
45/// Set radix depth (0 = flat/shallow, 1 = deep).
46pub fn nroot_set_depth(m: &mut NoseRootMorph, v: f32) {
47    m.depth = v.clamp(0.0, 1.0);
48}
49
50/// Set root height (vertical position on bridge).
51pub fn nroot_set_height(m: &mut NoseRootMorph, v: f32) {
52    m.height = v.clamp(0.0, 1.0);
53}
54
55/// Set bridge-to-forehead continuity smoothness.
56pub fn nroot_set_bridge_continuity(m: &mut NoseRootMorph, v: f32) {
57    m.bridge_continuity = v.clamp(0.0, 1.0);
58}
59
60/// Compute aspect ratio of root region.
61pub fn nroot_aspect_ratio(m: &NoseRootMorph) -> f32 {
62    if m.depth.abs() < 1e-6 {
63        return 0.0;
64    }
65    m.width / m.depth
66}
67
68/// Serialize to JSON-like string.
69pub fn nose_root_morph_to_json(m: &NoseRootMorph) -> String {
70    format!(
71        r#"{{"width":{:.4},"depth":{:.4},"height":{:.4},"bridge_continuity":{:.4},"intercanthal_ratio":{:.4}}}"#,
72        m.width, m.depth, m.height, m.bridge_continuity, m.intercanthal_ratio
73    )
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_defaults() {
82        let m = new_nose_root_morph();
83        assert!((m.width - 0.5).abs() < 1e-6);
84        assert!((m.depth - 0.5).abs() < 1e-6);
85    }
86
87    #[test]
88    fn test_width_clamp_high() {
89        let mut m = new_nose_root_morph();
90        nroot_set_width(&mut m, 5.0);
91        assert_eq!(m.width, 1.0);
92    }
93
94    #[test]
95    fn test_width_clamp_low() {
96        let mut m = new_nose_root_morph();
97        nroot_set_width(&mut m, -1.0);
98        assert_eq!(m.width, 0.0);
99    }
100
101    #[test]
102    fn test_depth_set() {
103        let mut m = new_nose_root_morph();
104        nroot_set_depth(&mut m, 0.7);
105        assert!((m.depth - 0.7).abs() < 1e-6);
106    }
107
108    #[test]
109    fn test_height_set() {
110        let mut m = new_nose_root_morph();
111        nroot_set_height(&mut m, 0.6);
112        assert!((m.height - 0.6).abs() < 1e-6);
113    }
114
115    #[test]
116    fn test_bridge_continuity_clamp() {
117        let mut m = new_nose_root_morph();
118        nroot_set_bridge_continuity(&mut m, 2.0);
119        assert_eq!(m.bridge_continuity, 1.0);
120    }
121
122    #[test]
123    fn test_aspect_ratio_positive() {
124        let m = new_nose_root_morph();
125        assert!(nroot_aspect_ratio(&m) > 0.0);
126    }
127
128    #[test]
129    fn test_aspect_ratio_zero_depth() {
130        let mut m = new_nose_root_morph();
131        m.depth = 0.0;
132        assert_eq!(nroot_aspect_ratio(&m), 0.0);
133    }
134
135    #[test]
136    fn test_json_keys() {
137        let m = new_nose_root_morph();
138        let s = nose_root_morph_to_json(&m);
139        assert!(s.contains("bridge_continuity"));
140    }
141
142    #[test]
143    fn test_clone() {
144        let m = new_nose_root_morph();
145        let m2 = m.clone();
146        assert!((m2.height - m.height).abs() < 1e-6);
147    }
148}