import { VecN, type SourceSimplexReferenceN } from '@holotope/core'; import { type ClampedLogBarrierForceN } from './clamped-log-barrier.js'; import { evaluateSourceSimplexPairDistanceN, type SourceSimplexPairSeparatedUniqueN } from './simplex-pair-distance.js'; import { type XpbdIncrementalPotentialStepFilterContextN, type XpbdIncrementalPotentialStepFilterN } from './xpbd-incremental-potential-step-filter.js'; import { XpbdParticleN, type XpbdConservativeForceProviderEvaluationN, type XpbdConservativeForceProviderN, type XpbdParticlePositionQueryN } from './xpbd-world.js'; /** Open-domain refusal vocabulary of the source-simplex pair barrier. */ export type XpbdSourceSimplexPairBarrierDomainReasonN = 'zero-or-intersecting' | 'at-or-below-minimum-distance' | 'tied-witness-no-unique-gradient' | 'uncertified-distance' | 'barrier-component-outside-float64'; /** Construction options for one conservative RN feature-pair barrier. */ export interface XpbdSourceSimplexPairBarrierNOptions { /** Stable provider identifier. */ readonly id: string; /** * Live particles carrying side A's vertices, **one per vertex of * `featureA`, in that reference's own vertex order** — slot `i` is the * candidate position of `featureA.vertexIndices[i]`. */ readonly particlesA: readonly XpbdParticleN[]; /** Persistent identity of the deforming feature. */ readonly featureA: SourceSimplexReferenceN; /** * Optional live particles for side B, same slot convention. Omitted, side B * is static and reads its reference's complex positions — the moving- * segment-against-static-segment composition this slice commissions first. */ readonly particlesB?: readonly XpbdParticleN[]; /** Persistent identity of the opposing feature. */ readonly featureB: SourceSimplexReferenceN; /** Open unsigned distance boundary. Default zero. */ readonly minimumDistance?: number; /** Distance at and above which the barrier is exactly zero. */ readonly activationDistance: number; /** Positive energy scale. */ readonly stiffness: number; /** Relative affine-rank tolerance forwarded to the pair query. Default `1e-10`. */ readonly rankTolerance?: number; } /** Conservative forces and closest-pair evidence at one candidate placement. */ export interface XpbdSourceSimplexPairBarrierEvaluationN extends XpbdConservativeForceProviderEvaluationN { /** The certified unique pair result the forces were derived from. */ readonly pair: SourceSimplexPairSeparatedUniqueN; /** Unsigned distance between the features. */ readonly distance: number; /** Unit vector from side B's witness toward side A's. */ readonly separationNormal: VecN; /** `distance - minimumDistance`. */ readonly barrierCoordinate: number; /** `activationDistance - minimumDistance`. */ readonly barrierActivation: number; /** * The graded order-1 scalar evaluation this force was built from. Both * required components were available (the provider refused otherwise). */ readonly barrier: ClampedLogBarrierForceN; /** One force per provider particle: side A slots first, then side B's. */ readonly forces: readonly VecN[]; } /** * Conservative RN distance barrier between two source-simplex features. * * This is the feature-pair generalization of * `XpbdParticleSourceSimplexBarrierN`: the same clamped-log law over the same * open coordinate `distance − minimumDistance`, with the distance now coming * from {@link evaluateSourceSimplexPairDistanceN} and the force distributed * through the returned **source-ordered barycentric weights** — the envelope * form. Each side-A particle `i` receives `−b′·λᵢ·n̂` and each side-B * particle `j` (when side B moves) receives `+b′·μⱼ·n̂`, so the pair's net * internal force is exactly zero in exact arithmetic and near roundoff in * Float64, and the RN antisymmetric first moment cancels because every force * acts along the common witness line. * * The energy is differentiable exactly where the closest pair is unique, and * the provider refuses everything else **by type instead of fabricating * physics**: a tied witness (`separated-multiple` — parallel segments are the * canonical case) has no unique gradient to distribute; zero distance has no * direction at all; an uncertified comparison is not a distance. Refusals are * `XpbdPotentialDomainErrorN` values naming the reason, and no particle or * source state is touched on any path — evaluation is read-only. * * Pair it with `XpbdSourceSimplexPairBarrierStepFilterN`: endpoint energy * alone cannot see a feature that sweeps through the other and ends clear. */ export declare class XpbdSourceSimplexPairBarrierN implements XpbdConservativeForceProviderN { /** Stable force-provider identity. */ readonly id: string; /** Ambient dimension shared by both features and every particle. */ readonly dimension: number; /** Side-A particles, one per `featureA` vertex, in source order. */ readonly particlesA: readonly XpbdParticleN[]; /** Side-B particles when side B moves; `undefined` for a static side B. */ readonly particlesB: readonly XpbdParticleN[] | undefined; /** Provider particle list: side A slots first, then side B's (if moving). */ readonly particles: readonly XpbdParticleN[]; /** Persistent deforming-feature identity. */ readonly featureA: SourceSimplexReferenceN; /** Persistent opposing-feature identity. */ readonly featureB: SourceSimplexReferenceN; /** Open hard unsigned-distance boundary. */ readonly minimumDistance: number; /** Distance at and above which energy and force are zero. */ readonly activationDistance: number; /** Positive scalar energy multiplier. */ readonly stiffness: number; /** Affine-rank tolerance forwarded to the pair query. */ readonly rankTolerance: number; /** Creates one source-retained feature-pair proximity barrier. */ constructor(options: XpbdSourceSimplexPairBarrierNOptions); /** Evaluates from the particles' current live positions without mutation. */ evaluate(): XpbdSourceSimplexPairBarrierEvaluationN; /** Evaluates one caller-supplied candidate placement without live-state writes. */ evaluateAt(positionOf: XpbdParticlePositionQueryN): XpbdSourceSimplexPairBarrierEvaluationN; /** The raw pair query at a candidate placement; used by the paired filter. */ evaluatePairAt(positionOf: XpbdParticlePositionQueryN, caller?: string): ReturnType; private packSide; } /** Construction options for the paired conservative step filter. */ export interface XpbdSourceSimplexPairBarrierStepFilterNOptions { /** Stable authored identity within one compiled problem. */ readonly id: string; /** Barrier whose features and open boundary define admissibility. */ readonly barrier: XpbdSourceSimplexPairBarrierN; /** Fraction of the certified Lipschitz prefix retained; default `0.9`. */ readonly conservativeScale?: number; } /** Why the pair filter could not certify any segment prefix. */ export type XpbdSourceSimplexPairBarrierStepFilterRefusalReasonN = 'initial-domain-violation' | 'initial-uncertified-distance'; /** Evidence behind one certified pair segment. */ export interface XpbdSourceSimplexPairBarrierStepFilterEvidenceN { /** Certified pair distance at the segment start. */ readonly startDistance: number; /** Start distance above the barrier's open minimum. */ readonly startMargin: number; /** Largest single-vertex displacement on side A over the full segment. */ readonly maxDisplacementA: number; /** Largest single-vertex displacement on side B (zero when static). */ readonly maxDisplacementB: number; /** `maxDisplacementA + maxDisplacementB` — the Lipschitz path bound. */ readonly totalDisplacement: number; /** Certified fraction of the requested segment, in `[0, 1]`. */ readonly certifiedFraction: number; /** Proof used; never an inferred or exact impact time. */ readonly certification: 'stationary' | 'global-lipschitz' | 'initial-domain-violation' | 'initial-uncertified-distance'; } /** Result of one conservative pair segment query. */ export type XpbdSourceSimplexPairBarrierStepFilterEvaluationN = XpbdSourceSimplexPairBarrierStepFilterEvidenceN & ({ readonly status: 'safe'; readonly maximumStepLength: number; } | { readonly status: 'limited'; readonly maximumStepLength: number; } | { readonly status: 'indeterminate'; readonly reason: XpbdSourceSimplexPairBarrierStepFilterRefusalReasonN; }); /** * Conservative RN feature-pair collision-free step filter. * * The proof is the two-sided Hausdorff/Lipschitz bound: moving every vertex * of a simplex by at most `δ` moves the whole convex set by at most `δ` in * Hausdorff distance (a convex combination of per-vertex displacements), and * the pair distance is 1-Lipschitz in each argument under that metric, so * along the linear segment * * ```text * d(t) >= d(0) - t * (maxDisplacementA + maxDisplacementB). * ``` * * A segment whose total displacement stays below the start margin is safe in * full; otherwise the bound certifies a strict prefix. The result reports a * `certifiedFraction`, **never a collision time** — this filter does not * solve the piecewise closest-feature crossing. A tied start (parallel * features) is fine: the bound needs only the certified distance, which ties * still carry; only zero distance and an uncertified start refuse, by type. */ export declare class XpbdSourceSimplexPairBarrierStepFilterN implements XpbdIncrementalPotentialStepFilterN { /** Stable authored filter identity. */ readonly id: string; /** Ambient dimension accepted by the filter. */ readonly dimension: number; /** Paired feature barrier. */ readonly barrier: XpbdSourceSimplexPairBarrierN; /** Exact particle identities read by this filter. */ readonly particles: readonly XpbdParticleN[]; /** Strict scale applied to the Lipschitz prefix. */ readonly conservativeScale: number; /** Creates one conservative feature-pair step filter. */ constructor(options: XpbdSourceSimplexPairBarrierStepFilterNOptions); /** Certifies a complete segment or a conservative strict prefix. */ evaluate(context: XpbdIncrementalPotentialStepFilterContextN): XpbdSourceSimplexPairBarrierStepFilterEvaluationN; } //# sourceMappingURL=xpbd-source-simplex-pair-barrier.d.ts.map