fbx_dom/objects/
line_geometry.rs1use std::convert::TryFrom;
4use std::num::ParseFloatError;
5use std::num::ParseIntError;
6
7use crate::OwnedObject;
8
9use super::AttrExtractor;
10use super::{FbxObjectTag, FbxTryFromReason, FbxTypeMismatch, fbx_object_tag};
11
12#[derive(Debug, PartialEq)]
13pub struct LineGeometry {
14 pub object: OwnedObject,
15 pub points: Vec<[f32; 3]>,
16 pub point_indices: Vec<i32>,
17}
18
19impl LineGeometry {
20 pub fn inner(&self) -> &OwnedObject {
21 &self.object
22 }
23
24 pub fn into_inner(self) -> OwnedObject {
25 self.object
26 }
27}
28
29impl TryFrom<OwnedObject> for LineGeometry {
30 type Error = FbxTypeMismatch;
31
32 fn try_from(o: OwnedObject) -> Result<Self, Self::Error> {
33 match fbx_object_tag(&o) {
34 FbxObjectTag::LineGeometry => {}
35 _ => {
36 return Err(FbxTypeMismatch::wrong_object_kind(
37 o,
38 "LineGeometry".to_string(),
39 ));
40 }
41 }
42
43 let attrs = &o.attributes;
44
45 let points_tokens_attr = match attrs.extract_case_insensitive("Points") {
46 Some(a) => a,
47 None => {
48 return Err(FbxTypeMismatch::new(
49 o,
50 FbxTryFromReason::MissingAttribute {
51 name: "Points".to_string(),
52 },
53 ));
54 }
55 };
56 let children = points_tokens_attr.get_children_distinct();
57 let payload = children.get("a").unwrap_or(points_tokens_attr);
58 let points_tokens = payload.get_tokens();
59 let points_result = points_tokens
60 .iter()
61 .flat_map(|t| t.split(','))
62 .map(|t| t.trim())
63 .filter(|t| !t.is_empty())
64 .map(|t| t.parse::<f32>())
65 .collect::<Result<Vec<f32>, ParseFloatError>>();
66 let Ok(points_unchunked) = points_result else {
67 return Err(FbxTypeMismatch::new(
68 o,
69 FbxTryFromReason::InvalidAttributeFormat {
70 name: "Points".to_string(),
71 detail: format!("invalid float token: {}", points_result.unwrap_err()),
72 },
73 ));
74 };
75 let points = points_unchunked
76 .chunks_exact(3)
77 .map(|c| [c[0], c[1], c[2]])
78 .collect::<Vec<[f32; 3]>>();
79
80 let idx_tokens = match attrs.extract_case_insensitive("PointsIndex") {
81 Some(a) => a,
82 None => {
83 return Err(FbxTypeMismatch::new(
84 o,
85 FbxTryFromReason::MissingAttribute {
86 name: "PointsIndex".to_string(),
87 },
88 ));
89 }
90 };
91 let children = idx_tokens.get_children_distinct();
92 let payload = children.get("a").unwrap_or(idx_tokens);
93 let idx_tokens = payload.get_tokens();
94 let point_indices_result = idx_tokens
95 .iter()
96 .flat_map(|t| t.split(','))
97 .map(|t| t.trim())
98 .filter(|t| !t.is_empty())
99 .map(|t| t.parse::<i32>())
100 .collect::<Result<Vec<i32>, ParseIntError>>();
101 let Ok(point_indices) = point_indices_result else {
102 return Err(FbxTypeMismatch::new(
103 o,
104 FbxTryFromReason::InvalidAttributeFormat {
105 name: "PointsIndex".to_string(),
106 detail: format!("invalid int token: {}", point_indices_result.unwrap_err()),
107 },
108 ));
109 };
110
111 Ok(LineGeometry {
112 object: o,
113 points,
114 point_indices,
115 })
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use std::{collections::HashMap, convert::TryFrom};
122
123 use fbxscii::{ElementAttribute, LeafAttribute};
124
125 use crate::OwnedObject;
126
127 use super::super::{GEOMETRY_LINE_CLASS_NAME, GEOMETRY_TYPE_NAME};
128 use super::LineGeometry;
129
130 fn leaf(tokens: &[&str]) -> ElementAttribute {
131 ElementAttribute::Leaf(Box::new(LeafAttribute {
132 key: String::new(),
133 tokens: tokens.iter().map(|s| (*s).to_string()).collect(),
134 }))
135 }
136
137 fn owned_line(attrs: HashMap<String, ElementAttribute>) -> OwnedObject {
138 OwnedObject {
139 object_index: 10,
140 name: "Geometry::TestLine".into(),
141 type_name: GEOMETRY_TYPE_NAME.into(),
142 class_name: GEOMETRY_LINE_CLASS_NAME.into(),
143 properties: HashMap::new(),
144 attributes: attrs,
145 connected_object_ids: vec![],
146 object_property_targets: vec![],
147 pp_property_targets: HashMap::new(),
148 }
149 }
150 #[test]
151 fn basic_parse() {
152 let mut attrs = HashMap::new();
153 attrs.insert("Points".into(), leaf(&["0,0,0,1,0,0,0,1,0"]));
154 attrs.insert("PointsIndex".into(), leaf(&["0,1,2"]));
155 let o = owned_line(attrs);
156 let lg = LineGeometry::try_from(o).unwrap();
157 let expected_points = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
158 let expected_point_indices = vec![0, 1, 2];
159 assert_eq!(lg.points, expected_points);
160 assert_eq!(lg.point_indices, expected_point_indices);
161 }
162}