ifc_lite_geometry/processors/
surface.rs1use crate::{Error, Mesh, Point2, Point3, Result, TessellationQuality, Vector3};
8use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcSchema, IfcType};
9use nalgebra::Matrix4;
10
11use super::helpers::{get_axis2_placement_transform_by_id, get_direction_by_id};
12use crate::router::GeometryProcessor;
13
14pub struct SurfaceOfLinearExtrusionProcessor;
17
18impl SurfaceOfLinearExtrusionProcessor {
19 pub fn new() -> Self {
20 Self
21 }
22}
23
24impl GeometryProcessor for SurfaceOfLinearExtrusionProcessor {
25 fn process(
26 &self,
27 entity: &DecodedEntity,
28 decoder: &mut EntityDecoder,
29 _schema: &IfcSchema,
30 _quality: TessellationQuality,
31 ) -> Result<Mesh> {
32 let curve_attr = entity.get(0).ok_or_else(|| {
40 Error::geometry("SurfaceOfLinearExtrusion missing SweptCurve".to_string())
41 })?;
42
43 let curve_id = curve_attr.as_entity_ref().ok_or_else(|| {
44 Error::geometry("Expected entity reference for SweptCurve".to_string())
45 })?;
46
47 let position_attr = entity.get(1);
49 let position_transform = if let Some(attr) = position_attr {
50 if let Some(pos_id) = attr.as_entity_ref() {
51 get_axis2_placement_transform_by_id(pos_id, decoder)?
52 } else {
53 Matrix4::identity()
54 }
55 } else {
56 Matrix4::identity()
57 };
58
59 let direction_attr = entity.get(2).ok_or_else(|| {
61 Error::geometry("SurfaceOfLinearExtrusion missing ExtrudedDirection".to_string())
62 })?;
63
64 let direction = if let Some(dir_id) = direction_attr.as_entity_ref() {
65 get_direction_by_id(dir_id, decoder)
66 .ok_or_else(|| Error::geometry("Failed to get direction".to_string()))?
67 } else {
68 Vector3::new(0.0, 0.0, 1.0) };
70
71 let depth = entity
73 .get(3)
74 .and_then(|v| v.as_float())
75 .ok_or_else(|| Error::geometry("SurfaceOfLinearExtrusion missing Depth".to_string()))?;
76
77 let curve_points = Self::get_profile_curve_points(curve_id, decoder)?;
79
80 if curve_points.len() < 2 {
81 return Ok(Mesh::new());
82 }
83
84 let extrusion = direction.normalize() * depth;
86
87 let mut positions = Vec::with_capacity(curve_points.len() * 2 * 3);
88 let mut indices = Vec::with_capacity((curve_points.len() - 1) * 6);
89
90 for point in &curve_points {
92 let p3d = position_transform.transform_point(&Point3::new(point.x, point.y, 0.0));
94 positions.push(p3d.x as f32);
95 positions.push(p3d.y as f32);
96 positions.push(p3d.z as f32);
97 }
98
99 for point in &curve_points {
100 let p3d = position_transform.transform_point(&Point3::new(point.x, point.y, 0.0));
102 let p_extruded = p3d + extrusion;
103 positions.push(p_extruded.x as f32);
104 positions.push(p_extruded.y as f32);
105 positions.push(p_extruded.z as f32);
106 }
107
108 let n = curve_points.len() as u32;
110 for i in 0..n - 1 {
111 indices.push(i);
114 indices.push(i + 1);
115 indices.push(i + n);
116
117 indices.push(i + 1);
119 indices.push(i + n + 1);
120 indices.push(i + n);
121 }
122
123 Ok(Mesh {
124 positions,
125 normals: Vec::new(),
126 indices,
127 rtc_applied: false,
128 origin: [0.0; 3], instance_meta: None, local_bounds: None, local_to_world: None })
129 }
130
131 fn supported_types(&self) -> Vec<IfcType> {
132 vec![IfcType::IfcSurfaceOfLinearExtrusion]
133 }
134}
135
136impl SurfaceOfLinearExtrusionProcessor {
137 fn get_profile_curve_points(
139 profile_id: u32,
140 decoder: &mut EntityDecoder,
141 ) -> Result<Vec<Point2<f64>>> {
142 let profile = decoder.decode_by_id(profile_id)?;
143
144 let curve_attr = profile
147 .get(2)
148 .ok_or_else(|| Error::geometry("Profile missing curve".to_string()))?;
149
150 let curve_id = curve_attr
151 .as_entity_ref()
152 .ok_or_else(|| Error::geometry("Expected entity reference for curve".to_string()))?;
153
154 let curve = decoder.decode_by_id(curve_id)?;
156
157 match curve.ifc_type {
158 IfcType::IfcPolyline => {
159 let point_ids = decoder
161 .get_polyloop_point_ids_fast(curve_id)
162 .ok_or_else(|| Error::geometry("Failed to get polyline points".to_string()))?;
163
164 let mut points = Vec::with_capacity(point_ids.len());
165 for point_id in point_ids {
166 if let Some((x, y, _z)) = decoder.get_cartesian_point_fast(point_id) {
167 points.push(Point2::new(x, y));
168 }
169 }
170 Ok(points)
171 }
172 IfcType::IfcCompositeCurve => {
173 Self::extract_composite_curve_points(curve_id, decoder)
175 }
176 _ => {
177 if let Some(point_ids) = decoder.get_polyloop_point_ids_fast(curve_id) {
179 let mut points = Vec::with_capacity(point_ids.len());
180 for point_id in point_ids {
181 if let Some((x, y, _z)) = decoder.get_cartesian_point_fast(point_id) {
182 points.push(Point2::new(x, y));
183 }
184 }
185 Ok(points)
186 } else {
187 Ok(Vec::new())
188 }
189 }
190 }
191 }
192
193 fn extract_composite_curve_points(
195 curve_id: u32,
196 decoder: &mut EntityDecoder,
197 ) -> Result<Vec<Point2<f64>>> {
198 let curve = decoder.decode_by_id(curve_id)?;
199
200 let segments_attr = curve
202 .get(0)
203 .ok_or_else(|| Error::geometry("CompositeCurve missing Segments".to_string()))?;
204
205 let segment_refs = segments_attr
206 .as_list()
207 .ok_or_else(|| Error::geometry("Expected segment list".to_string()))?;
208
209 let mut all_points = Vec::new();
210
211 for seg_ref in segment_refs {
212 let seg_id = seg_ref.as_entity_ref().ok_or_else(|| {
213 Error::geometry("Expected entity reference for segment".to_string())
214 })?;
215
216 let segment = decoder.decode_by_id(seg_id)?;
217
218 let parent_curve_attr = segment
220 .get(2)
221 .ok_or_else(|| Error::geometry("Segment missing ParentCurve".to_string()))?;
222
223 let parent_curve_id = parent_curve_attr.as_entity_ref().ok_or_else(|| {
224 Error::geometry("Expected entity reference for parent curve".to_string())
225 })?;
226
227 if let Ok(segment_points) = Self::get_profile_curve_points(parent_curve_id, decoder) {
229 let start_idx = if all_points.is_empty() { 0 } else { 1 };
231 all_points.extend(segment_points.into_iter().skip(start_idx));
232 }
233 }
234
235 Ok(all_points)
236 }
237}
238
239impl Default for SurfaceOfLinearExtrusionProcessor {
240 fn default() -> Self {
241 Self::new()
242 }
243}