ifc_lite_geometry/processors/
advanced.rs1use crate::{Error, Mesh, Result, TessellationQuality};
11use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcSchema, IfcType};
12
13use crate::router::GeometryProcessor;
14use super::advanced_face::{
15 parse_rational_weights, process_advanced_face, process_bspline_face,
16};
17
18#[allow(unused_variables)]
28fn trace_capped_advanced_brep_face(face_id: u32, error: &Error) {
29 crate::diag::diag_debug!(
30 { face_id, error = %error, "skipping unsupported advanced face in advanced_brep" }
31 else {}
32 );
33}
34
35pub struct AdvancedBrepProcessor;
39
40impl AdvancedBrepProcessor {
41 pub fn new() -> Self {
42 Self
43 }
44}
45
46impl AdvancedBrepProcessor {
47 fn process_rebased(
50 entity: &DecodedEntity,
51 decoder: &mut EntityDecoder,
52 quality: TessellationQuality,
53 rtc_file_units: Option<(f64, f64, f64)>,
54 ) -> Result<Mesh> {
55 let shell_attr = entity
60 .get(0)
61 .ok_or_else(|| Error::geometry("AdvancedBrep missing Outer shell".to_string()))?;
62
63 let shell = decoder
64 .resolve_ref(shell_attr)?
65 .ok_or_else(|| Error::geometry("Failed to resolve Outer shell".to_string()))?;
66
67 let faces_attr = shell
69 .get(0)
70 .ok_or_else(|| Error::geometry("ClosedShell missing CfsFaces".to_string()))?;
71
72 let faces = faces_attr
73 .as_list()
74 .ok_or_else(|| Error::geometry("Expected face list".to_string()))?;
75
76 let mut all_positions = Vec::new();
77 let mut all_indices = Vec::new();
78
79 #[cfg(any(feature = "debug_geometry", feature = "observability"))]
80 let mut empty_faces: Vec<(u32, String)> = Vec::new();
81
82 for face_ref in faces {
83 if let Some(face_id) = face_ref.as_entity_ref() {
84 let face = decoder.decode_by_id(face_id)?;
85
86 let face_mesh = process_advanced_face(&face, decoder, quality, rtc_file_units);
93 let (positions, indices) = match face_mesh {
94 Ok(result) => result,
95 Err(ref e) => {
96 trace_capped_advanced_brep_face(face_id, e);
97 #[cfg(any(feature = "debug_geometry", feature = "observability"))]
98 {
99 let surface_kind = face
100 .get(1)
101 .and_then(|a| decoder.resolve_ref(a).ok().flatten())
102 .map(|s| s.ifc_type.as_str().to_string())
103 .unwrap_or_else(|| "<unknown>".to_string());
104 empty_faces.push((face_id, surface_kind));
105 }
106 continue;
107 }
108 };
109
110 if !positions.is_empty() {
111 let base_idx = (all_positions.len() / 3) as u32;
113 all_positions.extend(positions);
114 for idx in indices {
115 all_indices.push(base_idx + idx);
116 }
117 } else {
118 #[cfg(any(feature = "debug_geometry", feature = "observability"))]
119 {
120 let surface_kind = face
121 .get(1)
122 .and_then(|a| decoder.resolve_ref(a).ok().flatten())
123 .map(|s| s.ifc_type.as_str().to_string())
124 .unwrap_or_else(|| "<unknown>".to_string());
125 empty_faces.push((face_id, surface_kind));
126 }
127 }
128 }
129 }
130
131 #[cfg(any(feature = "debug_geometry", feature = "observability"))]
135 if !empty_faces.is_empty() {
136 crate::diag::diag_warn!(
137 { entity_id = entity.id, empty_faces = empty_faces.len(),
138 faces = ?empty_faces, "advanced_brep: entity produced empty faces" }
139 else {
140 #[cfg(feature = "debug_geometry")]
141 eprintln!(
142 "[ifc-lite][advanced_brep] entity #{} produced {} empty face(s): {:?}",
143 entity.id,
144 empty_faces.len(),
145 empty_faces
146 );
147 }
148 );
149 }
150
151 Ok(Mesh {
152 positions: all_positions,
153 normals: Vec::new(),
154 indices: all_indices,
155 rtc_applied: rtc_file_units.is_some(),
156 welded_in_object_frame: false,
157 plane_tags: None,
158 origin: [0.0; 3], instance_meta: None, local_bounds: None, local_to_world: None })
159 }
160}
161
162impl GeometryProcessor for AdvancedBrepProcessor {
163 fn process(
164 &self,
165 entity: &DecodedEntity,
166 decoder: &mut EntityDecoder,
167 _schema: &IfcSchema,
168 quality: TessellationQuality,
169 ) -> Result<Mesh> {
170 Self::process_rebased(entity, decoder, quality, None)
171 }
172
173 fn process_in_rtc_frame(
174 &self,
175 entity: &DecodedEntity,
176 decoder: &mut EntityDecoder,
177 _schema: &IfcSchema,
178 quality: TessellationQuality,
179 rtc_file_units: (f64, f64, f64),
180 ) -> Option<Result<Mesh>> {
181 Some(Self::process_rebased(entity, decoder, quality, Some(rtc_file_units)))
182 }
183
184 fn supported_types(&self) -> Vec<IfcType> {
185 vec![IfcType::IfcAdvancedBrep, IfcType::IfcAdvancedBrepWithVoids]
186 }
187}
188
189impl Default for AdvancedBrepProcessor {
190 fn default() -> Self {
191 Self::new()
192 }
193}
194
195pub struct BSplineSurfaceProcessor;
201
202impl BSplineSurfaceProcessor {
203 pub fn new() -> Self {
204 Self
205 }
206
207 fn process_rebased(
210 entity: &DecodedEntity,
211 decoder: &mut EntityDecoder,
212 quality: TessellationQuality,
213 rtc_file_units: Option<(f64, f64, f64)>,
214 ) -> Result<Mesh> {
215 let weights = if entity.ifc_type == IfcType::IfcRationalBSplineSurfaceWithKnots {
216 parse_rational_weights(entity)
217 } else {
218 None
219 };
220 let (positions, indices) = process_bspline_face(
221 entity,
222 decoder,
223 weights.as_deref(),
224 quality,
225 rtc_file_units.unwrap_or((0.0, 0.0, 0.0)),
226 )?;
227 Ok(Mesh {
228 positions,
229 normals: Vec::new(),
230 indices,
231 rtc_applied: rtc_file_units.is_some(),
232 welded_in_object_frame: false,
233 plane_tags: None,
234 origin: [0.0; 3],
235 instance_meta: None,
236 local_bounds: None,
237 local_to_world: None,
238 })
239 }
240}
241
242impl Default for BSplineSurfaceProcessor {
243 fn default() -> Self {
244 Self::new()
245 }
246}
247
248impl GeometryProcessor for BSplineSurfaceProcessor {
249 fn process(
250 &self,
251 entity: &DecodedEntity,
252 decoder: &mut EntityDecoder,
253 _schema: &IfcSchema,
254 quality: TessellationQuality,
255 ) -> Result<Mesh> {
256 Self::process_rebased(entity, decoder, quality, None)
257 }
258
259 fn process_in_rtc_frame(
260 &self,
261 entity: &DecodedEntity,
262 decoder: &mut EntityDecoder,
263 _schema: &IfcSchema,
264 quality: TessellationQuality,
265 rtc_file_units: (f64, f64, f64),
266 ) -> Option<Result<Mesh>> {
267 Some(Self::process_rebased(entity, decoder, quality, Some(rtc_file_units)))
268 }
269
270 fn supported_types(&self) -> Vec<IfcType> {
271 vec![
272 IfcType::IfcBSplineSurfaceWithKnots,
273 IfcType::IfcRationalBSplineSurfaceWithKnots,
274 ]
275 }
276}