pax_runtime/engine/
occlusion.rs

1use std::rc::Rc;
2
3use pax_message::{borrow, OcclusionPatch};
4use pax_runtime_api::{Layer, Window};
5
6use crate::{node_interface::NodeLocal, ExpandedNode, RuntimeContext, TransformAndBounds};
7
8use super::expanded_node::Occlusion;
9
10#[derive(Clone, Copy, Debug)]
11pub struct OcclusionBox {
12    x1: f64,
13    y1: f64,
14    x2: f64,
15    y2: f64,
16}
17
18impl OcclusionBox {
19    fn intersects(&self, other: &Self) -> bool {
20        if self.x2 <= other.x1 || other.x2 <= self.x1 {
21            return false;
22        }
23        if self.y2 <= other.y1 || other.y2 <= self.y1 {
24            return false;
25        }
26        true
27    }
28
29    fn new_from_transform_and_bounds(t_and_b: TransformAndBounds<NodeLocal, Window>) -> Self {
30        let corners = t_and_b.corners();
31        let mut x1 = f64::MAX;
32        let mut y1 = f64::MAX;
33        let mut x2 = f64::MIN;
34        let mut y2 = f64::MIN;
35        for c in corners {
36            x1 = x1.min(c.x);
37            y1 = y1.min(c.y);
38            x2 = x2.max(c.x);
39            y2 = y2.max(c.y);
40        }
41        OcclusionBox { x1, y1, x2, y2 }
42    }
43}
44
45pub fn update_node_occlusion(root_node: &Rc<ExpandedNode>, ctx: &RuntimeContext) {
46    let mut occlusion_stack = vec![];
47    let mut z_index = 0;
48    update_node_occlusion_recursive(root_node, &mut occlusion_stack, ctx, false, &mut z_index);
49    let max_layer = occlusion_stack.len() - 1;
50    if ctx.layer_count.get() != max_layer {
51        ctx.layer_count.set(max_layer);
52        ctx.enqueue_native_message(pax_message::NativeMessage::ShrinkLayersTo(max_layer as u32));
53    }
54}
55
56// runtime is O(n^2) atm, could be improved by quadtree, or find an approximation
57// method using the tree structure that works well.
58fn update_node_occlusion_recursive(
59    node: &Rc<ExpandedNode>,
60    occlusion_stack: &mut Vec<(Vec<OcclusionBox>, Vec<OcclusionBox>)>,
61    ctx: &RuntimeContext,
62    clipping: bool,
63    z_index: &mut i32,
64) {
65    for child in node.children.get().iter().rev() {
66        let cp = child.get_common_properties();
67        let cp = borrow!(cp);
68        let unclippable = cp.unclippable.get().unwrap_or(false);
69        let clips = borrow!(child.instance_node).clips_content(&child);
70        update_node_occlusion_recursive(
71            child,
72            occlusion_stack,
73            ctx,
74            (clipping | clips) & !unclippable,
75            z_index,
76        );
77    }
78
79    let layer = borrow!(node.instance_node).base().flags().layer;
80    if layer != Layer::DontCare || borrow!(node.instance_node).clips_content(&node) {
81        let occlusion_box =
82            OcclusionBox::new_from_transform_and_bounds(node.transform_and_bounds.get());
83        let mut occlusion_index = 0;
84
85        for (index, stack) in occlusion_stack.iter().enumerate().rev() {
86            if stack.0.iter().any(|box_| occlusion_box.intersects(box_)) {
87                occlusion_index = match layer {
88                    Layer::Canvas => index + 1,
89                    _ => index,
90                };
91                break;
92            }
93            if stack.1.iter().any(|box_| occlusion_box.intersects(box_)) {
94                occlusion_index = index;
95                break;
96            }
97        }
98
99        if occlusion_stack.len() <= occlusion_index {
100            occlusion_stack.push(Default::default());
101        }
102
103        let occl_layer = &mut occlusion_stack[occlusion_index];
104        let set = match layer {
105            Layer::Native => &mut occl_layer.0,
106            _ => &mut occl_layer.1,
107        };
108        set.push(occlusion_box);
109
110        let new_occlusion = Occlusion {
111            occlusion_layer_id: occlusion_index,
112            z_index: *z_index,
113            parent_frame: node
114                .parent_frame
115                .get()
116                .filter(|_| clipping)
117                .map(|v| v.to_u32()),
118        };
119
120        if (layer == Layer::Native || borrow!(node.instance_node).clips_content(&node))
121            && node.occlusion.get() != new_occlusion
122        {
123            let occlusion_patch = OcclusionPatch {
124                id: node.id.to_u32(),
125                z_index: new_occlusion.z_index,
126                occlusion_layer_id: new_occlusion.occlusion_layer_id,
127                parent_frame: new_occlusion.parent_frame,
128            };
129            ctx.enqueue_native_message(pax_message::NativeMessage::OcclusionUpdate(
130                occlusion_patch,
131            ));
132        }
133        if new_occlusion != node.occlusion.get() {
134            let prev_layer = node.occlusion.get().occlusion_layer_id;
135            ctx.set_canvas_dirty(prev_layer as usize);
136            ctx.set_canvas_dirty(new_occlusion.occlusion_layer_id as usize);
137            node.occlusion.set(new_occlusion);
138        }
139
140        *z_index += 1;
141    }
142}