The BIM Layer
brepjs-bim turns brepjs geometry into building information. You describe elements as typed parametric specs (a wall is a length, height, thickness, placement, and material, not a mesh); the model assembles them into a spatial structure (project → site → building → storey), layers on property sets, materials, quantities, and classifications, and serializes the result to a valid IFC-SPF file. A matching importer reads IFC back in.
bash
npm install brepjs-bim brepjs web-ifcTwo ways in:
- Through families (recommended for new models): author a declarative element tree with brepjs-families and project it with
familiesToBim. Identity, openings, and containment come from the tree; GlobalIds derive from key paths and survive reorders. Start at IFC Export. - Direct
BimModel: imperativeadd*calls when you already know exactly what to build, or when you need elements the families projection does not cover yet.
typescript
import { BimModel, toIfc } from 'brepjs-bim';
import { unwrap } from 'brepjs';
const model = new BimModel();
model.init({ name: 'Example' });
const siteId = unwrap(model.addSite({ name: 'Site' }));
const buildingId = unwrap(model.addBuilding({ name: 'Building' }));
const storeyId = unwrap(model.addStorey({ name: 'Level 1', elevation: 0 }));
const project = model.getProject();
if (project) model.aggregate(project.localId, siteId);
model.aggregate(siteId, buildingId);
model.aggregate(buildingId, storeyId);
const wall = model.addWall({
length: 4000,
height: 3000,
thickness: 200,
origin: [0, 0, 0],
axisX: [1, 0, 0],
axisZ: [0, 0, 1],
materialName: 'Concrete',
});
if (wall.ok) model.placeIn(wall.value, storeyId);
const ifc = await toIfc(model, { applicationName: 'example-app', applicationVersion: '1' });
// ifc.ok && ifc.value instanceof Uint8ArrayThree design decisions carry the package:
- Specs are the source of truth. Every
add*call validates its spec (zod schemas; theparse*Specfunctions are exported for standalone use), builds the brepjs solid analytically, and stores a typed element. The IFC writer emits parametric entities (IfcExtrudedAreaSolid, profile defs) from the same spec, so the exported file stays editable data, not frozen triangles. - Geometry is unplaced template geometry. Element solids live in local coordinates;
origin/axisX/axisZare applied by the IFC layer viaIfcLocalPlacement. UseplacedSolids(element)when you need world-placed solids for display. - Results, not exceptions. Every operation returns
Result<T, BimError>from brepjs. Validation issues travel inside reports; nothing throws across the API boundary.
Dimensions are millimeters everywhere; IFC export emits SI metres. Reading element geometry needs only the brepjs kernel; toIfc / fromIfc additionally load the web-ifc peer dependency.
Continue with the element catalog, IFC export & import, validation, and interop.