Skip to main content

pite_core/
math.rs

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