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