1use cvkg_core::Rect;
2
3#[derive(Debug, Clone)]
5pub struct LayoutSpatialEntry {
6 pub hash: u64,
8 pub rect: Rect,
10}
11
12pub struct LayoutSpatialIndex {
14 root: Option<Box<QuadNode>>,
15 bounds: Rect,
17}
18
19const MAX_ITEMS_PER_NODE: usize = 16;
20const MAX_TREE_DEPTH: u32 = 8;
21
22struct QuadNode {
23 bounds: Rect,
24 entries: Vec<LayoutSpatialEntry>,
25 children: Option<Box<[Box<QuadNode>; 4]>>,
26}
27
28impl QuadNode {
29 fn new(bounds: Rect) -> Self {
30 Self {
31 bounds,
32 entries: Vec::new(),
33 children: None,
34 }
35 }
36
37 fn insert(&mut self, entry: LayoutSpatialEntry, depth: u32) {
38 if !self.bounds.intersects(&entry.rect) {
39 return;
40 }
41 if let Some(children) = &mut self.children {
42 for child in children.iter_mut() {
43 if child.bounds.intersects(&entry.rect) {
44 child.insert(entry.clone(), depth + 1);
45 }
46 }
47 return;
48 }
49 self.entries.push(entry);
50 if self.entries.len() > MAX_ITEMS_PER_NODE && depth < MAX_TREE_DEPTH {
51 self.split(depth);
52 }
53 }
54
55 fn split(&mut self, depth: u32) {
56 let hw = self.bounds.width * 0.5;
57 let hh = self.bounds.height * 0.5;
58 let mx = self.bounds.x + hw;
59 let my = self.bounds.y + hh;
60 let make = |x, y, w, h| Box::new(QuadNode::new(Rect { x, y, width: w, height: h }));
61 let mut children = Box::new([
62 make(self.bounds.x, self.bounds.y, hw, hh), make(mx, self.bounds.y, hw, hh), make(self.bounds.x, my, hw, hh), make(mx, my, hw, hh), ]);
67 let entries = std::mem::take(&mut self.entries);
68 for e in entries {
69 for child in children.iter_mut() {
70 if child.bounds.intersects(&e.rect) {
71 child.insert(e.clone(), depth + 1);
72 }
73 }
74 }
75 self.children = Some(children);
76 }
77
78 fn hit_test(&self, point: (f32, f32), out: &mut Vec<LayoutSpatialEntry>) {
79 if !self.bounds.contains(point.0, point.1) {
80 return;
81 }
82 for e in &self.entries {
83 if e.rect.contains(point.0, point.1) {
84 out.push(e.clone());
85 }
86 }
87 if let Some(children) = &self.children {
88 for child in children.iter() {
89 child.hit_test(point, out);
90 }
91 }
92 }
93
94 fn query_region(&self, region: &Rect, out: &mut Vec<LayoutSpatialEntry>) {
95 if !self.bounds.intersects(region) {
96 return;
97 }
98 for e in &self.entries {
99 if e.rect.intersects(region) {
100 out.push(e.clone());
101 }
102 }
103 if let Some(children) = &self.children {
104 for child in children.iter() {
105 child.query_region(region, out);
106 }
107 }
108 }
109}
110
111impl LayoutSpatialIndex {
112 pub fn new() -> Self {
114 Self { root: None, bounds: Rect::zero() }
115 }
116
117 pub fn rebuild(&mut self, root_bounds: Rect, entries: impl IntoIterator<Item = LayoutSpatialEntry>) {
119 self.bounds = root_bounds;
120 let mut root = QuadNode::new(root_bounds);
121 for e in entries {
122 if e.rect.width > 0.0 && e.rect.height > 0.0 {
123 root.insert(e, 0);
124 }
125 }
126 self.root = Some(Box::new(root));
127 }
128
129 pub fn hit_test(&self, x: f32, y: f32) -> Vec<LayoutSpatialEntry> {
131 let mut out = Vec::new();
132 if let Some(root) = &self.root {
133 root.hit_test((x, y), &mut out);
134 }
135 out
136 }
137
138 pub fn query_region(&self, region: &Rect) -> Vec<LayoutSpatialEntry> {
140 let mut out = Vec::new();
141 if let Some(root) = &self.root {
142 root.query_region(region, &mut out);
143 }
144 out
145 }
146}
147
148impl Default for LayoutSpatialIndex {
149 fn default() -> Self {
150 Self::new()
151 }
152}