Squared centroid magnitude cx² + cy² + cz² from the AABB. Cheap.
Per-vertex-slot remap: canonical[v] === v iff v is the
first occurrence of its byte signature; otherwise points at
the canonical slot to coalesce onto. Returns null for
geometries without positionsCompressed. Used by
geometryQuality's duplicate-vertex check and the
mergeDuplicateVertices fix.
Sorted-vertex-triple keys for triangle-dedup, plus the count
of duplicate (already-seen) triangles encountered during the
walk and a per-triangle "kept" bitmap. Each key is a_b_c
with a ≤ b ≤ c, so all rotations / reflections of the same
triangle collapse to one key. Degenerate triangles
(i0 === i1 || i1 === i2 || i0 === i2) are kept (they're a
separate concern). Returns null for non-triangle geometries.
keys — set of every distinct canonical key seen.duplicateCount — count of triangles whose canonical key
was already in keys at walk time. Read by
geometryQuality's duplicate-triangles check.kept — Uint8Array(triCount) with kept[t] === 1 iff
t was the first occurrence of its canonical key (or is
degenerate). Read by the dropDuplicateTriangles fix to
know which triangles to keep.FNV-1a fingerprint over typed-array bytes + primitive constant
contentHash have
identical content (modulo birthday-paradox collisions, which
are negligible for ≪ 10⁶ geometries). Used by
duplicateGeometries.Dequantized vertex positions (3 floats per vertex), produced
by decompressPositions3WithAABB3. The heaviest column —
roughly 3 × vertCount × 4 bytes per geometry — gated behind
lazy access so callers that don't need it don't pay. Used by
similarGeometries, geometryQuality's degenerate
triangle check, and the recenter / similar-merge fixes.
Directed edge → incidence count. Key is a_b. Manifold
meshes have each shared edge traversed in opposite
directions by its two adjacent triangles, so any directed
edge with count > 1 indicates inconsistent winding. Returns
null for non-triangle geometries. Used by
geometryQuality's winding check.
64-bin log₂ histogram of every triangle edge's length. Pose-
invariant shape fingerprint (translation / rotation /
reflection independent; NOT scale-invariant). Returns null
for non-triangle geometries. Used by
similarGeometries.
Undirected edge → list of triangle ids that include it,
plus a per-triangle degeneracy bitmap. Same key shape as
undirectedEdgeIncidence, but the heavier triangle-id
list is only paid when a consumer actually needs adjacency
(the unifyTriangleWinding fix's flood-fill walk). The
degenerate bitmap has degenerate[t] === 1 iff triangle
t has repeated indices (its edges are NOT registered in
edges). Returns null for non-triangle geometries.
Nuke every cached row in the index.
Drop one geometry's cached rows.
Drop one object's cached rows.
Drop the per-scene reverse-reference tables.
materialId → mesh ids that reference it.
World-space AABB (union of mesh world AABBs). Used by objectPlacement's far-from-origin and duplicate-AABB checks.
Free the heaviest cached rows (decompressed positions,
directed/undirected edge maps, canonical-triangle key sets)
but keep the cheap ones (content hash, topology key,
canonical slots, AABB centroid). Called by the orchestrator
after inspectSceneModel returns, unless the caller pinned
them.
textureId → material ids that reference it.
${primitive}|${vertCount}|${triCount} topology key. Two
geometries with the same key have the same primitive type
and the same vertex / triangle counts — a fast pre-bucket
for shape-similarity matchers. Used by
similarGeometries.
Undirected edge → incidence count. Key is lo_hi with
lo = min(a, b), hi = max(a, b). Watertight closed
manifolds have every edge incident to exactly 2 triangles.
Returns null for non-triangle geometries. Used by
geometryQuality's non-watertight check.
Bit per vertex slot — 1 = referenced by some entry of
geom.indices or geom.edgeIndices, 0 = unused. Used by
geometryQuality's unused-vertex check and the
compactUnusedVertices fix.
Lazy per-column cache of derived data the inspect / fix pipeline reads from a SceneModel. Single source of truth for "expensive precomputable rows" — content hashes, vertex dedup maps, edge incidence counts, decompressed positions, reverse-reference tables — so the same work isn't repeated by every inspection that needs it (and again by every fix that touches the same geometry).
Per-column lazy. Each accessor builds and caches on first call, returns the cached value thereafter. A consumer that only needs
contentHashdoesn't pay fordecompressedPositionsoredgeToTriangles.Where it lives. Held in a process-local WeakMap keyed by SceneModel via getInspectionIndex — no SceneModel API change, auto-GC'd, transparent to consumers. Each
inspectSceneModel/applyFixesrun reuses the same index across inspections + fixes.Invalidation. Coarse, orchestrator-driven. After a fix mutates a geometry the orchestrator calls invalidateGeometry; after a fix touches references it calls invalidateReferences. Clients that mutate the SceneModel outside the framework should call invalidateAll when they're done.