Skip to main content

ferroui_base/rendering/composition/hit_testing/
composition_hit_test_aabb_tree.rs

1use super::ICompositionHitTester;
2use crate::media::IntersectionResult;
3use crate::platform::LtrbRect;
4use crate::rendering::composition::{CompositionTarget, CompositionVisual, CompositionVisualCollection};
5use std::collections::HashMap;
6use std::rc::Rc;
7
8const NULL: i32 = -1;
9const ORDER_BUCKET_SIZE: i32 = 32;
10const FAT_BOUNDS_PADDING: f64 = 1.0;
11
12type VisualFilter<'a> = Option<&'a dyn Fn(&Rc<CompositionVisual>) -> bool>;
13
14#[derive(Clone, Copy, PartialEq, Eq)]
15enum BoundsState {
16    Empty,
17    Bounded,
18    Unbounded,
19}
20
21#[derive(Clone)]
22struct Node {
23    bounds: LtrbRect,
24    visual: Option<Rc<CompositionVisual>>,
25    parent: i32,
26    child1: i32,
27    child2: i32,
28    next: i32,
29    height: i32,
30    order: i32,
31    bucket: i32,
32}
33
34impl Node {
35    fn is_leaf(&self) -> bool {
36        self.child1 == NULL
37    }
38}
39
40struct Bucket {
41    root: i32,
42    unbounded: Option<Vec<Rc<CompositionVisual>>>,
43    readback_revision: u64,
44}
45
46impl Bucket {
47    fn new(root: i32) -> Self {
48        Self { root, unbounded: None, readback_revision: 0 }
49    }
50
51    fn is_empty(&self) -> bool {
52        self.root == NULL && self.unbounded.as_ref().is_none_or(Vec::is_empty)
53    }
54}
55
56#[derive(Clone, Copy)]
57struct Entry {
58    order: i32,
59    leaf: i32,
60    revision: u64,
61    is_unbounded: bool,
62}
63
64impl Entry {
65    fn new(order: i32) -> Self {
66        Self { order, leaf: NULL, revision: 0, is_unbounded: false }
67    }
68}
69
70struct Candidate {
71    visual: Rc<CompositionVisual>,
72    order: i32,
73}
74
75fn key(visual: &Rc<CompositionVisual>) -> *const CompositionVisual {
76    Rc::as_ptr(visual)
77}
78
79fn contains_rect(outer: &LtrbRect, rect: LtrbRect) -> bool {
80    rect.left >= outer.left && rect.right <= outer.right && rect.top >= outer.top && rect.bottom <= outer.bottom
81}
82
83/// An index of the children of a visual for hit testing: the children are
84/// grouped into buckets by their order, and each bucket is a dynamic tree
85/// of axis-aligned bounding boxes.
86pub struct CompositionHitTestAabbTree {
87    children: Rc<CompositionVisualCollection>,
88    entries: HashMap<*const CompositionVisual, Entry>,
89    buckets: Vec<Bucket>,
90    nodes: Vec<Node>,
91    free_list: i32,
92}
93
94impl CompositionHitTestAabbTree {
95    pub fn new(children: Rc<CompositionVisualCollection>) -> Self {
96        let mut tree = Self {
97            children: children.clone(),
98            entries: HashMap::new(),
99            buckets: Vec::new(),
100            nodes: Vec::new(),
101            free_list: NULL,
102        };
103        for (i, child) in children.items().iter().enumerate() {
104            tree.update(child, i as i32);
105        }
106        tree
107    }
108
109    pub fn clear(&mut self) {
110        self.entries.clear();
111        self.buckets.clear();
112        self.nodes.clear();
113        self.free_list = NULL;
114    }
115
116    pub fn update(&mut self, visual: &Rc<CompositionVisual>, order: i32) {
117        let mut entry = match self.entries.get(&key(visual)).copied() {
118            None => {
119                let bucket_index = Self::get_bucket_index(order);
120                self.get_or_create_bucket(bucket_index);
121                Entry::new(order)
122            }
123            Some(mut entry) => {
124                if entry.order != order {
125                    self.update_order_core(visual, &mut entry, order);
126                }
127                entry
128            }
129        };
130
131        let (state, bounds, revision) = Self::get_bounds_state(visual);
132        self.update_bounds(visual, &mut entry, state, bounds, revision);
133        self.entries.insert(key(visual), entry);
134    }
135
136    pub fn remove(&mut self, visual: &Rc<CompositionVisual>) {
137        let Some(entry) = self.entries.remove(&key(visual)) else { return };
138
139        if entry.leaf != NULL {
140            self.destroy_leaf(entry.leaf);
141        } else if entry.is_unbounded {
142            self.remove_unbounded(visual, entry.order);
143        }
144
145        self.remove_bucket_if_empty(Self::get_bucket_index(entry.order));
146    }
147
148    pub fn update_order(&mut self, visual: &Rc<CompositionVisual>, order: i32) {
149        let Some(mut entry) = self.entries.get(&key(visual)).copied() else {
150            self.entries.insert(key(visual), Entry::new(order));
151            return;
152        };
153
154        if entry.order != order {
155            self.update_order_core(visual, &mut entry, order);
156            self.entries.insert(key(visual), entry);
157        }
158    }
159
160    fn update_order_core(&mut self, visual: &Rc<CompositionVisual>, entry: &mut Entry, order: i32) {
161        let old_order = entry.order;
162        let old_bucket = Self::get_bucket_index(old_order);
163        let new_bucket = Self::get_bucket_index(order);
164        entry.order = order;
165
166        if entry.leaf != NULL {
167            self.move_leaf(entry.leaf, order);
168            return;
169        }
170
171        if entry.is_unbounded {
172            self.move_unbounded(visual, old_order, order);
173            return;
174        }
175
176        if old_bucket != new_bucket {
177            self.remove_bucket_if_empty(old_bucket);
178        }
179    }
180
181    /// Appends the children that may be hit by `input` to `results`,
182    /// topmost first.
183    pub fn query<H: ICompositionHitTester>(
184        &mut self,
185        input: &H::Input,
186        results: &mut Vec<Rc<CompositionVisual>>,
187        readback_revision: u64,
188    ) {
189        let mut candidates: Vec<Candidate> = Vec::with_capacity(ORDER_BUCKET_SIZE as usize);
190        let mut stack: Vec<i32> = Vec::with_capacity(16);
191
192        for i in (0..self.buckets.len()).rev() {
193            self.update_bucket(i, readback_revision);
194            // Updating the bounds can drop trailing buckets.
195            let Some(bucket) = self.buckets.get(i) else { continue };
196            if bucket.is_empty() {
197                continue;
198            }
199
200            candidates.clear();
201            stack.clear();
202            self.query_bucket::<H>(i, input, &mut candidates, &mut stack);
203            // Higher child order is topmost, sort descending.
204            candidates.sort_by(|left, right| right.order.cmp(&left.order));
205
206            for candidate in candidates.drain(..) {
207                results.push(candidate.visual);
208            }
209        }
210    }
211
212    /// Finds the topmost hit among the children that may be hit by
213    /// `input`.
214    pub fn query_first<H: ICompositionHitTester>(
215        &mut self,
216        target: &CompositionTarget,
217        input: &H::Input,
218        filter: VisualFilter<'_>,
219        result_filter: VisualFilter<'_>,
220        readback_revision: u64,
221    ) -> (Option<Rc<CompositionVisual>>, IntersectionResult) {
222        let mut intersection_result = IntersectionResult::NotCalculated;
223        let mut candidates: Vec<Candidate> = Vec::with_capacity(ORDER_BUCKET_SIZE as usize);
224        let mut stack: Vec<i32> = Vec::with_capacity(16);
225
226        for i in (0..self.buckets.len()).rev() {
227            self.update_bucket(i, readback_revision);
228            let Some(bucket) = self.buckets.get(i) else { continue };
229            if bucket.is_empty() {
230                continue;
231            }
232
233            candidates.clear();
234            stack.clear();
235            self.query_bucket::<H>(i, input, &mut candidates, &mut stack);
236            candidates.sort_by(|left, right| right.order.cmp(&left.order));
237
238            for candidate in candidates.drain(..) {
239                let (hit, result) = target.hit_test_first_core::<H>(&candidate.visual, input, filter, result_filter);
240                intersection_result = result;
241                if hit.is_some() {
242                    return (hit, intersection_result);
243                }
244            }
245        }
246
247        (None, intersection_result)
248    }
249
250    fn update_bucket(&mut self, bucket_index: usize, readback_revision: u64) {
251        if self.buckets[bucket_index].readback_revision == readback_revision {
252            return;
253        }
254
255        let children = self.children.items();
256        let end = children.len().min((bucket_index + 1) * ORDER_BUCKET_SIZE as usize);
257        for visual in children.iter().take(end).skip(bucket_index * ORDER_BUCKET_SIZE as usize) {
258            let Some(mut entry) = self.entries.get(&key(visual)).copied() else { continue };
259            let (state, bounds, revision) = Self::get_bounds_state(visual);
260            if entry.revision != revision {
261                self.update_bounds(visual, &mut entry, state, bounds, revision);
262                self.entries.insert(key(visual), entry);
263            }
264        }
265
266        if let Some(bucket) = self.buckets.get_mut(bucket_index) {
267            bucket.readback_revision = readback_revision;
268        }
269    }
270
271    fn update_bounds(
272        &mut self,
273        visual: &Rc<CompositionVisual>,
274        entry: &mut Entry,
275        state: BoundsState,
276        bounds: LtrbRect,
277        revision: u64,
278    ) {
279        entry.revision = revision;
280
281        if entry.leaf != NULL {
282            if state == BoundsState::Bounded {
283                self.update_leaf(entry.leaf, bounds, entry.order);
284            } else {
285                self.destroy_leaf(entry.leaf);
286                entry.leaf = NULL;
287                if state == BoundsState::Unbounded {
288                    self.add_unbounded(visual, entry.order, entry);
289                }
290            }
291
292            return;
293        }
294
295        if entry.is_unbounded {
296            if state == BoundsState::Unbounded {
297                return;
298            }
299
300            self.remove_unbounded(visual, entry.order);
301            entry.is_unbounded = false;
302            if state == BoundsState::Bounded {
303                entry.leaf = self.create_leaf(visual, bounds, entry.order);
304            }
305
306            return;
307        }
308
309        if state == BoundsState::Bounded {
310            entry.leaf = self.create_leaf(visual, bounds, entry.order);
311        } else if state == BoundsState::Unbounded {
312            self.add_unbounded(visual, entry.order, entry);
313        }
314    }
315
316    fn query_bucket<H: ICompositionHitTester>(
317        &self,
318        bucket_index: usize,
319        input: &H::Input,
320        candidates: &mut Vec<Candidate>,
321        stack: &mut Vec<i32>,
322    ) {
323        let bucket = &self.buckets[bucket_index];
324        if bucket.root != NULL {
325            stack.push(bucket.root);
326        }
327
328        while let Some(node_index) = stack.pop() {
329            let node = &self.nodes[node_index as usize];
330            if !H::transformed_sub_tree_bounds_match(node.bounds, input) {
331                continue;
332            }
333
334            if node.is_leaf() {
335                if let Some(visual) = &node.visual {
336                    candidates.push(Candidate { visual: visual.clone(), order: node.order });
337                }
338            } else {
339                if node.child1 != NULL {
340                    stack.push(node.child1);
341                }
342                if node.child2 != NULL {
343                    stack.push(node.child2);
344                }
345            }
346        }
347
348        if let Some(unbounded) = &bucket.unbounded {
349            for visual in unbounded {
350                if let Some(entry) = self.entries.get(&key(visual)) {
351                    candidates.push(Candidate { visual: visual.clone(), order: entry.order });
352                }
353            }
354        }
355    }
356
357    fn get_bucket_index(order: i32) -> i32 {
358        order / ORDER_BUCKET_SIZE
359    }
360
361    fn get_or_create_bucket(&mut self, bucket_index: i32) -> &mut Bucket {
362        while self.buckets.len() as i32 <= bucket_index {
363            self.buckets.push(Bucket::new(NULL));
364        }
365        &mut self.buckets[bucket_index as usize]
366    }
367
368    fn remove_bucket_if_empty(&mut self, bucket_index: i32) {
369        if bucket_index >= self.buckets.len() as i32 {
370            return;
371        }
372
373        let children_count = self.children.count() as i32;
374        let last_required_bucket_index =
375            if children_count == 0 { NULL } else { Self::get_bucket_index(children_count - 1) };
376
377        let remove_trailing_bucket = {
378            let count = self.buckets.len() as i32;
379            let bucket = &mut self.buckets[bucket_index as usize];
380            if bucket.unbounded.as_ref().is_some_and(Vec::is_empty) {
381                bucket.unbounded = None;
382            }
383
384            bucket.is_empty() && bucket_index == count - 1 && bucket_index > last_required_bucket_index
385        };
386
387        if !remove_trailing_bucket {
388            return;
389        }
390
391        loop {
392            self.buckets.pop();
393            let count = self.buckets.len() as i32;
394            if !(count > 0 && count - 1 > last_required_bucket_index && self.buckets[(count - 1) as usize].is_empty()) {
395                break;
396            }
397        }
398    }
399
400    fn create_leaf(&mut self, visual: &Rc<CompositionVisual>, bounds: LtrbRect, order: i32) -> i32 {
401        let leaf = self.allocate_node();
402        {
403            let node = &mut self.nodes[leaf as usize];
404            node.bounds = Self::fatten(bounds);
405            node.visual = Some(visual.clone());
406            node.order = order;
407            node.bucket = Self::get_bucket_index(order);
408            node.height = 0;
409        }
410
411        self.insert_leaf(leaf);
412        leaf
413    }
414
415    fn destroy_leaf(&mut self, leaf: i32) {
416        self.remove_leaf(leaf);
417        self.free_node(leaf);
418    }
419
420    fn update_leaf(&mut self, leaf: i32, bounds: LtrbRect, order: i32) {
421        let bucket = Self::get_bucket_index(order);
422        {
423            let node = &mut self.nodes[leaf as usize];
424            // If the exact bounds still fit inside the fat bounds, the tree shape can stay unchanged.
425            if node.bucket == bucket && contains_rect(&node.bounds, bounds) {
426                node.order = order;
427                return;
428            }
429        }
430
431        self.remove_leaf(leaf);
432
433        {
434            let removed_node = &mut self.nodes[leaf as usize];
435            removed_node.bounds = Self::fatten(bounds);
436            removed_node.bucket = bucket;
437            removed_node.order = order;
438        }
439
440        self.insert_leaf(leaf);
441    }
442
443    fn move_leaf(&mut self, leaf: i32, order: i32) {
444        let bucket = Self::get_bucket_index(order);
445        {
446            let node = &mut self.nodes[leaf as usize];
447            if node.bucket == bucket {
448                node.order = order;
449                return;
450            }
451        }
452
453        self.remove_leaf(leaf);
454
455        {
456            let removed_node = &mut self.nodes[leaf as usize];
457            removed_node.order = order;
458            removed_node.bucket = bucket;
459        }
460
461        self.insert_leaf(leaf);
462    }
463
464    fn allocate_node(&mut self) -> i32 {
465        if self.free_list == NULL {
466            self.nodes.push(Node {
467                bounds: LtrbRect::default(),
468                visual: None,
469                parent: NULL,
470                child1: NULL,
471                child2: NULL,
472                next: NULL,
473                height: 0,
474                order: 0,
475                bucket: 0,
476            });
477            return self.nodes.len() as i32 - 1;
478        }
479
480        let index = self.free_list;
481        let node = &mut self.nodes[index as usize];
482        self.free_list = node.next;
483        node.parent = NULL;
484        node.child1 = NULL;
485        node.child2 = NULL;
486        node.next = NULL;
487        node.height = 0;
488        node.visual = None;
489        node.order = 0;
490        node.bucket = 0;
491        index
492    }
493
494    fn free_node(&mut self, index: i32) {
495        let node = &mut self.nodes[index as usize];
496        node.next = self.free_list;
497        node.parent = NULL;
498        node.child1 = NULL;
499        node.child2 = NULL;
500        node.height = -1;
501        node.visual = None;
502        node.order = 0;
503        node.bucket = 0;
504        self.free_list = index;
505    }
506
507    fn insert_leaf(&mut self, leaf: i32) {
508        let bucket_index = self.nodes[leaf as usize].bucket;
509        let root = self.get_or_create_bucket(bucket_index).root;
510        if root == NULL {
511            self.buckets[bucket_index as usize].root = leaf;
512            self.nodes[leaf as usize].parent = NULL;
513            return;
514        }
515
516        let leaf_bounds = self.nodes[leaf as usize].bounds;
517        let sibling = self.find_best_sibling(root, leaf_bounds);
518        let old_parent = self.nodes[sibling as usize].parent;
519        let new_parent = self.allocate_node();
520
521        // Insert by replacing the chosen sibling with a new internal parent:
522        //
523        // Before: oldParent        After: oldParent
524        //             |                       |
525        //          sibling                newParent
526        //                                  /      \
527        //                             sibling    leaf
528        let sibling_bounds = self.nodes[sibling as usize].bounds;
529        let sibling_height = self.nodes[sibling as usize].height;
530        {
531            let parent_node = &mut self.nodes[new_parent as usize];
532            parent_node.parent = old_parent;
533            parent_node.bounds = leaf_bounds.union(sibling_bounds);
534            parent_node.height = sibling_height + 1;
535            parent_node.child1 = sibling;
536            parent_node.child2 = leaf;
537            parent_node.visual = None;
538            parent_node.bucket = bucket_index;
539        }
540
541        self.nodes[sibling as usize].parent = new_parent;
542        self.nodes[leaf as usize].parent = new_parent;
543
544        if old_parent == NULL {
545            self.buckets[bucket_index as usize].root = new_parent;
546        } else {
547            let old_parent_node = &mut self.nodes[old_parent as usize];
548            if old_parent_node.child1 == sibling {
549                old_parent_node.child1 = new_parent;
550            } else {
551                old_parent_node.child2 = new_parent;
552            }
553        }
554
555        self.fix_ancestors(new_parent);
556    }
557
558    fn find_best_sibling(&self, root: i32, leaf_bounds: LtrbRect) -> i32 {
559        let mut index = root;
560        while !self.nodes[index as usize].is_leaf() {
561            let node = &self.nodes[index as usize];
562            let child1 = node.child1;
563            let child2 = node.child2;
564
565            let area = Self::perimeter(node.bounds);
566            let combined_area = Self::perimeter(node.bounds.union(leaf_bounds));
567            let cost = 2.0 * combined_area;
568            let inheritance_cost = 2.0 * (combined_area - area);
569
570            let cost1 = self.get_insertion_cost(child1, leaf_bounds, inheritance_cost);
571            let cost2 = self.get_insertion_cost(child2, leaf_bounds, inheritance_cost);
572
573            // Stop descending when pairing with this internal node is already cheaper.
574            if cost < cost1 && cost < cost2 {
575                break;
576            }
577
578            index = if cost1 < cost2 { child1 } else { child2 };
579        }
580
581        index
582    }
583
584    fn get_insertion_cost(&self, node_index: i32, leaf_bounds: LtrbRect, inheritance_cost: f64) -> f64 {
585        let node = &self.nodes[node_index as usize];
586        let union = node.bounds.union(leaf_bounds);
587        if node.is_leaf() {
588            return Self::perimeter(union) + inheritance_cost;
589        }
590
591        Self::perimeter(union) - Self::perimeter(node.bounds) + inheritance_cost
592    }
593
594    fn remove_leaf(&mut self, leaf: i32) {
595        let bucket_index = self.nodes[leaf as usize].bucket;
596        let removed_root = {
597            let bucket = &mut self.buckets[bucket_index as usize];
598            if leaf == bucket.root {
599                bucket.root = NULL;
600                true
601            } else {
602                false
603            }
604        };
605
606        if removed_root {
607            self.remove_bucket_if_empty(bucket_index);
608            return;
609        }
610
611        let parent = self.nodes[leaf as usize].parent;
612        let (grand_parent, sibling) = {
613            let parent_node = &self.nodes[parent as usize];
614            (parent_node.parent, if parent_node.child1 == leaf { parent_node.child2 } else { parent_node.child1 })
615        };
616
617        // Collapse the removed leaf's parent and promote the sibling.
618        if grand_parent != NULL {
619            // Before: grandParent        After: grandParent
620            //             |                         |
621            //           parent                   sibling
622            //           /    \
623            //        leaf  sibling
624            {
625                let grand_parent_node = &mut self.nodes[grand_parent as usize];
626                if grand_parent_node.child1 == parent {
627                    grand_parent_node.child1 = sibling;
628                } else {
629                    grand_parent_node.child2 = sibling;
630                }
631            }
632
633            self.nodes[sibling as usize].parent = grand_parent;
634            self.free_node(parent);
635            self.fix_ancestors(grand_parent);
636        } else {
637            // If the parent was the root, the sibling becomes the new root.
638            //
639            // Before: parent(root)       After: sibling(root)
640            //          /    \
641            //       leaf  sibling
642            self.buckets[bucket_index as usize].root = sibling;
643            self.nodes[sibling as usize].parent = NULL;
644            self.free_node(parent);
645        }
646
647        self.nodes[leaf as usize].parent = NULL;
648
649        self.remove_bucket_if_empty(bucket_index);
650    }
651
652    fn fix_ancestors(&mut self, mut index: i32) {
653        while index != NULL {
654            index = self.balance(index);
655
656            let (child1, child2) = {
657                let node = &self.nodes[index as usize];
658                (node.child1, node.child2)
659            };
660            let (bounds1, height1) = {
661                let child = &self.nodes[child1 as usize];
662                (child.bounds, child.height)
663            };
664            let (bounds2, height2) = {
665                let child = &self.nodes[child2 as usize];
666                (child.bounds, child.height)
667            };
668
669            let node = &mut self.nodes[index as usize];
670            // Ancestor bounds always cover both children after insert/remove/rotate.
671            node.bounds = bounds1.union(bounds2);
672            node.height = 1 + height1.max(height2);
673            index = node.parent;
674        }
675    }
676
677    fn balance(&mut self, index_a: i32) -> i32 {
678        let (is_leaf, height, index_b, index_c) = {
679            let a = &self.nodes[index_a as usize];
680            (a.is_leaf(), a.height, a.child1, a.child2)
681        };
682        if is_leaf || height < 2 {
683            return index_a;
684        }
685
686        let balance = self.nodes[index_c as usize].height - self.nodes[index_b as usize].height;
687
688        // The right subtree is heavier than the left. Rotate C up.
689        if balance > 1 {
690            return self.rotate_c_up(index_a, index_b, index_c);
691        }
692
693        // The left subtree is heavier than the right. Rotate B up.
694        if balance < -1 {
695            return self.rotate_b_up(index_a, index_b, index_c);
696        }
697
698        index_a
699    }
700
701    fn bounds_and_height(&self, index: i32) -> (LtrbRect, i32) {
702        let node = &self.nodes[index as usize];
703        (node.bounds, node.height)
704    }
705
706    fn rotate_c_up(&mut self, index_a: i32, index_b: i32, index_c: i32) -> i32 {
707        // Rotate C above A:
708        //
709        // Before:      A                   After, if F taller:  C
710        //             / \                                      / \
711        //            B   C                                    A   F
712        //               / \                                  / \
713        //              F   G                                B   G
714        //
715        //                                  After, otherwise:    C
716        //                                                      / \
717        //                                                     A   G
718        //                                                    / \
719        //                                                   B   F
720        let index_f = self.nodes[index_c as usize].child1;
721        let index_g = self.nodes[index_c as usize].child2;
722        let a_parent = self.nodes[index_a as usize].parent;
723
724        self.nodes[index_c as usize].child1 = index_a;
725        self.nodes[index_c as usize].parent = a_parent;
726        self.nodes[index_a as usize].parent = index_c;
727
728        // C takes A's old place in the parent chain.
729        self.replace_parent_child(index_a, index_c, a_parent);
730
731        let (f_bounds, f_height) = self.bounds_and_height(index_f);
732        let (g_bounds, g_height) = self.bounds_and_height(index_g);
733        let (b_bounds, b_height) = self.bounds_and_height(index_b);
734
735        // Keep the taller C child with C, and move the other child under A.
736        if f_height > g_height {
737            self.nodes[index_c as usize].child2 = index_f;
738            self.nodes[index_a as usize].child2 = index_g;
739            self.nodes[index_g as usize].parent = index_a;
740
741            let a_bounds = b_bounds.union(g_bounds);
742            let a_height = 1 + b_height.max(g_height);
743            self.nodes[index_a as usize].bounds = a_bounds;
744            self.nodes[index_c as usize].bounds = a_bounds.union(f_bounds);
745            self.nodes[index_a as usize].height = a_height;
746            self.nodes[index_c as usize].height = 1 + a_height.max(f_height);
747        } else {
748            self.nodes[index_c as usize].child2 = index_g;
749            self.nodes[index_a as usize].child2 = index_f;
750            self.nodes[index_f as usize].parent = index_a;
751
752            let a_bounds = b_bounds.union(f_bounds);
753            let a_height = 1 + b_height.max(f_height);
754            self.nodes[index_a as usize].bounds = a_bounds;
755            self.nodes[index_c as usize].bounds = a_bounds.union(g_bounds);
756            self.nodes[index_a as usize].height = a_height;
757            self.nodes[index_c as usize].height = 1 + a_height.max(g_height);
758        }
759
760        index_c
761    }
762
763    fn rotate_b_up(&mut self, index_a: i32, index_b: i32, index_c: i32) -> i32 {
764        // Rotate B above A:
765        //
766        // Before:      A                  After, if D taller:   B
767        //             / \                                      / \
768        //            B   C                                    A   D
769        //           / \                                      / \
770        //          D   E                                    E   C
771        //
772        //                                 After, otherwise:     B
773        //                                                      / \
774        //                                                     A   E
775        //                                                    / \
776        //                                                   D   C
777        let index_d = self.nodes[index_b as usize].child1;
778        let index_e = self.nodes[index_b as usize].child2;
779        let a_parent = self.nodes[index_a as usize].parent;
780
781        self.nodes[index_b as usize].child1 = index_a;
782        self.nodes[index_b as usize].parent = a_parent;
783        self.nodes[index_a as usize].parent = index_b;
784
785        // B takes A's old place in the parent chain.
786        self.replace_parent_child(index_a, index_b, a_parent);
787
788        let (d_bounds, d_height) = self.bounds_and_height(index_d);
789        let (e_bounds, e_height) = self.bounds_and_height(index_e);
790        let (c_bounds, c_height) = self.bounds_and_height(index_c);
791
792        // Keep the taller B child with B, and move the other child under A.
793        if d_height > e_height {
794            self.nodes[index_b as usize].child2 = index_d;
795            self.nodes[index_a as usize].child1 = index_e;
796            self.nodes[index_e as usize].parent = index_a;
797
798            let a_bounds = c_bounds.union(e_bounds);
799            let a_height = 1 + c_height.max(e_height);
800            self.nodes[index_a as usize].bounds = a_bounds;
801            self.nodes[index_b as usize].bounds = a_bounds.union(d_bounds);
802            self.nodes[index_a as usize].height = a_height;
803            self.nodes[index_b as usize].height = 1 + a_height.max(d_height);
804        } else {
805            self.nodes[index_b as usize].child2 = index_e;
806            self.nodes[index_a as usize].child1 = index_d;
807            self.nodes[index_d as usize].parent = index_a;
808
809            let a_bounds = c_bounds.union(d_bounds);
810            let a_height = 1 + c_height.max(d_height);
811            self.nodes[index_a as usize].bounds = a_bounds;
812            self.nodes[index_b as usize].bounds = a_bounds.union(e_bounds);
813            self.nodes[index_a as usize].height = a_height;
814            self.nodes[index_b as usize].height = 1 + a_height.max(e_height);
815        }
816
817        index_b
818    }
819
820    fn replace_parent_child(&mut self, old_child: i32, new_child: i32, parent: i32) {
821        if parent == NULL {
822            let bucket = self.nodes[new_child as usize].bucket;
823            self.buckets[bucket as usize].root = new_child;
824            return;
825        }
826
827        let parent_node = &mut self.nodes[parent as usize];
828        if parent_node.child1 == old_child {
829            parent_node.child1 = new_child;
830        } else {
831            parent_node.child2 = new_child;
832        }
833    }
834
835    fn add_unbounded(&mut self, visual: &Rc<CompositionVisual>, order: i32, entry: &mut Entry) {
836        let bucket_index = Self::get_bucket_index(order);
837        let bucket = self.get_or_create_bucket(bucket_index);
838        bucket.unbounded.get_or_insert_with(Vec::new).push(visual.clone());
839        entry.is_unbounded = true;
840    }
841
842    fn move_unbounded(&mut self, visual: &Rc<CompositionVisual>, old_order: i32, order: i32) {
843        let old_bucket_index = Self::get_bucket_index(old_order);
844        let new_bucket_index = Self::get_bucket_index(order);
845        if old_bucket_index == new_bucket_index {
846            return;
847        }
848
849        self.remove_unbounded(visual, old_order);
850        let new_bucket = self.get_or_create_bucket(new_bucket_index);
851        new_bucket.unbounded.get_or_insert_with(Vec::new).push(visual.clone());
852    }
853
854    fn remove_unbounded(&mut self, visual: &Rc<CompositionVisual>, order: i32) {
855        let bucket_index = Self::get_bucket_index(order);
856        if let Some(unbounded) = self.buckets.get_mut(bucket_index as usize).and_then(|b| b.unbounded.as_mut()) {
857            if let Some(index) = unbounded.iter().position(|v| Rc::ptr_eq(v, visual)) {
858                unbounded.remove(index);
859            }
860        }
861
862        self.remove_bucket_if_empty(bucket_index);
863    }
864
865    fn get_bounds_state(visual: &Rc<CompositionVisual>) -> (BoundsState, LtrbRect, u64) {
866        let Some(readback) = visual.try_get_valid_readback() else {
867            return (BoundsState::Empty, LtrbRect::default(), 0);
868        };
869
870        let revision = readback.revision;
871        if visual.disable_sub_tree_bounds_hit_test_optimization() {
872            return (BoundsState::Unbounded, LtrbRect::default(), revision);
873        }
874
875        match readback.transformed_subtree_bounds {
876            Some(subtree_bounds) if !subtree_bounds.is_zero_size() => (BoundsState::Bounded, subtree_bounds, revision),
877            _ => (BoundsState::Empty, LtrbRect::default(), revision),
878        }
879    }
880
881    // Fatten the bounds by a small amount to avoid having to update the tree for every tiny movement.
882    fn fatten(bounds: LtrbRect) -> LtrbRect {
883        LtrbRect::new(
884            bounds.left - FAT_BOUNDS_PADDING,
885            bounds.top - FAT_BOUNDS_PADDING,
886            bounds.right + FAT_BOUNDS_PADDING,
887            bounds.bottom + FAT_BOUNDS_PADDING,
888        )
889    }
890
891    fn perimeter(bounds: LtrbRect) -> f64 {
892        2.0 * (bounds.width() + bounds.height())
893    }
894
895    /// The number of buckets (for tests).
896    #[cfg(test)]
897    pub(crate) fn bucket_count(&self) -> usize {
898        self.buckets.len()
899    }
900
901    /// Checks the structural invariants of the trees (for tests).
902    #[cfg(test)]
903    pub(crate) fn validate(&self) {
904        for (bucket_index, bucket) in self.buckets.iter().enumerate() {
905            if bucket.root != NULL {
906                assert_eq!(self.nodes[bucket.root as usize].parent, NULL);
907                self.validate_node(bucket.root, bucket_index as i32);
908            }
909        }
910    }
911
912    #[cfg(test)]
913    fn validate_node(&self, index: i32, bucket: i32) -> i32 {
914        let node = &self.nodes[index as usize];
915        assert_eq!(node.bucket, bucket);
916        if node.is_leaf() {
917            assert!(node.visual.is_some());
918            assert_eq!(node.height, 0);
919            return 0;
920        }
921        let (c1, c2) = (&self.nodes[node.child1 as usize], &self.nodes[node.child2 as usize]);
922        assert_eq!(c1.parent, index);
923        assert_eq!(c2.parent, index);
924        assert!(contains_rect(&node.bounds, c1.bounds) && contains_rect(&node.bounds, c2.bounds));
925        let height = 1 + self.validate_node(node.child1, bucket).max(self.validate_node(node.child2, bucket));
926        assert_eq!(node.height, height);
927        height
928    }
929}