Skip to main content

brepkit_topology/
shell.rs

1//! Shell — a connected set of faces forming a surface boundary.
2
3use crate::TopologyError;
4use crate::arena;
5use crate::face::FaceId;
6
7/// Typed handle for a [`Shell`] stored in an [`Arena`](crate::Arena).
8pub type ShellId = arena::Id<Shell>;
9
10/// A topological shell: a connected set of faces.
11///
12/// A closed shell bounds a volume (solid). An open shell represents
13/// a sheet or partial boundary.
14#[derive(Debug, Clone)]
15pub struct Shell {
16    /// The faces that make up this shell.
17    faces: Vec<FaceId>,
18}
19
20impl Shell {
21    /// Creates a new shell from a non-empty list of faces.
22    ///
23    /// # Errors
24    ///
25    /// Returns [`TopologyError::Empty`] if `faces` is empty.
26    pub fn new(faces: Vec<FaceId>) -> Result<Self, TopologyError> {
27        if faces.is_empty() {
28            return Err(TopologyError::Empty { entity: "shell" });
29        }
30        Ok(Self { faces })
31    }
32
33    /// Creates a faceless shell backing an empty-result sentinel.
34    ///
35    /// A regular shell rejects an empty face list because an ordinary
36    /// surface boundary must enclose something. The empty shell exists
37    /// only so a boolean whose algebraic outcome is the empty set
38    /// (e.g. the intersection of disjoint solids) can be represented as
39    /// a valid, queryable solid handle reporting zero faces and zero
40    /// volume — distinct from a malformed-input error.
41    #[must_use]
42    pub const fn empty() -> Self {
43        Self { faces: Vec::new() }
44    }
45
46    /// Returns `true` when this shell has no faces (the empty-result
47    /// sentinel — see [`Shell::empty`]).
48    #[must_use]
49    pub fn is_empty(&self) -> bool {
50        self.faces.is_empty()
51    }
52
53    /// Returns the faces of this shell.
54    #[must_use]
55    pub fn faces(&self) -> &[FaceId] {
56        &self.faces
57    }
58
59    /// Returns mutable access to the faces of this shell.
60    ///
61    /// Allows in-place mutation (reorder, replace) but not removal.
62    /// The shell must always contain at least one face.
63    pub fn faces_mut(&mut self) -> &mut [FaceId] {
64        &mut self.faces
65    }
66}