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 self.collect_submeshes_from_item(&nested_item, decoder, sub_meshes)?;
435
436 if let Some(mut transform) = mapping_transform.clone() {
438 self.scale_transform(&mut transform);
439 for sub in &mut sub_meshes.sub_meshes[count_before..] {
440 self.transform_mesh(&mut sub.mesh, &transform);
441 }
442 }
443 }
444 }
445 } else {
446 let mesh = self.process_representation_item(item, decoder)?;
448 if !mesh.is_empty() {
449 sub_meshes.add(item.id, mesh);
450 }
451 }
452
453 Ok(())
454 }
455
456 #[inline]
459 pub fn process_element_with_transform(
460 &self,
461 element: &DecodedEntity,
462 decoder: &mut EntityDecoder,
463 ) -> Result<(Mesh, Matrix4<f64>)> {
464 let representation_attr = element.get(6).ok_or_else(|| {
466 Error::geometry(format!(
467 "Element #{} has no representation attribute",
468 element.id
469 ))
470 })?;
471
472 if representation_attr.is_null() {
473 return Ok((Mesh::new(), Matrix4::identity())); }
475
476 let representation = decoder
477 .resolve_ref(representation_attr)?
478 .ok_or_else(|| Error::geometry("Failed to resolve representation".to_string()))?;
479
480 if representation.ifc_type != IfcType::IfcProductDefinitionShape {
481 return Err(Error::geometry(format!(
482 "Expected IfcProductDefinitionShape, got {}",
483 representation.ifc_type
484 )));
485 }
486
487 let representations_attr = representation.get(2).ok_or_else(|| {
489 Error::geometry("IfcProductDefinitionShape missing Representations".to_string())
490 })?;
491
492 let representations = decoder.resolve_ref_list(representations_attr)?;
493
494 let mut combined_mesh = Mesh::new();
496
497 let has_direct_geometry = representations.iter().any(|rep| {
499 if rep.ifc_type != IfcType::IfcShapeRepresentation {
500 return false;
501 }
502 if let Some(rep_type_attr) = rep.get(2) {
503 if let Some(rep_type) = rep_type_attr.as_string() {
504 matches!(
505 rep_type,
506 "Body"
507 | "SweptSolid"
508 | "SolidModel"
509 | "Brep"
510 | "CSG"
511 | "Clipping"
512 | "SurfaceModel"
513 | "Tessellation"
514 | "AdvancedSweptSolid"
515 | "AdvancedBrep"
516 )
517 } else {
518 false
519 }
520 } else {
521 false
522 }
523 });
524
525 for shape_rep in representations {
526 if shape_rep.ifc_type != IfcType::IfcShapeRepresentation {
527 continue;
528 }
529
530 if let Some(rep_type_attr) = shape_rep.get(2) {
531 if let Some(rep_type) = rep_type_attr.as_string() {
532 if rep_type == "MappedRepresentation" && has_direct_geometry {
533 continue;
534 }
535
536 if !matches!(
537 rep_type,
538 "Body"
539 | "SweptSolid"
540 | "SolidModel"
541 | "Brep"
542 | "CSG"
543 | "Clipping"
544 | "SurfaceModel"
545 | "Tessellation"
546 | "MappedRepresentation"
547 | "AdvancedSweptSolid"
548 | "AdvancedBrep"
549 ) {
550 continue;
551 }
552 }
553 }
554
555 let items_attr = shape_rep.get(3).ok_or_else(|| {
556 Error::geometry("IfcShapeRepresentation missing Items".to_string())
557 })?;
558
559 let items = decoder.resolve_ref_list(items_attr)?;
560
561 for item in items {
562 let mesh = self.process_representation_item(&item, decoder)?;
563 combined_mesh.merge(&mesh);
564 }
565 }
566
567 let transform = self.get_placement_transform_from_element(element, decoder)?;
569
570 Ok((combined_mesh, transform))
571 }
572
573 #[inline]
576 pub fn process_representation_item(
577 &self,
578 item: &DecodedEntity,
579 decoder: &mut EntityDecoder,
580 ) -> Result<Mesh> {
581 if item.ifc_type == IfcType::IfcMappedItem {
583 return self.process_mapped_item_cached(item, decoder);
584 }
585
586 if item.ifc_type == IfcType::IfcFacetedBrep {
588 if let Some(mut mesh) = self.take_cached_faceted_brep(item.id) {
589 self.scale_mesh(&mut mesh);
590 let cached = self.get_or_cache_by_hash(mesh);
591 return Ok((*cached).clone());
592 }
593 }
594
595 if let Some(processor) = self.processors.get(&item.ifc_type) {
597 let mut mesh = processor.process(item, decoder, &self.schema)?;
598 self.scale_mesh(&mut mesh);
599
600 if !mesh.positions.is_empty() {
602 let cached = self.get_or_cache_by_hash(mesh);
603 return Ok((*cached).clone());
604 }
605 return Ok(mesh);
606 }
607
608 match self.schema.geometry_category(&item.ifc_type) {
610 Some(GeometryCategory::SweptSolid) => {
611 Ok(Mesh::new())
613 }
614 Some(GeometryCategory::ExplicitMesh) => {
615 Ok(Mesh::new())
617 }
618 Some(GeometryCategory::Boolean) => {
619 Ok(Mesh::new())
621 }
622 Some(GeometryCategory::MappedItem) => {
623 Ok(Mesh::new())
625 }
626 _ => Err(Error::geometry(format!(
627 "Unsupported representation type: {}",
628 item.ifc_type
629 ))),
630 }
631 }
632
633 #[inline]
635 fn process_mapped_item_cached(
636 &self,
637 item: &DecodedEntity,
638 decoder: &mut EntityDecoder,
639 ) -> Result<Mesh> {
640 let source_attr = item
646 .get(0)
647 .ok_or_else(|| Error::geometry("MappedItem missing MappingSource".to_string()))?;
648
649 let source_entity = decoder
650 .resolve_ref(source_attr)?
651 .ok_or_else(|| Error::geometry("Failed to resolve MappingSource".to_string()))?;
652
653 let source_id = source_entity.id;
654
655 let mapping_transform = if let Some(target_attr) = item.get(1) {
657 if !target_attr.is_null() {
658 if let Some(target_entity) = decoder.resolve_ref(target_attr)? {
659 Some(self.parse_cartesian_transformation_operator(&target_entity, decoder)?)
660 } else {
661 None
662 }
663 } else {
664 None
665 }
666 } else {
667 None
668 };
669
670 {
672 let cache = self.mapped_item_cache.borrow();
673 if let Some(cached_mesh) = cache.get(&source_id) {
674 let mut mesh = cached_mesh.as_ref().clone();
675 if let Some(mut transform) = mapping_transform {
676 self.scale_transform(&mut transform);
677 self.transform_mesh(&mut mesh, &transform);
678 }
679 return Ok(mesh);
680 }
681 }
682
683 let mapped_rep_attr = source_entity.get(1).ok_or_else(|| {
689 Error::geometry("RepresentationMap missing MappedRepresentation".to_string())
690 })?;
691
692 let mapped_rep = decoder
693 .resolve_ref(mapped_rep_attr)?
694 .ok_or_else(|| Error::geometry("Failed to resolve MappedRepresentation".to_string()))?;
695
696 let items_attr = mapped_rep
698 .get(3)
699 .ok_or_else(|| Error::geometry("Representation missing Items".to_string()))?;
700
701 let items = decoder.resolve_ref_list(items_attr)?;
702
703 let mut mesh = Mesh::new();
705 for sub_item in items {
706 if sub_item.ifc_type == IfcType::IfcMappedItem {
707 continue; }
709 if let Some(processor) = self.processors.get(&sub_item.ifc_type) {
710 if let Ok(mut sub_mesh) = processor.process(&sub_item, decoder, &self.schema) {
711 self.scale_mesh(&mut sub_mesh);
712 mesh.merge(&sub_mesh);
713 }
714 }
715 }
716
717 {
719 let mut cache = self.mapped_item_cache.borrow_mut();
720 cache.insert(source_id, Arc::new(mesh.clone()));
721 }
722
723 if let Some(mut transform) = mapping_transform {
725 self.scale_transform(&mut transform);
726 self.transform_mesh(&mut mesh, &transform);
727 }
728
729 Ok(mesh)
730 }
731}