1use crate::schema::IfcType;
11use crate::parser::Token;
12use crate::error::{Error, Result};
13use std::collections::HashMap;
14
15#[derive(Debug, Clone)]
17pub enum AttributeValue {
18 EntityRef(u32),
20 String(String),
22 Integer(i64),
24 Float(f64),
26 Enum(String),
28 List(Vec<AttributeValue>),
30 Null,
32 Derived,
34}
35
36impl AttributeValue {
37 pub fn from_token(token: &Token) -> Self {
39 match token {
40 Token::EntityRef(id) => AttributeValue::EntityRef(*id),
41 Token::String(s) => AttributeValue::String(s.to_string()),
42 Token::Integer(i) => AttributeValue::Integer(*i),
43 Token::Float(f) => AttributeValue::Float(*f),
44 Token::Enum(e) => AttributeValue::Enum(e.to_string()),
45 Token::List(items) => {
46 AttributeValue::List(items.iter().map(Self::from_token).collect())
47 }
48 Token::TypedValue(type_name, args) => {
49 let mut values = vec![AttributeValue::String(type_name.to_string())];
52 values.extend(args.iter().map(Self::from_token));
53 AttributeValue::List(values)
54 }
55 Token::Null => AttributeValue::Null,
56 Token::Derived => AttributeValue::Derived,
57 }
58 }
59
60 #[inline]
62 pub fn as_entity_ref(&self) -> Option<u32> {
63 match self {
64 AttributeValue::EntityRef(id) => Some(*id),
65 _ => None,
66 }
67 }
68
69 #[inline]
71 pub fn as_string(&self) -> Option<&str> {
72 match self {
73 AttributeValue::String(s) => Some(s),
74 _ => None,
75 }
76 }
77
78 #[inline]
80 pub fn as_float(&self) -> Option<f64> {
81 match self {
82 AttributeValue::Float(f) => Some(*f),
83 AttributeValue::Integer(i) => Some(*i as f64),
84 _ => None,
85 }
86 }
87
88 #[inline]
90 pub fn as_int(&self) -> Option<i64> {
91 match self {
92 AttributeValue::Integer(i) => Some(*i),
93 AttributeValue::Float(f) => Some(*f as i64),
94 _ => None,
95 }
96 }
97
98 #[inline]
100 pub fn as_list(&self) -> Option<&[AttributeValue]> {
101 match self {
102 AttributeValue::List(items) => Some(items),
103 _ => None,
104 }
105 }
106
107 #[inline]
109 pub fn is_null(&self) -> bool {
110 matches!(self, AttributeValue::Null | AttributeValue::Derived)
111 }
112
113 #[inline]
117 pub fn parse_coordinate_list_3d(coord_list: &[AttributeValue]) -> Vec<f32> {
118 let mut result = Vec::with_capacity(coord_list.len() * 3);
119
120 for coord_attr in coord_list {
121 if let Some(coord) = coord_attr.as_list() {
122 let x = coord.get(0).and_then(|v| v.as_float()).unwrap_or(0.0) as f32;
124 let y = coord.get(1).and_then(|v| v.as_float()).unwrap_or(0.0) as f32;
125 let z = coord.get(2).and_then(|v| v.as_float()).unwrap_or(0.0) as f32;
126
127 result.push(x);
128 result.push(y);
129 result.push(z);
130 }
131 }
132
133 result
134 }
135
136 #[inline]
139 pub fn parse_coordinate_list_2d(coord_list: &[AttributeValue]) -> Vec<f32> {
140 let mut result = Vec::with_capacity(coord_list.len() * 2);
141
142 for coord_attr in coord_list {
143 if let Some(coord) = coord_attr.as_list() {
144 let x = coord.get(0).and_then(|v| v.as_float()).unwrap_or(0.0) as f32;
145 let y = coord.get(1).and_then(|v| v.as_float()).unwrap_or(0.0) as f32;
146
147 result.push(x);
148 result.push(y);
149 }
150 }
151
152 result
153 }
154
155 #[inline]
159 pub fn parse_index_list(face_list: &[AttributeValue]) -> Vec<u32> {
160 let mut result = Vec::with_capacity(face_list.len() * 3);
161
162 for face_attr in face_list {
163 if let Some(face) = face_attr.as_list() {
164 let i0 = (face.get(0).and_then(|v| v.as_int()).unwrap_or(1) - 1) as u32;
166 let i1 = (face.get(1).and_then(|v| v.as_int()).unwrap_or(1) - 1) as u32;
167 let i2 = (face.get(2).and_then(|v| v.as_int()).unwrap_or(1) - 1) as u32;
168
169 result.push(i0);
170 result.push(i1);
171 result.push(i2);
172 }
173 }
174
175 result
176 }
177
178 #[inline]
181 pub fn parse_coordinate_list_3d_f64(coord_list: &[AttributeValue]) -> Vec<(f64, f64, f64)> {
182 coord_list.iter()
183 .filter_map(|coord_attr| {
184 let coord = coord_attr.as_list()?;
185 let x = coord.get(0).and_then(|v| v.as_float()).unwrap_or(0.0);
186 let y = coord.get(1).and_then(|v| v.as_float()).unwrap_or(0.0);
187 let z = coord.get(2).and_then(|v| v.as_float()).unwrap_or(0.0);
188 Some((x, y, z))
189 })
190 .collect()
191 }
192}
193
194#[derive(Debug, Clone)]
196pub struct DecodedEntity {
197 pub id: u32,
198 pub ifc_type: IfcType,
199 pub attributes: Vec<AttributeValue>,
200}
201
202impl DecodedEntity {
203 pub fn new(id: u32, ifc_type: IfcType, attributes: Vec<AttributeValue>) -> Self {
205 Self {
206 id,
207 ifc_type,
208 attributes,
209 }
210 }
211
212 pub fn get(&self, index: usize) -> Option<&AttributeValue> {
214 self.attributes.get(index)
215 }
216
217 pub fn get_ref(&self, index: usize) -> Option<u32> {
219 self.get(index).and_then(|v| v.as_entity_ref())
220 }
221
222 pub fn get_string(&self, index: usize) -> Option<&str> {
224 self.get(index).and_then(|v| v.as_string())
225 }
226
227 pub fn get_float(&self, index: usize) -> Option<f64> {
229 self.get(index).and_then(|v| v.as_float())
230 }
231
232 pub fn get_list(&self, index: usize) -> Option<&[AttributeValue]> {
234 self.get(index).and_then(|v| v.as_list())
235 }
236}
237
238#[derive(Clone)]
240pub struct IfcSchema {
241 pub geometry_types: HashMap<IfcType, GeometryCategory>,
243 pub profile_types: HashMap<IfcType, ProfileCategory>,
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq)]
249pub enum GeometryCategory {
250 SweptSolid,
252 Boolean,
254 ExplicitMesh,
256 MappedItem,
258 Other,
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub enum ProfileCategory {
265 Parametric,
267 Arbitrary,
269 Composite,
271 Other,
273}
274
275impl IfcSchema {
276 pub fn new() -> Self {
278 let mut geometry_types = HashMap::new();
279 let mut profile_types = HashMap::new();
280
281 geometry_types.insert(IfcType::IfcExtrudedAreaSolid, GeometryCategory::SweptSolid);
283 geometry_types.insert(IfcType::IfcRevolvedAreaSolid, GeometryCategory::SweptSolid);
284
285 geometry_types.insert(IfcType::IfcBooleanResult, GeometryCategory::Boolean);
287 geometry_types.insert(IfcType::IfcBooleanClippingResult, GeometryCategory::Boolean);
288
289 geometry_types.insert(IfcType::IfcFacetedBrep, GeometryCategory::ExplicitMesh);
291 geometry_types.insert(IfcType::IfcTriangulatedFaceSet, GeometryCategory::ExplicitMesh);
292 geometry_types.insert(IfcType::IfcPolygonalFaceSet, GeometryCategory::ExplicitMesh);
293
294 geometry_types.insert(IfcType::IfcMappedItem, GeometryCategory::MappedItem);
296
297 profile_types.insert(IfcType::IfcRectangleProfileDef, ProfileCategory::Parametric);
299 profile_types.insert(IfcType::IfcCircleProfileDef, ProfileCategory::Parametric);
300 profile_types.insert(IfcType::IfcCircleHollowProfileDef, ProfileCategory::Parametric);
301 profile_types.insert(IfcType::IfcRectangleHollowProfileDef, ProfileCategory::Parametric);
302 profile_types.insert(IfcType::IfcIShapeProfileDef, ProfileCategory::Parametric);
303 profile_types.insert(IfcType::IfcLShapeProfileDef, ProfileCategory::Parametric);
304 profile_types.insert(IfcType::IfcUShapeProfileDef, ProfileCategory::Parametric);
305 profile_types.insert(IfcType::IfcTShapeProfileDef, ProfileCategory::Parametric);
306 profile_types.insert(IfcType::IfcCShapeProfileDef, ProfileCategory::Parametric);
307 profile_types.insert(IfcType::IfcZShapeProfileDef, ProfileCategory::Parametric);
308
309 profile_types.insert(IfcType::IfcArbitraryClosedProfileDef, ProfileCategory::Arbitrary);
311 profile_types.insert(IfcType::IfcArbitraryProfileDefWithVoids, ProfileCategory::Arbitrary);
312
313 profile_types.insert(IfcType::IfcCompositeProfileDef, ProfileCategory::Composite);
315
316 Self {
317 geometry_types,
318 profile_types,
319 }
320 }
321
322 pub fn geometry_category(&self, ifc_type: &IfcType) -> Option<GeometryCategory> {
324 self.geometry_types.get(ifc_type).copied()
325 }
326
327 pub fn profile_category(&self, ifc_type: &IfcType) -> Option<ProfileCategory> {
329 self.profile_types.get(ifc_type).copied()
330 }
331
332 pub fn is_geometry_type(&self, ifc_type: &IfcType) -> bool {
334 self.geometry_types.contains_key(ifc_type)
335 }
336
337 pub fn is_profile_type(&self, ifc_type: &IfcType) -> bool {
339 self.profile_types.contains_key(ifc_type)
340 }
341
342 pub fn has_geometry(&self, ifc_type: &IfcType) -> bool {
344 ifc_type.is_building_element() ||
346 matches!(
347 ifc_type,
348 IfcType::IfcFurnishingElement
349 | IfcType::IfcFurniture
350 | IfcType::IfcDuctSegment
351 | IfcType::IfcPipeSegment
352 | IfcType::IfcCableSegment
353 | IfcType::IfcProxy | IfcType::IfcProduct | IfcType::IfcDistributionElement
356 | IfcType::IfcFlowSegment
357 | IfcType::IfcFlowFitting
358 | IfcType::IfcFlowTerminal
359 )
360 }
361}
362
363impl Default for IfcSchema {
364 fn default() -> Self {
365 Self::new()
366 }
367}
368
369#[cfg(test)]
373mod tests {
374 use super::*;
375
376 #[test]
377 fn test_schema_geometry_categories() {
378 let schema = IfcSchema::new();
379
380 assert_eq!(
381 schema.geometry_category(&IfcType::IfcExtrudedAreaSolid),
382 Some(GeometryCategory::SweptSolid)
383 );
384
385 assert_eq!(
386 schema.geometry_category(&IfcType::IfcBooleanResult),
387 Some(GeometryCategory::Boolean)
388 );
389
390 assert_eq!(
391 schema.geometry_category(&IfcType::IfcTriangulatedFaceSet),
392 Some(GeometryCategory::ExplicitMesh)
393 );
394 }
395
396 #[test]
397 fn test_attribute_value_conversion() {
398 let token = Token::EntityRef(123);
399 let attr = AttributeValue::from_token(&token);
400 assert_eq!(attr.as_entity_ref(), Some(123));
401
402 let token = Token::String("test");
403 let attr = AttributeValue::from_token(&token);
404 assert_eq!(attr.as_string(), Some("test"));
405 }
406
407 #[test]
408 fn test_decoded_entity() {
409 let entity = DecodedEntity::new(
410 1,
411 IfcType::IfcWall,
412 vec![
413 AttributeValue::EntityRef(2),
414 AttributeValue::String("Wall-001".to_string()),
415 AttributeValue::Float(3.5),
416 ],
417 );
418
419 assert_eq!(entity.get_ref(0), Some(2));
420 assert_eq!(entity.get_string(1), Some("Wall-001"));
421 assert_eq!(entity.get_float(2), Some(3.5));
422 }
423}