ifc_lite_geometry/router/
processing.rs1use super::GeometryRouter;
8use crate::{Error, Mesh, Result, SubMeshCollection};
9use ifc_lite_core::{DecodedEntity, EntityDecoder, GeometryCategory, IfcType};
10use nalgebra::Matrix4;
11use std::sync::Arc;
12
13impl GeometryRouter {
14 fn rtc_offset_from_translations(translations: &[(f64, f64, f64)]) -> (f64, f64, f64) {
17 if translations.is_empty() {
18 return (0.0, 0.0, 0.0);
19 }
20
21 let mut x: Vec<f64> = translations.iter().map(|(x, _, _)| *x).collect();
22 let mut y: Vec<f64> = translations.iter().map(|(_, y, _)| *y).collect();
23 let mut z: Vec<f64> = translations.iter().map(|(_, _, z)| *z).collect();
24
25 x.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
26 y.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
27 z.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
28
29 let mid = x.len() / 2;
30 let centroid = (*x.get(mid).unwrap_or(&0.0), *y.get(mid).unwrap_or(&0.0), *z.get(mid).unwrap_or(&0.0));
31
32 const THRESHOLD: f64 = 10000.0;
33 if centroid.0.abs() > THRESHOLD
34 || centroid.1.abs() > THRESHOLD
35 || centroid.2.abs() > THRESHOLD
36 {
37 return centroid;
38 }
39
40 (0.0, 0.0, 0.0)
41 }
42
43 fn sample_element_translation(
46 &self,
47 entity: &DecodedEntity,
48 decoder: &mut EntityDecoder,
49 ) -> Option<(f64, f64, f64)> {
50 let has_rep = entity.get(6).map(|a| !a.is_null()).unwrap_or(false);
51 if !has_rep {
52 return None;
53 }
54 let mut transform = self
55 .get_placement_transform_from_element(entity, decoder)
56 .ok()?;
57 self.scale_transform(&mut transform);
58 let tx = transform[(0, 3)];
59 let ty = transform[(1, 3)];
60 let tz = transform[(2, 3)];
61 if tx.is_finite() && ty.is_finite() && tz.is_finite() {
62 Some((tx, ty, tz))
63 } else {
64 None
65 }
66 }
67
68 pub fn detect_rtc_offset_from_first_element(
71 &self,
72 content: &str,
73 decoder: &mut EntityDecoder,
74 ) -> (f64, f64, f64) {
75 use ifc_lite_core::EntityScanner;
76
77 let mut scanner = EntityScanner::new(content);
78 let mut translations: Vec<(f64, f64, f64)> = Vec::new();
79 const MAX_SAMPLES: usize = 50;
80
81 const BUILDING_ELEMENT_TYPES: &[&str] = &[
82 "IFCWALL", "IFCWALLSTANDARDCASE", "IFCSLAB", "IFCBEAM", "IFCCOLUMN",
83 "IFCPLATE", "IFCROOF", "IFCCOVERING", "IFCFOOTING", "IFCRAILING",
84 "IFCSTAIR", "IFCSTAIRFLIGHT", "IFCRAMP", "IFCRAMPFLIGHT",
85 "IFCDOOR", "IFCWINDOW", "IFCFURNISHINGELEMENT", "IFCBUILDINGELEMENTPROXY",
86 "IFCMEMBER", "IFCCURTAINWALL", "IFCPILE", "IFCSHADINGDEVICE",
87 ];
88
89 while let Some((_id, type_name, start, end)) = scanner.next_entity() {
90 if translations.len() >= MAX_SAMPLES {
91 break;
92 }
93 if !BUILDING_ELEMENT_TYPES.iter().any(|&t| t == type_name) {
94 continue;
95 }
96 if let Ok(entity) = decoder.decode_at(start, end) {
97 if let Some(t) = self.sample_element_translation(&entity, decoder) {
98 translations.push(t);
99 }
100 }
101 }
102
103 Self::rtc_offset_from_translations(&translations)
104 }
105
106 pub fn detect_rtc_offset_from_jobs(
108 &self,
109 jobs: &[(u32, usize, usize, IfcType)],
110 decoder: &mut EntityDecoder,
111 ) -> (f64, f64, f64) {
112 const MAX_SAMPLES: usize = 50;
113 let translations: Vec<(f64, f64, f64)> = jobs
114 .iter()
115 .take(MAX_SAMPLES)
116 .filter_map(|&(id, start, end, _)| {
117 let entity = decoder.decode_at_with_id(id, start, end).ok()?;
118 self.sample_element_translation(&entity, decoder)
119 })
120 .collect();
121
122 Self::rtc_offset_from_translations(&translations)
123 }
124
125 #[inline]
129 pub fn process_element(
130 &self,
131 element: &DecodedEntity,
132 decoder: &mut EntityDecoder,
133 ) -> Result<Mesh> {
134 let representation_attr = element.get(6).ok_or_else(|| {
137 Error::geometry(format!(
138 "Element #{} has no representation attribute",
139 element.id
140 ))
141 })?;
142
143 if representation_attr.is_null() {
144 return Ok(Mesh::new()); }
146
147 let representation = decoder
148 .resolve_ref(representation_attr)?
149 .ok_or_else(|| Error::geometry("Failed to resolve representation".to_string()))?;
150
151 if representation.ifc_type != IfcType::IfcProductDefinitionShape {
153 return Err(Error::geometry(format!(
154 "Expected IfcProductDefinitionShape, got {}",
155 representation.ifc_type
156 )));
157 }
158
159 let representations_attr = representation.get(2).ok_or_else(|| {
161 Error::geometry("IfcProductDefinitionShape missing Representations".to_string())
162 })?;
163
164 let representations = decoder.resolve_ref_list(representations_attr)?;
165
166 let mut combined_mesh = Mesh::new();
168
169 let has_direct_geometry = representations.iter().any(|rep| {
172 if rep.ifc_type != IfcType::IfcShapeRepresentation {
173 return false;
174 }
175 if let Some(rep_type_attr) = rep.get(2) {
176 if let Some(rep_type) = rep_type_attr.as_string() {
177 matches!(
178 rep_type,
179 "Body"
180 | "SweptSolid"
181 | "SolidModel"
182 | "Brep"
183 | "CSG"
184 | "Clipping"
185 | "SurfaceModel"
186 | "Tessellation"
187 | "AdvancedSweptSolid"
188 | "AdvancedBrep"
189 )
190 } else {
191 false
192 }
193 } else {
194 false
195 }
196 });
197
198 for shape_rep in representations {
199 if shape_rep.ifc_type != IfcType::IfcShapeRepresentation {
200 continue;
201 }
202
203 if let Some(rep_type_attr) = shape_rep.get(2) {
206 if let Some(rep_type) = rep_type_attr.as_string() {
207 if rep_type == "MappedRepresentation" && has_direct_geometry {
210 continue;
211 }
212
213 if !matches!(
215 rep_type,
216 "Body"
217 | "SweptSolid"
218 | "SolidModel"
219 | "Brep"
220 | "CSG"
221 | "Clipping"
222 | "SurfaceModel"
223 | "Tessellation"
224 | "MappedRepresentation"
225 | "AdvancedSweptSolid"
226 | "AdvancedBrep"
227 ) {
228 continue; }
230 }
231 }
232
233 let items_attr = shape_rep.get(3).ok_or_else(|| {
235 Error::geometry("IfcShapeRepresentation missing Items".to_string())
236 })?;
237
238 let items = decoder.resolve_ref_list(items_attr)?;
239
240 for item in items {
242 let mesh = self.process_representation_item(&item, decoder)?;
243 combined_mesh.merge(&mesh);
244 }
245 }
246
247 self.apply_placement(element, decoder, &mut combined_mesh)?;
249
250 Ok(combined_mesh)
251 }
252
253 pub fn process_element_with_submeshes(
259 &self,
260 element: &DecodedEntity,
261 decoder: &mut EntityDecoder,
262 ) -> Result<SubMeshCollection> {
263 let representation_attr = element.get(6).ok_or_else(|| {
265 Error::geometry(format!(
266 "Element #{} has no representation attribute",
267 element.id
268 ))
269 })?;
270
271 if representation_attr.is_null() {
272 return Ok(SubMeshCollection::new()); }
274
275 let representation = decoder
276 .resolve_ref(representation_attr)?
277 .ok_or_else(|| Error::geometry("Failed to resolve representation".to_string()))?;
278
279 if representation.ifc_type != IfcType::IfcProductDefinitionShape {
280 return Err(Error::geometry(format!(
281 "Expected IfcProductDefinitionShape, got {}",
282 representation.ifc_type
283 )));
284 }
285
286 let representations_attr = representation.get(2).ok_or_else(|| {
288 Error::geometry("IfcProductDefinitionShape missing Representations".to_string())
289 })?;
290
291 let representations = decoder.resolve_ref_list(representations_attr)?;
292
293 let mut sub_meshes = SubMeshCollection::new();
294
295 let has_direct_geometry = representations.iter().any(|rep| {
297 if rep.ifc_type != IfcType::IfcShapeRepresentation {
298 return false;
299 }
300 if let Some(rep_type_attr) = rep.get(2) {
301 if let Some(rep_type) = rep_type_attr.as_string() {
302 matches!(
303 rep_type,
304 "Body"
305 | "SweptSolid"
306 | "SolidModel"
307 | "Brep"
308 | "CSG"
309 | "Clipping"
310 | "SurfaceModel"
311 | "Tessellation"
312 | "AdvancedSweptSolid"
313 | "AdvancedBrep"
314 )
315 } else {
316 false
317 }
318 } else {
319 false
320 }
321 });
322
323 for shape_rep in representations {
324 if shape_rep.ifc_type != IfcType::IfcShapeRepresentation {
325 continue;
326 }
327
328 if let Some(rep_type_attr) = shape_rep.get(2) {
329 if let Some(rep_type) = rep_type_attr.as_string() {
330 if rep_type == "MappedRepresentation" && has_direct_geometry {
332 continue;
333 }
334
335 if !matches!(
337 rep_type,
338 "Body"
339 | "SweptSolid"
340 | "SolidModel"
341 | "Brep"
342 | "CSG"
343 | "Clipping"
344 | "SurfaceModel"
345 | "Tessellation"
346 | "MappedRepresentation"
347 | "AdvancedSweptSolid"
348 | "AdvancedBrep"
349 ) {
350 continue;
351 }
352 }
353 }
354
355 let items_attr = shape_rep.get(3).ok_or_else(|| {
357 Error::geometry("IfcShapeRepresentation missing Items".to_string())
358 })?;
359
360 let items = decoder.resolve_ref_list(items_attr)?;
361
362 for item in items {
364 self.collect_submeshes_from_item(&item, decoder, &mut sub_meshes)?;
365 }
366 }
367
368 if let Some(placement_attr) = element.get(5) {
372 if !placement_attr.is_null() {
373 if let Some(placement) = decoder.resolve_ref(placement_attr)? {
374 let mut transform = self.get_placement_transform(&placement, decoder)?;
375 self.scale_transform(&mut transform);
376 for sub in &mut sub_meshes.sub_meshes {
377 self.transform_mesh(&mut sub.mesh, &transform);
378 }
379 }
380 }
381 }
382
383 Ok(sub_meshes)
384 }
385
386 fn collect_submeshes_from_item(
388 &self,
389 item: &DecodedEntity,
390 decoder: &mut EntityDecoder,
391 sub_meshes: &mut SubMeshCollection,
392 ) -> Result<()> {
393 if item.ifc_type == IfcType::IfcMappedItem {
395 let source_attr = item
397 .get(0)
398 .ok_or_else(|| Error::geometry("MappedItem missing MappingSource".to_string()))?;
399
400 let source_entity = decoder
401 .resolve_ref(source_attr)?
402 .ok_or_else(|| Error::geometry("Failed to resolve MappingSource".to_string()))?;
403
404 let mapped_repr_attr = source_entity
406 .get(1)
407 .ok_or_else(|| Error::geometry("RepresentationMap missing MappedRepresentation".to_string()))?;
408
409 let mapped_repr = decoder
410 .resolve_ref(mapped_repr_attr)?
411 .ok_or_else(|| Error::geometry("Failed to resolve MappedRepresentation".to_string()))?;
412
413 let mapping_transform = if let Some(target_attr) = item.get(1) {
415 if !target_attr.is_null() {
416 if let Some(target_entity) = decoder.resolve_ref(target_attr)? {
417 Some(self.parse_cartesian_transformation_operator(&target_entity, decoder)?)
418 } else {
419 None
420 }
421 } else {
422 None
423 }
424 } else {
425 None
426 };
427
428 if let Some(items_attr) = mapped_repr.get(3) {
430 let items = decoder.resolve_ref_list(items_attr)?;
431 for nested_item in items {
432 let count_before = sub_meshes.len();
434 if let Err(_e) = self
435 .collect_submeshes_from_item(&nested_item, decoder, sub_meshes)
436 {
437 #[cfg(debug_assertions)]
438 eprintln!(
439 "[ifc-lite] Skipping unsupported nested geometry #{} ({:?}): {}",
440 nested_item.id, nested_item.ifc_type, _e
441 );
442 continue;
443 }
444
445 if let Some(mut transform) = mapping_transform.clone() {
447 self.scale_transform(&mut transform);
448 for sub in &mut sub_meshes.sub_meshes[count_before..] {
449 self.transform_mesh(&mut sub.mesh, &transform);
450 }
451 }
452 }
453 }
454 } else {
455 match self.process_representation_item(item, decoder) {
458 Ok(mesh) => {
459 if !mesh.is_empty() {
460 sub_meshes.add(item.id, mesh);
461 }
462 }
463 Err(_e) => {
464 #[cfg(debug_assertions)]
465 eprintln!(
466 "[ifc-lite] Skipping unsupported geometry #{} ({:?}): {}",
467 item.id, item.ifc_type, _e
468 );
469 }
470 }
471 }
472
473 Ok(())
474 }
475
476 #[inline]
479 pub fn process_element_with_transform(
480 &self,
481 element: &DecodedEntity,
482 decoder: &mut EntityDecoder,
483 ) -> Result<(Mesh, Matrix4<f64>)> {
484 let representation_attr = element.get(6).ok_or_else(|| {
486 Error::geometry(format!(
487 "Element #{} has no representation attribute",
488 element.id
489 ))
490 })?;
491
492 if representation_attr.is_null() {
493 return Ok((Mesh::new(), Matrix4::identity())); }
495
496 let representation = decoder
497 .resolve_ref(representation_attr)?
498 .ok_or_else(|| Error::geometry("Failed to resolve representation".to_string()))?;
499
500 if representation.ifc_type != IfcType::IfcProductDefinitionShape {
501 return Err(Error::geometry(format!(
502 "Expected IfcProductDefinitionShape, got {}",
503 representation.ifc_type
504 )));
505 }
506
507 let representations_attr = representation.get(2).ok_or_else(|| {
509 Error::geometry("IfcProductDefinitionShape missing Representations".to_string())
510 })?;
511
512 let representations = decoder.resolve_ref_list(representations_attr)?;
513
514 let mut combined_mesh = Mesh::new();
516
517 let has_direct_geometry = representations.iter().any(|rep| {
519 if rep.ifc_type != IfcType::IfcShapeRepresentation {
520 return false;
521 }
522 if let Some(rep_type_attr) = rep.get(2) {
523 if let Some(rep_type) = rep_type_attr.as_string() {
524 matches!(
525 rep_type,
526 "Body"
527 | "SweptSolid"
528 | "SolidModel"
529 | "Brep"
530 | "CSG"
531 | "Clipping"
532 | "SurfaceModel"
533 | "Tessellation"
534 | "AdvancedSweptSolid"
535 | "AdvancedBrep"
536 )
537 } else {
538 false
539 }
540 } else {
541 false
542 }
543 });
544
545 for shape_rep in representations {
546 if shape_rep.ifc_type != IfcType::IfcShapeRepresentation {
547 continue;
548 }
549
550 if let Some(rep_type_attr) = shape_rep.get(2) {
551 if let Some(rep_type) = rep_type_attr.as_string() {
552 if rep_type == "MappedRepresentation" && has_direct_geometry {
553 continue;
554 }
555
556 if !matches!(
557 rep_type,
558 "Body"
559 | "SweptSolid"
560 | "SolidModel"
561 | "Brep"
562 | "CSG"
563 | "Clipping"
564 | "SurfaceModel"
565 | "Tessellation"
566 | "MappedRepresentation"
567 | "AdvancedSweptSolid"
568 | "AdvancedBrep"
569 ) {
570 continue;
571 }
572 }
573 }
574
575 let items_attr = shape_rep.get(3).ok_or_else(|| {
576 Error::geometry("IfcShapeRepresentation missing Items".to_string())
577 })?;
578
579 let items = decoder.resolve_ref_list(items_attr)?;
580
581 for item in items {
582 let mesh = self.process_representation_item(&item, decoder)?;
583 combined_mesh.merge(&mesh);
584 }
585 }
586
587 let transform = self.get_placement_transform_from_element(element, decoder)?;
589
590 Ok((combined_mesh, transform))
591 }
592
593 #[inline]
596 pub fn process_representation_item(
597 &self,
598 item: &DecodedEntity,
599 decoder: &mut EntityDecoder,
600 ) -> Result<Mesh> {
601 if item.ifc_type == IfcType::IfcMappedItem {
603 return self.process_mapped_item_cached(item, decoder);
604 }
605
606 if item.ifc_type == IfcType::IfcFacetedBrep {
608 if let Some(mut mesh) = self.take_cached_faceted_brep(item.id) {
609 self.scale_mesh(&mut mesh);
610 let cached = self.get_or_cache_by_hash(mesh);
611 return Ok((*cached).clone());
612 }
613 }
614
615 if let Some(processor) = self.processors.get(&item.ifc_type) {
617 let mut mesh = processor.process(item, decoder, &self.schema)?;
618 self.scale_mesh(&mut mesh);
619
620 if !mesh.positions.is_empty() {
622 let cached = self.get_or_cache_by_hash(mesh);
623 return Ok((*cached).clone());
624 }
625 return Ok(mesh);
626 }
627
628 match self.schema.geometry_category(&item.ifc_type) {
630 Some(GeometryCategory::SweptSolid) => {
631 Ok(Mesh::new())
633 }
634 Some(GeometryCategory::ExplicitMesh) => {
635 Ok(Mesh::new())
637 }
638 Some(GeometryCategory::Boolean) => {
639 Ok(Mesh::new())
641 }
642 Some(GeometryCategory::MappedItem) => {
643 Ok(Mesh::new())
645 }
646 _ => Err(Error::geometry(format!(
647 "Unsupported representation type: {}",
648 item.ifc_type
649 ))),
650 }
651 }
652
653 #[inline]
655 fn process_mapped_item_cached(
656 &self,
657 item: &DecodedEntity,
658 decoder: &mut EntityDecoder,
659 ) -> Result<Mesh> {
660 let source_attr = item
666 .get(0)
667 .ok_or_else(|| Error::geometry("MappedItem missing MappingSource".to_string()))?;
668
669 let source_entity = decoder
670 .resolve_ref(source_attr)?
671 .ok_or_else(|| Error::geometry("Failed to resolve MappingSource".to_string()))?;
672
673 let source_id = source_entity.id;
674
675 let mapping_transform = if let Some(target_attr) = item.get(1) {
677 if !target_attr.is_null() {
678 if let Some(target_entity) = decoder.resolve_ref(target_attr)? {
679 Some(self.parse_cartesian_transformation_operator(&target_entity, decoder)?)
680 } else {
681 None
682 }
683 } else {
684 None
685 }
686 } else {
687 None
688 };
689
690 {
692 let cache = self.mapped_item_cache.borrow();
693 if let Some(cached_mesh) = cache.get(&source_id) {
694 let mut mesh = cached_mesh.as_ref().clone();
695 if let Some(mut transform) = mapping_transform {
696 self.scale_transform(&mut transform);
697 self.transform_mesh(&mut mesh, &transform);
698 }
699 return Ok(mesh);
700 }
701 }
702
703 let mapped_rep_attr = source_entity.get(1).ok_or_else(|| {
709 Error::geometry("RepresentationMap missing MappedRepresentation".to_string())
710 })?;
711
712 let mapped_rep = decoder
713 .resolve_ref(mapped_rep_attr)?
714 .ok_or_else(|| Error::geometry("Failed to resolve MappedRepresentation".to_string()))?;
715
716 let items_attr = mapped_rep
718 .get(3)
719 .ok_or_else(|| Error::geometry("Representation missing Items".to_string()))?;
720
721 let items = decoder.resolve_ref_list(items_attr)?;
722
723 let mut mesh = Mesh::new();
725 for sub_item in items {
726 if sub_item.ifc_type == IfcType::IfcMappedItem {
727 continue; }
729 if let Some(processor) = self.processors.get(&sub_item.ifc_type) {
730 if let Ok(mut sub_mesh) = processor.process(&sub_item, decoder, &self.schema) {
731 self.scale_mesh(&mut sub_mesh);
732 mesh.merge(&sub_mesh);
733 }
734 }
735 }
736
737 {
739 let mut cache = self.mapped_item_cache.borrow_mut();
740 cache.insert(source_id, Arc::new(mesh.clone()));
741 }
742
743 if let Some(mut transform) = mapping_transform {
745 self.scale_transform(&mut transform);
746 self.transform_mesh(&mut mesh, &transform);
747 }
748
749 Ok(mesh)
750 }
751}