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