ifc_lite_geometry/processors/
tessellated.rs1use crate::{Error, Mesh, Result};
11use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcSchema, IfcType};
12
13use crate::router::GeometryProcessor;
14
15pub struct TriangulatedFaceSetProcessor;
18
19impl TriangulatedFaceSetProcessor {
20 pub fn new() -> Self {
21 Self
22 }
23}
24
25impl GeometryProcessor for TriangulatedFaceSetProcessor {
26 #[inline]
27 fn process(
28 &self,
29 entity: &DecodedEntity,
30 decoder: &mut EntityDecoder,
31 _schema: &IfcSchema,
32 ) -> Result<Mesh> {
33 let coords_attr = entity.get(0).ok_or_else(|| {
41 Error::geometry("TriangulatedFaceSet missing Coordinates".to_string())
42 })?;
43
44 let coord_entity_id = coords_attr.as_entity_ref().ok_or_else(|| {
45 Error::geometry("Expected entity reference for Coordinates".to_string())
46 })?;
47
48 use ifc_lite_core::{extract_coordinate_list_from_entity, parse_indices_direct};
51
52 let positions = if let Some(raw_bytes) = decoder.get_raw_bytes(coord_entity_id) {
53 extract_coordinate_list_from_entity(raw_bytes).unwrap_or_default()
56 } else {
57 let coords_entity = decoder.decode_by_id(coord_entity_id)?;
59
60 let coord_list_attr = coords_entity.get(0).ok_or_else(|| {
61 Error::geometry("CartesianPointList3D missing CoordList".to_string())
62 })?;
63
64 let coord_list = coord_list_attr
65 .as_list()
66 .ok_or_else(|| Error::geometry("Expected coordinate list".to_string()))?;
67
68 use ifc_lite_core::AttributeValue;
69 AttributeValue::parse_coordinate_list_3d(coord_list)
70 };
71
72 let indices_attr = entity
74 .get(3)
75 .ok_or_else(|| Error::geometry("TriangulatedFaceSet missing CoordIndex".to_string()))?;
76
77 let indices = if let Some(raw_entity_bytes) = decoder.get_raw_bytes(entity.id) {
80 if let Some(coord_index_bytes) = super::extract_coord_index_bytes(raw_entity_bytes) {
83 parse_indices_direct(coord_index_bytes)
84 } else {
85 let face_list = indices_attr
87 .as_list()
88 .ok_or_else(|| Error::geometry("Expected face index list".to_string()))?;
89 use ifc_lite_core::AttributeValue;
90 AttributeValue::parse_index_list(face_list)
91 }
92 } else {
93 let face_list = indices_attr
94 .as_list()
95 .ok_or_else(|| Error::geometry("Expected face index list".to_string()))?;
96 use ifc_lite_core::AttributeValue;
97 AttributeValue::parse_index_list(face_list)
98 };
99
100 Ok(Mesh {
102 positions,
103 normals: Vec::new(),
104 indices,
105 })
106 }
107
108 fn supported_types(&self) -> Vec<IfcType> {
109 vec![IfcType::IfcTriangulatedFaceSet]
110 }
111}
112
113impl Default for TriangulatedFaceSetProcessor {
114 fn default() -> Self {
115 Self::new()
116 }
117}
118
119pub struct PolygonalFaceSetProcessor;
122
123impl PolygonalFaceSetProcessor {
124 pub fn new() -> Self {
125 Self
126 }
127
128 fn triangulate_polygon(
133 face_indices: &[u32],
134 positions: &[f32],
135 output: &mut Vec<u32>,
136 ) {
137 if face_indices.len() < 3 {
138 return;
139 }
140
141 if face_indices.len() == 3 {
143 output.push(face_indices[0] - 1);
144 output.push(face_indices[1] - 1);
145 output.push(face_indices[2] - 1);
146 return;
147 }
148
149 if face_indices.len() == 4 {
151 let first = face_indices[0] - 1;
152 output.push(first);
153 output.push(face_indices[1] - 1);
154 output.push(face_indices[2] - 1);
155 output.push(first);
156 output.push(face_indices[2] - 1);
157 output.push(face_indices[3] - 1);
158 return;
159 }
160
161 let get_pos = |idx: u32| -> Option<(f32, f32, f32)> {
163 let base = ((idx - 1) * 3) as usize;
164 if base + 2 < positions.len() {
165 Some((positions[base], positions[base + 1], positions[base + 2]))
166 } else {
167 None
168 }
169 };
170
171 let mut sum_x = 0.0f64;
177 let mut sum_y = 0.0f64;
178 let mut sum_z = 0.0f64;
179
180 for i in 0..face_indices.len() {
182 let v0 = match get_pos(face_indices[i]) {
183 Some(p) => p,
184 None => {
185 let first = face_indices[0] - 1;
187 for j in 1..face_indices.len() - 1 {
188 output.push(first);
189 output.push(face_indices[j] - 1);
190 output.push(face_indices[j + 1] - 1);
191 }
192 return;
193 }
194 };
195 let v1 = match get_pos(face_indices[(i + 1) % face_indices.len()]) {
196 Some(p) => p,
197 None => {
198 let first = face_indices[0] - 1;
199 for j in 1..face_indices.len() - 1 {
200 output.push(first);
201 output.push(face_indices[j] - 1);
202 output.push(face_indices[j + 1] - 1);
203 }
204 return;
205 }
206 };
207
208 sum_x += (v0.1 - v1.1) as f64 * (v0.2 + v1.2) as f64;
209 sum_y += (v0.2 - v1.2) as f64 * (v0.0 + v1.0) as f64;
210 sum_z += (v0.0 - v1.0) as f64 * (v0.1 + v1.1) as f64;
211 }
212
213 let abs_x = sum_x.abs();
215 let abs_y = sum_y.abs();
216 let abs_z = sum_z.abs();
217
218 let mut coords_2d: Vec<f64> = Vec::with_capacity(face_indices.len() * 2);
220
221 for &idx in face_indices {
222 let p = match get_pos(idx) {
223 Some(pos) => pos,
224 None => {
225 let first = face_indices[0] - 1;
227 for j in 1..face_indices.len() - 1 {
228 output.push(first);
229 output.push(face_indices[j] - 1);
230 output.push(face_indices[j + 1] - 1);
231 }
232 return;
233 }
234 };
235
236 if abs_z >= abs_x && abs_z >= abs_y {
238 coords_2d.push(p.0 as f64);
240 coords_2d.push(p.1 as f64);
241 } else if abs_y >= abs_x {
242 coords_2d.push(p.0 as f64);
244 coords_2d.push(p.2 as f64);
245 } else {
246 coords_2d.push(p.1 as f64);
248 coords_2d.push(p.2 as f64);
249 }
250 }
251
252 let hole_indices: Vec<usize> = vec![]; match earcutr::earcut(&coords_2d, &hole_indices, 2) {
255 Ok(tri_indices) => {
256 for tri_idx in tri_indices {
258 if tri_idx < face_indices.len() {
259 output.push(face_indices[tri_idx] - 1);
260 }
261 }
262 }
263 Err(_) => {
264 let first = face_indices[0] - 1;
266 for i in 1..face_indices.len() - 1 {
267 output.push(first);
268 output.push(face_indices[i] - 1);
269 output.push(face_indices[i + 1] - 1);
270 }
271 }
272 }
273 }
274}
275
276impl GeometryProcessor for PolygonalFaceSetProcessor {
277 fn process(
278 &self,
279 entity: &DecodedEntity,
280 decoder: &mut EntityDecoder,
281 _schema: &IfcSchema,
282 ) -> Result<Mesh> {
283 let coords_attr = entity.get(0).ok_or_else(|| {
291 Error::geometry("PolygonalFaceSet missing Coordinates".to_string())
292 })?;
293
294 let coord_entity_id = coords_attr.as_entity_ref().ok_or_else(|| {
295 Error::geometry("Expected entity reference for Coordinates".to_string())
296 })?;
297
298 use ifc_lite_core::extract_coordinate_list_from_entity;
300
301 let positions = if let Some(raw_bytes) = decoder.get_raw_bytes(coord_entity_id) {
302 extract_coordinate_list_from_entity(raw_bytes).unwrap_or_default()
303 } else {
304 let coords_entity = decoder.decode_by_id(coord_entity_id)?;
306 let coord_list_attr = coords_entity.get(0).ok_or_else(|| {
307 Error::geometry("CartesianPointList3D missing CoordList".to_string())
308 })?;
309 let coord_list = coord_list_attr
310 .as_list()
311 .ok_or_else(|| Error::geometry("Expected coordinate list".to_string()))?;
312 use ifc_lite_core::AttributeValue;
313 AttributeValue::parse_coordinate_list_3d(coord_list)
314 };
315
316 if positions.is_empty() {
317 return Ok(Mesh::new());
318 }
319
320 let faces_attr = entity.get(2).ok_or_else(|| {
322 Error::geometry("PolygonalFaceSet missing Faces".to_string())
323 })?;
324
325 let face_refs = faces_attr
326 .as_list()
327 .ok_or_else(|| Error::geometry("Expected faces list".to_string()))?;
328
329 let mut indices = Vec::with_capacity(face_refs.len() * 6);
331
332 for face_ref in face_refs {
334 let face_id = face_ref.as_entity_ref().ok_or_else(|| {
335 Error::geometry("Expected entity reference for face".to_string())
336 })?;
337
338 let face_entity = decoder.decode_by_id(face_id)?;
339
340 let coord_index_attr = face_entity.get(0).ok_or_else(|| {
343 Error::geometry("IndexedPolygonalFace missing CoordIndex".to_string())
344 })?;
345
346 let coord_indices = coord_index_attr
347 .as_list()
348 .ok_or_else(|| Error::geometry("Expected coord index list".to_string()))?;
349
350 let face_indices: Vec<u32> = coord_indices
352 .iter()
353 .filter_map(|v| v.as_int().map(|i| i as u32))
354 .collect();
355
356 Self::triangulate_polygon(&face_indices, &positions, &mut indices);
358 }
359
360 Ok(Mesh {
361 positions,
362 normals: Vec::new(), indices,
364 })
365 }
366
367 fn supported_types(&self) -> Vec<IfcType> {
368 vec![IfcType::IfcPolygonalFaceSet]
369 }
370}
371
372impl Default for PolygonalFaceSetProcessor {
373 fn default() -> Self {
374 Self::new()
375 }
376}