Skip to main content

oxihuman_export/
ao_map_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan) / SPDX-License-Identifier: Apache-2.0
2#![allow(dead_code)]
3
4//! Ambient occlusion map export.
5
6#[allow(dead_code)]
7#[derive(Debug, Clone)]
8pub struct AoMapConfig {
9    pub width: usize,
10    pub height: usize,
11    pub samples: usize,
12}
13
14#[allow(dead_code)]
15#[derive(Debug, Clone)]
16pub struct AoMapExport {
17    pub pixels: Vec<f32>,
18    pub width: usize,
19    pub height: usize,
20}
21
22#[allow(dead_code)]
23pub fn default_ao_map_config() -> AoMapConfig {
24    AoMapConfig {
25        width: 256,
26        height: 256,
27        samples: 16,
28    }
29}
30#[allow(dead_code)]
31pub fn new_ao_map(config: &AoMapConfig) -> AoMapExport {
32    AoMapExport {
33        pixels: vec![1.0; config.width * config.height],
34        width: config.width,
35        height: config.height,
36    }
37}
38#[allow(dead_code)]
39pub fn ao_set_pixel(map: &mut AoMapExport, x: usize, y: usize, val: f32) {
40    if x < map.width && y < map.height {
41        map.pixels[y * map.width + x] = val.clamp(0.0, 1.0);
42    }
43}
44#[allow(dead_code)]
45pub fn ao_get_pixel(map: &AoMapExport, x: usize, y: usize) -> f32 {
46    if x < map.width && y < map.height {
47        map.pixels[y * map.width + x]
48    } else {
49        0.0
50    }
51}
52#[allow(dead_code)]
53pub fn ao_pixel_count(map: &AoMapExport) -> usize {
54    map.pixels.len()
55}
56#[allow(dead_code)]
57pub fn ao_average(map: &AoMapExport) -> f32 {
58    if map.pixels.is_empty() {
59        0.0
60    } else {
61        map.pixels.iter().sum::<f32>() / map.pixels.len() as f32
62    }
63}
64#[allow(dead_code)]
65pub fn ao_to_bytes(map: &AoMapExport) -> Vec<u8> {
66    map.pixels.iter().map(|&v| (v * 255.0) as u8).collect()
67}
68#[allow(dead_code)]
69pub fn ao_to_json(map: &AoMapExport) -> String {
70    format!(
71        "{{\"width\":{},\"height\":{},\"pixels\":{}}}",
72        map.width,
73        map.height,
74        map.pixels.len()
75    )
76}
77#[allow(dead_code)]
78pub fn ao_validate(map: &AoMapExport) -> bool {
79    map.pixels.len() == map.width * map.height && map.pixels.iter().all(|v| (0.0..=1.0).contains(v))
80}
81
82// ── New required API ──────────────────────────────────────────────────────────
83
84pub struct AoMap {
85    pub width: u32,
86    pub height: u32,
87    pub data: Vec<f32>,
88}
89
90pub fn new_ao_map_req(w: u32, h: u32) -> AoMap {
91    AoMap {
92        width: w,
93        height: h,
94        data: vec![1.0; (w * h) as usize],
95    }
96}
97
98pub fn ao_set(m: &mut AoMap, x: u32, y: u32, v: f32) {
99    if x < m.width && y < m.height {
100        m.data[(y * m.width + x) as usize] = v.clamp(0.0, 1.0);
101    }
102}
103
104pub fn ao_get(m: &AoMap, x: u32, y: u32) -> f32 {
105    if x < m.width && y < m.height {
106        m.data[(y * m.width + x) as usize]
107    } else {
108        0.0
109    }
110}
111
112pub fn ao_to_u8(m: &AoMap) -> Vec<u8> {
113    m.data.iter().map(|&v| (v * 255.0) as u8).collect()
114}
115
116pub fn ao_mean(m: &AoMap) -> f32 {
117    if m.data.is_empty() {
118        return 0.0;
119    }
120    m.data.iter().sum::<f32>() / m.data.len() as f32
121}
122
123pub fn ao_to_bytes_req(m: &AoMap) -> Vec<u8> {
124    let mut b = Vec::new();
125    b.extend_from_slice(&m.width.to_le_bytes());
126    b.extend_from_slice(&m.height.to_le_bytes());
127    for &v in &m.data {
128        b.extend_from_slice(&v.to_le_bytes());
129    }
130    b
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    #[test]
137    fn test_default() {
138        let c = default_ao_map_config();
139        assert_eq!(c.width, 256);
140    }
141    #[test]
142    fn test_new() {
143        let m = new_ao_map(&default_ao_map_config());
144        assert_eq!(m.pixels.len(), 256 * 256);
145    }
146    #[test]
147    fn test_set_get() {
148        let mut m = new_ao_map(&AoMapConfig {
149            width: 4,
150            height: 4,
151            samples: 1,
152        });
153        ao_set_pixel(&mut m, 1, 1, 0.5);
154        assert!((ao_get_pixel(&m, 1, 1) - 0.5).abs() < 1e-6);
155    }
156    #[test]
157    fn test_pixel_count() {
158        let m = new_ao_map(&AoMapConfig {
159            width: 4,
160            height: 4,
161            samples: 1,
162        });
163        assert_eq!(ao_pixel_count(&m), 16);
164    }
165    #[test]
166    fn test_average() {
167        let m = new_ao_map(&AoMapConfig {
168            width: 2,
169            height: 2,
170            samples: 1,
171        });
172        assert!((ao_average(&m) - 1.0).abs() < 1e-6);
173    }
174    #[test]
175    fn test_to_bytes() {
176        let m = new_ao_map(&AoMapConfig {
177            width: 2,
178            height: 2,
179            samples: 1,
180        });
181        let b = ao_to_bytes(&m);
182        assert_eq!(b.len(), 4);
183    }
184    #[test]
185    fn test_to_json() {
186        let m = new_ao_map(&AoMapConfig {
187            width: 2,
188            height: 2,
189            samples: 1,
190        });
191        assert!(ao_to_json(&m).contains("width"));
192    }
193    #[test]
194    fn test_validate() {
195        let m = new_ao_map(&AoMapConfig {
196            width: 2,
197            height: 2,
198            samples: 1,
199        });
200        assert!(ao_validate(&m));
201    }
202    #[test]
203    fn test_clamp() {
204        let mut m = new_ao_map(&AoMapConfig {
205            width: 2,
206            height: 2,
207            samples: 1,
208        });
209        ao_set_pixel(&mut m, 0, 0, 2.0);
210        assert!((ao_get_pixel(&m, 0, 0) - 1.0).abs() < 1e-6);
211    }
212    #[test]
213    fn test_oob() {
214        let m = new_ao_map(&AoMapConfig {
215            width: 2,
216            height: 2,
217            samples: 1,
218        });
219        assert!((ao_get_pixel(&m, 99, 99)).abs() < 1e-6);
220    }
221}