GeoSpec
engine 0.4.0

Write a spec from a drawing

Map each callout on a part drawing to a matcher, take the tolerance from the tolerance block and run the spec against the export.

Open Markdown

A drawing says what a part must be. A spec says the same as claims the engine can decide: one callout, one matcher, one expectation. The claims come from the drawing; the measurements come from the model.

Map the callouts

CalloutClaim
Overall 40 × 40 × 20toHaveBoundingBox({ size: { x: 40, y: 40, z: 20 } })
Ø10 on centre, along ztoHaveCircularHole({ diameter: 10, axis: 'z', center: { x: 0, y: 0 } })
6 × Ø5 on a Ø45 bolt circletoHaveCircularHolePattern({ count: 6, holeDiameter: 5, boltCircleDiameter: 45, axis: 'z' })
1 × 45° chamfertoHaveChamferFeature({ distance: 1 })
Fillet radius 2toHaveFilletFeature({ radius: 2 })
Wall 2 mintoHaveMinimumWallThickness({ value: { greaterThanOrEqual: 2 } })
VolumetoHaveVolume({ value: 30429.2 })
Mass, aluminium 2.7 g/cm³toHaveMass({ value: 82.16, density: 0.0027 })

Python spells each matcher in snake case with the same keys as keyword arguments: to_have_circular_hole_pattern(count=6, holeDiameter=5, boltCircleDiameter=45, axis="z"). Every feature row needs a STEP export; three rows need care:

  • Holes. toHaveCircularHole decides diameter, axis, centre and through; it has no depth field, so a depth callout stays a requirement the spec reports as unsupported.
  • Wall thickness. The engine measures the exact minimum of a complete box or a right cylinder only (admitted geometry); a bored plate is neither, so the claim refuses with GEOSPEC_UNSUPPORTED_EVIDENCE. Keep the callout and report it as unsupported.
  • Mass. density in g/mm³ gives grams. Without it the claim refuses on every subject.

Take the tolerance from the drawing

A tolerance block of ±0.1 becomes tolerance: 0.1 on the claims it covers; a claim without one is held to 0.02 mm (Set tolerances and budgets). A limit such as "2 min" is a strict comparison, greaterThanOrEqual, with no tolerance. The tolerance never moves to make a claim pass: a failure at the drawing's tolerance is a model that misses the drawing.

The plate

The drawing in words: a 40 × 40 × 20 mm aluminium plate, origin at the centre of its bottom face, Ø10 bore on centre along z. Its volume is 40 × 40 × 20 less π × 5² × 20, or 30429.2 mm³; at 0.0027 g/mm³ that is 82.16 g, centre of mass [0, 0, 10].

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
check.ts
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 } });expectGeo(part)  .toHaveCircularHole({ diameter: 10, axis: 'z', center: { x: 0, y: 0 } });expectGeo(part)  .toHaveVolume({ value: 30429.2 });expectGeo(part)  .toHaveMass({ value: 82.16, density: 0.0027 });expectGeo(part)  .toHaveCenterOfMass({ point: [0, 0, 10] });await loadModel.dispose();engine.close();

The engine loads in this tab when you run the spec.

The model’s bounds (solid); this claim pins no axis the view can draw.

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.

 

All five pass. Set x to 41, or diameter to 12, and that claim fails with the measured value in its diagnostic; the drawing's number then comes from the model.

As a file, give each callout its own test: a failed or refused claim ends the test it is in, so one test per callout keeps every verdict visible. GeoSpec for agents runs a spec from a shell.

Variations

A mesh export. On a GLB of the part the size, volume, mass and centre rows still answer; the bore row refuses with GEOSPEC_UNSUPPORTED_EVIDENCE. Read a refusal says what to change.

A range on the drawing. "30400 to 30500 mm³" is toHaveVolume({ value: { greaterThan: 30400, lessThan: 30500 } }), in Python to_have_volume(value={"greaterThan": 30400, "lessThan": 30500}); the comparisons are strict and carry no tolerance.

A part with no drawing. Query analyzeBrep to list the holes, patterns, chamfers and fillets the model carries, then write claims from the requirement, not the inventory. A claim copied from the measurement passes on every model, including a wrong one.

On this page