1use crate::topology::{
2 BrepSolid, CoedgeRecord, EdgeRecord, FaceRecord, LoopRecord, ShellRecord, VertexRecord,
3};
4use crate::{
5 make_cone_surface, make_line, make_plane, make_sphere_surface, make_torus_surface, NurbsCurve,
6 Vec3, Vec4,
7};
8
9fn parameter_line(u0: f64, v0: f64, u1: f64, v1: f64) -> Result<NurbsCurve, String> {
10 make_line(Vec3::new(u0, v0, 0.0), Vec3::new(u1, v1, 0.0))
11}
12
13fn curve_to_plane_parameters(
14 curve: &NurbsCurve,
15 origin: Vec3,
16 x_axis: Vec3,
17 y_axis: Vec3,
18) -> Result<NurbsCurve, String> {
19 let mut points = Vec::with_capacity(curve.control_points.len());
20 for control_point in &curve.control_points {
21 let point = Vec3::new(
22 control_point.x / control_point.w,
23 control_point.y / control_point.w,
24 control_point.z / control_point.w,
25 );
26 let delta = point.sub(origin);
27 points.push(Vec4 {
28 x: delta.dot(x_axis) * control_point.w,
29 y: delta.dot(y_axis) * control_point.w,
30 z: 0.0,
31 w: control_point.w,
32 });
33 }
34 NurbsCurve::new(curve.degree, curve.knots.clone(), points)
35}
36
37fn validated(solid: BrepSolid, label: &str) -> Result<BrepSolid, String> {
38 let issues = solid.validate();
39 if issues.is_empty() {
40 Ok(solid)
41 } else {
42 Err(format!(
43 "Rust {label} builder produced invalid topology: {issues:?}"
44 ))
45 }
46}
47
48pub fn make_sphere_brep(center: Vec3, radius: f64, polar_axis: Vec3) -> Result<BrepSolid, String> {
49 let surface = make_sphere_surface(center, radius, polar_axis)?;
50 let south = surface.evaluate(0.0, 0.0)?;
51 let north = surface.evaluate(0.0, 1.0)?;
52 let seam = surface.iso_curve_u(0.0)?;
53 validated(
54 BrepSolid {
55 id: 120,
56 vertices: vec![
57 VertexRecord {
58 id: 1,
59 point: south,
60 },
61 VertexRecord {
62 id: 2,
63 point: north,
64 },
65 ],
66 edges: vec![
67 EdgeRecord {
68 id: 10,
69 curve: seam,
70 t0: 0.0,
71 t1: 1.0,
72 start_vertex_id: 1,
73 end_vertex_id: 2,
74 degenerate: false,
75 name: None,
76 },
77 EdgeRecord {
78 id: 11,
79 curve: make_line(south, south)?,
80 t0: 0.0,
81 t1: 1.0,
82 start_vertex_id: 1,
83 end_vertex_id: 1,
84 degenerate: true,
85 name: None,
86 },
87 EdgeRecord {
88 id: 12,
89 curve: make_line(north, north)?,
90 t0: 0.0,
91 t1: 1.0,
92 start_vertex_id: 2,
93 end_vertex_id: 2,
94 degenerate: true,
95 name: None,
96 },
97 ],
98 shells: vec![ShellRecord {
99 id: 119,
100 faces: vec![FaceRecord {
101 id: 118,
102 surface,
103 same_sense: true,
104 loops: vec![LoopRecord {
105 id: 117,
106 coedges: vec![
107 CoedgeRecord {
108 id: 101,
109 edge_id: 11,
110 forward: true,
111 pcurve: parameter_line(0.0, 0.0, 1.0, 0.0)?,
112 },
113 CoedgeRecord {
114 id: 102,
115 edge_id: 10,
116 forward: true,
117 pcurve: parameter_line(1.0, 0.0, 1.0, 1.0)?,
118 },
119 CoedgeRecord {
120 id: 103,
121 edge_id: 12,
122 forward: true,
123 pcurve: parameter_line(1.0, 1.0, 0.0, 1.0)?,
124 },
125 CoedgeRecord {
126 id: 104,
127 edge_id: 10,
128 forward: false,
129 pcurve: parameter_line(0.0, 1.0, 0.0, 0.0)?,
130 },
131 ],
132 }],
133 name: None,
134 }],
135 }],
136 genus: 0,
137 },
138 "sphere",
139 )
140}
141
142pub fn make_torus_brep(
143 center: Vec3,
144 axis_direction: Vec3,
145 major_radius: f64,
146 minor_radius: f64,
147) -> Result<BrepSolid, String> {
148 let surface = make_torus_surface(center, axis_direction, major_radius, minor_radius)?;
149 let corner = surface.evaluate(0.0, 0.0)?;
150 let major_curve = surface.iso_curve_v(0.0)?;
151 let tube_curve = surface.iso_curve_u(0.0)?;
152 validated(
153 BrepSolid {
154 id: 130,
155 vertices: vec![VertexRecord {
156 id: 1,
157 point: corner,
158 }],
159 edges: vec![
160 EdgeRecord {
161 id: 10,
162 curve: major_curve,
163 t0: 0.0,
164 t1: 1.0,
165 start_vertex_id: 1,
166 end_vertex_id: 1,
167 degenerate: false,
168 name: None,
169 },
170 EdgeRecord {
171 id: 11,
172 curve: tube_curve,
173 t0: 0.0,
174 t1: 1.0,
175 start_vertex_id: 1,
176 end_vertex_id: 1,
177 degenerate: false,
178 name: None,
179 },
180 ],
181 shells: vec![ShellRecord {
182 id: 129,
183 faces: vec![FaceRecord {
184 id: 128,
185 surface,
186 same_sense: true,
187 loops: vec![LoopRecord {
188 id: 127,
189 coedges: vec![
190 CoedgeRecord {
191 id: 101,
192 edge_id: 10,
193 forward: true,
194 pcurve: parameter_line(0.0, 0.0, 1.0, 0.0)?,
195 },
196 CoedgeRecord {
197 id: 102,
198 edge_id: 11,
199 forward: true,
200 pcurve: parameter_line(1.0, 0.0, 1.0, 1.0)?,
201 },
202 CoedgeRecord {
203 id: 103,
204 edge_id: 10,
205 forward: false,
206 pcurve: parameter_line(1.0, 1.0, 0.0, 1.0)?,
207 },
208 CoedgeRecord {
209 id: 104,
210 edge_id: 11,
211 forward: false,
212 pcurve: parameter_line(0.0, 1.0, 0.0, 0.0)?,
213 },
214 ],
215 }],
216 name: None,
217 }],
218 }],
219 genus: 1,
220 },
221 "torus",
222 )
223}
224
225pub fn make_cone_brep(
226 base: Vec3,
227 axis_direction: Vec3,
228 radius_bottom: f64,
229 radius_top: f64,
230 height: f64,
231) -> Result<BrepSolid, String> {
232 if radius_bottom <= 0.0 || radius_top < 0.0 || height <= 0.0 {
233 return Err("makeConeSolid: invalid radius or height".into());
234 }
235 let axis = axis_direction.normalized()?;
236 let side = make_cone_surface(base, axis, radius_bottom, radius_top, height)?;
237 let bottom_point = side.evaluate(0.0, 0.0)?;
238 let bottom_circle = side.iso_curve_v(0.0)?;
239 let seam = side.iso_curve_u(0.0)?;
240 let x_axis = axis.perpendicular()?;
241 let y_axis = axis.cross(x_axis).normalized()?;
242 let bottom_origin = base
243 .add(x_axis.scale(-radius_bottom))
244 .add(y_axis.scale(-radius_bottom));
245 let bottom_plane = make_plane(
246 bottom_origin,
247 x_axis,
248 y_axis,
249 2.0 * radius_bottom,
250 2.0 * radius_bottom,
251 )?;
252 let bottom_pcurve =
253 curve_to_plane_parameters(&bottom_circle, bottom_origin, x_axis, y_axis)?.reversed()?;
254 let bottom_face = FaceRecord {
255 id: 108,
256 surface: bottom_plane,
257 same_sense: false,
258 loops: vec![LoopRecord {
259 id: 107,
260 coedges: vec![CoedgeRecord {
261 id: 106,
262 edge_id: 10,
263 forward: false,
264 pcurve: bottom_pcurve,
265 }],
266 }],
267 name: None,
268 };
269
270 if radius_top <= 1e-7 {
271 let apex = side.evaluate(0.0, 1.0)?;
272 return validated(
273 BrepSolid {
274 id: 140,
275 vertices: vec![
276 VertexRecord {
277 id: 1,
278 point: bottom_point,
279 },
280 VertexRecord { id: 2, point: apex },
281 ],
282 edges: vec![
283 EdgeRecord {
284 id: 10,
285 curve: bottom_circle,
286 t0: 0.0,
287 t1: 1.0,
288 start_vertex_id: 1,
289 end_vertex_id: 1,
290 degenerate: false,
291 name: None,
292 },
293 EdgeRecord {
294 id: 11,
295 curve: seam,
296 t0: 0.0,
297 t1: 1.0,
298 start_vertex_id: 1,
299 end_vertex_id: 2,
300 degenerate: false,
301 name: None,
302 },
303 EdgeRecord {
304 id: 12,
305 curve: make_line(apex, apex)?,
306 t0: 0.0,
307 t1: 1.0,
308 start_vertex_id: 2,
309 end_vertex_id: 2,
310 degenerate: true,
311 name: None,
312 },
313 ],
314 shells: vec![ShellRecord {
315 id: 139,
316 faces: vec![
317 FaceRecord {
318 id: 105,
319 surface: side,
320 same_sense: true,
321 loops: vec![LoopRecord {
322 id: 100,
323 coedges: vec![
324 CoedgeRecord {
325 id: 101,
326 edge_id: 10,
327 forward: true,
328 pcurve: parameter_line(0.0, 0.0, 1.0, 0.0)?,
329 },
330 CoedgeRecord {
331 id: 102,
332 edge_id: 11,
333 forward: true,
334 pcurve: parameter_line(1.0, 0.0, 1.0, 1.0)?,
335 },
336 CoedgeRecord {
337 id: 103,
338 edge_id: 12,
339 forward: true,
340 pcurve: parameter_line(1.0, 1.0, 0.0, 1.0)?,
341 },
342 CoedgeRecord {
343 id: 104,
344 edge_id: 11,
345 forward: false,
346 pcurve: parameter_line(0.0, 1.0, 0.0, 0.0)?,
347 },
348 ],
349 }],
350 name: None,
351 },
352 bottom_face,
353 ],
354 }],
355 genus: 0,
356 },
357 "cone",
358 );
359 }
360
361 let top_point = side.evaluate(0.0, 1.0)?;
362 let top_circle = side.iso_curve_v(1.0)?;
363 let top_origin = base
364 .add(axis.scale(height))
365 .add(x_axis.scale(-radius_top))
366 .add(y_axis.scale(-radius_top));
367 let top_plane = make_plane(
368 top_origin,
369 x_axis,
370 y_axis,
371 2.0 * radius_top,
372 2.0 * radius_top,
373 )?;
374 let top_pcurve = curve_to_plane_parameters(&top_circle, top_origin, x_axis, y_axis)?;
375 validated(
376 BrepSolid {
377 id: 150,
378 vertices: vec![
379 VertexRecord {
380 id: 1,
381 point: bottom_point,
382 },
383 VertexRecord {
384 id: 2,
385 point: top_point,
386 },
387 ],
388 edges: vec![
389 EdgeRecord {
390 id: 10,
391 curve: bottom_circle,
392 t0: 0.0,
393 t1: 1.0,
394 start_vertex_id: 1,
395 end_vertex_id: 1,
396 degenerate: false,
397 name: None,
398 },
399 EdgeRecord {
400 id: 11,
401 curve: top_circle,
402 t0: 0.0,
403 t1: 1.0,
404 start_vertex_id: 2,
405 end_vertex_id: 2,
406 degenerate: false,
407 name: None,
408 },
409 EdgeRecord {
410 id: 12,
411 curve: seam,
412 t0: 0.0,
413 t1: 1.0,
414 start_vertex_id: 1,
415 end_vertex_id: 2,
416 degenerate: false,
417 name: None,
418 },
419 ],
420 shells: vec![ShellRecord {
421 id: 149,
422 faces: vec![
423 FaceRecord {
424 id: 105,
425 surface: side,
426 same_sense: true,
427 loops: vec![LoopRecord {
428 id: 100,
429 coedges: vec![
430 CoedgeRecord {
431 id: 101,
432 edge_id: 10,
433 forward: true,
434 pcurve: parameter_line(0.0, 0.0, 1.0, 0.0)?,
435 },
436 CoedgeRecord {
437 id: 102,
438 edge_id: 12,
439 forward: true,
440 pcurve: parameter_line(1.0, 0.0, 1.0, 1.0)?,
441 },
442 CoedgeRecord {
443 id: 103,
444 edge_id: 11,
445 forward: false,
446 pcurve: parameter_line(1.0, 1.0, 0.0, 1.0)?,
447 },
448 CoedgeRecord {
449 id: 104,
450 edge_id: 12,
451 forward: false,
452 pcurve: parameter_line(0.0, 1.0, 0.0, 0.0)?,
453 },
454 ],
455 }],
456 name: None,
457 },
458 bottom_face,
459 FaceRecord {
460 id: 111,
461 surface: top_plane,
462 same_sense: true,
463 loops: vec![LoopRecord {
464 id: 110,
465 coedges: vec![CoedgeRecord {
466 id: 109,
467 edge_id: 11,
468 forward: true,
469 pcurve: top_pcurve,
470 }],
471 }],
472 name: None,
473 },
474 ],
475 }],
476 genus: 0,
477 },
478 "cone frustum",
479 )
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485
486 #[test]
487 fn exact_revolved_primitives_are_valid() {
488 let axis = Vec3::new(0.2, 1.0, 0.4);
489 assert!(make_sphere_brep(Vec3::default(), 5.0, axis)
490 .unwrap()
491 .validate()
492 .is_empty());
493 assert!(make_torus_brep(Vec3::default(), axis, 10.0, 2.0)
494 .unwrap()
495 .validate()
496 .is_empty());
497 assert!(make_cone_brep(Vec3::default(), axis, 5.0, 0.0, 11.0)
498 .unwrap()
499 .validate()
500 .is_empty());
501 assert!(make_cone_brep(Vec3::default(), axis, 5.0, 2.0, 11.0)
502 .unwrap()
503 .validate()
504 .is_empty());
505 }
506}