ifc_lite_geometry/processors/
surface.rs1use crate::{Error, Mesh, Point2, Point3, Result, Vector3};
8use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcSchema, IfcType};
9use nalgebra::Matrix4;
10
11use crate::router::GeometryProcessor;
12use super::helpers::{get_axis2_placement_transform_by_id, get_direction_by_id};
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 ) -> Result<Mesh> {
31 let curve_attr = entity
39 .get(0)
40 .ok_or_else(|| Error::geometry("SurfaceOfLinearExtrusion missing SweptCurve".to_string()))?;
41
42 let curve_id = curve_attr
43 .as_entity_ref()
44 .ok_or_else(|| Error::geometry("Expected entity reference for SweptCurve".to_string()))?;
45
46 let position_attr = entity.get(1);
48 let position_transform = if let Some(attr) = position_attr {
49 if let Some(pos_id) = attr.as_entity_ref() {
50 get_axis2_placement_transform_by_id(pos_id, decoder)?
51 } else {
52 Matrix4::identity()
53 }
54 } else {
55 Matrix4::identity()
56 };
57
58 let direction_attr = entity
60 .get(2)
61 .ok_or_else(|| Error::geometry("SurfaceOfLinearExtrusion missing ExtrudedDirection".to_string()))?;
62
63 let direction = if let Some(dir_id) = direction_attr.as_entity_ref() {
64 get_direction_by_id(dir_id, decoder)
65 .ok_or_else(|| Error::geometry("Failed to get direction".to_string()))?
66 } else {
67 Vector3::new(0.0, 0.0, 1.0) };
69
70 let depth = entity
72 .get(3)
73 .and_then(|v| v.as_float())
74 .ok_or_else(|| Error::geometry("SurfaceOfLinearExtrusion missing Depth".to_string()))?;
75
76 let curve_points = Self::get_profile_curve_points(curve_id, decoder)?;
78
79 if curve_points.len() < 2 {
80 return Ok(Mesh::new());
81 }
82
83 let extrusion = direction.normalize() * depth;
85
86 let mut positions = Vec::with_capacity(curve_points.len() * 2 * 3);
87 let mut indices = Vec::with_capacity((curve_points.len() - 1) * 6);
88
89 for point in &curve_points {
91 let p3d = position_transform.transform_point(&Point3::new(point.x, point.y, 0.0));
93 positions.push(p3d.x as f32);
94 positions.push(p3d.y as f32);
95 positions.push(p3d.z as f32);
96 }
97
98 for point in &curve_points {
99 let p3d = position_transform.transform_point(&Point3::new(point.x, point.y, 0.0));
101 let p_extruded = p3d + extrusion;
102 positions.push(p_extruded.x as f32);
103 positions.push(p_extruded.y as f32);
104 positions.push(p_extruded.z as f32);
105 }
106
107 let n = curve_points.len() as u32;
109 for i in 0..n - 1 {
110 indices.push(i);
113 indices.push(i + 1);
114 indices.push(i + n);
115
116 indices.push(i + 1);
118 indices.push(i + n + 1);
119 indices.push(i + n);
120 }
121
122 Ok(Mesh {
123 positions,
124 normals: Vec::new(),
125 indices,
126 })
127 }
128
129 fn supported_types(&self) -> Vec<IfcType> {
130 vec![IfcType::IfcSurfaceOfLinearExtrusion]
131 }
132}
133
134impl SurfaceOfLinearExtrusionProcessor {
135 fn get_profile_curve_points(
137 profile_id: u32,
138 decoder: &mut EntityDecoder,
139 ) -> Result<Vec<Point2<f64>>> {
140 let profile = decoder.decode_by_id(profile_id)?;
141
142 let curve_attr = profile
145 .get(2)
146 .ok_or_else(|| Error::geometry("Profile missing curve".to_string()))?;
147
148 let curve_id = curve_attr
149 .as_entity_ref()
150 .ok_or_else(|| Error::geometry("Expected entity reference for curve".to_string()))?;
151
152 let curve = decoder.decode_by_id(curve_id)?;
154
155 match curve.ifc_type {
156 IfcType::IfcPolyline => {
157 let point_ids = decoder
159 .get_polyloop_point_ids_fast(curve_id)
160 .ok_or_else(|| Error::geometry("Failed to get polyline points".to_string()))?;
161
162 let mut points = Vec::with_capacity(point_ids.len());
163 for point_id in point_ids {
164 if let Some((x, y, _z)) = decoder.get_cartesian_point_fast(point_id) {
165 points.push(Point2::new(x, y));
166 }
167 }
168 Ok(points)
169 }
170 IfcType::IfcCompositeCurve => {
171 Self::extract_composite_curve_points(curve_id, decoder)
173 }
174 _ => {
175 if let Some(point_ids) = decoder.get_polyloop_point_ids_fast(curve_id) {
177 let mut points = Vec::with_capacity(point_ids.len());
178 for point_id in point_ids {
179 if let Some((x, y, _z)) = decoder.get_cartesian_point_fast(point_id) {
180 points.push(Point2::new(x, y));
181 }
182 }
183 Ok(points)
184 } else {
185 Ok(Vec::new())
186 }
187 }
188 }
189 }
190
191 fn extract_composite_curve_points(
193 curve_id: u32,
194 decoder: &mut EntityDecoder,
195 ) -> Result<Vec<Point2<f64>>> {
196 let curve = decoder.decode_by_id(curve_id)?;
197
198 let segments_attr = curve
200 .get(0)
201 .ok_or_else(|| Error::geometry("CompositeCurve missing Segments".to_string()))?;
202
203 let segment_refs = segments_attr
204 .as_list()
205 .ok_or_else(|| Error::geometry("Expected segment list".to_string()))?;
206
207 let mut all_points = Vec::new();
208
209 for seg_ref in segment_refs {
210 let seg_id = seg_ref.as_entity_ref().ok_or_else(|| {
211 Error::geometry("Expected entity reference for segment".to_string())
212 })?;
213
214 let segment = decoder.decode_by_id(seg_id)?;
215
216 let parent_curve_attr = segment
218 .get(2)
219 .ok_or_else(|| Error::geometry("Segment missing ParentCurve".to_string()))?;
220
221 let parent_curve_id = parent_curve_attr
222 .as_entity_ref()
223 .ok_or_else(|| Error::geometry("Expected entity reference for parent curve".to_string()))?;
224
225 if let Ok(segment_points) = Self::get_profile_curve_points(parent_curve_id, decoder) {
227 let start_idx = if all_points.is_empty() { 0 } else { 1 };
229 all_points.extend(segment_points.into_iter().skip(start_idx));
230 }
231 }
232
233 Ok(all_points)
234 }
235}
236
237impl Default for SurfaceOfLinearExtrusionProcessor {
238 fn default() -> Self {
239 Self::new()
240 }
241}