mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-04 02:05:31 +00:00
293 lines
8.8 KiB
Rust
293 lines
8.8 KiB
Rust
|
use crate::{Bounds, Half};
|
||
|
use std::{
|
||
|
cmp,
|
||
|
fmt::Debug,
|
||
|
ops::{Add, Sub},
|
||
|
};
|
||
|
|
||
|
#[derive(Debug)]
|
||
|
pub(crate) struct BoundsTree<U>
|
||
|
where
|
||
|
U: Default + Clone + Debug,
|
||
|
{
|
||
|
root: Option<usize>,
|
||
|
nodes: Vec<Node<U>>,
|
||
|
stack: Vec<usize>,
|
||
|
}
|
||
|
|
||
|
impl<U> BoundsTree<U>
|
||
|
where
|
||
|
U: Clone + Debug + PartialOrd + Add<U, Output = U> + Sub<Output = U> + Half + Default,
|
||
|
{
|
||
|
pub fn clear(&mut self) {
|
||
|
self.root = None;
|
||
|
self.nodes.clear();
|
||
|
self.stack.clear();
|
||
|
}
|
||
|
|
||
|
pub fn insert(&mut self, new_bounds: Bounds<U>) -> u32 {
|
||
|
// If the tree is empty, make the root the new leaf.
|
||
|
if self.root.is_none() {
|
||
|
let new_node = self.push_leaf(new_bounds, 1);
|
||
|
self.root = Some(new_node);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// Search for the best place to add the new leaf based on heuristics.
|
||
|
let mut max_intersecting_ordering = 0;
|
||
|
let mut index = self.root.unwrap();
|
||
|
while let Node::Internal {
|
||
|
left,
|
||
|
right,
|
||
|
bounds: node_bounds,
|
||
|
..
|
||
|
} = &mut self.nodes[index]
|
||
|
{
|
||
|
let left = *left;
|
||
|
let right = *right;
|
||
|
*node_bounds = node_bounds.union(&new_bounds);
|
||
|
self.stack.push(index);
|
||
|
|
||
|
// Descend to the best-fit child, based on which one would increase
|
||
|
// the surface area the least. This attempts to keep the tree balanced
|
||
|
// in terms of surface area. If there is an intersection with the other child,
|
||
|
// add its keys to the intersections vector.
|
||
|
let left_cost = new_bounds
|
||
|
.union(&self.nodes[left].bounds())
|
||
|
.half_perimeter();
|
||
|
let right_cost = new_bounds
|
||
|
.union(&self.nodes[right].bounds())
|
||
|
.half_perimeter();
|
||
|
if left_cost < right_cost {
|
||
|
max_intersecting_ordering =
|
||
|
self.find_max_ordering(right, &new_bounds, max_intersecting_ordering);
|
||
|
index = left;
|
||
|
} else {
|
||
|
max_intersecting_ordering =
|
||
|
self.find_max_ordering(left, &new_bounds, max_intersecting_ordering);
|
||
|
index = right;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We've found a leaf ('index' now refers to a leaf node).
|
||
|
// We'll insert a new parent node above the leaf and attach our new leaf to it.
|
||
|
let sibling = index;
|
||
|
|
||
|
// Check for collision with the located leaf node
|
||
|
let Node::Leaf {
|
||
|
bounds: sibling_bounds,
|
||
|
order: sibling_ordering,
|
||
|
..
|
||
|
} = &self.nodes[index]
|
||
|
else {
|
||
|
unreachable!();
|
||
|
};
|
||
|
if sibling_bounds.intersects(&new_bounds) {
|
||
|
max_intersecting_ordering = cmp::max(max_intersecting_ordering, *sibling_ordering);
|
||
|
}
|
||
|
|
||
|
let ordering = max_intersecting_ordering + 1;
|
||
|
let new_node = self.push_leaf(new_bounds, ordering);
|
||
|
let new_parent = self.push_internal(sibling, new_node);
|
||
|
|
||
|
// If there was an old parent, we need to update its children indices.
|
||
|
if let Some(old_parent) = self.stack.last().copied() {
|
||
|
let Node::Internal { left, right, .. } = &mut self.nodes[old_parent] else {
|
||
|
unreachable!();
|
||
|
};
|
||
|
|
||
|
if *left == sibling {
|
||
|
*left = new_parent;
|
||
|
} else {
|
||
|
*right = new_parent;
|
||
|
}
|
||
|
} else {
|
||
|
// If the old parent was the root, the new parent is the new root.
|
||
|
self.root = Some(new_parent);
|
||
|
}
|
||
|
|
||
|
for node_index in self.stack.drain(..) {
|
||
|
let Node::Internal {
|
||
|
max_order: max_ordering,
|
||
|
..
|
||
|
} = &mut self.nodes[node_index]
|
||
|
else {
|
||
|
unreachable!()
|
||
|
};
|
||
|
*max_ordering = cmp::max(*max_ordering, ordering);
|
||
|
}
|
||
|
|
||
|
ordering
|
||
|
}
|
||
|
|
||
|
fn find_max_ordering(&self, index: usize, bounds: &Bounds<U>, mut max_ordering: u32) -> u32 {
|
||
|
match &self.nodes[index] {
|
||
|
Node::Leaf {
|
||
|
bounds: node_bounds,
|
||
|
order: ordering,
|
||
|
..
|
||
|
} => {
|
||
|
if bounds.intersects(node_bounds) {
|
||
|
max_ordering = cmp::max(*ordering, max_ordering);
|
||
|
}
|
||
|
}
|
||
|
Node::Internal {
|
||
|
left,
|
||
|
right,
|
||
|
bounds: node_bounds,
|
||
|
max_order: node_max_ordering,
|
||
|
..
|
||
|
} => {
|
||
|
if bounds.intersects(node_bounds) && max_ordering < *node_max_ordering {
|
||
|
let left_max_ordering = self.nodes[*left].max_ordering();
|
||
|
let right_max_ordering = self.nodes[*right].max_ordering();
|
||
|
if left_max_ordering > right_max_ordering {
|
||
|
max_ordering = self.find_max_ordering(*left, bounds, max_ordering);
|
||
|
max_ordering = self.find_max_ordering(*right, bounds, max_ordering);
|
||
|
} else {
|
||
|
max_ordering = self.find_max_ordering(*right, bounds, max_ordering);
|
||
|
max_ordering = self.find_max_ordering(*left, bounds, max_ordering);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
max_ordering
|
||
|
}
|
||
|
|
||
|
fn push_leaf(&mut self, bounds: Bounds<U>, order: u32) -> usize {
|
||
|
self.nodes.push(Node::Leaf { bounds, order });
|
||
|
self.nodes.len() - 1
|
||
|
}
|
||
|
|
||
|
fn push_internal(&mut self, left: usize, right: usize) -> usize {
|
||
|
let left_node = &self.nodes[left];
|
||
|
let right_node = &self.nodes[right];
|
||
|
let new_bounds = left_node.bounds().union(right_node.bounds());
|
||
|
let max_ordering = cmp::max(left_node.max_ordering(), right_node.max_ordering());
|
||
|
self.nodes.push(Node::Internal {
|
||
|
bounds: new_bounds,
|
||
|
left,
|
||
|
right,
|
||
|
max_order: max_ordering,
|
||
|
});
|
||
|
self.nodes.len() - 1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<U> Default for BoundsTree<U>
|
||
|
where
|
||
|
U: Default + Clone + Debug,
|
||
|
{
|
||
|
fn default() -> Self {
|
||
|
BoundsTree {
|
||
|
root: None,
|
||
|
nodes: Vec::new(),
|
||
|
stack: Vec::new(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Clone)]
|
||
|
enum Node<U>
|
||
|
where
|
||
|
U: Clone + Default + Debug,
|
||
|
{
|
||
|
Leaf {
|
||
|
bounds: Bounds<U>,
|
||
|
order: u32,
|
||
|
},
|
||
|
Internal {
|
||
|
left: usize,
|
||
|
right: usize,
|
||
|
bounds: Bounds<U>,
|
||
|
max_order: u32,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
impl<U> Node<U>
|
||
|
where
|
||
|
U: Clone + Default + Debug,
|
||
|
{
|
||
|
fn bounds(&self) -> &Bounds<U> {
|
||
|
match self {
|
||
|
Node::Leaf { bounds, .. } => bounds,
|
||
|
Node::Internal { bounds, .. } => bounds,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn max_ordering(&self) -> u32 {
|
||
|
match self {
|
||
|
Node::Leaf {
|
||
|
order: ordering, ..
|
||
|
} => *ordering,
|
||
|
Node::Internal {
|
||
|
max_order: max_ordering,
|
||
|
..
|
||
|
} => *max_ordering,
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[cfg(test)]
|
||
|
mod tests {
|
||
|
use super::*;
|
||
|
use crate::{Bounds, Point, Size};
|
||
|
|
||
|
#[test]
|
||
|
fn test_insert() {
|
||
|
let mut tree = BoundsTree::<f32>::default();
|
||
|
let bounds1 = Bounds {
|
||
|
origin: Point { x: 0.0, y: 0.0 },
|
||
|
size: Size {
|
||
|
width: 10.0,
|
||
|
height: 10.0,
|
||
|
},
|
||
|
};
|
||
|
let bounds2 = Bounds {
|
||
|
origin: Point { x: 5.0, y: 5.0 },
|
||
|
size: Size {
|
||
|
width: 10.0,
|
||
|
height: 10.0,
|
||
|
},
|
||
|
};
|
||
|
let bounds3 = Bounds {
|
||
|
origin: Point { x: 10.0, y: 10.0 },
|
||
|
size: Size {
|
||
|
width: 10.0,
|
||
|
height: 10.0,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
// Insert the bounds into the tree and verify the order is correct
|
||
|
assert_eq!(tree.insert(bounds1), 1);
|
||
|
assert_eq!(tree.insert(bounds2), 2);
|
||
|
assert_eq!(tree.insert(bounds3), 3);
|
||
|
|
||
|
// Insert non-overlapping bounds and verify they can reuse orders
|
||
|
let bounds4 = Bounds {
|
||
|
origin: Point { x: 20.0, y: 20.0 },
|
||
|
size: Size {
|
||
|
width: 10.0,
|
||
|
height: 10.0,
|
||
|
},
|
||
|
};
|
||
|
let bounds5 = Bounds {
|
||
|
origin: Point { x: 40.0, y: 40.0 },
|
||
|
size: Size {
|
||
|
width: 10.0,
|
||
|
height: 10.0,
|
||
|
},
|
||
|
};
|
||
|
let bounds6 = Bounds {
|
||
|
origin: Point { x: 25.0, y: 25.0 },
|
||
|
size: Size {
|
||
|
width: 10.0,
|
||
|
height: 10.0,
|
||
|
},
|
||
|
};
|
||
|
assert_eq!(tree.insert(bounds4), 1); // bounds4 does not overlap with bounds1, bounds2, or bounds3
|
||
|
assert_eq!(tree.insert(bounds5), 1); // bounds5 does not overlap with any other bounds
|
||
|
assert_eq!(tree.insert(bounds6), 2); // bounds6 overlaps with bounds4, so it should have a different order
|
||
|
}
|
||
|
}
|