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 },
127 "sphere",
128 )
129}
130
131pub fn make_torus_brep(
132 center: Vec3,
133 axis_direction: Vec3,
134 major_radius: f64,
135 minor_radius: f64,
136) -> Result<BrepSolid, String> {
137 let surface = make_torus_surface(center, axis_direction, major_radius, minor_radius)?;
138 let corner = surface.evaluate(0.0, 0.0)?;
139 let major_curve = surface.iso_curve_v(0.0)?;
140 let tube_curve = surface.iso_curve_u(0.0)?;
141 validated(
142 BrepSolid {
143 mass_properties_cache: Default::default(),
144 id: 130,
145 vertices: vec![VertexRecord {
146 id: 1,
147 point: corner,
148 }],
149 edges: vec![
150 EdgeRecord {
151 id: 10,
152 curve: major_curve,
153 t0: 0.0,
154 t1: 1.0,
155 start_vertex_id: 1,
156 end_vertex_id: 1,
157 degenerate: false,
158 name: None,
159 },
160 EdgeRecord {
161 id: 11,
162 curve: tube_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 ],
171 shells: vec![ShellRecord {
172 id: 129,
173 faces: vec![FaceRecord {
174 id: 128,
175 surface,
176 same_sense: true,
177 loops: vec![LoopRecord {
178 id: 127,
179 coedges: vec![
180 CoedgeRecord {
181 id: 101,
182 edge_id: 10,
183 forward: true,
184 pcurve: parameter_line(0.0, 0.0, 1.0, 0.0)?,
185 },
186 CoedgeRecord {
187 id: 102,
188 edge_id: 11,
189 forward: true,
190 pcurve: parameter_line(1.0, 0.0, 1.0, 1.0)?,
191 },
192 CoedgeRecord {
193 id: 103,
194 edge_id: 10,
195 forward: false,
196 pcurve: parameter_line(1.0, 1.0, 0.0, 1.0)?,
197 },
198 CoedgeRecord {
199 id: 104,
200 edge_id: 11,
201 forward: false,
202 pcurve: parameter_line(0.0, 1.0, 0.0, 0.0)?,
203 },
204 ],
205 }],
206 name: None,
207 }],
208 }],
209 genus: 1,
210 },
211 "torus",
212 )
213}
214
215pub fn make_cone_brep(
216 base: Vec3,
217 axis_direction: Vec3,
218 radius_bottom: f64,
219 radius_top: f64,
220 height: f64,
221) -> Result<BrepSolid, String> {
222 if radius_bottom <= 0.0 || radius_top < 0.0 || height <= 0.0 {
223 return Err("makeConeSolid: invalid radius or height".into());
224 }
225 let axis = axis_direction.normalized()?;
226 let side = make_cone_surface(base, axis, radius_bottom, radius_top, height)?;
227 let bottom_point = side.evaluate(0.0, 0.0)?;
228 let bottom_circle = side.iso_curve_v(0.0)?;
229 let seam = side.iso_curve_u(0.0)?;
230 let x_axis = axis.perpendicular()?;
231 let y_axis = axis.cross(x_axis).normalized()?;
232 let bottom_origin = base
233 .add(x_axis.scale(-radius_bottom))
234 .add(y_axis.scale(-radius_bottom));
235 let bottom_plane = make_plane(
236 bottom_origin,
237 x_axis,
238 y_axis,
239 2.0 * radius_bottom,
240 2.0 * radius_bottom,
241 )?;
242 let bottom_pcurve =
243 curve_to_plane_parameters(&bottom_circle, bottom_origin, x_axis, y_axis)?.reversed()?;
244 let bottom_face = FaceRecord {
245 id: 108,
246 surface: bottom_plane,
247 same_sense: false,
248 loops: vec![LoopRecord {
249 id: 107,
250 coedges: vec![CoedgeRecord {
251 id: 106,
252 edge_id: 10,
253 forward: false,
254 pcurve: bottom_pcurve,
255 }],
256 }],
257 name: None,
258 };
259
260 if radius_top <= 1e-7 {
261 let apex = side.evaluate(0.0, 1.0)?;
262 return validated(
263 BrepSolid {
264 mass_properties_cache: Default::default(),
265 id: 140,
266 vertices: vec![
267 VertexRecord {
268 id: 1,
269 point: bottom_point,
270 },
271 VertexRecord { id: 2, point: apex },
272 ],
273 edges: vec![
274 EdgeRecord {
275 id: 10,
276 curve: bottom_circle,
277 t0: 0.0,
278 t1: 1.0,
279 start_vertex_id: 1,
280 end_vertex_id: 1,
281 degenerate: false,
282 name: None,
283 },
284 EdgeRecord {
285 id: 11,
286 curve: seam,
287 t0: 0.0,
288 t1: 1.0,
289 start_vertex_id: 1,
290 end_vertex_id: 2,
291 degenerate: false,
292 name: None,
293 },
294 EdgeRecord {
295 id: 12,
296 curve: make_line(apex, apex)?,
297 t0: 0.0,
298 t1: 1.0,
299 start_vertex_id: 2,
300 end_vertex_id: 2,
301 degenerate: true,
302 name: None,
303 },
304 ],
305 shells: vec![ShellRecord {
306 id: 139,
307 faces: vec![
308 FaceRecord {
309 id: 105,
310 surface: side,
311 same_sense: true,
312 loops: vec![LoopRecord {
313 id: 100,
314 coedges: vec![
315 CoedgeRecord {
316 id: 101,
317 edge_id: 10,
318 forward: true,
319 pcurve: parameter_line(0.0, 0.0, 1.0, 0.0)?,
320 },
321 CoedgeRecord {
322 id: 102,
323 edge_id: 11,
324 forward: true,
325 pcurve: parameter_line(1.0, 0.0, 1.0, 1.0)?,
326 },
327 CoedgeRecord {
328 id: 103,
329 edge_id: 12,
330 forward: true,
331 pcurve: parameter_line(1.0, 1.0, 0.0, 1.0)?,
332 },
333 CoedgeRecord {
334 id: 104,
335 edge_id: 11,
336 forward: false,
337 pcurve: parameter_line(0.0, 1.0, 0.0, 0.0)?,
338 },
339 ],
340 }],
341 name: None,
342 },
343 bottom_face,
344 ],
345 }],
346 genus: 0,
347 },
348 "cone",
349 );
350 }
351
352 let top_point = side.evaluate(0.0, 1.0)?;
353 let top_circle = side.iso_curve_v(1.0)?;
354 let top_origin = base
355 .add(axis.scale(height))
356 .add(x_axis.scale(-radius_top))
357 .add(y_axis.scale(-radius_top));
358 let top_plane = make_plane(
359 top_origin,
360 x_axis,
361 y_axis,
362 2.0 * radius_top,
363 2.0 * radius_top,
364 )?;
365 let top_pcurve = curve_to_plane_parameters(&top_circle, top_origin, x_axis, y_axis)?;
366 validated(
367 BrepSolid {
368 mass_properties_cache: Default::default(),
369 id: 150,
370 vertices: vec![
371 VertexRecord {
372 id: 1,
373 point: bottom_point,
374 },
375 VertexRecord {
376 id: 2,
377 point: top_point,
378 },
379 ],
380 edges: vec![
381 EdgeRecord {
382 id: 10,
383 curve: bottom_circle,
384 t0: 0.0,
385 t1: 1.0,
386 start_vertex_id: 1,
387 end_vertex_id: 1,
388 degenerate: false,
389 name: None,
390 },
391 EdgeRecord {
392 id: 11,
393 curve: top_circle,
394 t0: 0.0,
395 t1: 1.0,
396 start_vertex_id: 2,
397 end_vertex_id: 2,
398 degenerate: false,
399 name: None,
400 },
401 EdgeRecord {
402 id: 12,
403 curve: seam,
404 t0: 0.0,
405 t1: 1.0,
406 start_vertex_id: 1,
407 end_vertex_id: 2,
408 degenerate: false,
409 name: None,
410 },
411 ],
412 shells: vec![ShellRecord {
413 id: 149,
414 faces: vec![
415 FaceRecord {
416 id: 105,
417 surface: side,
418 same_sense: true,
419 loops: vec![LoopRecord {
420 id: 100,
421 coedges: vec![
422 CoedgeRecord {
423 id: 101,
424 edge_id: 10,
425 forward: true,
426 pcurve: parameter_line(0.0, 0.0, 1.0, 0.0)?,
427 },
428 CoedgeRecord {
429 id: 102,
430 edge_id: 12,
431 forward: true,
432 pcurve: parameter_line(1.0, 0.0, 1.0, 1.0)?,
433 },
434 CoedgeRecord {
435 id: 103,
436 edge_id: 11,
437 forward: false,
438 pcurve: parameter_line(1.0, 1.0, 0.0, 1.0)?,
439 },
440 CoedgeRecord {
441 id: 104,
442 edge_id: 12,
443 forward: false,
444 pcurve: parameter_line(0.0, 1.0, 0.0, 0.0)?,
445 },
446 ],
447 }],
448 name: None,
449 },
450 bottom_face,
451 FaceRecord {
452 id: 111,
453 surface: top_plane,
454 same_sense: true,
455 loops: vec![LoopRecord {
456 id: 110,
457 coedges: vec![CoedgeRecord {
458 id: 109,
459 edge_id: 11,
460 forward: true,
461 pcurve: top_pcurve,
462 }],
463 }],
464 name: None,
465 },
466 ],
467 }],
468 genus: 0,
469 },
470 "cone frustum",
471 )
472}
473