oxihuman_export/
displacement_map_export.rs1#[allow(dead_code)]
5#[derive(Debug, Clone)]
6pub struct DisplacementMapExport {
7 pub width: u32,
8 pub height: u32,
9 pub data: Vec<f32>,
10 pub scale: f32,
11}
12
13#[allow(dead_code)]
14impl DisplacementMapExport {
15 pub fn flat(width: u32, height: u32) -> Self {
17 Self {
18 width,
19 height,
20 data: vec![0.0; (width * height) as usize],
21 scale: 1.0,
22 }
23 }
24
25 pub fn from_data(width: u32, height: u32, data: Vec<f32>, scale: f32) -> Self {
27 Self {
28 width,
29 height,
30 data,
31 scale,
32 }
33 }
34
35 pub fn pixel_count(&self) -> usize {
37 (self.width * self.height) as usize
38 }
39
40 pub fn get(&self, x: u32, y: u32) -> f32 {
42 self.data[(y * self.width + x) as usize] * self.scale
43 }
44
45 pub fn set(&mut self, x: u32, y: u32, value: f32) {
47 self.data[(y * self.width + x) as usize] = value;
48 }
49
50 pub fn range(&self) -> (f32, f32) {
52 let min = self.data.iter().cloned().fold(f32::MAX, f32::min);
53 let max = self.data.iter().cloned().fold(f32::MIN, f32::max);
54 (min * self.scale, max * self.scale)
55 }
56
57 pub fn to_u16_bytes(&self) -> Vec<u8> {
59 let (min, max) = self.range();
60 let range = (max - min).max(1e-10);
61 let mut bytes = Vec::new();
62 bytes.extend_from_slice(&self.width.to_le_bytes());
63 bytes.extend_from_slice(&self.height.to_le_bytes());
64 for &v in &self.data {
65 let norm = ((v * self.scale - min) / range * 65535.0) as u16;
66 bytes.extend_from_slice(&norm.to_le_bytes());
67 }
68 bytes
69 }
70
71 pub fn u16_byte_size(&self) -> usize {
73 8 + self.pixel_count() * 2
74 }
75}
76
77#[allow(dead_code)]
79pub fn displacement_map_to_json(map: &DisplacementMapExport) -> String {
80 let (min, max) = map.range();
81 format!(
82 "{{\"width\":{},\"height\":{},\"scale\":{},\"min\":{},\"max\":{}}}",
83 map.width, map.height, map.scale, min, max
84 )
85}
86
87#[allow(dead_code)]
89pub fn validate_displacement_map(map: &DisplacementMapExport) -> bool {
90 map.data.len() == map.pixel_count() && map.data.iter().all(|v| v.is_finite())
91}
92
93pub struct DisplacementMap {
96 pub width: u32,
97 pub height: u32,
98 pub data: Vec<f32>,
99 pub midlevel: f32,
100 pub strength: f32,
101}
102
103pub fn new_displacement_map(w: u32, h: u32) -> DisplacementMap {
104 DisplacementMap {
105 width: w,
106 height: h,
107 data: vec![0.0; (w * h) as usize],
108 midlevel: 0.5,
109 strength: 1.0,
110 }
111}
112
113pub fn disp_set(m: &mut DisplacementMap, x: u32, y: u32, v: f32) {
114 if x < m.width && y < m.height {
115 m.data[(y * m.width + x) as usize] = v;
116 }
117}
118
119pub fn disp_get(m: &DisplacementMap, x: u32, y: u32) -> f32 {
120 if x < m.width && y < m.height {
121 m.data[(y * m.width + x) as usize]
122 } else {
123 0.0
124 }
125}
126
127pub fn disp_to_u16(m: &DisplacementMap) -> Vec<u16> {
128 m.data
129 .iter()
130 .map(|&v| {
131 let norm = ((v - m.midlevel) * m.strength * 0.5 + 0.5).clamp(0.0, 1.0);
132 (norm * 65535.0) as u16
133 })
134 .collect()
135}
136
137pub fn disp_max_height(m: &DisplacementMap) -> f32 {
138 m.data.iter().cloned().fold(f32::NEG_INFINITY, f32::max)
139}
140
141pub fn disp_to_bytes(m: &DisplacementMap) -> Vec<u8> {
142 let mut b = Vec::new();
143 b.extend_from_slice(&m.width.to_le_bytes());
144 b.extend_from_slice(&m.height.to_le_bytes());
145 for &v in &m.data {
146 b.extend_from_slice(&v.to_le_bytes());
147 }
148 b
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_flat() {
157 let m = DisplacementMapExport::flat(4, 4);
158 assert_eq!(m.pixel_count(), 16);
159 }
160
161 #[test]
162 fn test_get_set() {
163 let mut m = DisplacementMapExport::flat(2, 2);
164 m.set(0, 0, 0.5);
165 assert!((m.get(0, 0) - 0.5).abs() < 1e-5);
166 }
167
168 #[test]
169 fn test_range() {
170 let m = DisplacementMapExport::from_data(2, 1, vec![0.0, 1.0], 2.0);
171 let (min, max) = m.range();
172 assert!((min).abs() < 1e-5);
173 assert!((max - 2.0).abs() < 1e-5);
174 }
175
176 #[test]
177 fn test_to_u16_bytes() {
178 let m = DisplacementMapExport::flat(2, 2);
179 let bytes = m.to_u16_bytes();
180 assert_eq!(bytes.len(), m.u16_byte_size());
181 }
182
183 #[test]
184 fn test_validate() {
185 let m = DisplacementMapExport::flat(2, 2);
186 assert!(validate_displacement_map(&m));
187 }
188
189 #[test]
190 fn test_to_json() {
191 let m = DisplacementMapExport::flat(2, 2);
192 let json = displacement_map_to_json(&m);
193 assert!(json.contains("width"));
194 }
195
196 #[test]
197 fn test_custom_scale() {
198 let m = DisplacementMapExport::from_data(1, 1, vec![1.0], 3.0);
199 assert!((m.get(0, 0) - 3.0).abs() < 1e-5);
200 }
201
202 #[test]
203 fn test_invalid() {
204 let m = DisplacementMapExport {
205 width: 2,
206 height: 2,
207 data: vec![0.0],
208 scale: 1.0,
209 };
210 assert!(!validate_displacement_map(&m));
211 }
212
213 #[test]
214 fn test_pixel_count() {
215 let m = DisplacementMapExport::flat(3, 5);
216 assert_eq!(m.pixel_count(), 15);
217 }
218
219 #[test]
220 fn test_u16_byte_size() {
221 let m = DisplacementMapExport::flat(4, 4);
222 assert_eq!(m.u16_byte_size(), 8 + 32);
223 }
224}