1use crate::{Mesh, Point3, Vector3, Result, Error};
10use crate::processors::{ExtrudedAreaSolidProcessor, TriangulatedFaceSetProcessor, MappedItemProcessor, FacetedBrepProcessor, BooleanClippingProcessor, SweptDiskSolidProcessor, RevolvedAreaSolidProcessor, AdvancedBrepProcessor};
11use ifc_lite_core::{
12 DecodedEntity, EntityDecoder, GeometryCategory, IfcSchema, IfcType, ProfileCategory,
13};
14use nalgebra::{Matrix4, Rotation3};
15use rustc_hash::FxHashMap;
16use std::cell::RefCell;
17use std::collections::HashMap;
18use std::hash::{Hash, Hasher};
19use std::sync::Arc;
20
21pub trait GeometryProcessor {
24 fn process(
26 &self,
27 entity: &DecodedEntity,
28 decoder: &mut EntityDecoder,
29 schema: &IfcSchema,
30 ) -> Result<Mesh>;
31
32 fn supported_types(&self) -> Vec<IfcType>;
34}
35
36pub struct GeometryRouter {
38 schema: IfcSchema,
39 processors: HashMap<IfcType, Arc<dyn GeometryProcessor>>,
40 mapped_item_cache: RefCell<FxHashMap<u32, Arc<Mesh>>>,
43 faceted_brep_cache: RefCell<FxHashMap<u32, Mesh>>,
47 geometry_hash_cache: RefCell<FxHashMap<u64, Arc<Mesh>>>,
51}
52
53impl GeometryRouter {
54 pub fn new() -> Self {
56 let schema = IfcSchema::new();
57 let schema_clone = schema.clone();
58 let mut router = Self {
59 schema,
60 processors: HashMap::new(),
61 mapped_item_cache: RefCell::new(FxHashMap::default()),
62 faceted_brep_cache: RefCell::new(FxHashMap::default()),
63 geometry_hash_cache: RefCell::new(FxHashMap::default()),
64 };
65
66 router.register(Box::new(ExtrudedAreaSolidProcessor::new(schema_clone.clone())));
68 router.register(Box::new(TriangulatedFaceSetProcessor::new()));
69 router.register(Box::new(MappedItemProcessor::new()));
70 router.register(Box::new(FacetedBrepProcessor::new()));
71 router.register(Box::new(BooleanClippingProcessor::new()));
72 router.register(Box::new(SweptDiskSolidProcessor::new(schema_clone.clone())));
73 router.register(Box::new(RevolvedAreaSolidProcessor::new(schema_clone.clone())));
74 router.register(Box::new(AdvancedBrepProcessor::new()));
75
76 router
77 }
78
79 pub fn register(&mut self, processor: Box<dyn GeometryProcessor>) {
81 let processor_arc: Arc<dyn GeometryProcessor> = Arc::from(processor);
82 for ifc_type in processor_arc.supported_types() {
83 self.processors.insert(ifc_type, Arc::clone(&processor_arc));
84 }
85 }
86
87 pub fn preprocess_faceted_breps(&self, brep_ids: &[u32], decoder: &mut EntityDecoder) {
91 if brep_ids.is_empty() {
92 return;
93 }
94
95 let processor = FacetedBrepProcessor::new();
97 let results = processor.process_batch(brep_ids, decoder);
98
99 let mut cache = self.faceted_brep_cache.borrow_mut();
101 cache.reserve(results.len());
102 for (brep_idx, mesh) in results {
103 let brep_id = brep_ids[brep_idx];
104 cache.insert(brep_id, mesh);
105 }
106 }
107
108 #[inline]
111 pub fn take_cached_faceted_brep(&self, brep_id: u32) -> Option<Mesh> {
112 self.faceted_brep_cache.borrow_mut().remove(&brep_id)
113 }
114
115 #[inline]
118 fn compute_mesh_hash(mesh: &Mesh) -> u64 {
119 use rustc_hash::FxHasher;
120 let mut hasher = FxHasher::default();
121
122 mesh.positions.len().hash(&mut hasher);
124 mesh.indices.len().hash(&mut hasher);
125
126 for pos in &mesh.positions {
129 pos.to_bits().hash(&mut hasher);
130 }
131
132 for idx in &mesh.indices {
134 idx.hash(&mut hasher);
135 }
136
137 hasher.finish()
138 }
139
140 #[inline]
146 fn get_or_cache_by_hash(&self, mesh: Mesh) -> Arc<Mesh> {
147 let hash = Self::compute_mesh_hash(&mesh);
148
149 {
151 let cache = self.geometry_hash_cache.borrow();
152 if let Some(cached) = cache.get(&hash) {
153 return Arc::clone(cached);
154 }
155 }
156
157 let arc_mesh = Arc::new(mesh);
159 {
160 let mut cache = self.geometry_hash_cache.borrow_mut();
161 cache.insert(hash, Arc::clone(&arc_mesh));
162 }
163 arc_mesh
164 }
165
166 #[inline]
170 pub fn process_element(
171 &self,
172 element: &DecodedEntity,
173 decoder: &mut EntityDecoder,
174 ) -> Result<Mesh> {
175 let representation_attr = element.get(6).ok_or_else(|| {
178 Error::geometry(format!(
179 "Element #{} has no representation attribute",
180 element.id
181 ))
182 })?;
183
184 if representation_attr.is_null() {
185 return Ok(Mesh::new()); }
187
188 let representation = decoder
189 .resolve_ref(representation_attr)?
190 .ok_or_else(|| Error::geometry("Failed to resolve representation".to_string()))?;
191
192 if representation.ifc_type != IfcType::IfcProductDefinitionShape {
194 return Err(Error::geometry(format!(
195 "Expected IfcProductDefinitionShape, got {}",
196 representation.ifc_type
197 )));
198 }
199
200 let representations_attr = representation.get(2).ok_or_else(|| {
202 Error::geometry("IfcProductDefinitionShape missing Representations".to_string())
203 })?;
204
205 let representations = decoder.resolve_ref_list(representations_attr)?;
206
207 let mut combined_mesh = Mesh::new();
209
210 let has_direct_geometry = representations.iter().any(|rep| {
213 if rep.ifc_type != IfcType::IfcShapeRepresentation {
214 return false;
215 }
216 if let Some(rep_type_attr) = rep.get(2) {
217 if let Some(rep_type) = rep_type_attr.as_string() {
218 matches!(
219 rep_type,
220 "Body" | "SweptSolid" | "Brep" | "CSG" | "Clipping" | "SurfaceModel" | "Tessellation" | "AdvancedSweptSolid" | "AdvancedBrep"
221 )
222 } else {
223 false
224 }
225 } else {
226 false
227 }
228 });
229
230 for shape_rep in representations {
231 if shape_rep.ifc_type != IfcType::IfcShapeRepresentation {
232 continue;
233 }
234
235 if let Some(rep_type_attr) = shape_rep.get(2) {
238 if let Some(rep_type) = rep_type_attr.as_string() {
239 if rep_type == "MappedRepresentation" && has_direct_geometry {
242 continue;
243 }
244
245 if !matches!(
247 rep_type,
248 "Body" | "SweptSolid" | "Brep" | "CSG" | "Clipping" | "SurfaceModel" | "Tessellation" | "MappedRepresentation" | "AdvancedSweptSolid" | "AdvancedBrep"
249 ) {
250 continue; }
252 }
253 }
254
255 let items_attr = shape_rep.get(3).ok_or_else(|| {
257 Error::geometry("IfcShapeRepresentation missing Items".to_string())
258 })?;
259
260 let items = decoder.resolve_ref_list(items_attr)?;
261
262 for item in items {
264 let mesh = self.process_representation_item(&item, decoder)?;
265 combined_mesh.merge(&mesh);
266 }
267 }
268
269 self.apply_placement(element, decoder, &mut combined_mesh)?;
271
272 Ok(combined_mesh)
273 }
274
275 #[inline]
278 pub fn process_element_with_transform(
279 &self,
280 element: &DecodedEntity,
281 decoder: &mut EntityDecoder,
282 ) -> Result<(Mesh, Matrix4<f64>)> {
283 let representation_attr = element.get(6).ok_or_else(|| {
285 Error::geometry(format!(
286 "Element #{} has no representation attribute",
287 element.id
288 ))
289 })?;
290
291 if representation_attr.is_null() {
292 return Ok((Mesh::new(), Matrix4::identity())); }
294
295 let representation = decoder
296 .resolve_ref(representation_attr)?
297 .ok_or_else(|| Error::geometry("Failed to resolve representation".to_string()))?;
298
299 if representation.ifc_type != IfcType::IfcProductDefinitionShape {
300 return Err(Error::geometry(format!(
301 "Expected IfcProductDefinitionShape, got {}",
302 representation.ifc_type
303 )));
304 }
305
306 let representations_attr = representation.get(2).ok_or_else(|| {
308 Error::geometry("IfcProductDefinitionShape missing Representations".to_string())
309 })?;
310
311 let representations = decoder.resolve_ref_list(representations_attr)?;
312
313 let mut combined_mesh = Mesh::new();
315
316 let has_direct_geometry = representations.iter().any(|rep| {
318 if rep.ifc_type != IfcType::IfcShapeRepresentation {
319 return false;
320 }
321 if let Some(rep_type_attr) = rep.get(2) {
322 if let Some(rep_type) = rep_type_attr.as_string() {
323 matches!(
324 rep_type,
325 "Body" | "SweptSolid" | "Brep" | "CSG" | "Clipping" | "SurfaceModel" | "Tessellation" | "AdvancedSweptSolid" | "AdvancedBrep"
326 )
327 } else {
328 false
329 }
330 } else {
331 false
332 }
333 });
334
335 for shape_rep in representations {
336 if shape_rep.ifc_type != IfcType::IfcShapeRepresentation {
337 continue;
338 }
339
340 if let Some(rep_type_attr) = shape_rep.get(2) {
341 if let Some(rep_type) = rep_type_attr.as_string() {
342 if rep_type == "MappedRepresentation" && has_direct_geometry {
343 continue;
344 }
345
346 if !matches!(
347 rep_type,
348 "Body" | "SweptSolid" | "Brep" | "CSG" | "Clipping" | "SurfaceModel" | "Tessellation" | "MappedRepresentation" | "AdvancedSweptSolid" | "AdvancedBrep"
349 ) {
350 continue;
351 }
352 }
353 }
354
355 let items_attr = shape_rep.get(3).ok_or_else(|| {
356 Error::geometry("IfcShapeRepresentation missing Items".to_string())
357 })?;
358
359 let items = decoder.resolve_ref_list(items_attr)?;
360
361 for item in items {
362 let mesh = self.process_representation_item(&item, decoder)?;
363 combined_mesh.merge(&mesh);
364 }
365 }
366
367 let transform = self.get_placement_transform_from_element(element, decoder)?;
369
370 Ok((combined_mesh, transform))
371 }
372
373 fn get_placement_transform_from_element(
375 &self,
376 element: &DecodedEntity,
377 decoder: &mut EntityDecoder,
378 ) -> Result<Matrix4<f64>> {
379 let placement_attr = match element.get(5) {
381 Some(attr) if !attr.is_null() => attr,
382 _ => return Ok(Matrix4::identity()), };
384
385 let placement = match decoder.resolve_ref(placement_attr)? {
386 Some(p) => p,
387 None => return Ok(Matrix4::identity()),
388 };
389
390 self.get_placement_transform(&placement, decoder)
392 }
393
394 #[inline]
397 pub fn process_representation_item(
398 &self,
399 item: &DecodedEntity,
400 decoder: &mut EntityDecoder,
401 ) -> Result<Mesh> {
402 if item.ifc_type == IfcType::IfcMappedItem {
404 return self.process_mapped_item_cached(item, decoder);
405 }
406
407 if item.ifc_type == IfcType::IfcFacetedBrep {
409 if let Some(mesh) = self.take_cached_faceted_brep(item.id) {
410 let cached = self.get_or_cache_by_hash(mesh);
412 return Ok((*cached).clone());
413 }
414 }
415
416 if let Some(processor) = self.processors.get(&item.ifc_type) {
418 let mesh = processor.process(item, decoder, &self.schema)?;
419 if !mesh.positions.is_empty() {
421 let cached = self.get_or_cache_by_hash(mesh);
422 return Ok((*cached).clone());
423 }
424 return Ok(mesh);
425 }
426
427 match self.schema.geometry_category(&item.ifc_type) {
429 Some(GeometryCategory::SweptSolid) => {
430 Ok(Mesh::new())
432 }
433 Some(GeometryCategory::ExplicitMesh) => {
434 Ok(Mesh::new())
436 }
437 Some(GeometryCategory::Boolean) => {
438 Ok(Mesh::new())
440 }
441 Some(GeometryCategory::MappedItem) => {
442 Ok(Mesh::new())
444 }
445 _ => Err(Error::geometry(format!(
446 "Unsupported representation type: {}",
447 item.ifc_type
448 ))),
449 }
450 }
451
452 #[inline]
454 fn process_mapped_item_cached(
455 &self,
456 item: &DecodedEntity,
457 decoder: &mut EntityDecoder,
458 ) -> Result<Mesh> {
459 let source_attr = item
465 .get(0)
466 .ok_or_else(|| Error::geometry("MappedItem missing MappingSource".to_string()))?;
467
468 let source_entity = decoder
469 .resolve_ref(source_attr)?
470 .ok_or_else(|| Error::geometry("Failed to resolve MappingSource".to_string()))?;
471
472 let source_id = source_entity.id;
473
474 let mapping_transform = if let Some(target_attr) = item.get(1) {
476 if !target_attr.is_null() {
477 if let Some(target_entity) = decoder.resolve_ref(target_attr)? {
478 Some(self.parse_cartesian_transformation_operator(&target_entity, decoder)?)
479 } else {
480 None
481 }
482 } else {
483 None
484 }
485 } else {
486 None
487 };
488
489 {
491 let cache = self.mapped_item_cache.borrow();
492 if let Some(cached_mesh) = cache.get(&source_id) {
493 let mut mesh = cached_mesh.as_ref().clone();
495 if let Some(transform) = &mapping_transform {
496 self.transform_mesh(&mut mesh, transform);
497 }
498 return Ok(mesh);
499 }
500 }
501
502 let mapped_rep_attr = source_entity
508 .get(1)
509 .ok_or_else(|| {
510 Error::geometry("RepresentationMap missing MappedRepresentation".to_string())
511 })?;
512
513 let mapped_rep = decoder
514 .resolve_ref(mapped_rep_attr)?
515 .ok_or_else(|| Error::geometry("Failed to resolve MappedRepresentation".to_string()))?;
516
517 let items_attr = mapped_rep
519 .get(3)
520 .ok_or_else(|| Error::geometry("Representation missing Items".to_string()))?;
521
522 let items = decoder.resolve_ref_list(items_attr)?;
523
524 let mut mesh = Mesh::new();
526 for sub_item in items {
527 if sub_item.ifc_type == IfcType::IfcMappedItem {
528 continue; }
530 if let Some(processor) = self.processors.get(&sub_item.ifc_type) {
531 if let Ok(sub_mesh) = processor.process(&sub_item, decoder, &self.schema) {
532 mesh.merge(&sub_mesh);
533 }
534 }
535 }
536
537 {
539 let mut cache = self.mapped_item_cache.borrow_mut();
540 cache.insert(source_id, Arc::new(mesh.clone()));
541 }
542
543 if let Some(transform) = &mapping_transform {
545 self.transform_mesh(&mut mesh, transform);
546 }
547
548 Ok(mesh)
549 }
550
551 fn apply_placement(
553 &self,
554 element: &DecodedEntity,
555 decoder: &mut EntityDecoder,
556 mesh: &mut Mesh,
557 ) -> Result<()> {
558 let placement_attr = match element.get(5) {
560 Some(attr) if !attr.is_null() => attr,
561 _ => return Ok(()), };
563
564 let placement = match decoder.resolve_ref(placement_attr)? {
565 Some(p) => p,
566 None => return Ok(()),
567 };
568
569 let transform = self.get_placement_transform(&placement, decoder)?;
571 self.transform_mesh(mesh, &transform);
572 Ok(())
573 }
574
575 fn get_placement_transform(
577 &self,
578 placement: &DecodedEntity,
579 decoder: &mut EntityDecoder,
580 ) -> Result<Matrix4<f64>> {
581 if placement.ifc_type != IfcType::IfcLocalPlacement {
582 return Ok(Matrix4::identity());
583 }
584
585 let parent_transform = if let Some(parent_attr) = placement.get(0) {
587 if !parent_attr.is_null() {
588 if let Some(parent) = decoder.resolve_ref(parent_attr)? {
589 self.get_placement_transform(&parent, decoder)?
590 } else {
591 Matrix4::identity()
592 }
593 } else {
594 Matrix4::identity()
595 }
596 } else {
597 Matrix4::identity()
598 };
599
600 let local_transform = if let Some(rel_attr) = placement.get(1) {
602 if !rel_attr.is_null() {
603 if let Some(rel) = decoder.resolve_ref(rel_attr)? {
604 if rel.ifc_type == IfcType::IfcAxis2Placement3D {
605 self.parse_axis2_placement_3d(&rel, decoder)?
606 } else {
607 Matrix4::identity()
608 }
609 } else {
610 Matrix4::identity()
611 }
612 } else {
613 Matrix4::identity()
614 }
615 } else {
616 Matrix4::identity()
617 };
618
619 Ok(parent_transform * local_transform)
621 }
622
623 fn parse_axis2_placement_3d(
625 &self,
626 placement: &DecodedEntity,
627 decoder: &mut EntityDecoder,
628 ) -> Result<Matrix4<f64>> {
629 let location = self.parse_cartesian_point(placement, decoder, 0)?;
631
632 let z_axis = if let Some(axis_attr) = placement.get(1) {
634 if !axis_attr.is_null() {
635 if let Some(axis_entity) = decoder.resolve_ref(axis_attr)? {
636 self.parse_direction(&axis_entity)?
637 } else {
638 Vector3::new(0.0, 0.0, 1.0)
639 }
640 } else {
641 Vector3::new(0.0, 0.0, 1.0)
642 }
643 } else {
644 Vector3::new(0.0, 0.0, 1.0)
645 };
646
647 let x_axis = if let Some(ref_dir_attr) = placement.get(2) {
648 if !ref_dir_attr.is_null() {
649 if let Some(ref_dir_entity) = decoder.resolve_ref(ref_dir_attr)? {
650 self.parse_direction(&ref_dir_entity)?
651 } else {
652 Vector3::new(1.0, 0.0, 0.0)
653 }
654 } else {
655 Vector3::new(1.0, 0.0, 0.0)
656 }
657 } else {
658 Vector3::new(1.0, 0.0, 0.0)
659 };
660
661 let y_axis = z_axis.cross(&x_axis).normalize();
663 let x_axis = y_axis.cross(&z_axis).normalize();
664 let z_axis = z_axis.normalize();
665
666 let mut transform = Matrix4::identity();
668 transform[(0, 0)] = x_axis.x;
669 transform[(1, 0)] = x_axis.y;
670 transform[(2, 0)] = x_axis.z;
671 transform[(0, 1)] = y_axis.x;
672 transform[(1, 1)] = y_axis.y;
673 transform[(2, 1)] = y_axis.z;
674 transform[(0, 2)] = z_axis.x;
675 transform[(1, 2)] = z_axis.y;
676 transform[(2, 2)] = z_axis.z;
677 transform[(0, 3)] = location.x;
678 transform[(1, 3)] = location.y;
679 transform[(2, 3)] = location.z;
680
681 Ok(transform)
682 }
683
684 #[inline]
686 fn parse_cartesian_point(
687 &self,
688 parent: &DecodedEntity,
689 decoder: &mut EntityDecoder,
690 attr_index: usize,
691 ) -> Result<Point3<f64>> {
692 let point_attr = parent
693 .get(attr_index)
694 .ok_or_else(|| Error::geometry("Missing cartesian point".to_string()))?;
695
696 let point_entity = decoder
697 .resolve_ref(point_attr)?
698 .ok_or_else(|| Error::geometry("Failed to resolve cartesian point".to_string()))?;
699
700 if point_entity.ifc_type != IfcType::IfcCartesianPoint {
701 return Err(Error::geometry(format!(
702 "Expected IfcCartesianPoint, got {}",
703 point_entity.ifc_type
704 )));
705 }
706
707 let coords_attr = point_entity
709 .get(0)
710 .ok_or_else(|| Error::geometry("IfcCartesianPoint missing coordinates".to_string()))?;
711
712 let coords = coords_attr
713 .as_list()
714 .ok_or_else(|| Error::geometry("Expected coordinate list".to_string()))?;
715
716 let x = coords
717 .get(0)
718 .and_then(|v| v.as_float())
719 .unwrap_or(0.0);
720 let y = coords
721 .get(1)
722 .and_then(|v| v.as_float())
723 .unwrap_or(0.0);
724 let z = coords
725 .get(2)
726 .and_then(|v| v.as_float())
727 .unwrap_or(0.0);
728
729 Ok(Point3::new(x, y, z))
730 }
731
732 #[inline]
734 fn parse_direction(&self, direction_entity: &DecodedEntity) -> Result<Vector3<f64>> {
735 if direction_entity.ifc_type != IfcType::IfcDirection {
736 return Err(Error::geometry(format!(
737 "Expected IfcDirection, got {}",
738 direction_entity.ifc_type
739 )));
740 }
741
742 let ratios_attr = direction_entity
744 .get(0)
745 .ok_or_else(|| Error::geometry("IfcDirection missing ratios".to_string()))?;
746
747 let ratios = ratios_attr
748 .as_list()
749 .ok_or_else(|| Error::geometry("Expected ratio list".to_string()))?;
750
751 let x = ratios.get(0).and_then(|v| v.as_float()).unwrap_or(0.0);
752 let y = ratios.get(1).and_then(|v| v.as_float()).unwrap_or(0.0);
753 let z = ratios.get(2).and_then(|v| v.as_float()).unwrap_or(0.0);
754
755 Ok(Vector3::new(x, y, z))
756 }
757
758 #[inline]
761 fn parse_cartesian_transformation_operator(
762 &self,
763 entity: &DecodedEntity,
764 decoder: &mut EntityDecoder,
765 ) -> Result<Matrix4<f64>> {
766 let origin = if let Some(origin_attr) = entity.get(2) {
775 if !origin_attr.is_null() {
776 if let Some(origin_entity) = decoder.resolve_ref(origin_attr)? {
777 if origin_entity.ifc_type == IfcType::IfcCartesianPoint {
778 let coords_attr = origin_entity.get(0);
779 if let Some(coords) = coords_attr.and_then(|a| a.as_list()) {
780 Point3::new(
781 coords.get(0).and_then(|v| v.as_float()).unwrap_or(0.0),
782 coords.get(1).and_then(|v| v.as_float()).unwrap_or(0.0),
783 coords.get(2).and_then(|v| v.as_float()).unwrap_or(0.0),
784 )
785 } else {
786 Point3::origin()
787 }
788 } else {
789 Point3::origin()
790 }
791 } else {
792 Point3::origin()
793 }
794 } else {
795 Point3::origin()
796 }
797 } else {
798 Point3::origin()
799 };
800
801 let scale = entity.get_float(3).unwrap_or(1.0);
803
804 let x_axis = if let Some(axis1_attr) = entity.get(0) {
806 if !axis1_attr.is_null() {
807 if let Some(axis1_entity) = decoder.resolve_ref(axis1_attr)? {
808 self.parse_direction(&axis1_entity)?.normalize()
809 } else {
810 Vector3::new(1.0, 0.0, 0.0)
811 }
812 } else {
813 Vector3::new(1.0, 0.0, 0.0)
814 }
815 } else {
816 Vector3::new(1.0, 0.0, 0.0)
817 };
818
819 let z_axis = if let Some(axis3_attr) = entity.get(4) {
821 if !axis3_attr.is_null() {
822 if let Some(axis3_entity) = decoder.resolve_ref(axis3_attr)? {
823 self.parse_direction(&axis3_entity)?.normalize()
824 } else {
825 Vector3::new(0.0, 0.0, 1.0)
826 }
827 } else {
828 Vector3::new(0.0, 0.0, 1.0)
829 }
830 } else {
831 Vector3::new(0.0, 0.0, 1.0)
832 };
833
834 let y_axis = z_axis.cross(&x_axis).normalize();
836 let x_axis = y_axis.cross(&z_axis).normalize();
837
838 let mut transform = Matrix4::identity();
840 transform[(0, 0)] = x_axis.x * scale;
841 transform[(1, 0)] = x_axis.y * scale;
842 transform[(2, 0)] = x_axis.z * scale;
843 transform[(0, 1)] = y_axis.x * scale;
844 transform[(1, 1)] = y_axis.y * scale;
845 transform[(2, 1)] = y_axis.z * scale;
846 transform[(0, 2)] = z_axis.x * scale;
847 transform[(1, 2)] = z_axis.y * scale;
848 transform[(2, 2)] = z_axis.z * scale;
849 transform[(0, 3)] = origin.x;
850 transform[(1, 3)] = origin.y;
851 transform[(2, 3)] = origin.z;
852
853 Ok(transform)
854 }
855
856 #[inline]
858 fn transform_mesh(&self, mesh: &mut Mesh, transform: &Matrix4<f64>) {
859 mesh.positions.chunks_exact_mut(3).for_each(|chunk| {
861 let point = Point3::new(
862 chunk[0] as f64,
863 chunk[1] as f64,
864 chunk[2] as f64,
865 );
866 let transformed = transform.transform_point(&point);
867 chunk[0] = transformed.x as f32;
868 chunk[1] = transformed.y as f32;
869 chunk[2] = transformed.z as f32;
870 });
871
872 let rotation = transform.fixed_view::<3, 3>(0, 0);
874 mesh.normals.chunks_exact_mut(3).for_each(|chunk| {
875 let normal = Vector3::new(
876 chunk[0] as f64,
877 chunk[1] as f64,
878 chunk[2] as f64,
879 );
880 let transformed = (rotation * normal).normalize();
881 chunk[0] = transformed.x as f32;
882 chunk[1] = transformed.y as f32;
883 chunk[2] = transformed.z as f32;
884 });
885 }
886
887 pub fn schema(&self) -> &IfcSchema {
889 &self.schema
890 }
891}
892
893impl Default for GeometryRouter {
894 fn default() -> Self {
895 Self::new()
896 }
897}
898
899#[cfg(test)]
900mod tests {
901 use super::*;
902
903 #[test]
904 fn test_router_creation() {
905 let router = GeometryRouter::new();
906 assert!(!router.processors.is_empty());
908 }
909
910 #[test]
911 fn test_parse_cartesian_point() {
912 let content = r#"
913#1=IFCCARTESIANPOINT((100.0,200.0,300.0));
914#2=IFCWALL('guid',$,$,$,$,$,#1,$);
915"#;
916
917 let mut decoder = EntityDecoder::new(content);
918 let router = GeometryRouter::new();
919
920 let wall = decoder.decode_by_id(2).unwrap();
921 let point = router.parse_cartesian_point(&wall, &mut decoder, 6).unwrap();
922
923 assert_eq!(point.x, 100.0);
924 assert_eq!(point.y, 200.0);
925 assert_eq!(point.z, 300.0);
926 }
927
928 #[test]
929 fn test_parse_direction() {
930 let content = r#"
931#1=IFCDIRECTION((1.0,0.0,0.0));
932"#;
933
934 let mut decoder = EntityDecoder::new(content);
935 let router = GeometryRouter::new();
936
937 let direction = decoder.decode_by_id(1).unwrap();
938 let vec = router.parse_direction(&direction).unwrap();
939
940 assert_eq!(vec.x, 1.0);
941 assert_eq!(vec.y, 0.0);
942 assert_eq!(vec.z, 0.0);
943 }
944}