Skip to main content

oxihuman_export/
vdb_export.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3
4//! OpenVDB volume stub export.
5
6#![allow(dead_code)]
7
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct VdbConfig {
11    pub grid_name: String,
12    pub voxel_size: f32,
13    pub background_value: f32,
14}
15
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct VdbGrid {
19    pub config: VdbConfig,
20    pub active_voxels: Vec<([i32; 3], f32)>,
21}
22
23#[allow(dead_code)]
24#[derive(Debug, Clone)]
25pub struct VdbExportResult {
26    pub voxel_count: usize,
27    pub min_value: f32,
28    pub max_value: f32,
29}
30
31#[allow(dead_code)]
32pub fn default_vdb_config() -> VdbConfig {
33    VdbConfig {
34        grid_name: "density".to_string(),
35        voxel_size: 0.1,
36        background_value: 0.0,
37    }
38}
39
40#[allow(dead_code)]
41pub fn new_vdb_grid(config: VdbConfig) -> VdbGrid {
42    VdbGrid {
43        config,
44        active_voxels: Vec::new(),
45    }
46}
47
48#[allow(dead_code)]
49pub fn vdb_set_voxel(grid: &mut VdbGrid, coord: [i32; 3], value: f32) {
50    if let Some(entry) = grid.active_voxels.iter_mut().find(|(c, _)| *c == coord) {
51        entry.1 = value;
52    } else {
53        grid.active_voxels.push((coord, value));
54    }
55}
56
57#[allow(dead_code)]
58pub fn vdb_get_voxel(grid: &VdbGrid, coord: [i32; 3]) -> f32 {
59    grid.active_voxels
60        .iter()
61        .find(|(c, _)| *c == coord)
62        .map(|(_, v)| *v)
63        .unwrap_or(grid.config.background_value)
64}
65
66#[allow(dead_code)]
67pub fn vdb_active_count(grid: &VdbGrid) -> usize {
68    grid.active_voxels.len()
69}
70
71#[allow(dead_code)]
72pub fn vdb_clear(grid: &mut VdbGrid) {
73    grid.active_voxels.clear();
74}
75
76#[allow(dead_code)]
77pub fn vdb_stats(grid: &VdbGrid) -> VdbExportResult {
78    if grid.active_voxels.is_empty() {
79        return VdbExportResult {
80            voxel_count: 0,
81            min_value: grid.config.background_value,
82            max_value: grid.config.background_value,
83        };
84    }
85    let values: Vec<f32> = grid.active_voxels.iter().map(|(_, v)| *v).collect();
86    let min_value = values.iter().cloned().fold(f32::MAX, f32::min);
87    let max_value = values.iter().cloned().fold(f32::MIN, f32::max);
88    VdbExportResult {
89        voxel_count: grid.active_voxels.len(),
90        min_value,
91        max_value,
92    }
93}
94
95#[allow(dead_code)]
96pub fn vdb_export_to_bytes(grid: &VdbGrid) -> Vec<u8> {
97    // Stub: encode as simple binary (coord + value per active voxel).
98    let mut bytes = Vec::with_capacity(grid.active_voxels.len() * 16);
99    for (coord, value) in &grid.active_voxels {
100        for &c in coord {
101            bytes.extend_from_slice(&c.to_le_bytes());
102        }
103        bytes.extend_from_slice(&value.to_le_bytes());
104    }
105    bytes
106}
107
108#[allow(dead_code)]
109pub fn vdb_to_json(grid: &VdbGrid) -> String {
110    let stats = vdb_stats(grid);
111    format!(
112        "{{\"grid_name\":\"{}\",\"voxel_size\":{},\"active_voxels\":{},\"min\":{},\"max\":{}}}",
113        grid.config.grid_name,
114        grid.config.voxel_size,
115        stats.voxel_count,
116        stats.min_value,
117        stats.max_value
118    )
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_default_config() {
127        let cfg = default_vdb_config();
128        assert_eq!(cfg.grid_name, "density");
129    }
130
131    #[test]
132    fn test_set_get_voxel() {
133        let mut grid = new_vdb_grid(default_vdb_config());
134        vdb_set_voxel(&mut grid, [0, 0, 0], 1.0);
135        assert!((vdb_get_voxel(&grid, [0, 0, 0]) - 1.0).abs() < 1e-6);
136    }
137
138    #[test]
139    fn test_get_background() {
140        let grid = new_vdb_grid(default_vdb_config());
141        assert!((vdb_get_voxel(&grid, [5, 5, 5]) - 0.0).abs() < 1e-6);
142    }
143
144    #[test]
145    fn test_active_count() {
146        let mut grid = new_vdb_grid(default_vdb_config());
147        vdb_set_voxel(&mut grid, [0, 0, 0], 1.0);
148        vdb_set_voxel(&mut grid, [1, 0, 0], 0.5);
149        assert_eq!(vdb_active_count(&grid), 2);
150    }
151
152    #[test]
153    fn test_set_voxel_updates() {
154        let mut grid = new_vdb_grid(default_vdb_config());
155        vdb_set_voxel(&mut grid, [0, 0, 0], 1.0);
156        vdb_set_voxel(&mut grid, [0, 0, 0], 2.0);
157        assert_eq!(vdb_active_count(&grid), 1);
158        assert!((vdb_get_voxel(&grid, [0, 0, 0]) - 2.0).abs() < 1e-6);
159    }
160
161    #[test]
162    fn test_clear() {
163        let mut grid = new_vdb_grid(default_vdb_config());
164        vdb_set_voxel(&mut grid, [0, 0, 0], 1.0);
165        vdb_clear(&mut grid);
166        assert_eq!(vdb_active_count(&grid), 0);
167    }
168
169    #[test]
170    fn test_stats() {
171        let mut grid = new_vdb_grid(default_vdb_config());
172        vdb_set_voxel(&mut grid, [0, 0, 0], 0.2);
173        vdb_set_voxel(&mut grid, [1, 0, 0], 0.8);
174        let stats = vdb_stats(&grid);
175        assert!((stats.min_value - 0.2).abs() < 1e-5);
176        assert!((stats.max_value - 0.8).abs() < 1e-5);
177    }
178
179    #[test]
180    fn test_export_bytes() {
181        let mut grid = new_vdb_grid(default_vdb_config());
182        vdb_set_voxel(&mut grid, [0, 0, 0], 1.0);
183        let bytes = vdb_export_to_bytes(&grid);
184        assert_eq!(bytes.len(), 16); // 3*4 + 4 bytes
185    }
186
187    #[test]
188    fn test_to_json() {
189        let grid = new_vdb_grid(default_vdb_config());
190        let j = vdb_to_json(&grid);
191        assert!(j.contains("grid_name"));
192    }
193}