oxihuman_morph/
wrinkle_depth_control.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub enum WrinkleZone {
11 Forehead,
12 Glabella,
13 EyeCorner,
14 NasolabialFold,
15 Mouth,
16 Neck,
17 Custom,
18}
19
20#[allow(dead_code)]
22#[derive(Debug, Clone)]
23pub struct WrinkleDepthParams {
24 pub zone: WrinkleZone,
25 pub depth: f32,
26 pub density: f32,
27 pub softness: f32,
28}
29
30impl WrinkleDepthParams {
31 #[allow(dead_code)]
32 pub fn new(zone: WrinkleZone) -> Self {
33 WrinkleDepthParams {
34 zone,
35 depth: 0.0,
36 density: 0.0,
37 softness: 0.5,
38 }
39 }
40}
41
42#[allow(dead_code)]
43pub fn wd_set_depth(p: &mut WrinkleDepthParams, v: f32) {
44 p.depth = v.clamp(0.0, 1.0);
45}
46
47#[allow(dead_code)]
48pub fn wd_set_density(p: &mut WrinkleDepthParams, v: f32) {
49 p.density = v.clamp(0.0, 1.0);
50}
51
52#[allow(dead_code)]
53pub fn wd_set_softness(p: &mut WrinkleDepthParams, v: f32) {
54 p.softness = v.clamp(0.0, 1.0);
55}
56
57#[allow(dead_code)]
58pub fn wd_reset(p: &mut WrinkleDepthParams) {
59 let zone = p.zone;
60 *p = WrinkleDepthParams::new(zone);
61}
62
63#[allow(dead_code)]
64pub fn wd_is_neutral(p: &WrinkleDepthParams) -> bool {
65 p.depth.abs() < 1e-6 && p.density.abs() < 1e-6
66}
67
68#[allow(dead_code)]
69pub fn wd_visibility(p: &WrinkleDepthParams) -> f32 {
70 p.depth * 0.6 + p.density * 0.4
71}
72
73#[allow(dead_code)]
74pub fn wd_blend(a: &WrinkleDepthParams, b: &WrinkleDepthParams, t: f32) -> WrinkleDepthParams {
75 let t = t.clamp(0.0, 1.0);
76 WrinkleDepthParams {
77 zone: a.zone,
78 depth: a.depth + (b.depth - a.depth) * t,
79 density: a.density + (b.density - a.density) * t,
80 softness: a.softness + (b.softness - a.softness) * t,
81 }
82}
83
84#[allow(dead_code)]
85pub fn wd_to_json(p: &WrinkleDepthParams) -> String {
86 format!(
87 r#"{{"depth":{:.4},"density":{:.4},"softness":{:.4}}}"#,
88 p.depth, p.density, p.softness
89 )
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn new_is_neutral() {
98 assert!(wd_is_neutral(&WrinkleDepthParams::new(
99 WrinkleZone::Forehead
100 )));
101 }
102
103 #[test]
104 fn set_depth_clamps() {
105 let mut p = WrinkleDepthParams::new(WrinkleZone::Glabella);
106 wd_set_depth(&mut p, 3.0);
107 assert!((p.depth - 1.0).abs() < 1e-6);
108 }
109
110 #[test]
111 fn set_density_clamps_negative() {
112 let mut p = WrinkleDepthParams::new(WrinkleZone::Neck);
113 wd_set_density(&mut p, -1.0);
114 assert!(p.density.abs() < 1e-6);
115 }
116
117 #[test]
118 fn reset_clears() {
119 let mut p = WrinkleDepthParams::new(WrinkleZone::Mouth);
120 wd_set_depth(&mut p, 0.7);
121 wd_reset(&mut p);
122 assert!(wd_is_neutral(&p));
123 }
124
125 #[test]
126 fn visibility_zero_when_neutral() {
127 let p = WrinkleDepthParams::new(WrinkleZone::EyeCorner);
128 assert!(wd_visibility(&p).abs() < 1e-6);
129 }
130
131 #[test]
132 fn visibility_increases() {
133 let mut p = WrinkleDepthParams::new(WrinkleZone::Forehead);
134 wd_set_depth(&mut p, 1.0);
135 assert!(wd_visibility(&p) > 0.0);
136 }
137
138 #[test]
139 fn blend_midpoint() {
140 let a = WrinkleDepthParams::new(WrinkleZone::Forehead);
141 let mut b = WrinkleDepthParams::new(WrinkleZone::Forehead);
142 wd_set_depth(&mut b, 1.0);
143 let m = wd_blend(&a, &b, 0.5);
144 assert!((m.depth - 0.5).abs() < 1e-5);
145 }
146
147 #[test]
148 fn zone_preserved_after_reset() {
149 let mut p = WrinkleDepthParams::new(WrinkleZone::NasolabialFold);
150 wd_reset(&mut p);
151 assert_eq!(p.zone, WrinkleZone::NasolabialFold);
152 }
153
154 #[test]
155 fn to_json_contains_depth() {
156 let p = WrinkleDepthParams::new(WrinkleZone::Forehead);
157 assert!(wd_to_json(&p).contains("depth"));
158 }
159}