ifc_lite_geometry/processors/brep/
surface_model.rs1use crate::{Error, Mesh, Point3, Result, TessellationQuality};
6use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcSchema, IfcType};
7
8use super::super::advanced_face::process_advanced_face;
9use super::super::helpers::{extract_loop_points_by_id, FaceData};
10use super::faceted::FacetedBrepProcessor;
11use crate::router::GeometryProcessor;
12
13#[allow(unused_variables)]
19fn trace_capped_face(face_id: u32, error: &Error) {
20 crate::diag::diag_debug!(
21 { face_id, error = %error, "skipping unsupported advanced face in surface model" }
22 else {
23 #[cfg(debug_assertions)]
24 eprintln!("[ifc-lite] Skipping unsupported advanced face #{face_id} in surface model: {error}");
25 }
26 );
27}
28
29fn mesh_face_collections(
42 collections: &[ifc_lite_core::AttributeValue],
43 decoder: &mut EntityDecoder,
44 quality: TessellationQuality,
45 rtc_file_units: Option<(f64, f64, f64)>,
46 collection_noun: &str,
47) -> Result<Mesh> {
48 let mut all_positions = Vec::new();
49 let mut all_indices = Vec::new();
50
51 for collection_ref in collections {
52 let collection_id = collection_ref.as_entity_ref().ok_or_else(|| {
53 Error::geometry(format!("Expected entity reference for {collection_noun}"))
54 })?;
55
56 let face_ids = match decoder.get_entity_ref_list_fast(collection_id) {
58 Some(ids) => ids,
59 None => continue,
60 };
61
62 for face_id in face_ids {
63 let face = match decoder.decode_by_id(face_id) {
66 Ok(f) => f,
67 Err(_) => continue,
68 };
69
70 if face.ifc_type == IfcType::IfcAdvancedFace {
71 let (positions, indices) =
81 match process_advanced_face(&face, decoder, quality, rtc_file_units) {
82 Ok(result) => result,
83 Err(ref e) => {
84 trace_capped_face(face.id, e);
85 continue;
86 }
87 };
88
89 if !positions.is_empty() {
90 let base_idx = (all_positions.len() / 3) as u32;
91 all_positions.extend(positions);
92 for idx in indices {
93 all_indices.push(base_idx + idx);
94 }
95 }
96 } else {
97 let bound_ids = match decoder.get_entity_ref_list_fast(face_id) {
99 Some(ids) => ids,
100 None => continue,
101 };
102
103 let mut outer_points: Option<Vec<Point3<f64>>> = None;
104 let mut hole_points: Vec<Vec<Point3<f64>>> = Vec::new();
105
106 for bound_id in bound_ids {
107 let (loop_id, orientation, is_outer) =
109 match decoder.get_face_bound_fast(bound_id) {
110 Some(data) => data,
111 None => continue,
112 };
113
114 let mut points = match extract_loop_points_by_id(loop_id, decoder) {
115 Some(p) => p,
116 None => continue,
117 };
118
119 if !orientation {
120 points.reverse();
121 }
122
123 if is_outer || outer_points.is_none() {
124 if outer_points.is_some() && is_outer {
128 if let Some(prev_outer) = outer_points.take() {
129 hole_points.push(prev_outer);
130 }
131 }
132 outer_points = Some(points);
133 } else {
134 hole_points.push(points);
135 }
136 }
137
138 if let Some(outer) = outer_points {
148 if outer.len() >= 3 {
149 let face_data = FaceData {
150 outer_points: outer,
151 hole_points,
152 };
153 let result = FacetedBrepProcessor::triangulate_face(
154 &face_data,
155 rtc_file_units.unwrap_or((0.0, 0.0, 0.0)),
156 );
157 let base_idx = (all_positions.len() / 3) as u32;
158 all_positions.extend(result.positions);
159 for idx in result.indices {
160 all_indices.push(base_idx + idx);
161 }
162 }
163 }
164 }
165 }
166 }
167
168 Ok(Mesh {
169 positions: all_positions,
170 normals: Vec::new(),
171 indices: all_indices,
172 rtc_applied: rtc_file_units.is_some(),
173 welded_in_object_frame: false,
174 plane_tags: None,
175 origin: [0.0; 3],
176 instance_meta: None,
177 local_bounds: None,
178 local_to_world: None,
179 })
180}
181
182fn mesh_surface_model(
185 entity: &DecodedEntity,
186 decoder: &mut EntityDecoder,
187 quality: TessellationQuality,
188 rtc_file_units: Option<(f64, f64, f64)>,
189 names: (&str, &str),
190) -> Result<Mesh> {
191 let (missing_attribute, collection) = names;
192 let collections = entity
193 .get(0)
194 .ok_or_else(|| Error::geometry(missing_attribute.to_string()))?
195 .as_list()
196 .ok_or_else(|| Error::geometry(format!("Expected {collection} list")))?;
197 mesh_face_collections(collections, decoder, quality, rtc_file_units, collection)
198}
199
200pub struct FaceBasedSurfaceModelProcessor;
205
206impl FaceBasedSurfaceModelProcessor {
207 const NAMES: (&'static str, &'static str) =
208 ("FaceBasedSurfaceModel missing FbsmFaces", "face set");
209
210 pub fn new() -> Self {
211 Self
212 }
213}
214
215impl GeometryProcessor for FaceBasedSurfaceModelProcessor {
216 fn process(
217 &self,
218 entity: &DecodedEntity,
219 decoder: &mut EntityDecoder,
220 _schema: &IfcSchema,
221 quality: TessellationQuality,
222 ) -> Result<Mesh> {
223 mesh_surface_model(entity, decoder, quality, None, Self::NAMES)
224 }
225
226 fn process_in_rtc_frame(
227 &self,
228 entity: &DecodedEntity,
229 decoder: &mut EntityDecoder,
230 _schema: &IfcSchema,
231 quality: TessellationQuality,
232 rtc_file_units: (f64, f64, f64),
233 ) -> Option<Result<Mesh>> {
234 Some(mesh_surface_model(
235 entity,
236 decoder,
237 quality,
238 Some(rtc_file_units),
239 Self::NAMES,
240 ))
241 }
242
243 fn supported_types(&self) -> Vec<IfcType> {
244 vec![IfcType::IfcFaceBasedSurfaceModel]
245 }
246}
247
248impl Default for FaceBasedSurfaceModelProcessor {
249 fn default() -> Self {
250 Self::new()
251 }
252}
253
254pub struct ShellBasedSurfaceModelProcessor;
260
261impl ShellBasedSurfaceModelProcessor {
262 const NAMES: (&'static str, &'static str) =
263 ("ShellBasedSurfaceModel missing SbsmBoundary", "shell");
264
265 pub fn new() -> Self {
266 Self
267 }
268}
269
270impl GeometryProcessor for ShellBasedSurfaceModelProcessor {
271 fn process(
272 &self,
273 entity: &DecodedEntity,
274 decoder: &mut EntityDecoder,
275 _schema: &IfcSchema,
276 quality: TessellationQuality,
277 ) -> Result<Mesh> {
278 mesh_surface_model(entity, decoder, quality, None, Self::NAMES)
279 }
280
281 fn process_in_rtc_frame(
282 &self,
283 entity: &DecodedEntity,
284 decoder: &mut EntityDecoder,
285 _schema: &IfcSchema,
286 quality: TessellationQuality,
287 rtc_file_units: (f64, f64, f64),
288 ) -> Option<Result<Mesh>> {
289 Some(mesh_surface_model(
290 entity,
291 decoder,
292 quality,
293 Some(rtc_file_units),
294 Self::NAMES,
295 ))
296 }
297
298 fn supported_types(&self) -> Vec<IfcType> {
299 vec![IfcType::IfcShellBasedSurfaceModel]
300 }
301}
302
303impl Default for ShellBasedSurfaceModelProcessor {
304 fn default() -> Self {
305 Self::new()
306 }
307}