ifc_lite_geometry/processors/tessellated/
polygonal.rs1use crate::{Error, Mesh, Result, TessellationQuality};
6use ifc_lite_core::{AttributeValue, DecodedEntity, EntityDecoder, IfcSchema, IfcType};
7
8use crate::router::GeometryProcessor;
9
10pub struct PolygonalFaceSetProcessor;
13
14impl PolygonalFaceSetProcessor {
15 pub fn new() -> Self {
16 Self
17 }
18
19 #[inline]
20 fn parse_index_loop(indices: &[AttributeValue], pn_index: Option<&[u32]>) -> Vec<u32> {
21 indices
22 .iter()
23 .filter_map(|value| {
24 let idx = value.as_int()?;
25 if idx <= 0 {
26 return None;
27 }
28 let idx = idx as usize;
29
30 if let Some(remap) = pn_index {
31 remap.get(idx - 1).copied().filter(|mapped| *mapped > 0)
32 } else {
33 Some(idx as u32)
34 }
35 })
36 .collect()
37 }
38
39 #[inline]
40 fn parse_face_inner_indices(
41 face_entity: &DecodedEntity,
42 pn_index: Option<&[u32]>,
43 ) -> Vec<Vec<u32>> {
44 if face_entity.ifc_type != IfcType::IfcIndexedPolygonalFaceWithVoids {
45 return Vec::new();
46 }
47
48 let Some(inner_attr) = face_entity.get(1).and_then(|a| a.as_list()) else {
49 return Vec::new();
50 };
51
52 let mut result = Vec::with_capacity(inner_attr.len());
53 for loop_attr in inner_attr {
54 let Some(loop_values) = loop_attr.as_list() else {
55 continue;
56 };
57 let parsed = Self::parse_index_loop(loop_values, pn_index);
58 if parsed.len() >= 3 {
59 result.push(parsed);
60 }
61 }
62
63 result
64 }
65}
66
67impl GeometryProcessor for PolygonalFaceSetProcessor {
68 fn process(
69 &self,
70 entity: &DecodedEntity,
71 decoder: &mut EntityDecoder,
72 _schema: &IfcSchema,
73 _quality: TessellationQuality,
74 ) -> Result<Mesh> {
75 let coords_attr = entity
83 .get(0)
84 .ok_or_else(|| Error::geometry("PolygonalFaceSet missing Coordinates".to_string()))?;
85
86 let coord_entity_id = coords_attr.as_entity_ref().ok_or_else(|| {
87 Error::geometry("Expected entity reference for Coordinates".to_string())
88 })?;
89
90 use ifc_lite_core::extract_coordinate_list_from_entity;
92
93 let positions = if let Some(raw_bytes) = decoder.get_raw_bytes(coord_entity_id) {
94 extract_coordinate_list_from_entity(raw_bytes).unwrap_or_default()
95 } else {
96 let coords_entity = decoder.decode_by_id(coord_entity_id)?;
98 let coord_list_attr = coords_entity.get(0).ok_or_else(|| {
99 Error::geometry("CartesianPointList3D missing CoordList".to_string())
100 })?;
101 let coord_list = coord_list_attr
102 .as_list()
103 .ok_or_else(|| Error::geometry("Expected coordinate list".to_string()))?;
104 AttributeValue::parse_coordinate_list_3d(coord_list)
105 };
106
107 if positions.is_empty() {
108 return Ok(Mesh::new());
109 }
110
111 let faces_attr = entity
113 .get(2)
114 .ok_or_else(|| Error::geometry("PolygonalFaceSet missing Faces".to_string()))?;
115
116 let face_refs = faces_attr
117 .as_list()
118 .ok_or_else(|| Error::geometry("Expected faces list".to_string()))?;
119
120 let pn_index = entity.get(3).and_then(|attr| attr.as_list()).map(|list| {
123 list.iter()
124 .filter_map(|value| value.as_int())
125 .filter(|v| *v > 0)
126 .map(|v| v as u32)
127 .collect::<Vec<u32>>()
128 });
129
130 let mut indices = Vec::with_capacity(face_refs.len() * 6);
132
133 for face_ref in face_refs {
135 let face_id = face_ref
136 .as_entity_ref()
137 .ok_or_else(|| Error::geometry("Expected entity reference for face".to_string()))?;
138
139 let face_entity = decoder.decode_by_id(face_id)?;
140
141 let coord_index_attr = face_entity.get(0).ok_or_else(|| {
144 Error::geometry("IndexedPolygonalFace missing CoordIndex".to_string())
145 })?;
146
147 let coord_indices = coord_index_attr
148 .as_list()
149 .ok_or_else(|| Error::geometry("Expected coord index list".to_string()))?;
150
151 let face_indices = Self::parse_index_loop(coord_indices, pn_index.as_deref());
153 if face_indices.len() < 3 {
154 continue;
155 }
156
157 let inner_indices = Self::parse_face_inner_indices(&face_entity, pn_index.as_deref());
159
160 Self::triangulate_polygon(&face_indices, &inner_indices, &positions, &mut indices);
162 }
163
164 let is_closed = entity
167 .get(1)
168 .and_then(|a| a.as_enum())
169 .map(|v| v == "T")
170 .unwrap_or(false);
171 if is_closed {
172 Self::orient_closed_shell_outward(&positions, &mut indices);
173 }
174
175 Ok(Self::build_flat_shaded_mesh(&positions, &indices))
176 }
177
178 fn supported_types(&self) -> Vec<IfcType> {
179 vec![IfcType::IfcPolygonalFaceSet]
180 }
181}
182
183impl Default for PolygonalFaceSetProcessor {
184 fn default() -> Self {
185 Self::new()
186 }
187}