Spatial trees, (aka QuadTrees, OctTrees, LodTrees) are a family of fast tree data structures that supports complex spatial queries at various level of detail. They are particularly well suited for sparse data storage and neighbor queries.
Internals are partially based on: * https://stackoverflow.com/questions/41946007/efficient-and-well-explained-implementation-of-a-quadtree-for-2d-collision-det * https://github.com/Dimev/lodtree
The aim of this crate is to provide a generic, easy to use tree data structure that can be used to make Quadtrees, Octrees for various realtime applications (e.g. games or GIS software).
Internally, the tree tries to keep all needed memory allocated in slab arenas to avoid the memory fragmentation and allocator pressure. All operations, where possible, use either stack allocations or allocate at most once.
Import the crate
rust
use spatialtree::*;
The tree is it's own struct, and accepts a chunk (anything that implements Sized) and the coordinate vector (Anything that implements the LodVec trait). ```rust
struct Chunk {
//important useful fileds go here
}
// a new OctTree with no capacity
let mut tree = OctTree::
The given LodVec implementations (OctVec and QuadVec) take in 4 and 3 arguments respectively. The first 3/2 are the position in the tree, which is dependant on the lod level. and the last parameter is the lod level. No lods smaller than this will be generated for this target.
```rust
// QuadVec takes x,y and depth let qv1 = QuadVec::build(1u8, 2, 3); let qv2 = QuadVec::new([1u8, 2], 3); asserteq!(qv1, qv2); // OctVec takes x,y,z and depth let ov1 = OctVec::build(1u8, 2, 3, 3); let ov2 = OctVec::new([1u8, 2, 3], 3); asserteq!(ov1, ov2); ```
Inserts are most efficient when performed in large batches, as this minimizes tree traverse overhead. ```rust
// create a tree
let mut tree = QuadTree::
Alternatively, if you want to insert data one chunk at a time: ```rust
// create a tree with usize as data
let mut tree = QuadTree::
This structure can be used for purposes such as progressive LOD.
If you want to update chunks due to the camera being moved, you can do so with lod_update. It takes in 3 parameters.
Targets: is the array of locations around which to generate the most detail.
Detail: The amount of detail for the targets. The default implementation defines this as the amount of chunks at the target lod level surrounding the target chunk.
chunkcreator: the function to call when new chunk is needed evictcallback: function to call when chunk is evicted from data structure
The purpose of evict_callback is to enable things such as caching, object reuse etc. If this is done it may be wise to keep chunks fairly small such that moving them between tree and cache is not too expensive.
```rust
// Tree with "active" data
let mut tree = QuadTree::
//function to populate new chunks. Will read from cache if possible
let mut chunkcreator = |c:QuadVec|->Vec
//Function to deal with evicted chunks. Will move them to cache.
let mut chunkevict = |c:QuadVec, d:Vec
// construct vector pointing to location that we want to detail let qv = QuadVec::fromfloatcoords([1.1, 2.4], 6); // run the actual update rebuilding the tree tree.lodupdate(&[qv], 2, chunkcreator, chunkevict); ``` Internally, lodupdate will rebuild the tree to match needed node structure that reflects locations of all targets. Thus, defragmentnodes is never needed after lodupdate. You may want to defragment_chunks if you are going to iterate over them.
For best performance, memory compactness, you may wish to ensure that chunks are stored in a contigous array. While tree will try to ensure this at all times, it will not move data it does not have to, unless explicitly instructed to do so. Thus, after multiple deletions it may be necessary to defragment the storage.
```rust
// make sure there are no holes in chunks array for fast iteration tree.defragmentchunks(); ``` Note that defragmentchunks will not do anything if chunks array has no holes already, so it is safe to call it every time you suspect you might need to.
Similarly, nodes storage can be rebuilt and defragmented, though this is a substantially more costly operation, and needs to allocate memory. Thus, call this only when you have strong reasons (i.e. benchmarks) to do so. ```rust
// make sure there are no holes in nodes array for fast iteration tree.defragment_nodes(); ```
Once structures are defragmented, any memory that was freed can be reclaimed with ```rust
tree.shrinktofit(); ```
Licensed under either * GPL, Version 3.0 (LICENSE.txt ) * Proprietary license for commercial use (contact author to arrange licensing)
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, shall be licensed as above, without any additional terms or conditions.