Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Iterator by Node ID #268

Merged
merged 14 commits into from
Jun 6, 2019
75 changes: 0 additions & 75 deletions src/octree/batch_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,78 +164,3 @@ impl<'a> BatchIterator<'a> {
point_stream.callback()
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::color::Color;
use crate::generation::build_octree;
use cgmath::Point3;
use tempdir::TempDir;

fn build_test_octree(batch_size: usize) -> Box<octree::Octree> {
let default_point = Point {
position: Vector3::new(-2_699_182.0, -4_294_938.0, 3_853_373.0), //ECEF parking lot porter dr
color: Color {
red: 255,
green: 0,
blue: 0,
alpha: 255,
},
intensity: None,
};

let mut points = vec![default_point; 4 * batch_size + 1];
points[3 * batch_size].position = Vector3::new(-2_702_846.0, -4_291_151.0, 3_855_012.0); // ECEF STANFORD

let p = Point3::new(6_400_000.0, 6_400_000.0, 6_400_000.0);
let bounding_box = Aabb3::new(-1.0 * p, p);

let pool = scoped_pool::Pool::new(10);
let tmp_dir = TempDir::new("octree").unwrap();

build_octree(&pool, &tmp_dir, 1.0, bounding_box, points.into_iter());
crate::octree::on_disk::octree_from_directory(tmp_dir.into_path()).unwrap()
}

#[test]
//#[ignore]
fn test_batch_iterator() {
let batch_size = NUM_POINTS_PER_BATCH / 10;
// define function
let mut point_count: usize = 0;
let mut print_count: usize = 1;
let num_points = 25 * batch_size / 10;
println!("batch_size= {} , num_points= {}", batch_size, num_points);
let callback_func = |point_data: PointData| -> Result<()> {
point_count += point_data.position.len();
if point_count >= print_count * 2 * batch_size {
print_count += 1;
println!("Streamed {} points", point_count);
}
if point_count >= num_points {
return Err(std::io::Error::new(
std::io::ErrorKind::Interrupted,
format!("Maximum number of {} points reached.", num_points),
)
.into());
}
Ok(())
};

// octree and iterator
let octree = build_test_octree(batch_size);
let location = PointLocation {
culling: PointCulling::Any(),
global_from_local: None,
};
let mut batch_iterator = BatchIterator::new(&octree, &location, batch_size);

let _err_stop = batch_iterator
.try_for_each_batch(callback_func)
.expect_err("Test error");

assert_eq!(3 * batch_size, point_count);
assert_eq!(2, print_count);
}
}
7 changes: 5 additions & 2 deletions src/octree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ pub use self::factory::OctreeFactory;

mod octree_iterator;
pub use self::octree_iterator::{
contains, intersecting_node_ids, AllPointsIterator, FilteredPointsIterator,
contains, intersecting_node_ids, AllPointsIterator, FilteredPointsIterator, NodeIdsIterator,
};

mod batch_iterator;
pub use self::batch_iterator::{BatchIterator, PointCulling, PointLocation, NUM_POINTS_PER_BATCH};

#[cfg(test)]
catevita marked this conversation as resolved.
Show resolved Hide resolved
mod octree_test;

// Version 9 -> 10: Change in NodeId proto from level (u8) and index (u64) to high (u64) and low
// (u64). We are able to convert the proto on read, so the tools can still read version 9.
// Version 10 -> 11: Change in AxisAlignedCuboid proto from Vector3f min/max to Vector3d min/max.
Expand Down Expand Up @@ -338,11 +341,11 @@ impl Octree {
AllPointsIterator::new(&self)
}

/// return the bounding box saved in meta
pub fn bounding_box(&self) -> &Aabb3<f64> {
&self.meta.bounding_box
}
}

struct OpenNode {
node: Node,
relation: Relation,
Expand Down
35 changes: 35 additions & 0 deletions src/octree/octree_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,38 @@ pub fn intersecting_node_ids(
}
node_ids
}

pub struct NodeIdsIterator<'a> {
octree: &'a Octree,
filter_func: Box<Fn(&NodeId, &Octree) -> bool + 'a>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My first preference would be to integrate this directly with PointCulling instead of functions, and to use the more optimized distinction between Inside, Crossing and Outside to automatically add child nodes. If we use functions, how about making this a template parameter instead of a trait object?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned offline, the iterator is more generic than the filtering of the point culling. This distinction occurs for Frustum and a specific method returns the node ids.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but can we make this a parameter of the struct, I.e. struct NodeIdsIterator<F> and filter_func: F, and restrict it to Fn(&NodeId, &Octree) -> bool in the impl?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, seems like you can directly embed the trait bound into the struct definition (where F: Fn(&NodeId, &Octree) -> bool)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

node_ids: VecDeque<NodeId>,
}

impl<'a> NodeIdsIterator<'a> {
pub fn new(octree: &'a Octree, filter_func: Box<Fn(&NodeId, &Octree) -> bool + 'a>) -> Self {
NodeIdsIterator {
octree,
node_ids: vec![NodeId::from_level_index(0, 0)].into(),
filter_func,
}
}
}

impl<'a> Iterator for NodeIdsIterator<'a> {
type Item = NodeId;

fn next(&mut self) -> Option<NodeId> {
while let Some(current) = self.node_ids.pop_front() {
if (self.filter_func)(&current, &self.octree) {
for child_index in 0..8 {
let child_id = current.get_child_id(ChildIndex::from_u8(child_index));
if self.octree.nodes.contains_key(&child_id) {
self.node_ids.push_back(child_id);
}
}
return Some(current);
}
}
None
}
}
147 changes: 147 additions & 0 deletions src/octree/octree_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#[cfg(test)]
mod tests {
use crate::color::Color;
use crate::errors::Result;
use crate::generation::build_octree;
use crate::octree::{self, BatchIterator, PointCulling, PointLocation};
use crate::{Point, PointData};
use cgmath::{EuclideanSpace, Point3, Vector3};
use collision::{Aabb, Aabb3};
use tempdir::TempDir;

fn build_big_test_octree() -> Box<octree::Octree> {
let default_point = Point {
position: Vector3::new(-2_699_182.0, -4_294_938.0, 3_853_373.0), //ECEF parking lot porter dr
color: Color {
red: 255,
green: 0,
blue: 0,
alpha: 255,
},
intensity: None,
};

let mut points = vec![default_point; 200_000];
points[3].position = Vector3::new(-2_702_846.0, -4_291_151.0, 3_855_012.0); // ECEF STANFORD

let p = Point3::new(6_400_000.0, 6_400_000.0, 6_400_000.0);
let bounding_box = Aabb3::new(-1.0 * p, p);

let pool = scoped_pool::Pool::new(10);
let tmp_dir = TempDir::new("octree").unwrap();

build_octree(&pool, &tmp_dir, 1.0, bounding_box, points.into_iter());
crate::octree::on_disk::octree_from_directory(tmp_dir.into_path()).unwrap()
}

fn build_test_octree() -> Box<octree::Octree> {
let default_point = Point {
position: Vector3::new(0.0, 0.0, 0.0),
color: Color {
red: 255,
green: 0,
blue: 0,
alpha: 255,
},
intensity: None,
};

let mut points = vec![default_point; 100_001];
points[100_000].position = Vector3::new(-200., -40., 30.);

let bounding_box = Aabb3::zero().grow(Point3::from_vec(points[100_000].position));

let pool = scoped_pool::Pool::new(10);
let tmp_dir = TempDir::new("octree").unwrap();

build_octree(&pool, &tmp_dir, 1.0, bounding_box, points.into_iter());
crate::octree::on_disk::octree_from_directory(tmp_dir.into_path()).unwrap()
}

#[test]
//#[ignore]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Please delete this line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

fn test_batch_iterator() {
let batch_size = 5000;
// define function
let mut callback_count: usize = 0;
let max_num_points = 13_000; // 2*batch size + 3000
let mut delivered_points = 0;
println!(
"batch_size= {} , num_points= {}",
batch_size, max_num_points
);
let callback_func = |point_data: PointData| -> Result<()> {
callback_count += 1;
delivered_points += point_data.position.len();

if delivered_points >= max_num_points {
return Err(std::io::Error::new(
std::io::ErrorKind::Interrupted,
format!("Maximum number of {} points reached.", max_num_points),
)
.into());
}
Ok(())
};

// octree and iterator
let octree = build_test_octree();
let location = PointLocation {
culling: PointCulling::Any(),
global_from_local: None,
};
let mut batch_iterator = BatchIterator::new(&octree, &location, batch_size);

let _err_stop = batch_iterator
.try_for_each_batch(callback_func)
.expect_err("Test error");

assert_eq!(3, callback_count);
assert_eq!(3 * batch_size, delivered_points);
assert!(delivered_points as i32 - max_num_points as i32 >= 0);
}

#[test]
#[ignore]
fn test_batch_iterator_big_octree() {
let batch_size = 5000;
// define function
let mut callback_count: usize = 0;
let max_num_points = 13_000; // 2*batch size + 3000
let mut delivered_points = 0;
println!(
"batch_size= {} , num_points= {}",
batch_size, max_num_points
);
let callback_func = |point_data: PointData| -> Result<()> {
callback_count += 1;
delivered_points += point_data.position.len();

if delivered_points >= max_num_points {
return Err(std::io::Error::new(
std::io::ErrorKind::Interrupted,
format!("Maximum number of {} points reached.", max_num_points),
)
.into());
}
Ok(())
};

// octree and iterator
let octree = build_big_test_octree();
let location = PointLocation {
culling: PointCulling::Any(),
global_from_local: None,
};
let mut batch_iterator = BatchIterator::new(&octree, &location, batch_size);

let _err_stop = batch_iterator
.try_for_each_batch(callback_func)
.expect_err("Test error");

assert_eq!(3, callback_count);
assert_eq!(3 * batch_size, delivered_points);
assert!(delivered_points as i32 - max_num_points as i32 >= 0);
}

}