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