import type { CellComplex, CellGroup, CellKind } from '../geometry/cell-complex.js'; /** * In-memory identity for one cell in one `CellComplex` group. * * The group object is the identity anchor. The reference survives vertex * position changes, unrelated group insertion, and group reordering. It is * retired when the group object is removed, its cell metadata changes, or * the referenced vertex tuple changes. It intentionally does not claim to * survive regeneration into a different `CellComplex` instance. * * `SourceCellIdN` is the same identity at the other lifetime: this one is * anchored to a live object and is cheap to resolve, that one is serializable * and re-checked against a rebuilt complex. Convert when crossing a boundary * the object cannot cross — storage, a worker, or a regenerated complex. */ export interface SourceCellReferenceN { /** Discriminant, for narrowing a union of source identities. */ readonly kind: 'source-cell-reference'; /** The complex the cell belongs to, held by reference. */ readonly complex: CellComplex; /** The group object, which is what gives this reference its identity: * matching is by object, so reordering the complex's groups does not * disturb it and an equal-looking group from elsewhere does not satisfy it. */ readonly group: CellGroup; /** Where the group sat when the reference was made. A hint for locating it * quickly, not part of the identity — the group object decides that. */ readonly groupIndexAtCreation: number; /** Ordinal of the cell within its group. */ readonly cellIndex: number; /** Dimension of the cell itself. */ readonly intrinsicDim: number; /** Its kind. */ readonly cellKind: CellKind; /** The cell's own vertex indices, which retire the reference if rewritten. */ readonly vertexIndices: readonly number[]; } export type SourceCellReferenceRetirementReason = 'group-removed' | 'group-metadata-changed' | 'cell-removed' | 'cell-vertices-changed'; export type SourceCellReferenceStatusN = { readonly kind: 'current'; readonly groupIndex: number; } | { readonly kind: 'retired'; readonly reason: SourceCellReferenceRetirementReason; }; export type SourceCellGroupKeyKind = 'explicit' | 'derived'; /** * Serializable structural identity for one cell. * * The topology fingerprint prevents a key/ordinal from silently retargeting * after incompatible regeneration. Explicit group keys survive group * reordering; derived keys are deterministic only while construction order is * preserved. * * Every field here is a guard rather than a description: resolution checks * them in order and refuses with a named reason at the first that no longer * holds, so an id that resolves has been proven to still mean what it meant. * * | field | reason reported if it changed | * | --- | --- | * | `ambientDim` | `ambient-dimension-changed` | * | `groupKey` | `group-key-missing`, or `group-key-ambiguous` | * | `intrinsicDim`, `cellKind`, `verticesPerCell` | `group-metadata-changed` | * | `cellIndex` | `cell-removed` | * | `vertexIndices` | `cell-vertices-changed` | * * Refusing is the point. A cell ordinal alone would keep resolving after the * complex was rebuilt differently and would quietly name a different cell; * this returns unavailable instead, and says which assumption broke. */ export interface SourceCellIdN { /** Discriminant, for narrowing a union of source identities. */ readonly kind: 'source-cell-id'; /** Dimension the complex was in; a change invalidates everything below. */ readonly ambientDim: number; /** Identifies the cell group. Its meaning depends on `groupKeyKind`. */ readonly groupKey: string; /** `explicit` when the group carried an authored key, which survives * reordering. `derived` when the key encodes the group's position, which * only holds while construction order is preserved. */ readonly groupKeyKind: SourceCellGroupKeyKind; /** Ordinal of the cell within its group. */ readonly cellIndex: number; /** Dimension of the cell itself, checked against the group. */ readonly intrinsicDim: number; /** Its kind, checked against the group. */ readonly cellKind: CellKind; /** Vertices per cell in the group, checked against it. */ readonly verticesPerCell: number; /** The cell's own vertex indices, compared entry for entry — the fingerprint * that catches a complex rebuilt with the same shape but different content. */ readonly vertexIndices: readonly number[]; } export type SourceCellIdResolutionFailureReason = 'ambient-dimension-changed' | 'group-key-missing' | 'group-key-ambiguous' | 'group-metadata-changed' | 'cell-removed' | 'cell-vertices-changed'; export type SourceCellIdResolutionN = { readonly kind: 'resolved'; readonly reference: SourceCellReferenceN; } | { readonly kind: 'unavailable'; readonly reason: SourceCellIdResolutionFailureReason; }; /** Creates a lifecycle-aware reference to a group-local cell ordinal. */ export declare function createSourceCellReferenceN(complex: CellComplex, group: CellGroup, cellIndex: number): SourceCellReferenceN; /** Audits whether a source-cell reference still names the same topology. */ export declare function inspectSourceCellReferenceN(reference: SourceCellReferenceN): SourceCellReferenceStatusN; /** Returns the explicit group key or its deterministic order-based fallback. */ export declare function sourceCellGroupKeyN(complex: CellComplex, group: CellGroup): { readonly key: string; readonly kind: SourceCellGroupKeyKind; }; /** Snapshots a current in-memory cell reference as a structural id. */ export declare function createSourceCellIdN(reference: SourceCellReferenceN): SourceCellIdN; /** Resolve a structural id against one compatible current complex. */ export declare function resolveSourceCellIdN(complex: CellComplex, id: SourceCellIdN): SourceCellIdResolutionN; /** * Where a queried vertex tuple sits in a group, and how it is stored there. * * `orientation` is reported rather than normalised because the two producers * that meet at this seam agree today by coincidence rather than by contract: * `sliceTetrahedra` emits its provenance pairs ascending, and every cell group * built in this package stores them ascending, so a caller who assumes the * orders match is right — until a complex arrives that stores one descending. * * The failure that assumption produces is silent. A reversed pair turns * parameter `t` into `1 - t`, and the mirrored point **stays collinear with the * correct edge**, so an "is this point on the edge?" check still passes. Only a * hyperplane residual catches it. Reporting the orientation makes the caller * decide, which is the one thing a silent mirror never lets them do. */ export interface SourceCellLookupMatchN { /** Discriminant, for narrowing against a miss. */ readonly kind: 'source-cell-lookup-match'; /** Ordinal of the matching cell within the group. */ readonly cellIndex: number; /** * How the group stores the tuple relative to the query: `aligned` for the * same sequence, `reversed` for the exact reverse, `permuted` for the same * vertices in some other order. A parameter along the cell must be inverted * when this is `reversed`. */ readonly orientation: 'aligned' | 'reversed' | 'permuted'; } /** A queried vertex tuple that is not a cell of the group. */ export interface SourceCellLookupMissN { /** Discriminant, for narrowing against a match. */ readonly kind: 'source-cell-lookup-miss'; /** * `not-a-cell` — these vertices bound no cell of this group. The common * cause is a simplexization diagonal: a Kuhn decomposition cuts each cuboid * along its main diagonal, so most tetrahedron edges are diagonals rather * than 1-cells, and a point interpolated along one has no cell-level source * to name. Roughly three quarters of a tesseract section's vertices are in * this position. Resolve those through the parent-cell ordinal from * `simplexizeCuboidGroupN` instead. * * `arity-mismatch` — the tuple length does not match `verticesPerCell`, so * it could not name a cell of this group whatever its contents. */ readonly reason: 'not-a-cell' | 'arity-mismatch'; } /** The result of locating a vertex tuple in a group: found, or why not. */ export type SourceCellLookupN = SourceCellLookupMatchN | SourceCellLookupMissN; /** Reusable vertex-tuple index over one group. */ export interface SourceCellLookupIndexN { /** The group this index was built over; lookups are only valid against it. */ readonly group: CellGroup; /** Locate the cell whose vertex set equals `vertices`. */ find(vertices: ArrayLike): SourceCellLookupN; } /** * Builds a reusable vertex-tuple → cell-index map over one group. * * This is the seam between the module that *produces* provenance and the one * that *models* it. `sliceTetrahedra` reports which source vertices a section * vertex was interpolated between; {@link createSourceCellReferenceN} needs the * cell's ordinal. Nothing bridged them, so every caller doing this wrote the * same linear scan — and wrote it without noticing the orientation question. * * Prefer this over {@link findSourceCellByVerticesN} when resolving more than a * handful of vertices: building the map is one pass over the group, after which * each lookup is constant-time rather than a scan. * * @example * ```ts * const complex = createHypercube({ dim: 4, size: 2 }); * const [edges] = complex.cellsOfDim(1); * if (!edges) throw new Error('no edge group to index'); * * const index = createSourceCellLookupN(edges); * * // A section vertex reported by `sliceTetrahedra` as lying `t` of the way * // from source vertex `from` to source vertex `to`. The second edge, so * // indices 2 and 3; a group carries no cell count, only this buffer. * const [from, to] = edges.indices.subarray(2, 4); * if (from === undefined || to === undefined) throw new Error('no second edge'); * * const t = 0.25; * * const found = index.find([from, to]); * if (found.kind === 'source-cell-lookup-match') { * const reference = createSourceCellReferenceN(complex, edges, found.cellIndex); * // The stored edge may run the other way, which would mirror the parameter. * const parameter = found.orientation === 'reversed' ? 1 - t : t; * const point = evaluateSourceEdgeCoordinateN( * createSourceEdgeCoordinateN(reference, parameter) * ); * console.log(point.data[0]); * } * ``` */ export declare function createSourceCellLookupN(group: CellGroup): SourceCellLookupIndexN; /** * Locates a single cell by its vertex tuple. * * A one-shot convenience over {@link createSourceCellLookupN}; it builds the * same index and discards it, so resolving many vertices through this is a * full pass per lookup. */ export declare function findSourceCellByVerticesN(group: CellGroup, vertices: ArrayLike): SourceCellLookupN; //# sourceMappingURL=source-reference.d.ts.map