Expand description
§brepkit-topology
Arena-allocated boundary representation (B-Rep) data structures.
Layer L1, depending only on brepkit-math.
§Topology and geometry are separate
A B-Rep solid is defined by its boundary: the surfaces, edges, and vertices that form its skin. brepkit keeps how things connect apart from where they are in space.
- Topology:
VertextoEdgetoWiretoFacetoShelltoSolid. - Geometry: points, curves, and surfaces, all owned by
brepkit-math.
A Face knows which wires bound it (topology) and which
FaceSurface defines its shape (geometry). Keeping
the two apart is what lets a boolean reason about connectivity without
re-deriving it from coordinates every time.
§Arena allocation
Every entity lives in a central Arena and is referenced by a typed
Id<T> handle rather than a pointer or an Rc. This keeps
traversal cache-friendly, drops reference-counting overhead, gives O(1)
lookup, and makes ownership unambiguous: the arena owns everything, and a
handle is just an index.
The consequence to know about is aliasing. You cannot hold a shared borrow of the arena while taking a mutable one, so read what you need into locals first, then allocate:
use brepkit_math::vec::Point3;
use brepkit_topology::Topology;
use brepkit_topology::vertex::Vertex;
let mut topo = Topology::new();
let original = topo.add_vertex(Vertex::new(Point3::new(1.0, 2.0, 3.0), 1e-7));
// Snapshot the read, then allocate. Doing both in one expression would
// borrow the arena immutably and mutably at the same time.
let position = topo.vertex(original)?.point();
let copy = topo.add_vertex(Vertex::new(position, 1e-7));
assert_eq!(topo.vertex(copy)?.point().x(), 1.0);§Surfaces and curves are enums
FaceSurface is one of Plane, Cylinder, Cone,
Sphere, Torus, or Nurbs. EdgeCurve is one of
Line, Circle, Ellipse, or NurbsCurve. Analytic types are
deliberately special-cased rather than collapsed into NURBS:
- Operations preserve them. A cylinder cut by a plane stays a cylinder, so face counts stay flat across chained booleans instead of growing at every step.
- Intersections take exact closed-form paths where a pair allows one, falling back to NURBS marching only when no analytic solution exists.
- STEP export writes them as native surface entities, so a round-trip is lossless rather than an approximation.
Both enums are exhaustive, with no _ => wildcards in production code.
That is a deliberate trade: adding a variant is a breaking change for every
downstream matcher, but the compiler finds every site that needs updating.
Prefer the delegate methods (evaluate, normal, type_tag, and the
rest, defined in brepkit_math::traits) over matching variants directly,
since code that goes through a delegate keeps compiling when a variant is
added.
§A solid is not just its outer shell
This is the trap that costs the most time. A Solid has an
outer shell and zero or more inner shells, which are the cavity walls
left by a hollowing operation or a boolean cut that opened a void. Code
that reaches through outer_shell() and iterates its faces compiles, runs,
and gives the right answer on every model without a cavity. On a hollow
part it silently skips the interior and reports a volume or a face count
that is quietly wrong. (A bounding box survives, because a cavity sits
inside the outer shell and cannot extend it.)
Use explorer::solid_faces, which flattens outer and inner shells into
one list:
use brepkit_topology::Topology;
use brepkit_topology::explorer::solid_faces;
let mut topo = Topology::new();
let solid = topo.add_empty_solid();
// Covers cavity faces too. Walking `outer_shell()` by hand does not.
let faces = solid_faces(&topo, solid)?;
assert!(faces.is_empty());The exception is work that is genuinely per-shell: orientation fixes, sewing, and any guard whose job is to reason about one shell at a time. Those should keep iterating shell by shell. The rule is about scope. If the question is about the solid (how many faces, what does it weigh, what type is this feature), flatten. If the question is about a shell, do not.
explorer::solid_edges and explorer::solid_vertices follow the same
convention.
§See also
brepkit_math: the geometry these structures carry, and the tolerance model they compare with.brepkit_operations: the modeling operations that build and consume this topology.- brepjs.dev: the same hierarchy from the TypeScript side.
Re-exports§
pub use arena::Arena;pub use compound::CompoundId;pub use compsolid::CompSolidId;pub use edge::EdgeId;pub use face::FaceId;pub use shell::ShellId;pub use solid::SolidId;pub use topology::Topology;pub use vertex::VertexId;pub use wire::OrientedEdge;pub use wire::WireId;
Modules§
- adjacency
- Solid-scoped adjacency index for edge-to-face and face-to-face queries.
- arena
- A typed arena allocator for topological entities.
- builder
- Builder utilities for edges and wires.
- compound
- Compound — a collection of solids treated as a single entity.
- compsolid
CompSolid— a set of solids sharing faces.- edge
- Edge — a curve bounded by two vertices.
- explorer
- Topology exploration and query utilities.
- face
- Face — a bounded region of a surface.
- orientation
- Face-orientation propagation and normal alignment.
- pcurve
PCurve— 2D parametric curves on surfaces.- shell
- Shell — a connected set of faces forming a surface boundary.
- solid
- Solid — a volume bounded by closed shells.
- test_
utils - Test helper functions for building common topological shapes.
- topology
- Central context holding all topological arenas.
- validation
- Topology validation utilities.
- vertex
- Vertex — a point in 3D space with an associated tolerance.
- wire
- Wire — an ordered sequence of oriented edges forming a path or loop.
Enums§
- Topology
Error - Errors from topology operations.