GeoSpec
engine 0.4.0
Matcher reference

Features

Planar and cylindrical faces, holes, hole patterns, chamfers, fillets, wall thickness and void continuity.

Open Markdown

These assertions find manufacturing features in a STEP file's exact BRep. A mesh has no surface types, so each refuses on a mesh subject. Every feature claim refuses when the engine cannot classify every face it would need: an unclassified face could be the missing feature, so a pass or a failure would be a guess.

plate, box and gear are the site's demo models; coupling, chamferBlock, filletBlock and guide are repository fixtures: six 5 mm holes on a 45 mm bolt circle, one 1 mm planar chamfer, one 2 mm fillet, and a valve guide with a straight bore along z in its guide occurrence. Python matchers are snake_case; their keywords keep the camelCase field names (holeDiameter=). A failed or refused call throws GeoSpecAssertionError in TypeScript and raises it in Python. Refusal codes add to the common refusals.

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 plate = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });
const box = await loadModel({ source: 'box-10x20x30.step', format: 'step' });
const gear = await loadModel({ source: 'involute-gear.glb', format: 'glb', sourceUnit: 'mm' });
const coupling = await loadModel({
  source: 'packages/geospec-engine/native/occt/rust/tests/fixtures/kernel-skill-acceptance/build123d-coupling.step',
  format: 'step',
});
const chamferBlock = await loadModel({
  source: 'packages/geospec-engine/native/occt/rust/tests/fixtures/edge-treatments/01-planar-chamfer.step',
  format: 'step',
});
const filletBlock = await loadModel({
  source: 'packages/geospec-engine/native/occt/rust/tests/fixtures/edge-treatments/02-cylindrical-fillet.step',
  format: 'step',
});
const guide = await loadModel({
  source: 'packages/geospec-engine/native/occt/rust/tests/fixtures/nominal-bore-void/guide.step',
  format: 'step',
});
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")
    box = load_model(file="box-10x20x30.step", format="step")
    gear = load_model(file="involute-gear.glb", format="glb", source_unit="mm")
    coupling = load_model(
        file="packages/geospec-engine/native/occt/rust/tests/fixtures/kernel-skill-acceptance/build123d-coupling.step",
        format="step",
    )
    chamfer_block = load_model(
        file="packages/geospec-engine/native/occt/rust/tests/fixtures/edge-treatments/01-planar-chamfer.step",
        format="step",
    )
    fillet_block = load_model(
        file="packages/geospec-engine/native/occt/rust/tests/fixtures/edge-treatments/02-cylindrical-fillet.step",
        format="step",
    )
    guide = load_model(
        file="packages/geospec-engine/native/occt/rust/tests/fixtures/nominal-bore-void/guide.step",
        format="step",
    )

toHavePlanarFace

Asserts that at least one planar face lies on the declared plane.

Signature

expectGeo(part).toHavePlanarFace(expected: GeoSpecPlanarFaceExpectation): GeoSpecAssertion
expect_geo(part).to_have_planar_face(normal=..., offset=..., area=..., tolerance=...) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
normalpointrequiredPlane normal
offsetnumber, mmrequiredSigned distance from the origin along normal
areanumeric expectation, mm²not checkedFace area
tolerancenumber, 0 or more0.02Error per normal component, offset and area

The plane is the set of points p with normal · p = offset. A face whose outward normal points the other way matches when both the normal and the offset flip.

Admitted geometry

  • STEP with exact BRep faces whose surface is a plane.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: the subject carries no BRep.

A failed claim carries GEOSPEC_FEATURE_MISMATCH, listing every planar face it found.

Examples

// passed: plate, its top face is z = 20
expectGeo(plate).toHavePlanarFace({ normal: [0, 0, 1], offset: 20 });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expectGeo(gear).toHavePlanarFace({ normal: [0, 0, 1], offset: 5 });
# passed: plate, its top face is z = 20
expect_geo(plate).to_have_planar_face(normal=[0, 0, 1], offset=20)
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expect_geo(gear).to_have_planar_face(normal=[0, 0, 1], offset=5)

toHaveCylindricalFace

Asserts that at least one cylindrical face has the declared radius along the declared axis.

Signature

expectGeo(part).toHaveCylindricalFace(expected: GeoSpecCylindricalFaceExpectation): GeoSpecAssertion
expect_geo(part).to_have_cylindrical_face(radius=..., axis=..., tolerance=...) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
radiusnumber, 0 or more, mmrequiredFace radius
axis'x', 'y' or 'z'requiredPrincipal axis the cylinder axis points most along
tolerancenumber, 0 or more0.02Error on radius

Admitted geometry

  • STEP with exact BRep faces whose surface is a cylinder. The face can be a boss or a bore; the claim does not tell them apart.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: the subject carries no BRep.

A failed claim carries GEOSPEC_FEATURE_MISMATCH.

Examples

// passed: plate, the bore's face has radius 5 along z
expectGeo(plate).toHaveCylindricalFace({ radius: 5, axis: 'z' });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expectGeo(gear).toHaveCylindricalFace({ radius: 2, axis: 'z' });
# passed: plate, the bore's face has radius 5 along z
expect_geo(plate).to_have_cylindrical_face(radius=5, axis="z")
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expect_geo(gear).to_have_cylindrical_face(radius=2, axis="z")

toHaveCircularHole

Asserts that the part has a circular hole of the declared diameter.

Signature

expectGeo(part).toHaveCircularHole(expected: GeoSpecCircularHoleExpectation): GeoSpecAssertion
expect_geo(part).to_have_circular_hole(diameter=..., through=..., axis=..., center=..., tolerance=...) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
diameternumber, 0 or more, mmrequiredBore diameter
throughbooleannot checkedtrue when both ends open onto outside faces
axis'x', 'y' or 'z'not checkedBore axis
centerpointnot checkedCentre of the bore's cylindrical face
tolerancenumber, 0 or more0.02Error on diameter and center

Admitted geometry

  • STEP with exact BRep faces. A hole is a cylindrical bore the engine qualifies from the solid's topology: material all round it, and each end a mouth, a floor or, opposite a mouth, a counterbore shoulder. A chamfered, countersunk or rounded rim still opens the bore, and its cone or torus counts as part of that mouth. The bore may be one seamed cylinder or a band of coaxial partial cylinders that closes the full turn, which is what a sketched circle drawn as two arcs leaves. It measures diameter, axis, centre and ends, not manufacturability.
  • A cross-drilled hole may pierce a bore's wall between its rims, and the bore still counts. Where a cross hole leaves a pin or a hole leaves a slope, its rim is an oblique mouth. A curved floor or a tangent hole stays undecided. An outer chamfer is an exterior cone, and a rounded corner, slot end or flute an open arc, never part of a hole.
  • One qualified matching hole passes. A failure needs every bore candidate qualified: an unqualified bore could be the hole, so the claim refuses.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: the subject carries no BRep, or no qualified hole matches and at least one bore candidate is unqualified.

A failed claim carries GEOSPEC_FEATURE_MISMATCH: the plate fails diameter: 12.

Examples

// passed: plate, a 10 mm through bore along z
expectGeo(plate).toHaveCircularHole({ diameter: 10, through: true, axis: 'z' });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expectGeo(gear).toHaveCircularHole({ diameter: 4 });
# passed: plate, a 10 mm through bore along z
expect_geo(plate).to_have_circular_hole(diameter=10, through=True, axis="z")
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expect_geo(gear).to_have_circular_hole(diameter=4)

toHaveCircularHolePattern

Asserts that the part has a circular pattern of equal holes.

Signature

expectGeo(part).toHaveCircularHolePattern(expected: GeoSpecCircularHolePatternExpectation): GeoSpecAssertion
expect_geo(part).to_have_circular_hole_pattern(count=..., holeDiameter=..., boltCircleDiameter=..., axis=..., center=..., tolerance=...) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
countinteger, 0 or morerequiredHoles in the pattern, matched exactly
holeDiameternumber, 0 or more, mmrequiredDiameter of every hole
boltCircleDiameternumber, 0 or more, mmnot checkedBolt circle diameter
axis'x', 'y' or 'z'not checkedCommon axis
centerpointnot checkedBolt circle centre
tolerancenumber, 0 or more0.02Error on the diameters and the centre

A pattern is two or more qualified holes with the same axis, diameter and ends. Holes more than 20 mm apart along the axis start a new pattern.

Admitted geometry

  • STEP with exact BRep faces, as for toHaveCircularHole.
  • A pattern needs the complete hole inventory, so any unqualified bore candidate refuses the claim.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: the subject carries no BRep, or at least one bore candidate is unqualified.

A failed claim carries GEOSPEC_FEATURE_MISMATCH.

Examples

// passed: coupling, six 5 mm holes on a 45 mm bolt circle along z
expectGeo(coupling).toHaveCircularHolePattern({ count: 6, holeDiameter: 5, boltCircleDiameter: 45, axis: 'z' });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expectGeo(gear).toHaveCircularHolePattern({ count: 6, holeDiameter: 5 });
# passed: coupling, six 5 mm holes on a 45 mm bolt circle along z
expect_geo(coupling).to_have_circular_hole_pattern(count=6, holeDiameter=5, boltCircleDiameter=45, axis="z")
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expect_geo(gear).to_have_circular_hole_pattern(count=6, holeDiameter=5)

toHaveChamferFeature

Asserts that the part has a chamfer of the declared distance.

Signature

expectGeo(part).toHaveChamferFeature(expected: GeoSpecChamferFeatureExpectation): GeoSpecAssertion
expect_geo(part).to_have_chamfer_feature(distance=..., selection=..., tolerance=...) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
distancenumber, 0 or more, mmrequiredChamfer setback
selectionstringany chamferThe unique label the STEP source gives the chamfer
tolerancenumber, 0 or more0.02Error on distance

Admitted geometry

  • STEP with exact BRep faces. A chamfer is a planar or conical face the engine qualifies between two adjacent faces, with a measured setback.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: the subject carries no BRep; no qualified chamfer matches and a face's membership is unknown; or the matching chamfer has no unique label for selection.

A failed claim carries GEOSPEC_FEATURE_MISMATCH. Its details.mismatchKind separates a wrong label (selectorMismatch) from a wrong distance (geometricMismatch).

Examples

// passed: chamferBlock, one 1 mm planar chamfer
expectGeo(chamferBlock).toHaveChamferFeature({ distance: 1 });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expectGeo(gear).toHaveChamferFeature({ distance: 1 });
# passed: chamfer_block, one 1 mm planar chamfer
expect_geo(chamfer_block).to_have_chamfer_feature(distance=1)
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expect_geo(gear).to_have_chamfer_feature(distance=1)

toHaveFilletFeature

Asserts that the part has a fillet of the declared radius.

Signature

expectGeo(part).toHaveFilletFeature(expected: GeoSpecFilletFeatureExpectation): GeoSpecAssertion
expect_geo(part).to_have_fillet_feature(radius=..., selection=..., tolerance=...) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
radiusnumber, 0 or more, mmrequiredFillet radius
selectionstringany filletThe unique label the STEP source gives the fillet
tolerancenumber, 0 or more0.02Error on radius

Admitted geometry

  • STEP with exact BRep faces. A fillet is a cylindrical or toroidal face the engine qualifies as tangent to its two neighbours.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: as for toHaveChamferFeature.

A failed claim carries GEOSPEC_FEATURE_MISMATCH.

Examples

// passed: filletBlock, one 2 mm fillet
expectGeo(filletBlock).toHaveFilletFeature({ radius: 2 });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expectGeo(gear).toHaveFilletFeature({ radius: 2 });
# passed: fillet_block, one 2 mm fillet
expect_geo(fillet_block).to_have_fillet_feature(radius=2)
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh
expect_geo(gear).to_have_fillet_feature(radius=2)

toHaveMinimumWallThickness

Asserts the thinnest wall of the part.

Signature

expectGeo(part).toHaveMinimumWallThickness(expected: GeoSpecMinimumWallThicknessExpectation): GeoSpecAssertion
expect_geo(part).to_have_minimum_wall_thickness(value=..., tolerance=...) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
valuenumeric expectation, mmrequiredThinnest wall; { greaterThanOrEqual: 2 } states a lower limit
tolerancenumber, 0 or more0.02Error on value

Admitted geometry

  • A STEP part whose whole shape is a complete axis-aligned box or a right circular cylinder. The thickness is the exact minimum over the whole part, not a sample.
  • Any other shape refuses, the bored plate included. The engine does not estimate wall thickness from samples or a mesh.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: the subject carries no BRep, or its shape is outside the qualified box and cylinder domains.

A failed claim carries GEOSPEC_FEATURE_MISMATCH.

Examples

// passed: box, 10 × 20 × 30 mm, thinnest across x
expectGeo(box).toHaveMinimumWallThickness({ value: 10 });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): plate, a bored plate is outside the box and cylinder domains
expectGeo(plate).toHaveMinimumWallThickness({ value: { greaterThanOrEqual: 2 } });
# passed: box, 10 × 20 × 30 mm, thinnest across x
expect_geo(box).to_have_minimum_wall_thickness(value=10)
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): plate, a bored plate is outside the box and cylinder domains
expect_geo(plate).to_have_minimum_wall_thickness(value={"greaterThanOrEqual": 2})

toHaveVoidContinuity

Asserts that an open channel runs through the material along a declared path, such as a bore, duct or passage.

Signature

expectGeo(part).toHaveVoidContinuity(expected: GeoSpecVoidContinuityExpectation): GeoSpecAssertion
expect_geo(part).to_have_void_continuity(path=..., material=..., bounds=..., minCrossSection=..., isolatedFrom=...) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
patharray of points or { occurrence }requiredOrdered waypoints in the void; { occurrence } means the centre of its bounds
materialarray of occurrence namesrequired unless boundsOccurrences whose solids bound the void
bounds{ min, max }required unless materialRegion decided in; without it, the material bounds padded by 2 mm
minCrossSectionnumber, 0 or more, mm²not checkedSmallest cross-section along the path
isolatedFromarray of points[]Points the void must not reach

Every point must lie inside the region. The published types declare defaults for material and bounds; the engine refuses a claim with neither. The path's shape picks the route:

  • Points and bounds: the engine follows the void through a complete box or right circular cylinder. Elsewhere, each straight segment must clear the material: grazing or isolatedFrom refuses, and a point in material fails.
  • Points without bounds: the path must run along one straight bore in exactly one material occurrence, with 2 to 16 points and no isolatedFrom. This route passes or refuses; it never fails.
  • Any { occurrence } waypoint: the engine decides the path on an exact solid model of the material. minCrossSection refuses here, because the sections are sampled.

Admitted geometry

  • STEP with exact BRep faces and the named material occurrences.
  • A path the engine cannot decide refuses. An unresolved section does not show a blocked channel.

Refusal codes

  • GEOSPEC_VOID_CONTINUITY_UNSUPPORTED: neither material nor bounds, an absent occurrence, a point outside the region, or a path outside the route's limits.
  • GEOSPEC_UNSUPPORTED_EVIDENCE: minCrossSection on the occurrence route, the subject carries no BRep, a material is not a complete solid, or the occurrence route has no exact solid model.
  • GEOSPEC_ANALYSIS_LIMIT: the arrangement needs more than a capacity the proof profile fixes, such as 16 distinct materials, or a cell, record or exact-arithmetic capacity. details carry limit and required, and no host limit raises them.

A failed claim carries GEOSPEC_VOID_CONTINUITY_MISMATCH.

Examples

// passed: guide, a straight bore along z through the 'guide' occurrence
expectGeo(guide).toHaveVoidContinuity({
  material: ['guide'],
  path: [
    [0, 0, 3],
    [0, 0, 42],
  ],
});
// refused (GEOSPEC_VOID_CONTINUITY_UNSUPPORTED): gear, a GLB mesh has no 'guide' occurrence
expectGeo(gear).toHaveVoidContinuity({
  material: ['guide'],
  path: [
    [0, 0, 3],
    [0, 0, 42],
  ],
});
# passed: guide, a straight bore along z through the 'guide' occurrence
expect_geo(guide).to_have_void_continuity(material=["guide"], path=[[0, 0, 3], [0, 0, 42]])
# refused (GEOSPEC_VOID_CONTINUITY_UNSUPPORTED): gear, a GLB mesh has no 'guide' occurrence
expect_geo(gear).to_have_void_continuity(material=["guide"], path=[[0, 0, 3], [0, 0, 42]])

On this page