Skip to main content

fbx_dom/objects/
limb_node.rs

1//! FBX `NodeAttribute` / `LimbNode` — Assimp [`LimbNode`](https://github.com/assimp/assimp/blob/master/code/AssetLib/FBX/FBXDocument.h).
2
3use std::collections::HashMap;
4use std::convert::TryFrom;
5
6use crate::{OwnedObject, Property};
7
8use super::{FbxObjectTag, FbxTypeMismatch, fbx_object_tag};
9
10const PROP_LIMB_LENGTH: &str = "LimbLength";
11const PROP_SIZE: &str = "Size";
12
13#[derive(Debug, PartialEq)]
14pub struct LimbNode(pub OwnedObject);
15
16impl LimbNode {
17    pub fn inner(&self) -> &OwnedObject {
18        &self.0
19    }
20
21    pub fn into_inner(self) -> OwnedObject {
22        self.0
23    }
24
25    /// Temporary bridge to Assimp-style node-attribute properties until typed accessors are added.
26    pub fn properties(&self) -> &HashMap<String, Property> {
27        &self.0.properties
28    }
29
30    pub fn property(&self, name: &str) -> Option<&Property> {
31        self.0.properties.get(name)
32    }
33
34    /// Common skeleton length property used by some exporters.
35    pub fn limb_length(&self) -> f32 {
36        match self.property(PROP_LIMB_LENGTH) {
37            Some(Property::Float(v)) => *v,
38            _ => 0.0,
39        }
40    }
41
42    /// Marker/display size value when present on limb-node properties.
43    pub fn size(&self) -> f32 {
44        match self.property(PROP_SIZE) {
45            Some(Property::Float(v)) => *v,
46            _ => 0.0,
47        }
48    }
49}
50
51impl TryFrom<OwnedObject> for LimbNode {
52    type Error = FbxTypeMismatch;
53
54    fn try_from(o: OwnedObject) -> Result<Self, Self::Error> {
55        match fbx_object_tag(&o) {
56            FbxObjectTag::LimbNode => Ok(LimbNode(o)),
57            _ => Err(FbxTypeMismatch::wrong_object_kind(
58                o,
59                "LimbNode".to_string(),
60            )),
61        }
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use std::collections::HashMap;
68    use std::convert::TryFrom;
69
70    use crate::objects::{NODE_ATTRIBUTE_LIMB_NODE_CLASS_NAME, NODE_ATTRIBUTE_TYPE_NAME};
71    use crate::{OwnedObject, Property};
72
73    use super::LimbNode;
74
75    #[test]
76    fn property_accessors_return_owned_object_properties() {
77        let mut properties = HashMap::new();
78        properties.insert("LimbLength".to_string(), Property::Float(10.0));
79        let o = OwnedObject {
80            object_index: 99,
81            name: "Limb".into(),
82            type_name: NODE_ATTRIBUTE_TYPE_NAME.into(),
83            class_name: NODE_ATTRIBUTE_LIMB_NODE_CLASS_NAME.into(),
84            properties,
85            attributes: HashMap::new(),
86            connected_object_ids: vec![],
87            object_property_targets: vec![],
88            pp_property_targets: HashMap::new(),
89        };
90        let limb_node = LimbNode::try_from(o).unwrap();
91        assert_eq!(
92            limb_node.property("LimbLength"),
93            Some(&Property::Float(10.0))
94        );
95        assert_eq!(limb_node.properties().len(), 1);
96    }
97
98    #[test]
99    fn typed_accessors_return_defaults_and_values() {
100        let o_default = OwnedObject {
101            object_index: 99,
102            name: "Limb".into(),
103            type_name: NODE_ATTRIBUTE_TYPE_NAME.into(),
104            class_name: NODE_ATTRIBUTE_LIMB_NODE_CLASS_NAME.into(),
105            properties: HashMap::new(),
106            attributes: HashMap::new(),
107            connected_object_ids: vec![],
108            object_property_targets: vec![],
109            pp_property_targets: HashMap::new(),
110        };
111        let limb_default = LimbNode::try_from(o_default).unwrap();
112        assert_eq!(limb_default.limb_length(), 0.0);
113        assert_eq!(limb_default.size(), 0.0);
114
115        let mut properties = HashMap::new();
116        properties.insert("LimbLength".to_string(), Property::Float(8.5));
117        properties.insert("Size".to_string(), Property::Float(2.0));
118        let o_values = OwnedObject {
119            object_index: 100,
120            name: "Limb".into(),
121            type_name: NODE_ATTRIBUTE_TYPE_NAME.into(),
122            class_name: NODE_ATTRIBUTE_LIMB_NODE_CLASS_NAME.into(),
123            properties,
124            attributes: HashMap::new(),
125            connected_object_ids: vec![],
126            object_property_targets: vec![],
127            pp_property_targets: HashMap::new(),
128        };
129        let limb_values = LimbNode::try_from(o_values).unwrap();
130        assert_eq!(limb_values.limb_length(), 8.5);
131        assert_eq!(limb_values.size(), 2.0);
132    }
133}