Skip to main content
Boundary Representation (BREP) is the core data structure used in OpenGeometry to represent geometric entities. It describes 3D objects by defining their boundaries through vertices, edges, and faces.

What is BREP?

BREP is a method of representing shapes by explicitly defining their boundaries. Instead of storing a solid as a filled volume, BREP stores:
  • Vertices - Points in 3D space
  • Edges - Connectivity between vertices
  • Faces - Surfaces bounded by edges (with explicit boundary loops)
This representation is:
  • Exact - Preserves geometric precision
  • Topology-aware - Maintains relationships between elements
  • Efficient - Avoids redundant storage of shared vertices/edges

Why BREP?

OpenGeometry uses BREP because it:
  1. Supports complex operations - Boolean operations, offsetting, and extrusion work naturally on boundaries
  2. Maintains precision - No approximation errors from voxelization or tessellation
  3. Industry standard - Compatible with CAD file formats like STEP, IGES, and Parasolid
  4. Flexible - Can represent wireframes, surfaces, and solids with the same structure

BREP Body Types

OpenGeometry can represent different types of geometric bodies:
  • Solid body - Has volume (e.g., cube, sphere)
  • Shell body - Has surface but no volume (e.g., car body)
  • Sheet body - Has 2D surface (e.g., flat plate)
  • Wireframe body - Has only edges (e.g., line drawing)
  • Point body - Single vertex in space

Data Structure

The BREP structure is defined in the Rust core (main/opengeometry/src/brep/mod.rs in this repo):
pub struct Brep {
    pub id: Uuid,
    pub vertices: Vec<Vertex>,
    pub halfedges: Vec<HalfEdge>,
    pub edges: Vec<Edge>,
    pub loops: Vec<Loop>,
    pub faces: Vec<Face>,
    pub wires: Vec<Wire>,
    pub shells: Vec<Shell>,
}
IDs (u32) in the BREP are array indices. For example, brep.vertices[vertex_id as usize] is the vertex with id vertex_id.

Core Components

Vertex

Defined in main/opengeometry/src/brep/vertex.rs, a vertex represents a point in 3D space and may reference an outgoing half-edge for topology traversal:
pub struct Vertex {
    pub id: u32,
    pub position: Vector3,
    pub outgoing_halfedge: Option<u32>,
}
Example:
let v = Vertex::new(0, Vector3::new(1.0, 2.0, 3.0));

HalfEdge

Defined in main/opengeometry/src/brep/halfedge.rs, a half-edge is the directed topology primitive. Half-edges are the basis for efficient adjacency queries, loop traversal, and hidden line removal.
pub struct HalfEdge {
    pub id: u32,
    pub from: u32,
    pub to: u32,
    pub twin: Option<u32>,
    pub next: Option<u32>,
    pub prev: Option<u32>,
    pub edge: u32,
    pub face: Option<u32>,
    pub loop_ref: Option<u32>,
    pub wire_ref: Option<u32>,
}
Key links:
  • twin points to the opposite half-edge (when the underlying edge is shared).
  • next and prev walk the boundary of a face loop (or a closed wire).

Edge

Defined in main/opengeometry/src/brep/edge.rs, an edge is an undirected edge that points at one or two half-edges:
pub struct Edge {
    pub id: u32,
    pub halfedge: u32,
    pub twin_halfedge: Option<u32>,
}
The endpoints of an edge come from its referenced half-edge(s).

Face

Defined in main/opengeometry/src/brep/face.rs, a face is bounded by an outer loop and zero or more inner loops (holes):
pub struct Face {
    pub id: u32,
    pub normal: Vector3,
    pub outer_loop: u32,
    pub inner_loops: Vec<u32>,
    pub shell_ref: Option<u32>,
}
Example:
let face = Face::new(0, Vector3::new(0.0, 0.0, 1.0), 0, Vec::new(), None);

Loop

Defined in main/opengeometry/src/brep/loop.rs, a loop is a face boundary (outer boundary or an inner hole) represented by a half-edge cycle:
pub struct Loop {
    pub id: u32,
    pub halfedge: u32,
    pub face: u32,
    pub is_hole: bool,
}

Wire

Defined in main/opengeometry/src/brep/wire.rs, a wire is an (optionally closed) polyline stored as a list of half-edges:
pub struct Wire {
    pub id: u32,
    pub halfedges: Vec<u32>,
    pub is_closed: bool,
}

Shell

Defined in main/opengeometry/src/brep/shell.rs, a shell groups faces and can be marked as open or closed:
pub struct Shell {
    pub id: u32,
    pub faces: Vec<u32>,
    pub is_closed: bool,
}

BREP Operations

The Brep struct provides several utility methods. Most callers should construct BReps through BrepBuilder (see below) and treat the resulting BRep as an immutable kernel output.

Querying Geometry

// Get count of elements
let vertex_count = brep.get_vertex_count();
let halfedge_count = brep.get_halfedge_count();
let edge_count = brep.get_edge_count();
let loop_count = brep.get_loop_count();
let face_count = brep.get_face_count();
let wire_count = brep.get_wire_count();
let shell_count = brep.get_shell_count();

// Get vertices for a specific face
let vertices: Vec<Vector3> = brep.get_vertices_by_face_id(0);

// Get all vertices as flat array
let all_vertices: Vec<Vector3> = brep.get_flattened_vertices();

// Get a flat triangle buffer (x, y, z triples)
let triangle_buffer: Vec<f64> = brep.get_triangle_vertex_buffer();

// Get a flat outline buffer (line segments)
let outline_buffer: Vec<f64> = brep.get_outline_vertex_buffer();

Handling Holes

Faces can contain holes (inner loops):
// Get face with holes
let (face_vertices, hole_vertices) = brep.get_vertices_and_holes_by_face_id(0);
// face_vertices: outer boundary
// hole_vertices: Vec of hole boundaries
If you need to build or modify topology, use BrepBuilder (below). You can also clear all geometry with brep.clear().

Example: building a BREP with BrepBuilder

BrepBuilder creates consistent half-edge/edge/loop links and validates topology on build():
use openmaths::Vector3;
use opengeometry::brep::BrepBuilder;
use uuid::Uuid;

let mut builder = BrepBuilder::new(Uuid::new_v4());

// Add vertices
builder.add_vertices(&[
    Vector3::new(0.0, 0.0, 0.0),
    Vector3::new(1.0, 0.0, 0.0),
    Vector3::new(1.0, 0.0, 1.0),
    Vector3::new(0.0, 0.0, 1.0),
]);

// Add a quad face using vertex indices (the builder creates loops, half-edges, and edges).
builder.add_face(&[0, 1, 2, 3], &[])?;

let brep = builder.build()?;

Serialization

BREP structures are serializable via serde:
// Serialize to JSON
let json = serde_json::to_string(&brep)?;

// Deserialize from JSON
let brep: Brep = serde_json::from_str(&json)?;
This enables passing BREP data between Rust and JavaScript via WASM.

Topology Validation

BrepBuilder::build() calls brep.validate_topology() before returning. Exporters can also validate topology (depending on the export config). Valid BREP structures should maintain:
  • Vertex references - All referenced vertex IDs exist in vertex list
  • Half-edge symmetry - twin, next, and prev links are consistent
  • Manifold edges - An edge has at most two incident faces

References

The BREP implementation is based on:

Next Steps

Architecture

Understand how BREP fits in the system architecture

Primitives and Shapes

Explore the available 2D and 3D primitives you can create
Last modified on March 14, 2026