Skip to content
CADCAMFUN
Docs

Documentation

Exact B-rep kernel

Exact boundary representation: analytic and NURBS geometry, Booleans, fillets, hole recognition, mass properties, validation, tessellation and STEP.

What the B-rep kernel is

The exact kernel in packages/core/src/cad/brep (24 source files, about 8 660 lines, plus tests) represents solids by their exact boundary: analytic and NURBS surfaces, trimmed by loops of exact edges. Everything is plain data and pure functions, so a B-rep serialises to JSON, crosses a worker boundary and diffs in the project history unchanged. Booleans, fillets and chamfers, hole recognition, mass properties, validation, watertight tessellation and STEP exchange all work on this one structure.

Exact plate after subtract and fillet Drag to orbit · scroll to zoom · right-drag to pan
A 60 × 40 × 15 box minus a Ø12 cylinder (booleanBrep), then the four vertical edges filleted R4 (blendBrep). Mesh from brepToMesh at chordTol 0.05. 18 vertices, 27 edges, 11 faces; exact volume 34 097.522 mm³.

Topology tables

A Brep is a set of index tables. Entities refer to each other by index, never by object reference.

EntityHoldsSTEP counterpart
Vertexpoint, toleranceVERTEX_POINT
Edgeexact curve, [t0, t1] with t0 < t1, v0 = C(t0), v1 = C(t1), toleranceEDGE_CURVE
Coedgeone use of an edge by a loop: edge index, reversed, optional p-curve in the face (u, v)ORIENTED_EDGE + PCURVE
Loopclosed cycle of coedges; material on the left seen from outside (outer CCW, holes CW)EDGE_LOOP
Faceregion of one surface; reversed means the outward normal is −(S_u × S_v)ADVANCED_FACE
Shellconnected set of faces; a closed shell bounds a volumeCLOSED_SHELL

Seams (an edge used twice by the same face, such as the generator line of a full cylinder) and poles (a loop through a degenerate point, such as a cone apex) need no special entities. That is why the exact sphere has 2 vertices, 1 edge and 1 face, and the torus 1 vertex, 2 edges and 1 face.

Counts of the primitives — real output
makeBox(60, 40, 15)    {"V":8,"E":12,"F":6,"S":1,"surfaces":["plane"]}
makeSphere(10)         {"V":2,"E":1,"F":1,"S":1,"surfaces":["sphere"]}
makeTorus(20, 5)       {"V":1,"E":2,"F":1,"S":1,"surfaces":["torus"]}
makeCone(10, 4, 20)    {"V":2,"E":3,"F":3,"S":1,"surfaces":["cone","plane"]}

Curves and surfaces

Analytic types use the STEP (ISO 10303-42) parameterisations, so faces read from and written to STEP keep their parameters.

TypeParameterisation
lineC(t) = O + t·d, d unit, t arc length
circleC(t) = O + r(cos t·x + sin t·y)
ellipseC(t) = O + a cos t·x + b sin t·y
planeS(u,v) = O + u·x + v·y
cylinderS(u,v) = O + r(cos u·x + sin u·y) + v·z
coneS(u,v) = O + (R + v tan α)(cos u·x + sin u·y) + v·z
sphereS(u,v) = O + r cos v(cos u·x + sin u·y) + r sin v·z
torusS(u,v) = O + (R + r cos v)(cos u·x + sin u·y) + r sin v·z
revolutionS(u,v) = rotate(C(v), axis, u)
extrusionS(u,v) = C(u) + v·d
nurbsrational B-spline curve or surface (Piegl & Tiller algorithms in nurbs.ts)
Exact primitives, tessellated Drag to orbit · scroll to zoom · right-drag to pan
makeBox(10,10,10), makeCylinder(5,10), makeCone(5,2,10) and makeSphere(5), each tessellated by brepToMesh at chordTol 0.2 (12, 430, 122 and 1810 triangles) and placed side by side.
Torus R20 r5 Drag to orbit · scroll to zoom · right-drag to pan
makeTorus(20, 5): a single toroidal face with a seam and no other edges, tessellated at chordTol 1 and 30°. The exact volume 2π²Rr² = 9 869.604 mm³ is what massProperties returns.

Exact Booleans

  1. Intersect. Edges of each solid against the faces of the other (exact points by Newton), edges lying on the other solid's surfaces, and the intersection curves of every pair of non-coincident faces, refined onto both surfaces and recognised as lines and circles, otherwise fitted as cubic B-splines within the fit tolerance.
  2. Imprint. Every intersection point becomes a vertex in one registry for both solids, edges are split there, and each face is cut by the curves on it through a planar arrangement in its (u, v) domain.
  3. Classify. A point inside each fragment is located against the other solid: in, out, or on with the same or opposite orientation.
  4. Sew. Fragments are kept by the operation's rules, coincident pieces become one edge, coplanar neighbours merge, and the result is validated. A result that is not a closed manifold solid throws BooleanError; it is never returned.
boolean.ts
export const booleanBrep: (A: Brep, B: Brep, op: BooleanOp, options?: BooleanOptions) => BooleanResult
export interface BooleanOptions {
  tol?: number          // coincidence, default 1e-8 × model size, at least 1e-7 mm
  fitTol?: number       // fitted intersection curves, default 1e-6 × size, at least 1e-6 mm
  seedAngleDeg?: number // normal turn per seed-mesh triangle, default 10
  debug?: (stage: string, data: unknown) => void
}
export interface BooleanResult {
  brep: Brep; warnings: string[]
  stats: { intersectionCurves: number; splitPoints: number; fragments: number; keptFaces: number }
}
Box minus cylinder — real output
import { brep } from "@cadcamfun/core"
const { makeBox, makeCylinder, frame, booleanBrep, validateBrep, massProperties } = brep

const box = makeBox(60, 40, 15)
const hole = makeCylinder(6, 25, frame({ x: 30, y: 20, z: -5 }))
const r = booleanBrep(box, hole, "subtract")          // 141 ms
r.stats     // {"intersectionCurves":2,"splitPoints":12,"fragments":13,"keptFaces":7}
r.warnings  // []
validateBrep(r.brep)
// {"valid":true,"manifoldSolid":true,"genus":1,"volume":34303.53996706151,"issues":[]}
massProperties(r.brep).volume   // 34303.53996706151  = 60·40·15 − π·6²·15 exactly

Mass properties

Volume, area, centroid and the inertia tensor (unit density, about the centroid, row-major) come from the divergence theorem over the boundary faces. Planar faces are integrated exactly by Green's theorem along the exact edge curves; curved faces are integrated on a parameter-step tessellation of the exact surface.

BodymassPropertiesClosed form
box − Ø12 hole, volume34303.5399670615134303.539967061515
same, area8139.2920065876848139.292006587698
same, centroid(30.000, 20.000, 7.500)(30, 20, 7.5)
sphere r10, volume4188.7902047863794188.790204786391
torus 20/5, volume9869.604401094759869.604401089358
filleted plate, volume34097.5222039230234097.522203923065

The filleted-plate closed form is the subtracted plate minus four corner slivers of (r² − πr²/4)·h = (16 − 4π)·15 each.

Fillets, chamfers and hole recognition

Signatures
export const blendCandidates: (b: Brep, minAngleDegrees?: number /* 0.5 */) => BlendCandidate[]
export const selectBlendEdges: (edges: ReadonlyArray<BlendCandidate>, s?: BlendSelector) => BlendCandidate[]
// BlendSelector: parallel?: "x"|"y"|"z", within?: box, kind?: "convex"|"concave"|"both" (default convex), minLength?, curved?
export const blendBrep: (b: Brep, edges: ReadonlyArray<BlendCandidate>, size: number,
  style?: "fillet" | "chamfer", options?: BlendOptions) => BlendResult
// BlendResult: brep, blended, vertices (rolling-ball corners), rejected (edge ids), ...
export const recognizeHoles: (b: Brep, options?: HoleRecognitionOptions) => ReadonlyArray<RecognizedHole>

On the box − cylinder body there were 14 blend candidates; selectBlendEdges(c, { parallel: "z" }) picked the 4 vertical outer edges, and blendBrep(b, sel, 4) filleted all 4 with none rejected in 94 ms. The hole read back from the faces alone:

recognizeHoles — real output
[{"entry":{"x":30,"y":20,"z":15},"direction":{"x":0,"y":0,"z":-1},"depth":15,"diameter":12,
  "form":"simple","bottom":"through",
  "segments":[{"kind":"cylinder","start":0,"end":15,"diameterStart":12,"diameterEnd":12,"faces":[6]}]}]

Forms are simple, counterbore and countersink; bottoms are through, flat and drill-point. Recognition reads faces, not history, so a STEP import, a Boolean result and a parametric part are recognised alike.

Watertight tessellation

Every edge is sampled once on its exact curve and every face that uses it shares those samples, so the welded mesh has no cracks. Faces are triangulated by constrained Delaunay in their (u, v) domain.

OptionDefaultMeaning
chordTol1/2000 of the model diagonal, clamped to [0.001, 0.05] mmmax distance between mesh and exact surface
angleTolDeg15max normal turn across a triangle (0 disables)
maxEdge0 (none)edge-length target
maxPointsPerFace200 000safety cap
paramStep—integration mode used by massProperties

For the filleted plate defaultChordTol returned 0.0368 mm and tessellateBrep produced 652 triangles plus per-face meshes, edge polylines and warnings.

STEP exchange

writeStepBreps writes an AP214 (AUTOMOTIVE_DESIGN) file in millimetres and radians, one ADVANCED_BREP_SHAPE_REPRESENTATION per part. readStepBreps reads AP203, AP214 and AP242 geometry and topology, including assemblies with their transformations and product names. Unknown entities are reported in warnings and their faces skipped; nothing is guessed.
writeStepBreps([{ name: "plate", brep }]) — first lines of the real 358-line file
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('CADCAMFUN exact B-rep'),'2;1');
FILE_NAME('plate','2026-10-15T04:49:21',(''),(''),'CADCAMFUN B-rep kernel','CADCAMFUN','');
FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'));
ENDSEC;
DATA;
#1=APPLICATION_CONTEXT('core data for automotive mechanical design processes');
#2=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#1);
#3=PRODUCT_CONTEXT('',#1,'mechanical');
#4=PRODUCT_DEFINITION_CONTEXT('part definition',#1,'design');
#5=(LENGTH_UNIT()NAMED_UNIT(*)SI_UNIT(.MILLI.,.METRE.));

Reading that file back gave one part whose volume was 34097.52220392303 mm³, equal to the written body to 11 significant digits.

Time budget

The kernel is synchronous and runs on the caller's thread. withBudget(ms, f) sets a deadline; long loops (face pairs of a Boolean, face splitting, surface intersection, tessellation) call checkBudget, which throws BudgetExceeded. Nested budgets keep the earlier deadline; without a budget a check costs one comparison.

Real output
withBudget(1, () => booleanBrep(makeSphere(10), makeBox(10, 10, 10), "union"))
// BudgetExceeded: the exact model exceeded its time budget of 1 ms

Troubleshooting

SymptomCauseFix
BooleanErrorthe sewn result is not a closed manifold solid (tangent or near-coincident faces)move an operand slightly off tangency; check both with validateBrep first
BudgetExceededthe operation took longer than its budgetsimplify, or give the caller a larger budget
RangeError from a primitivenon-positive size, or torus minor ≥ majore.g. "torus minor radius must be smaller than the major radius"
blend edge in rejectedface too narrow for the radius, knife edge or slanted wallsmaller radius, or a chamfer
STEP part missing facesunsupported entity in the fileread warnings from readStepBreps
validateBrep reports issuesindex, orientation or edge-on-surface error in a built bodythe issue names the entity; nothing is repaired automatically