ifc_lite_geometry/processors/
brep.rs1use crate::{Error, Mesh, Point3, Result};
11use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcSchema, IfcType};
12
13use crate::router::GeometryProcessor;
14use super::helpers::{extract_loop_points_by_id, FaceData, FaceResult};
15
16pub struct FacetedBrepProcessor;
23
24impl FacetedBrepProcessor {
25 pub fn new() -> Self {
26 Self
27 }
28
29 #[allow(dead_code)]
32 #[inline]
33 fn extract_loop_points(
34 &self,
35 loop_entity: &DecodedEntity,
36 decoder: &mut EntityDecoder,
37 ) -> Option<Vec<Point3<f64>>> {
38 let polygon_attr = loop_entity.get(0)?;
40
41 let point_refs = polygon_attr.as_list()?;
43
44 let mut polygon_points = Vec::with_capacity(point_refs.len());
46
47 for point_ref in point_refs {
48 let point_id = point_ref.as_entity_ref()?;
49
50 if let Some((x, y, z)) = decoder.get_cartesian_point_fast(point_id) {
52 polygon_points.push(Point3::new(x, y, z));
53 } else {
54 let point = decoder.decode_by_id(point_id).ok()?;
56 let coords_attr = point.get(0)?;
57 let coords = coords_attr.as_list()?;
58 use ifc_lite_core::AttributeValue;
59 let x = coords.first().and_then(|v: &AttributeValue| v.as_float())?;
60 let y = coords.get(1).and_then(|v: &AttributeValue| v.as_float())?;
61 let z = coords.get(2).and_then(|v: &AttributeValue| v.as_float())?;
62 polygon_points.push(Point3::new(x, y, z));
63 }
64 }
65
66 if polygon_points.len() >= 3 {
67 Some(polygon_points)
68 } else {
69 None
70 }
71 }
72
73 #[inline]
77 fn extract_loop_points_fast(
78 &self,
79 loop_entity_id: u32,
80 decoder: &mut EntityDecoder,
81 ) -> Option<Vec<Point3<f64>>> {
82 let coords = decoder.get_polyloop_coords_cached(loop_entity_id)?;
86
87 let polygon_points: Vec<Point3<f64>> = coords
89 .into_iter()
90 .map(|(x, y, z)| Point3::new(x, y, z))
91 .collect();
92
93 if polygon_points.len() >= 3 {
94 Some(polygon_points)
95 } else {
96 None
97 }
98 }
99
100 #[inline]
103 fn triangulate_face(face: &FaceData) -> FaceResult {
104 let n = face.outer_points.len();
105
106 if n == 3 && face.hole_points.is_empty() {
108 let mut positions = Vec::with_capacity(9);
109 for point in &face.outer_points {
110 positions.push(point.x as f32);
111 positions.push(point.y as f32);
112 positions.push(point.z as f32);
113 }
114 return FaceResult {
115 positions,
116 indices: vec![0, 1, 2],
117 };
118 }
119
120 if n == 4 && face.hole_points.is_empty() {
122 let mut positions = Vec::with_capacity(12);
123 for point in &face.outer_points {
124 positions.push(point.x as f32);
125 positions.push(point.y as f32);
126 positions.push(point.z as f32);
127 }
128 return FaceResult {
129 positions,
130 indices: vec![0, 1, 2, 0, 2, 3],
131 };
132 }
133
134 if face.hole_points.is_empty() && n <= 8 {
136 let mut is_convex = true;
138 if n > 4 {
139 use crate::triangulation::calculate_polygon_normal;
140 let normal = calculate_polygon_normal(&face.outer_points);
141 let mut sign = 0i8;
142
143 for i in 0..n {
144 let p0 = &face.outer_points[i];
145 let p1 = &face.outer_points[(i + 1) % n];
146 let p2 = &face.outer_points[(i + 2) % n];
147
148 let v1 = p1 - p0;
149 let v2 = p2 - p1;
150 let cross = v1.cross(&v2);
151 let dot = cross.dot(&normal);
152
153 if dot.abs() > 1e-10 {
154 let current_sign = if dot > 0.0 { 1i8 } else { -1i8 };
155 if sign == 0 {
156 sign = current_sign;
157 } else if sign != current_sign {
158 is_convex = false;
159 break;
160 }
161 }
162 }
163 }
164
165 if is_convex {
166 let mut positions = Vec::with_capacity(n * 3);
167 for point in &face.outer_points {
168 positions.push(point.x as f32);
169 positions.push(point.y as f32);
170 positions.push(point.z as f32);
171 }
172 let mut indices = Vec::with_capacity((n - 2) * 3);
173 for i in 1..n - 1 {
174 indices.push(0);
175 indices.push(i as u32);
176 indices.push(i as u32 + 1);
177 }
178 return FaceResult { positions, indices };
179 }
180 }
181
182 use crate::triangulation::{
184 calculate_polygon_normal, project_to_2d, project_to_2d_with_basis,
185 triangulate_polygon_with_holes,
186 };
187
188 let mut positions = Vec::new();
189 let mut indices = Vec::new();
190
191 let normal = calculate_polygon_normal(&face.outer_points);
193
194 let (outer_2d, u_axis, v_axis, origin) = project_to_2d(&face.outer_points, &normal);
196
197 let holes_2d: Vec<Vec<nalgebra::Point2<f64>>> = face
199 .hole_points
200 .iter()
201 .map(|hole| project_to_2d_with_basis(hole, &u_axis, &v_axis, &origin))
202 .collect();
203
204 let tri_indices = match triangulate_polygon_with_holes(&outer_2d, &holes_2d) {
206 Ok(idx) => idx,
207 Err(_) => {
208 for point in &face.outer_points {
210 positions.push(point.x as f32);
211 positions.push(point.y as f32);
212 positions.push(point.z as f32);
213 }
214 for i in 1..face.outer_points.len() - 1 {
215 indices.push(0);
216 indices.push(i as u32);
217 indices.push(i as u32 + 1);
218 }
219 return FaceResult { positions, indices };
220 }
221 };
222
223 let mut all_points_3d: Vec<&Point3<f64>> = face.outer_points.iter().collect();
225 for hole in &face.hole_points {
226 all_points_3d.extend(hole.iter());
227 }
228
229 for point in &all_points_3d {
231 positions.push(point.x as f32);
232 positions.push(point.y as f32);
233 positions.push(point.z as f32);
234 }
235
236 for i in (0..tri_indices.len()).step_by(3) {
238 indices.push(tri_indices[i] as u32);
239 indices.push(tri_indices[i + 1] as u32);
240 indices.push(tri_indices[i + 2] as u32);
241 }
242
243 FaceResult { positions, indices }
244 }
245
246 pub fn process_batch(
250 &self,
251 brep_ids: &[u32],
252 decoder: &mut EntityDecoder,
253 ) -> Vec<(usize, Mesh)> {
254 #[cfg(not(target_arch = "wasm32"))]
255 use rayon::prelude::*;
256
257 let mut all_faces: Vec<(usize, FaceData)> = Vec::with_capacity(brep_ids.len() * 10);
260
261 for (brep_idx, &brep_id) in brep_ids.iter().enumerate() {
262 let shell_id = match decoder.get_first_entity_ref_fast(brep_id) {
264 Some(id) => id,
265 None => continue,
266 };
267
268 let face_ids = match decoder.get_entity_ref_list_fast(shell_id) {
270 Some(ids) => ids,
271 None => continue,
272 };
273
274 for face_id in face_ids {
276 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
277 Some(ids) => ids,
278 None => continue,
279 };
280
281 let mut outer_bound_points: Option<Vec<Point3<f64>>> = None;
282 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
283
284 for bound_id in bound_ids {
285 let (loop_id, orientation, is_outer) =
288 match decoder.get_face_bound_fast(bound_id) {
289 Some(data) => data,
290 None => continue,
291 };
292
293 let mut points = match self.extract_loop_points_fast(loop_id, decoder) {
295 Some(p) => p,
296 None => continue,
297 };
298
299 if !orientation {
300 points.reverse();
301 }
302
303 if is_outer || outer_bound_points.is_none() {
304 if outer_bound_points.is_some() && is_outer {
305 if let Some(prev_outer) = outer_bound_points.take() {
306 hole_points.push(prev_outer);
307 }
308 }
309 outer_bound_points = Some(points);
310 } else {
311 hole_points.push(points);
312 }
313 }
314
315 if let Some(outer_points) = outer_bound_points {
316 all_faces.push((
317 brep_idx,
318 FaceData {
319 outer_points,
320 hole_points,
321 },
322 ));
323 }
324 }
325 }
326
327 #[cfg(not(target_arch = "wasm32"))]
331 let face_results: Vec<(usize, FaceResult)> = all_faces
332 .par_iter()
333 .map(|(brep_idx, face)| (*brep_idx, Self::triangulate_face(face)))
334 .collect();
335
336 #[cfg(target_arch = "wasm32")]
337 let face_results: Vec<(usize, FaceResult)> = all_faces
338 .iter()
339 .map(|(brep_idx, face)| (*brep_idx, Self::triangulate_face(face)))
340 .collect();
341
342 let mut face_counts = vec![0usize; brep_ids.len()];
345 for (brep_idx, _) in &face_results {
346 face_counts[*brep_idx] += 1;
347 }
348
349 let mut mesh_builders: Vec<(Vec<f32>, Vec<u32>)> = face_counts
351 .iter()
352 .map(|&count| {
353 (
354 Vec::with_capacity(count * 100),
355 Vec::with_capacity(count * 50),
356 )
357 })
358 .collect();
359
360 for (brep_idx, result) in face_results {
362 let (positions, indices) = &mut mesh_builders[brep_idx];
363 let base_idx = (positions.len() / 3) as u32;
364 positions.extend(result.positions);
365 for idx in result.indices {
366 indices.push(base_idx + idx);
367 }
368 }
369
370 mesh_builders
372 .into_iter()
373 .enumerate()
374 .filter(|(_, (positions, _))| !positions.is_empty())
375 .map(|(brep_idx, (positions, indices))| {
376 (
377 brep_idx,
378 Mesh {
379 positions,
380 normals: Vec::new(),
381 indices,
382 },
383 )
384 })
385 .collect()
386 }
387}
388
389impl GeometryProcessor for FacetedBrepProcessor {
390 fn process(
391 &self,
392 entity: &DecodedEntity,
393 decoder: &mut EntityDecoder,
394 _schema: &IfcSchema,
395 ) -> Result<Mesh> {
396 #[cfg(not(target_arch = "wasm32"))]
397 use rayon::prelude::*;
398
399 let shell_attr = entity
404 .get(0)
405 .ok_or_else(|| Error::geometry("FacetedBrep missing Outer shell".to_string()))?;
406
407 let shell_id = shell_attr
408 .as_entity_ref()
409 .ok_or_else(|| Error::geometry("Expected entity ref for Outer shell".to_string()))?;
410
411 let face_ids = decoder
413 .get_entity_ref_list_fast(shell_id)
414 .ok_or_else(|| Error::geometry("Failed to get faces from ClosedShell".to_string()))?;
415
416 let mut face_data_list: Vec<FaceData> = Vec::with_capacity(face_ids.len());
418
419 for face_id in face_ids {
420 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
422 Some(ids) => ids,
423 None => continue,
424 };
425
426 let mut outer_bound_points: Option<Vec<Point3<f64>>> = None;
428 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
429
430 for bound_id in bound_ids {
431 let (loop_id, orientation, is_outer) =
434 match decoder.get_face_bound_fast(bound_id) {
435 Some(data) => data,
436 None => continue,
437 };
438
439 let mut points = match self.extract_loop_points_fast(loop_id, decoder) {
441 Some(p) => p,
442 None => continue,
443 };
444
445 if !orientation {
446 points.reverse();
447 }
448
449 if is_outer || outer_bound_points.is_none() {
450 if outer_bound_points.is_some() && is_outer {
451 if let Some(prev_outer) = outer_bound_points.take() {
452 hole_points.push(prev_outer);
453 }
454 }
455 outer_bound_points = Some(points);
456 } else {
457 hole_points.push(points);
458 }
459 }
460
461 if let Some(outer_points) = outer_bound_points {
462 face_data_list.push(FaceData {
463 outer_points,
464 hole_points,
465 });
466 }
467 }
468
469 #[cfg(not(target_arch = "wasm32"))]
473 let face_results: Vec<FaceResult> = face_data_list
474 .par_iter()
475 .map(Self::triangulate_face)
476 .collect();
477
478 #[cfg(target_arch = "wasm32")]
479 let face_results: Vec<FaceResult> = face_data_list
480 .iter()
481 .map(Self::triangulate_face)
482 .collect();
483
484 let total_positions: usize = face_results.iter().map(|r| r.positions.len()).sum();
487 let total_indices: usize = face_results.iter().map(|r| r.indices.len()).sum();
488
489 let mut positions = Vec::with_capacity(total_positions);
490 let mut indices = Vec::with_capacity(total_indices);
491
492 for result in face_results {
493 let base_idx = (positions.len() / 3) as u32;
494 positions.extend(result.positions);
495
496 for idx in result.indices {
498 indices.push(base_idx + idx);
499 }
500 }
501
502 Ok(Mesh {
503 positions,
504 normals: Vec::new(),
505 indices,
506 })
507 }
508
509 fn supported_types(&self) -> Vec<IfcType> {
510 vec![IfcType::IfcFacetedBrep]
511 }
512}
513
514impl Default for FacetedBrepProcessor {
515 fn default() -> Self {
516 Self::new()
517 }
518}
519
520pub struct FaceBasedSurfaceModelProcessor;
526
527impl FaceBasedSurfaceModelProcessor {
528 pub fn new() -> Self {
529 Self
530 }
531}
532
533impl GeometryProcessor for FaceBasedSurfaceModelProcessor {
534 fn process(
535 &self,
536 entity: &DecodedEntity,
537 decoder: &mut EntityDecoder,
538 _schema: &IfcSchema,
539 ) -> Result<Mesh> {
540 let faces_attr = entity
544 .get(0)
545 .ok_or_else(|| Error::geometry("FaceBasedSurfaceModel missing FbsmFaces".to_string()))?;
546
547 let face_set_refs = faces_attr
548 .as_list()
549 .ok_or_else(|| Error::geometry("Expected face set list".to_string()))?;
550
551 let mut all_positions = Vec::new();
552 let mut all_indices = Vec::new();
553
554 for face_set_ref in face_set_refs {
556 let face_set_id = face_set_ref.as_entity_ref().ok_or_else(|| {
557 Error::geometry("Expected entity reference for face set".to_string())
558 })?;
559
560 let face_ids = match decoder.get_entity_ref_list_fast(face_set_id) {
562 Some(ids) => ids,
563 None => continue,
564 };
565
566 for face_id in face_ids {
568 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
570 Some(ids) => ids,
571 None => continue,
572 };
573
574 let mut outer_points: Option<Vec<Point3<f64>>> = None;
575 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
576
577 for bound_id in bound_ids {
578 let (loop_id, orientation, is_outer) =
581 match decoder.get_face_bound_fast(bound_id) {
582 Some(data) => data,
583 None => continue,
584 };
585
586 let mut points = match extract_loop_points_by_id(loop_id, decoder) {
588 Some(p) => p,
589 None => continue,
590 };
591
592 if !orientation {
593 points.reverse();
594 }
595
596 if is_outer || outer_points.is_none() {
597 outer_points = Some(points);
598 } else {
599 hole_points.push(points);
600 }
601 }
602
603 if let Some(outer) = outer_points {
605 if outer.len() >= 3 {
606 let base_idx = (all_positions.len() / 3) as u32;
607
608 for p in &outer {
610 all_positions.push(p.x as f32);
611 all_positions.push(p.y as f32);
612 all_positions.push(p.z as f32);
613 }
614
615 for i in 1..outer.len() - 1 {
617 all_indices.push(base_idx);
618 all_indices.push(base_idx + i as u32);
619 all_indices.push(base_idx + i as u32 + 1);
620 }
621 }
622 }
623 }
624 }
625
626 Ok(Mesh {
627 positions: all_positions,
628 normals: Vec::new(),
629 indices: all_indices,
630 })
631 }
632
633 fn supported_types(&self) -> Vec<IfcType> {
634 vec![IfcType::IfcFaceBasedSurfaceModel]
635 }
636}
637
638impl Default for FaceBasedSurfaceModelProcessor {
639 fn default() -> Self {
640 Self::new()
641 }
642}
643
644pub struct ShellBasedSurfaceModelProcessor;
650
651impl ShellBasedSurfaceModelProcessor {
652 pub fn new() -> Self {
653 Self
654 }
655}
656
657impl GeometryProcessor for ShellBasedSurfaceModelProcessor {
658 fn process(
659 &self,
660 entity: &DecodedEntity,
661 decoder: &mut EntityDecoder,
662 _schema: &IfcSchema,
663 ) -> Result<Mesh> {
664 let shells_attr = entity
668 .get(0)
669 .ok_or_else(|| Error::geometry("ShellBasedSurfaceModel missing SbsmBoundary".to_string()))?;
670
671 let shell_refs = shells_attr
672 .as_list()
673 .ok_or_else(|| Error::geometry("Expected shell list".to_string()))?;
674
675 let mut all_positions = Vec::new();
676 let mut all_indices = Vec::new();
677
678 for shell_ref in shell_refs {
680 let shell_id = shell_ref.as_entity_ref().ok_or_else(|| {
681 Error::geometry("Expected entity reference for shell".to_string())
682 })?;
683
684 let face_ids = match decoder.get_entity_ref_list_fast(shell_id) {
687 Some(ids) => ids,
688 None => continue,
689 };
690
691 for face_id in face_ids {
693 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
695 Some(ids) => ids,
696 None => continue,
697 };
698
699 let mut outer_points: Option<Vec<Point3<f64>>> = None;
700 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
701
702 for bound_id in bound_ids {
703 let (loop_id, orientation, is_outer) =
705 match decoder.get_face_bound_fast(bound_id) {
706 Some(data) => data,
707 None => continue,
708 };
709
710 let mut points = match extract_loop_points_by_id(loop_id, decoder) {
712 Some(p) => p,
713 None => continue,
714 };
715
716 if !orientation {
717 points.reverse();
718 }
719
720 if is_outer || outer_points.is_none() {
721 outer_points = Some(points);
722 } else {
723 hole_points.push(points);
724 }
725 }
726
727 if let Some(outer) = outer_points {
729 if outer.len() >= 3 {
730 let base_idx = (all_positions.len() / 3) as u32;
731
732 for p in &outer {
734 all_positions.push(p.x as f32);
735 all_positions.push(p.y as f32);
736 all_positions.push(p.z as f32);
737 }
738
739 for i in 1..outer.len() - 1 {
741 all_indices.push(base_idx);
742 all_indices.push(base_idx + i as u32);
743 all_indices.push(base_idx + i as u32 + 1);
744 }
745 }
746 }
747 }
748 }
749
750 Ok(Mesh {
751 positions: all_positions,
752 normals: Vec::new(),
753 indices: all_indices,
754 })
755 }
756
757 fn supported_types(&self) -> Vec<IfcType> {
758 vec![IfcType::IfcShellBasedSurfaceModel]
759 }
760}
761
762impl Default for ShellBasedSurfaceModelProcessor {
763 fn default() -> Self {
764 Self::new()
765 }
766}