oxihuman_morph/
hair_wave_morph.rs1#![allow(dead_code)]
4
5use std::f32::consts::PI;
8
9#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum CurlPattern {
12 Straight,
13 Wavy,
14 Curly,
15 Coily,
16}
17
18#[derive(Debug, Clone)]
20pub struct HairWaveMorph {
21 pub pattern: CurlPattern,
22 pub amplitude: f32,
23 pub frequency: f32,
24 pub tightness: f32,
25}
26
27impl HairWaveMorph {
28 pub fn new() -> Self {
29 Self {
30 pattern: CurlPattern::Straight,
31 amplitude: 0.0,
32 frequency: 1.0,
33 tightness: 0.0,
34 }
35 }
36}
37
38impl Default for HairWaveMorph {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44pub fn new_hair_wave_morph() -> HairWaveMorph {
46 HairWaveMorph::new()
47}
48
49pub fn hair_wave_set_amplitude(morph: &mut HairWaveMorph, amplitude: f32) {
51 morph.amplitude = amplitude.clamp(0.0, 1.0);
52}
53
54pub fn hair_wave_set_frequency(morph: &mut HairWaveMorph, frequency: f32) {
56 morph.frequency = frequency.clamp(0.1, 10.0);
57}
58
59pub fn hair_wave_set_tightness(morph: &mut HairWaveMorph, tightness: f32) {
61 morph.tightness = tightness.clamp(0.0, 1.0);
62}
63
64pub fn hair_wave_displacement_at(morph: &HairWaveMorph, t: f32) -> f32 {
66 morph.amplitude * (morph.frequency * t * PI * 2.0).sin()
67}
68
69pub fn hair_wave_morph_to_json(morph: &HairWaveMorph) -> String {
71 let pattern_str = match morph.pattern {
72 CurlPattern::Straight => "straight",
73 CurlPattern::Wavy => "wavy",
74 CurlPattern::Curly => "curly",
75 CurlPattern::Coily => "coily",
76 };
77 format!(
78 r#"{{"pattern":"{pattern_str}","amplitude":{:.4},"frequency":{:.4},"tightness":{:.4}}}"#,
79 morph.amplitude, morph.frequency, morph.tightness
80 )
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_defaults() {
89 let m = new_hair_wave_morph();
90 assert_eq!(m.pattern, CurlPattern::Straight);
91 assert_eq!(m.amplitude, 0.0);
92 }
93
94 #[test]
95 fn test_amplitude_clamp() {
96 let mut m = new_hair_wave_morph();
97 hair_wave_set_amplitude(&mut m, 5.0);
98 assert_eq!(m.amplitude, 1.0);
99 }
100
101 #[test]
102 fn test_frequency_clamp_low() {
103 let mut m = new_hair_wave_morph();
104 hair_wave_set_frequency(&mut m, 0.0);
105 assert!((m.frequency - 0.1).abs() < 1e-5);
106 }
107
108 #[test]
109 fn test_tightness() {
110 let mut m = new_hair_wave_morph();
111 hair_wave_set_tightness(&mut m, 0.6);
112 assert!((m.tightness - 0.6).abs() < 1e-6);
113 }
114
115 #[test]
116 fn test_displacement_zero_amplitude() {
117 let m = new_hair_wave_morph();
118 assert_eq!(hair_wave_displacement_at(&m, 0.5), 0.0);
119 }
120
121 #[test]
122 fn test_json_contains_pattern() {
123 let m = new_hair_wave_morph();
124 let s = hair_wave_morph_to_json(&m);
125 assert!(s.contains("straight"));
126 }
127
128 #[test]
129 fn test_clone() {
130 let m = new_hair_wave_morph();
131 let m2 = m.clone();
132 assert_eq!(m2.pattern, m.pattern);
133 }
134
135 #[test]
136 fn test_displacement_nonzero() {
137 let mut m = new_hair_wave_morph();
138 hair_wave_set_amplitude(&mut m, 1.0);
139 hair_wave_set_frequency(&mut m, 1.0);
140 let d = hair_wave_displacement_at(&m, 0.25);
141 assert!(d.abs() > 0.9);
142 }
143
144 #[test]
145 fn test_default_trait() {
146 let m: HairWaveMorph = Default::default();
147 assert!((m.frequency - 1.0).abs() < 1e-6);
148 }
149}