Skip to main content

oxihuman_morph/
brow_ridge_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//! Brow ridge morphology control for character faces.
5
6/// Brow ridge parameters.
7#[allow(dead_code)]
8#[derive(Debug, Clone)]
9pub struct BrowRidgeParams {
10    pub prominence: f32,
11    pub width: f32,
12    pub height: f32,
13    pub asymmetry: f32,
14}
15
16/// Result of brow ridge evaluation.
17#[allow(dead_code)]
18#[derive(Debug, Clone)]
19pub struct BrowRidgeResult {
20    pub left_weight: f32,
21    pub right_weight: f32,
22    pub prominence_weight: f32,
23    pub width_weight: f32,
24}
25
26/// Default brow ridge parameters.
27#[allow(dead_code)]
28pub fn default_brow_ridge() -> BrowRidgeParams {
29    BrowRidgeParams {
30        prominence: 0.5,
31        width: 0.5,
32        height: 0.5,
33        asymmetry: 0.0,
34    }
35}
36
37/// Clamp a value to morph range.
38#[allow(dead_code)]
39pub fn clamp_morph(v: f32) -> f32 {
40    v.clamp(0.0, 1.0)
41}
42
43/// Evaluate brow ridge morph weights.
44#[allow(dead_code)]
45pub fn evaluate_brow_ridge(params: &BrowRidgeParams) -> BrowRidgeResult {
46    let p = clamp_morph(params.prominence);
47    let w = clamp_morph(params.width);
48    let asym = params.asymmetry.clamp(-1.0, 1.0);
49    let base = p * 0.7 + w * 0.3;
50    BrowRidgeResult {
51        left_weight: (base + asym * 0.5).clamp(0.0, 1.0),
52        right_weight: (base - asym * 0.5).clamp(0.0, 1.0),
53        prominence_weight: p,
54        width_weight: w,
55    }
56}
57
58/// Blend two brow ridge params.
59#[allow(dead_code)]
60pub fn blend_brow_ridge(a: &BrowRidgeParams, b: &BrowRidgeParams, t: f32) -> BrowRidgeParams {
61    let t = t.clamp(0.0, 1.0);
62    BrowRidgeParams {
63        prominence: a.prominence + (b.prominence - a.prominence) * t,
64        width: a.width + (b.width - a.width) * t,
65        height: a.height + (b.height - a.height) * t,
66        asymmetry: a.asymmetry + (b.asymmetry - a.asymmetry) * t,
67    }
68}
69
70/// Set prominence and clamp.
71#[allow(dead_code)]
72pub fn set_prominence(params: &mut BrowRidgeParams, value: f32) {
73    params.prominence = clamp_morph(value);
74}
75
76/// Set width and clamp.
77#[allow(dead_code)]
78pub fn set_ridge_width(params: &mut BrowRidgeParams, value: f32) {
79    params.width = clamp_morph(value);
80}
81
82/// Compute combined intensity.
83#[allow(dead_code)]
84pub fn ridge_intensity(params: &BrowRidgeParams) -> f32 {
85    (params.prominence * 0.6 + params.width * 0.4).clamp(0.0, 1.0)
86}
87
88/// Check if ridge is in valid range.
89#[allow(dead_code)]
90pub fn is_valid_ridge(params: &BrowRidgeParams) -> bool {
91    (0.0..=1.0).contains(&params.prominence)
92        && (0.0..=1.0).contains(&params.width)
93        && (0.0..=1.0).contains(&params.height)
94        && (-1.0..=1.0).contains(&params.asymmetry)
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_default_brow_ridge() {
103        let p = default_brow_ridge();
104        assert!((p.prominence - 0.5).abs() < 1e-6);
105        assert!((p.asymmetry).abs() < 1e-6);
106    }
107
108    #[test]
109    fn test_clamp_morph() {
110        assert!((clamp_morph(-0.5)).abs() < 1e-6);
111        assert!((clamp_morph(1.5) - 1.0).abs() < 1e-6);
112    }
113
114    #[test]
115    fn test_evaluate_brow_ridge() {
116        let p = default_brow_ridge();
117        let r = evaluate_brow_ridge(&p);
118        assert!((0.0..=1.0).contains(&r.left_weight));
119        assert!((0.0..=1.0).contains(&r.right_weight));
120    }
121
122    #[test]
123    fn test_blend_brow_ridge() {
124        let a = default_brow_ridge();
125        let mut b = default_brow_ridge();
126        b.prominence = 1.0;
127        let c = blend_brow_ridge(&a, &b, 0.5);
128        assert!((c.prominence - 0.75).abs() < 1e-6);
129    }
130
131    #[test]
132    fn test_set_prominence() {
133        let mut p = default_brow_ridge();
134        set_prominence(&mut p, 0.8);
135        assert!((p.prominence - 0.8).abs() < 1e-6);
136    }
137
138    #[test]
139    fn test_set_ridge_width() {
140        let mut p = default_brow_ridge();
141        set_ridge_width(&mut p, 0.3);
142        assert!((p.width - 0.3).abs() < 1e-6);
143    }
144
145    #[test]
146    fn test_ridge_intensity() {
147        let p = default_brow_ridge();
148        let v = ridge_intensity(&p);
149        assert!((0.0..=1.0).contains(&v));
150    }
151
152    #[test]
153    fn test_is_valid_ridge() {
154        let p = default_brow_ridge();
155        assert!(is_valid_ridge(&p));
156    }
157
158    #[test]
159    fn test_asymmetry_effect() {
160        let mut p = default_brow_ridge();
161        p.asymmetry = 1.0;
162        let r = evaluate_brow_ridge(&p);
163        assert!(r.left_weight > r.right_weight);
164    }
165
166    #[test]
167    fn test_invalid_ridge() {
168        let p = BrowRidgeParams {
169            prominence: 2.0,
170            width: 0.5,
171            height: 0.5,
172            asymmetry: 0.0,
173        };
174        assert!(!is_valid_ridge(&p));
175    }
176}