Soundness
Connected components, watertightness, mesh integrity and subject diagnostics.
These assertions check whether the geometry is sound enough for other measurements: how many pieces it has, whether it is closed, whether its mesh is fit to measure, and what its loader reported.
gear and box are the site's demo models; tessellated and gearbox are repository fixtures: a STEP box whose faces carry no surface, and a planetary gearbox exported as a GLB with four non-manifold edges. Python adds y_up_plate, the demo plate admitted in a y-up frame through engine.; no loader in geospec@beta admits one. Python matchers are snake_case; their keywords keep the camelCase field names (toleranceMm=). A failed or refused call throws Geo in TypeScript and raises it in Python. Refusal codes add to the common refusals.
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 gear = await loadModel({ source: 'involute-gear.glb', format: 'glb', sourceUnit: 'mm' });
const box = await loadModel({ source: 'box-10x20x30.step', format: 'step' });
const tessellated = await loadModel({
source: 'packages/geospec-engine/native/occt/rust/tests/fixtures/read-profile/box-tessellated-only.step',
format: 'step',
});
const gearbox = await loadModel({
source: 'packages/geospec-engine/bench/fixtures/performance-lab/generated/planetary-gearbox-cad.glb',
format: 'glb',
sourceUnit: 'mm',
});from pathlib import Path
from geospec import GeoSpecEngine, expect_geo, load_model
with GeoSpecEngine(model_root=".") as engine:
gear = load_model(file="involute-gear.glb", format="glb", source_unit="mm")
box = load_model(file="box-10x20x30.step", format="step")
tessellated = load_model(
file="packages/geospec-engine/native/occt/rust/tests/fixtures/read-profile/box-tessellated-only.step",
format="step",
)
gearbox = load_model(
file="packages/geospec-engine/bench/fixtures/performance-lab/generated/planetary-gearbox-cad.glb",
format="glb",
source_unit="mm",
)
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",
)to Have Connected Components
Asserts how many separate pieces of material the subject has.
Signature
expectGeo(part).toHaveConnectedComponents(expected: GeoSpecConnectedComponentsExpectation): GeoSpecAssertionexpect_geo(part).to_have_connected_components(count=..., toleranceMm=..., tolerance=...) -> GeoSpecAssertionReportFields
| Field | Type | Default | Meaning |
|---|---|---|---|
count | integer, 0 or more | required | Separate pieces of material |
toleranceMm | number, 0 or more | tolerance | Pieces closer than this count as one; wins when both are given |
tolerance | number, 0 or more | 0.02 | Fallback for toleranceMm |
Admitted geometry
- STEP: clusters of BRep bodies.
- Mesh and glTF/GLB: closed material shells. Every edge must be shared by exactly two oppositely directed triangles; an open or inconsistently oriented mesh refuses rather than being counted.
- Touching that only looks connected in a render is not connectivity, and does not justify a larger tolerance.
Refusal codes
GEOSPEC_: the mesh is not made of closed, consistently oriented shells.INVALID_ EVIDENCE GEOSPEC_: the exact material classification needs more scratch thanANALYSIS_ LIMIT maxMeshBytesallows. The reservation is sized from the mesh, anddetailsaddreservation,positionsandtrianglesbesidelimitandrequired.
A failed claim carries GEOSPEC_.
Examples
// passed: gear, one shell of 624 triangles
expectGeo(gear).toHaveConnectedComponents({ count: 1 });
// refused (GEOSPEC_INVALID_EVIDENCE): gearbox, four non-manifold edges
expectGeo(gearbox).toHaveConnectedComponents({ count: 1 });# passed: gear, one shell of 624 triangles
expect_geo(gear).to_have_connected_components(count=1)
# refused (GEOSPEC_INVALID_EVIDENCE): gearbox, four non-manifold edges
expect_geo(gearbox).to_have_connected_components(count=1)to Be Watertight
Asserts that the subject's surface is closed: every edge borders exactly two faces.
Signature
expectGeo(part).toBeWatertight(): GeoSpecAssertionexpect_geo(part).to_be_watertight() -> GeoSpecAssertionReportFields
None.
Admitted geometry
- STEP: edge incidence in the exact BRep topology. A BRep subject is never tessellated for this claim.
- Mesh and glTF/GLB: edge incidence in the triangles.
- The claim checks closure only. It does not establish manifold validity, fusion or material connectivity, and bounds establish none of these.
Refusal codes
GEOSPEC_: the watertight analysis needs more working memory thanANALYSIS_ LIMIT maxMeshBytesallows. A closed mesh needs about 200 bytes per triangle, so the 256 MiB entry limit decides meshes up to roughly 1.3 million triangles; raisemaxMeshBytesfor larger ones.
A failed claim carries GEOSPEC_, with the counts of open and non-manifold edges.
Examples
// passed: gear, 936 edges, each on two triangles
expectGeo(gear).toBeWatertight();
// failed (GEOSPEC_WATERTIGHT_MISMATCH): gearbox, 4 non-manifold edges, 0 open
expectGeo(gearbox).toBeWatertight();
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its six faces have no surface
expectGeo(tessellated).toBeWatertight();# passed: gear, 936 edges, each on two triangles
expect_geo(gear).to_be_watertight()
# failed (GEOSPEC_WATERTIGHT_MISMATCH): gearbox, 4 non-manifold edges, 0 open
expect_geo(gearbox).to_be_watertight()
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): tessellated, its six faces have no surface
expect_geo(tessellated).to_be_watertight()to Have Mesh Integrity
Asserts that the mesh evidence is sound enough for the checks that follow it.
Signature
expectGeo(part).toHaveMeshIntegrity(expected: GeoSpecMeshIntegrityExpectation): GeoSpecAssertionexpect_geo(part).to_have_mesh_integrity(finitePositions=..., triangleCount=...) -> GeoSpecAssertionReportFields
| Field | Type | Default | Meaning |
|---|---|---|---|
finitePositions | boolean | not checked | Every vertex coordinate is finite |
degenerateTriangles | { count?, maxCount?, areaTolerance? } | not checked | Zero-area triangles, or at most areaTolerance |
duplicateFaces | { count?, maxCount? } | not checked | Triangles that repeat another's vertices |
watertight | boolean | not checked | Closed edge incidence, as toBeWatertight |
triangleCount | numeric expectation | not checked | Triangles in the mesh |
Admitted geometry
- Mesh and glTF/GLB: every field.
- STEP:
watertightonly, answered from the exact BRep topology. The other fields would measure a tessellation GeoSpec made, not the subject, so a STEP claim that declares them refuses.
Refusal codes
GEOSPEC_: a STEP claim declares a field other thanUNSUPPORTED_ EVIDENCE watertight.GEOSPEC_: a declaredANALYSIS_ LIMIT watertightneeds more working memory thanmaxMeshBytesallows, as fortoBeWatertight.
A failed claim carries GEOSPEC_.
Examples
// passed: gear, 624 finite triangles and no duplicates
expectGeo(gear).toHaveMeshIntegrity({ finitePositions: true, duplicateFaces: { count: 0 }, triangleCount: 624 });
// refused (GEOSPEC_UNSUPPORTED_EVIDENCE): box, a STEP subject answers watertight only
expectGeo(box).toHaveMeshIntegrity({ finitePositions: true });# passed: gear, 624 finite triangles and no duplicates
expect_geo(gear).to_have_mesh_integrity(finitePositions=True, duplicateFaces={"count": 0}, triangleCount=624)
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): box, a STEP subject answers watertight only
expect_geo(box).to_have_mesh_integrity(finitePositions=True)to Have No Diagnostics
Asserts that the subject was admitted without diagnostics at the rejected severities.
Signature
expectGeo(part).toHaveNoDiagnostics(expected?: GeoSpecNoDiagnosticsExpectation): GeoSpecAssertionexpect_geo(part).to_have_no_diagnostics(severities=...) -> GeoSpecAssertionReportFields
| Field | Type | Default | Meaning |
|---|---|---|---|
severities | array of 'error', 'warning', 'info' | ['error', 'warning'] | Severities that fail the claim |
Admitted geometry
- Any admitted subject in a z-up frame. The claim reads the diagnostics the host attached when it ingested the subject; it measures no geometry, so a STEP box whose faces carry no surface still answers.
Refusal codes
The common refusals only. A failed claim carries GEOSPEC_, listing each rejected diagnostic.
Examples
// passed: gear, admitted without diagnostics
expectGeo(gear).toHaveNoDiagnostics();The refused case is Python only: no loader in geospec@beta admits y_up_plate.
# passed: gear, admitted without diagnostics
expect_geo(gear).to_have_no_diagnostics()
# refused (GEOSPEC_UNSUPPORTED_EVIDENCE): y_up_plate, a y-up subject answers minimumDistance only
expect_geo(y_up_plate).to_have_no_diagnostics()