Skip to main content

brep_kernel/brep/
analytic_topology.rs

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