bimtex
Start

Quickstart

From nothing to a full sheet set in about a minute.

Install

npm install bimtex

Zero runtime dependencies. Node 18+.

Write a model

One JSON object per line. Save as cabin.ndjson.

{"t":"project","id":"cabin","name":"Cabin","unit":"m"}
{"t":"footprint","id":"fp","x":0,"y":0,"w":7.2,"d":5.4}
{"t":"level","id":"g","name":"Ground","elev":0,"height":2.7}
{"t":"room","id":"main","label":"Living","use":"living","level":"g","x":0.3,"y":0.3,"w":6.6,"d":4.8}
{"t":"wall","id":"n","level":"g","side":"north","type":"exterior","thickness":0.3}
{"t":"wall","id":"s","level":"g","side":"south","type":"exterior","thickness":0.3}
{"t":"wall","id":"e","level":"g","side":"east","type":"exterior","thickness":0.3}
{"t":"wall","id":"w","level":"g","side":"west","type":"exterior","thickness":0.3}
{"t":"opening","id":"d1","on":"s","kind":"door","at":"center","width":0.9,"room":"main"}
{"t":"opening","id":"w1","on":"n","kind":"window","at":"center","width":1.8,"sill":0.9,"head":2.3}
{"t":"roof","id":"r","type":"gable","ridgeAxis":"x","pitch":0.45,"overhang":0.4}

Draw it

npx bimtex cabin.ndjson -o drawings/
A-101.svg
A-201.svg
A-301.svg
A-401.svg

Plans, four elevations, two sections and axonometrics — all derived, none authored. Add a parcel and you also get A-001; add a second room and you get the A-501 egress plan.

Or call it directly

import { compile, parseNdjson, drawPlan, drawElevation, validate } from 'bimtex';

const { entities } = parseNdjson(source);
const model = compile(entities, { detail: 1 });   // 0 MASSING · 1 STANDARD · 2 DETAILED

drawPlan(model, model.levels[0]);   // SVG string, cut at +1.20 m
drawElevation(model, 'south');      // the elevation with the door in it
validate(model);                    // { ok, diagnostics }

drawPlan returns an SVG fragment. There is no canvas, no headless browser and no 3D engine anywhere in the drawing path.

What you just made

Compiling drawing set…

The ridge height on that section was never typed. It is eave + (halfSpan + overhang) × pitch — derived, because a ridge that disagrees with its eaves is exactly the contradiction this format exists to prevent.

Next