Skip to main content

pite_core/
math.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2/// A 2D floating-point vector with `x` and `y` components.
3#[derive(Clone, Copy, Debug, Default, PartialEq)]
4pub struct Vec2 {
5    pub x: f64,
6    pub y: f64,
7}
8
9/// Calculates the global world-space position of a node within a [`NodeTree`](crate::NodeTree).
10///
11/// Computes global position by accumulating local `position` vectors along the ancestor
12/// chain from the target node up to the root. Nodes lacking a `position` property are
13/// treated as having an offset of `(0.0, 0.0)`. Moving a parent node automatically carries
14/// all of its descendants.
15pub fn global_position(tree: &crate::NodeTree, id: &crate::NodeId) -> (f64, f64) {
16    let mut x = 0.0;
17    let mut y = 0.0;
18    let mut cursor = tree.get(id);
19    while let Some(node) = cursor {
20        if let Some(crate::PropValue::Vec2(px, py)) = node.props.get("position") {
21            x += px;
22            y += py;
23        }
24        cursor = node.parent.as_ref().and_then(|p| tree.get(p));
25    }
26    (x, y)
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32    use crate::{NodeDesc, NodeId, NodeTree, PropValue, Props};
33
34    fn placed(id: &str, parent: Option<&str>, pos: Option<(f64, f64)>) -> NodeDesc {
35        let mut d = NodeDesc::new(NodeId::from(id.to_string()), "Node2D");
36        d.name = id.to_string();
37        d.parent = parent.map(|p| NodeId::from(p.to_string()));
38        let mut props = Props::default();
39        if let Some((x, y)) = pos {
40            props.insert("position", PropValue::Vec2(x, y));
41        }
42        d.props = props;
43        d
44    }
45
46    #[test]
47    fn child_follows_moving_parent() {
48        let mut tree = NodeTree::new();
49        tree.insert(placed("root", None, Some((10.0, 0.0))))
50            .unwrap();
51        tree.insert(placed("child", Some("root"), Some((5.0, 0.0))))
52            .unwrap();
53        tree.insert(placed("grand", Some("child"), None)).unwrap();
54        assert_eq!(
55            global_position(&tree, &NodeId::from("grand".to_string())),
56            (15.0, 0.0)
57        );
58        let root_id = NodeId::from("root".to_string());
59        tree.get_mut(&root_id)
60            .unwrap()
61            .props
62            .insert("position", PropValue::Vec2(100.0, 0.0));
63        assert_eq!(
64            global_position(&tree, &NodeId::from("grand".to_string())),
65            (105.0, 0.0)
66        );
67    }
68}