1use std::collections::HashMap;
6use std::io::Cursor;
7
8use crate::types::{Aabb, VoxelKey};
9use byteorder::{LittleEndian, ReadBytesExt};
10
11use crate::byte_source::ByteSource;
12use crate::error::CopcError;
13use crate::header::CopcInfo;
14
15#[derive(Debug, Clone)]
20#[non_exhaustive]
21pub struct HierarchyEntry {
22 pub key: VoxelKey,
24 pub offset: u64,
26 pub byte_size: u32,
28 pub point_count: u32,
30}
31
32#[derive(Debug, Clone)]
34struct PendingPage {
35 key: VoxelKey,
37 offset: u64,
38 size: u64,
39}
40
41pub struct HierarchyCache {
43 entries: HashMap<VoxelKey, HierarchyEntry>,
45 pending_pages: Vec<PendingPage>,
47 root_loaded: bool,
49}
50
51impl Default for HierarchyCache {
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl HierarchyCache {
58 pub fn new() -> Self {
60 Self {
61 entries: HashMap::new(),
62 pending_pages: Vec::new(),
63 root_loaded: false,
64 }
65 }
66
67 pub async fn load_root(
69 &mut self,
70 source: &impl ByteSource,
71 info: &CopcInfo,
72 ) -> Result<(), CopcError> {
73 if self.root_loaded {
74 return Ok(());
75 }
76 let data = source
77 .read_range(info.root_hier_offset, info.root_hier_size)
78 .await?;
79 self.parse_page(&data, info.root_hier_offset)?;
80 self.root_loaded = true;
81 Ok(())
82 }
83
84 pub async fn load_pending_pages(&mut self, source: &impl ByteSource) -> Result<(), CopcError> {
86 if self.pending_pages.is_empty() {
87 return Ok(());
88 }
89 let pages: Vec<_> = self.pending_pages.drain(..).collect();
90 let ranges: Vec<_> = pages.iter().map(|p| (p.offset, p.size)).collect();
91 let results = source.read_ranges(&ranges).await?;
92
93 for (page, data) in pages.iter().zip(results) {
94 self.parse_page(&data, page.offset)?;
95 }
96
97 Ok(())
98 }
99
100 pub async fn load_all(
106 &mut self,
107 source: &impl ByteSource,
108 info: &CopcInfo,
109 ) -> Result<(), CopcError> {
110 self.load_root(source, info).await?;
111
112 while !self.pending_pages.is_empty() {
113 self.load_pending_pages(source).await?;
114 }
115
116 Ok(())
117 }
118
119 pub async fn load_pages_for_bounds(
126 &mut self,
127 source: &impl ByteSource,
128 bounds: &Aabb,
129 root_bounds: &Aabb,
130 ) -> Result<(), CopcError> {
131 loop {
132 let matching: Vec<PendingPage> = self
133 .pending_pages
134 .iter()
135 .filter(|p| p.key.bounds(root_bounds).intersects(bounds))
136 .cloned()
137 .collect();
138
139 if matching.is_empty() {
140 break;
141 }
142
143 self.pending_pages
144 .retain(|p| !p.key.bounds(root_bounds).intersects(bounds));
145
146 let ranges: Vec<_> = matching.iter().map(|p| (p.offset, p.size)).collect();
147 let results = source.read_ranges(&ranges).await?;
148
149 for (page, data) in matching.iter().zip(results) {
150 self.parse_page(&data, page.offset)?;
151 }
152 }
153
154 Ok(())
155 }
156
157 pub async fn load_pages_for_bounds_to_level(
161 &mut self,
162 source: &impl ByteSource,
163 bounds: &Aabb,
164 root_bounds: &Aabb,
165 max_level: i32,
166 ) -> Result<(), CopcError> {
167 loop {
168 let matching: Vec<PendingPage> = self
169 .pending_pages
170 .iter()
171 .filter(|p| {
172 p.key.level <= max_level && p.key.bounds(root_bounds).intersects(bounds)
173 })
174 .cloned()
175 .collect();
176
177 if matching.is_empty() {
178 break;
179 }
180
181 self.pending_pages.retain(|p| {
182 !(p.key.level <= max_level && p.key.bounds(root_bounds).intersects(bounds))
183 });
184
185 let ranges: Vec<_> = matching.iter().map(|p| (p.offset, p.size)).collect();
186 let results = source.read_ranges(&ranges).await?;
187
188 for (page, data) in matching.iter().zip(results) {
189 self.parse_page(&data, page.offset)?;
190 }
191 }
192
193 Ok(())
194 }
195
196 pub fn has_pending_pages(&self) -> bool {
198 !self.pending_pages.is_empty()
199 }
200
201 pub fn pending_page_keys(&self) -> impl Iterator<Item = VoxelKey> + '_ {
211 self.pending_pages.iter().map(|p| p.key)
212 }
213
214 pub fn get(&self, key: &VoxelKey) -> Option<&HierarchyEntry> {
216 self.entries.get(key)
217 }
218
219 pub fn iter(&self) -> impl Iterator<Item = (&VoxelKey, &HierarchyEntry)> {
221 self.entries.iter()
222 }
223
224 pub fn len(&self) -> usize {
226 self.entries.len()
227 }
228
229 pub fn is_empty(&self) -> bool {
231 self.entries.is_empty()
232 }
233
234 fn parse_page(&mut self, data: &[u8], _base_offset: u64) -> Result<(), CopcError> {
236 let entry_size = 32; let mut r = Cursor::new(data);
238
239 while (r.position() as usize + entry_size) <= data.len() {
240 let level = r.read_i32::<LittleEndian>()?;
241 let x = r.read_i32::<LittleEndian>()?;
242 let y = r.read_i32::<LittleEndian>()?;
243 let z = r.read_i32::<LittleEndian>()?;
244 let offset = r.read_u64::<LittleEndian>()?;
245 let byte_size = r.read_i32::<LittleEndian>()?;
246 let point_count = r.read_i32::<LittleEndian>()?;
247
248 let key = VoxelKey { level, x, y, z };
249
250 if point_count == -1 {
251 self.pending_pages.push(PendingPage {
253 key,
254 offset,
255 size: byte_size as u64,
256 });
257 } else if point_count >= 0 && byte_size >= 0 {
258 self.entries.insert(
259 key,
260 HierarchyEntry {
261 key,
262 offset,
263 byte_size: byte_size as u32,
264 point_count: point_count as u32,
265 },
266 );
267 }
268 }
270
271 Ok(())
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278 use byteorder::WriteBytesExt;
279
280 #[allow(clippy::too_many_arguments)]
281 fn write_hierarchy_entry(
282 buf: &mut Vec<u8>,
283 level: i32,
284 x: i32,
285 y: i32,
286 z: i32,
287 offset: u64,
288 byte_size: i32,
289 point_count: i32,
290 ) {
291 buf.write_i32::<LittleEndian>(level).unwrap();
292 buf.write_i32::<LittleEndian>(x).unwrap();
293 buf.write_i32::<LittleEndian>(y).unwrap();
294 buf.write_i32::<LittleEndian>(z).unwrap();
295 buf.write_u64::<LittleEndian>(offset).unwrap();
296 buf.write_i32::<LittleEndian>(byte_size).unwrap();
297 buf.write_i32::<LittleEndian>(point_count).unwrap();
298 }
299
300 fn build_two_child_source() -> (Vec<u8>, Aabb) {
309 let root_bounds = Aabb {
310 min: [0.0, 0.0, 0.0],
311 max: [100.0, 100.0, 100.0],
312 };
313
314 let mut left_page = Vec::new();
316 write_hierarchy_entry(&mut left_page, 2, 0, 0, 0, 9000, 64, 10);
318
319 let mut right_page = Vec::new();
320 write_hierarchy_entry(&mut right_page, 2, 2, 0, 0, 9500, 64, 20);
322
323 let mut root_page = Vec::new();
325 write_hierarchy_entry(&mut root_page, 0, 0, 0, 0, 1000, 256, 100);
326
327 let root_page_size = 3 * 32;
329 let left_page_offset = root_page_size as u64;
330 let right_page_offset = left_page_offset + left_page.len() as u64;
331
332 write_hierarchy_entry(
334 &mut root_page,
335 1,
336 0,
337 0,
338 0,
339 left_page_offset,
340 left_page.len() as i32,
341 -1,
342 );
343 write_hierarchy_entry(
345 &mut root_page,
346 1,
347 1,
348 0,
349 0,
350 right_page_offset,
351 right_page.len() as i32,
352 -1,
353 );
354
355 let mut source = root_page;
356 source.extend_from_slice(&left_page);
357 source.extend_from_slice(&right_page);
358
359 (source, root_bounds)
360 }
361
362 #[tokio::test]
363 async fn test_load_pages_for_bounds_filters_spatially() {
364 let (source, root_bounds) = build_two_child_source();
365
366 let mut cache = HierarchyCache::new();
367 cache.parse_page(&source[..96], 0).unwrap();
369
370 assert_eq!(cache.len(), 1); assert_eq!(cache.pending_pages.len(), 2); let left_query = Aabb {
375 min: [0.0, 0.0, 0.0],
376 max: [30.0, 100.0, 100.0],
377 };
378 cache
379 .load_pages_for_bounds(&source, &left_query, &root_bounds)
380 .await
381 .unwrap();
382
383 assert_eq!(cache.len(), 2); assert!(
386 cache
387 .get(&VoxelKey {
388 level: 2,
389 x: 0,
390 y: 0,
391 z: 0,
392 })
393 .is_some()
394 );
395
396 assert_eq!(cache.pending_pages.len(), 1);
398 assert_eq!(
399 cache.pending_pages[0].key,
400 VoxelKey {
401 level: 1,
402 x: 1,
403 y: 0,
404 z: 0
405 }
406 );
407
408 let right_query = Aabb {
410 min: [60.0, 0.0, 0.0],
411 max: [100.0, 100.0, 100.0],
412 };
413 cache
414 .load_pages_for_bounds(&source, &right_query, &root_bounds)
415 .await
416 .unwrap();
417
418 assert_eq!(cache.len(), 3); assert!(
420 cache
421 .get(&VoxelKey {
422 level: 2,
423 x: 2,
424 y: 0,
425 z: 0,
426 })
427 .is_some()
428 );
429 assert!(cache.pending_pages.is_empty());
430 }
431
432 #[tokio::test]
433 async fn test_load_pages_for_bounds_to_level_stops_at_max_level() {
434 let (source, root_bounds) = build_two_child_source();
435
436 let mut cache = HierarchyCache::new();
437 cache.parse_page(&source[..96], 0).unwrap();
438
439 assert_eq!(cache.len(), 1); assert_eq!(cache.pending_pages.len(), 2); let full_query = Aabb {
445 min: [0.0, 0.0, 0.0],
446 max: [100.0, 100.0, 100.0],
447 };
448 cache
449 .load_pages_for_bounds_to_level(&source, &full_query, &root_bounds, 0)
450 .await
451 .unwrap();
452
453 assert_eq!(cache.len(), 1); assert_eq!(cache.pending_pages.len(), 2); cache
458 .load_pages_for_bounds_to_level(&source, &full_query, &root_bounds, 1)
459 .await
460 .unwrap();
461
462 assert_eq!(cache.len(), 3); assert!(cache.pending_pages.is_empty());
464 }
465
466 #[tokio::test]
467 async fn test_parse_hierarchy_page() {
468 let mut page_data = Vec::new();
469 write_hierarchy_entry(&mut page_data, 0, 0, 0, 0, 1000, 256, 100);
470 write_hierarchy_entry(&mut page_data, 1, 0, 0, 0, 2000, 512, 50);
471 write_hierarchy_entry(&mut page_data, 1, 1, 0, 0, 5000, 128, -1);
472
473 let mut cache = HierarchyCache::new();
474 cache.parse_page(&page_data, 0).unwrap();
475
476 assert_eq!(cache.len(), 2); assert_eq!(cache.pending_pages.len(), 1); let root = cache
480 .get(&VoxelKey {
481 level: 0,
482 x: 0,
483 y: 0,
484 z: 0,
485 })
486 .unwrap();
487 assert_eq!(root.point_count, 100);
488 assert_eq!(root.offset, 1000);
489 }
490}