Architecture¤
Brep is a thin public interface. It holds a reference to a native
object — the backend's own Brep representation — and owns no geometry
logic itself. Every method delegates to whichever backend is active.
graph TD
U["your code"] --> B["Brep<br/>(public interface, brep.py)"]
B --> P["operations.py<br/>@pluggable functions"]
P -->|"OCP importable"| OP["backend/occ/plugins.py<br/>@plugin"]
P -->|"Rhino importable"| RP["backend/rhino/plugins.py<br/>@plugin"]
OP --> OI["backend/occ/*.py"]
RP --> RI["backend/rhino/*.py"]
OI --> ON["TopoDS_Shape"]
RI --> RN["Rhino.Geometry.Brep"]
Backend dispatch¤
Dispatch uses COMPAS's plugin system.
Every operation is declared once as a @pluggable function in
operations.py —
just a signature, no implementation. Each backend registers a @plugin
implementation, gated by what's importable:
- OCC activates when
OCP(cadquery-ocp-novtk) is importable. - Rhino activates when
Rhinois importable (rhinoinside, or running inside Rhino/Grasshopper).
Calling brep.fillet(...) calls the brep_fillet pluggable, and COMPAS
routes it to whichever @plugin is active. Nothing in Brep, and nothing in
your code, knows or cares which one that is.
Topology objects¤
BrepVertex, BrepEdge, BrepLoop,
BrepFace, BrepTrim (see What is a Brep?) are thin
wrappers around a native handle, not data containers. A property like
BrepFace.surface calls into the native kernel on first access and caches
the result — it doesn't copy geometry out eagerly.
NurbsCurve and NurbsSurface are the exception: plain Python value types
(control points, knots, weights) with no backend dependency, used as the
return type for curve/surface properties.
Exchange format¤
A Brep is only ever alive inside one backend process. Moving one across a
process boundary — Grasshopper to CI, OCC to Rhino — happens through a
STEP-inspired JSON exchange document, produced by Brep.__data__ /
Brep.__from_data__. It encodes the same entities STEP does (vertices,
edges with curves, faces with surfaces, loops with trims) but as COMPAS
JSON. See ADR-0001
for the reasoning.
Brep.to_step / Brep.from_step are separate — for interop with
third-party CAD tools, not for moving between the two backends.