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        },
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