ifc_lite_geometry/processors/
swept.rs1use crate::{profiles::ProfileProcessor, Error, Mesh, Point3, Result, Vector3};
8use ifc_lite_core::{DecodedEntity, EntityDecoder, IfcSchema, IfcType};
9
10use crate::router::GeometryProcessor;
11
12pub struct SweptDiskSolidProcessor {
15 profile_processor: ProfileProcessor,
16}
17
18impl SweptDiskSolidProcessor {
19 pub fn new(schema: IfcSchema) -> Self {
20 Self {
21 profile_processor: ProfileProcessor::new(schema),
22 }
23 }
24}
25
26impl GeometryProcessor for SweptDiskSolidProcessor {
27 fn process(
28 &self,
29 entity: &DecodedEntity,
30 decoder: &mut EntityDecoder,
31 _schema: &IfcSchema,
32 ) -> Result<Mesh> {
33 let directrix_attr = entity
41 .get(0)
42 .ok_or_else(|| Error::geometry("SweptDiskSolid missing Directrix".to_string()))?;
43
44 let radius = entity
45 .get_float(1)
46 .ok_or_else(|| Error::geometry("SweptDiskSolid missing Radius".to_string()))?;
47
48 let _inner_radius = entity.get_float(2);
50
51 let directrix = decoder
53 .resolve_ref(directrix_attr)?
54 .ok_or_else(|| Error::geometry("Failed to resolve Directrix".to_string()))?;
55
56 let curve_points = self
58 .profile_processor
59 .get_curve_points(&directrix, decoder)?;
60
61 if curve_points.len() < 2 {
62 return Ok(Mesh::new()); }
64
65 let segments = 24; let mut positions = Vec::new();
68 let mut indices = Vec::new();
69
70 for i in 0..curve_points.len() {
72 let p = curve_points[i];
73
74 let tangent = if i == 0 {
76 (curve_points[1] - curve_points[0]).normalize()
77 } else if i == curve_points.len() - 1 {
78 (curve_points[i] - curve_points[i - 1]).normalize()
79 } else {
80 ((curve_points[i + 1] - curve_points[i - 1]) / 2.0).normalize()
81 };
82
83 let up = if tangent.x.abs() < 0.9 {
86 Vector3::new(1.0, 0.0, 0.0)
87 } else {
88 Vector3::new(0.0, 1.0, 0.0)
89 };
90
91 let perp1 = tangent.cross(&up).normalize();
92 let perp2 = tangent.cross(&perp1).normalize();
93
94 for j in 0..segments {
96 let angle = 2.0 * std::f64::consts::PI * j as f64 / segments as f64;
97 let offset = perp1 * (radius * angle.cos()) + perp2 * (radius * angle.sin());
98 let vertex = p + offset;
99
100 positions.push(vertex.x as f32);
101 positions.push(vertex.y as f32);
102 positions.push(vertex.z as f32);
103 }
104
105 if i < curve_points.len() - 1 {
107 let base = (i * segments) as u32;
108 let next_base = ((i + 1) * segments) as u32;
109
110 for j in 0..segments {
111 let j_next = (j + 1) % segments;
112
113 indices.push(base + j as u32);
115 indices.push(next_base + j as u32);
116 indices.push(next_base + j_next as u32);
117
118 indices.push(base + j as u32);
119 indices.push(next_base + j_next as u32);
120 indices.push(base + j_next as u32);
121 }
122 }
123 }
124
125 let center_idx = (positions.len() / 3) as u32;
128 let start = curve_points[0];
129 positions.push(start.x as f32);
130 positions.push(start.y as f32);
131 positions.push(start.z as f32);
132
133 for j in 0..segments {
134 let j_next = (j + 1) % segments;
135 indices.push(center_idx);
136 indices.push(j_next as u32);
137 indices.push(j as u32);
138 }
139
140 let end_center_idx = (positions.len() / 3) as u32;
142 let end_base = ((curve_points.len() - 1) * segments) as u32;
143 let end = curve_points[curve_points.len() - 1];
144 positions.push(end.x as f32);
145 positions.push(end.y as f32);
146 positions.push(end.z as f32);
147
148 for j in 0..segments {
149 let j_next = (j + 1) % segments;
150 indices.push(end_center_idx);
151 indices.push(end_base + j as u32);
152 indices.push(end_base + j_next as u32);
153 }
154
155 Ok(Mesh {
156 positions,
157 normals: Vec::new(),
158 indices,
159 rtc_applied: false,
160 })
161 }
162
163 fn supported_types(&self) -> Vec<IfcType> {
164 vec![IfcType::IfcSweptDiskSolid]
165 }
166}
167
168impl Default for SweptDiskSolidProcessor {
169 fn default() -> Self {
170 Self::new(IfcSchema::new())
171 }
172}
173
174pub struct RevolvedAreaSolidProcessor {
177 profile_processor: ProfileProcessor,
178}
179
180impl RevolvedAreaSolidProcessor {
181 pub fn new(schema: IfcSchema) -> Self {
182 Self {
183 profile_processor: ProfileProcessor::new(schema),
184 }
185 }
186}
187
188impl GeometryProcessor for RevolvedAreaSolidProcessor {
189 fn process(
190 &self,
191 entity: &DecodedEntity,
192 decoder: &mut EntityDecoder,
193 _schema: &IfcSchema,
194 ) -> Result<Mesh> {
195 let profile_attr = entity
202 .get(0)
203 .ok_or_else(|| Error::geometry("RevolvedAreaSolid missing SweptArea".to_string()))?;
204
205 let profile = decoder
206 .resolve_ref(profile_attr)?
207 .ok_or_else(|| Error::geometry("Failed to resolve SweptArea".to_string()))?;
208
209 let axis_attr = entity
211 .get(2)
212 .ok_or_else(|| Error::geometry("RevolvedAreaSolid missing Axis".to_string()))?;
213
214 let axis_placement = decoder
215 .resolve_ref(axis_attr)?
216 .ok_or_else(|| Error::geometry("Failed to resolve Axis".to_string()))?;
217
218 let angle = entity
220 .get_float(3)
221 .ok_or_else(|| Error::geometry("RevolvedAreaSolid missing Angle".to_string()))?;
222
223 let profile_2d = self.profile_processor.process(&profile, decoder)?;
225 if profile_2d.outer.is_empty() {
226 return Ok(Mesh::new());
227 }
228
229 let axis_location = {
232 let loc_attr = axis_placement
233 .get(0)
234 .ok_or_else(|| Error::geometry("Axis1Placement missing Location".to_string()))?;
235 let loc = decoder
236 .resolve_ref(loc_attr)?
237 .ok_or_else(|| Error::geometry("Failed to resolve axis location".to_string()))?;
238 let coords = loc
239 .get(0)
240 .and_then(|v| v.as_list())
241 .ok_or_else(|| Error::geometry("Axis location missing coordinates".to_string()))?;
242 Point3::new(
243 coords.first().and_then(|v| v.as_float()).unwrap_or(0.0),
244 coords.get(1).and_then(|v| v.as_float()).unwrap_or(0.0),
245 coords.get(2).and_then(|v| v.as_float()).unwrap_or(0.0),
246 )
247 };
248
249 let axis_direction = {
250 if let Some(dir_attr) = axis_placement.get(1) {
251 if !dir_attr.is_null() {
252 let dir = decoder.resolve_ref(dir_attr)?.ok_or_else(|| {
253 Error::geometry("Failed to resolve axis direction".to_string())
254 })?;
255 let coords = dir.get(0).and_then(|v| v.as_list()).ok_or_else(|| {
256 Error::geometry("Axis direction missing coordinates".to_string())
257 })?;
258 Vector3::new(
259 coords.first().and_then(|v| v.as_float()).unwrap_or(0.0),
260 coords.get(1).and_then(|v| v.as_float()).unwrap_or(1.0),
261 coords.get(2).and_then(|v| v.as_float()).unwrap_or(0.0),
262 )
263 .normalize()
264 } else {
265 Vector3::new(0.0, 1.0, 0.0) }
267 } else {
268 Vector3::new(0.0, 1.0, 0.0) }
270 };
271
272 let full_circle = angle.abs() >= std::f64::consts::PI * 1.99;
275 let segments = if full_circle {
276 24 } else {
278 ((angle.abs() / std::f64::consts::PI * 12.0).ceil() as usize).max(4)
279 };
280
281 let profile_points = &profile_2d.outer;
282 let num_profile_points = profile_points.len();
283
284 let mut positions = Vec::new();
285 let mut indices = Vec::new();
286
287 for i in 0..=segments {
289 let t = if full_circle && i == segments {
290 0.0 } else {
292 angle * i as f64 / segments as f64
293 };
294
295 let cos_t = t.cos();
297 let sin_t = t.sin();
298 let (ax, ay, az) = (axis_direction.x, axis_direction.y, axis_direction.z);
299
300 let k_matrix = |v: Vector3<f64>| -> Vector3<f64> {
302 Vector3::new(
303 ay * v.z - az * v.y,
304 az * v.x - ax * v.z,
305 ax * v.y - ay * v.x,
306 )
307 };
308
309 for (j, p2d) in profile_points.iter().enumerate() {
311 let radius = p2d.x;
314 let height = p2d.y;
315
316 let v = Vector3::new(radius, 0.0, 0.0);
318
319 let k_cross_v = k_matrix(v);
321 let k_dot_v = ax * v.x + ay * v.y + az * v.z;
322
323 let v_rot =
324 v * cos_t + k_cross_v * sin_t + axis_direction * k_dot_v * (1.0 - cos_t);
325
326 let pos = axis_location + axis_direction * height + v_rot;
328
329 positions.push(pos.x as f32);
330 positions.push(pos.y as f32);
331 positions.push(pos.z as f32);
332
333 if i < segments && j < num_profile_points - 1 {
335 let current = (i * num_profile_points + j) as u32;
336 let next_seg = ((i + 1) * num_profile_points + j) as u32;
337 let current_next = current + 1;
338 let next_seg_next = next_seg + 1;
339
340 indices.push(current);
342 indices.push(next_seg);
343 indices.push(next_seg_next);
344
345 indices.push(current);
346 indices.push(next_seg_next);
347 indices.push(current_next);
348 }
349 }
350 }
351
352 if !full_circle {
354 let start_center_idx = (positions.len() / 3) as u32;
356 let start_center = axis_location
357 + axis_direction
358 * (profile_points.iter().map(|p| p.y).sum::<f64>()
359 / profile_points.len() as f64);
360 positions.push(start_center.x as f32);
361 positions.push(start_center.y as f32);
362 positions.push(start_center.z as f32);
363
364 for j in 0..num_profile_points - 1 {
365 indices.push(start_center_idx);
366 indices.push(j as u32 + 1);
367 indices.push(j as u32);
368 }
369
370 let end_center_idx = (positions.len() / 3) as u32;
372 let end_base = (segments * num_profile_points) as u32;
373 positions.push(start_center.x as f32);
374 positions.push(start_center.y as f32);
375 positions.push(start_center.z as f32);
376
377 for j in 0..num_profile_points - 1 {
378 indices.push(end_center_idx);
379 indices.push(end_base + j as u32);
380 indices.push(end_base + j as u32 + 1);
381 }
382 }
383
384 Ok(Mesh {
385 positions,
386 normals: Vec::new(),
387 indices,
388 rtc_applied: false,
389 })
390 }
391
392 fn supported_types(&self) -> Vec<IfcType> {
393 vec![IfcType::IfcRevolvedAreaSolid]
394 }
395}
396
397impl Default for RevolvedAreaSolidProcessor {
398 fn default() -> Self {
399 Self::new(IfcSchema::new())
400 }
401}