1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::rc::Rc;

use pax_message::{borrow, OcclusionPatch};
use pax_runtime_api::{Layer, Window};

use crate::{node_interface::NodeLocal, ExpandedNode, RuntimeContext, TransformAndBounds};

use super::expanded_node::Occlusion;

#[derive(Clone, Copy, Debug)]
pub struct OcclusionBox {
    x1: f64,
    y1: f64,
    x2: f64,
    y2: f64,
}

impl OcclusionBox {
    fn intersects(&self, other: &Self) -> bool {
        if self.x2 < other.x1 || other.x2 < self.x1 {
            return false;
        }
        if self.y2 < other.y1 || other.y2 < self.y1 {
            return false;
        }
        true
    }

    fn new_from_transform_and_bounds(t_and_b: TransformAndBounds<NodeLocal, Window>) -> Self {
        let corners = t_and_b.corners();
        let mut x1 = f64::MAX;
        let mut y1 = f64::MAX;
        let mut x2 = f64::MIN;
        let mut y2 = f64::MIN;
        for c in corners {
            x1 = x1.min(c.x);
            y1 = y1.min(c.y);
            x2 = x2.max(c.x);
            y2 = y2.max(c.y);
        }
        OcclusionBox { x1, y1, x2, y2 }
    }
}

pub fn update_node_occlusion(root_node: &Rc<ExpandedNode>, ctx: &RuntimeContext) {
    let mut occlusion_stack = vec![];
    let mut z_index = 0;
    update_node_occlusion_recursive(root_node, &mut occlusion_stack, ctx, false, &mut z_index);
    let max_layer = occlusion_stack.len();
    ctx.enqueue_native_message(pax_message::NativeMessage::ShrinkLayersTo(max_layer as u32));
}

// runtime is O(n^2) atm, could be improved by quadtree, or find an approximation
// method using the tree structure that works well.
fn update_node_occlusion_recursive(
    node: &Rc<ExpandedNode>,
    occlusion_stack: &mut Vec<(Vec<OcclusionBox>, Vec<OcclusionBox>)>,
    ctx: &RuntimeContext,
    clipping: bool,
    z_index: &mut i32,
) {
    for child in node.children.get().iter().rev() {
        let cp = child.get_common_properties();
        let cp = borrow!(cp);
        let unclippable = cp.unclippable.get().unwrap_or(false);
        let clips = borrow!(child.instance_node).clips_content(&child);
        update_node_occlusion_recursive(
            child,
            occlusion_stack,
            ctx,
            (clipping | clips) & !unclippable,
            z_index,
        );
    }

    let layer = borrow!(node.instance_node).base().flags().layer;
    if layer != Layer::DontCare || borrow!(node.instance_node).clips_content(&node) {
        let occlusion_box =
            OcclusionBox::new_from_transform_and_bounds(node.transform_and_bounds.get());
        let mut occlusion_index = 0;

        for (index, stack) in occlusion_stack.iter().enumerate().rev() {
            if stack.0.iter().any(|box_| occlusion_box.intersects(box_)) {
                occlusion_index = match layer {
                    Layer::Canvas => index + 1,
                    _ => index,
                };
                break;
            }
            if stack.1.iter().any(|box_| occlusion_box.intersects(box_)) {
                occlusion_index = index;
                break;
            }
        }

        if occlusion_stack.len() <= occlusion_index {
            occlusion_stack.push(Default::default());
        }

        let occl_layer = &mut occlusion_stack[occlusion_index];
        let set = match layer {
            Layer::Native => &mut occl_layer.0,
            _ => &mut occl_layer.1,
        };
        set.push(occlusion_box);

        let new_occlusion = Occlusion {
            occlusion_layer_id: occlusion_index as u32,
            z_index: *z_index,
            parent_frame: node
                .parent_frame
                .get()
                .filter(|_| clipping)
                .map(|v| v.to_u32()),
        };

        if (layer == Layer::Native || borrow!(node.instance_node).clips_content(&node))
            && node.occlusion.get() != new_occlusion
        {
            let occlusion_patch = OcclusionPatch {
                id: node.id.to_u32(),
                z_index: new_occlusion.z_index,
                occlusion_layer_id: new_occlusion.occlusion_layer_id,
                parent_frame: new_occlusion.parent_frame,
            };
            ctx.enqueue_native_message(pax_message::NativeMessage::OcclusionUpdate(
                occlusion_patch,
            ));
        }
        node.occlusion.set(new_occlusion);
        *z_index += 1;
    }
}