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 if i + 2 >= tri_indices.len() {
239 break;
240 }
241 indices.push(tri_indices[i] as u32);
242 indices.push(tri_indices[i + 1] as u32);
243 indices.push(tri_indices[i + 2] as u32);
244 }
245
246 FaceResult { positions, indices }
247 }
248
249 pub fn process_batch(
253 &self,
254 brep_ids: &[u32],
255 decoder: &mut EntityDecoder,
256 ) -> Vec<(usize, Mesh)> {
257 use rayon::prelude::*;
258
259 let mut all_faces: Vec<(usize, FaceData)> = Vec::with_capacity(brep_ids.len() * 10);
262
263 for (brep_idx, &brep_id) in brep_ids.iter().enumerate() {
264 let shell_id = match decoder.get_first_entity_ref_fast(brep_id) {
266 Some(id) => id,
267 None => continue,
268 };
269
270 let face_ids = match decoder.get_entity_ref_list_fast(shell_id) {
272 Some(ids) => ids,
273 None => continue,
274 };
275
276 for face_id in face_ids {
278 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
279 Some(ids) => ids,
280 None => continue,
281 };
282
283 let mut outer_bound_points: Option<Vec<Point3<f64>>> = None;
284 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
285
286 for bound_id in bound_ids {
287 let (loop_id, orientation, is_outer) =
290 match decoder.get_face_bound_fast(bound_id) {
291 Some(data) => data,
292 None => continue,
293 };
294
295 let mut points = match self.extract_loop_points_fast(loop_id, decoder) {
297 Some(p) => p,
298 None => continue,
299 };
300
301 if !orientation {
302 points.reverse();
303 }
304
305 if is_outer || outer_bound_points.is_none() {
306 if outer_bound_points.is_some() && is_outer {
307 if let Some(prev_outer) = outer_bound_points.take() {
308 hole_points.push(prev_outer);
309 }
310 }
311 outer_bound_points = Some(points);
312 } else {
313 hole_points.push(points);
314 }
315 }
316
317 if let Some(outer_points) = outer_bound_points {
318 all_faces.push((
319 brep_idx,
320 FaceData {
321 outer_points,
322 hole_points,
323 },
324 ));
325 }
326 }
327 }
328
329 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 let mut face_counts = vec![0usize; brep_ids.len()];
339 for (brep_idx, _) in &face_results {
340 face_counts[*brep_idx] += 1;
341 }
342
343 let mut mesh_builders: Vec<(Vec<f32>, Vec<u32>)> = face_counts
345 .iter()
346 .map(|&count| {
347 (
348 Vec::with_capacity(count * 100),
349 Vec::with_capacity(count * 50),
350 )
351 })
352 .collect();
353
354 for (brep_idx, result) in face_results {
356 let (positions, indices) = &mut mesh_builders[brep_idx];
357 let base_idx = (positions.len() / 3) as u32;
358 positions.extend(result.positions);
359 for idx in result.indices {
360 indices.push(base_idx + idx);
361 }
362 }
363
364 mesh_builders
366 .into_iter()
367 .enumerate()
368 .filter(|(_, (positions, _))| !positions.is_empty())
369 .map(|(brep_idx, (positions, indices))| {
370 (
371 brep_idx,
372 Mesh {
373 positions,
374 normals: Vec::new(),
375 indices,
376 },
377 )
378 })
379 .collect()
380 }
381}
382
383impl GeometryProcessor for FacetedBrepProcessor {
384 fn process(
385 &self,
386 entity: &DecodedEntity,
387 decoder: &mut EntityDecoder,
388 _schema: &IfcSchema,
389 ) -> Result<Mesh> {
390 use rayon::prelude::*;
391
392 let shell_attr = entity
397 .get(0)
398 .ok_or_else(|| Error::geometry("FacetedBrep missing Outer shell".to_string()))?;
399
400 let shell_id = shell_attr
401 .as_entity_ref()
402 .ok_or_else(|| Error::geometry("Expected entity ref for Outer shell".to_string()))?;
403
404 let face_ids = decoder
406 .get_entity_ref_list_fast(shell_id)
407 .ok_or_else(|| Error::geometry("Failed to get faces from ClosedShell".to_string()))?;
408
409 let mut face_data_list: Vec<FaceData> = Vec::with_capacity(face_ids.len());
411
412 for face_id in face_ids {
413 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
415 Some(ids) => ids,
416 None => continue,
417 };
418
419 let mut outer_bound_points: Option<Vec<Point3<f64>>> = None;
421 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
422
423 for bound_id in bound_ids {
424 let (loop_id, orientation, is_outer) =
427 match decoder.get_face_bound_fast(bound_id) {
428 Some(data) => data,
429 None => continue,
430 };
431
432 let mut points = match self.extract_loop_points_fast(loop_id, decoder) {
434 Some(p) => p,
435 None => continue,
436 };
437
438 if !orientation {
439 points.reverse();
440 }
441
442 if is_outer || outer_bound_points.is_none() {
443 if outer_bound_points.is_some() && is_outer {
444 if let Some(prev_outer) = outer_bound_points.take() {
445 hole_points.push(prev_outer);
446 }
447 }
448 outer_bound_points = Some(points);
449 } else {
450 hole_points.push(points);
451 }
452 }
453
454 if let Some(outer_points) = outer_bound_points {
455 face_data_list.push(FaceData {
456 outer_points,
457 hole_points,
458 });
459 }
460 }
461
462 let face_results: Vec<FaceResult> = face_data_list
464 .par_iter()
465 .map(Self::triangulate_face)
466 .collect();
467
468 let total_positions: usize = face_results.iter().map(|r| r.positions.len()).sum();
471 let total_indices: usize = face_results.iter().map(|r| r.indices.len()).sum();
472
473 let mut positions = Vec::with_capacity(total_positions);
474 let mut indices = Vec::with_capacity(total_indices);
475
476 for result in face_results {
477 let base_idx = (positions.len() / 3) as u32;
478 positions.extend(result.positions);
479
480 for idx in result.indices {
482 indices.push(base_idx + idx);
483 }
484 }
485
486 Ok(Mesh {
487 positions,
488 normals: Vec::new(),
489 indices,
490 })
491 }
492
493 fn supported_types(&self) -> Vec<IfcType> {
494 vec![IfcType::IfcFacetedBrep]
495 }
496}
497
498impl Default for FacetedBrepProcessor {
499 fn default() -> Self {
500 Self::new()
501 }
502}
503
504pub struct FaceBasedSurfaceModelProcessor;
510
511impl FaceBasedSurfaceModelProcessor {
512 pub fn new() -> Self {
513 Self
514 }
515}
516
517impl GeometryProcessor for FaceBasedSurfaceModelProcessor {
518 fn process(
519 &self,
520 entity: &DecodedEntity,
521 decoder: &mut EntityDecoder,
522 _schema: &IfcSchema,
523 ) -> Result<Mesh> {
524 let faces_attr = entity
528 .get(0)
529 .ok_or_else(|| Error::geometry("FaceBasedSurfaceModel missing FbsmFaces".to_string()))?;
530
531 let face_set_refs = faces_attr
532 .as_list()
533 .ok_or_else(|| Error::geometry("Expected face set list".to_string()))?;
534
535 let mut all_positions = Vec::new();
536 let mut all_indices = Vec::new();
537
538 for face_set_ref in face_set_refs {
540 let face_set_id = face_set_ref.as_entity_ref().ok_or_else(|| {
541 Error::geometry("Expected entity reference for face set".to_string())
542 })?;
543
544 let face_ids = match decoder.get_entity_ref_list_fast(face_set_id) {
546 Some(ids) => ids,
547 None => continue,
548 };
549
550 for face_id in face_ids {
552 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
554 Some(ids) => ids,
555 None => continue,
556 };
557
558 let mut outer_points: Option<Vec<Point3<f64>>> = None;
559 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
560
561 for bound_id in bound_ids {
562 let (loop_id, orientation, is_outer) =
565 match decoder.get_face_bound_fast(bound_id) {
566 Some(data) => data,
567 None => continue,
568 };
569
570 let mut points = match extract_loop_points_by_id(loop_id, decoder) {
572 Some(p) => p,
573 None => continue,
574 };
575
576 if !orientation {
577 points.reverse();
578 }
579
580 if is_outer || outer_points.is_none() {
581 outer_points = Some(points);
582 } else {
583 hole_points.push(points);
584 }
585 }
586
587 if let Some(outer) = outer_points {
589 if outer.len() >= 3 {
590 let base_idx = (all_positions.len() / 3) as u32;
591
592 for p in &outer {
594 all_positions.push(p.x as f32);
595 all_positions.push(p.y as f32);
596 all_positions.push(p.z as f32);
597 }
598
599 for i in 1..outer.len() - 1 {
601 all_indices.push(base_idx);
602 all_indices.push(base_idx + i as u32);
603 all_indices.push(base_idx + i as u32 + 1);
604 }
605 }
606 }
607 }
608 }
609
610 Ok(Mesh {
611 positions: all_positions,
612 normals: Vec::new(),
613 indices: all_indices,
614 })
615 }
616
617 fn supported_types(&self) -> Vec<IfcType> {
618 vec![IfcType::IfcFaceBasedSurfaceModel]
619 }
620}
621
622impl Default for FaceBasedSurfaceModelProcessor {
623 fn default() -> Self {
624 Self::new()
625 }
626}
627
628pub struct ShellBasedSurfaceModelProcessor;
634
635impl ShellBasedSurfaceModelProcessor {
636 pub fn new() -> Self {
637 Self
638 }
639}
640
641impl GeometryProcessor for ShellBasedSurfaceModelProcessor {
642 fn process(
643 &self,
644 entity: &DecodedEntity,
645 decoder: &mut EntityDecoder,
646 _schema: &IfcSchema,
647 ) -> Result<Mesh> {
648 let shells_attr = entity
652 .get(0)
653 .ok_or_else(|| Error::geometry("ShellBasedSurfaceModel missing SbsmBoundary".to_string()))?;
654
655 let shell_refs = shells_attr
656 .as_list()
657 .ok_or_else(|| Error::geometry("Expected shell list".to_string()))?;
658
659 let mut all_positions = Vec::new();
660 let mut all_indices = Vec::new();
661
662 for shell_ref in shell_refs {
664 let shell_id = shell_ref.as_entity_ref().ok_or_else(|| {
665 Error::geometry("Expected entity reference for shell".to_string())
666 })?;
667
668 let face_ids = match decoder.get_entity_ref_list_fast(shell_id) {
671 Some(ids) => ids,
672 None => continue,
673 };
674
675 for face_id in face_ids {
677 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
679 Some(ids) => ids,
680 None => continue,
681 };
682
683 let mut outer_points: Option<Vec<Point3<f64>>> = None;
684 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
685
686 for bound_id in bound_ids {
687 let (loop_id, orientation, is_outer) =
689 match decoder.get_face_bound_fast(bound_id) {
690 Some(data) => data,
691 None => continue,
692 };
693
694 let mut points = match extract_loop_points_by_id(loop_id, decoder) {
696 Some(p) => p,
697 None => continue,
698 };
699
700 if !orientation {
701 points.reverse();
702 }
703
704 if is_outer || outer_points.is_none() {
705 outer_points = Some(points);
706 } else {
707 hole_points.push(points);
708 }
709 }
710
711 if let Some(outer) = outer_points {
713 if outer.len() >= 3 {
714 let base_idx = (all_positions.len() / 3) as u32;
715
716 for p in &outer {
718 all_positions.push(p.x as f32);
719 all_positions.push(p.y as f32);
720 all_positions.push(p.z as f32);
721 }
722
723 for i in 1..outer.len() - 1 {
725 all_indices.push(base_idx);
726 all_indices.push(base_idx + i as u32);
727 all_indices.push(base_idx + i as u32 + 1);
728 }
729 }
730 }
731 }
732 }
733
734 Ok(Mesh {
735 positions: all_positions,
736 normals: Vec::new(),
737 indices: all_indices,
738 })
739 }
740
741 fn supported_types(&self) -> Vec<IfcType> {
742 vec![IfcType::IfcShellBasedSurfaceModel]
743 }
744}
745
746impl Default for ShellBasedSurfaceModelProcessor {
747 fn default() -> Self {
748 Self::new()
749 }
750}