ifc_lite_geometry/processors/
advanced.rs1use crate::{Error, Mesh, Point3, Result};
11use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcSchema, IfcType};
12use nalgebra::Matrix4;
13
14use crate::router::GeometryProcessor;
15use super::helpers::get_axis2_placement_transform_by_id;
16
17pub struct AdvancedBrepProcessor;
21
22impl AdvancedBrepProcessor {
23 pub fn new() -> Self {
24 Self
25 }
26
27 #[inline]
29 fn bspline_basis(i: usize, p: usize, u: f64, knots: &[f64]) -> f64 {
30 if p == 0 {
31 if knots[i] <= u && u < knots[i + 1] {
32 1.0
33 } else {
34 0.0
35 }
36 } else {
37 let left = {
38 let denom = knots[i + p] - knots[i];
39 if denom.abs() < 1e-10 {
40 0.0
41 } else {
42 (u - knots[i]) / denom * Self::bspline_basis(i, p - 1, u, knots)
43 }
44 };
45 let right = {
46 let denom = knots[i + p + 1] - knots[i + 1];
47 if denom.abs() < 1e-10 {
48 0.0
49 } else {
50 (knots[i + p + 1] - u) / denom * Self::bspline_basis(i + 1, p - 1, u, knots)
51 }
52 };
53 left + right
54 }
55 }
56
57 fn evaluate_bspline_surface(
59 u: f64,
60 v: f64,
61 u_degree: usize,
62 v_degree: usize,
63 control_points: &[Vec<Point3<f64>>],
64 u_knots: &[f64],
65 v_knots: &[f64],
66 ) -> Point3<f64> {
67 let _n_u = control_points.len();
68
69 let mut result = Point3::new(0.0, 0.0, 0.0);
70
71 for (i, row) in control_points.iter().enumerate() {
72 let n_i = Self::bspline_basis(i, u_degree, u, u_knots);
73 for (j, cp) in row.iter().enumerate() {
74 let n_j = Self::bspline_basis(j, v_degree, v, v_knots);
75 let weight = n_i * n_j;
76 if weight.abs() > 1e-10 {
77 result.x += weight * cp.x;
78 result.y += weight * cp.y;
79 result.z += weight * cp.z;
80 }
81 }
82 }
83
84 result
85 }
86
87 fn tessellate_bspline_surface(
89 u_degree: usize,
90 v_degree: usize,
91 control_points: &[Vec<Point3<f64>>],
92 u_knots: &[f64],
93 v_knots: &[f64],
94 u_segments: usize,
95 v_segments: usize,
96 ) -> (Vec<f32>, Vec<u32>) {
97 let mut positions = Vec::new();
98 let mut indices = Vec::new();
99
100 let u_min = u_knots[u_degree];
102 let u_max = u_knots[u_knots.len() - u_degree - 1];
103 let v_min = v_knots[v_degree];
104 let v_max = v_knots[v_knots.len() - v_degree - 1];
105
106 for i in 0..=u_segments {
108 let u = u_min + (u_max - u_min) * (i as f64 / u_segments as f64);
109 let u = u.min(u_max - 1e-6).max(u_min);
111
112 for j in 0..=v_segments {
113 let v = v_min + (v_max - v_min) * (j as f64 / v_segments as f64);
114 let v = v.min(v_max - 1e-6).max(v_min);
115
116 let point = Self::evaluate_bspline_surface(
117 u,
118 v,
119 u_degree,
120 v_degree,
121 control_points,
122 u_knots,
123 v_knots,
124 );
125
126 positions.push(point.x as f32);
127 positions.push(point.y as f32);
128 positions.push(point.z as f32);
129
130 if i < u_segments && j < v_segments {
132 let base = (i * (v_segments + 1) + j) as u32;
133 let next_u = base + (v_segments + 1) as u32;
134
135 indices.push(base);
137 indices.push(base + 1);
138 indices.push(next_u + 1);
139
140 indices.push(base);
141 indices.push(next_u + 1);
142 indices.push(next_u);
143 }
144 }
145 }
146
147 (positions, indices)
148 }
149
150 fn parse_control_points(
152 &self,
153 bspline: &DecodedEntity,
154 decoder: &mut EntityDecoder,
155 ) -> Result<Vec<Vec<Point3<f64>>>> {
156 let cp_list_attr = bspline.get(2).ok_or_else(|| {
158 Error::geometry("BSplineSurface missing ControlPointsList".to_string())
159 })?;
160
161 let rows = cp_list_attr
162 .as_list()
163 .ok_or_else(|| Error::geometry("Expected control point list".to_string()))?;
164
165 let mut result = Vec::with_capacity(rows.len());
166
167 for row in rows {
168 let cols = row
169 .as_list()
170 .ok_or_else(|| Error::geometry("Expected control point row".to_string()))?;
171
172 let mut row_points = Vec::with_capacity(cols.len());
173 for col in cols {
174 if let Some(point_id) = col.as_entity_ref() {
175 let point = decoder.decode_by_id(point_id)?;
176 let coords = point.get(0).and_then(|v| v.as_list()).ok_or_else(|| {
177 Error::geometry("CartesianPoint missing coordinates".to_string())
178 })?;
179
180 let x = coords.first().and_then(|v| v.as_float()).unwrap_or(0.0);
181 let y = coords.get(1).and_then(|v| v.as_float()).unwrap_or(0.0);
182 let z = coords.get(2).and_then(|v| v.as_float()).unwrap_or(0.0);
183
184 row_points.push(Point3::new(x, y, z));
185 }
186 }
187 result.push(row_points);
188 }
189
190 Ok(result)
191 }
192
193 fn expand_knots(knot_values: &[f64], multiplicities: &[i64]) -> Vec<f64> {
195 let mut expanded = Vec::new();
196 for (knot, &mult) in knot_values.iter().zip(multiplicities.iter()) {
197 for _ in 0..mult {
198 expanded.push(*knot);
199 }
200 }
201 expanded
202 }
203
204 fn parse_knot_vectors(&self, bspline: &DecodedEntity) -> Result<(Vec<f64>, Vec<f64>)> {
206 let u_mult_attr = bspline
222 .get(7)
223 .ok_or_else(|| Error::geometry("BSplineSurface missing UMultiplicities".to_string()))?;
224 let u_mults: Vec<i64> = u_mult_attr
225 .as_list()
226 .ok_or_else(|| Error::geometry("Expected U multiplicities list".to_string()))?
227 .iter()
228 .filter_map(|v| v.as_int())
229 .collect();
230
231 let v_mult_attr = bspline
233 .get(8)
234 .ok_or_else(|| Error::geometry("BSplineSurface missing VMultiplicities".to_string()))?;
235 let v_mults: Vec<i64> = v_mult_attr
236 .as_list()
237 .ok_or_else(|| Error::geometry("Expected V multiplicities list".to_string()))?
238 .iter()
239 .filter_map(|v| v.as_int())
240 .collect();
241
242 let u_knots_attr = bspline
244 .get(9)
245 .ok_or_else(|| Error::geometry("BSplineSurface missing UKnots".to_string()))?;
246 let u_knot_values: Vec<f64> = u_knots_attr
247 .as_list()
248 .ok_or_else(|| Error::geometry("Expected U knots list".to_string()))?
249 .iter()
250 .filter_map(|v| v.as_float())
251 .collect();
252
253 let v_knots_attr = bspline
255 .get(10)
256 .ok_or_else(|| Error::geometry("BSplineSurface missing VKnots".to_string()))?;
257 let v_knot_values: Vec<f64> = v_knots_attr
258 .as_list()
259 .ok_or_else(|| Error::geometry("Expected V knots list".to_string()))?
260 .iter()
261 .filter_map(|v| v.as_float())
262 .collect();
263
264 let u_knots = Self::expand_knots(&u_knot_values, &u_mults);
266 let v_knots = Self::expand_knots(&v_knot_values, &v_mults);
267
268 Ok((u_knots, v_knots))
269 }
270
271 fn process_planar_face(
273 &self,
274 face: &DecodedEntity,
275 decoder: &mut EntityDecoder,
276 ) -> Result<(Vec<f32>, Vec<u32>)> {
277 let bounds_attr = face
279 .get(0)
280 .ok_or_else(|| Error::geometry("AdvancedFace missing Bounds".to_string()))?;
281
282 let bounds = bounds_attr
283 .as_list()
284 .ok_or_else(|| Error::geometry("Expected bounds list".to_string()))?;
285
286 let mut positions = Vec::new();
287 let mut indices = Vec::new();
288
289 for bound in bounds {
290 if let Some(bound_id) = bound.as_entity_ref() {
291 let bound_entity = decoder.decode_by_id(bound_id)?;
292
293 let loop_attr = bound_entity
295 .get(0)
296 .ok_or_else(|| Error::geometry("FaceBound missing Bound".to_string()))?;
297
298 let loop_entity = decoder
299 .resolve_ref(loop_attr)?
300 .ok_or_else(|| Error::geometry("Failed to resolve loop".to_string()))?;
301
302 if loop_entity
304 .ifc_type
305 .as_str()
306 .eq_ignore_ascii_case("IFCEDGELOOP")
307 {
308 let edges_attr = loop_entity
309 .get(0)
310 .ok_or_else(|| Error::geometry("EdgeLoop missing EdgeList".to_string()))?;
311
312 let edges = edges_attr
313 .as_list()
314 .ok_or_else(|| Error::geometry("Expected edge list".to_string()))?;
315
316 let mut polygon_points = Vec::new();
317
318 for edge_ref in edges {
319 if let Some(edge_id) = edge_ref.as_entity_ref() {
320 let oriented_edge = decoder.decode_by_id(edge_id)?;
321
322 let vertex = oriented_edge.get(0)
327 .and_then(|attr| decoder.resolve_ref(attr).ok().flatten())
328 .or_else(|| {
329 oriented_edge.get(2)
331 .and_then(|attr| decoder.resolve_ref(attr).ok().flatten())
332 .and_then(|edge_curve| {
333 edge_curve.get(0)
335 .and_then(|attr| decoder.resolve_ref(attr).ok().flatten())
336 })
337 });
338
339 if let Some(vertex) = vertex {
340 if let Some(point_attr) = vertex.get(0) {
342 if let Some(point) = decoder.resolve_ref(point_attr).ok().flatten() {
343 if let Some(coords) = point.get(0).and_then(|v| v.as_list()) {
344 let x = coords.first().and_then(|v| v.as_float()).unwrap_or(0.0);
345 let y = coords.get(1).and_then(|v| v.as_float()).unwrap_or(0.0);
346 let z = coords.get(2).and_then(|v| v.as_float()).unwrap_or(0.0);
347
348 polygon_points.push(Point3::new(x, y, z));
349 }
350 }
351 }
352 }
353 }
354 }
355
356 if polygon_points.len() >= 3 {
358 let base_idx = (positions.len() / 3) as u32;
359
360 for point in &polygon_points {
361 positions.push(point.x as f32);
362 positions.push(point.y as f32);
363 positions.push(point.z as f32);
364 }
365
366 for i in 1..polygon_points.len() - 1 {
370 indices.push(base_idx);
371 indices.push(base_idx + i as u32);
372 indices.push(base_idx + i as u32 + 1);
373 }
374 }
375 }
376 }
377 }
378
379 Ok((positions, indices))
380 }
381
382 fn process_bspline_face(
384 &self,
385 bspline: &DecodedEntity,
386 decoder: &mut EntityDecoder,
387 ) -> Result<(Vec<f32>, Vec<u32>)> {
388 let u_degree = bspline.get_float(0).unwrap_or(3.0) as usize;
390 let v_degree = bspline.get_float(1).unwrap_or(1.0) as usize;
391
392 let control_points = self.parse_control_points(bspline, decoder)?;
394
395 let (u_knots, v_knots) = self.parse_knot_vectors(bspline)?;
397
398 let u_segments = (control_points.len() * 3).clamp(8, 24);
400 let v_segments = if !control_points.is_empty() {
401 (control_points[0].len() * 3).clamp(4, 24)
402 } else {
403 4
404 };
405
406 let (positions, indices) = Self::tessellate_bspline_surface(
408 u_degree,
409 v_degree,
410 &control_points,
411 &u_knots,
412 &v_knots,
413 u_segments,
414 v_segments,
415 );
416
417 Ok((positions, indices))
418 }
419
420 fn process_cylindrical_face(
422 &self,
423 face: &DecodedEntity,
424 surface: &DecodedEntity,
425 decoder: &mut EntityDecoder,
426 ) -> Result<(Vec<f32>, Vec<u32>)> {
427 let radius = surface
429 .get(1)
430 .and_then(|v| v.as_float())
431 .ok_or_else(|| Error::geometry("CylindricalSurface missing Radius".to_string()))?;
432
433 let position_attr = surface.get(0);
435 let axis_transform = if let Some(attr) = position_attr {
436 if let Some(pos_id) = attr.as_entity_ref() {
437 get_axis2_placement_transform_by_id(pos_id, decoder)?
438 } else {
439 Matrix4::identity()
440 }
441 } else {
442 Matrix4::identity()
443 };
444
445 let bounds_attr = face
447 .get(0)
448 .ok_or_else(|| Error::geometry("AdvancedFace missing Bounds".to_string()))?;
449
450 let bounds = bounds_attr
451 .as_list()
452 .ok_or_else(|| Error::geometry("Expected bounds list".to_string()))?;
453
454 let mut boundary_points: Vec<Point3<f64>> = Vec::new();
456
457 for bound in bounds {
458 if let Some(bound_id) = bound.as_entity_ref() {
459 let bound_entity = decoder.decode_by_id(bound_id)?;
460 let loop_attr = bound_entity.get(0).ok_or_else(|| {
461 Error::geometry("FaceBound missing Bound".to_string())
462 })?;
463
464 if let Some(loop_entity) = decoder.resolve_ref(loop_attr)? {
465 if loop_entity.ifc_type.as_str().eq_ignore_ascii_case("IFCEDGELOOP") {
466 if let Some(edges_attr) = loop_entity.get(0) {
467 if let Some(edges) = edges_attr.as_list() {
468 for edge_ref in edges {
469 if let Some(edge_id) = edge_ref.as_entity_ref() {
470 if let Ok(oriented_edge) = decoder.decode_by_id(edge_id) {
471 let start_vertex = oriented_edge.get(0)
476 .and_then(|attr| decoder.resolve_ref(attr).ok().flatten());
477
478 let vertex = if start_vertex.is_some() {
480 start_vertex
481 } else if let Some(edge_elem_attr) = oriented_edge.get(2) {
482 if let Some(edge_curve) = decoder.resolve_ref(edge_elem_attr).ok().flatten() {
484 edge_curve.get(0)
486 .and_then(|attr| decoder.resolve_ref(attr).ok().flatten())
487 } else {
488 None
489 }
490 } else {
491 None
492 };
493
494 if let Some(vertex) = vertex {
495 if let Some(point_attr) = vertex.get(0) {
497 if let Some(point) = decoder.resolve_ref(point_attr).ok().flatten() {
498 if let Some(coords) = point.get(0).and_then(|v| v.as_list()) {
499 let x = coords.first().and_then(|v| v.as_float()).unwrap_or(0.0);
500 let y = coords.get(1).and_then(|v| v.as_float()).unwrap_or(0.0);
501 let z = coords.get(2).and_then(|v| v.as_float()).unwrap_or(0.0);
502 boundary_points.push(Point3::new(x, y, z));
503 }
504 }
505 }
506 }
507 }
508 }
509 }
510 }
511 }
512 }
513 }
514 }
515 }
516
517 if boundary_points.is_empty() {
518 return Ok((Vec::new(), Vec::new()));
519 }
520
521 let inv_transform = axis_transform.try_inverse().unwrap_or(Matrix4::identity());
523 let local_points: Vec<Point3<f64>> = boundary_points
524 .iter()
525 .map(|p| inv_transform.transform_point(p))
526 .collect();
527
528 let mut min_angle = f64::MAX;
530 let mut max_angle = f64::MIN;
531 let mut min_z = f64::MAX;
532 let mut max_z = f64::MIN;
533
534 for p in &local_points {
535 let angle = p.y.atan2(p.x);
536 min_angle = min_angle.min(angle);
537 max_angle = max_angle.max(angle);
538 min_z = min_z.min(p.z);
539 max_z = max_z.max(p.z);
540 }
541
542 if max_angle - min_angle > std::f64::consts::PI * 1.5 {
544 let positive_angles: Vec<f64> = local_points.iter()
546 .map(|p| {
547 let a = p.y.atan2(p.x);
548 if a < 0.0 { a + 2.0 * std::f64::consts::PI } else { a }
549 })
550 .collect();
551 min_angle = positive_angles.iter().cloned().fold(f64::MAX, f64::min);
552 max_angle = positive_angles.iter().cloned().fold(f64::MIN, f64::max);
553 }
554
555 let angle_span = max_angle - min_angle;
557 let height = max_z - min_z;
558
559 let angle_segments = ((angle_span / (std::f64::consts::PI / 12.0)).ceil() as usize).clamp(3, 16);
562 let height_segments = ((height / (radius * 2.0)).ceil() as usize).clamp(1, 4);
564
565 let mut positions = Vec::new();
566 let mut indices = Vec::new();
567
568 for h in 0..=height_segments {
570 let z = min_z + (height * h as f64 / height_segments as f64);
571 for a in 0..=angle_segments {
572 let angle = min_angle + (angle_span * a as f64 / angle_segments as f64);
573 let x = radius * angle.cos();
574 let y = radius * angle.sin();
575
576 let local_point = Point3::new(x, y, z);
578 let world_point = axis_transform.transform_point(&local_point);
579
580 positions.push(world_point.x as f32);
581 positions.push(world_point.y as f32);
582 positions.push(world_point.z as f32);
583 }
584 }
585
586 let cols = angle_segments + 1;
588 for h in 0..height_segments {
589 for a in 0..angle_segments {
590 let base = (h * cols + a) as u32;
591 let next_row = base + cols as u32;
592
593 indices.push(base);
595 indices.push(base + 1);
596 indices.push(next_row + 1);
597
598 indices.push(base);
599 indices.push(next_row + 1);
600 indices.push(next_row);
601 }
602 }
603
604 Ok((positions, indices))
605 }
606}
607
608impl GeometryProcessor for AdvancedBrepProcessor {
609 fn process(
610 &self,
611 entity: &DecodedEntity,
612 decoder: &mut EntityDecoder,
613 _schema: &IfcSchema,
614 ) -> Result<Mesh> {
615 let shell_attr = entity
620 .get(0)
621 .ok_or_else(|| Error::geometry("AdvancedBrep missing Outer shell".to_string()))?;
622
623 let shell = decoder
624 .resolve_ref(shell_attr)?
625 .ok_or_else(|| Error::geometry("Failed to resolve Outer shell".to_string()))?;
626
627 let faces_attr = shell
629 .get(0)
630 .ok_or_else(|| Error::geometry("ClosedShell missing CfsFaces".to_string()))?;
631
632 let faces = faces_attr
633 .as_list()
634 .ok_or_else(|| Error::geometry("Expected face list".to_string()))?;
635
636 let mut all_positions = Vec::new();
637 let mut all_indices = Vec::new();
638
639 for face_ref in faces {
640 if let Some(face_id) = face_ref.as_entity_ref() {
641 let face = decoder.decode_by_id(face_id)?;
642
643 let surface_attr = face.get(1).ok_or_else(|| {
649 Error::geometry("AdvancedFace missing FaceSurface".to_string())
650 })?;
651
652 let surface = decoder
653 .resolve_ref(surface_attr)?
654 .ok_or_else(|| Error::geometry("Failed to resolve FaceSurface".to_string()))?;
655
656 let surface_type = surface.ifc_type.as_str().to_uppercase();
657 let (positions, indices) = if surface_type == "IFCPLANE" {
658 self.process_planar_face(&face, decoder)?
660 } else if surface_type == "IFCBSPLINESURFACEWITHKNOTS"
661 || surface_type == "IFCRATIONALBSPLINESURFACEWITHKNOTS"
662 {
663 self.process_bspline_face(&surface, decoder)?
665 } else if surface_type == "IFCCYLINDRICALSURFACE" {
666 self.process_cylindrical_face(&face, &surface, decoder)?
668 } else {
669 continue;
671 };
672
673 let base_idx = (all_positions.len() / 3) as u32;
675 all_positions.extend(positions);
676 for idx in indices {
677 all_indices.push(base_idx + idx);
678 }
679 }
680 }
681
682 Ok(Mesh {
683 positions: all_positions,
684 normals: Vec::new(),
685 indices: all_indices,
686 })
687 }
688
689 fn supported_types(&self) -> Vec<IfcType> {
690 vec![IfcType::IfcAdvancedBrep, IfcType::IfcAdvancedBrepWithVoids]
691 }
692}
693
694impl Default for AdvancedBrepProcessor {
695 fn default() -> Self {
696 Self::new()
697 }
698}