oxihuman_morph/
gum_line_control.rs1#![allow(dead_code)]
3
4use std::f32::consts::PI;
7
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct GumLineControlConfig {
11 pub exposure: f32,
12 pub curvature: f32,
13 pub width: f32,
14}
15
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct GumLineControlState {
19 pub exposure: f32,
20 pub curvature: f32,
21 pub width: f32,
22 pub recession: f32,
23}
24
25#[allow(dead_code)]
26#[derive(Debug, Clone)]
27pub struct GumLineControlWeights {
28 pub exposed: f32,
29 pub curved: f32,
30 pub wide: f32,
31 pub receded: f32,
32 pub minimal: f32,
33}
34
35#[allow(dead_code)]
36pub fn default_gum_line_control_config() -> GumLineControlConfig {
37 GumLineControlConfig {
38 exposure: 0.5,
39 curvature: 0.5,
40 width: 0.5,
41 }
42}
43
44#[allow(dead_code)]
45pub fn new_gum_line_control_state() -> GumLineControlState {
46 GumLineControlState {
47 exposure: 0.5,
48 curvature: 0.5,
49 width: 0.5,
50 recession: 0.5,
51 }
52}
53
54#[allow(dead_code)]
55pub fn set_gum_line_control_exposure(state: &mut GumLineControlState, value: f32) {
56 state.exposure = value.clamp(0.0, 1.0);
57}
58
59#[allow(dead_code)]
60pub fn set_gum_line_control_curvature(state: &mut GumLineControlState, value: f32) {
61 state.curvature = value.clamp(0.0, 1.0);
62}
63
64#[allow(dead_code)]
65pub fn set_gum_line_control_width(state: &mut GumLineControlState, value: f32) {
66 state.width = value.clamp(0.0, 1.0);
67}
68
69#[allow(dead_code)]
70pub fn set_gum_line_control_recession(state: &mut GumLineControlState, value: f32) {
71 state.recession = value.clamp(0.0, 1.0);
72}
73
74#[allow(dead_code)]
75pub fn compute_gum_line_control_weights(
76 state: &GumLineControlState,
77 cfg: &GumLineControlConfig,
78) -> GumLineControlWeights {
79 let exposed = (state.exposure * cfg.exposure * (PI * 0.25).sin()).clamp(0.0, 1.0);
80 let curved = (state.curvature * cfg.curvature).clamp(0.0, 1.0);
81 let wide = (state.width * cfg.width).clamp(0.0, 1.0);
82 let receded = state.recession.clamp(0.0, 1.0);
83 let minimal = (1.0 - state.exposure).clamp(0.0, 1.0);
84 GumLineControlWeights {
85 exposed,
86 curved,
87 wide,
88 receded,
89 minimal,
90 }
91}
92
93#[allow(dead_code)]
94pub fn gum_line_control_to_json(state: &GumLineControlState) -> String {
95 format!(
96 r#"{{\"exposure\":{},\"curvature\":{},\"width\":{},\"recession\":{}}}"#,
97 state.exposure, state.curvature, state.width, state.recession
98 )
99}
100
101#[allow(dead_code)]
102pub fn blend_gum_line_controls(
103 a: &GumLineControlState,
104 b: &GumLineControlState,
105 t: f32,
106) -> GumLineControlState {
107 let t = t.clamp(0.0, 1.0);
108 GumLineControlState {
109 exposure: a.exposure + (b.exposure - a.exposure) * t,
110 curvature: a.curvature + (b.curvature - a.curvature) * t,
111 width: a.width + (b.width - a.width) * t,
112 recession: a.recession + (b.recession - a.recession) * t,
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_default_config() {
122 let cfg = default_gum_line_control_config();
123 assert!((0.0..=1.0).contains(&cfg.exposure));
124 }
125
126 #[test]
127 fn test_new_state() {
128 let s = new_gum_line_control_state();
129 assert!((s.exposure - 0.5).abs() < 1e-6);
130 }
131
132 #[test]
133 fn test_set_exposure_clamp() {
134 let mut s = new_gum_line_control_state();
135 set_gum_line_control_exposure(&mut s, 1.5);
136 assert!((s.exposure - 1.0).abs() < 1e-6);
137 }
138
139 #[test]
140 fn test_set_curvature() {
141 let mut s = new_gum_line_control_state();
142 set_gum_line_control_curvature(&mut s, 0.8);
143 assert!((s.curvature - 0.8).abs() < 1e-6);
144 }
145
146 #[test]
147 fn test_set_width() {
148 let mut s = new_gum_line_control_state();
149 set_gum_line_control_width(&mut s, 0.7);
150 assert!((s.width - 0.7).abs() < 1e-6);
151 }
152
153 #[test]
154 fn test_set_recession() {
155 let mut s = new_gum_line_control_state();
156 set_gum_line_control_recession(&mut s, 0.6);
157 assert!((s.recession - 0.6).abs() < 1e-6);
158 }
159
160 #[test]
161 fn test_compute_weights() {
162 let s = new_gum_line_control_state();
163 let cfg = default_gum_line_control_config();
164 let w = compute_gum_line_control_weights(&s, &cfg);
165 assert!((0.0..=1.0).contains(&w.exposed));
166 assert!((0.0..=1.0).contains(&w.curved));
167 }
168
169 #[test]
170 fn test_to_json() {
171 let s = new_gum_line_control_state();
172 let json = gum_line_control_to_json(&s);
173 assert!(json.contains("exposure"));
174 assert!(json.contains("recession"));
175 }
176
177 #[test]
178 fn test_blend() {
179 let a = new_gum_line_control_state();
180 let mut b = new_gum_line_control_state();
181 b.exposure = 1.0;
182 let mid = blend_gum_line_controls(&a, &b, 0.5);
183 assert!((mid.exposure - 0.75).abs() < 1e-6);
184 }
185
186 #[test]
187 fn test_blend_identity() {
188 let a = new_gum_line_control_state();
189 let r = blend_gum_line_controls(&a, &a, 0.5);
190 assert!((r.exposure - a.exposure).abs() < 1e-6);
191 }
192}