Skip to main content

brep_kernel/brep/
analytic_topology.rs

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
25/// A sphere solid whose SEAM direction is chosen as well as its polar axis — see
26/// [`make_sphere_surface_framed`]. The topology is identical either way (one
27/// face, one pole-to-pole seam edge, two degenerate pole edges); only WHERE on
28/// the ball the seam and the poles sit changes. A caller that will trim the ball
29/// uses it to hide both inside the removed region, so the surviving face has no
30/// edge bordering itself.
31pub 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