GeoSpec
engine 0.4.0
Matcher reference

Queries

Positive-only queries that report what the engine measured, without asserting an expected value.

Open Markdown

A query reports a measurement instead of comparing it with an expectation; a negated query is an invalid-claim error. passed carries the measurement in evidence; refused means the evidence the query reads is missing or cannot decide, under the common refusals and the codes below. A query asserts nothing, so it never fails.

client.query({ capability, subject, payload }) resolves to a GeoSpecCanonicalClaimReport; query_geo(subject, capability, payload) returns a GeoSpecAssertionReport whose passed means status passed. Neither throws for the three outcomes. client.query admits the subjects of createGeoSpecNativeModelLoader and rejects those of createModelLoader, so this page uses the native loader throughout. Payload keys keep the camelCase field names in both languages (maxRecords).

plate and gear are the site's demo models; the rest are repository fixtures: tessellated, a STEP box whose faces carry no surface; housingShaft, a housing and a shaft that share material; nist, a NIST AP242 test case with semantic PMI. Python adds y_up_plate, the demo plate admitted y-up through engine.ingest_subject; no loader in geospec@beta admits one. Refusal codes add to the common refusals.

check.ts
import { readFile } from 'node:fs/promises';
import { createGeoSpecAssertionClient } from 'geospec/assertion-client';
import { createGeoSpecNativeModelLoader } from 'geospec/runner/native';
import { Engine } from 'geospec-engine/node';

const engine = new Engine();
const loadModel = createGeoSpecNativeModelLoader({
  engine,
  readSource: async (source) => new Uint8Array(await readFile(source as string)),
});
const client = createGeoSpecAssertionClient({ engine });
const plate = await loadModel({ source: 'plate-with-through-bore.step', format: 'step' });
const gear = await loadModel({ source: 'involute-gear.glb', format: 'glb', sourceUnit: 'mm' });
const tessellated = await loadModel({
  source: 'packages/geospec-engine/native/occt/rust/tests/fixtures/read-profile/box-tessellated-only.step',
  format: 'step',
});
const housingShaft = await loadModel({
  source: 'packages/geospec-engine/native/occt/rust/tests/fixtures/component-interference/original.step',
  format: 'step',
});
const nist = await loadModel({
  source: 'packages/geospec-engine/fixtures/interop/nist-pmi/nist_ctc_05_asme1_ap242-e1.stp',
  format: 'stp',
});
check.py
from pathlib import Path

from geospec import GeoSpecEngine, GeoSpecRegex, load_model, query_geo

with GeoSpecEngine(model_root=".") as engine:
    plate = load_model(file="plate-with-through-bore.step", format="step")
    gear = load_model(file="involute-gear.glb", format="glb", source_unit="mm")
    tessellated = load_model(
        file="packages/geospec-engine/native/occt/rust/tests/fixtures/read-profile/box-tessellated-only.step",
        format="step",
    )
    housing_shaft = load_model(
        file="packages/geospec-engine/native/occt/rust/tests/fixtures/component-interference/original.step",
        format="step",
    )
    nist = load_model(
        file="packages/geospec-engine/fixtures/interop/nist-pmi/nist_ctc_05_asme1_ap242-e1.stp",
        format="stp",
    )
    y_up_plate = engine.ingest_subject(
        Path("plate-with-through-bore.step").read_bytes(),
        format="step", coordinate_system="y-up", source_unit="auto", output_unit="mm",
    )

analyzeBrep

Reports a STEP subject's exact BRep facts: bounds, validity, topology counts, mass properties and the face, hole, chamfer and fillet inventories.

Signature

client.query({ capability: 'analyzeBrep', subject: part }): Promise<GeoSpecCanonicalClaimReport>
query_geo(part, "analyzeBrep") -> GeoSpecAssertionReport

Fields

None.

Admitted geometry

  • STEP with exact BRep faces.
  • An incomplete hole or edge-treatment inventory is a warning in the evidence.

Refusal codes

  • GEOSPEC_BREP_EVIDENCE_UNAVAILABLE: the subject has no BRep, such as a mesh or glTF subject.

Examples

// passed: plate, evidence.brep lists 7 faces and a 10 mm through hole along z
await client.query({ capability: 'analyzeBrep', subject: plate });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its faces carry no surface
await client.query({ capability: 'analyzeBrep', subject: tessellated });
# passed: plate, evidence["brep"] lists 7 faces and a 10 mm through hole along z
query_geo(plate, "analyzeBrep")
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its faces carry no surface
query_geo(tessellated, "analyzeBrep")

analyzeMesh

Reports the mesh the engine measures: counts, per-triangle rows, quality findings, watertightness and bounds per primitive.

Signature

client.query({ capability: 'analyzeMesh', subject: part }): Promise<GeoSpecCanonicalClaimReport>
query_geo(part, "analyzeMesh") -> GeoSpecAssertionReport

Fields

None.

Admitted geometry

  • Mesh and glTF/GLB: the admitted triangles.
  • STEP: the engine's own tessellation, so a STEP subject whose faces carry no surface is admitted.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: the subject has no mesh record, such as a rational plate, or was admitted in a y-up frame.
  • GEOSPEC_ANALYSIS_LIMIT: the watertight analysis inside the report needs more working memory than maxMeshBytes allows.

Examples

// passed: gear, 1872 vertices, 624 triangles, watertight, signedVolume 911.53 mm³
await client.query({ capability: 'analyzeMesh', subject: gear });

The refused case is Python only: no loader in geospec@beta admits y_up_plate.

# passed: gear, 1872 vertices, 624 triangles, watertight, signedVolume 911.53 mm³
query_geo(gear, "analyzeMesh")
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): y_up_plate, a y-up subject answers minimumDistance only
query_geo(y_up_plate, "analyzeMesh")

inspectGeometry

Resolves selectors and reports each match: occurrences and bodies with bounds and centre, planes with normal and offset, axes with direction and radius.

Signature

client.query({ capability: 'inspectGeometry', subject: part, payload: { selectors, evidence? } }): Promise<GeoSpecCanonicalClaimReport>
query_geo(part, "inspectGeometry", {"selectors": [...], "evidence": [...]}) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
selectorsarray of selectorsrequiredNames, RegExp (GeoSpecRegex in Python) or catalog objects
evidencearray of 'bounds', 'facts', 'frames'noneRecorded in the claim; does not narrow the report

The selectors are those of toHaveSpatialRelationships; payload is typed unknown, so every catalog form compiles here.

Admitted geometry

  • STEP: occurrences, faces, axes and planes, from the exact BRep.
  • glTF/GLB: bodies, by { kind: 'body', of: 'cubeA#0' }.
  • One selector that cannot resolve refuses the whole query, and the report lists no matches.

Refusal codes

  • GEOSPEC_SELECTOR_UNSUPPORTED_EVIDENCE: a selector reads evidence the subject does not carry, such as a face, axis or plane selector on a glTF subject.
  • GEOSPEC_SELECTOR_UNMATCHED, GEOSPEC_SELECTOR_AMBIGUOUS: a selector resolves to no entity, or to more than its cardinality allows.

Examples

// passed: housingShaft, housing and shaft, each with bounds and centre
await client.query({
  capability: 'inspectGeometry',
  subject: housingShaft,
  payload: { selectors: [{ kind: 'occurrence', name: /.*/, expect: 'many' }] },
});
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its faces carry no surface
await client.query({ capability: 'inspectGeometry', subject: tessellated, payload: { selectors: ['box'] } });
# passed: housing_shaft, housing and shaft, each with bounds and centre
query_geo(housing_shaft, "inspectGeometry", {"selectors": [dict(kind="occurrence", name=GeoSpecRegex(".*"), expect="many")]})
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its faces carry no surface
query_geo(tessellated, "inspectGeometry", {"selectors": ["box"]})

analyzeMeshOverlap

Reports each component pair's shared volume, without comparing it with an allowance.

Signature

client.query({ capability: 'analyzeMeshOverlap', subject: part, payload: { pairs?, tolerance? } }): Promise<GeoSpecCanonicalClaimReport>
query_geo(part, "analyzeMeshOverlap", {"pairs": [...], "tolerance": ...}) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
pairsarray of { left, right }every pairPairs to measure
tolerancenumber, 0 or more, mm0.001Widens the candidate bounds check; a pair below its cube is omitted

The payload object is required: {} measures every pair. Components and selectors follow toHaveNoComponentInterference.

Admitted geometry

  • STEP: an exact Boolean intersection of each candidate pair's leaf solids; the evidence says it is not an allowance check.
  • glTF/GLB: a polyhedral Boolean observation with no error bound; an omitted pair is not shown to be zero.

Refusal codes

  • GEOSPEC_COMPONENT_PARTITION_INCONCLUSIVE: fewer than two components.
  • GEOSPEC_EXACT_COMPONENT_INVALID, GEOSPEC_MANIFOLD_COMPONENT_INVALID: a candidate component is not a valid closed solid, so its overlap cannot be measured.
  • GEOSPEC_SELECTOR_UNMATCHED: a pairs entry matches no pair of distinct components.
  • MATCHER_TIMEOUT: one pair's Boolean work exceeds the budget; the diagnostic names the pair and its cost, 210 units for housing and shaft.
  • GEOSPEC_ANALYSIS_LIMIT: the component partition or the overlap observation needs more than maxMeshBytes or maxMeshEntries allows.

Examples

// passed: housingShaft, intersectionVolume 380.81 mm³ for housing and shaft
await client.query({ capability: 'analyzeMeshOverlap', subject: housingShaft, payload: {} });
// refused (GEOSPEC_SELECTOR_UNMATCHED): housingShaft, no component is named nut
await client.query({
  capability: 'analyzeMeshOverlap',
  subject: housingShaft,
  payload: { pairs: [{ left: 'housing', right: 'nut' }] },
});
# passed: housing_shaft, intersectionVolume 380.81 mm³ for housing and shaft
query_geo(housing_shaft, "analyzeMeshOverlap", {})
# refused (GEOSPEC_SELECTOR_UNMATCHED): housing_shaft, no component is named nut
query_geo(housing_shaft, "analyzeMeshOverlap", {"pairs": [dict(left="housing", right="nut")]})

queryPmi

Reports a STEP file's product manufacturing information (PMI) records and the faces they refer to, as an inventory; it does not interpret or check tolerances.

Signature

client.query({ capability: 'queryPmi', subject: part, payload: { maxRecords?, maxOutputBytes? } }): Promise<GeoSpecCanonicalClaimReport>
query_geo(part, "queryPmi", {"maxRecords": ..., "maxOutputBytes": ...}) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
maxRecordsinteger, 1 to 1,0241024Record limit
maxOutputBytesinteger, 1 to 1,048,5761048576Byte limit

Admitted geometry

  • STEP whose source is within 8 MiB. A file with no PMI, such as the plate, answers passed with an empty inventory.
  • The query reads no face geometry, so a STEP subject whose faces carry no surface is admitted.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: the subject is not STEP; details.applicability.condition is retained-step-source.
  • GEOSPEC_ANALYSIS_LIMIT: the inventory exceeds maxRecords or maxOutputBytes, as the NIST case does with maxRecords: 1, and nothing truncated is returned; or the source exceeds 8 MiB, a capacity the inventory profile fixes, or the engine's maxMeshBytes. The message names the limit and what the query needed, and details carry limit and required.

Examples

// passed: nist, 94 records with inventory status 'semantic'
await client.query({ capability: 'queryPmi', subject: nist });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh carries no STEP source
await client.query({ capability: 'queryPmi', subject: gear });
// refused (GEOSPEC_ANALYSIS_LIMIT): nist with maxRecords 1, the inventory needs 2 records
await client.query({ capability: 'queryPmi', subject: nist, payload: { maxRecords: 1 } });
# passed: nist, 94 records with inventory status "semantic"
query_geo(nist, "queryPmi")
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): gear, a GLB mesh carries no STEP source
query_geo(gear, "queryPmi")
# refused (GEOSPEC_ANALYSIS_LIMIT): nist with maxRecords 1, the inventory needs 2 records
query_geo(nist, "queryPmi", {"maxRecords": 1})

minimumDistance

Reports the exact minimum distance between two occurrences of a STEP assembly, with a closest point on each.

Signature

client.query({ capability: 'minimumDistance', subject: part, payload: { pair: [{ occurrencePath }, { occurrencePath }] } }): Promise<MinimumDistanceResult>
query_geo(part, "minimumDistance", {"pair": [{"occurrencePath": ...}, {"occurrencePath": ...}]}) -> GeoSpecAssertionReport

Fields

FieldTypeDefaultMeaning
pairtwo { occurrencePath: string } itemsrequiredTwo distinct occurrence paths

Returns

type MinimumDistanceResult =
  | { status: 'complete'; fact: MinimumDistanceFact }
  | { status: 'refused'; code: 'unsupported-evidence' | 'invalid-selection' | 'work-limit'; message: string }
  | { status: 'interrupted'; code: 'cancelled' | 'executor-exited' | 'deadline' | 'engine-error'; message: string };
type MinimumDistanceFact = {
  distance: number;
  points: [[number, number, number], [number, number, number]];
  occurrences: [string, string];
  unit: 'mm';
  coordinateSystem: 'z-up';
  source: 'ap242';
  assurance: 'exact-brep';
  subjectHash: string;
  algorithmProfile: 'geospec-minimum-distance-v1';
};

The report: passed carries the same fact in result["evidence"]["fact"], and refused keeps the engine's code in diagnostics.

refused maps the engine's GEOSPEC_UNSUPPORTED_EVIDENCE to unsupported-evidence, GEOSPEC_INVALID_SELECTION to invalid-selection and MATCHER_TIMEOUT to work-limit; interrupted is a stop, not a verdict.

Admitted geometry

  • An AP242 STEP subject with exact BRep faces, every length unit in millimetres.
  • The one capability a y-up STEP subject answers; its points are reported z-up.

Refusal codes

  • GEOSPEC_UNSUPPORTED_EVIDENCE: the subject is not STEP, the file is not AP242, or a length unit is not millimetres.
  • GEOSPEC_INVALID_SELECTION: a path names no occurrence, or more than one.

Examples

// passed: housingShaft, fact.distance 0 with coincident points; housing and shaft share material
await client.query({
  capability: 'minimumDistance',
  subject: housingShaft,
  payload: { pair: [{ occurrencePath: 'housing' }, { occurrencePath: 'shaft' }] },
});
// refused (GEOSPEC_INVALID_SELECTION): housingShaft, no occurrence is named nut
await client.query({
  capability: 'minimumDistance',
  subject: housingShaft,
  payload: { pair: [{ occurrencePath: 'housing' }, { occurrencePath: 'nut' }] },
});
# passed: housing_shaft, fact distance 0 with coincident points; housing and shaft share material
query_geo(housing_shaft, "minimumDistance", {"pair": [{"occurrencePath": "housing"}, {"occurrencePath": "shaft"}]})
# refused (GEOSPEC_INVALID_SELECTION): housing_shaft, no occurrence is named nut
query_geo(housing_shaft, "minimumDistance", {"pair": [{"occurrencePath": "housing"}, {"occurrencePath": "nut"}]})

On this page