oxihuman_morph/
brow_ridge_control.rs1#![allow(dead_code)]
3
4#[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#[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#[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#[allow(dead_code)]
39pub fn clamp_morph(v: f32) -> f32 {
40 v.clamp(0.0, 1.0)
41}
42
43#[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#[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#[allow(dead_code)]
72pub fn set_prominence(params: &mut BrowRidgeParams, value: f32) {
73 params.prominence = clamp_morph(value);
74}
75
76#[allow(dead_code)]
78pub fn set_ridge_width(params: &mut BrowRidgeParams, value: f32) {
79 params.width = clamp_morph(value);
80}
81
82#[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#[allow(dead_code)]
90pub fn is_valid_ridge(params: &BrowRidgeParams) -> bool {
91 (0.0..=1.0).contains(¶ms.prominence)
92 && (0.0..=1.0).contains(¶ms.width)
93 && (0.0..=1.0).contains(¶ms.height)
94 && (-1.0..=1.0).contains(¶ms.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}