import { VecN, type SourceSimplexCoordinateN, type SourceSimplexReferenceN } from '@holotope/core'; /** * One side of a source-simplex pair query: persistent identity, and * optionally the packed candidate positions to evaluate at. Omitted positions * read the reference's live complex at its own vertex indices, so a static * obstacle feature needs nothing beyond its reference. */ export interface SourceSimplexPairSideN { /** Persistent source-simplex identity; refused by name when retired. */ readonly reference: SourceSimplexReferenceN; /** * Packed candidate positions, `vertexIndices.length * ambientDim` entries * in the reference's own vertex order — a deforming feature's proposed * placement. Positions are geometry only; identity always comes from the * reference. */ readonly positions?: Float64Array; } /** One certified closest-pair witness, in source vertex order on both sides. */ export interface SourceSimplexPairWitnessN { /** Ordered barycentric weights on A, as a source-simplex coordinate. */ readonly coordinateA: SourceSimplexCoordinateN; /** Ordered barycentric weights on B. */ readonly coordinateB: SourceSimplexCoordinateN; /** Closest point on A at the evaluated positions. */ readonly pointA: VecN; /** Closest point on B at the evaluated positions. */ readonly pointB: VecN; /** Active A-side slots (indices into the reference's vertex order). */ readonly activeSlotsA: readonly number[]; /** Active B-side slots. */ readonly activeSlotsB: readonly number[]; } /** Certified separated pair whose closest points are unique. */ export interface SourceSimplexPairSeparatedUniqueN { /** Certified separation with a unique closest pair. */ readonly status: 'separated-unique'; /** Certified unsigned distance between the features. */ readonly distance: number; /** `distance * distance`, from the optimal candidate's own arithmetic. */ readonly squaredDistance: number; /** Unit separating direction from B toward A. */ readonly direction: VecN; /** The unique closest pair, in source vertex order on both sides. */ readonly witness: SourceSimplexPairWitnessN; /** * Squared-distance gap to the best geometrically distinct candidate — the * measured margin that justifies differentiating through the witness * (Danskin/envelope form). `Infinity` when no competitor exists. */ readonly uniquenessGap: number; /** Worst variational-certificate residual of the accepted witness. */ readonly certificateResidual: number; /** Scale-derived certification tolerance used by this evaluation. */ readonly tolerance: number; } /** Certified distance whose optimal witness pair is not unique. */ export interface SourceSimplexPairSeparatedMultipleN { /** Certified separation whose optimal witness pair is not unique. */ readonly status: 'separated-multiple'; /** Certified unsigned distance, shared by every returned witness. */ readonly distance: number; /** `distance * distance`, from the best candidate's own arithmetic. */ readonly squaredDistance: number; /** Shared unit separating direction (B toward A), common to all witnesses. */ readonly direction: VecN; /** * Every certified, geometrically distinct optimal witness. A continuum of * closest pairs (exactly parallel segments) appears as its extreme * representatives. No single witness is blessed, and no gradient exists — * the gradient set is not a singleton here (Danskin), so returning one * would fabricate physics. */ readonly witnesses: readonly SourceSimplexPairWitnessN[]; /** Worst variational-certificate residual of the accepted optimum. */ readonly certificateResidual: number; /** Scale-derived certification tolerance used by this evaluation. */ readonly tolerance: number; } /** * Certified zero distance: the witnesses coincide within tolerance. Convex * simplices at distance zero touch or overlap; the query does not pretend to * distinguish which, and **no separating normal is invented**. */ export interface SourceSimplexPairZeroDistanceN { /** Certified contact-or-overlap; no separating direction exists. */ readonly status: 'zero-distance'; /** Residual squared distance of the coinciding witnesses. */ readonly squaredDistance: number; /** The coinciding pair, still in source vertex order. */ readonly witness: SourceSimplexPairWitnessN; /** Scale-derived certification tolerance used by this evaluation. */ readonly tolerance: number; } /** * Explicit refusal: Float64 could not certify the comparison at the derived * tolerance. Carries the evidence a caller needs to audit the refusal — * never silently converted into a separation. */ export interface SourceSimplexPairIndeterminateN { /** Explicit refusal to certify; never converted into a separation. */ readonly status: 'indeterminate'; /** Best uncertified candidate's squared distance — evidence, not a claim. */ readonly bestSquaredDistance: number; /** The certificate residual that exceeded tolerance. */ readonly certificateResidual: number; /** The tolerance it exceeded — the audit trail of the refusal. */ readonly tolerance: number; } /** Discriminated result of one source-simplex pair distance evaluation. */ export type SourceSimplexPairDistanceN = SourceSimplexPairSeparatedUniqueN | SourceSimplexPairSeparatedMultipleN | SourceSimplexPairZeroDistanceN | SourceSimplexPairIndeterminateN; /** Options for {@link evaluateSourceSimplexPairDistanceN}. */ export interface SourceSimplexPairDistanceOptionsN { /** Relative affine-rank tolerance per side. Default `1e-10`. */ readonly rankTolerance?: number; /** Barycentric feasibility band. Default `1e-10`. */ readonly barycentricTolerance?: number; } /** * Minimum distance between two finite source simplices in RN, with * source-retained witnesses, a variational certificate, honest ties, and * typed refusals. * * The kernel enumerates every nonempty face pair, solves the unconstrained * closest-affine-pair system per pair, keeps feasible candidates, and * certifies the optimum by the variational inequality against **every** * input vertex (`⟨n, v − p⟩ ≥ −τ` over A and `⟨n, q − w⟩ ≥ −τ` over B, with * `n = p − q` and τ derived from Float64 forward error, `128ε·max(1, M²)`). * The certificate is checked against the inputs, not against the enumeration * that produced the candidate. Distance between convex sets is differentiable * exactly where the closest pair is unique, so the result separates * `'separated-unique'` — carrying the measured `uniquenessGap` that justifies * the envelope-form gradient `∂d/∂aᵢ = λᵢ·n̂`, `∂d/∂bⱼ = −μⱼ·n̂` — from * `'separated-multiple'`, which returns every distinct optimal witness and * no gradient. Zero distance is certified with no invented normal, and an * uncertifiable comparison refuses with its own residuals. * * Weights are ordered by each reference's **own vertex order**, whatever any * internal solve does; pair swap preserves the distance, swaps the evidence, * and negates the direction. This is a mathematical result, not a solver * cache: there is no iteration budget, and identical input replays * identically. * * Prior art: the closest-pair KKT characterization is classical convex * analysis; the feature-pair decomposition of proximity follows Li et al., * *Incremental Potential Contact* (SIGGRAPH 2020) as mathematical prior art. * The implementation is original to this repository. * * @example * A deforming segment approaching a static obstacle edge, with the witness * explaining exactly which feature carried the answer — and the parallel tie * refusing to fabricate one: * ```ts * const complex = new CellComplex(3, Float64Array.from([ * -1, 0, 0, * 1, 0, 0, * -1, 0.75, 0.4, * 1, 0.75, -0.4 * ]), [{ dim: 1, verticesPerCell: 2, kind: 'simplex', * indices: Uint32Array.from([0, 1, 2, 3]) }]); * const group = complex.groups[0]; * if (group === undefined) throw new Error('expected the segment group'); * const obstacle = createSourceSimplexReferenceN( * createSourceCellReferenceN(complex, group, 0), [0, 1] * ); * const mover = createSourceSimplexReferenceN( * createSourceCellReferenceN(complex, group, 1), [2, 3] * ); * * const result = evaluateSourceSimplexPairDistanceN( * { reference: mover }, { reference: obstacle } * ); * log(result.status); // 'separated-unique' — the segments are skew * if (result.status === 'separated-unique') { * log('distance', result.distance); // 0.75 * log('weights on the mover', result.witness.coordinateA.weights); * // Which feature carried the answer: the active slots name the vertices * // (in source order) whose convex combination is the witness. * log('active mover vertices', result.witness.activeSlotsA); * log('active obstacle vertices', result.witness.activeSlotsB); * log('margin', result.uniquenessGap); // > 0: a derivative is justified * } * * // Propose a parallel placement: the tie is evidence, not a choice. * const parallel = evaluateSourceSimplexPairDistanceN( * { reference: mover, positions: Float64Array.from([ * -0.5, 0.75, 0, * 0.5, 0.75, 0 * ]) }, * { reference: obstacle } * ); * log(parallel.status); // 'separated-multiple' * if (parallel.status === 'separated-multiple') { * log('tied witnesses', parallel.witnesses.length); // >= 2, none blessed * } * ``` */ export declare function evaluateSourceSimplexPairDistanceN(sideA: SourceSimplexPairSideN, sideB: SourceSimplexPairSideN, options?: SourceSimplexPairDistanceOptionsN): SourceSimplexPairDistanceN; //# sourceMappingURL=simplex-pair-distance.d.ts.map