From 320eb0fccf41afc2532c3179c766c1d1382ae3b5 Mon Sep 17 00:00:00 2001 From: turist Date: Fri, 10 Feb 2023 09:38:26 +0000 Subject: [PATCH 01/15] Drawing a circle, reorganized code --- examples/glium.rs | 293 ++++++++++++++++++++++++++-------------------- src/tree.rs | 65 +++++++++- 2 files changed, 227 insertions(+), 131 deletions(-) diff --git a/examples/glium.rs b/examples/glium.rs index c5d7673..526ff3c 100644 --- a/examples/glium.rs +++ b/examples/glium.rs @@ -1,5 +1,7 @@ +use std::f32::consts::PI; use glium::index::PrimitiveType; -use glium::{glutin, implement_vertex, program, uniform, Surface}; +use glium::{glutin, implement_vertex, program, uniform, Surface, Program, Display, VertexBuffer, IndexBuffer}; +use glium::glutin::event_loop::EventLoop; use lodtree::coords::QuadVec; use lodtree::*; @@ -7,59 +9,15 @@ use lodtree::*; // the chunk struct for the tree struct Chunk { visible: bool, - cache_state: i32, // 0 is new, 1 is merged, 2 is cached, 3 is both + cache_state: i32, + // 0 is new, 1 is merged, 2 is cached, 3 is both selected: bool, in_bounds: bool, } -fn main() { - // start the glium event loop - let event_loop = glutin::event_loop::EventLoop::new(); - let wb = glutin::window::WindowBuilder::new().with_title("Quadtree demo"); - let cb = glutin::ContextBuilder::new().with_vsync(true); - let display = glium::Display::new(wb, cb, &event_loop).unwrap(); - - // make a vertex buffer - // we'll reuse it as we only need to draw one quad multiple times anyway - let vertex_buffer = { - #[derive(Copy, Clone)] - struct Vertex { - // only need a 2d position - position: [f32; 2], - } - - implement_vertex!(Vertex, position); - - glium::VertexBuffer::new( - &display, - &[ - Vertex { - position: [-1.0, -1.0], - }, - Vertex { - position: [-1.0, 1.0], - }, - Vertex { - position: [1.0, -1.0], - }, - Vertex { - position: [1.0, 1.0], - }, - ], - ) - .unwrap() - }; - - // and the index buffer to form the triangle - let index_buffer = glium::IndexBuffer::new( - &display, - PrimitiveType::TrianglesList, - &[0 as u16, 1, 2, 1, 2, 3], - ) - .unwrap(); - - // and get the shaders - let program = program!(&display, +fn make_shaders(display: &Display) -> Program +{ + let program = program!(display, 140 => { vertex: " #version 140 @@ -100,101 +58,182 @@ fn main() { " } ) - .unwrap(); - - let draw = move |mouse_pos: (f64, f64), - tree: &mut Tree, - display: &glium::Display| { - // update the tree - // adding chunks to their respective position, and also set them visible when adding - if tree.prepare_update( - &[QuadVec::from_float_coords( - mouse_pos.0, - 1.0 - mouse_pos.1, - 6, - )], - 2, - |_position| Chunk { - visible: true, - cache_state: 0, - selected: false, - in_bounds: false, - }, - ) { - // position should already have been set, so we can just change the visibility - for chunk in tree.iter_chunks_to_activate_mut() { - chunk.visible = true; - chunk.cache_state |= 1; - } + .unwrap(); + return program; +} + +#[derive(Copy, Clone)] +struct Vertex { + // only need a 2d position + position: [f32; 2], +} +implement_vertex!(Vertex, position); + +struct RenderContext { + display: Display, + vertex_buffer: VertexBuffer, + + shaders: Program, + index_buffer: IndexBuffer, +} + +impl RenderContext { + pub fn new( event_loop: &EventLoop<()>) -> Self + { + + let wb = glutin::window::WindowBuilder::new().with_title("Quadtree demo"); + let cb = glutin::ContextBuilder::new().with_vsync(true); + let display = glium::Display::new(wb, cb, &event_loop).unwrap(); + // make a vertex buffer + // we'll reuse it as we only need to draw one quad multiple times anyway + let vertex_buffer = { + glium::VertexBuffer::new( + &display, + &[ + Vertex { + position: [-1.0, -1.0], + }, + Vertex { + position: [-1.0, 1.0], + }, + Vertex { + position: [1.0, -1.0], + }, + Vertex { + position: [1.0, 1.0], + }, + ], + ) + .unwrap() + }; + // and the index buffer to form the triangle + let index_buffer = glium::IndexBuffer::new( + &display, + PrimitiveType::TrianglesList, + &[0 as u16, 1, 2, 1, 2, 3], + ) + .unwrap(); + + let shaders = make_shaders(&display); + Self { + display, + vertex_buffer, + index_buffer, + shaders, + } + } +} + + +fn draw(mouse_pos: (f32, f32), + tree: &mut Tree, + ctx: &RenderContext) { - for chunk in tree.iter_chunks_to_deactivate_mut() { - chunk.visible = false; - } - // and make chunks that are cached visible - for chunk in tree.iter_chunks_to_remove_mut() { - chunk.cache_state = 2; - } - // do the update - tree.do_update(); + // update the tree + // adding chunks to their respective position, and also set them visible when adding + fn chunk_creator(position:QuadVec)->Chunk + { + let R = 6; - // and clean - tree.complete_update(); + let visible = match position.depth { + 4=> ((position.x as i32 - R).pow(2) + (position.y as i32 - R).pow(2) < R), + _=>false, + }; + + // dbg!(position); + // dbg!(visible); + Chunk { + visible:true, + cache_state: visible as i32, + selected: false, + in_bounds: false, + } + } + let qv = QuadVec::new(6,6,4); + if tree.prepare_update( + &[qv], + 6, + chunk_creator, + ) { + // position should already have been set, so we can just change the visibility + for chunk in tree.iter_chunks_to_activate_mut() { + chunk.visible = true; + // chunk.cache_state |= 1; } - // go over all chunks in the tree and set them to not be selected - for chunk in tree.iter_chunks_mut() { - chunk.selected = false; + for chunk in tree.iter_chunks_to_deactivate_mut() { + //chunk.visible = false; } - // and select the chunk at the mouse position - if let Some(chunk) = tree.get_chunk_from_position_mut(QuadVec::from_float_coords( - mouse_pos.0, - 1.0 - mouse_pos.1, - 6, - )) { - chunk.selected = true; + // and make chunks that are cached visible + for chunk in tree.iter_chunks_to_remove_mut() { + chunk.cache_state = 2; } - // and select a number of chunks in a region when the mouse buttons are selected + // do the update + tree.do_update(); - // and, Redraw! - let mut target = display.draw(); - target.clear_color(0.6, 0.6, 0.6, 1.0); + // and clean + tree.complete_update(); + } + dbg!("Searching!"); + tree.search_around(qv, 7, | c|{ + c.chunk.cache_state = 4 + }); + // go over all chunks in the tree and set them to not be selected + for chunk in tree.iter_chunks_mut() { + chunk.selected = false; + } + + // and select the chunk at the mouse position + // if let Some(chunk) = tree.get_chunk_from_position_mut(qv) { + // chunk.selected = true; + // chunk.visible = true; + // } + + // and select a number of chunks in a region when the mouse buttons are selected - // go over all chunks, iterator version - for (chunk, position) in tree.iter_chunks_and_positions() { - if chunk.visible { - // draw it if it's visible - // here we get the chunk position and size - let uniforms = uniform! { + // and, Redraw! + let mut target = ctx.display.draw(); + target.clear_color(0.6, 0.6, 0.6, 1.0); + + // go over all chunks, iterator version + for (chunk, position) in tree.iter_chunks_and_positions() { + if chunk.visible { + // draw it if it's visible + // here we get the chunk position and size + let uniforms = uniform! { offset: [position.get_float_coords().0 as f32, position.get_float_coords().1 as f32], scale: position.get_size() as f32, state: chunk.cache_state, selected: chunk.selected as i32, }; - // draw it with glium - target - .draw( - &vertex_buffer, - &index_buffer, - &program, - &uniforms, - &Default::default(), - ) - .unwrap(); - } + // draw it with glium + target + .draw( + &ctx.vertex_buffer, + &ctx.index_buffer, + &ctx.shaders, + &uniforms, + &Default::default(), + ) + .unwrap(); } + } + target.finish().unwrap(); +} - target.finish().unwrap(); - }; +fn main() { // set up the tree - let mut tree = Tree::::new(64); - - draw((0.5, 0.5), &mut tree, &display); + let mut tree = Tree::::new(0); + // start the glium event loop + let event_loop = glutin::event_loop::EventLoop::new(); + let context = RenderContext::new(&event_loop); + draw((0.5, 0.5), &mut tree, &context); // the mouse cursor position let mut mouse_pos = (0.5, 0.5); @@ -208,7 +247,7 @@ fn main() { glutin::event::Event::RedrawRequested(_) => { // and draw, if enough time elapses if last_redraw.elapsed().as_millis() > 16 { - draw(mouse_pos, &mut tree, &display); + draw(mouse_pos, &mut tree, &context); last_redraw = std::time::Instant::now(); } @@ -220,12 +259,12 @@ fn main() { glutin::event::WindowEvent::CursorMoved { position, .. } => { // get the mouse position mouse_pos = ( - position.x / display.get_framebuffer_dimensions().0 as f64, - position.y / display.get_framebuffer_dimensions().1 as f64, + position.x as f32 / context.display.get_framebuffer_dimensions().0 as f32, + position.y as f32 / context.display.get_framebuffer_dimensions().1 as f32, ); // request a redraw - display.gl_window().window().request_redraw(); + context.display.gl_window().window().request_redraw(); glutin::event_loop::ControlFlow::Wait } diff --git a/src/tree.rs b/src/tree.rs index c247bd4..01f1990 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -3,7 +3,9 @@ use crate::traits::*; use std::collections::{HashMap, VecDeque}; +use std::fmt::Debug; use std::num::NonZeroUsize; +use crate::coords::QuadVec; // struct for keeping track of chunks // keeps track of the parent and child indices @@ -19,10 +21,10 @@ pub(crate) struct TreeNode { // utility struct for holding actual chunks and the node that owns them #[derive(Clone, Debug)] -pub(crate) struct ChunkContainer { - pub(crate) chunk: C, - pub(crate) index: usize, - pub(crate) position: L, +pub struct ChunkContainer { + pub chunk: C, + pub index: usize, + pub position: L, } /// holds a chunk to add and it's position @@ -107,11 +109,64 @@ pub struct Tree { chunks_to_delete: Vec>, } +// struct TreeCursor<'a, C, L>{ +// tree: &'a Tree< C, L>, +// current: L +// } +// +// impl Iterator for TreeCursor +// { +// type Item = C; +// +// fn next(&mut self) -> Option { +// Some(self.c) +// } +// } + impl Tree where C: Sized, L: LodVec, { + pub fn search_around(&mut self, center: L, bounds:u32, mutator: fn(&mut ChunkContainer )) + where L:Debug + { + + let mut node = *self.nodes.get(0).unwrap(); + let mut position = L::root(); + // then loop + loop { + + // if the current node is the one we are looking for, return + if position == center { + mutator( self.chunks.get_mut(node.chunk).unwrap()); + } + + // if the current node does not have children, stop + // this works according to clippy + if node.children.is_none() + { + return; + + } + + // if not, go over the node children + if let Some((index, found_position)) = (0..L::num_children()) + .map(|i| (i, position.get_child(i))) + .find(|(_, x)| x.contains_child_node(center)) + { + // we found the position to go to + position = found_position; + dbg!(position); + // and the node is at the index of the child nodes + index + node = self.nodes[node.children.unwrap().get() + index]; + } else { + // if no child got found that matched the item, return none + return; + } + } + } + // helper function for later, gets a node index from a position fn get_node_index_from_position(&self, position: L) -> Option { // the current node @@ -149,6 +204,8 @@ where } } + + /// create a new, empty tree pub fn new(cache_size: usize) -> Self { // make a new Tree From 302e713b380f083a25ad94f2f8f8b397339cc29b Mon Sep 17 00:00:00 2001 From: turist Date: Mon, 13 Feb 2023 17:57:13 +0000 Subject: [PATCH 02/15] Updated unittest test_bound for QuadTree --- Cargo.lock | 199 ++++++++++++++++++++++++++++--- Cargo.toml | 2 + examples/glium.rs | 293 ++++++++++++++++++++++++++-------------------- src/coords.rs | 13 +- src/iter.rs | 43 +++++-- src/traits.rs | 4 +- 6 files changed, 399 insertions(+), 155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb2a2ac..e700448 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -42,6 +42,26 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -75,6 +95,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + [[package]] name = "calloop" version = "0.6.5" @@ -112,6 +138,20 @@ dependencies = [ "libc", ] +[[package]] +name = "chrono" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f725f340c3854e3cb3ab736dc21f0cca183303acea3b3ffec30f141503ac8eb" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "wasm-bindgen", + "winapi", +] + [[package]] name = "cocoa" version = "0.24.0" @@ -510,6 +550,19 @@ dependencies = [ "libc", ] +[[package]] +name = "iana-time-zone" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9512e544c25736b82aebbd2bf739a47c8a1c935dfcc3a6adcde10e35cd3cd468" +dependencies = [ + "android_system_properties", + "core-foundation 0.9.1", + "js-sys", + "wasm-bindgen", + "winapi", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -531,6 +584,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "khronos_api" version = "3.1.0" @@ -545,9 +607,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.100" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libloading" @@ -583,14 +645,16 @@ name = "lodtree" version = "0.1.4" dependencies = [ "glium", + "log", "rayon", + "stderrlog", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", ] @@ -773,6 +837,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -825,9 +908,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.8.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "osmesa-sys" @@ -905,11 +988,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.28" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1042,6 +1125,19 @@ dependencies = [ "wayland-protocols", ] +[[package]] +name = "stderrlog" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69a26bbf6de627d389164afa9783739b56746c6c72c4ed16539f4ff54170327b" +dependencies = [ + "atty", + "chrono", + "log", + "termcolor", + "thread_local", +] + [[package]] name = "strsim" version = "0.9.3" @@ -1050,13 +1146,13 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" [[package]] name = "syn" -version = "1.0.75" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1065,6 +1161,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36ae8932fcfea38b7d3883ae2ab357b0d57a02caaa18ebb4f5ece08beaec4aa0" +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.26" @@ -1085,6 +1190,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f297120ff9d4efe680df143d5631bba9c75fa371992b7fcb33eb3453cb0a07" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + [[package]] name = "toml" version = "0.5.8" @@ -1101,10 +1216,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "unicode-ident" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "version_check" @@ -1123,6 +1238,60 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + [[package]] name = "wayland-client" version = "0.28.6" diff --git a/Cargo.toml b/Cargo.toml index 150b377..f0aa62a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,5 @@ categories = ["data-structures"] [dev_dependencies] rayon = "1.5" glium = "0.30" +log = "0.4.17" +stderrlog = "0.5.4" diff --git a/examples/glium.rs b/examples/glium.rs index c5d7673..622efd8 100644 --- a/examples/glium.rs +++ b/examples/glium.rs @@ -1,5 +1,7 @@ +use std::f32::consts::PI; use glium::index::PrimitiveType; -use glium::{glutin, implement_vertex, program, uniform, Surface}; +use glium::{glutin, implement_vertex, program, uniform, Surface, Program, Display, VertexBuffer, IndexBuffer}; +use glium::glutin::event_loop::EventLoop; use lodtree::coords::QuadVec; use lodtree::*; @@ -7,59 +9,15 @@ use lodtree::*; // the chunk struct for the tree struct Chunk { visible: bool, - cache_state: i32, // 0 is new, 1 is merged, 2 is cached, 3 is both + cache_state: i32, + // 0 is new, 1 is merged, 2 is cached, 3 is both selected: bool, in_bounds: bool, } -fn main() { - // start the glium event loop - let event_loop = glutin::event_loop::EventLoop::new(); - let wb = glutin::window::WindowBuilder::new().with_title("Quadtree demo"); - let cb = glutin::ContextBuilder::new().with_vsync(true); - let display = glium::Display::new(wb, cb, &event_loop).unwrap(); - - // make a vertex buffer - // we'll reuse it as we only need to draw one quad multiple times anyway - let vertex_buffer = { - #[derive(Copy, Clone)] - struct Vertex { - // only need a 2d position - position: [f32; 2], - } - - implement_vertex!(Vertex, position); - - glium::VertexBuffer::new( - &display, - &[ - Vertex { - position: [-1.0, -1.0], - }, - Vertex { - position: [-1.0, 1.0], - }, - Vertex { - position: [1.0, -1.0], - }, - Vertex { - position: [1.0, 1.0], - }, - ], - ) - .unwrap() - }; - - // and the index buffer to form the triangle - let index_buffer = glium::IndexBuffer::new( - &display, - PrimitiveType::TrianglesList, - &[0 as u16, 1, 2, 1, 2, 3], - ) - .unwrap(); - - // and get the shaders - let program = program!(&display, +fn make_shaders(display: &Display) -> Program +{ + let program = program!(display, 140 => { vertex: " #version 140 @@ -100,101 +58,182 @@ fn main() { " } ) - .unwrap(); - - let draw = move |mouse_pos: (f64, f64), - tree: &mut Tree, - display: &glium::Display| { - // update the tree - // adding chunks to their respective position, and also set them visible when adding - if tree.prepare_update( - &[QuadVec::from_float_coords( - mouse_pos.0, - 1.0 - mouse_pos.1, - 6, - )], - 2, - |_position| Chunk { - visible: true, - cache_state: 0, - selected: false, - in_bounds: false, - }, - ) { - // position should already have been set, so we can just change the visibility - for chunk in tree.iter_chunks_to_activate_mut() { - chunk.visible = true; - chunk.cache_state |= 1; - } + .unwrap(); + return program; +} - for chunk in tree.iter_chunks_to_deactivate_mut() { - chunk.visible = false; - } +#[derive(Copy, Clone)] +struct Vertex { + // only need a 2d position + position: [f32; 2], +} +implement_vertex!(Vertex, position); - // and make chunks that are cached visible - for chunk in tree.iter_chunks_to_remove_mut() { - chunk.cache_state = 2; - } +struct RenderContext { + display: Display, + vertex_buffer: VertexBuffer, - // do the update - tree.do_update(); + shaders: Program, + index_buffer: IndexBuffer, +} - // and clean - tree.complete_update(); +impl RenderContext { + pub fn new( event_loop: &EventLoop<()>) -> Self + { + + let wb = glutin::window::WindowBuilder::new().with_title("Quadtree demo"); + let cb = glutin::ContextBuilder::new().with_vsync(true); + let display = glium::Display::new(wb, cb, &event_loop).unwrap(); + // make a vertex buffer + // we'll reuse it as we only need to draw one quad multiple times anyway + let vertex_buffer = { + glium::VertexBuffer::new( + &display, + &[ + Vertex { + position: [-1.0, -1.0], + }, + Vertex { + position: [-1.0, 1.0], + }, + Vertex { + position: [1.0, -1.0], + }, + Vertex { + position: [1.0, 1.0], + }, + ], + ) + .unwrap() + }; + // and the index buffer to form the triangle + let index_buffer = glium::IndexBuffer::new( + &display, + PrimitiveType::TrianglesList, + &[0 as u16, 1, 2, 1, 2, 3], + ) + .unwrap(); + + let shaders = make_shaders(&display); + Self { + display, + vertex_buffer, + index_buffer, + shaders, } + } +} + - // go over all chunks in the tree and set them to not be selected - for chunk in tree.iter_chunks_mut() { - chunk.selected = false; +fn draw(mouse_pos: (f32, f32), + tree: &mut Tree, + ctx: &RenderContext) { + + + + // update the tree + // adding chunks to their respective position, and also set them visible when adding + fn chunk_creator(position:QuadVec)->Chunk + { + let R = 6; + + let visible = match position.depth { + 4=> ((position.x as i32 - R).pow(2) + (position.y as i32 - R).pow(2) < R), + _=>false, + }; + + // dbg!(position); + // dbg!(visible); + Chunk { + visible:true, + cache_state: visible as i32, + selected: false, + in_bounds: false, + } + } + let qv = QuadVec::new(6,6,4); + if tree.prepare_update( + &[qv], + 6, + chunk_creator, + ) { + // position should already have been set, so we can just change the visibility + for chunk in tree.iter_chunks_to_activate_mut() { + chunk.visible = true; + // chunk.cache_state |= 1; } - // and select the chunk at the mouse position - if let Some(chunk) = tree.get_chunk_from_position_mut(QuadVec::from_float_coords( - mouse_pos.0, - 1.0 - mouse_pos.1, - 6, - )) { - chunk.selected = true; + for chunk in tree.iter_chunks_to_deactivate_mut() { + //chunk.visible = false; } - // and select a number of chunks in a region when the mouse buttons are selected + // and make chunks that are cached visible + for chunk in tree.iter_chunks_to_remove_mut() { + chunk.cache_state = 2; + } + + // do the update + tree.do_update(); + + // and clean + tree.complete_update(); + } + // dbg!("Searching!"); + // tree.search_around(qv, 7, | c|{ + // c.chunk.cache_state = 4 + // }); + // go over all chunks in the tree and set them to not be selected + for chunk in tree.iter_chunks_mut() { + chunk.selected = false; + } + + // and select the chunk at the mouse position + // if let Some(chunk) = tree.get_chunk_from_position_mut(qv) { + // chunk.selected = true; + // chunk.visible = true; + // } - // and, Redraw! - let mut target = display.draw(); - target.clear_color(0.6, 0.6, 0.6, 1.0); + // and select a number of chunks in a region when the mouse buttons are selected - // go over all chunks, iterator version - for (chunk, position) in tree.iter_chunks_and_positions() { - if chunk.visible { - // draw it if it's visible - // here we get the chunk position and size - let uniforms = uniform! { + // and, Redraw! + let mut target = ctx.display.draw(); + target.clear_color(0.6, 0.6, 0.6, 1.0); + + // go over all chunks, iterator version + for (chunk, position) in tree.iter_chunks_and_positions() { + if chunk.visible { + // draw it if it's visible + // here we get the chunk position and size + let uniforms = uniform! { offset: [position.get_float_coords().0 as f32, position.get_float_coords().1 as f32], scale: position.get_size() as f32, state: chunk.cache_state, selected: chunk.selected as i32, }; - // draw it with glium - target - .draw( - &vertex_buffer, - &index_buffer, - &program, - &uniforms, - &Default::default(), - ) - .unwrap(); - } + // draw it with glium + target + .draw( + &ctx.vertex_buffer, + &ctx.index_buffer, + &ctx.shaders, + &uniforms, + &Default::default(), + ) + .unwrap(); } + } + target.finish().unwrap(); +} - target.finish().unwrap(); - }; +fn main() { // set up the tree - let mut tree = Tree::::new(64); - - draw((0.5, 0.5), &mut tree, &display); + let mut tree = Tree::::new(0); + // start the glium event loop + let event_loop = glutin::event_loop::EventLoop::new(); + let context = RenderContext::new(&event_loop); + draw((0.5, 0.5), &mut tree, &context); // the mouse cursor position let mut mouse_pos = (0.5, 0.5); @@ -208,7 +247,7 @@ fn main() { glutin::event::Event::RedrawRequested(_) => { // and draw, if enough time elapses if last_redraw.elapsed().as_millis() > 16 { - draw(mouse_pos, &mut tree, &display); + draw(mouse_pos, &mut tree, &context); last_redraw = std::time::Instant::now(); } @@ -220,12 +259,12 @@ fn main() { glutin::event::WindowEvent::CursorMoved { position, .. } => { // get the mouse position mouse_pos = ( - position.x / display.get_framebuffer_dimensions().0 as f64, - position.y / display.get_framebuffer_dimensions().1 as f64, + position.x as f32 / context.display.get_framebuffer_dimensions().0 as f32, + position.y as f32 / context.display.get_framebuffer_dimensions().1 as f32, ); // request a redraw - display.gl_window().window().request_redraw(); + context.display.gl_window().window().request_redraw(); glutin::event_loop::ControlFlow::Wait } diff --git a/src/coords.rs b/src/coords.rs index eb0d176..140bbcf 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -25,6 +25,9 @@ impl QuadVec { /// * `depth` the lod depth the coord is at. This is soft limited at roughly 60, and the tree might behave weird if it gets higher #[inline] pub fn new(x: u64, y: u64, depth: u8) -> Self { + debug_assert!(x < (1< Self { + debug_assert!(index < 4); // the positions, doubled in scale let x = self.x << 1; let y = self.y << 1; @@ -139,7 +143,7 @@ impl LodVec for QuadVec { let self_difference = self.depth - level; let min_difference = min.depth - level; let max_difference = max.depth - level; - + // println!("diff {:?}, {:?}, {:?}", self_difference, min_difference,max_difference); // get the coords to that level let self_x = self.x >> self_difference; let self_y = self.y >> self_difference; @@ -149,13 +153,13 @@ impl LodVec for QuadVec { let max_x = max.x >> max_difference; let max_y = max.y >> max_difference; - + // dbg!(min_x, min_y, max_x, max_y); // then check if we are inside the AABB self.depth as u64 <= max_depth && self_x >= min_x - && self_x < max_x + && self_x <= max_x && self_y >= min_y - && self_y < max_y + && self_y <= max_y } #[inline] @@ -263,6 +267,7 @@ impl LodVec for OctVec { #[inline] fn get_child(self, index: usize) -> Self { + debug_assert!(index < 8); // the positions, doubled in scale let x = self.x << 1; let y = self.y << 1; diff --git a/src/iter.rs b/src/iter.rs index f0d8c96..0c43f39 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,5 +1,6 @@ //! Iterators over chunks +use crate::coords::QuadVec; use crate::traits::*; use crate::tree::*; @@ -422,13 +423,14 @@ impl Iterator for ChunksInBoundIter { #[inline] fn next(&mut self) -> Option { let current = self.stack.pop()?; - + //println!("current {:?}", current); // go over all child nodes for i in 0..L::num_children() { let position = current.get_child(i); - + //println!("try {:?}",position); // if they are in bounds, and the correct depth, add them to the stack if position.is_inside_bounds(self.bound_min, self.bound_max, self.max_depth) { + //println!("push {:?}", position); self.stack.push(position); } } @@ -763,7 +765,14 @@ where } } } - +// fn correct_clipping(min:QuadVec, max:QuadVec, depth: usize){ +// +// for pos in Tree::{ +// +// if pos. >= min.x +// } +// +// } #[cfg(test)] mod tests { @@ -775,13 +784,31 @@ mod tests { #[test] fn test_bounds() { struct C; - + let min = QuadVec::new(1, 1, 3); + let max = QuadVec::new(5, 3, 3); + let mut count = 0; for pos in Tree::::iter_all_chunks_in_bounds( - QuadVec::new(1, 1, 4), - QuadVec::new(8, 8, 4), - 4, + min, max, + 3 ) { - println!("{:?}", pos); + //assert!(QuadVec > min ); + //assert!(QuadVec < max ); + //println!("visit {:?}", pos); } + //assert!(count == 42); + } + #[test] + fn test_1(){ + struct C; + let min = QuadVec::new(1, 1, 4); + let max = QuadVec::new(5, 3, 4); + + for pos in Tree::::iter_all_chunks_in_bounds_and_tree_mut(&mut Default::default(), + min, + max, 3) { + + println!(" Say hey {:?}", pos.0); + } + } } diff --git a/src/traits.rs b/src/traits.rs index f2a8987..d608400 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,9 +1,11 @@ //! Contains LodVec trait, which is needed for the coordinate system to be used in a tree. //! Sample implementations for this are in coords.rs. +use std::fmt::Debug; + /// trait for defining a Level of Detail vector. /// such a vector contains the current position in the octree (3d coords), as well as the lod level it's at, in integer coords. -pub trait LodVec: std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + Default { +pub trait LodVec: std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + Default + Debug { /// gets one of the child node position of this node, defined by it's index. fn get_child(self, index: usize) -> Self; From aecfb7743c29bbbab76b0a1629b285664abc64e5 Mon Sep 17 00:00:00 2001 From: turist Date: Tue, 14 Feb 2023 10:48:30 +0000 Subject: [PATCH 03/15] Updated unittests for test_bounds --- Cargo.lock | 133 ++++++++++++++++++++++++---- Cargo.toml | 2 + examples/glium.rs | 29 +++++- src/coords.rs | 57 ++++++++++-- src/iter.rs | 219 ++++++++++++++++++++++++++++++++++++++++++---- src/traits.rs | 4 +- src/tree.rs | 2 +- 7 files changed, 396 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb2a2ac..558f31a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -312,9 +312,9 @@ dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote", + "quote 1.0.9", "strsim", - "syn", + "syn 1.0.75", ] [[package]] @@ -324,8 +324,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core", - "quote", - "syn", + "quote 1.0.9", + "syn 1.0.75", ] [[package]] @@ -335,8 +335,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", - "quote", - "syn", + "quote 1.0.9", + "syn 1.0.75", ] [[package]] @@ -396,6 +396,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.25.0" @@ -545,9 +556,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.100" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libloading" @@ -583,6 +594,8 @@ name = "lodtree" version = "0.1.4" dependencies = [ "glium", + "rand", + "rand_derive", "rayon", ] @@ -719,8 +732,8 @@ dependencies = [ "darling", "proc-macro-crate 0.1.5", "proc-macro2", - "quote", - "syn", + "quote 1.0.9", + "syn 1.0.75", ] [[package]] @@ -801,8 +814,8 @@ checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" dependencies = [ "proc-macro-crate 1.0.0", "proc-macro2", - "quote", - "syn", + "quote 1.0.9", + "syn 1.0.75", ] [[package]] @@ -884,6 +897,12 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -909,9 +928,15 @@ version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" dependencies = [ - "unicode-xid", + "unicode-xid 0.2.2", ] +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" + [[package]] name = "quote" version = "1.0.9" @@ -921,6 +946,46 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f00303d5bccc2d79947dd033b160a7e661b1df3d3165d2eea03a79ebd4e1af" +dependencies = [ + "quote 0.3.15", + "syn 0.11.11", +] + [[package]] name = "raw-window-handle" version = "0.3.3" @@ -1048,6 +1113,17 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +dependencies = [ + "quote 0.3.15", + "synom", + "unicode-xid 0.0.4", +] + [[package]] name = "syn" version = "1.0.75" @@ -1055,8 +1131,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" dependencies = [ "proc-macro2", - "quote", - "unicode-xid", + "quote 1.0.9", + "unicode-xid 0.2.2", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +dependencies = [ + "unicode-xid 0.0.4", ] [[package]] @@ -1081,8 +1166,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" dependencies = [ "proc-macro2", - "quote", - "syn", + "quote 1.0.9", + "syn 1.0.75", ] [[package]] @@ -1100,6 +1185,12 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -1123,6 +1214,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wayland-client" version = "0.28.6" @@ -1191,7 +1288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.9", "xml-rs", ] diff --git a/Cargo.toml b/Cargo.toml index 150b377..0e2dc81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,5 @@ categories = ["data-structures"] [dev_dependencies] rayon = "1.5" glium = "0.30" +rand ="0.8.5" +rand_derive = "0.5.0" \ No newline at end of file diff --git a/examples/glium.rs b/examples/glium.rs index 526ff3c..6972d81 100644 --- a/examples/glium.rs +++ b/examples/glium.rs @@ -1,4 +1,6 @@ use std::f32::consts::PI; +use std::thread::sleep; +use std::time::Duration; use glium::index::PrimitiveType; use glium::{glutin, implement_vertex, program, uniform, Surface, Program, Display, VertexBuffer, IndexBuffer}; use glium::glutin::event_loop::EventLoop; @@ -159,12 +161,12 @@ fn draw(mouse_pos: (f32, f32), ) { // position should already have been set, so we can just change the visibility for chunk in tree.iter_chunks_to_activate_mut() { - chunk.visible = true; + chunk.visible = false; // chunk.cache_state |= 1; } for chunk in tree.iter_chunks_to_deactivate_mut() { - //chunk.visible = false; + chunk.visible = false; } // and make chunks that are cached visible @@ -182,6 +184,29 @@ fn draw(mouse_pos: (f32, f32), tree.search_around(qv, 7, | c|{ c.chunk.cache_state = 4 }); + let min = QuadVec::new(0, 0, 4); + let max = QuadVec::new(2, 2, 4); + let mut count = 0; + + for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, 4){ + + //sleep(Duration::from_secs_f32(0.1)); + + if !i.0.contains_child_node(QuadVec::new(i.0.x<<1, i.0.y<<1, 4))&&i.1.visible { + + println!(" YES CHUNK x: {:?} y: {:?}, on depth: {:?}", i.0.x, i.0.y, i.0.depth); + count += 1; + } + + else{println!(" NO CHUNK x: {:?} y: {:?}, on depth: {:?}", i.0.x, i.0.y, i.0.depth); + count += 1;} + println!("{:?}", count); + } + + + + + // go over all chunks in the tree and set them to not be selected for chunk in tree.iter_chunks_mut() { chunk.selected = false; diff --git a/src/coords.rs b/src/coords.rs index eb0d176..948445b 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -1,10 +1,12 @@ //! Contains coordinate structs, QuadVec for quadtrees, and OctVec for octrees, as well as their LodVec implementation +use std::cmp::Ordering; use crate::traits::LodVec; /// A Lod Vector for use in a quadtree. /// It subdivides into 4 children of equal size. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +//#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Default, Debug, Hash)] pub struct QuadVec { /// x position in the quadtree. pub x: u64, @@ -17,6 +19,26 @@ pub struct QuadVec { pub depth: u8, } +impl PartialOrd for QuadVec{ + fn partial_cmp(&self, other: &Self) -> Option { + if self.depth != other.depth + { + return None; + } + if (self.x == other.x) && (self.y == other.y){ + return Some(Ordering::Equal); + } + + if(self.x < other.x) &&(self.y < other.y){ + return Some(Ordering::Less); + } + else if(self.x > other.x) &&(self.y > other.y){ + return Some(Ordering::Greater); + } + return None; + } +} + impl QuadVec { /// creates a new vector from the raw x and y coords. /// # Args @@ -131,7 +153,7 @@ impl LodVec for QuadVec { local.0 >= min.0 && local.0 < max.0 && local.1 >= min.1 && local.1 < max.1 } - fn is_inside_bounds(self, min: Self, max: Self, max_depth: u64) -> bool { + fn is_inside_bounds(self, min: Self, max: Self, max_depth: u8) -> bool { // get the lowest lod level let level = self.depth.min(min.depth.min(max.depth)); @@ -151,11 +173,11 @@ impl LodVec for QuadVec { let max_y = max.y >> max_difference; // then check if we are inside the AABB - self.depth as u64 <= max_depth + self.depth as u8 <= max_depth && self_x >= min_x - && self_x < max_x + && self_x <= max_x && self_y >= min_y - && self_y < max_y + && self_y <= max_y } #[inline] @@ -174,7 +196,7 @@ impl LodVec for QuadVec { /// A Lod Vector for use in an octree. /// It subdivides into 8 children of equal size. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Default, Debug, Hash)] pub struct OctVec { /// x position in the octree. pub x: u64, @@ -190,6 +212,25 @@ pub struct OctVec { pub depth: u8, } +impl PartialOrd for OctVec { + fn partial_cmp(&self, other: &Self) -> Option { + if self.depth != other.depth + { + return None; + } + if (self.x == other.x) && (self.y == other.y) && (self.z == other.z){ + return Some(Ordering::Equal); + } + + if(self.x < other.x) &&(self.y < other.y)&& (self.z < other.z){ + return Some(Ordering::Less); + } + else if(self.x > other.x) &&(self.y > other.y)&& (self.z > other.z){ + return Some(Ordering::Greater); + } + return None; + } +} impl OctVec { /// creates a new vector from the raw x and y coords. /// # Args @@ -324,7 +365,7 @@ impl LodVec for OctVec { && local.2 < max.2 } - fn is_inside_bounds(self, min: Self, max: Self, max_depth: u64) -> bool { + fn is_inside_bounds(self, min: Self, max: Self, max_depth: u8) -> bool { // get the lowest lod level let level = self.depth.min(min.depth.min(max.depth)); @@ -347,7 +388,7 @@ impl LodVec for OctVec { let max_z = max.z >> max_difference; // then check if we are inside the AABB - self.depth as u64 <= max_depth + self.depth as u8 <= max_depth && self_x >= min_x && self_x < max_x && self_y >= min_y diff --git a/src/iter.rs b/src/iter.rs index f0d8c96..19506cb 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,4 +1,8 @@ //! Iterators over chunks +use std::ops::Range; +use rand::distributions::uniform::SampleUniform; +use rand::Rng; + use crate::traits::*; use crate::tree::*; @@ -407,7 +411,7 @@ pub struct ChunksInBoundIter { stack: Vec, // and maximum depth to go to - max_depth: u64, + max_depth: u8, // and the min of the bound bound_min: L, @@ -445,7 +449,7 @@ pub struct ChunksInBoundAndMaybeTreeIter<'a, C: Sized, L: LodVec> { stack: Vec<(L, Option)>, // and maximum depth to go to - max_depth: u64, + max_depth: u8, // and the min of the bound bound_min: L, @@ -506,7 +510,7 @@ pub struct ChunksInBoundAndTreeIter<'a, C: Sized, L: LodVec> { stack: Vec<(L, TreeNode)>, // and maximum depth to go to - max_depth: u64, + max_depth: u8, // and the min of the bound bound_min: L, @@ -553,7 +557,7 @@ pub struct ChunksInBoundAndMaybeTreeIterMut<'a, C: Sized, L: LodVec> { stack: Vec<(L, Option)>, // and maximum depth to go to - max_depth: u64, + max_depth: u8, // and the min of the bound bound_min: L, @@ -615,7 +619,7 @@ pub struct ChunksInBoundAndTreeIterMut<'a, C: Sized, L: LodVec> { stack: Vec<(L, TreeNode)>, // and maximum depth to go to - max_depth: u64, + max_depth: u8, // and the min of the bound bound_min: L, @@ -669,8 +673,9 @@ where pub fn iter_all_chunks_in_bounds( bound_min: L, bound_max: L, - max_depth: u64, + max_depth: u8, ) -> ChunksInBoundIter { + debug_assert!(bound_min ChunksInBoundAndMaybeTreeIter { ChunksInBoundAndMaybeTreeIter { stack: vec![(L::root(), self.nodes.first().copied())], @@ -703,7 +708,7 @@ where &'a self, bound_min: L, bound_max: L, - max_depth: u64, + max_depth: u8, ) -> ChunksInBoundAndTreeIter { // get the stack, empty if we can't get the first node let stack = if let Some(node) = self.nodes.first() { @@ -727,7 +732,7 @@ where &'a mut self, bound_min: L, bound_max: L, - max_depth: u64, + max_depth: u8, ) -> ChunksInBoundAndMaybeTreeIterMut { ChunksInBoundAndMaybeTreeIterMut { stack: vec![(L::root(), self.nodes.first().copied())], @@ -745,7 +750,7 @@ where &'a mut self, bound_min: L, bound_max: L, - max_depth: u64, + max_depth: u8, ) -> ChunksInBoundAndTreeIterMut { // get the stack, empty if we can't get the first node let stack = if let Some(node) = self.nodes.first() { @@ -753,7 +758,6 @@ where } else { vec![] }; - ChunksInBoundAndTreeIterMut { stack, tree: self, @@ -762,26 +766,203 @@ where bound_max, } } + } #[cfg(test)] mod tests { + use std::cmp::Ordering; + use super::*; use crate::coords::*; - // TODO: also test the other iters +pub trait SafeRngRange{ + fn safe_uniform(&mut self, range:Range)->T + where + T: SampleUniform + PartialOrd; +} + +impl SafeRngRange for rand::rngs::ThreadRng{ + //#[no_panic] + fn safe_uniform(&mut self, range:Range)->T + where + T: SampleUniform + PartialOrd + { + if range.is_empty(){ + range.start + } + else { + self.gen_range(range) + } + } +} + // TODO: also test the other iters + fn get_chunk_count_at_max_depth(a:QuadVec, b:QuadVec) -> u64 + { + assert_eq!(a.depth, b.depth); + (( b.x- a.x )+1)*(( b.y- a.y )+1) + } #[test] fn test_bounds() { - struct C; + const D:u8 = 4; + + let mut rng = rand::thread_rng(); + + for i in 1..100 { + let cmax = 1< { + (max, min) + }, + Ordering::Less => { + (min, max) + }, + Ordering::Equal => { + continue; + } + }; + struct C; + let mut count = 0; + for pos in Tree::::iter_all_chunks_in_bounds(min, max, D) { + // println!("{:?}", pos); + + if pos.depth == 4 { + count += 1; + } + } + assert_eq!(count, get_chunk_count_at_max_depth(min, max)); + } + } - for pos in Tree::::iter_all_chunks_in_bounds( - QuadVec::new(1, 1, 4), - QuadVec::new(8, 8, 4), - 4, - ) { - println!("{:?}", pos); + fn get_chunk_count_at_max_depth_oct(b:OctVec,a:OctVec)-> u64{ + assert_eq!(a.depth, b.depth); + (( b.x- a.x )+1)*(( b.y- a.y )+1)*((b.z - a.z)+1) + + } + ///The same unit test as test_bounds juts for OctVec: + /// //todo check it once again + #[test] + fn test_bounds_octree() { + + const D:u8 = 4; + + let mut rng = rand::thread_rng(); + + for i in 1..100 { + let cmax = 1< { + (max, min) + }, + Ordering::Less => { + (min, max) + }, + Ordering::Equal => { + continue; + } + }; + struct C; + let mut count = 0; + for pos in Tree::::iter_all_chunks_in_bounds(min, max, D) { + // println!("{:?}", pos); + + if pos.depth == 4 { + count += 1; + } + } + assert_eq!(count, get_chunk_count_at_max_depth_oct(min, max)); } } + + + #[test] + fn test_iter_all_chunks_in_bounds_and_tree_mut() { + struct Chunk { + visible: bool, + cache_state: i32, + // 0 is new, 1 is merged, 2 is cached, 3 is both + selected: bool, + in_bounds: bool, } + + fn chunk_creator( position: QuadVec) -> Chunk + { + let r = 6; + + let visible = match position.depth { + 4 => (position.x as i32 - r).pow(2) + (position.y as i32 - r).pow(2) < r, + _ => false, + }; + // dbg!(position); + // dbg!(visible); + Chunk { + visible: true, + cache_state: visible as i32, + selected: false, + in_bounds: false, + } + + + } + let mut tree = Tree::new(65); + let qv = QuadVec::new(6, 6, 4); + if tree.prepare_update( + &[qv], + 6, + chunk_creator, + ) { + // position should already have been set, so we can just change the visibility + for chunk in tree.iter_chunks_to_activate_mut() { + chunk.visible = true; + // chunk.cache_state |= 1; + } + + for chunk in tree.iter_chunks_to_deactivate_mut() { + chunk.visible = false; + } + + // and make chunks that are cached visible + for chunk in tree.iter_chunks_to_remove_mut() { + chunk.cache_state = 2; + } + + // do the update + tree.do_update(); + + // and clean + tree.complete_update(); + } + + let min = QuadVec::new(0, 0, 4); + let max = QuadVec::new(8, 8, 4); + let mut count = 0; + + for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, 4) { + if i.0.contains_child_node(QuadVec::new(i.0.x<<1, i.0.y<<1, 4)) { + i.1.visible = true; + println!("cords:{:?}", i.0.get_float_coords()) + } + } + } +} \ No newline at end of file diff --git a/src/traits.rs b/src/traits.rs index f2a8987..fb44552 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -3,7 +3,7 @@ /// trait for defining a Level of Detail vector. /// such a vector contains the current position in the octree (3d coords), as well as the lod level it's at, in integer coords. -pub trait LodVec: std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + Default { +pub trait LodVec: std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + Default + PartialOrd { /// gets one of the child node position of this node, defined by it's index. fn get_child(self, index: usize) -> Self; @@ -92,7 +92,7 @@ pub trait LodVec: std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + De /// && self_y < max_y /// # } /// ``` - fn is_inside_bounds(self, min: Self, max: Self, max_depth: u64) -> bool; + fn is_inside_bounds(self, min: Self, max: Self, max_depth: u8) -> bool; /// Wether this node contains a child node fn contains_child_node(self, child: Self) -> bool; diff --git a/src/tree.rs b/src/tree.rs index 01f1990..90f7dc1 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -5,7 +5,7 @@ use crate::traits::*; use std::collections::{HashMap, VecDeque}; use std::fmt::Debug; use std::num::NonZeroUsize; -use crate::coords::QuadVec; + // struct for keeping track of chunks // keeps track of the parent and child indices From 2a9b8c52f1ed224364d83ee45b033066d7e4198a Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Tue, 14 Feb 2023 11:59:13 +0200 Subject: [PATCH 04/15] fix gitignore --- .gitignore | 1 + Cargo.lock | 1400 ---------------------------------------------------- 2 files changed, 1 insertion(+), 1400 deletions(-) delete mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index ea8c4bf..96ef6c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 558f31a..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,1400 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fe5e32de01730eb1f6b7f5b51c17e03e2325bf40a74f754f04f130043affff" - -[[package]] -name = "addr2line" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "andrew" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4afb09dd642feec8408e33f92f3ffc4052946f6b20f32fb99c1f58cd4fa7cf" -dependencies = [ - "bitflags", - "rusttype", - "walkdir", - "xdg", - "xml-rs", -] - -[[package]] -name = "android_glue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "backtrace" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01" -dependencies = [ - "addr2line", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "calloop" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c" -dependencies = [ - "log", - "nix 0.18.0", -] - -[[package]] -name = "cc" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cgl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" -dependencies = [ - "libc", -] - -[[package]] -name = "cocoa" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "core-foundation 0.9.1", - "core-graphics 0.22.2", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" -dependencies = [ - "bitflags", - "block", - "core-foundation 0.9.1", - "core-graphics-types", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "core-foundation" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" -dependencies = [ - "core-foundation-sys 0.7.0", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" -dependencies = [ - "core-foundation-sys 0.8.2", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" - -[[package]] -name = "core-foundation-sys" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" - -[[package]] -name = "core-graphics" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" -dependencies = [ - "bitflags", - "core-foundation 0.7.0", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" -dependencies = [ - "bitflags", - "core-foundation 0.9.1", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" -dependencies = [ - "bitflags", - "core-foundation 0.9.1", - "foreign-types", - "libc", -] - -[[package]] -name = "core-video-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" -dependencies = [ - "cfg-if 0.1.10", - "core-foundation-sys 0.7.0", - "core-graphics 0.19.2", - "libc", - "objc", -] - -[[package]] -name = "crossbeam" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" -dependencies = [ - "cfg-if 1.0.0", - "lazy_static", -] - -[[package]] -name = "darling" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote 1.0.9", - "strsim", - "syn 1.0.75", -] - -[[package]] -name = "darling_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -dependencies = [ - "darling_core", - "quote 1.0.9", - "syn 1.0.75", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn 1.0.75", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "dlib" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" -dependencies = [ - "libloading 0.6.7", -] - -[[package]] -name = "dlib" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" -dependencies = [ - "libloading 0.7.0", -] - -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" - -[[package]] -name = "gl_generator" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" -dependencies = [ - "khronos_api", - "log", - "xml-rs", -] - -[[package]] -name = "glium" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6dfaf64eee4e23d1d5429945816ab083cb94b44e00c3c5f9f9aebfd09ec63df" -dependencies = [ - "backtrace", - "fnv", - "gl_generator", - "glutin", - "lazy_static", - "memoffset", - "smallvec", - "takeable-option", -] - -[[package]] -name = "glutin" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "762d6cd2e1b855d99668ebe591cc9058659d85ac39a9a2078000eb122ddba8f0" -dependencies = [ - "android_glue", - "cgl", - "cocoa", - "core-foundation 0.9.1", - "glutin_egl_sys", - "glutin_emscripten_sys", - "glutin_gles2_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "lazy_static", - "libloading 0.7.0", - "log", - "objc", - "osmesa-sys", - "parking_lot", - "wayland-client", - "wayland-egl", - "winapi", - "winit", -] - -[[package]] -name = "glutin_egl_sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211" -dependencies = [ - "gl_generator", - "winapi", -] - -[[package]] -name = "glutin_emscripten_sys" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" - -[[package]] -name = "glutin_gles2_sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" -dependencies = [ - "gl_generator", - "objc", -] - -[[package]] -name = "glutin_glx_sys" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351" -dependencies = [ - "gl_generator", - "x11-dl", -] - -[[package]] -name = "glutin_wgl_sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" -dependencies = [ - "gl_generator", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "instant" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "khronos_api" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "libloading" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" -dependencies = [ - "cfg-if 1.0.0", - "winapi", -] - -[[package]] -name = "libloading" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" -dependencies = [ - "cfg-if 1.0.0", - "winapi", -] - -[[package]] -name = "lock_api" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "lodtree" -version = "0.1.4" -dependencies = [ - "glium", - "rand", - "rand_derive", - "rayon", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - -[[package]] -name = "memmap2" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" -dependencies = [ - "autocfg", -] - -[[package]] -name = "minimal-lexical" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "mio" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "mio-misc" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ddf05411bb159cdb5801bb10002afb66cb4572be656044315e363460ce69dc2" -dependencies = [ - "crossbeam", - "crossbeam-queue", - "log", - "mio", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "ndk" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" -dependencies = [ - "jni-sys", - "ndk-sys", - "num_enum", - "thiserror", -] - -[[package]] -name = "ndk-glue" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385" -dependencies = [ - "lazy_static", - "libc", - "log", - "ndk", - "ndk-macro", - "ndk-sys", -] - -[[package]] -name = "ndk-macro" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" -dependencies = [ - "darling", - "proc-macro-crate 0.1.5", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.75", -] - -[[package]] -name = "ndk-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d" - -[[package]] -name = "nix" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" -dependencies = [ - "bitflags", - "cc", - "cfg-if 0.1.10", - "libc", -] - -[[package]] -name = "nix" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", -] - -[[package]] -name = "nom" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" -dependencies = [ - "memchr", - "minimal-lexical", - "version_check", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f" -dependencies = [ - "derivative", - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9" -dependencies = [ - "proc-macro-crate 1.0.0", - "proc-macro2", - "quote 1.0.9", - "syn 1.0.75", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - -[[package]] -name = "object" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2766204889d09937d00bfbb7fec56bb2a199e2ade963cab19185d8a6104c7c" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "osmesa-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" -dependencies = [ - "shared_library", -] - -[[package]] -name = "owned_ttf_parser" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" -dependencies = [ - "ttf-parser", -] - -[[package]] -name = "parking_lot" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" -dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pkg-config" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - -[[package]] -name = "proc-macro-crate" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fdbd1df62156fbc5945f4762632564d7d038153091c3fcf1067f6aef7cff92" -dependencies = [ - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" -dependencies = [ - "unicode-xid 0.2.2", -] - -[[package]] -name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" - -[[package]] -name = "quote" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f00303d5bccc2d79947dd033b160a7e661b1df3d3165d2eea03a79ebd4e1af" -dependencies = [ - "quote 0.3.15", - "syn 0.11.11", -] - -[[package]] -name = "raw-window-handle" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211" -dependencies = [ - "libc", -] - -[[package]] -name = "rayon" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49" - -[[package]] -name = "rusttype" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scoped-tls" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "serde" -version = "1.0.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1" - -[[package]] -name = "shared_library" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" -dependencies = [ - "lazy_static", - "libc", -] - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - -[[package]] -name = "smithay-client-toolkit" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" -dependencies = [ - "andrew", - "bitflags", - "calloop", - "dlib 0.4.2", - "lazy_static", - "log", - "memmap2", - "nix 0.18.0", - "wayland-client", - "wayland-cursor", - "wayland-protocols", -] - -[[package]] -name = "strsim" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" - -[[package]] -name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -dependencies = [ - "quote 0.3.15", - "synom", - "unicode-xid 0.0.4", -] - -[[package]] -name = "syn" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "unicode-xid 0.2.2", -] - -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -dependencies = [ - "unicode-xid 0.0.4", -] - -[[package]] -name = "takeable-option" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ae8932fcfea38b7d3883ae2ab357b0d57a02caaa18ebb4f5ece08beaec4aa0" - -[[package]] -name = "thiserror" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "syn 1.0.75", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "ttf-parser" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" - -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wayland-client" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" -dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix 0.20.0", - "scoped-tls", - "wayland-commons", - "wayland-scanner", - "wayland-sys", -] - -[[package]] -name = "wayland-commons" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" -dependencies = [ - "nix 0.20.0", - "once_cell", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-cursor" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" -dependencies = [ - "nix 0.20.0", - "wayland-client", - "xcursor", -] - -[[package]] -name = "wayland-egl" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ba1ab1e18756b23982d36f08856d521d7df45015f404a2d7c4f0b2d2f66956" -dependencies = [ - "wayland-client", - "wayland-sys", -] - -[[package]] -name = "wayland-protocols" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" -dependencies = [ - "bitflags", - "wayland-client", - "wayland-commons", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" -dependencies = [ - "proc-macro2", - "quote 1.0.9", - "xml-rs", -] - -[[package]] -name = "wayland-sys" -version = "0.28.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" -dependencies = [ - "dlib 0.5.0", - "lazy_static", - "pkg-config", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winit" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" -dependencies = [ - "bitflags", - "cocoa", - "core-foundation 0.9.1", - "core-graphics 0.22.2", - "core-video-sys", - "dispatch", - "instant", - "lazy_static", - "libc", - "log", - "mio", - "mio-misc", - "ndk", - "ndk-glue", - "ndk-sys", - "objc", - "parking_lot", - "percent-encoding", - "raw-window-handle", - "scopeguard", - "smithay-client-toolkit", - "wayland-client", - "winapi", - "x11-dl", -] - -[[package]] -name = "x11-dl" -version = "2.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8" -dependencies = [ - "lazy_static", - "libc", - "maybe-uninit", - "pkg-config", -] - -[[package]] -name = "xcursor" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" -dependencies = [ - "nom", -] - -[[package]] -name = "xdg" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" - -[[package]] -name = "xml-rs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" From 476b12e230fa14982bfa0783a83e6b392edd9d16 Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Tue, 14 Feb 2023 12:20:22 +0200 Subject: [PATCH 05/15] Finalize unittests for tree iterators --- examples/glium.rs | 318 ++++++++++++++++++---------------------------- src/coords.rs | 97 +++++++------- src/iter.rs | 152 +++++++++++----------- src/traits.rs | 4 +- src/tree.rs | 102 +++++---------- 5 files changed, 282 insertions(+), 391 deletions(-) diff --git a/examples/glium.rs b/examples/glium.rs index 6972d81..c5d7673 100644 --- a/examples/glium.rs +++ b/examples/glium.rs @@ -1,9 +1,5 @@ -use std::f32::consts::PI; -use std::thread::sleep; -use std::time::Duration; use glium::index::PrimitiveType; -use glium::{glutin, implement_vertex, program, uniform, Surface, Program, Display, VertexBuffer, IndexBuffer}; -use glium::glutin::event_loop::EventLoop; +use glium::{glutin, implement_vertex, program, uniform, Surface}; use lodtree::coords::QuadVec; use lodtree::*; @@ -11,15 +7,59 @@ use lodtree::*; // the chunk struct for the tree struct Chunk { visible: bool, - cache_state: i32, - // 0 is new, 1 is merged, 2 is cached, 3 is both + cache_state: i32, // 0 is new, 1 is merged, 2 is cached, 3 is both selected: bool, in_bounds: bool, } -fn make_shaders(display: &Display) -> Program -{ - let program = program!(display, +fn main() { + // start the glium event loop + let event_loop = glutin::event_loop::EventLoop::new(); + let wb = glutin::window::WindowBuilder::new().with_title("Quadtree demo"); + let cb = glutin::ContextBuilder::new().with_vsync(true); + let display = glium::Display::new(wb, cb, &event_loop).unwrap(); + + // make a vertex buffer + // we'll reuse it as we only need to draw one quad multiple times anyway + let vertex_buffer = { + #[derive(Copy, Clone)] + struct Vertex { + // only need a 2d position + position: [f32; 2], + } + + implement_vertex!(Vertex, position); + + glium::VertexBuffer::new( + &display, + &[ + Vertex { + position: [-1.0, -1.0], + }, + Vertex { + position: [-1.0, 1.0], + }, + Vertex { + position: [1.0, -1.0], + }, + Vertex { + position: [1.0, 1.0], + }, + ], + ) + .unwrap() + }; + + // and the index buffer to form the triangle + let index_buffer = glium::IndexBuffer::new( + &display, + PrimitiveType::TrianglesList, + &[0 as u16, 1, 2, 1, 2, 3], + ) + .unwrap(); + + // and get the shaders + let program = program!(&display, 140 => { vertex: " #version 140 @@ -60,205 +100,101 @@ fn make_shaders(display: &Display) -> Program " } ) - .unwrap(); - return program; -} - -#[derive(Copy, Clone)] -struct Vertex { - // only need a 2d position - position: [f32; 2], -} -implement_vertex!(Vertex, position); - -struct RenderContext { - display: Display, - vertex_buffer: VertexBuffer, - - shaders: Program, - index_buffer: IndexBuffer, -} - -impl RenderContext { - pub fn new( event_loop: &EventLoop<()>) -> Self - { - - let wb = glutin::window::WindowBuilder::new().with_title("Quadtree demo"); - let cb = glutin::ContextBuilder::new().with_vsync(true); - let display = glium::Display::new(wb, cb, &event_loop).unwrap(); - // make a vertex buffer - // we'll reuse it as we only need to draw one quad multiple times anyway - let vertex_buffer = { - glium::VertexBuffer::new( - &display, - &[ - Vertex { - position: [-1.0, -1.0], - }, - Vertex { - position: [-1.0, 1.0], - }, - Vertex { - position: [1.0, -1.0], - }, - Vertex { - position: [1.0, 1.0], - }, - ], - ) - .unwrap() - }; - // and the index buffer to form the triangle - let index_buffer = glium::IndexBuffer::new( - &display, - PrimitiveType::TrianglesList, - &[0 as u16, 1, 2, 1, 2, 3], - ) - .unwrap(); - - let shaders = make_shaders(&display); - Self { - display, - vertex_buffer, - index_buffer, - shaders, - } - } -} - - -fn draw(mouse_pos: (f32, f32), - tree: &mut Tree, - ctx: &RenderContext) { - - + .unwrap(); + + let draw = move |mouse_pos: (f64, f64), + tree: &mut Tree, + display: &glium::Display| { + // update the tree + // adding chunks to their respective position, and also set them visible when adding + if tree.prepare_update( + &[QuadVec::from_float_coords( + mouse_pos.0, + 1.0 - mouse_pos.1, + 6, + )], + 2, + |_position| Chunk { + visible: true, + cache_state: 0, + selected: false, + in_bounds: false, + }, + ) { + // position should already have been set, so we can just change the visibility + for chunk in tree.iter_chunks_to_activate_mut() { + chunk.visible = true; + chunk.cache_state |= 1; + } - // update the tree - // adding chunks to their respective position, and also set them visible when adding - fn chunk_creator(position:QuadVec)->Chunk - { - let R = 6; + for chunk in tree.iter_chunks_to_deactivate_mut() { + chunk.visible = false; + } - let visible = match position.depth { - 4=> ((position.x as i32 - R).pow(2) + (position.y as i32 - R).pow(2) < R), - _=>false, - }; + // and make chunks that are cached visible + for chunk in tree.iter_chunks_to_remove_mut() { + chunk.cache_state = 2; + } - // dbg!(position); - // dbg!(visible); - Chunk { - visible:true, - cache_state: visible as i32, - selected: false, - in_bounds: false, - } - } - let qv = QuadVec::new(6,6,4); - if tree.prepare_update( - &[qv], - 6, - chunk_creator, - ) { - // position should already have been set, so we can just change the visibility - for chunk in tree.iter_chunks_to_activate_mut() { - chunk.visible = false; - // chunk.cache_state |= 1; - } + // do the update + tree.do_update(); - for chunk in tree.iter_chunks_to_deactivate_mut() { - chunk.visible = false; + // and clean + tree.complete_update(); } - // and make chunks that are cached visible - for chunk in tree.iter_chunks_to_remove_mut() { - chunk.cache_state = 2; + // go over all chunks in the tree and set them to not be selected + for chunk in tree.iter_chunks_mut() { + chunk.selected = false; } - // do the update - tree.do_update(); - - // and clean - tree.complete_update(); - } - dbg!("Searching!"); - tree.search_around(qv, 7, | c|{ - c.chunk.cache_state = 4 - }); - let min = QuadVec::new(0, 0, 4); - let max = QuadVec::new(2, 2, 4); - let mut count = 0; - - for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, 4){ - - //sleep(Duration::from_secs_f32(0.1)); - - if !i.0.contains_child_node(QuadVec::new(i.0.x<<1, i.0.y<<1, 4))&&i.1.visible { - - println!(" YES CHUNK x: {:?} y: {:?}, on depth: {:?}", i.0.x, i.0.y, i.0.depth); - count += 1; + // and select the chunk at the mouse position + if let Some(chunk) = tree.get_chunk_from_position_mut(QuadVec::from_float_coords( + mouse_pos.0, + 1.0 - mouse_pos.1, + 6, + )) { + chunk.selected = true; } - else{println!(" NO CHUNK x: {:?} y: {:?}, on depth: {:?}", i.0.x, i.0.y, i.0.depth); - count += 1;} - println!("{:?}", count); - } - + // and select a number of chunks in a region when the mouse buttons are selected + // and, Redraw! + let mut target = display.draw(); + target.clear_color(0.6, 0.6, 0.6, 1.0); - - - // go over all chunks in the tree and set them to not be selected - for chunk in tree.iter_chunks_mut() { - chunk.selected = false; - } - - // and select the chunk at the mouse position - // if let Some(chunk) = tree.get_chunk_from_position_mut(qv) { - // chunk.selected = true; - // chunk.visible = true; - // } - - // and select a number of chunks in a region when the mouse buttons are selected - - // and, Redraw! - let mut target = ctx.display.draw(); - target.clear_color(0.6, 0.6, 0.6, 1.0); - - // go over all chunks, iterator version - for (chunk, position) in tree.iter_chunks_and_positions() { - if chunk.visible { - // draw it if it's visible - // here we get the chunk position and size - let uniforms = uniform! { + // go over all chunks, iterator version + for (chunk, position) in tree.iter_chunks_and_positions() { + if chunk.visible { + // draw it if it's visible + // here we get the chunk position and size + let uniforms = uniform! { offset: [position.get_float_coords().0 as f32, position.get_float_coords().1 as f32], scale: position.get_size() as f32, state: chunk.cache_state, selected: chunk.selected as i32, }; - // draw it with glium - target - .draw( - &ctx.vertex_buffer, - &ctx.index_buffer, - &ctx.shaders, - &uniforms, - &Default::default(), - ) - .unwrap(); + // draw it with glium + target + .draw( + &vertex_buffer, + &index_buffer, + &program, + &uniforms, + &Default::default(), + ) + .unwrap(); + } } - } - target.finish().unwrap(); -} + target.finish().unwrap(); + }; -fn main() { // set up the tree - let mut tree = Tree::::new(0); - // start the glium event loop - let event_loop = glutin::event_loop::EventLoop::new(); - let context = RenderContext::new(&event_loop); - draw((0.5, 0.5), &mut tree, &context); + let mut tree = Tree::::new(64); + + draw((0.5, 0.5), &mut tree, &display); // the mouse cursor position let mut mouse_pos = (0.5, 0.5); @@ -272,7 +208,7 @@ fn main() { glutin::event::Event::RedrawRequested(_) => { // and draw, if enough time elapses if last_redraw.elapsed().as_millis() > 16 { - draw(mouse_pos, &mut tree, &context); + draw(mouse_pos, &mut tree, &display); last_redraw = std::time::Instant::now(); } @@ -284,12 +220,12 @@ fn main() { glutin::event::WindowEvent::CursorMoved { position, .. } => { // get the mouse position mouse_pos = ( - position.x as f32 / context.display.get_framebuffer_dimensions().0 as f32, - position.y as f32 / context.display.get_framebuffer_dimensions().1 as f32, + position.x / display.get_framebuffer_dimensions().0 as f64, + position.y / display.get_framebuffer_dimensions().1 as f64, ); // request a redraw - context.display.gl_window().window().request_redraw(); + display.gl_window().window().request_redraw(); glutin::event_loop::ControlFlow::Wait } diff --git a/src/coords.rs b/src/coords.rs index 948445b..35ce07e 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -1,12 +1,12 @@ //! Contains coordinate structs, QuadVec for quadtrees, and OctVec for octrees, as well as their LodVec implementation -use std::cmp::Ordering; use crate::traits::LodVec; +use std::cmp::Ordering; /// A Lod Vector for use in a quadtree. /// It subdivides into 4 children of equal size. //#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)] -#[derive(Copy, Clone, PartialEq, Eq, Default, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Default, Debug, Hash)] pub struct QuadVec { /// x position in the quadtree. pub x: u64, @@ -19,23 +19,21 @@ pub struct QuadVec { pub depth: u8, } -impl PartialOrd for QuadVec{ +impl PartialOrd for QuadVec { fn partial_cmp(&self, other: &Self) -> Option { - if self.depth != other.depth - { + if self.depth != other.depth { return None; } - if (self.x == other.x) && (self.y == other.y){ + if (self.x == other.x) && (self.y == other.y) { return Some(Ordering::Equal); } - if(self.x < other.x) &&(self.y < other.y){ + if (self.x < other.x) && (self.y < other.y) { return Some(Ordering::Less); - } - else if(self.x > other.x) &&(self.y > other.y){ + } else if (self.x > other.x) && (self.y > other.y) { return Some(Ordering::Greater); } - return None; + return None; } } @@ -88,20 +86,6 @@ impl QuadVec { } impl LodVec for QuadVec { - #[inline] - fn num_children() -> usize { - 4 - } - - #[inline] - fn root() -> Self { - Self { - x: 0, - y: 0, - depth: 0, - } - } - #[inline] fn get_child(self, index: usize) -> Self { // the positions, doubled in scale @@ -120,6 +104,20 @@ impl LodVec for QuadVec { } } + #[inline] + fn num_children() -> usize { + 4 + } + + #[inline] + fn root() -> Self { + Self { + x: 0, + y: 0, + depth: 0, + } + } + #[inline] fn can_subdivide(self, node: Self, detail: u64) -> bool { // return early if the level of this chunk is too high @@ -196,7 +194,7 @@ impl LodVec for QuadVec { /// A Lod Vector for use in an octree. /// It subdivides into 8 children of equal size. -#[derive(Copy, Clone, PartialEq, Eq, Default, Debug, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Default, Debug, Hash)] pub struct OctVec { /// x position in the octree. pub x: u64, @@ -214,21 +212,19 @@ pub struct OctVec { impl PartialOrd for OctVec { fn partial_cmp(&self, other: &Self) -> Option { - if self.depth != other.depth - { + if self.depth != other.depth { return None; } - if (self.x == other.x) && (self.y == other.y) && (self.z == other.z){ + if (self.x == other.x) && (self.y == other.y) && (self.z == other.z) { return Some(Ordering::Equal); } - if(self.x < other.x) &&(self.y < other.y)&& (self.z < other.z){ + if (self.x < other.x) && (self.y < other.y) && (self.z < other.z) { return Some(Ordering::Less); - } - else if(self.x > other.x) &&(self.y > other.y)&& (self.z > other.z){ + } else if (self.x > other.x) && (self.y > other.y) && (self.z > other.z) { return Some(Ordering::Greater); } - return None; + return None; } } impl OctVec { @@ -287,21 +283,6 @@ impl OctVec { } impl LodVec for OctVec { - #[inline] - fn num_children() -> usize { - 8 - } - - #[inline] - fn root() -> Self { - Self { - x: 0, - y: 0, - z: 0, - depth: 0, - } - } - #[inline] fn get_child(self, index: usize) -> Self { // the positions, doubled in scale @@ -323,6 +304,21 @@ impl LodVec for OctVec { } } + #[inline] + fn num_children() -> usize { + 8 + } + + #[inline] + fn root() -> Self { + Self { + x: 0, + y: 0, + z: 0, + depth: 0, + } + } + #[inline] fn can_subdivide(self, node: Self, detail: u64) -> bool { // return early if the level of this chunk is too high @@ -365,6 +361,7 @@ impl LodVec for OctVec { && local.2 < max.2 } + #[inline] fn is_inside_bounds(self, min: Self, max: Self, max_depth: u8) -> bool { // get the lowest lod level let level = self.depth.min(min.depth.min(max.depth)); @@ -390,11 +387,11 @@ impl LodVec for OctVec { // then check if we are inside the AABB self.depth as u8 <= max_depth && self_x >= min_x - && self_x < max_x + && self_x <= max_x && self_y >= min_y - && self_y < max_y + && self_y <= max_y && self_z >= min_z - && self_z < max_z + && self_z <= max_z } #[inline] diff --git a/src/iter.rs b/src/iter.rs index 19506cb..9921706 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,9 +1,4 @@ //! Iterators over chunks -use std::ops::Range; -use rand::distributions::uniform::SampleUniform; -use rand::Rng; - - use crate::traits::*; use crate::tree::*; @@ -675,7 +670,10 @@ where bound_max: L, max_depth: u8, ) -> ChunksInBoundIter { - debug_assert!(bound_min(&mut self, range:Range)->T - where - T: SampleUniform + PartialOrd; -} + pub trait SafeRngRange { + fn safe_uniform(&mut self, range: Range) -> T + where + T: SampleUniform + PartialOrd; + } -impl SafeRngRange for rand::rngs::ThreadRng{ - //#[no_panic] - fn safe_uniform(&mut self, range:Range)->T - where - T: SampleUniform + PartialOrd - { - if range.is_empty(){ - range.start - } - else { - self.gen_range(range) + impl SafeRngRange for rand::rngs::ThreadRng { + //#[no_panic] + fn safe_uniform(&mut self, range: Range) -> T + where + T: SampleUniform + PartialOrd, + { + if range.is_empty() { + range.start + } else { + self.gen_range(range) + } } } -} // TODO: also test the other iters - fn get_chunk_count_at_max_depth(a:QuadVec, b:QuadVec) -> u64 - { + fn get_chunk_count_at_max_depth(a: QuadVec, b: QuadVec) -> u64 { assert_eq!(a.depth, b.depth); - (( b.x- a.x )+1)*(( b.y- a.y )+1) + ((b.x - a.x) + 1) * ((b.y - a.y) + 1) } #[test] - fn test_bounds() { - const D:u8 = 4; + fn test_bounds_quadtree() { + const D: u8 = 4; let mut rng = rand::thread_rng(); for i in 1..100 { - let cmax = 1< { - (max, min) - }, - Ordering::Less => { - (min, max) - }, + Ordering::Greater => (max, min), + Ordering::Less => (min, max), Ordering::Equal => { continue; } @@ -835,7 +827,7 @@ impl SafeRngRange for rand::rngs::ThreadRng{ struct C; let mut count = 0; for pos in Tree::::iter_all_chunks_in_bounds(min, max, D) { - // println!("{:?}", pos); + // println!("{:?}", pos); if pos.depth == 4 { count += 1; @@ -845,39 +837,43 @@ impl SafeRngRange for rand::rngs::ThreadRng{ } } - fn get_chunk_count_at_max_depth_oct(b:OctVec,a:OctVec)-> u64{ + fn get_chunk_count_at_max_depth_oct(a: OctVec, b: OctVec) -> u64 { assert_eq!(a.depth, b.depth); - (( b.x- a.x )+1)*(( b.y- a.y )+1)*((b.z - a.z)+1) - + ((b.x - a.x) + 1) * ((b.y - a.y) + 1) * ((b.z - a.z) + 1) } ///The same unit test as test_bounds juts for OctVec: /// //todo check it once again #[test] fn test_bounds_octree() { - - const D:u8 = 4; + const D: u8 = 4; let mut rng = rand::thread_rng(); for i in 1..100 { - let cmax = 1< { - (max, min) - }, - Ordering::Less => { - (min, max) - }, + Ordering::Greater => (max, min), + Ordering::Less => (min, max), Ordering::Equal => { continue; } @@ -885,7 +881,7 @@ impl SafeRngRange for rand::rngs::ThreadRng{ struct C; let mut count = 0; for pos in Tree::::iter_all_chunks_in_bounds(min, max, D) { - // println!("{:?}", pos); + // println!("{:?}", pos); if pos.depth == 4 { count += 1; @@ -895,19 +891,17 @@ impl SafeRngRange for rand::rngs::ThreadRng{ } } - #[test] fn test_iter_all_chunks_in_bounds_and_tree_mut() { - struct Chunk { - visible: bool, - cache_state: i32, - // 0 is new, 1 is merged, 2 is cached, 3 is both - selected: bool, - in_bounds: bool, -} + struct Chunk { + visible: bool, + cache_state: i32, + // 0 is new, 1 is merged, 2 is cached, 3 is both + selected: bool, + in_bounds: bool, + } - fn chunk_creator( position: QuadVec) -> Chunk - { + fn chunk_creator(position: QuadVec) -> Chunk { let r = 6; let visible = match position.depth { @@ -922,16 +916,10 @@ impl SafeRngRange for rand::rngs::ThreadRng{ selected: false, in_bounds: false, } - - } let mut tree = Tree::new(65); let qv = QuadVec::new(6, 6, 4); - if tree.prepare_update( - &[qv], - 6, - chunk_creator, - ) { + if tree.prepare_update(&[qv], 6, chunk_creator) { // position should already have been set, so we can just change the visibility for chunk in tree.iter_chunks_to_activate_mut() { chunk.visible = true; @@ -959,10 +947,12 @@ impl SafeRngRange for rand::rngs::ThreadRng{ let mut count = 0; for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, 4) { - if i.0.contains_child_node(QuadVec::new(i.0.x<<1, i.0.y<<1, 4)) { + if i.0 + .contains_child_node(QuadVec::new(i.0.x << 1, i.0.y << 1, 4)) + { i.1.visible = true; println!("cords:{:?}", i.0.get_float_coords()) } } } -} \ No newline at end of file +} diff --git a/src/traits.rs b/src/traits.rs index fb44552..6322852 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -3,7 +3,9 @@ /// trait for defining a Level of Detail vector. /// such a vector contains the current position in the octree (3d coords), as well as the lod level it's at, in integer coords. -pub trait LodVec: std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + Default + PartialOrd { +pub trait LodVec: + std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + Default + PartialOrd +{ /// gets one of the child node position of this node, defined by it's index. fn get_child(self, index: usize) -> Self; diff --git a/src/tree.rs b/src/tree.rs index 90f7dc1..4907544 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -6,7 +6,6 @@ use std::collections::{HashMap, VecDeque}; use std::fmt::Debug; use std::num::NonZeroUsize; - // struct for keeping track of chunks // keeps track of the parent and child indices #[derive(Copy, Clone, Debug, Default)] @@ -109,65 +108,14 @@ pub struct Tree { chunks_to_delete: Vec>, } -// struct TreeCursor<'a, C, L>{ -// tree: &'a Tree< C, L>, -// current: L -// } -// -// impl Iterator for TreeCursor -// { -// type Item = C; -// -// fn next(&mut self) -> Option { -// Some(self.c) -// } -// } impl Tree where C: Sized, L: LodVec, { - pub fn search_around(&mut self, center: L, bounds:u32, mutator: fn(&mut ChunkContainer )) - where L:Debug - { - - let mut node = *self.nodes.get(0).unwrap(); - let mut position = L::root(); - // then loop - loop { - - // if the current node is the one we are looking for, return - if position == center { - mutator( self.chunks.get_mut(node.chunk).unwrap()); - } - - // if the current node does not have children, stop - // this works according to clippy - if node.children.is_none() - { - return; - - } - - // if not, go over the node children - if let Some((index, found_position)) = (0..L::num_children()) - .map(|i| (i, position.get_child(i))) - .find(|(_, x)| x.contains_child_node(center)) - { - // we found the position to go to - position = found_position; - dbg!(position); - // and the node is at the index of the child nodes + index - node = self.nodes[node.children.unwrap().get() + index]; - } else { - // if no child got found that matched the item, return none - return; - } - } - } - - // helper function for later, gets a node index from a position + /// Gets an index in self.nodes vector from a position. + /// If position is not pointing to a node, None is returned. fn get_node_index_from_position(&self, position: L) -> Option { // the current node let mut current = *self.nodes.get(0)?; @@ -183,9 +131,8 @@ where } // if the current node does not have children, stop - // this works according to clippy + // this works according to clippy current.children?; - // if not, go over the node children if let Some((index, found_position)) = (0..L::num_children()) @@ -204,22 +151,41 @@ where } } - - - /// create a new, empty tree + /// create a new, empty tree, with a cache of given size pub fn new(cache_size: usize) -> Self { // make a new Tree // also allocate some room for nodes Self { - chunks_to_add_parent: Vec::with_capacity(512), - chunks_to_add: Vec::with_capacity(512), - chunks_to_remove: Vec::with_capacity(512), - chunks_to_activate: Vec::with_capacity(512), - chunks_to_deactivate: Vec::with_capacity(512), - chunks: Vec::with_capacity(512), - nodes: Vec::with_capacity(512), - free_list: VecDeque::with_capacity(512), - processing_queue: Vec::with_capacity(512), + chunks_to_add_parent: Vec::new(), + chunks_to_add: Vec::new(), + chunks_to_remove: Vec::new(), + chunks_to_activate: Vec::new(), + chunks_to_deactivate: Vec::new(), + chunks: Vec::new(), + nodes: Vec::new(), + free_list: VecDeque::new(), + processing_queue: Vec::new(), + cache_size, + chunk_cache: HashMap::with_capacity(cache_size), + cache_queue: VecDeque::with_capacity(cache_size), + chunks_to_delete: Vec::with_capacity(cache_size), + } + } + + /// create a tree with preallocated memory for chunks and nodes + pub fn with_capacity(capacity:usize, cache_size: usize) -> Self { + // make a new Tree + // also allocate some room for nodes + Self { + chunks_to_add_parent: Vec::with_capacity(capacity), + chunks_to_add: Vec::with_capacity(capacity), + chunks_to_remove: Vec::with_capacity(capacity), + chunks_to_activate: Vec::with_capacity(capacity), + chunks_to_deactivate: Vec::with_capacity(capacity), + chunks: Vec::with_capacity(capacity), + nodes: Vec::with_capacity(capacity), + free_list: VecDeque::with_capacity(capacity), + processing_queue: Vec::with_capacity(capacity), cache_size, chunk_cache: HashMap::with_capacity(cache_size), cache_queue: VecDeque::with_capacity(cache_size), From 099562ce947ec96ec82c0f9eb4566f1e1b25c4ff Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Tue, 14 Feb 2023 14:53:54 +0200 Subject: [PATCH 06/15] Fixet all tests in lib.rs, fixed warnings etc. cleaning up done --- examples/glium.rs | 102 +++++++++++++++++----------------------------- src/coords.rs | 12 +++--- src/iter.rs | 16 ++++---- src/lib.rs | 25 ++++++------ src/traits.rs | 12 +++--- src/tree.rs | 3 +- 6 files changed, 73 insertions(+), 97 deletions(-) diff --git a/examples/glium.rs b/examples/glium.rs index 622efd8..d473ce7 100644 --- a/examples/glium.rs +++ b/examples/glium.rs @@ -1,12 +1,15 @@ -use std::f32::consts::PI; -use glium::index::PrimitiveType; -use glium::{glutin, implement_vertex, program, uniform, Surface, Program, Display, VertexBuffer, IndexBuffer}; use glium::glutin::event_loop::EventLoop; +use glium::index::PrimitiveType; +use glium::{ + glutin, implement_vertex, program, uniform, Display, IndexBuffer, Program, Surface, + VertexBuffer, +}; use lodtree::coords::QuadVec; use lodtree::*; // the chunk struct for the tree +#[allow(dead_code)] struct Chunk { visible: bool, cache_state: i32, @@ -15,8 +18,7 @@ struct Chunk { in_bounds: bool, } -fn make_shaders(display: &Display) -> Program -{ +fn make_shaders(display: &Display) -> Program { let program = program!(display, 140 => { vertex: " @@ -58,7 +60,7 @@ fn make_shaders(display: &Display) -> Program " } ) - .unwrap(); + .unwrap(); return program; } @@ -72,22 +74,19 @@ implement_vertex!(Vertex, position); struct RenderContext { display: Display, vertex_buffer: VertexBuffer, - shaders: Program, index_buffer: IndexBuffer, } impl RenderContext { - pub fn new( event_loop: &EventLoop<()>) -> Self - { - + pub fn new(event_loop: &EventLoop<()>) -> Self { let wb = glutin::window::WindowBuilder::new().with_title("Quadtree demo"); let cb = glutin::ContextBuilder::new().with_vsync(true); - let display = glium::Display::new(wb, cb, &event_loop).unwrap(); + let display = Display::new(wb, cb, &event_loop).unwrap(); // make a vertex buffer // we'll reuse it as we only need to draw one quad multiple times anyway let vertex_buffer = { - glium::VertexBuffer::new( + VertexBuffer::new( &display, &[ Vertex { @@ -104,15 +103,15 @@ impl RenderContext { }, ], ) - .unwrap() + .unwrap() }; // and the index buffer to form the triangle - let index_buffer = glium::IndexBuffer::new( + let index_buffer = IndexBuffer::new( &display, PrimitiveType::TrianglesList, &[0 as u16, 1, 2, 1, 2, 3], ) - .unwrap(); + .unwrap(); let shaders = make_shaders(&display); Self { @@ -124,47 +123,27 @@ impl RenderContext { } } - -fn draw(mouse_pos: (f32, f32), - tree: &mut Tree, - ctx: &RenderContext) { - - - - // update the tree - // adding chunks to their respective position, and also set them visible when adding - fn chunk_creator(position:QuadVec)->Chunk - { - let R = 6; - - let visible = match position.depth { - 4=> ((position.x as i32 - R).pow(2) + (position.y as i32 - R).pow(2) < R), - _=>false, - }; - - // dbg!(position); - // dbg!(visible); - Chunk { - visible:true, - cache_state: visible as i32, +fn draw(mouse_pos: (f32, f32), tree: &mut Tree, ctx: &RenderContext) { + //function for adding chunks to their respective position, and also set their properties + fn chunk_creator(_position: QuadVec) -> Chunk { + Chunk { + visible: true, + cache_state: 0, selected: false, in_bounds: false, } } - let qv = QuadVec::new(6,6,4); - if tree.prepare_update( - &[qv], - 6, - chunk_creator, - ) { + + let qv = QuadVec::from_float_coords(mouse_pos.0 as f64, (1.0 - mouse_pos.1) as f64, 6); + if tree.prepare_update(&[qv], 2, chunk_creator) { // position should already have been set, so we can just change the visibility - for chunk in tree.iter_chunks_to_activate_mut() { - chunk.visible = true; - // chunk.cache_state |= 1; + for chunk in tree.iter_chunks_to_activate_mut() { + chunk.visible = true; + chunk.cache_state |= 1; } for chunk in tree.iter_chunks_to_deactivate_mut() { - //chunk.visible = false; + chunk.visible = false; } // and make chunks that are cached visible @@ -178,20 +157,16 @@ fn draw(mouse_pos: (f32, f32), // and clean tree.complete_update(); } - // dbg!("Searching!"); - // tree.search_around(qv, 7, | c|{ - // c.chunk.cache_state = 4 - // }); + // go over all chunks in the tree and set them to not be selected for chunk in tree.iter_chunks_mut() { chunk.selected = false; } // and select the chunk at the mouse position - // if let Some(chunk) = tree.get_chunk_from_position_mut(qv) { - // chunk.selected = true; - // chunk.visible = true; - // } + if let Some(chunk) = tree.get_chunk_from_position_mut(qv) { + chunk.selected = true; + } // and select a number of chunks in a region when the mouse buttons are selected @@ -205,11 +180,11 @@ fn draw(mouse_pos: (f32, f32), // draw it if it's visible // here we get the chunk position and size let uniforms = uniform! { - offset: [position.get_float_coords().0 as f32, position.get_float_coords().1 as f32], - scale: position.get_size() as f32, - state: chunk.cache_state, - selected: chunk.selected as i32, - }; + offset: [position.get_float_coords().0 as f32, position.get_float_coords().1 as f32], + scale: position.get_size() as f32, + state: chunk.cache_state, + selected: chunk.selected as i32, + }; // draw it with glium target @@ -226,11 +201,10 @@ fn draw(mouse_pos: (f32, f32), target.finish().unwrap(); } - fn main() { // set up the tree - let mut tree = Tree::::new(0); - // start the glium event loop + let mut tree = Tree::::new(32); + // start the glium event loop let event_loop = glutin::event_loop::EventLoop::new(); let context = RenderContext::new(&event_loop); draw((0.5, 0.5), &mut tree, &context); diff --git a/src/coords.rs b/src/coords.rs index fdd9453..0354f1b 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -45,9 +45,9 @@ impl QuadVec { /// * `depth` the lod depth the coord is at. This is soft limited at roughly 60, and the tree might behave weird if it gets higher #[inline] pub fn new(x: u64, y: u64, depth: u8) -> Self { - debug_assert!(x < (1<> self_difference; let self_y = self.y >> self_difference; @@ -173,7 +173,7 @@ impl LodVec for QuadVec { let max_x = max.x >> max_difference; let max_y = max.y >> max_difference; - // dbg!(min_x, min_y, max_x, max_y); + // dbg!(min_x, min_y, max_x, max_y); // then check if we are inside the AABB self.depth as u8 <= max_depth && self_x >= min_x @@ -289,7 +289,7 @@ impl OctVec { impl LodVec for OctVec { #[inline] fn get_child(self, index: usize) -> Self { - debug_assert!(index < 8); + debug_assert!(index < 8); // the positions, doubled in scale let x = self.x << 1; let y = self.y << 1; diff --git a/src/iter.rs b/src/iter.rs index 9921706..fece562 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -770,10 +770,10 @@ where mod tests { use super::*; use crate::coords::*; - use std::ops::Range; use rand::distributions::uniform::SampleUniform; use rand::Rng; use std::cmp::Ordering; + use std::ops::Range; pub trait SafeRngRange { fn safe_uniform(&mut self, range: Range) -> T @@ -806,7 +806,7 @@ mod tests { let mut rng = rand::thread_rng(); - for i in 1..100 { + for _i in 1..100 { let cmax = 1 << D; let min = QuadVec::new(rng.safe_uniform(0..cmax), rng.safe_uniform(0..cmax), D); let max = QuadVec::new(rng.safe_uniform(0..cmax), rng.safe_uniform(0..cmax), D); @@ -849,7 +849,7 @@ mod tests { let mut rng = rand::thread_rng(); - for i in 1..100 { + for _i in 1..100 { let cmax = 1 << D; let min = OctVec::new( rng.safe_uniform(0..cmax), @@ -897,8 +897,8 @@ mod tests { visible: bool, cache_state: i32, // 0 is new, 1 is merged, 2 is cached, 3 is both - selected: bool, - in_bounds: bool, + // selected: bool, + // in_bounds: bool, } fn chunk_creator(position: QuadVec) -> Chunk { @@ -913,8 +913,8 @@ mod tests { Chunk { visible: true, cache_state: visible as i32, - selected: false, - in_bounds: false, + // selected: false, + // in_bounds: false, } } let mut tree = Tree::new(65); @@ -944,7 +944,7 @@ mod tests { let min = QuadVec::new(0, 0, 4); let max = QuadVec::new(8, 8, 4); - let mut count = 0; + let mut _count = 0; for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, 4) { if i.0 diff --git a/src/lib.rs b/src/lib.rs index fdba650..bd7c7de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ //! # use lodtree::*; //! # use lodtree::coords::OctVec; //! # struct Chunk {} -//! let mut tree = Tree::::new(); +//! let mut tree = Tree::::new(64); //! ``` //! //! If you want to update chunks due to the camera being moved, you can check if it's needed with prepare_update. @@ -62,13 +62,13 @@ //! # impl Chunk { //! # fn expensive_init(&mut self, pos: QuadVec) {} //! # } -//! # let mut tree = Tree::::new(); +//! # let mut tree = Tree::::new(64); +//! //! tree.get_chunks_to_add_slice_mut() //! .iter_mut() // or par_iter_mut() if you're using rayon -//! .for_each(|(position, chunk)| { -//! +//! .for_each(|to_add| { //! // and run expensive init, probably does something with procedural generation -//! chunk.expensive_init(*position); +//! to_add.chunk.expensive_init(to_add.position); //! }); //! ``` //! @@ -80,7 +80,7 @@ //! # impl Chunk { //! # fn set_visible(&mut self, v: bool) {} //! # } -//! # let mut tree = Tree::::new(); +//! # let mut tree = Tree::::new(64); //! // and make all chunks visible or not //! for chunk in tree.iter_chunks_to_activate_mut() { //! chunk.set_visible(true); @@ -99,7 +99,7 @@ //! # impl Chunk { //! # fn cleanup(&mut self) {} //! # } -//! # let mut tree = Tree::::new(); +//! # let mut tree = Tree::::new(64); //! for chunk in tree.iter_chunks_to_remove_mut() { //! chunk.cleanup(); //! } @@ -110,7 +110,7 @@ //! # use lodtree::*; //! # use lodtree::coords::QuadVec; //! # struct Chunk {} -//! # let mut tree = Tree::::new(); +//! # let mut tree = Tree::::new(64); //! tree.do_update(); //! ``` //! But we're not done yet! @@ -123,10 +123,11 @@ //! # impl Chunk { //! # fn true_cleanup(&mut self) {} //! # } -//! # let mut tree = Tree::::new(); -//! for (position, chunk) in tree.get_chunks_to_delete_slice_mut().iter_mut() { // there's also an iterator for just chunks here -//! chunk.true_cleanup(); -//! } +//! +//! let mut tree = Tree::::new(64); +//! for td in tree.get_chunks_to_delete_slice_mut().iter_mut() { // there's also an iterator for just chunks here +//! td.chunk.true_cleanup(); +//! } //! //! // and finally, complete the entire update //! tree.complete_update(); diff --git a/src/traits.rs b/src/traits.rs index 6322852..dd8b28e 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -66,8 +66,9 @@ pub trait LodVec: /// where min is the lowest corner of the box, and max is the highest corner /// The implementation for QuadVec is as follows: /// ```rust - /// # struct Chunk { x: u64, y: u64, depth: u8 } - /// # impl Chunk { + /// struct Chunk { x: u64, y: u64, depth: u8 } + /// impl Chunk { + /// fn is_inside_bounds(self, min: Self, max: Self, max_depth: u8) -> bool{ /// // get the lowest lod level /// let level = self.depth.min(min.depth.min(max.depth)); /// @@ -76,7 +77,7 @@ pub trait LodVec: /// let min_difference = min.depth - level; /// let max_difference = max.depth - level; /// - //// // get the coords to that level + /// // get the coords to that level /// let self_x = self.x >> self_difference; /// let self_y = self.y >> self_difference; /// @@ -87,12 +88,13 @@ pub trait LodVec: /// let max_y = max.y >> max_difference; /// /// // then check if we are inside the AABB - /// self.depth as u64 <= max_depth + /// self.depth <= max_depth /// && self_x >= min_x /// && self_x < max_x /// && self_y >= min_y /// && self_y < max_y - /// # } + /// } + /// } /// ``` fn is_inside_bounds(self, min: Self, max: Self, max_depth: u8) -> bool; diff --git a/src/tree.rs b/src/tree.rs index 4907544..2eddf0e 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -108,7 +108,6 @@ pub struct Tree { chunks_to_delete: Vec>, } - impl Tree where C: Sized, @@ -173,7 +172,7 @@ where } /// create a tree with preallocated memory for chunks and nodes - pub fn with_capacity(capacity:usize, cache_size: usize) -> Self { + pub fn with_capacity(capacity: usize, cache_size: usize) -> Self { // make a new Tree // also allocate some room for nodes Self { From c49c72d41a968748ce18066e2174850a6a05d4d1 Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Mon, 27 Feb 2023 10:35:12 +0200 Subject: [PATCH 07/15] Added criterion bench test, little clean up --- Cargo.toml | 9 ++- benches/iterators.rs | 183 +++++++++++++++++++++++++++++++++++++++++++ examples/glium.rs | 2 +- src/iter.rs | 151 +++++++++++++++++------------------ src/tree.rs | 75 +++++++++--------- 5 files changed, 303 insertions(+), 117 deletions(-) create mode 100644 benches/iterators.rs diff --git a/Cargo.toml b/Cargo.toml index 0e2dc81..04b9494 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,5 +16,10 @@ categories = ["data-structures"] [dev_dependencies] rayon = "1.5" glium = "0.30" -rand ="0.8.5" -rand_derive = "0.5.0" \ No newline at end of file +rand ={version="0.8.5", features=['small_rng']} +rand_derive = "0.5.0" +criterion = "0.3" + +[[bench]] +name = "iterators" +harness = false \ No newline at end of file diff --git a/benches/iterators.rs b/benches/iterators.rs new file mode 100644 index 0000000..75b3bf0 --- /dev/null +++ b/benches/iterators.rs @@ -0,0 +1,183 @@ +use lodtree::coords::OctVec; +use lodtree::Tree; +use rand::rngs::SmallRng; +use rand::{Rng, SeedableRng}; +use std::cmp::Ordering; + +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode}; + +const N_LOOKUPS:usize = 40; +fn generate_area_bounds(rng: &mut SmallRng) -> (OctVec, OctVec) { + const D: u8 = 4; + let cmax = 1 << D; + + let min = OctVec::new( + rng.gen_range(0..cmax-1), + rng.gen_range(0..cmax-1), + rng.gen_range(0..cmax-1), + D, + ); + let max = OctVec::new( + rng.gen_range(min.x+1..cmax), + rng.gen_range(min.y+1..cmax), + rng.gen_range(min.z+1..cmax), + D, + ); + return (min, max); +} + +struct ChuChunk { + tx_index: u8, + rx_index: u8, + material_index: u16, + tx_visible: u64, + rx_visible: u64, +} + +impl Default for ChuChunk { + fn default() -> ChuChunk { + ChuChunk { + tx_index: 1, + rx_index: 2, + material_index: 3, + tx_visible: 4, + rx_visible: 5, + } + } +} + +fn create_and_fill_octree(num_chunks: u32, depth: u8) -> Tree { + let mut rng = SmallRng::seed_from_u64(42); + let mut tree: Tree = Tree::with_capacity( 0, 0); + + let cmax = 1 << depth; + + for _c in 0..num_chunks { + let qv = OctVec::new( + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), + depth, + ); + + while tree.prepare_update(&[qv], 0, |_p| C::default()) { + // do the update + tree.do_update(); + // and clean + tree.complete_update(); + } + } + tree +} + +fn bench_lookups_in_octree(tree: & Tree) { + let mut rng = SmallRng::seed_from_u64(42); + for _ in 0..N_LOOKUPS { + let (min, max) = generate_area_bounds(&mut rng); + for i in tree.iter_all_chunks_in_bounds_and_tree(min, max, 4) { + black_box(i); + } + } +} + +fn bench_mut_lookups_in_octree(tree: &mut Tree) { + let mut rng = SmallRng::seed_from_u64(42); + for _ in 0..N_LOOKUPS { + let (min, max) = generate_area_bounds(&mut rng); + for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, 4) { + i.1.material_index += 1; + i.1.rx_index += 1; + i.1.tx_visible += 1; + i.1.tx_index += 1; + } + } +} + +pub fn bench_iteration(c: &mut Criterion) { + let mut group = c.benchmark_group("mutable iteration"); + let mut samples_num = 100; + + for depth in [4u8, 6, 8, 10].iter() { + + if *depth as i8 == 4{ + samples_num = 100; + } + if *depth as i8 ==6{ + samples_num = 40; + } + if *depth as i8 ==8{ + samples_num = 10; + } + group.significance_level(0.1).sample_size(samples_num); + + let num_chunks: u32 = 2u32.pow(*depth as u32).pow(3) / 10; + group.bench_with_input(BenchmarkId::from_parameter(depth), depth, |b, depth| { + let mut tree = create_and_fill_octree::(num_chunks, *depth); + b.iter(|| { + bench_mut_lookups_in_octree(&mut tree); + }); + black_box(tree); + }); + } + group.finish(); + + let mut group = c.benchmark_group("immutable iteration"); + let mut samples_num = 10; + + + for depth in [4u8, 6, 8, 10].iter() { + + if *depth as i8 == 4{ + samples_num = 100; + } + if *depth as i8 ==6{ + samples_num = 40; + } + if *depth as i8 ==8{ + samples_num = 10; + } + group.significance_level(0.1).sample_size(samples_num); + let num_chunks: u32 = 2u32.pow(*depth as u32).pow(3) / 10; + group.bench_with_input(BenchmarkId::from_parameter(depth), depth, |b, depth| { + let tree = create_and_fill_octree::(num_chunks, *depth); + b.iter(|| { + bench_lookups_in_octree(&tree); + }); + }); + } + group.finish(); +} + +pub fn bench_creation (c: &mut Criterion) { + let mut group = c.benchmark_group("tree creation"); + + let mut samples_num = 10; + + for depth in [4u8, 6, 8].iter() { + + if *depth as i8 == 4{ + samples_num = 100; + } + if *depth as i8 ==6{ + samples_num = 40; + } + if *depth as i8 ==8{ + samples_num = 10; + } + group.significance_level(0.1).sample_size(samples_num); + group.bench_with_input(BenchmarkId::from_parameter(depth), depth, |b, &depth| { + let volume = 2u32.pow(depth as u32).pow(3); + let num_chunks: u32 = volume / 10; + println!("Creating {num_chunks} voxels out of {volume} possible"); + b.iter(|| { + + let t = create_and_fill_octree::(num_chunks, depth); + black_box(t); + }); + }); + } + group.finish(); +} + +criterion_group!(benches, bench_creation, bench_iteration); +criterion_main!(benches); diff --git a/examples/glium.rs b/examples/glium.rs index d473ce7..be29a40 100644 --- a/examples/glium.rs +++ b/examples/glium.rs @@ -145,7 +145,7 @@ fn draw(mouse_pos: (f32, f32), tree: &mut Tree, ctx: &RenderCont for chunk in tree.iter_chunks_to_deactivate_mut() { chunk.visible = false; } - + tree.get_chunk_to_delete() // and make chunks that are cached visible for chunk in tree.iter_chunks_to_remove_mut() { chunk.cache_state = 2; diff --git a/src/iter.rs b/src/iter.rs index fece562..87b035b 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -770,46 +770,28 @@ where mod tests { use super::*; use crate::coords::*; - use rand::distributions::uniform::SampleUniform; - use rand::Rng; + use rand::rngs::SmallRng; + use rand::{Rng, SeedableRng}; use std::cmp::Ordering; - use std::ops::Range; - pub trait SafeRngRange { - fn safe_uniform(&mut self, range: Range) -> T - where - T: SampleUniform + PartialOrd; - } + const NUM_QUERIES: usize = 100; - impl SafeRngRange for rand::rngs::ThreadRng { - //#[no_panic] - fn safe_uniform(&mut self, range: Range) -> T - where - T: SampleUniform + PartialOrd, - { - if range.is_empty() { - range.start - } else { - self.gen_range(range) - } - } - } - // TODO: also test the other iters - fn get_chunk_count_at_max_depth(a: QuadVec, b: QuadVec) -> u64 { + fn get_chunk_count_at_max_depth_quad(a: QuadVec, b: QuadVec) -> u64 { assert_eq!(a.depth, b.depth); ((b.x - a.x) + 1) * ((b.y - a.y) + 1) } #[test] + ///Tests generation of coordinates in bounds over QuadTree fn test_bounds_quadtree() { const D: u8 = 4; let mut rng = rand::thread_rng(); - for _i in 1..100 { + for _i in 1..NUM_QUERIES { let cmax = 1 << D; - let min = QuadVec::new(rng.safe_uniform(0..cmax), rng.safe_uniform(0..cmax), D); - let max = QuadVec::new(rng.safe_uniform(0..cmax), rng.safe_uniform(0..cmax), D); + let min = QuadVec::new(rng.gen_range(0..cmax), rng.gen_range(0..cmax), D); + let max = QuadVec::new(rng.gen_range(0..cmax), rng.gen_range(0..cmax), D); //println!("Generated min {:?}", min); //println!("Generated max {:?}", max); let cmp = min.partial_cmp(&max); @@ -833,7 +815,7 @@ mod tests { count += 1; } } - assert_eq!(count, get_chunk_count_at_max_depth(min, max)); + assert_eq!(count, get_chunk_count_at_max_depth_quad(min, max)); } } @@ -841,8 +823,8 @@ mod tests { assert_eq!(a.depth, b.depth); ((b.x - a.x) + 1) * ((b.y - a.y) + 1) * ((b.z - a.z) + 1) } - ///The same unit test as test_bounds juts for OctVec: - /// //todo check it once again + + ///Tests generation of coordinates in bounds over OctTree #[test] fn test_bounds_octree() { const D: u8 = 4; @@ -852,15 +834,15 @@ mod tests { for _i in 1..100 { let cmax = 1 << D; let min = OctVec::new( - rng.safe_uniform(0..cmax), - rng.safe_uniform(0..cmax), - rng.safe_uniform(0..cmax), + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), D, ); let max = OctVec::new( - rng.safe_uniform(0..cmax), - rng.safe_uniform(0..cmax), - rng.safe_uniform(0..cmax), + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), D, ); @@ -892,67 +874,80 @@ mod tests { } #[test] - fn test_iter_all_chunks_in_bounds_and_tree_mut() { + fn test_readonly_iterator_over_chunks_oct() { struct Chunk { visible: bool, - cache_state: i32, - // 0 is new, 1 is merged, 2 is cached, 3 is both - // selected: bool, - // in_bounds: bool, } + const D: u8 = 4; + const R: u64 = 3; - fn chunk_creator(position: QuadVec) -> Chunk { - let r = 6; + fn chunk_creator(position: OctVec) -> Chunk { + let r = (R * R) as i32 - 2; let visible = match position.depth { - 4 => (position.x as i32 - r).pow(2) + (position.y as i32 - r).pow(2) < r, + D => { + (position.x as i32 - r).pow(2) + + (position.y as i32 - r).pow(2) + + (position.z as i32 - r).pow(2) + < r + } _ => false, }; - // dbg!(position); - // dbg!(visible); - Chunk { - visible: true, - cache_state: visible as i32, - // selected: false, - // in_bounds: false, - } + //println!("create {:?} {:?}", position, visible); + Chunk { visible } } - let mut tree = Tree::new(65); - let qv = QuadVec::new(6, 6, 4); - if tree.prepare_update(&[qv], 6, chunk_creator) { - // position should already have been set, so we can just change the visibility - for chunk in tree.iter_chunks_to_activate_mut() { - chunk.visible = true; - // chunk.cache_state |= 1; - } - - for chunk in tree.iter_chunks_to_deactivate_mut() { - chunk.visible = false; - } - - // and make chunks that are cached visible - for chunk in tree.iter_chunks_to_remove_mut() { - chunk.cache_state = 2; - } + let mut tree = Tree::new(65); + let qv = OctVec::new(R, R, R, D); + while tree.prepare_update(&[qv], R, chunk_creator) { // do the update tree.do_update(); - // and clean tree.complete_update(); } - let min = QuadVec::new(0, 0, 4); - let max = QuadVec::new(8, 8, 4); - let mut _count = 0; + for i in &tree.chunks {} + + let mut rng = SmallRng::seed_from_u64(42); + let mut _rng = rand::thread_rng(); - for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, 4) { - if i.0 - .contains_child_node(QuadVec::new(i.0.x << 1, i.0.y << 1, 4)) - { - i.1.visible = true; - println!("cords:{:?}", i.0.get_float_coords()) + for ite in 1..NUM_QUERIES { + let cmax = 1 << D; + let min = OctVec::new( + _rng.gen_range(0..cmax), + _rng.gen_range(0..cmax), + _rng.gen_range(0..cmax), + D, + ); + let max = OctVec::new( + _rng.gen_range(0..cmax), + _rng.gen_range(0..cmax), + _rng.gen_range(0..cmax), + D, + ); + let cmp = min.partial_cmp(&max); + if cmp.is_none() { + //println!("Can not compare {min:?} and {max:?}"); + continue; } + let (min, max) = match cmp.unwrap() { + Ordering::Greater => (max, min), + Ordering::Less => (min, max), + Ordering::Equal => { + continue; + } + }; + let mut filled_voxels: u32 = 0; + //println!("{:?} {:?} {:?}", ite, min, max); + for i in tree.iter_all_chunks_in_bounds_and_tree(min, max, D) { + if i.1.visible { + println!(" Sphere chunk {:?}", i.0); + filled_voxels += 1; + } + } + println!(" filled {:?}", filled_voxels); } } + + } diff --git a/src/tree.rs b/src/tree.rs index 2eddf0e..8204355 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -66,7 +66,7 @@ struct QueueContainer { // partially based on: https://stackoverflow.com/questions/41946007/efficient-and-well-explained-implementation-of-a-quadtree-for-2d-collision-det // assumption here is that because of the fact that we need to keep inactive chunks in memory for later use, we can keep them together with the actual nodes. #[derive(Clone, Debug)] -pub struct Tree { +pub struct Tree { /// All chunks in the tree pub(crate) chunks: Vec>, @@ -74,7 +74,7 @@ pub struct Tree { pub(crate) nodes: Vec, /// list of free nodes in the Tree, to allocate new nodes into - free_list: VecDeque, + free_list: VecDeque, /// parent chunk indices of the chunks to be added. /// tuple of the parent index and the position. @@ -150,7 +150,8 @@ where } } - /// create a new, empty tree, with a cache of given size + /// Create a new, empty tree, with a cache of given size + /// Set cache to zero to disable it entirely (may speed up certain workloads by ~50%) pub fn new(cache_size: usize) -> Self { // make a new Tree // also allocate some room for nodes @@ -172,6 +173,7 @@ where } /// create a tree with preallocated memory for chunks and nodes + /// Set cache to zero to disable it entirely (may speed up certain workloads by ~50%) pub fn with_capacity(capacity: usize, cache_size: usize) -> Self { // make a new Tree // also allocate some room for nodes @@ -429,7 +431,7 @@ where // if so, queue children for removal, and self for activation (child indices, chunk pointer) // if we can subdivide, and have no children, queue children for addition, and self for removal (child positions, chunk pointer) // if none of the above and have children, queue children for processing - // processing queue is only the node positon and node index + // processing queue is only the node position and node index // when removing nodes, do so in groups of num children, and use the free list // clear the free list once we only have one chunk (the root) active @@ -441,7 +443,7 @@ where /// * `targets` The target positions to generate the lod around (QuadVec and OctVec define the center position and max lod in depth for this) /// * `detail` The detail for these targets (QuadVec and OctVec define this as amount of chunks around this point) /// * `chunk_creator` function to create a new chunk from a given position - /// returns wether any update is needed. + /// returns whether any update is needed. pub fn prepare_update( &mut self, targets: &[L], @@ -622,21 +624,20 @@ where break; } } + if self.cache_size >0 { + // then assign this chunk into the cache + if let Some(cached_chunk) = self.chunk_cache.insert(old_chunk.position, old_chunk.chunk) + { + // there might have been another cached chunk + self.chunks_to_delete.push(ToDeleteContainer { + position: old_chunk.position, + chunk: cached_chunk, + }); + } - // then assign this chunk into the cache - if let Some(cached_chunk) = - self.chunk_cache.insert(old_chunk.position, old_chunk.chunk) - { - // there might have been another cached chunk - self.chunks_to_delete.push(ToDeleteContainer { - position: old_chunk.position, - chunk: cached_chunk, - }); + // and make sure it's tracked + self.cache_queue.push_back(old_chunk.position); } - - // and make sure it's tracked - self.cache_queue.push_back(old_chunk.position); - x } // This can't be reached due to us *always* adding a chunk to the free list before popping it @@ -674,20 +675,20 @@ where break; } } - - // then assign this chunk into the cache - if let Some(cached_chunk) = - self.chunk_cache.insert(old_chunk.position, old_chunk.chunk) - { - // there might have been another cached chunk - self.chunks_to_delete.push(ToDeleteContainer { - position: old_chunk.position, - chunk: cached_chunk, - }); + if self.cache_size > 0 { + //then assign this chunk into the cache + if let Some(cached_chunk) = + self.chunk_cache.insert(old_chunk.position, old_chunk.chunk) + { + // there might have been another cached chunk + self.chunks_to_delete.push(ToDeleteContainer { + position: old_chunk.position, + chunk: cached_chunk, + }); + } + // and make sure it's tracked + self.cache_queue.push_back(old_chunk.position); } - - // and make sure it's tracked - self.cache_queue.push_back(old_chunk.position); } // and properly set the chunk pointer of the node of the chunk we just moved, if any @@ -813,11 +814,13 @@ where // gets a chunk from the cache, otehrwise generates one from the given function #[inline] fn get_chunk_from_cache(&mut self, position: L, chunk_creator: fn(L) -> C) -> C { - if let Some(chunk) = self.chunk_cache.remove(&position) { - chunk - } else { - chunk_creator(position) - } + if self.cache_size >0 { + if let Some(chunk) = self.chunk_cache.remove(&position) + { + return chunk; + } + } + return chunk_creator(position); } } From 4bdd3ad0ac86c002c0eb70737d52b8828c4ea4d2 Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Mon, 27 Feb 2023 11:50:55 +0200 Subject: [PATCH 08/15] Changed sintature in prepare_update on Fn() trait --- src/iter.rs | 2 +- src/tree.rs | 96 ++++++++++++++++++++++++++--------------------------- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/iter.rs b/src/iter.rs index 87b035b..5b5b6b0 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -899,7 +899,7 @@ mod tests { let mut tree = Tree::new(65); let qv = OctVec::new(R, R, R, D); - while tree.prepare_update(&[qv], R, chunk_creator) { + while tree.prepare_update(&[qv], R, &chunk_creator) { // do the update tree.do_update(); // and clean diff --git a/src/tree.rs b/src/tree.rs index 8204355..b118e63 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -448,7 +448,7 @@ where &mut self, targets: &[L], detail: u64, - chunk_creator: fn(L) -> C, + chunk_creator: &dyn Fn(L) -> C, ) -> bool { // first, clear the previous arrays self.chunks_to_add.clear(); @@ -813,7 +813,7 @@ where // gets a chunk from the cache, otehrwise generates one from the given function #[inline] - fn get_chunk_from_cache(&mut self, position: L, chunk_creator: fn(L) -> C) -> C { + fn get_chunk_from_cache(&mut self, position: L, chunk_creator: &dyn Fn(L) -> C) -> C { if self.cache_size >0 { if let Some(chunk) = self.chunk_cache.remove(&position) { @@ -846,51 +846,51 @@ mod tests { #[test] fn new_tree() { // make a tree - let mut tree = Tree::::new(64); - - // as long as we need to update, do so - while tree.prepare_update(&[QuadVec::new(128, 128, 32)], 8, |_| TestChunk {}) { - // and actually update - tree.do_update(); - } - - // and move the target - while tree.prepare_update(&[QuadVec::new(16, 8, 16)], 8, |_| TestChunk {}) { - // and actually update - tree.do_update(); - } - - // get the resulting chunk from a search - let found_chunk = tree.get_chunk_from_position(QuadVec::new(16, 8, 16)); - - // and find the resulting chunk - println!("{:?}", found_chunk.is_some()); - - // and make the tree have no items - while tree.prepare_update(&[], 8, |_| TestChunk {}) { - // and actually update - tree.do_update(); - } - - // and do the same for an octree - let mut tree = Tree::::new(64); - - // as long as we need to update, do so - while tree.prepare_update(&[OctVec::new(128, 128, 128, 32)], 8, |_| TestChunk {}) { - // and actually update - tree.do_update(); - } - - // and move the target - while tree.prepare_update(&[OctVec::new(16, 8, 32, 16)], 8, |_| TestChunk {}) { - // and actually update - tree.do_update(); - } - - // and make the tree have no items - while tree.prepare_update(&[], 8, |_| TestChunk {}) { - // and actually update - tree.do_update(); - } + // let mut tree = Tree::::new(64); + // + // // as long as we need to update, do so + // while tree.prepare_update(&[QuadVec::new(128, 128, 32)], 8, &TestChunk ) { + // // and actually update + // tree.do_update(); + // } + // + // // and move the target + // while tree.prepare_update(&[QuadVec::new(16, 8, 16)], 8, |_| TestChunk {}) { + // // and actually update + // tree.do_update(); + // } + // + // // get the resulting chunk from a search + // let found_chunk = tree.get_chunk_from_position(QuadVec::new(16, 8, 16)); + // + // // and find the resulting chunk + // println!("{:?}", found_chunk.is_some()); + // + // // and make the tree have no items + // while tree.prepare_update(&[], 8, |_| TestChunk {}) { + // // and actually update + // tree.do_update(); + // } + // + // // and do the same for an octree + // let mut tree = Tree::::new(64); + // + // // as long as we need to update, do so + // while tree.prepare_update(&[OctVec::new(128, 128, 128, 32)], 8, |_| TestChunk {}) { + // // and actually update + // tree.do_update(); + // } + // + // // and move the target + // while tree.prepare_update(&[OctVec::new(16, 8, 32, 16)], 8, |_| TestChunk {}) { + // // and actually update + // tree.do_update(); + // } + // + // // and make the tree have no items + // while tree.prepare_update(&[], 8, |_| TestChunk {}) { + // // and actually update + // tree.do_update(); + // } } } From c463f5b2a1e92e5396dba38666aa228fbd6e0dfc Mon Sep 17 00:00:00 2001 From: Alex Pyattaev Date: Fri, 3 Mar 2023 00:54:07 +0200 Subject: [PATCH 09/15] Added insert operation to lodtree (useful when using lodtree for generic storage) --- Cargo.toml | 4 +- benches/iterators.rs | 75 ++++++++--------- examples/glium.rs | 4 +- examples/rayon.rs | 2 +- src/coords.rs | 8 +- src/iter.rs | 19 ++--- src/lib.rs | 22 ++--- src/traits.rs | 8 +- src/tree.rs | 188 +++++++++++++++++++++++++++++++++++++------ 9 files changed, 229 insertions(+), 101 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 04b9494..376a102 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,8 @@ rayon = "1.5" glium = "0.30" rand ={version="0.8.5", features=['small_rng']} rand_derive = "0.5.0" -criterion = "0.3" +criterion = {version="0.3", features=['html_reports']} [[bench]] name = "iterators" -harness = false \ No newline at end of file +harness = false diff --git a/benches/iterators.rs b/benches/iterators.rs index 75b3bf0..44a697a 100644 --- a/benches/iterators.rs +++ b/benches/iterators.rs @@ -2,53 +2,48 @@ use lodtree::coords::OctVec; use lodtree::Tree; use rand::rngs::SmallRng; use rand::{Rng, SeedableRng}; -use std::cmp::Ordering; -use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; -const N_LOOKUPS:usize = 40; +const N_LOOKUPS: usize = 40; fn generate_area_bounds(rng: &mut SmallRng) -> (OctVec, OctVec) { const D: u8 = 4; let cmax = 1 << D; let min = OctVec::new( - rng.gen_range(0..cmax-1), - rng.gen_range(0..cmax-1), - rng.gen_range(0..cmax-1), + rng.gen_range(0..cmax - 1), + rng.gen_range(0..cmax - 1), + rng.gen_range(0..cmax - 1), D, ); let max = OctVec::new( - rng.gen_range(min.x+1..cmax), - rng.gen_range(min.y+1..cmax), - rng.gen_range(min.z+1..cmax), - D, - ); + rng.gen_range(min.x + 1..cmax), + rng.gen_range(min.y + 1..cmax), + rng.gen_range(min.z + 1..cmax), + D, + ); return (min, max); } struct ChuChunk { - tx_index: u8, - rx_index: u8, + a_index: u8, + b_index: u8, material_index: u16, - tx_visible: u64, - rx_visible: u64, } impl Default for ChuChunk { fn default() -> ChuChunk { ChuChunk { - tx_index: 1, - rx_index: 2, + a_index: 1, + b_index: 2, material_index: 3, - tx_visible: 4, - rx_visible: 5, } } } fn create_and_fill_octree(num_chunks: u32, depth: u8) -> Tree { let mut rng = SmallRng::seed_from_u64(42); - let mut tree: Tree = Tree::with_capacity( 0, 0); + let mut tree: Tree = Tree::with_capacity(0, 0); let cmax = 1 << depth; @@ -60,7 +55,7 @@ fn create_and_fill_octree(num_chunks: u32, depth: u8) -> Tree(num_chunks: u32, depth: u8) -> Tree) { +fn bench_lookups_in_octree(tree: &Tree) { let mut rng = SmallRng::seed_from_u64(42); for _ in 0..N_LOOKUPS { let (min, max) = generate_area_bounds(&mut rng); @@ -86,9 +81,8 @@ fn bench_mut_lookups_in_octree(tree: &mut Tree) { let (min, max) = generate_area_bounds(&mut rng); for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, 4) { i.1.material_index += 1; - i.1.rx_index += 1; - i.1.tx_visible += 1; - i.1.tx_index += 1; + i.1.a_index += 1; + i.1.b_index += 1; } } } @@ -98,14 +92,13 @@ pub fn bench_iteration(c: &mut Criterion) { let mut samples_num = 100; for depth in [4u8, 6, 8, 10].iter() { - - if *depth as i8 == 4{ + if *depth as i8 == 4 { samples_num = 100; } - if *depth as i8 ==6{ + if *depth as i8 == 6 { samples_num = 40; } - if *depth as i8 ==8{ + if *depth as i8 == 8 { samples_num = 10; } group.significance_level(0.1).sample_size(samples_num); @@ -121,25 +114,23 @@ pub fn bench_iteration(c: &mut Criterion) { } group.finish(); - let mut group = c.benchmark_group("immutable iteration"); - let mut samples_num = 10; - + let mut group = c.benchmark_group("immutable iteration"); + let mut samples_num = 10; for depth in [4u8, 6, 8, 10].iter() { - - if *depth as i8 == 4{ + if *depth as i8 == 4 { samples_num = 100; } - if *depth as i8 ==6{ + if *depth as i8 == 6 { samples_num = 40; } - if *depth as i8 ==8{ + if *depth as i8 == 8 { samples_num = 10; } group.significance_level(0.1).sample_size(samples_num); let num_chunks: u32 = 2u32.pow(*depth as u32).pow(3) / 10; group.bench_with_input(BenchmarkId::from_parameter(depth), depth, |b, depth| { - let tree = create_and_fill_octree::(num_chunks, *depth); + let tree = create_and_fill_octree::(num_chunks, *depth); b.iter(|| { bench_lookups_in_octree(&tree); }); @@ -148,20 +139,19 @@ pub fn bench_iteration(c: &mut Criterion) { group.finish(); } -pub fn bench_creation (c: &mut Criterion) { +pub fn bench_creation(c: &mut Criterion) { let mut group = c.benchmark_group("tree creation"); let mut samples_num = 10; for depth in [4u8, 6, 8].iter() { - - if *depth as i8 == 4{ + if *depth as i8 == 4 { samples_num = 100; } - if *depth as i8 ==6{ + if *depth as i8 == 6 { samples_num = 40; } - if *depth as i8 ==8{ + if *depth as i8 == 8 { samples_num = 10; } group.significance_level(0.1).sample_size(samples_num); @@ -170,7 +160,6 @@ pub fn bench_creation (c: &mut Criterion) { let num_chunks: u32 = volume / 10; println!("Creating {num_chunks} voxels out of {volume} possible"); b.iter(|| { - let t = create_and_fill_octree::(num_chunks, depth); black_box(t); }); diff --git a/examples/glium.rs b/examples/glium.rs index be29a40..f16a348 100644 --- a/examples/glium.rs +++ b/examples/glium.rs @@ -135,7 +135,7 @@ fn draw(mouse_pos: (f32, f32), tree: &mut Tree, ctx: &RenderCont } let qv = QuadVec::from_float_coords(mouse_pos.0 as f64, (1.0 - mouse_pos.1) as f64, 6); - if tree.prepare_update(&[qv], 2, chunk_creator) { + if tree.prepare_update(&[qv], 2, &chunk_creator) { // position should already have been set, so we can just change the visibility for chunk in tree.iter_chunks_to_activate_mut() { chunk.visible = true; @@ -145,7 +145,7 @@ fn draw(mouse_pos: (f32, f32), tree: &mut Tree, ctx: &RenderCont for chunk in tree.iter_chunks_to_deactivate_mut() { chunk.visible = false; } - tree.get_chunk_to_delete() + // and make chunks that are cached visible for chunk in tree.iter_chunks_to_remove_mut() { chunk.cache_state = 2; diff --git a/examples/rayon.rs b/examples/rayon.rs index 52f5f10..365595b 100644 --- a/examples/rayon.rs +++ b/examples/rayon.rs @@ -44,7 +44,7 @@ fn main() { if tree.prepare_update( &[OctVec::new(4096, 4096, 4096, 32)], // target position in the tree 2, // the amount of detail - |position_in_tree| Chunk::new(position_in_tree), // and how we should make a new tree inside the function here. This should be done quickly + &|position_in_tree| Chunk::new(position_in_tree), // and how we should make a new tree inside the function here. This should be done quickly ) { let duration = start_time.elapsed().as_micros(); diff --git a/src/coords.rs b/src/coords.rs index 0354f1b..1678731 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -33,7 +33,7 @@ impl PartialOrd for QuadVec { } else if (self.x > other.x) && (self.y > other.y) { return Some(Ordering::Greater); } - return None; + None } } @@ -175,7 +175,7 @@ impl LodVec for QuadVec { let max_y = max.y >> max_difference; // dbg!(min_x, min_y, max_x, max_y); // then check if we are inside the AABB - self.depth as u8 <= max_depth + self.depth <= max_depth && self_x >= min_x && self_x <= max_x && self_y >= min_y @@ -228,7 +228,7 @@ impl PartialOrd for OctVec { } else if (self.x > other.x) && (self.y > other.y) && (self.z > other.z) { return Some(Ordering::Greater); } - return None; + None } } impl OctVec { @@ -390,7 +390,7 @@ impl LodVec for OctVec { let max_z = max.z >> max_difference; // then check if we are inside the AABB - self.depth as u8 <= max_depth + self.depth <= max_depth && self_x >= min_x && self_x <= max_x && self_y >= min_y diff --git a/src/iter.rs b/src/iter.rs index 5b5b6b0..344626b 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -906,23 +906,22 @@ mod tests { tree.complete_update(); } - for i in &tree.chunks {} + //for i in &tree.chunks {} let mut rng = SmallRng::seed_from_u64(42); - let mut _rng = rand::thread_rng(); - for ite in 1..NUM_QUERIES { + for _ite in 1..NUM_QUERIES { let cmax = 1 << D; let min = OctVec::new( - _rng.gen_range(0..cmax), - _rng.gen_range(0..cmax), - _rng.gen_range(0..cmax), + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), D, ); let max = OctVec::new( - _rng.gen_range(0..cmax), - _rng.gen_range(0..cmax), - _rng.gen_range(0..cmax), + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), + rng.gen_range(0..cmax), D, ); let cmp = min.partial_cmp(&max); @@ -948,6 +947,4 @@ mod tests { println!(" filled {:?}", filled_voxels); } } - - } diff --git a/src/lib.rs b/src/lib.rs index bd7c7de..8cd375f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,9 +45,9 @@ //! # struct Chunk {} //! # let mut tree = Tree::::new(64); //! let needs_updating = tree.prepare_update( -//! &[OctVec::new(8, 8, 8, 8)], // the target positions to generate the lod around -//! 4, // amount of detail -//! |pos| Chunk {} // and the function to construct the chunk with +//! &[OctVec::new(8, 8, 8, 8)], // the target positions to generate the lod around +//! 4, // amount of detail +//! |pos| Chunk {} // and the function to construct the chunk with //! // NOTE: this is only called for completely new chunks, not the ones loaded from the chunk cache! //! ); //! ``` @@ -65,11 +65,11 @@ //! # let mut tree = Tree::::new(64); //! //! tree.get_chunks_to_add_slice_mut() -//! .iter_mut() // or par_iter_mut() if you're using rayon -//! .for_each(|to_add| { -//! // and run expensive init, probably does something with procedural generation -//! to_add.chunk.expensive_init(to_add.position); -//! }); +//! .iter_mut() // or par_iter_mut() if you're using rayon +//! .for_each(|to_add| { +//! // and run expensive init, probably does something with procedural generation +//! to_add.chunk.expensive_init(to_add.position); +//! }); //! ``` //! //! Next, we'll also want to change the visibility of some chunks so they don't overlap with higher detail lods. @@ -83,11 +83,11 @@ //! # let mut tree = Tree::::new(64); //! // and make all chunks visible or not //! for chunk in tree.iter_chunks_to_activate_mut() { -//! chunk.set_visible(true); +//! chunk.set_visible(true); //! } //! //! for chunk in tree.iter_chunks_to_deactivate_mut() { -//! chunk.set_visible(false); +//! chunk.set_visible(false); //! } //! ``` //! We'll probably also want to do some cleanup with chunks that are removed. @@ -101,7 +101,7 @@ //! # } //! # let mut tree = Tree::::new(64); //! for chunk in tree.iter_chunks_to_remove_mut() { -//! chunk.cleanup(); +//! chunk.cleanup(); //! } //! ``` //! And finally, actually update the tree with the new chunks. diff --git a/src/traits.rs b/src/traits.rs index dd8b28e..b5a1cce 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -89,10 +89,10 @@ pub trait LodVec: /// /// // then check if we are inside the AABB /// self.depth <= max_depth - /// && self_x >= min_x - /// && self_x < max_x - /// && self_y >= min_y - /// && self_y < max_y + /// && self_x >= min_x + /// && self_x < max_x + /// && self_y >= min_y + /// && self_y < max_y /// } /// } /// ``` diff --git a/src/tree.rs b/src/tree.rs index b118e63..396a25a 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -66,7 +66,7 @@ struct QueueContainer { // partially based on: https://stackoverflow.com/questions/41946007/efficient-and-well-explained-implementation-of-a-quadtree-for-2d-collision-det // assumption here is that because of the fact that we need to keep inactive chunks in memory for later use, we can keep them together with the actual nodes. #[derive(Clone, Debug)] -pub struct Tree { +pub struct Tree { /// All chunks in the tree pub(crate) chunks: Vec>, @@ -80,16 +80,16 @@ pub struct Tree { /// tuple of the parent index and the position. chunks_to_add_parent: Vec, - /// actual chunk to add + /// actual chunks to add during next update chunks_to_add: Vec>, /// chunk indices to be removed, tuple of index, parent index chunks_to_remove: Vec, - /// indices of the chunks that need to be activated + /// indices of the chunks that need to be activated (i.e. the chunks that have just lost children) chunks_to_activate: Vec, - /// indices of the chunks that need to be deactivated + /// indices of the chunks that need to be deactivated (i.e. chunks that have been subdivided in this iteration) chunks_to_deactivate: Vec, /// internal queue for processing, that way we won't need to reallocate it @@ -424,6 +424,98 @@ where &mut self.chunks_to_delete[..] } + /// Adds chunks at and around specified locations. + /// This opertion will also add chunks at other locations around the target to fullfill the + /// datastructure constraints (such that no partially filled nodes exist). + pub fn prepare_insert( + &mut self, + targets: &[L], + detail: u64, + chunk_creator: &dyn Fn(L) -> C, + ) -> bool { + // first, clear the previous arrays + self.chunks_to_add.clear(); + self.chunks_to_remove.clear(); + self.chunks_to_activate.clear(); + self.chunks_to_deactivate.clear(); + + // if we don't have a root, make one pending for creation + if self.nodes.is_empty() { + // chunk to add + let chunk_to_add = self.get_chunk_from_cache(L::root(), chunk_creator); + + // we need to add the root as pending + self.chunks_to_add.push(ToAddContainer { + position: L::root(), + chunk: chunk_to_add, + }); + + // and the parent + self.chunks_to_add_parent.push(0); + + // and an update is needed + return true; + } + + // clear the processing queue from any previous updates + self.processing_queue.clear(); + + // add the root node (always at 0, if there is no root we would have returned earlier) to the processing queue + self.processing_queue.push(QueueContainer { + position: L::root(), + node: 0, + }); + + // then, traverse the tree, as long as something is inside the queue + while let Some(QueueContainer { + position: current_position, + node: current_node_index, + }) = self.processing_queue.pop() + { + // fetch the current node + let current_node = self.nodes[current_node_index]; + + // if we can subdivide, and the current node does not have children, subdivide the current node + if current_node.children.is_none() { + // add children to be added + for i in 0..L::num_children() { + // chunk to add + let chunk_to_add = + self.get_chunk_from_cache(current_position.get_child(i), chunk_creator); + + // add the new chunk to be added + self.chunks_to_add.push(ToAddContainer { + position: current_position.get_child(i), + chunk: chunk_to_add, + }); + + // and add the parent + self.chunks_to_add_parent.push(current_node_index); + } + + // and add ourselves for deactivation + self.chunks_to_deactivate.push(current_node_index); + } else if let Some(index) = current_node.children { + // queue child nodes for processing + for i in 0..L::num_children() { + // wether we can subdivide + let child_pos = current_position.get_child(i); + let can_subdivide = targets.iter().any(|x| x.can_subdivide(child_pos, detail)); + if !can_subdivide { + continue; + } + self.processing_queue.push(QueueContainer { + position: child_pos, + node: index.get() + i, + }); + } + } + } + + // and return wether an update needs to be done + !self.chunks_to_add.is_empty() + } + // how it works: // each node contains a pointer to it's chunk data and first child // start from the root node, which is at 0 @@ -437,8 +529,9 @@ where // clear the free list once we only have one chunk (the root) active // swap remove chunks, and update the node that references them (nodes won't move due to free list) - /// prepares the tree for an update. - /// this fills the internal lists of what chunks need to be added or removed. + /// prepares the tree for an update, an update is an operation that + /// adds chunks around specified locations (targets) while also erasing all other chunks. + /// this fills the internal lists of what chunks need to be added or removed as appropriate. /// # Params /// * `targets` The target positions to generate the lod around (QuadVec and OctVec define the center position and max lod in depth for this) /// * `detail` The detail for these targets (QuadVec and OctVec define this as amount of chunks around this point) @@ -624,9 +717,10 @@ where break; } } - if self.cache_size >0 { + if self.cache_size > 0 { // then assign this chunk into the cache - if let Some(cached_chunk) = self.chunk_cache.insert(old_chunk.position, old_chunk.chunk) + if let Some(cached_chunk) = + self.chunk_cache.insert(old_chunk.position, old_chunk.chunk) { // there might have been another cached chunk self.chunks_to_delete.push(ToDeleteContainer { @@ -814,13 +908,12 @@ where // gets a chunk from the cache, otehrwise generates one from the given function #[inline] fn get_chunk_from_cache(&mut self, position: L, chunk_creator: &dyn Fn(L) -> C) -> C { - if self.cache_size >0 { - if let Some(chunk) = self.chunk_cache.remove(&position) - { - return chunk; - } - } - return chunk_creator(position); + if self.cache_size > 0 { + if let Some(chunk) = self.chunk_cache.remove(&position) { + return chunk; + } + } + chunk_creator(position) } } @@ -844,15 +937,64 @@ mod tests { struct TestChunk; #[test] - fn new_tree() { + fn update_tree() { // make a tree - // let mut tree = Tree::::new(64); - // - // // as long as we need to update, do so - // while tree.prepare_update(&[QuadVec::new(128, 128, 32)], 8, &TestChunk ) { - // // and actually update - // tree.do_update(); - // } + let mut tree = Tree::::new(64); + // as long as we need to update, do so + for tgt in [QuadVec::new(1, 1, 2), QuadVec::new(2, 3, 2)] { + dbg!(tgt); + while tree.prepare_update(&[tgt], 0, &|_| TestChunk {}) { + for c in tree.iter_chunks_to_activate_positions() { + println!("* {c:?}"); + } + for c in tree.iter_chunks_to_deactivate_positions() { + println!("o {c:?}"); + } + + for c in tree.iter_chunks_to_remove_positions() { + println!("- {c:?}"); + } + + for c in tree.iter_chunks_to_add_positions() { + println!("+ {c:?}"); + } + println!("updating..."); + // and actually update + tree.do_update(); + } + } + } + #[test] + fn insert_into_tree() { + // make a tree + let mut tree = Tree::::new(64); + // as long as we need to update, do so + for tgt in [QuadVec::new(1, 1, 2), QuadVec::new(2, 3, 2)] { + dbg!(tgt); + while tree.prepare_insert(&[tgt], 0, &|_| TestChunk {}) { + for c in tree.iter_chunks_to_activate_positions() { + println!("* {c:?}"); + } + for c in tree.iter_chunks_to_deactivate_positions() { + println!("o {c:?}"); + } + + for c in tree.iter_chunks_to_remove_positions() { + println!("- {c:?}"); + } + + for c in tree.iter_chunks_to_add_positions() { + println!("+ {c:?}"); + } + println!("updating..."); + // and actually update + tree.do_update(); + } + } + } + + #[test] + pub fn things() { // // // and move the target // while tree.prepare_update(&[QuadVec::new(16, 8, 16)], 8, |_| TestChunk {}) { From 6ac39062b060bb9f513ac0dc35ef10d179964a36 Mon Sep 17 00:00:00 2001 From: Alex Pyattaev Date: Fri, 3 Mar 2023 11:42:45 +0200 Subject: [PATCH 10/15] reduce memory overhead by 30 percent --- src/coords.rs | 14 ++++--- src/iter.rs | 18 ++++----- src/lib.rs | 2 +- src/traits.rs | 6 +-- src/tree.rs | 100 ++++++++++++++++++++++++++------------------------ 5 files changed, 73 insertions(+), 67 deletions(-) diff --git a/src/coords.rs b/src/coords.rs index 1678731..b1b7454 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -90,7 +90,7 @@ impl QuadVec { impl LodVec for QuadVec { #[inline] - fn get_child(self, index: usize) -> Self { + fn get_child(self, index: u32) -> Self { debug_assert!(index < 4); // the positions, doubled in scale let x = self.x << 1; @@ -109,7 +109,7 @@ impl LodVec for QuadVec { } #[inline] - fn num_children() -> usize { + fn num_children() -> u32 { 4 } @@ -123,7 +123,8 @@ impl LodVec for QuadVec { } #[inline] - fn can_subdivide(self, node: Self, detail: u64) -> bool { + fn can_subdivide(self, node: Self, detail: u32) -> bool { + let detail = detail as u64; // return early if the level of this chunk is too high if node.depth >= self.depth { return false; @@ -288,7 +289,7 @@ impl OctVec { impl LodVec for OctVec { #[inline] - fn get_child(self, index: usize) -> Self { + fn get_child(self, index: u32) -> Self { debug_assert!(index < 8); // the positions, doubled in scale let x = self.x << 1; @@ -310,7 +311,7 @@ impl LodVec for OctVec { } #[inline] - fn num_children() -> usize { + fn num_children() -> u32 { 8 } @@ -325,7 +326,8 @@ impl LodVec for OctVec { } #[inline] - fn can_subdivide(self, node: Self, detail: u64) -> bool { + fn can_subdivide(self, node: Self, detail: u32) -> bool { + let detail = detail as u64; // return early if the level of this chunk is too high if node.depth >= self.depth { return false; diff --git a/src/iter.rs b/src/iter.rs index 344626b..a066764 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -472,7 +472,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndMaybeTreeIter<'a, C, if let Some(children) = node.children { // children, so node self.stack - .push((position, Some(self.tree.nodes[children.get() + i]))); + .push((position, Some(self.tree.nodes[(children.get() + i) as usize]))); } else { // no node, so no chunk self.stack.push((position, None)); @@ -486,7 +486,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndMaybeTreeIter<'a, C, // and return this item from the stack if let Some(node) = current_node { // there is a node, so get the chunk it has - let chunk = &self.tree.chunks[node.chunk].chunk; + let chunk = &self.tree.chunks[node.chunk as usize].chunk; // and return it Some((current_position, Some(chunk))) @@ -531,7 +531,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndTreeIter<'a, C, L> { if position.is_inside_bounds(self.bound_min, self.bound_max, self.max_depth) { // and push to the stack self.stack - .push((position, self.tree.nodes[children.get() + i])); + .push((position, self.tree.nodes[(children.get() + i) as usize])); } } } @@ -539,7 +539,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndTreeIter<'a, C, L> { // and return the position and node Some(( current_position, - &self.tree.chunks[current_node.chunk].chunk, + &self.tree.chunks[current_node.chunk as usize].chunk, )) } } @@ -580,7 +580,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndMaybeTreeIterMut<'a, if let Some(children) = node.children { // children, so node self.stack - .push((position, Some(self.tree.nodes[children.get() + i]))); + .push((position, Some(self.tree.nodes[(children.get() + i) as usize]))); } else { // no node, so no chunk self.stack.push((position, None)); @@ -594,7 +594,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndMaybeTreeIterMut<'a, // and return this item from the stack if let Some(node) = current_node { // there is a node, so get the chunk it has - let chunk = &mut self.tree.chunks[node.chunk].chunk as *mut C; + let chunk = &mut self.tree.chunks[node.chunk as usize].chunk as *mut C; // and return it // Safety: The iterator lives at least as long as the tree, and no changes can be made to the tree while it's borrowed by the iterator @@ -640,7 +640,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndTreeIterMut<'a, C, L> if position.is_inside_bounds(self.bound_min, self.bound_max, self.max_depth) { // and push to the stack self.stack - .push((position, self.tree.nodes[children.get() + i])); + .push((position, self.tree.nodes[(children.get() + i)as usize])); } } } @@ -648,7 +648,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndTreeIterMut<'a, C, L> // and return the position and node // Safety: The iterator lives at least as long as the tree, and no changes can be made to the tree while it's borrowed by the iterator Some((current_position, unsafe { - (&mut self.tree.chunks[current_node.chunk].chunk as *mut C).as_mut()? + (&mut self.tree.chunks[current_node.chunk as usize].chunk as *mut C).as_mut()? })) } } @@ -899,7 +899,7 @@ mod tests { let mut tree = Tree::new(65); let qv = OctVec::new(R, R, R, D); - while tree.prepare_update(&[qv], R, &chunk_creator) { + while tree.prepare_update(&[qv], R as u32, &chunk_creator) { // do the update tree.do_update(); // and clean diff --git a/src/lib.rs b/src/lib.rs index 8cd375f..9bffd8e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ //! let needs_updating = tree.prepare_update( //! &[OctVec::new(8, 8, 8, 8)], // the target positions to generate the lod around //! 4, // amount of detail -//! |pos| Chunk {} // and the function to construct the chunk with +//! &|pos| Chunk {} // and the function to construct the chunk with //! // NOTE: this is only called for completely new chunks, not the ones loaded from the chunk cache! //! ); //! ``` diff --git a/src/traits.rs b/src/traits.rs index b5a1cce..efbfbcd 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -7,10 +7,10 @@ pub trait LodVec: std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + Default + PartialOrd { /// gets one of the child node position of this node, defined by it's index. - fn get_child(self, index: usize) -> Self; + fn get_child(self, index: u32) -> Self; /// get the number of child nodes a node can have in the tree. - fn num_children() -> usize; + fn num_children() -> u32; /// returns the lod vector as if it's at the root of the tree. fn root() -> Self; @@ -60,7 +60,7 @@ pub trait LodVec: /// } /// # } /// ``` - fn can_subdivide(self, node: Self, detail: u64) -> bool; + fn can_subdivide(self, node: Self, detail: u32) -> bool; /// check if this chunk is inside of a bounding box /// where min is the lowest corner of the box, and max is the highest corner diff --git a/src/tree.rs b/src/tree.rs index 396a25a..2a75b84 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -4,7 +4,7 @@ use crate::traits::*; use std::collections::{HashMap, VecDeque}; use std::fmt::Debug; -use std::num::NonZeroUsize; +use std::num::NonZeroU32; // struct for keeping track of chunks // keeps track of the parent and child indices @@ -12,18 +12,18 @@ use std::num::NonZeroUsize; pub(crate) struct TreeNode { // children, these can't be the root (index 0), so we can use Some and Nonzero for slightly more compact memory // children are also contiguous, so we can assume that this to this + num children - 1 are all the children of this node - pub(crate) children: Option, + pub(crate) children: Option, // where the chunk for this node is stored - pub(crate) chunk: usize, + pub(crate) chunk: u32, //TODO change this to Option such that nodes with no chunks can be created } // utility struct for holding actual chunks and the node that owns them #[derive(Clone, Debug)] -pub struct ChunkContainer { - pub chunk: C, - pub index: usize, - pub position: L, +pub(crate) struct ChunkContainer { + pub(crate) chunk: C, // actual data inside the chunk + pub(crate) index: u32, // index of the node that holds this chunk + pub(crate) position: L, // where the chunk is (as this can not be recovered from node tree) } /// holds a chunk to add and it's position @@ -41,8 +41,8 @@ pub struct ToAddContainer { // utility struct for holding chunks to remove #[derive(Clone, Debug)] struct ToRemoveContainer { - chunk: usize, // chunk index - parent: usize, // parent index + chunk: u32, // chunk index + parent: u32, // parent index } /// holds a chunk that's going to be deleted and it's position @@ -58,7 +58,7 @@ pub struct ToDeleteContainer { // utility struct for holding chunks in the queue #[derive(Clone, Debug)] struct QueueContainer { - node: usize, // chunk index + node: u32, // chunk index position: L, // and it's position } @@ -66,7 +66,7 @@ struct QueueContainer { // partially based on: https://stackoverflow.com/questions/41946007/efficient-and-well-explained-implementation-of-a-quadtree-for-2d-collision-det // assumption here is that because of the fact that we need to keep inactive chunks in memory for later use, we can keep them together with the actual nodes. #[derive(Clone, Debug)] -pub struct Tree { +pub struct Tree { /// All chunks in the tree pub(crate) chunks: Vec>, @@ -74,11 +74,11 @@ pub struct Tree { pub(crate) nodes: Vec, /// list of free nodes in the Tree, to allocate new nodes into - free_list: VecDeque, + free_list: VecDeque, /// parent chunk indices of the chunks to be added. /// tuple of the parent index and the position. - chunks_to_add_parent: Vec, + chunks_to_add_parent: Vec, /// actual chunks to add during next update chunks_to_add: Vec>, @@ -87,10 +87,10 @@ pub struct Tree { chunks_to_remove: Vec, /// indices of the chunks that need to be activated (i.e. the chunks that have just lost children) - chunks_to_activate: Vec, + chunks_to_activate: Vec, /// indices of the chunks that need to be deactivated (i.e. chunks that have been subdivided in this iteration) - chunks_to_deactivate: Vec, + chunks_to_deactivate: Vec, /// internal queue for processing, that way we won't need to reallocate it processing_queue: Vec>, @@ -126,7 +126,7 @@ where loop { // if the current node is the one we are looking for, return if current_position == position { - return Some(current.chunk); + return Some(current.chunk as usize); } // if the current node does not have children, stop @@ -142,7 +142,7 @@ where current_position = found_position; // and the node is at the index of the child nodes + index - current = self.nodes[current.children.unwrap().get() + index]; + current = self.nodes[(current.children.unwrap().get() + index ) as usize]; } else { // if no child got found that matched the item, return none return None; @@ -254,13 +254,13 @@ where /// get a chunk pending activation #[inline] pub fn get_chunk_to_activate(&self, index: usize) -> &C { - &self.chunks[self.nodes[self.chunks_to_activate[index]].chunk].chunk + &self.chunks[self.nodes[self.chunks_to_activate[index] as usize].chunk as usize].chunk } /// get a mutable chunk pending activation #[inline] pub fn get_chunk_to_activate_mut(&mut self, index: usize) -> &mut C { - &mut self.chunks[self.nodes[self.chunks_to_activate[index]].chunk].chunk + &mut self.chunks[self.nodes[self.chunks_to_activate[index] as usize].chunk as usize].chunk } /// gets a mutable pointer to a chunk that is pending activation @@ -273,7 +273,7 @@ where /// get the position of a chunk pending activation #[inline] pub fn get_position_of_chunk_to_activate(&self, index: usize) -> L { - self.chunks[self.nodes[self.chunks_to_activate[index]].chunk].position + self.chunks[self.nodes[self.chunks_to_activate[index]as usize].chunk as usize].position } /// get the number of chunks pending deactivation @@ -285,13 +285,13 @@ where /// get a chunk pending deactivation #[inline] pub fn get_chunk_to_deactivate(&self, index: usize) -> &C { - &self.chunks[self.nodes[self.chunks_to_deactivate[index]].chunk].chunk + &self.chunks[self.nodes[self.chunks_to_deactivate[index]as usize].chunk as usize].chunk } /// get a mutable chunk pending deactivation #[inline] pub fn get_chunk_to_deactivate_mut(&mut self, index: usize) -> &mut C { - &mut self.chunks[self.nodes[self.chunks_to_deactivate[index]].chunk].chunk + &mut self.chunks[self.nodes[self.chunks_to_deactivate[index] as usize ].chunk as usize].chunk } /// gets a mutable pointer to a chunk that is pending deactivation @@ -304,7 +304,7 @@ where /// get the position of a chunk pending deactivation #[inline] pub fn get_position_of_chunk_to_deactivate(&self, index: usize) -> L { - self.chunks[self.nodes[self.chunks_to_deactivate[index]].chunk].position + self.chunks[self.nodes[self.chunks_to_deactivate[index]as usize].chunk as usize].position } /// get the number of chunks pending removal @@ -316,13 +316,13 @@ where /// get a chunk pending removal #[inline] pub fn get_chunk_to_remove(&self, index: usize) -> &C { - &self.chunks[self.nodes[self.chunks_to_remove[index].chunk].chunk].chunk + &self.chunks[self.nodes[self.chunks_to_remove[index].chunk as usize ].chunk as usize].chunk } /// get a mutable chunk pending removal #[inline] pub fn get_chunk_to_remove_mut(&mut self, index: usize) -> &mut C { - &mut self.chunks[self.nodes[self.chunks_to_remove[index].chunk].chunk].chunk + &mut self.chunks[self.nodes[self.chunks_to_remove[index].chunk as usize ].chunk as usize].chunk } /// gets a mutable pointer to a chunk that is pending removal @@ -335,7 +335,7 @@ where /// get the position of a chunk pending removal #[inline] pub fn get_position_of_chunk_to_remove(&self, index: usize) -> L { - self.chunks[self.nodes[self.chunks_to_remove[index].chunk].chunk].position + self.chunks[self.nodes[self.chunks_to_remove[index].chunk as usize ].chunk as usize].position } /// get the number of chunks to be added @@ -430,7 +430,7 @@ where pub fn prepare_insert( &mut self, targets: &[L], - detail: u64, + detail: u32, chunk_creator: &dyn Fn(L) -> C, ) -> bool { // first, clear the previous arrays @@ -473,7 +473,7 @@ where }) = self.processing_queue.pop() { // fetch the current node - let current_node = self.nodes[current_node_index]; + let current_node = self.nodes[current_node_index as usize]; // if we can subdivide, and the current node does not have children, subdivide the current node if current_node.children.is_none() { @@ -540,7 +540,7 @@ where pub fn prepare_update( &mut self, targets: &[L], - detail: u64, + detail: u32, chunk_creator: &dyn Fn(L) -> C, ) -> bool { // first, clear the previous arrays @@ -583,7 +583,7 @@ where }) = self.processing_queue.pop() { // fetch the current node - let current_node = self.nodes[current_node_index]; + let current_node = self.nodes[current_node_index as usize]; // wether we can subdivide let can_subdivide = targets @@ -615,7 +615,7 @@ where if !can_subdivide && !(0..L::num_children()) .into_iter() - .any(|i| self.nodes[i + index.get()].children.is_some()) + .any(|i| self.nodes[(i + index.get()) as usize].children.is_some()) { // first, queue ourselves for activation self.chunks_to_activate.push(current_node_index); @@ -623,7 +623,7 @@ where for i in 0..L::num_children() { // no need to do this in reverse, that way the last node removed will be added to the free list, which is also the first thing used by the adding logic self.chunks_to_remove.push(ToRemoveContainer { - chunk: index.get() + i, + chunk: index.get() + i , parent: current_node_index, }); } @@ -666,11 +666,11 @@ where // but we do need to cache these { // remove the node from the tree - self.nodes[parent_index].children = None; + self.nodes[parent_index as usize].children = None; self.free_list.push_back(index); // and remove the chunk - let chunk_index = self.nodes[index].chunk; + let chunk_index = self.nodes[index as usize].chunk; // but not so fast, because if we can overwrite it with a new chunk, do so // that way we can avoid a copy later on, which might be expensive @@ -681,7 +681,7 @@ where let new_node_index = match self.free_list.pop_front() { Some(x) => { // reuse a free node - self.nodes[x] = TreeNode { + self.nodes[x as usize] = TreeNode { children: None, chunk: chunk_index, }; @@ -694,7 +694,7 @@ where position, }; - std::mem::swap(&mut old_chunk, &mut self.chunks[chunk_index]); + std::mem::swap(&mut old_chunk, &mut self.chunks[chunk_index as usize]); // old chunk shouldn't be mutable anymore let old_chunk = old_chunk; @@ -745,12 +745,12 @@ where if new_node_index >= L::num_children() { // because we loop in order, and our nodes are contiguous, the first node of the children got added on index i - (num children - 1) // so we need to adjust for that - self.nodes[parent_index].children = - NonZeroUsize::new(new_node_index - (L::num_children() - 1)); + self.nodes[parent_index as usize].children = + NonZeroU32::new(new_node_index - (L::num_children() - 1)); } } else { // otherwise we do need to do a regular swap remove - let old_chunk = self.chunks.swap_remove(chunk_index); + let old_chunk = self.chunks.swap_remove(chunk_index as usize); // now, we can try to add this chunk into the cache // first, remove any extra nodes if they are in the cache @@ -787,8 +787,8 @@ where // and properly set the chunk pointer of the node of the chunk we just moved, if any // if we removed the last chunk, no need to update anything - if chunk_index < self.chunks.len() { - self.nodes[self.chunks[chunk_index].index].chunk = chunk_index; + if chunk_index < self.chunks.len() as u32 { + self.nodes[self.chunks[chunk_index as usize].index as usize].chunk = chunk_index; } } @@ -799,9 +799,9 @@ where let new_node_index = match self.free_list.pop_front() { Some(x) => { // reuse a free node - self.nodes[x] = TreeNode { + self.nodes[x as usize] = TreeNode { children: None, - chunk: self.chunks.len(), + chunk: self.chunks.len() as u32, }; self.chunks.push(ChunkContainer { index: x, @@ -814,14 +814,14 @@ where // otherwise, use a new index self.nodes.push(TreeNode { children: None, - chunk: self.chunks.len(), + chunk: self.chunks.len() as u32, }); self.chunks.push(ChunkContainer { - index: self.nodes.len() - 1, + index: self.nodes.len() as u32 - 1 , chunk, position, }); - self.nodes.len() - 1 + (self.nodes.len() - 1) as u32 } }; @@ -832,8 +832,8 @@ where if new_node_index >= L::num_children() { // because we loop in order, and our nodes are contiguous, the first node of the children got added on index i - (num children - 1) // so we need to adjust for that - self.nodes[parent_index].children = - NonZeroUsize::new(new_node_index - (L::num_children() - 1)); + self.nodes[parent_index as usize].children = + NonZeroU32::new(new_node_index - (L::num_children() - 1)); } } @@ -1035,4 +1035,8 @@ mod tests { // tree.do_update(); // } } + #[test] + pub fn alignment(){ + assert_eq!(std::mem::size_of::(), 8); + } } From e92836cd8a9f7ea6e9172f376f01365f7d64e8e0 Mon Sep 17 00:00:00 2001 From: Alex Pyattaev Date: Fri, 3 Mar 2023 11:44:34 +0200 Subject: [PATCH 11/15] format --- src/coords.rs | 4 ++-- src/iter.rs | 14 +++++++++----- src/tree.rs | 43 ++++++++++++++++++++++--------------------- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/coords.rs b/src/coords.rs index b1b7454..aaeb59f 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -176,7 +176,7 @@ impl LodVec for QuadVec { let max_y = max.y >> max_difference; // dbg!(min_x, min_y, max_x, max_y); // then check if we are inside the AABB - self.depth <= max_depth + self.depth <= max_depth && self_x >= min_x && self_x <= max_x && self_y >= min_y @@ -392,7 +392,7 @@ impl LodVec for OctVec { let max_z = max.z >> max_difference; // then check if we are inside the AABB - self.depth <= max_depth + self.depth <= max_depth && self_x >= min_x && self_x <= max_x && self_y >= min_y diff --git a/src/iter.rs b/src/iter.rs index a066764..f0c493c 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -471,8 +471,10 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndMaybeTreeIter<'a, C, // and if it has children if let Some(children) = node.children { // children, so node - self.stack - .push((position, Some(self.tree.nodes[(children.get() + i) as usize]))); + self.stack.push(( + position, + Some(self.tree.nodes[(children.get() + i) as usize]), + )); } else { // no node, so no chunk self.stack.push((position, None)); @@ -579,8 +581,10 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndMaybeTreeIterMut<'a, // and if it has children if let Some(children) = node.children { // children, so node - self.stack - .push((position, Some(self.tree.nodes[(children.get() + i) as usize]))); + self.stack.push(( + position, + Some(self.tree.nodes[(children.get() + i) as usize]), + )); } else { // no node, so no chunk self.stack.push((position, None)); @@ -640,7 +644,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndTreeIterMut<'a, C, L> if position.is_inside_bounds(self.bound_min, self.bound_max, self.max_depth) { // and push to the stack self.stack - .push((position, self.tree.nodes[(children.get() + i)as usize])); + .push((position, self.tree.nodes[(children.get() + i) as usize])); } } } diff --git a/src/tree.rs b/src/tree.rs index 2a75b84..96b84a9 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -21,8 +21,8 @@ pub(crate) struct TreeNode { // utility struct for holding actual chunks and the node that owns them #[derive(Clone, Debug)] pub(crate) struct ChunkContainer { - pub(crate) chunk: C, // actual data inside the chunk - pub(crate) index: u32, // index of the node that holds this chunk + pub(crate) chunk: C, // actual data inside the chunk + pub(crate) index: u32, // index of the node that holds this chunk pub(crate) position: L, // where the chunk is (as this can not be recovered from node tree) } @@ -58,7 +58,7 @@ pub struct ToDeleteContainer { // utility struct for holding chunks in the queue #[derive(Clone, Debug)] struct QueueContainer { - node: u32, // chunk index + node: u32, // chunk index position: L, // and it's position } @@ -142,7 +142,7 @@ where current_position = found_position; // and the node is at the index of the child nodes + index - current = self.nodes[(current.children.unwrap().get() + index ) as usize]; + current = self.nodes[(current.children.unwrap().get() + index) as usize]; } else { // if no child got found that matched the item, return none return None; @@ -273,7 +273,7 @@ where /// get the position of a chunk pending activation #[inline] pub fn get_position_of_chunk_to_activate(&self, index: usize) -> L { - self.chunks[self.nodes[self.chunks_to_activate[index]as usize].chunk as usize].position + self.chunks[self.nodes[self.chunks_to_activate[index] as usize].chunk as usize].position } /// get the number of chunks pending deactivation @@ -285,13 +285,13 @@ where /// get a chunk pending deactivation #[inline] pub fn get_chunk_to_deactivate(&self, index: usize) -> &C { - &self.chunks[self.nodes[self.chunks_to_deactivate[index]as usize].chunk as usize].chunk + &self.chunks[self.nodes[self.chunks_to_deactivate[index] as usize].chunk as usize].chunk } /// get a mutable chunk pending deactivation #[inline] pub fn get_chunk_to_deactivate_mut(&mut self, index: usize) -> &mut C { - &mut self.chunks[self.nodes[self.chunks_to_deactivate[index] as usize ].chunk as usize].chunk + &mut self.chunks[self.nodes[self.chunks_to_deactivate[index] as usize].chunk as usize].chunk } /// gets a mutable pointer to a chunk that is pending deactivation @@ -304,7 +304,7 @@ where /// get the position of a chunk pending deactivation #[inline] pub fn get_position_of_chunk_to_deactivate(&self, index: usize) -> L { - self.chunks[self.nodes[self.chunks_to_deactivate[index]as usize].chunk as usize].position + self.chunks[self.nodes[self.chunks_to_deactivate[index] as usize].chunk as usize].position } /// get the number of chunks pending removal @@ -316,13 +316,14 @@ where /// get a chunk pending removal #[inline] pub fn get_chunk_to_remove(&self, index: usize) -> &C { - &self.chunks[self.nodes[self.chunks_to_remove[index].chunk as usize ].chunk as usize].chunk + &self.chunks[self.nodes[self.chunks_to_remove[index].chunk as usize].chunk as usize].chunk } /// get a mutable chunk pending removal #[inline] pub fn get_chunk_to_remove_mut(&mut self, index: usize) -> &mut C { - &mut self.chunks[self.nodes[self.chunks_to_remove[index].chunk as usize ].chunk as usize].chunk + &mut self.chunks[self.nodes[self.chunks_to_remove[index].chunk as usize].chunk as usize] + .chunk } /// gets a mutable pointer to a chunk that is pending removal @@ -335,7 +336,7 @@ where /// get the position of a chunk pending removal #[inline] pub fn get_position_of_chunk_to_remove(&self, index: usize) -> L { - self.chunks[self.nodes[self.chunks_to_remove[index].chunk as usize ].chunk as usize].position + self.chunks[self.nodes[self.chunks_to_remove[index].chunk as usize].chunk as usize].position } /// get the number of chunks to be added @@ -583,7 +584,7 @@ where }) = self.processing_queue.pop() { // fetch the current node - let current_node = self.nodes[current_node_index as usize]; + let current_node = self.nodes[current_node_index as usize]; // wether we can subdivide let can_subdivide = targets @@ -623,7 +624,7 @@ where for i in 0..L::num_children() { // no need to do this in reverse, that way the last node removed will be added to the free list, which is also the first thing used by the adding logic self.chunks_to_remove.push(ToRemoveContainer { - chunk: index.get() + i , + chunk: index.get() + i, parent: current_node_index, }); } @@ -666,11 +667,11 @@ where // but we do need to cache these { // remove the node from the tree - self.nodes[parent_index as usize].children = None; + self.nodes[parent_index as usize].children = None; self.free_list.push_back(index); // and remove the chunk - let chunk_index = self.nodes[index as usize].chunk; + let chunk_index = self.nodes[index as usize].chunk; // but not so fast, because if we can overwrite it with a new chunk, do so // that way we can avoid a copy later on, which might be expensive @@ -681,7 +682,7 @@ where let new_node_index = match self.free_list.pop_front() { Some(x) => { // reuse a free node - self.nodes[x as usize] = TreeNode { + self.nodes[x as usize] = TreeNode { children: None, chunk: chunk_index, }; @@ -694,7 +695,7 @@ where position, }; - std::mem::swap(&mut old_chunk, &mut self.chunks[chunk_index as usize]); + std::mem::swap(&mut old_chunk, &mut self.chunks[chunk_index as usize]); // old chunk shouldn't be mutable anymore let old_chunk = old_chunk; @@ -745,12 +746,12 @@ where if new_node_index >= L::num_children() { // because we loop in order, and our nodes are contiguous, the first node of the children got added on index i - (num children - 1) // so we need to adjust for that - self.nodes[parent_index as usize].children = + self.nodes[parent_index as usize].children = NonZeroU32::new(new_node_index - (L::num_children() - 1)); } } else { // otherwise we do need to do a regular swap remove - let old_chunk = self.chunks.swap_remove(chunk_index as usize); + let old_chunk = self.chunks.swap_remove(chunk_index as usize); // now, we can try to add this chunk into the cache // first, remove any extra nodes if they are in the cache @@ -817,7 +818,7 @@ where chunk: self.chunks.len() as u32, }); self.chunks.push(ChunkContainer { - index: self.nodes.len() as u32 - 1 , + index: self.nodes.len() as u32 - 1, chunk, position, }); @@ -1036,7 +1037,7 @@ mod tests { // } } #[test] - pub fn alignment(){ + pub fn alignment() { assert_eq!(std::mem::size_of::(), 8); } } From 95ea1334174b61ed5cbbb0cd06501d228cc022fb Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Fri, 3 Mar 2023 19:04:12 +0200 Subject: [PATCH 12/15] Fixed prepare_insert --- Cargo.toml | 5 +-- rust-toolchain.toml | 3 ++ src/coords.rs | 10 ++---- src/iter.rs | 10 +++--- src/lib.rs | 1 + src/traits.rs | 7 ++-- src/tree.rs | 85 +++++++++++++++++++++++++++++++++------------ 7 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/Cargo.toml b/Cargo.toml index 376a102..5609b7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,15 @@ [package] name = "lodtree" description = "A simple crate to help create octrees and quadtrees for chunked level of detail" -version = "0.1.4" -edition = "2018" +version = "0.1.5" +edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/Dimev/lodtree" documentation = "https://docs.rs/lodtree" keywords = ["lod", "terrain", "octree", "quadtree", "tree"] categories = ["data-structures"] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..0542f3e --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +#channel = "nightly" +channel = "stable" \ No newline at end of file diff --git a/src/coords.rs b/src/coords.rs index aaeb59f..692b9ca 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -108,10 +108,7 @@ impl LodVec for QuadVec { } } - #[inline] - fn num_children() -> u32 { - 4 - } + const NUM_CHILDREN: u32 = 4; #[inline] fn root() -> Self { @@ -310,10 +307,7 @@ impl LodVec for OctVec { } } - #[inline] - fn num_children() -> u32 { - 8 - } + const NUM_CHILDREN: u32 = 8; #[inline] fn root() -> Self { diff --git a/src/iter.rs b/src/iter.rs index f0c493c..0b54ec2 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -423,7 +423,7 @@ impl Iterator for ChunksInBoundIter { let current = self.stack.pop()?; // go over all child nodes - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { let position = current.get_child(i); // if they are in bounds, and the correct depth, add them to the stack @@ -461,7 +461,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndMaybeTreeIter<'a, C, let (current_position, current_node) = self.stack.pop()?; // go over all child nodes - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { let position = current_position.get_child(i); // if they are in bounds, and the correct depth, add them to the stack @@ -524,7 +524,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndTreeIter<'a, C, L> { let (current_position, current_node) = self.stack.pop()?; // go over all child nodes - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { let position = current_position.get_child(i); // if the node has children @@ -571,7 +571,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndMaybeTreeIterMut<'a, let (current_position, current_node) = self.stack.pop()?; // go over all child nodes - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { let position = current_position.get_child(i); // if they are in bounds, and the correct depth, add them to the stack @@ -635,7 +635,7 @@ impl<'a, C: Sized, L: LodVec> Iterator for ChunksInBoundAndTreeIterMut<'a, C, L> let (current_position, current_node) = self.stack.pop()?; // go over all child nodes - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { let position = current_position.get_child(i); // if the node has children diff --git a/src/lib.rs b/src/lib.rs index 9bffd8e..2b7abf5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +//#![feature(generic_const_exprs)] //! # LodTree //! LodTree, a simple tree data structure for doing chunk-based level of detail. //! diff --git a/src/traits.rs b/src/traits.rs index efbfbcd..777c8fa 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -4,13 +4,12 @@ /// trait for defining a Level of Detail vector. /// such a vector contains the current position in the octree (3d coords), as well as the lod level it's at, in integer coords. pub trait LodVec: - std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + Default + PartialOrd + std::hash::Hash + Eq + Sized + Copy + Clone + Send + Sync + Default + PartialOrd + std::fmt::Debug { /// gets one of the child node position of this node, defined by it's index. fn get_child(self, index: u32) -> Self; - - /// get the number of child nodes a node can have in the tree. - fn num_children() -> u32; + /// the number of child nodes a node can have in the tree. + const NUM_CHILDREN: u32; /// returns the lod vector as if it's at the root of the tree. fn root() -> Self; diff --git a/src/tree.rs b/src/tree.rs index 96b84a9..15ee175 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -18,6 +18,21 @@ pub(crate) struct TreeNode { pub(crate) chunk: u32, //TODO change this to Option such that nodes with no chunks can be created } +//TODO: a better treenode structure +// Tree node that encompasses 4/8 children at once. Each child has two pointers: +// children[i] will point to the TreeNode which encompasses its children +// chunk[i] will point to the data chunk. +// both pointers may be "None", indicating either no children, or no data +//#[derive(Clone, Debug)] +// pub(crate) struct TreeNode2 where [(); L::NUM_CHILDREN as usize]: { +// // children, these can't be the root (index 0), so we can use Some and Nonzero for slightly more compact memory +// // children are also contiguous, so we can assume that this to this + num children - 1 are all the children of this node +// pub(crate) children: [Option; L::NUM_CHILDREN as usize], +// +// // where the chunk for this node is stored +// pub(crate) chunk: [Option; L::NUM_CHILDREN as usize], +// } + // utility struct for holding actual chunks and the node that owns them #[derive(Clone, Debug)] pub(crate) struct ChunkContainer { @@ -134,7 +149,7 @@ where current.children?; // if not, go over the node children - if let Some((index, found_position)) = (0..L::num_children()) + if let Some((index, found_position)) = (0..L::NUM_CHILDREN) .map(|i| (i, current_position.get_child(i))) .find(|(_, x)| x.contains_child_node(position)) { @@ -426,7 +441,7 @@ where } /// Adds chunks at and around specified locations. - /// This opertion will also add chunks at other locations around the target to fullfill the + /// This operation will also add chunks at other locations around the target to fullfill the /// datastructure constraints (such that no partially filled nodes exist). pub fn prepare_insert( &mut self, @@ -434,8 +449,12 @@ where detail: u32, chunk_creator: &dyn Fn(L) -> C, ) -> bool { + //FIXME: this function currently will dry-run once for every update to make sure + // there is nothing left to update. This is a waste of CPU time, especially for many targets + // first, clear the previous arrays self.chunks_to_add.clear(); + self.chunks_to_add_parent.clear(); self.chunks_to_remove.clear(); self.chunks_to_activate.clear(); self.chunks_to_deactivate.clear(); @@ -475,11 +494,12 @@ where { // fetch the current node let current_node = self.nodes[current_node_index as usize]; - + //dbg!(current_node_index, current_node); // if we can subdivide, and the current node does not have children, subdivide the current node if current_node.children.is_none() { + //println!("adding children"); // add children to be added - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { // chunk to add let chunk_to_add = self.get_chunk_from_cache(current_position.get_child(i), chunk_creator); @@ -497,23 +517,32 @@ where // and add ourselves for deactivation self.chunks_to_deactivate.push(current_node_index); } else if let Some(index) = current_node.children { + //println!("has children at {index:?}"); // queue child nodes for processing - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { // wether we can subdivide let child_pos = current_position.get_child(i); - let can_subdivide = targets.iter().any(|x| x.can_subdivide(child_pos, detail)); - if !can_subdivide { - continue; + //dbg!(child_pos); + for t in targets { + if *t == child_pos { + //println!("Found match for target {t:?}"); + self.chunks[(index.get() + i) as usize].chunk = + chunk_creator(child_pos); + continue; + } + if t.can_subdivide(child_pos, detail) { + self.processing_queue.push(QueueContainer { + position: child_pos, + node: index.get() + i, + }); + break; + } } - self.processing_queue.push(QueueContainer { - position: child_pos, - node: index.get() + i, - }); } } } - // and return wether an update needs to be done + // and return whether an update needs to be done !self.chunks_to_add.is_empty() } @@ -544,8 +573,12 @@ where detail: u32, chunk_creator: &dyn Fn(L) -> C, ) -> bool { + //FIXME: this function currently will dry-run once for every update to make sure + // there is nothing left to update. This is a waste of CPU time, especially for many targets + // first, clear the previous arrays self.chunks_to_add.clear(); + self.chunks_to_add_parent.clear(); self.chunks_to_remove.clear(); self.chunks_to_activate.clear(); self.chunks_to_deactivate.clear(); @@ -594,7 +627,7 @@ where // if we can subdivide, and the current node does not have children, subdivide the current node if can_subdivide && current_node.children.is_none() { // add children to be added - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { // chunk to add let chunk_to_add = self.get_chunk_from_cache(current_position.get_child(i), chunk_creator); @@ -614,14 +647,14 @@ where } else if let Some(index) = current_node.children { // otherwise, if we cant subdivide and have children, remove our children if !can_subdivide - && !(0..L::num_children()) + && !(0..L::NUM_CHILDREN) .into_iter() .any(|i| self.nodes[(i + index.get()) as usize].children.is_some()) { // first, queue ourselves for activation self.chunks_to_activate.push(current_node_index); - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { // no need to do this in reverse, that way the last node removed will be added to the free list, which is also the first thing used by the adding logic self.chunks_to_remove.push(ToRemoveContainer { chunk: index.get() + i, @@ -630,7 +663,7 @@ where } } else { // queue child nodes for processing if we didn't subdivide or clean up our children - for i in 0..L::num_children() { + for i in 0..L::NUM_CHILDREN { self.processing_queue.push(QueueContainer { position: current_position.get_child(i), node: index.get() + i, @@ -743,11 +776,11 @@ where // because the last node we come by in with ordered iteration is on num_children - 1, we need to set it as such]. // node 0 is the root, so the last child it has will be on num_children. // then subtracting num_children - 1 from that gives us node 1, which is the first child of the root. - if new_node_index >= L::num_children() { + if new_node_index >= L::NUM_CHILDREN { // because we loop in order, and our nodes are contiguous, the first node of the children got added on index i - (num children - 1) // so we need to adjust for that self.nodes[parent_index as usize].children = - NonZeroU32::new(new_node_index - (L::num_children() - 1)); + NonZeroU32::new(new_node_index - (L::NUM_CHILDREN - 1)); } } else { // otherwise we do need to do a regular swap remove @@ -830,11 +863,11 @@ where // because the last node we come by in with ordered iteration is on num_children - 1, we need to set it as such]. // node 0 is the root, so the last child it has will be on num_children. // then subtracting num_children - 1 from that gives us node 1, which is the first child of the root. - if new_node_index >= L::num_children() { + if new_node_index >= L::NUM_CHILDREN { // because we loop in order, and our nodes are contiguous, the first node of the children got added on index i - (num children - 1) // so we need to adjust for that self.nodes[parent_index as usize].children = - NonZeroU32::new(new_node_index - (L::num_children() - 1)); + NonZeroU32::new(new_node_index - (L::NUM_CHILDREN - 1)); } } @@ -852,6 +885,7 @@ where // and clear all internal arrays, so if this method is accidentally called twice, no weird behavior would happen self.chunks_to_add.clear(); + self.chunks_to_add_parent.clear(); self.chunks_to_remove.clear(); self.chunks_to_activate.clear(); self.chunks_to_deactivate.clear(); @@ -871,6 +905,7 @@ where self.nodes.clear(); self.free_list.clear(); self.chunks_to_add.clear(); + self.chunks_to_add_parent.clear(); self.chunks_to_remove.clear(); self.chunks_to_activate.clear(); self.chunks_to_deactivate.clear(); @@ -970,7 +1005,13 @@ mod tests { // make a tree let mut tree = Tree::::new(64); // as long as we need to update, do so - for tgt in [QuadVec::new(1, 1, 2), QuadVec::new(2, 3, 2)] { + for tgt in [ + QuadVec::new(1, 1, 1), + QuadVec::new(0, 1, 1), + QuadVec::new(2, 3, 2), + QuadVec::new(2, 2, 2), + ] { + println!("====NEXT TARGET ====="); dbg!(tgt); while tree.prepare_insert(&[tgt], 0, &|_| TestChunk {}) { for c in tree.iter_chunks_to_activate_positions() { From 23ccedfbd3f97cfcf3535c059539be6a8ce7ca06 Mon Sep 17 00:00:00 2001 From: Alex Pyattaev Date: Mon, 6 Mar 2023 11:14:57 +0200 Subject: [PATCH 13/15] remove unnecessary vec --- doc/node_structure.md | 32 ++++++++++++++++++++++++++++++++ examples/rayon.rs | 2 +- src/tree.rs | 34 +++++++++------------------------- 3 files changed, 42 insertions(+), 26 deletions(-) create mode 100644 doc/node_structure.md diff --git a/doc/node_structure.md b/doc/node_structure.md new file mode 100644 index 0000000..ea2cd39 --- /dev/null +++ b/doc/node_structure.md @@ -0,0 +1,32 @@ +Each node has array of children, which are indices into the array of nodes. +Chlidren array can contain zeros, which indicate absence of appropriate child. + +Chunks array points towards chunks associated with a given child. The root node (nodes[0]) +always gets a chunk assigned when it is first created (as zero would be a valid index for nodes otherwise). +There is no way to delete the root node. + +For all other nodes chunks are optional. + +Benefits of this layout: + * nodes are automatically grouped, so less operations on nodes array are needed to traverse the same depth of tree. + * vast majority of chunks are optional, which means we can store sparse data more efficiently + * + +In this example we assume QuadVec addressing. Thus, children and chunks are both 4 elements long, +and their encoding matches the offsets defined in appropriate fn get_child(self, index: u32). + +Pos does not need to be stored in nodes array, we keep it here for clarity of example. + +``` rust +nodes:Vec=vec![ +{pos:(0,0,0),children:[0,1,2,0],chunks:[0,0,0,0]}, +{pos:(0,1,1),children:[0,0,0,0],chunks:[1,0,0,0]}, +{pos:(1,0,1),children:[0,0,0,0],chunks:[0,2,0,0]}, +]; + +chunks:Vec=vec![ +{node:0, pos:(0,0,0)},// this chunk belongs to root node, if that is present. no way to disable this +{node:1, pos:(0,3,2)}, +{node:2, pos:(3,1,2)}, + ]; +``` diff --git a/examples/rayon.rs b/examples/rayon.rs index 365595b..51fe23e 100644 --- a/examples/rayon.rs +++ b/examples/rayon.rs @@ -55,7 +55,7 @@ fn main() { // if there was an update, we need to first generate new chunks with expensive_init tree.get_chunks_to_add_slice_mut().par_iter_mut().for_each( - |ToAddContainer { position, chunk }| { + |ToAddContainer { position, chunk,.. }| { // and run expensive init chunk.expensive_init(*position); }, diff --git a/src/tree.rs b/src/tree.rs index 15ee175..741a64b 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -51,6 +51,8 @@ pub struct ToAddContainer { /// Position of the chunk to add pub position: L, + /// Index of the parent node + parent_node_index: u32, } // utility struct for holding chunks to remove @@ -91,10 +93,6 @@ pub struct Tree { /// list of free nodes in the Tree, to allocate new nodes into free_list: VecDeque, - /// parent chunk indices of the chunks to be added. - /// tuple of the parent index and the position. - chunks_to_add_parent: Vec, - /// actual chunks to add during next update chunks_to_add: Vec>, @@ -171,7 +169,6 @@ where // make a new Tree // also allocate some room for nodes Self { - chunks_to_add_parent: Vec::new(), chunks_to_add: Vec::new(), chunks_to_remove: Vec::new(), chunks_to_activate: Vec::new(), @@ -193,7 +190,6 @@ where // make a new Tree // also allocate some room for nodes Self { - chunks_to_add_parent: Vec::with_capacity(capacity), chunks_to_add: Vec::with_capacity(capacity), chunks_to_remove: Vec::with_capacity(capacity), chunks_to_activate: Vec::with_capacity(capacity), @@ -454,7 +450,6 @@ where // first, clear the previous arrays self.chunks_to_add.clear(); - self.chunks_to_add_parent.clear(); self.chunks_to_remove.clear(); self.chunks_to_activate.clear(); self.chunks_to_deactivate.clear(); @@ -468,11 +463,9 @@ where self.chunks_to_add.push(ToAddContainer { position: L::root(), chunk: chunk_to_add, + parent_node_index:0, }); - // and the parent - self.chunks_to_add_parent.push(0); - // and an update is needed return true; } @@ -508,10 +501,9 @@ where self.chunks_to_add.push(ToAddContainer { position: current_position.get_child(i), chunk: chunk_to_add, + parent_node_index:current_node_index, }); - // and add the parent - self.chunks_to_add_parent.push(current_node_index); } // and add ourselves for deactivation @@ -578,7 +570,6 @@ where // first, clear the previous arrays self.chunks_to_add.clear(); - self.chunks_to_add_parent.clear(); self.chunks_to_remove.clear(); self.chunks_to_activate.clear(); self.chunks_to_deactivate.clear(); @@ -592,10 +583,9 @@ where self.chunks_to_add.push(ToAddContainer { position: L::root(), chunk: chunk_to_add, + parent_node_index:0, }); - // and the parent - self.chunks_to_add_parent.push(0); // and an update is needed return true; @@ -636,10 +626,9 @@ where self.chunks_to_add.push(ToAddContainer { position: current_position.get_child(i), chunk: chunk_to_add, + parent_node_index:current_node_index, }); - // and add the parent - self.chunks_to_add_parent.push(current_node_index); } // and add ourselves for deactivation @@ -686,10 +675,7 @@ where // first, get the iterator for chunks that will be added // this becomes useful later - let mut chunks_to_add_iter = self - .chunks_to_add_parent - .drain(..) - .zip(self.chunks_to_add.drain(..)); + let mut chunks_to_add_iter = self.chunks_to_add.drain(..); // then, remove old chunks, or cache them // we'll drain the vector, as we don't need it anymore afterward @@ -708,7 +694,7 @@ where // but not so fast, because if we can overwrite it with a new chunk, do so // that way we can avoid a copy later on, which might be expensive - if let Some((parent_index, ToAddContainer { position, chunk })) = + if let Some(ToAddContainer { position, chunk, parent_node_index:parent_index}) = chunks_to_add_iter.next() { // add the node @@ -828,7 +814,7 @@ where // add new chunks // we'll drain the vector here as well, as we won't need it anymore afterward - for (parent_index, ToAddContainer { position, chunk }) in chunks_to_add_iter { + for ToAddContainer { position, chunk, parent_node_index:parent_index } in chunks_to_add_iter { // add the node let new_node_index = match self.free_list.pop_front() { Some(x) => { @@ -885,7 +871,6 @@ where // and clear all internal arrays, so if this method is accidentally called twice, no weird behavior would happen self.chunks_to_add.clear(); - self.chunks_to_add_parent.clear(); self.chunks_to_remove.clear(); self.chunks_to_activate.clear(); self.chunks_to_deactivate.clear(); @@ -905,7 +890,6 @@ where self.nodes.clear(); self.free_list.clear(); self.chunks_to_add.clear(); - self.chunks_to_add_parent.clear(); self.chunks_to_remove.clear(); self.chunks_to_activate.clear(); self.chunks_to_deactivate.clear(); From dce9fa2b81fa0e988bc53c16b640bf14201f8bb3 Mon Sep 17 00:00:00 2001 From: Igor Bologov Date: Fri, 17 Mar 2023 14:48:32 +0200 Subject: [PATCH 14/15] Replace Fn with FnMut --- benches/iterators.rs | 2 +- examples/glium.rs | 2 +- examples/rayon.rs | 2 +- src/coords.rs | 5 ++++- src/iter.rs | 8 ++++---- src/lib.rs | 2 +- src/tree.rs | 10 +++++----- 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/benches/iterators.rs b/benches/iterators.rs index 44a697a..08db34d 100644 --- a/benches/iterators.rs +++ b/benches/iterators.rs @@ -55,7 +55,7 @@ fn create_and_fill_octree(num_chunks: u32, depth: u8) -> Tree, ctx: &RenderCont } let qv = QuadVec::from_float_coords(mouse_pos.0 as f64, (1.0 - mouse_pos.1) as f64, 6); - if tree.prepare_update(&[qv], 2, &chunk_creator) { + if tree.prepare_update(&[qv], 2, &mut chunk_creator) { // position should already have been set, so we can just change the visibility for chunk in tree.iter_chunks_to_activate_mut() { chunk.visible = true; diff --git a/examples/rayon.rs b/examples/rayon.rs index 365595b..2b8afdd 100644 --- a/examples/rayon.rs +++ b/examples/rayon.rs @@ -44,7 +44,7 @@ fn main() { if tree.prepare_update( &[OctVec::new(4096, 4096, 4096, 32)], // target position in the tree 2, // the amount of detail - &|position_in_tree| Chunk::new(position_in_tree), // and how we should make a new tree inside the function here. This should be done quickly + &mut |position_in_tree| Chunk::new(position_in_tree), // and how we should make a new tree inside the function here. This should be done quickly ) { let duration = start_time.elapsed().as_micros(); diff --git a/src/coords.rs b/src/coords.rs index 692b9ca..36c485c 100644 --- a/src/coords.rs +++ b/src/coords.rs @@ -238,9 +238,12 @@ impl OctVec { /// * `depth` the lod depth the coord is at. This is soft limited at roughly 60, and the tree might behave weird if it gets higher. #[inline] pub fn new(x: u64, y: u64, z: u64, depth: u8) -> Self { + debug_assert!(x < (1 << depth)); + debug_assert!(y < (1 << depth)); + debug_assert!(z < (1 << depth)); + debug_assert!(depth <= 60); Self { x, y, z, depth } } - /// creates a new vector from floating point coords. /// mapped so that (0, 0, 0) is the front bottom left corner and (1, 1, 1) is the back top right. /// # Args diff --git a/src/iter.rs b/src/iter.rs index 0b54ec2..e0ac6b1 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -903,7 +903,7 @@ mod tests { let mut tree = Tree::new(65); let qv = OctVec::new(R, R, R, D); - while tree.prepare_update(&[qv], R as u32, &chunk_creator) { + while tree.prepare_update(&[qv], R as u32, &mut chunk_creator) { // do the update tree.do_update(); // and clean @@ -942,9 +942,9 @@ mod tests { }; let mut filled_voxels: u32 = 0; //println!("{:?} {:?} {:?}", ite, min, max); - for i in tree.iter_all_chunks_in_bounds_and_tree(min, max, D) { - if i.1.visible { - println!(" Sphere chunk {:?}", i.0); + for (l, c) in tree.iter_all_chunks_in_bounds_and_tree(min, max, D) { + if c.visible { + println!(" Sphere chunk {:?}", l); filled_voxels += 1; } } diff --git a/src/lib.rs b/src/lib.rs index 2b7abf5..7bd4d95 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,7 @@ //! let needs_updating = tree.prepare_update( //! &[OctVec::new(8, 8, 8, 8)], // the target positions to generate the lod around //! 4, // amount of detail -//! &|pos| Chunk {} // and the function to construct the chunk with +//! &mut |pos| Chunk {} // and the function to construct the chunk with //! // NOTE: this is only called for completely new chunks, not the ones loaded from the chunk cache! //! ); //! ``` diff --git a/src/tree.rs b/src/tree.rs index 15ee175..915a99b 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -447,7 +447,7 @@ where &mut self, targets: &[L], detail: u32, - chunk_creator: &dyn Fn(L) -> C, + chunk_creator: &mut dyn FnMut(L) -> C, ) -> bool { //FIXME: this function currently will dry-run once for every update to make sure // there is nothing left to update. This is a waste of CPU time, especially for many targets @@ -571,7 +571,7 @@ where &mut self, targets: &[L], detail: u32, - chunk_creator: &dyn Fn(L) -> C, + chunk_creator: &mut dyn FnMut(L) -> C, ) -> bool { //FIXME: this function currently will dry-run once for every update to make sure // there is nothing left to update. This is a waste of CPU time, especially for many targets @@ -943,7 +943,7 @@ where // gets a chunk from the cache, otehrwise generates one from the given function #[inline] - fn get_chunk_from_cache(&mut self, position: L, chunk_creator: &dyn Fn(L) -> C) -> C { + fn get_chunk_from_cache(&mut self, position: L, chunk_creator: &mut dyn FnMut(L) -> C) -> C { if self.cache_size > 0 { if let Some(chunk) = self.chunk_cache.remove(&position) { return chunk; @@ -979,7 +979,7 @@ mod tests { // as long as we need to update, do so for tgt in [QuadVec::new(1, 1, 2), QuadVec::new(2, 3, 2)] { dbg!(tgt); - while tree.prepare_update(&[tgt], 0, &|_| TestChunk {}) { + while tree.prepare_update(&[tgt], 0, &mut |_| TestChunk {}) { for c in tree.iter_chunks_to_activate_positions() { println!("* {c:?}"); } @@ -1013,7 +1013,7 @@ mod tests { ] { println!("====NEXT TARGET ====="); dbg!(tgt); - while tree.prepare_insert(&[tgt], 0, &|_| TestChunk {}) { + while tree.prepare_insert(&[tgt], 0, &mut |_| TestChunk {}) { for c in tree.iter_chunks_to_activate_positions() { println!("* {c:?}"); } From b332a5aa2042ea01e8efd351ece3829dd65559f3 Mon Sep 17 00:00:00 2001 From: Alex Pyattaev Date: Thu, 1 Jun 2023 10:17:57 +0300 Subject: [PATCH 15/15] tune down benches --- benches/iterators.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/benches/iterators.rs b/benches/iterators.rs index 08db34d..751884a 100644 --- a/benches/iterators.rs +++ b/benches/iterators.rs @@ -6,21 +6,20 @@ use rand::{Rng, SeedableRng}; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; const N_LOOKUPS: usize = 40; -fn generate_area_bounds(rng: &mut SmallRng) -> (OctVec, OctVec) { - const D: u8 = 4; - let cmax = 1 << D; +fn generate_area_bounds(rng: &mut SmallRng, depth:u8) -> (OctVec, OctVec) { + let cmax = 1 << depth; let min = OctVec::new( rng.gen_range(0..cmax - 1), rng.gen_range(0..cmax - 1), rng.gen_range(0..cmax - 1), - D, + depth, ); let max = OctVec::new( rng.gen_range(min.x + 1..cmax), rng.gen_range(min.y + 1..cmax), rng.gen_range(min.z + 1..cmax), - D, + depth, ); return (min, max); } @@ -65,21 +64,21 @@ fn create_and_fill_octree(num_chunks: u32, depth: u8) -> Tree) { +fn bench_lookups_in_octree(tree: &Tree, depth:u8) { let mut rng = SmallRng::seed_from_u64(42); for _ in 0..N_LOOKUPS { - let (min, max) = generate_area_bounds(&mut rng); - for i in tree.iter_all_chunks_in_bounds_and_tree(min, max, 4) { + let (min, max) = generate_area_bounds(&mut rng, depth); + for i in tree.iter_all_chunks_in_bounds_and_tree(min, max, min.depth) { black_box(i); } } } -fn bench_mut_lookups_in_octree(tree: &mut Tree) { +fn bench_mut_lookups_in_octree(tree: &mut Tree, depth:u8) { let mut rng = SmallRng::seed_from_u64(42); for _ in 0..N_LOOKUPS { - let (min, max) = generate_area_bounds(&mut rng); - for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, 4) { + let (min, max) = generate_area_bounds(&mut rng, depth); + for i in tree.iter_all_chunks_in_bounds_and_tree_mut(min, max, min.depth) { i.1.material_index += 1; i.1.a_index += 1; i.1.b_index += 1; @@ -91,7 +90,7 @@ pub fn bench_iteration(c: &mut Criterion) { let mut group = c.benchmark_group("mutable iteration"); let mut samples_num = 100; - for depth in [4u8, 6, 8, 10].iter() { + for depth in [4u8, 6, 8].iter() { if *depth as i8 == 4 { samples_num = 100; } @@ -107,7 +106,7 @@ pub fn bench_iteration(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(depth), depth, |b, depth| { let mut tree = create_and_fill_octree::(num_chunks, *depth); b.iter(|| { - bench_mut_lookups_in_octree(&mut tree); + bench_mut_lookups_in_octree(&mut tree, *depth); }); black_box(tree); }); @@ -117,7 +116,7 @@ pub fn bench_iteration(c: &mut Criterion) { let mut group = c.benchmark_group("immutable iteration"); let mut samples_num = 10; - for depth in [4u8, 6, 8, 10].iter() { + for depth in [4u8, 6, 8].iter() { if *depth as i8 == 4 { samples_num = 100; } @@ -132,7 +131,7 @@ pub fn bench_iteration(c: &mut Criterion) { group.bench_with_input(BenchmarkId::from_parameter(depth), depth, |b, depth| { let tree = create_and_fill_octree::(num_chunks, *depth); b.iter(|| { - bench_lookups_in_octree(&tree); + bench_lookups_in_octree(&tree, *depth); }); }); }