Quick start
Install geospec, claim the size of a STEP part and read passed, failed or refused in TypeScript or Python.
Install, claim the size of one STEP part and read the verdict: in the page, then as a script.
The API is the geospec package, published on the beta tag, over the geospec-engine package. Node 24 or later.
npm install geospec@beta geospec-engineOne wheel installs the geospec package and the engine. CPython 3.13 or 3.14.
pip install geospec-engineInstall covers what each package delivers, browsers and bundlers.
Claim the size of a plate
The plate is a 40 × 40 × 20 mm STEP part with a 10 mm bore. toHaveBoundingBox compares the measured bounds with the size you state, at a tolerance of 0.02 mm. Change z: 20 to z: 25 and the claim fails with the measured size in its diagnostic.
Python spells each matcher in snake case: to_have_bounding_box.
const part = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });Tab indents. Press Escape, then Tab, to leave the editor.
The spec as a file, with the model loaded over the native addon
import { readFile } from 'node:fs/promises';import { expectGeo } from 'geospec';import { createModelLoader } from 'geospec/model';import { Engine } from 'geospec-engine/node';const engine = new Engine();const loadModel = createModelLoader({ engine, readSource: async (source) => new Uint8Array(await readFile(source as string)) });const part = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });expectGeo(part) .toHaveBoundingBox({ size: { x: 40, y: 40, z: 20 } });await loadModel.dispose();engine.close();plate-with-through-bore.step · 19 KB · copied from an engine test fixture
Run the spec to see what the engine answers for each claim. Then change an expected value and run it again.
Run it as a script
The same claim over the WASM build, which runs on every Node host. Save the script next to the plate and run node check.ts.
import { readFile } from 'node:fs/promises';
import { expectGeo } from 'geospec';
import { createModelLoader } from 'geospec/model';
import { Engine, initialize } from 'geospec-engine';
await initialize();
const engine = new Engine();
const loadModel = createModelLoader({
engine,
readSource: async (source) => new Uint8Array(await readFile(source as string)),
});
const plate = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });
const size = expectGeo(plate).toHaveBoundingBox({ size: { x: 40, y: 40, z: 20 } });
console.log(size.report?.status);
await loadModel.dispose();
engine.close();Save the script next to the plate and run python check.py.
from geospec import GeoSpecEngine, expect_geo, load_model
with GeoSpecEngine(model_root="."):
plate = load_model(file="plate-with-through-bore.step", format="step")
size = expect_geo(plate).to_have_bounding_box(size={"x": 40, "y": 40, "z": 20})
print(size.result["status"])Expected output
passedA matcher returns an assertion with passed, report.status and report., here size: [40, 40, 20]. A failed or refused claim throws Geo from geospec/: its message is the diagnostic and diagnostics[0].code the code.
expect_geo returns a report with passed, result["status"] and result[, here size: [40, 40, 20]. A failed or refused claim raises Geo: its message is the diagnostic and .code the code.
A missed size reads GEOSPEC_; a refusal names the evidence the subject lacks. Read a refusal tells the two apart.
What it does not do
- Meshes carry no feature evidence. A GLB or glTF subject refuses hole, face, chamfer and fillet claims with
GEOSPEC_; load a STEP export, as Find holes and features does.UNSUPPORTED_ EVIDENCE - No unit or frame inference. A STEP file is read in its own unit. A GLB is metres unless the load says millimetres (
sourceUnit: 'mm', in Pythonsource_unit="mm"). Measurements are millimetres. How it works has what each format carries. - No rendering or repair. The engine measures the geometry it is given and changes nothing.