GeoSpec
engine 0.4.0

Find holes and features

Claim holes, faces, edge treatments and wall thickness from the exact BRep of a STEP part.

Open Markdown

Feature claims read the exact BRep of a STEP file: the faces, their surfaces and how they meet. A mesh carries none of that, so every claim on this page is refused on a mesh subject rather than guessed; Read a refusal covers what a refusal asks for.

1. Find the bore

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)  .toHaveCircularHole({ diameter: 10, axis: 'z' });expectGeo(part)  .toHavePlanarFace({ normal: [0, 0, 1], offset: 20 });expectGeo(part)  .toHaveCylindricalFace({ radius: 5, axis: 'z' });await loadModel.dispose();engine.close();

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

Model
This claim looks for cylindrical faces; the display mesh has none to mark.

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.

 

The plate has one 10 mm hole through its centre along z. Set diameter to 12 and the hole claim fails, listing the hole it did find. Switch the model to the gear, a GLB mesh with a 4 mm centre hole, and all three are refused with GEOSPEC_UNSUPPORTED_EVIDENCE: the mesh has no faces to classify, so neither a pass nor a failure would be an answer.

2. Read a hole result

A failed hole claim carries GEOSPEC_FEATURE_MISMATCH, and the diagnostic's details list the holes the engine qualified:

{
  "circularHoles": [
    {
      "axis": "z",
      "axisRange": { "max": 20, "min": 0 },
      "center": [3.2862601528904634e-13, 5.731384959986079e-17, 10],
      "diameter": 10,
      "through": true
    }
  ],
  "expected": { "diameter": 12, "tolerance": 0.02 },
  "matcher": "toHaveCircularHole"
}

diameter is measured within the tolerance; axis, center and through narrow which hole may match. through compares the engine's reading of the bore's two ends, each a mouth or a floor, so through: false fails on the plate. A bore the engine cannot qualify refuses the claim with GEOSPEC_UNSUPPORTED_EVIDENCE when no qualified hole matches, because the unqualified bore could be the one.

3. Claim a pattern, a chamfer and a fillet

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)  .toHaveCircularHolePattern({ count: 2, holeDiameter: 10 });expectGeo(part)  .toHaveChamferFeature({ distance: 1 });expectGeo(part)  .toHaveFilletFeature({ radius: 1 });await loadModel.dispose();engine.close();

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

Select a claim to see what it checks.

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.

 

The plate has one hole and sharp edges, so each claim fails with GEOSPEC_FEATURE_MISMATCH and its message says what it looked for: No circular-hole pattern has 2 holes of diameter 10 within 0.02 mm. A pattern needs two or more qualified holes with the same axis and diameter, count must match exactly, and boltCircleDiameter checks the spacing. The chamfer and fillet failures carry mismatchKind: 'geometricMismatch'; selection names one feature by the label its STEP source gives it, and a face whose membership the engine cannot settle refuses with GEOSPEC_UNSUPPORTED_EVIDENCE.

4. Claim wall thickness

const part = await loadModel({ source: 'box-10x20x30.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: 'box-10x20x30.step', format: 'step' });expectGeo(part)  .toHaveMinimumWallThickness({ value: { greaterThanOrEqual: 2 } });await loadModel.dispose();engine.close();

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

Model
The whole body: this claim measures all of it.

box-10x20x30.step · 17 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.

 

The engine computes the exact minimum over the whole part for a complete box or a right circular cylinder only; it does not estimate thickness from samples or from a mesh. The 10 × 20 × 30 mm box is 10 mm thin across x: set greaterThanOrEqual to 12 and it fails with The minimum wall thickness is 10 mm, which does not satisfy >= 12. The plate, with its bore, is outside that domain and is refused with GEOSPEC_UNSUPPORTED_EVIDENCE: The subject is outside the qualified continuous wall-thickness domains.

Variations

A planar face with an area. Add area: 1521.46 to the top-face claim: the plate's top is 40 × 40 mm less the bore's 78.54 mm². A face whose normal points the other way matches when both the normal and the offset flip: normal: [0, 0, -1], offset: -20 passes too.

Boss or bore. toHaveCylindricalFace matches any cylindrical face of the radius on the axis; it does not tell a boss from a bore. Claim the hole when the hole is what you mean.

The inventory without a claim. analyzeBrep reports every hole, face, chamfer and fillet the engine found in one query; Query geometry shows it on the plate. The features reference lists every field and refusal code.

On this page