oxihuman_export/
gradient_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub enum GradientType {
11 Linear,
12 Radial,
13}
14
15#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct GradientStop {
19 pub t: f32,
20 pub color: [f32; 4],
21}
22
23#[allow(dead_code)]
25#[derive(Debug, Clone)]
26pub struct GradientExport {
27 pub gradient_type: GradientType,
28 pub stops: Vec<GradientStop>,
29}
30
31#[allow(dead_code)]
32pub fn new_gradient(gtype: GradientType) -> GradientExport {
33 GradientExport {
34 gradient_type: gtype,
35 stops: Vec::new(),
36 }
37}
38
39#[allow(dead_code)]
40pub fn grad_add_stop(g: &mut GradientExport, t: f32, color: [f32; 4]) {
41 g.stops.push(GradientStop {
42 t: t.clamp(0.0, 1.0),
43 color,
44 });
45 g.stops
46 .sort_by(|a, b| a.t.partial_cmp(&b.t).unwrap_or(std::cmp::Ordering::Equal));
47}
48
49#[allow(dead_code)]
50pub fn grad_stop_count(g: &GradientExport) -> usize {
51 g.stops.len()
52}
53
54#[allow(dead_code)]
55pub fn grad_sample(g: &GradientExport, t: f32) -> [f32; 4] {
56 if g.stops.is_empty() {
57 return [0.0; 4];
58 }
59 if g.stops.len() == 1 || t <= g.stops[0].t {
60 return g.stops[0].color;
61 }
62 if let Some(last) = g.stops.last() {
63 if t >= last.t {
64 return last.color;
65 }
66 }
67 for w in g.stops.windows(2) {
68 if t >= w[0].t && t <= w[1].t {
69 let f = (t - w[0].t) / (w[1].t - w[0].t);
70 let mut c = [0.0f32; 4];
71 for (i, ci) in c.iter_mut().enumerate() {
72 *ci = w[0].color[i] * (1.0 - f) + w[1].color[i] * f;
73 }
74 return c;
75 }
76 }
77 g.stops.last().map_or([0.0; 4], |s| s.color)
78}
79
80#[allow(dead_code)]
81pub fn grad_type_name(g: &GradientExport) -> &str {
82 match g.gradient_type {
83 GradientType::Linear => "linear",
84 GradientType::Radial => "radial",
85 }
86}
87
88#[allow(dead_code)]
89pub fn grad_clear(g: &mut GradientExport) {
90 g.stops.clear();
91}
92
93#[allow(dead_code)]
94pub fn gradient_to_json(g: &GradientExport) -> String {
95 format!(
96 "{{\"type\":\"{}\",\"stops\":{}}}",
97 grad_type_name(g),
98 g.stops.len()
99 )
100}
101
102#[allow(dead_code)]
103pub fn grad_validate(g: &GradientExport) -> bool {
104 g.stops.windows(2).all(|w| w[0].t <= w[1].t)
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_new_linear() {
113 assert_eq!(
114 grad_type_name(&new_gradient(GradientType::Linear)),
115 "linear"
116 );
117 }
118
119 #[test]
120 fn test_add_stop() {
121 let mut g = new_gradient(GradientType::Linear);
122 grad_add_stop(&mut g, 0.5, [1.0; 4]);
123 assert_eq!(grad_stop_count(&g), 1);
124 }
125
126 #[test]
127 fn test_sample_lerp() {
128 let mut g = new_gradient(GradientType::Linear);
129 grad_add_stop(&mut g, 0.0, [0.0; 4]);
130 grad_add_stop(&mut g, 1.0, [1.0; 4]);
131 let c = grad_sample(&g, 0.5);
132 assert!((c[0] - 0.5).abs() < 1e-6);
133 }
134
135 #[test]
136 fn test_sample_empty() {
137 assert!((grad_sample(&new_gradient(GradientType::Radial), 0.5)[0]).abs() < 1e-6);
138 }
139
140 #[test]
141 fn test_clear() {
142 let mut g = new_gradient(GradientType::Linear);
143 grad_add_stop(&mut g, 0.0, [0.0; 4]);
144 grad_clear(&mut g);
145 assert_eq!(grad_stop_count(&g), 0);
146 }
147
148 #[test]
149 fn test_validate() {
150 let mut g = new_gradient(GradientType::Linear);
151 grad_add_stop(&mut g, 0.0, [0.0; 4]);
152 grad_add_stop(&mut g, 1.0, [1.0; 4]);
153 assert!(grad_validate(&g));
154 }
155
156 #[test]
157 fn test_to_json() {
158 assert!(
159 gradient_to_json(&new_gradient(GradientType::Radial)).contains("\"type\":\"radial\"")
160 );
161 }
162
163 #[test]
164 fn test_radial_type() {
165 assert_eq!(
166 grad_type_name(&new_gradient(GradientType::Radial)),
167 "radial"
168 );
169 }
170
171 #[test]
172 fn test_sample_before_first() {
173 let mut g = new_gradient(GradientType::Linear);
174 grad_add_stop(&mut g, 0.5, [1.0; 4]);
175 let c = grad_sample(&g, 0.0);
176 assert!((c[0] - 1.0).abs() < 1e-6);
177 }
178
179 #[test]
180 fn test_sorted_insertion() {
181 let mut g = new_gradient(GradientType::Linear);
182 grad_add_stop(&mut g, 0.9, [1.0; 4]);
183 grad_add_stop(&mut g, 0.1, [0.0; 4]);
184 assert!(g.stops[0].t < g.stops[1].t);
185 }
186}