An arris is the sharp edge where two surfaces meet — the thing a B-Rep kernel exists to compute.
Arris is a library, not an application. It builds solids, cuts and fuses them, blends their edges, measures, tessellates and exports them, from Rust or from Python. Four things set it apart:
- Every operation says what it did. It returns the new body and a provenance record — the faces, edges and vertices it generated, modified and deleted — so a script can pick "the edge the extrude made" instead of an index that shifts when the model changes.
- It refuses rather than guesses. Geometry it cannot do is a typed error naming the entities involved, never a panic and never an invalid shape; every result passes the invariant checker. Every vertex, edge and face carries its own tolerance instead of one global epsilon.
- It is deterministic. Same input, same output, same entity ids, on
every platform: a body built for
wasm32is byte-identical to the native one. - It is pure Rust. No
unsafe, no C++ underneath, one crate to depend on, and a Python wheel built from the same code.
Pre-1.0: the API breaks between minor versions. CHANGELOG.md lists every change, and every break with its fix.
Install
Python — a prebuilt wheel for Linux, macOS and Windows, no Rust toolchain needed:
Rust — one crate, arris, re-exports the whole kernel; the API is on
docs.rs:
Example: a bracket in Python
# A motor-mount bracket: extruded L profile, fused boss, bore and bolt pattern cut
# in one call, fillets on the bend and hole rims, and a chamfer on the boss.
# Full script with per-operation face colouring: crates/arris-py/examples/bracket.py
# 1. Extrude the L plate, then fuse a boss on the upright leg
=
=
= # a tag follows its faces through every later operation
=
# 2. Cut bore, bolt holes and counterbores in one multi-tool call
=
=
=
=
# 3. Fillets and chamfer: edges selected by provenance, never by index
=
=
= - -
=
= &
=
=
=
=
The binding beneath
arris.build is pure Python over the binding, arris.Model, which stays
available for anything the layer does not wrap:
# A motor-mount bracket: extruded L profile, fused boss, bore and bolt pattern cut
# in one call, fillets on the bend and hole rims, and a chamfer on the boss.
# Every face is coloured by the operation that made it, followed through provenance.
# The same part on the raw binding; the layer above it is the first snippet.
=
# 1. Extrude the L plate
=
, =
# 2. Fuse a boss on the upright leg
, =
, =
# 3. Cut bore, bolt holes, and counterbores in one multi-tool call
=
=
, =
=
# 4. Fillets and chamfer: edges selected by provenance
=
, =
=
, =
=
, =
What it does
| Area | What you get |
|---|---|
| Modelling | Primitives, rigid transforms and mirroring; extrude (in any direction) and revolve of profiles of lines, arcs and elliptic arcs; offset faces, shell to a wall, split by a plane; a profile as a one-face sheet body |
| Sweep and loft | Sweep a profile along lines, arcs, helix arcs and B-spline curves, with rotation-minimising or fixed-binormal frames; loft through two or more profiles, ruled or smooth |
| Booleans | Cut, fuse and common, one tool or many in one call, in any pose, over planes, cylinders, elliptic cylinders, cones, spheres, tori and NURBS faces — free-form against analytic and free-form against free-form; results with cavities, split pieces and disjoint shells |
| Fillet and chamfer | Constant radius along chains of edges, with mitres, corners, steps and hole rims; on free-form faces and between two of them, the blend a NURBS surface within your tolerance of the exact one |
| Queries | Ray hits, minimum distance, interference with the shared volume, clash screening of an assembly, the planar section of a body; volume, area, centroid and inertia; face frames and plane projection. None of them changes the body |
| Meshing | Tessellation to a chord tolerance, per face or whole, faces meshed at different times welded together; NumPy arrays from Python |
| STEP | Every solid or surface model in a file read as a checked body or a typed refusal naming why; small gaps closed and reversed faces turned back, each repair named on the body; assemblies; AP214 export in the unit you choose |
| Other formats | STL and OBJ export; a deterministic native format in JSON and bytes, which every later release reads |
Not yet
Each of these is a typed refusal today, never a wrong answer:
- variable-radius fillets and chamfers;
- offset and shell of elliptic-cylinder and NURBS faces;
- a NURBS face lying flush against another face over an area;
- sewing the faces of a STEP file that share no edge;
- non-manifold bodies.
Arris is narrower than Open CASCADE and is not aiming for feature parity. docs/COMPARISON.md sets out what each does today and where they differ, and its capability matrix, held cell for cell by a test, gives what each operation does and refuses for each face kind.
How it is tested
- An oracle for every fixture. Each fixture in the test corpus carries values computed by Open CASCADE — volume, area, centroid, counts, point classifications — that Arris must match within the fixture's tolerance. Open CASCADE runs as the oracle through Python and is never a build or runtime dependency (PROVENANCE.md).
- A checker after every operation. Every result passes the invariant checker, which runs after every operation in debug builds.
- Properties over random operands. Volume additivity, cut-then-fuse, commutativity, STEP round trips and more, over random operands in random poses from a fixed seed; nightly at five times CI's case count.
- Real parts. A corpus of real STEP parts is read, checked, cut and blended, and a differential runs random recipes through Arris and Open CASCADE side by side.
Workspace
crates/arris is the facade a consumer depends on. It re-exports the
layered crates beneath it, and a crate never depends on one above it
(tools/check-layers.sh holds that in CI and in the pre-commit hook):
| Crate | Holds |
|---|---|
arris-math |
Points, frames, intervals, tolerances, the deterministic transcendentals |
arris-geom |
Curves and surfaces, NURBS, intersections, sections and fits |
arris-topo |
The B-Rep in an arena: bodies, shells, faces, edges, vertices, provenance |
arris-check |
The invariant checker |
arris-ops |
Primitives, booleans, blends, sweeps, lofts, queries |
arris-mesh |
Tessellation |
arris-io |
STEP, STL, OBJ and the native format |
arris-py |
The Python binding, published to PyPI |
arris-debug holds the developer tools — the rasteriser, the fixtures, the
property-test strategies — and is not published. The layout and its reasons
are in docs/ARCHITECTURE.md.
Prior work
Arris stands on four open projects, and it reads them, not copies them. Arris contains no code, comments or identifiers from any of them, and no line-by-line translation. Each one was read for its algorithms and for the mistakes it made, and the approach was reimplemented for Arris's own representation. PROVENANCE.md sets out the rule.
- Open CASCADE (LGPL 2.1, with an exception). The mature C++ kernel, and the test oracle. Arris runs it through Python to produce expected values; no crate links it, at build or run time. See docs/COMPARISON.md.
- truck. The original pure-Rust NURBS B-Rep. Arris learns from its NURBS evaluation, its tessellation and its marching intersections, and from why they fail at seams.
- monstertruck. An active fork of truck, with fillets, healing and more STEP. Arris learns from its fillet and healing work.
- Fornjot. A validation-first B-Rep kernel, which never reached booleans. Arris learns from its layered validation and its per-object checks.
Licence
MIT or Apache-2.0, at your option. See LICENSE-MIT and LICENSE-APACHE.