oxihuman_export/
geo_instance_export.rs1#![allow(dead_code)]
4
5#[allow(dead_code)]
8#[derive(Debug, Clone)]
9pub struct GeoInstanceEntry {
10 pub mesh_name: String,
11 pub position: [f32; 3],
12 pub rotation: [f32; 4],
13 pub scale: [f32; 3],
14}
15
16#[allow(dead_code)]
17#[derive(Debug, Clone)]
18pub struct GeoInstanceSetExport {
19 pub instances: Vec<GeoInstanceEntry>,
20}
21
22#[allow(dead_code)]
23pub fn new_geo_instance_set() -> GeoInstanceSetExport {
24 GeoInstanceSetExport {
25 instances: Vec::new(),
26 }
27}
28
29#[allow(dead_code)]
30pub fn add_instance_entry(exp: &mut GeoInstanceSetExport, mesh: &str, pos: [f32; 3]) {
31 exp.instances.push(GeoInstanceEntry {
32 mesh_name: mesh.to_string(),
33 position: pos,
34 rotation: [0.0, 0.0, 0.0, 1.0],
35 scale: [1.0, 1.0, 1.0],
36 });
37}
38
39#[allow(dead_code)]
40pub fn instance_count(exp: &GeoInstanceSetExport) -> usize {
41 exp.instances.len()
42}
43
44#[allow(dead_code)]
45pub fn instances_of_mesh<'a>(
46 exp: &'a GeoInstanceSetExport,
47 mesh: &str,
48) -> Vec<&'a GeoInstanceEntry> {
49 exp.instances
50 .iter()
51 .filter(|i| i.mesh_name == mesh)
52 .collect()
53}
54
55#[allow(dead_code)]
56pub fn unique_mesh_names(exp: &GeoInstanceSetExport) -> Vec<String> {
57 let mut names: Vec<String> = exp.instances.iter().map(|i| i.mesh_name.clone()).collect();
58 names.sort_unstable();
59 names.dedup();
60 names
61}
62
63#[allow(dead_code)]
64pub fn instance_bounds(exp: &GeoInstanceSetExport) -> ([f32; 3], [f32; 3]) {
65 if exp.instances.is_empty() {
66 return ([0.0; 3], [0.0; 3]);
67 }
68 let mut mn = [f32::MAX; 3];
69 let mut mx = [f32::MIN; 3];
70 for inst in &exp.instances {
71 for j in 0..3 {
72 mn[j] = mn[j].min(inst.position[j]);
73 mx[j] = mx[j].max(inst.position[j]);
74 }
75 }
76 (mn, mx)
77}
78
79#[allow(dead_code)]
80pub fn geo_instance_to_json(exp: &GeoInstanceSetExport) -> String {
81 format!(
82 "{{\"instance_count\":{},\"mesh_types\":{}}}",
83 instance_count(exp),
84 unique_mesh_names(exp).len()
85 )
86}
87
88#[allow(dead_code)]
89pub fn validate_instances(exp: &GeoInstanceSetExport) -> bool {
90 exp.instances.iter().all(|i| !i.mesh_name.is_empty())
91}
92
93#[allow(dead_code)]
94pub fn clear_instances(exp: &mut GeoInstanceSetExport) {
95 exp.instances.clear();
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_empty() {
104 let exp = new_geo_instance_set();
105 assert_eq!(instance_count(&exp), 0);
106 }
107
108 #[test]
109 fn test_add_instance() {
110 let mut exp = new_geo_instance_set();
111 add_instance_entry(&mut exp, "tree", [1.0, 0.0, 0.0]);
112 assert_eq!(instance_count(&exp), 1);
113 }
114
115 #[test]
116 fn test_instances_of_mesh() {
117 let mut exp = new_geo_instance_set();
118 add_instance_entry(&mut exp, "rock", [0.0; 3]);
119 add_instance_entry(&mut exp, "tree", [1.0, 0.0, 0.0]);
120 add_instance_entry(&mut exp, "rock", [2.0, 0.0, 0.0]);
121 assert_eq!(instances_of_mesh(&exp, "rock").len(), 2);
122 }
123
124 #[test]
125 fn test_unique_mesh_names() {
126 let mut exp = new_geo_instance_set();
127 add_instance_entry(&mut exp, "tree", [0.0; 3]);
128 add_instance_entry(&mut exp, "rock", [0.0; 3]);
129 add_instance_entry(&mut exp, "tree", [1.0, 0.0, 0.0]);
130 assert_eq!(unique_mesh_names(&exp).len(), 2);
131 }
132
133 #[test]
134 fn test_bounds() {
135 let mut exp = new_geo_instance_set();
136 add_instance_entry(&mut exp, "x", [-1.0, 0.0, 0.0]);
137 add_instance_entry(&mut exp, "x", [2.0, 0.0, 0.0]);
138 let (mn, mx) = instance_bounds(&exp);
139 assert!((mn[0] - -1.0).abs() < 1e-5);
140 assert!((mx[0] - 2.0).abs() < 1e-5);
141 }
142
143 #[test]
144 fn test_json_output() {
145 let exp = new_geo_instance_set();
146 let j = geo_instance_to_json(&exp);
147 assert!(j.contains("instance_count"));
148 }
149
150 #[test]
151 fn test_validate_ok() {
152 let mut exp = new_geo_instance_set();
153 add_instance_entry(&mut exp, "mesh", [0.0; 3]);
154 assert!(validate_instances(&exp));
155 }
156
157 #[test]
158 fn test_clear() {
159 let mut exp = new_geo_instance_set();
160 add_instance_entry(&mut exp, "mesh", [0.0; 3]);
161 clear_instances(&mut exp);
162 assert_eq!(instance_count(&exp), 0);
163 }
164
165 #[test]
166 fn test_default_scale_one() {
167 let mut exp = new_geo_instance_set();
168 add_instance_entry(&mut exp, "mesh", [0.0; 3]);
169 assert_eq!(exp.instances[0].scale, [1.0, 1.0, 1.0]);
170 }
171
172 #[test]
173 fn test_default_rotation_identity() {
174 let mut exp = new_geo_instance_set();
175 add_instance_entry(&mut exp, "mesh", [0.0; 3]);
176 assert!((exp.instances[0].rotation[3] - 1.0).abs() < 1e-6);
177 }
178}