1use crate::{Error, Mesh, Point3, Result};
11use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcSchema, IfcType};
12
13use super::advanced_face::process_advanced_face;
14use super::helpers::{extract_loop_points_by_id, FaceData, FaceResult};
15use crate::router::GeometryProcessor;
16
17pub struct FacetedBrepProcessor;
24
25impl FacetedBrepProcessor {
26 pub fn new() -> Self {
27 Self
28 }
29
30 #[allow(dead_code)]
33 #[inline]
34 fn extract_loop_points(
35 &self,
36 loop_entity: &DecodedEntity,
37 decoder: &mut EntityDecoder,
38 ) -> Option<Vec<Point3<f64>>> {
39 let polygon_attr = loop_entity.get(0)?;
41
42 let point_refs = polygon_attr.as_list()?;
44
45 let mut polygon_points = Vec::with_capacity(point_refs.len());
47
48 for point_ref in point_refs {
49 let point_id = point_ref.as_entity_ref()?;
50
51 if let Some((x, y, z)) = decoder.get_cartesian_point_fast(point_id) {
53 polygon_points.push(Point3::new(x, y, z));
54 } else {
55 let point = decoder.decode_by_id(point_id).ok()?;
57 let coords_attr = point.get(0)?;
58 let coords = coords_attr.as_list()?;
59 use ifc_lite_core::AttributeValue;
60 let x = coords.first().and_then(|v: &AttributeValue| v.as_float())?;
61 let y = coords.get(1).and_then(|v: &AttributeValue| v.as_float())?;
62 let z = coords.get(2).and_then(|v: &AttributeValue| v.as_float())?;
63 polygon_points.push(Point3::new(x, y, z));
64 }
65 }
66
67 if polygon_points.len() >= 3 {
68 Some(polygon_points)
69 } else {
70 None
71 }
72 }
73
74 #[inline]
78 fn extract_loop_points_fast(
79 &self,
80 loop_entity_id: u32,
81 decoder: &mut EntityDecoder,
82 ) -> Option<Vec<Point3<f64>>> {
83 let coords = decoder.get_polyloop_coords_cached(loop_entity_id)?;
87
88 let polygon_points: Vec<Point3<f64>> = coords
90 .into_iter()
91 .map(|(x, y, z)| Point3::new(x, y, z))
92 .collect();
93
94 if polygon_points.len() >= 3 {
95 Some(polygon_points)
96 } else {
97 None
98 }
99 }
100
101 #[inline]
109 fn triangulate_face(face: &FaceData, rtc: (f64, f64, f64)) -> FaceResult {
110 let n = face.outer_points.len();
111
112 if n == 3 && face.hole_points.is_empty() {
114 let mut positions = Vec::with_capacity(9);
115 for point in &face.outer_points {
116 positions.push((point.x - rtc.0) as f32);
117 positions.push((point.y - rtc.1) as f32);
118 positions.push((point.z - rtc.2) as f32);
119 }
120 return FaceResult {
121 positions,
122 indices: vec![0, 1, 2],
123 };
124 }
125
126 if n == 4 && face.hole_points.is_empty() {
128 let mut positions = Vec::with_capacity(12);
129 for point in &face.outer_points {
130 positions.push((point.x - rtc.0) as f32);
131 positions.push((point.y - rtc.1) as f32);
132 positions.push((point.z - rtc.2) as f32);
133 }
134 return FaceResult {
135 positions,
136 indices: vec![0, 1, 2, 0, 2, 3],
137 };
138 }
139
140 if face.hole_points.is_empty() && n <= 8 {
142 let mut is_convex = true;
144 if n > 4 {
145 use crate::triangulation::calculate_polygon_normal;
146 let normal = calculate_polygon_normal(&face.outer_points);
147 let mut sign = 0i8;
148
149 for i in 0..n {
150 let p0 = &face.outer_points[i];
151 let p1 = &face.outer_points[(i + 1) % n];
152 let p2 = &face.outer_points[(i + 2) % n];
153
154 let v1 = p1 - p0;
155 let v2 = p2 - p1;
156 let cross = v1.cross(&v2);
157 let dot = cross.dot(&normal);
158
159 if dot.abs() > 1e-10 {
160 let current_sign = if dot > 0.0 { 1i8 } else { -1i8 };
161 if sign == 0 {
162 sign = current_sign;
163 } else if sign != current_sign {
164 is_convex = false;
165 break;
166 }
167 }
168 }
169 }
170
171 if is_convex {
172 let mut positions = Vec::with_capacity(n * 3);
173 for point in &face.outer_points {
174 positions.push((point.x - rtc.0) as f32);
175 positions.push((point.y - rtc.1) as f32);
176 positions.push((point.z - rtc.2) as f32);
177 }
178 let mut indices = Vec::with_capacity((n - 2) * 3);
179 for i in 1..n - 1 {
180 indices.push(0);
181 indices.push(i as u32);
182 indices.push(i as u32 + 1);
183 }
184 return FaceResult { positions, indices };
185 }
186 }
187
188 use crate::triangulation::{
190 calculate_polygon_normal, project_to_2d, project_to_2d_with_basis,
191 triangulate_polygon_with_holes,
192 };
193
194 let mut positions = Vec::new();
195 let mut indices = Vec::new();
196
197 let normal = calculate_polygon_normal(&face.outer_points);
199
200 let (outer_2d, u_axis, v_axis, origin) = project_to_2d(&face.outer_points, &normal);
202
203 let holes_2d: Vec<Vec<nalgebra::Point2<f64>>> = face
205 .hole_points
206 .iter()
207 .map(|hole| project_to_2d_with_basis(hole, &u_axis, &v_axis, &origin))
208 .collect();
209
210 let tri_indices = match triangulate_polygon_with_holes(&outer_2d, &holes_2d) {
212 Ok(idx) => idx,
213 Err(_) => {
214 for point in &face.outer_points {
216 positions.push((point.x - rtc.0) as f32);
217 positions.push((point.y - rtc.1) as f32);
218 positions.push((point.z - rtc.2) as f32);
219 }
220 for i in 1..face.outer_points.len() - 1 {
221 indices.push(0);
222 indices.push(i as u32);
223 indices.push(i as u32 + 1);
224 }
225 return FaceResult { positions, indices };
226 }
227 };
228
229 let mut all_points_3d: Vec<&Point3<f64>> = face.outer_points.iter().collect();
231 for hole in &face.hole_points {
232 all_points_3d.extend(hole.iter());
233 }
234
235 for point in &all_points_3d {
237 positions.push((point.x - rtc.0) as f32);
238 positions.push((point.y - rtc.1) as f32);
239 positions.push((point.z - rtc.2) as f32);
240 }
241
242 for i in (0..tri_indices.len()).step_by(3) {
244 if i + 2 >= tri_indices.len() {
245 break;
246 }
247 indices.push(tri_indices[i] as u32);
248 indices.push(tri_indices[i + 1] as u32);
249 indices.push(tri_indices[i + 2] as u32);
250 }
251
252 FaceResult { positions, indices }
253 }
254
255 pub fn process_batch(
260 &self,
261 brep_ids: &[u32],
262 decoder: &mut EntityDecoder,
263 rtc: (f64, f64, f64),
264 large_coord_threshold_file_units: f64,
265 ) -> Vec<(usize, Mesh)> {
266 use rayon::prelude::*;
267
268 let mut all_faces: Vec<(usize, FaceData)> = Vec::with_capacity(brep_ids.len() * 10);
271 let mut raw_large_by_brep = vec![false; brep_ids.len()];
272
273 for (brep_idx, &brep_id) in brep_ids.iter().enumerate() {
274 let shell_id = match decoder.get_first_entity_ref_fast(brep_id) {
276 Some(id) => id,
277 None => continue,
278 };
279
280 let face_ids = match decoder.get_entity_ref_list_fast(shell_id) {
282 Some(ids) => ids,
283 None => continue,
284 };
285
286 for face_id in face_ids {
288 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
289 Some(ids) => ids,
290 None => continue,
291 };
292
293 let mut outer_bound_points: Option<Vec<Point3<f64>>> = None;
294 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
295
296 for bound_id in bound_ids {
297 let (loop_id, orientation, is_outer) =
300 match decoder.get_face_bound_fast(bound_id) {
301 Some(data) => data,
302 None => continue,
303 };
304
305 let mut points = match self.extract_loop_points_fast(loop_id, decoder) {
307 Some(p) => p,
308 None => continue,
309 };
310
311 if !raw_large_by_brep[brep_idx]
312 && points.iter().any(|point| {
313 point.x.abs().max(point.y.abs()).max(point.z.abs())
314 > large_coord_threshold_file_units
315 })
316 {
317 raw_large_by_brep[brep_idx] = true;
318 }
319
320 if !orientation {
321 points.reverse();
322 }
323
324 if is_outer || outer_bound_points.is_none() {
325 if outer_bound_points.is_some() && is_outer {
326 if let Some(prev_outer) = outer_bound_points.take() {
327 hole_points.push(prev_outer);
328 }
329 }
330 outer_bound_points = Some(points);
331 } else {
332 hole_points.push(points);
333 }
334 }
335
336 if let Some(outer_points) = outer_bound_points {
337 all_faces.push((
338 brep_idx,
339 FaceData {
340 outer_points,
341 hole_points,
342 },
343 ));
344 }
345 }
346 }
347
348 let face_results: Vec<(usize, FaceResult)> = all_faces
351 .par_iter()
352 .map(|(brep_idx, face)| {
353 let face_rtc = if raw_large_by_brep[*brep_idx] {
354 rtc
355 } else {
356 (0.0, 0.0, 0.0)
357 };
358 (*brep_idx, Self::triangulate_face(face, face_rtc))
359 })
360 .collect();
361
362 let mut face_counts = vec![0usize; brep_ids.len()];
365 for (brep_idx, _) in &face_results {
366 face_counts[*brep_idx] += 1;
367 }
368
369 let mut mesh_builders: Vec<(Vec<f32>, Vec<u32>)> = face_counts
371 .iter()
372 .map(|&count| {
373 (
374 Vec::with_capacity(count * 100),
375 Vec::with_capacity(count * 50),
376 )
377 })
378 .collect();
379
380 for (brep_idx, result) in face_results {
382 let (positions, indices) = &mut mesh_builders[brep_idx];
383 let base_idx = (positions.len() / 3) as u32;
384 positions.extend(result.positions);
385 for idx in result.indices {
386 indices.push(base_idx + idx);
387 }
388 }
389
390 mesh_builders
392 .into_iter()
393 .enumerate()
394 .filter(|(_, (positions, _))| !positions.is_empty())
395 .map(|(brep_idx, (positions, indices))| {
396 (
397 brep_idx,
398 Mesh {
399 positions,
400 normals: Vec::new(),
401 indices,
402 rtc_applied: raw_large_by_brep[brep_idx]
404 && (rtc.0 != 0.0 || rtc.1 != 0.0 || rtc.2 != 0.0),
405 },
406 )
407 })
408 .collect()
409 }
410}
411
412impl FacetedBrepProcessor {
413 pub fn process_with_rtc(
417 &self,
418 entity: &DecodedEntity,
419 decoder: &mut EntityDecoder,
420 _schema: &IfcSchema,
421 rtc: (f64, f64, f64),
422 ) -> Result<Mesh> {
423 use rayon::prelude::*;
424
425 let shell_attr = entity
426 .get(0)
427 .ok_or_else(|| Error::geometry("FacetedBrep missing Outer shell".to_string()))?;
428 let shell_id = shell_attr
429 .as_entity_ref()
430 .ok_or_else(|| Error::geometry("Expected entity ref for Outer shell".to_string()))?;
431 let face_ids = decoder
432 .get_entity_ref_list_fast(shell_id)
433 .ok_or_else(|| Error::geometry("Failed to get faces from ClosedShell".to_string()))?;
434
435 let mut face_data_list: Vec<FaceData> = Vec::with_capacity(face_ids.len());
436 for face_id in face_ids {
437 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
438 Some(ids) => ids,
439 None => continue,
440 };
441 let mut outer_bound_points: Option<Vec<Point3<f64>>> = None;
442 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
443 for bound_id in bound_ids {
444 let (loop_id, orientation, is_outer) = match decoder.get_face_bound_fast(bound_id) {
445 Some(data) => data,
446 None => continue,
447 };
448 let mut points = match self.extract_loop_points_fast(loop_id, decoder) {
449 Some(p) => p,
450 None => continue,
451 };
452 if !orientation {
453 points.reverse();
454 }
455 if is_outer || outer_bound_points.is_none() {
456 if outer_bound_points.is_some() && is_outer {
457 if let Some(prev_outer) = outer_bound_points.take() {
458 hole_points.push(prev_outer);
459 }
460 }
461 outer_bound_points = Some(points);
462 } else {
463 hole_points.push(points);
464 }
465 }
466 if let Some(outer_points) = outer_bound_points {
467 face_data_list.push(FaceData {
468 outer_points,
469 hole_points,
470 });
471 }
472 }
473
474 let face_results: Vec<FaceResult> = face_data_list
475 .par_iter()
476 .map(|face| Self::triangulate_face(face, rtc))
477 .collect();
478
479 let total_positions: usize = face_results.iter().map(|r| r.positions.len()).sum();
480 let total_indices: usize = face_results.iter().map(|r| r.indices.len()).sum();
481 let mut positions = Vec::with_capacity(total_positions);
482 let mut indices = Vec::with_capacity(total_indices);
483 for result in face_results {
484 let base_idx = (positions.len() / 3) as u32;
485 positions.extend(result.positions);
486 for idx in result.indices {
487 indices.push(base_idx + idx);
488 }
489 }
490 Ok(Mesh {
491 positions,
492 normals: Vec::new(),
493 indices,
494 rtc_applied: true, })
496 }
497}
498
499impl GeometryProcessor for FacetedBrepProcessor {
500 fn process(
501 &self,
502 entity: &DecodedEntity,
503 decoder: &mut EntityDecoder,
504 _schema: &IfcSchema,
505 ) -> Result<Mesh> {
506 use rayon::prelude::*;
507
508 let shell_attr = entity
513 .get(0)
514 .ok_or_else(|| Error::geometry("FacetedBrep missing Outer shell".to_string()))?;
515
516 let shell_id = shell_attr
517 .as_entity_ref()
518 .ok_or_else(|| Error::geometry("Expected entity ref for Outer shell".to_string()))?;
519
520 let face_ids = decoder
522 .get_entity_ref_list_fast(shell_id)
523 .ok_or_else(|| Error::geometry("Failed to get faces from ClosedShell".to_string()))?;
524
525 let mut face_data_list: Vec<FaceData> = Vec::with_capacity(face_ids.len());
527
528 for face_id in face_ids {
529 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
531 Some(ids) => ids,
532 None => continue,
533 };
534
535 let mut outer_bound_points: Option<Vec<Point3<f64>>> = None;
537 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
538
539 for bound_id in bound_ids {
540 let (loop_id, orientation, is_outer) = match decoder.get_face_bound_fast(bound_id) {
543 Some(data) => data,
544 None => continue,
545 };
546
547 let mut points = match self.extract_loop_points_fast(loop_id, decoder) {
549 Some(p) => p,
550 None => continue,
551 };
552
553 if !orientation {
554 points.reverse();
555 }
556
557 if is_outer || outer_bound_points.is_none() {
558 if outer_bound_points.is_some() && is_outer {
559 if let Some(prev_outer) = outer_bound_points.take() {
560 hole_points.push(prev_outer);
561 }
562 }
563 outer_bound_points = Some(points);
564 } else {
565 hole_points.push(points);
566 }
567 }
568
569 if let Some(outer_points) = outer_bound_points {
570 face_data_list.push(FaceData {
571 outer_points,
572 hole_points,
573 });
574 }
575 }
576
577 let face_results: Vec<FaceResult> = face_data_list
582 .par_iter()
583 .map(|face| Self::triangulate_face(face, (0.0, 0.0, 0.0)))
584 .collect();
585
586 let total_positions: usize = face_results.iter().map(|r| r.positions.len()).sum();
589 let total_indices: usize = face_results.iter().map(|r| r.indices.len()).sum();
590
591 let mut positions = Vec::with_capacity(total_positions);
592 let mut indices = Vec::with_capacity(total_indices);
593
594 for result in face_results {
595 let base_idx = (positions.len() / 3) as u32;
596 positions.extend(result.positions);
597
598 for idx in result.indices {
600 indices.push(base_idx + idx);
601 }
602 }
603
604 Ok(Mesh {
605 positions,
606 normals: Vec::new(),
607 indices,
608 rtc_applied: false,
609 })
610 }
611
612 fn supported_types(&self) -> Vec<IfcType> {
613 vec![IfcType::IfcFacetedBrep]
614 }
615}
616
617impl Default for FacetedBrepProcessor {
618 fn default() -> Self {
619 Self::new()
620 }
621}
622
623pub struct FaceBasedSurfaceModelProcessor;
635
636impl FaceBasedSurfaceModelProcessor {
637 pub fn new() -> Self {
638 Self
639 }
640}
641
642impl GeometryProcessor for FaceBasedSurfaceModelProcessor {
643 fn process(
644 &self,
645 entity: &DecodedEntity,
646 decoder: &mut EntityDecoder,
647 _schema: &IfcSchema,
648 ) -> Result<Mesh> {
649 let faces_attr = entity.get(0).ok_or_else(|| {
653 Error::geometry("FaceBasedSurfaceModel missing FbsmFaces".to_string())
654 })?;
655
656 let face_set_refs = faces_attr
657 .as_list()
658 .ok_or_else(|| Error::geometry("Expected face set list".to_string()))?;
659
660 let mut all_positions = Vec::new();
661 let mut all_indices = Vec::new();
662
663 for face_set_ref in face_set_refs {
665 let face_set_id = face_set_ref.as_entity_ref().ok_or_else(|| {
666 Error::geometry("Expected entity reference for face set".to_string())
667 })?;
668
669 let face_ids = match decoder.get_entity_ref_list_fast(face_set_id) {
671 Some(ids) => ids,
672 None => continue,
673 };
674
675 for face_id in face_ids {
677 let face = match decoder.decode_by_id(face_id) {
681 Ok(f) => f,
682 Err(_) => continue,
683 };
684
685 if face.ifc_type == IfcType::IfcAdvancedFace {
686 let (positions, indices) = match process_advanced_face(&face, decoder) {
688 Ok(result) => result,
689 Err(_) => continue,
690 };
691
692 if !positions.is_empty() {
693 let base_idx = (all_positions.len() / 3) as u32;
694 all_positions.extend(positions);
695 for idx in indices {
696 all_indices.push(base_idx + idx);
697 }
698 }
699 } else {
700 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
702 Some(ids) => ids,
703 None => continue,
704 };
705
706 let mut outer_points: Option<Vec<Point3<f64>>> = None;
707 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
708
709 for bound_id in bound_ids {
710 let (loop_id, orientation, is_outer) =
713 match decoder.get_face_bound_fast(bound_id) {
714 Some(data) => data,
715 None => continue,
716 };
717
718 let mut points = match extract_loop_points_by_id(loop_id, decoder) {
720 Some(p) => p,
721 None => continue,
722 };
723
724 if !orientation {
725 points.reverse();
726 }
727
728 if is_outer || outer_points.is_none() {
729 outer_points = Some(points);
730 } else {
731 hole_points.push(points);
732 }
733 }
734
735 if let Some(outer) = outer_points {
737 if outer.len() >= 3 {
738 let base_idx = (all_positions.len() / 3) as u32;
739
740 for p in &outer {
742 all_positions.push(p.x as f32);
743 all_positions.push(p.y as f32);
744 all_positions.push(p.z as f32);
745 }
746
747 for i in 1..outer.len() - 1 {
749 all_indices.push(base_idx);
750 all_indices.push(base_idx + i as u32);
751 all_indices.push(base_idx + i as u32 + 1);
752 }
753 }
754 }
755 }
756 }
757 }
758
759 Ok(Mesh {
760 positions: all_positions,
761 normals: Vec::new(),
762 indices: all_indices,
763 rtc_applied: false,
764 })
765 }
766
767 fn supported_types(&self) -> Vec<IfcType> {
768 vec![IfcType::IfcFaceBasedSurfaceModel]
769 }
770}
771
772impl Default for FaceBasedSurfaceModelProcessor {
773 fn default() -> Self {
774 Self::new()
775 }
776}
777
778pub struct ShellBasedSurfaceModelProcessor;
790
791impl ShellBasedSurfaceModelProcessor {
792 pub fn new() -> Self {
793 Self
794 }
795}
796
797impl GeometryProcessor for ShellBasedSurfaceModelProcessor {
798 fn process(
799 &self,
800 entity: &DecodedEntity,
801 decoder: &mut EntityDecoder,
802 _schema: &IfcSchema,
803 ) -> Result<Mesh> {
804 let shells_attr = entity.get(0).ok_or_else(|| {
808 Error::geometry("ShellBasedSurfaceModel missing SbsmBoundary".to_string())
809 })?;
810
811 let shell_refs = shells_attr
812 .as_list()
813 .ok_or_else(|| Error::geometry("Expected shell list".to_string()))?;
814
815 let mut all_positions = Vec::new();
816 let mut all_indices = Vec::new();
817
818 for shell_ref in shell_refs {
820 let shell_id = shell_ref.as_entity_ref().ok_or_else(|| {
821 Error::geometry("Expected entity reference for shell".to_string())
822 })?;
823
824 let face_ids = match decoder.get_entity_ref_list_fast(shell_id) {
827 Some(ids) => ids,
828 None => continue,
829 };
830
831 for face_id in face_ids {
833 let face = match decoder.decode_by_id(face_id) {
838 Ok(f) => f,
839 Err(_) => continue,
840 };
841
842 if face.ifc_type == IfcType::IfcAdvancedFace {
843 let (positions, indices) = match process_advanced_face(&face, decoder) {
845 Ok(result) => result,
846 Err(_) => continue,
847 };
848
849 if !positions.is_empty() {
850 let base_idx = (all_positions.len() / 3) as u32;
851 all_positions.extend(positions);
852 for idx in indices {
853 all_indices.push(base_idx + idx);
854 }
855 }
856 } else {
857 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
859 Some(ids) => ids,
860 None => continue,
861 };
862
863 let mut outer_points: Option<Vec<Point3<f64>>> = None;
864 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
865
866 for bound_id in bound_ids {
867 let (loop_id, orientation, is_outer) =
869 match decoder.get_face_bound_fast(bound_id) {
870 Some(data) => data,
871 None => continue,
872 };
873
874 let mut points = match extract_loop_points_by_id(loop_id, decoder) {
876 Some(p) => p,
877 None => continue,
878 };
879
880 if !orientation {
881 points.reverse();
882 }
883
884 if is_outer || outer_points.is_none() {
885 outer_points = Some(points);
886 } else {
887 hole_points.push(points);
888 }
889 }
890
891 if let Some(outer) = outer_points {
893 if outer.len() >= 3 {
894 let base_idx = (all_positions.len() / 3) as u32;
895
896 for p in &outer {
898 all_positions.push(p.x as f32);
899 all_positions.push(p.y as f32);
900 all_positions.push(p.z as f32);
901 }
902
903 for i in 1..outer.len() - 1 {
905 all_indices.push(base_idx);
906 all_indices.push(base_idx + i as u32);
907 all_indices.push(base_idx + i as u32 + 1);
908 }
909 }
910 }
911 }
912 }
913 }
914
915 Ok(Mesh {
916 positions: all_positions,
917 normals: Vec::new(),
918 indices: all_indices,
919 rtc_applied: false,
920 })
921 }
922
923 fn supported_types(&self) -> Vec<IfcType> {
924 vec![IfcType::IfcShellBasedSurfaceModel]
925 }
926}
927
928impl Default for ShellBasedSurfaceModelProcessor {
929 fn default() -> Self {
930 Self::new()
931 }
932}