Check meshes
Claim that a mesh is closed, sound and in one piece, and count the parts of an assembly mesh.
A mesh answers for its triangles: whether they close, whether any are degenerate or duplicated, and how many pieces they form. Mesh checks run on GLB files; neither loadModel nor load_model admits raw triangles.
1. Claim closure and integrity
const part = await loadModel({ source: 'involute-gear.glb', format: 'glb', sourceUnit: 'mm' });Tab indents. Press Escape, then Tab, to leave the editor.
The spec as a file, with the model loaded over the native addon
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: 'involute-gear.glb', format: 'glb', sourceUnit: 'mm' });expectGeo(part) .toBeWatertight();expectGeo(part) .toHaveMeshIntegrity({ finitePositions: true, degenerateTriangles: { count: 0 }, duplicateFaces: { count: 0 }, triangleCount: 624, });expectGeo(part) .toHaveNoDiagnostics();await loadModel.dispose();engine.close();involute-gear.glb · 52 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 gear is a GLB mesh of 624 triangles. Set triangleCount to 600 and the integrity claim fails with GEOSPEC_, stating 624 triangles, which does not satisfy 600. toBeWatertight reports the edges it checked, { "irregular, and toHaveNoDiagnostics reads what the loader reported at admission, errors and warnings by default, and measures no geometry.
The measurement is report. on the assertion expectGeo returns.
The measurement is report. on the report expect_geo returns.
Switch the model to the plate, a STEP part: closure passes from the exact BRep topology, and the integrity claim is refused with GEOSPEC_, because its fields would measure a tessellation the engine made; to is the one form STEP answers.
2. Count the pieces
to counts closed material shells, and shells closer than toleranceMm count as one. The gear is one piece, so { count: 2 } fails with GEOSPEC_; the count indexes every triangle, which keeps it out of the live demo. The script counts the two 10 mm cubes, 20 mm apart, of an engine fixture, packages/, copied next to it:
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 cubes = await loadModel({ source: 'assembly.glb', format: 'glb', sourceUnit: 'mm' });
const pieces = expectGeo(cubes).toHaveConnectedComponents({ count: 2 });
const merged = expectGeo(cubes).toHaveConnectedComponents({ count: 1, toleranceMm: 21 });
console.log(pieces.report?.status, merged.report?.status);
await loadModel.dispose();
engine.close();$ node check.ts
passed passedfrom geospec import GeoSpecEngine, expect_geo, load_model
with GeoSpecEngine(model_root="."):
cubes = load_model(file="assembly.glb", format="glb", source_unit="mm")
pieces = expect_geo(cubes).to_have_connected_components(count=2)
merged = expect_geo(cubes).to_have_connected_components(count=1, toleranceMm=21)
print(pieces.result["status"], merged.result["status"])$ python check.py
passed passedKeyword arguments keep the camelCase field names, as toleranceMm=21 does.
The second claim spans the 20 mm gap with toleranceMm: 21, so the cubes count as one. The tolerance states how close two shells may sit and still be separate parts; it is not a way to make a count pass. Set tolerances and budgets covers it.
Variations
An open mesh. Remove one triangle from a closed box mesh and the claims answer differently: closure fails with GEOSPEC_, The mesh has 3 irregular edges (3 open, 0 non-manifold), so it is not a closed manifold surface.; the component count is refused with GEOSPEC_ rather than guessed, because shells are counted over closed, consistently oriented triangles; the integrity claim fails on its triangle count. The open box still reports a volume, 5000 mm³ where the closed one has 6000, which is why closure comes before Check mass properties.
A STEP subject. to reads the exact BRep topology and passes on the plate with { count: 1 }.
The reference. The mesh reference lists every field of the four claims.