import { CellComplex, VecN, type CellGroup, type SourceCellIdN, type SourceCellReferenceN } from '@holotope/core'; import { type LinearSimplexMeasureAnalysisN } from './simplex-measure-cast.js'; import { type SimplexHingeCosineEvaluationN } from './simplex-hinge-cosine.js'; import type { XpbdIncrementalPotentialStepFilterContextN, XpbdIncrementalPotentialStepFilterEvaluationN, XpbdIncrementalPotentialStepFilterN } from './xpbd-incremental-potential-step-filter.js'; import { XpbdParticleBindingN } from './xpbd-particle-binding.js'; import { XpbdParticleN, XpbdWorldN, type XpbdConservativeForceProviderEvaluationN, type XpbdConservativeForceProviderN, type XpbdParticlePositionQueryN } from './xpbd-world.js'; /** * One interior hinge: a shared codimension-one face and its two apexes. * * Identity is source-side throughout. Cell ordinals and vertex indices name * the authored complex, never a position in this family's own arrays, so a * hinge stays recognizable across recompilation of an equivalent source. */ export interface XpbdSourceSimplexBendingHingeN { /** Family-scoped stable identity. */ readonly id: string; /** Shared-face source vertices, numerically ascending. */ readonly sharedVertices: readonly number[]; /** Source vertex of the lower-ordinal incident cell, opposite the face. */ readonly oppositeVertexA: number; /** Source vertex of the higher-ordinal incident cell, opposite the face. */ readonly oppositeVertexB: number; /** Persistent reference to the lower-ordinal incident simplex. */ readonly cellA: SourceCellReferenceN; /** Persistent reference to the higher-ordinal incident simplex. */ readonly cellB: SourceCellReferenceN; /** Structural ids, stable across order-independent regeneration. */ /** Structural id of the lower-ordinal incident cell. */ readonly cellIdA: SourceCellIdN; /** Structural id of the higher-ordinal incident cell. */ readonly cellIdB: SourceCellIdN; /** Fold coordinate captured from the source at compilation. */ readonly restCoordinate: number; /** Conormal heights at rest, for auditing a near-degenerate rest shape. */ /** First apex conormal height at rest. */ readonly restHeightA: number; /** Second apex conormal height at rest. */ readonly restHeightB: number; /** Shared-face conditioning at rest. */ readonly restConditioning: number; } /** One hinge's contribution at a candidate state. */ export interface XpbdSourceSimplexBendingHingeEvaluationN { /** The compiled hinge this record belongs to. */ readonly hinge: XpbdSourceSimplexBendingHingeN; /** Complete P48 geometry, including the per-vertex gradient. */ readonly geometry: SimplexHingeCosineEvaluationN; /** `c - cRest`; zero at the captured rest shape. */ readonly coordinateError: number; /** `0.5 * stiffness * weight * (c - cRest)^2`, with `weight = 1`. */ readonly energy: number; } /** Aggregate conservative evaluation over every compiled hinge. */ export interface XpbdSourceSimplexCosineBendingFamilyEvaluationN extends XpbdConservativeForceProviderEvaluationN { /** Per-hinge records in compiled hinge order; never flattened to counts. */ readonly hinges: readonly XpbdSourceSimplexBendingHingeEvaluationN[]; /** One accumulated force per bound source vertex, in binding order. */ readonly forces: readonly VecN[]; /** Interior hinges evaluated. */ readonly hingeCount: number; /** Codimension-one faces with exactly one incident cell; not hinges. */ readonly boundaryFaceCount: number; /** Largest `|c - cRest|` over the evaluated hinges. */ readonly maximumCoordinateError: number; /** Smallest conormal height seen; small means an ill-posed hinge. */ readonly minimumConormalHeight: number; /** Smallest shared-face conditioning seen. */ readonly minimumConditioning: number; /** Norm of the summed force; a bending potential exerts no net force. */ readonly netForceResidual: number; /** * Largest skew component of `sum(x ⊗ f)`. * * Zero for a coordinate invariant under rigid motion, so a non-trivial value * would mean the gradient had drifted from the coordinate it claims to * differentiate. */ readonly rotationalFirstMomentResidual: number; /** Weighting policy actually applied. */ readonly weighting: 'unit-discrete'; /** The literal weight; `1` for every hinge in this stage. */ readonly weight: 1; } /** Provider and its paired filter, accepted together by a compiled problem. */ export interface XpbdSourceSimplexCosineBendingFamilyTermsN { /** Existing providers followed by this bending family provider. */ readonly providers: readonly XpbdConservativeForceProviderN[]; /** Existing filters followed by this family's paired segment certificate. */ readonly stepFilters: readonly XpbdIncrementalPotentialStepFilterN[]; } /** Why an aggregate bending segment query refused certification. */ export type XpbdSourceSimplexCosineBendingFilterRefusalReasonN = 'initial-measure-violation' | 'no-certifiable-prefix'; /** One inspected source simplex paired with its measure analysis. */ export interface XpbdSourceSimplexBendingCellAnalysisN { /** Persistent reference to the inspected source simplex. */ readonly cell: SourceCellReferenceN; /** Its ordinal within the compiled simplex group. */ readonly cellIndex: number; /** Complete polynomial enclosure evidence for this cell over the segment. */ readonly analysis: LinearSimplexMeasureAnalysisN; } /** Aggregate intrinsic-rank certification for one proposed segment. */ export type XpbdSourceSimplexCosineBendingFamilyStepFilterEvaluationN = { /** Per-cell analyses in persistent source-cell order. */ readonly cells: readonly XpbdSourceSimplexBendingCellAnalysisN[]; /** Source cell imposing the aggregate limit or refusal, otherwise `null`. */ readonly blockingCellIndex: number | null; } & ({ readonly status: 'safe'; readonly maximumStepLength: number; } | { readonly status: 'limited'; readonly maximumStepLength: number; } | { readonly status: 'indeterminate'; readonly reason: XpbdSourceSimplexCosineBendingFilterRefusalReasonN; }); /** Construction options for one source-retained cosine-bending family. */ export interface CompileXpbdSourceSimplexCosineBendingFamilyNOptions { /** Stable provider identity and hinge-ID prefix. */ readonly id: string; /** Authoritative source-vertex to particle mapping. */ readonly binding: XpbdParticleBindingN; /** Simplex group belonging to `binding.source`. */ readonly simplexGroup: CellGroup; /** * Uniform finite positive stiffness. * * Discretization-dependent. The energy is quartic in small fold angle, so a * value tuned on one mesh does not transfer to a refinement of it. */ readonly stiffness: number; /** * Rest fold coordinate. `'source'` (the default) captures each hinge's * coordinate from the authored geometry; a finite scalar in `[-1, 1]` sets * every hinge, with `1` meaning flat. */ readonly restCoordinate?: 'source' | number; /** * Minimum current/rest intrinsic measure ratio the paired filter certifies. * * Required, because endpoint evaluation alone cannot see a segment passing * through zero conormal height and arriving non-degenerate. */ readonly minimumMeasureRatio: number; /** Relative rank/height tolerance for the hinge geometry. Default `1e-10`. */ readonly tolerance?: number; /** Fraction of a certified prefix retained. Default `0.9`. */ readonly conservativeScale?: number; /** Bracket resolution forwarded to `analyzeLinearSimplexMeasureN`. */ readonly timeTolerance?: number; /** Subdivision depth bound forwarded to `analyzeLinearSimplexMeasureN`. */ readonly maximumDepth?: number; /** Relative coefficient tolerance forwarded to the same analysis. */ readonly relativeCoefficientTolerance?: number; } interface CompiledCellN { readonly cellIndex: number; readonly reference: SourceCellReferenceN; readonly vertices: readonly number[]; readonly restPositions: readonly VecN[]; } /** * Source-retained discrete cosine-fold bending over adjacent simplices. * * **This is a discrete stiffness, not a shell model.** The energy is * `0.5 k (c - c_rest)²` in the cosine coordinate, which makes it *quartic* in * the fold angle where a continuum bending energy is quadratic. The P48 * measurement refined a fixed cylindrical strip in place and found the total * falling as `n^-2.99`: it does not converge to a non-zero continuum limit, and * no weighting fixes that. Stiffness values are therefore tied to the mesh they * were tuned on. * * The same fact has a practical consequence worth knowing before use: at a flat * rest shape the first derivative vanishes, so small folds produce very weak * restoring force. This resists large folds well and barely notices small ones. * * Only unit weighting exists here. The Discrete Shells face/height weight was * measured (`n^-2.00`, still not convergent) and deliberately not exposed, * because a weight enum would imply a calibration choice that neither option * earns. * * Identity is source-side. Hinges are keyed by their sorted shared-vertex * tuple and then by ascending incident cell ordinals, compared numerically — * a string sort would order vertex 10 before vertex 9. Rest geometry is * snapshotted at compilation, so writing new coordinates through * `binding.writeSourcePositions()` deforms the mesh against its captured rest * shape rather than silently redefining it. Changing the rest state is a * recompilation. * * First-order only: the family implements `XpbdConservativeForceProviderN` and * exposes no Hessian, so Newton-CG refuses the provider mixture rather than * silently dropping bending curvature. */ export declare class XpbdSourceSimplexCosineBendingFamilyN implements XpbdConservativeForceProviderN { /** Stable provider identity and hinge-ID prefix. */ readonly id: string; /** Ambient dimension `N`, taken from the binding. */ readonly dimension: number; /** Authoritative source-vertex to particle mapping. */ readonly binding: XpbdParticleBindingN; /** Bound particles in source-vertex order. */ readonly particles: readonly XpbdParticleN[]; /** Source complex owning the indexed simplex group. */ readonly source: CellComplex; /** Compiled simplex group; identity, not structural equality, is required. */ readonly simplexGroup: CellGroup; /** Intrinsic simplex dimension `d`, with `1 <= d < dimension`. */ readonly simplexDimension: number; /** Compiled interior hinges in canonical source order. */ readonly hinges: readonly XpbdSourceSimplexBendingHingeN[]; /** Codimension-one faces with exactly one incident cell. */ readonly boundaryFaceCount: number; /** Uniform stiffness; discretization-dependent, not a material constant. */ readonly stiffness: number; /** Relative rank/height tolerance used by every hinge evaluation. */ readonly tolerance: number; /** Minimum current/rest measure ratio the paired filter certifies. */ readonly minimumMeasureRatio: number; /** Fraction of a certified prefix the filter retains. */ readonly conservativeScale: number; /** Paired continuous-domain filter; pass it to every incremental solve. */ readonly stepFilter: XpbdSourceSimplexCosineBendingFamilyStepFilterN; /** Weighting policy, exposed so the choice is visible rather than implied. */ readonly weighting: 'unit-discrete'; /** @internal Distinct source simplices the filter inspects, in cell order. */ readonly compiledCells: readonly CompiledCellN[]; /** @internal Forwarded measure-analysis controls. */ readonly analysisOptions: Readonly<{ timeTolerance?: number; maximumDepth?: number; relativeCoefficientTolerance?: number; }>; private attachedWorld; private constructor(); /** Compiles interior hinges, rest geometry, and the paired filter. */ static compile(options: CompileXpbdSourceSimplexCosineBendingFamilyNOptions): XpbdSourceSimplexCosineBendingFamilyN; /** Evaluates from the particles' current positions. */ evaluate(): XpbdSourceSimplexCosineBendingFamilyEvaluationN; /** Evaluates one candidate state without mutating live particles. */ evaluateAt(positionOf: XpbdParticlePositionQueryN): XpbdSourceSimplexCosineBendingFamilyEvaluationN; /** Returns this provider with its paired filter, optionally after base terms. */ incrementalPotentialTerms(base?: XpbdSourceSimplexCosineBendingFamilyTermsN): XpbdSourceSimplexCosineBendingFamilyTermsN; /** Registers this provider; its particles must already be in the world. */ addToWorld(world: XpbdWorldN): XpbdWorldN; } /** * Aggregate intrinsic-rank certificate for one proposed bending segment. * * Endpoint evaluation is not enough on its own. A linear search segment can * begin and end with perfectly good hinges while passing through zero conormal * height in between, and at that instant the fold coordinate does not exist. * Both endpoints would look finite and the crossing would be invisible. * * The certificate reuses `analyzeLinearSimplexMeasureN` over each distinct * source simplex rather than inventing a second polynomial or sampling the * chord: non-zero intrinsic measure of both incident simplices implies a * full-rank shared face and a non-zero apex conormal, which is exactly the * condition the coordinate needs. * * What it returns is a conservative admissible prefix, not an exact collapse * time and not a collision response. */ export declare class XpbdSourceSimplexCosineBendingFamilyStepFilterN implements XpbdIncrementalPotentialStepFilterN { readonly id: string; readonly dimension: number; readonly particles: readonly XpbdParticleN[]; private readonly family; /** * Binds one filter to its family; the family constructs its own. * * @param family - The compiled family whose source cells this certifies. */ constructor(family: XpbdSourceSimplexCosineBendingFamilyN); /** * Certifies the complete segment, a strict prefix, or refuses. * * This is {@link evaluateSegment} under the narrower solver-seam type. The * two are one computation returning one object: nothing is recomputed and * nothing is stripped, so `cells` and `blockingCellIndex` are present on this * result at runtime even though the seam type does not declare them. Call * {@link evaluateSegment} to have them typed rather than to obtain them. */ evaluate(context: XpbdIncrementalPotentialStepFilterContextN): XpbdIncrementalPotentialStepFilterEvaluationN; /** * The same query, returning the family's own per-cell evidence as its type. * * Identical work and an identical object to {@link evaluate} — the difference * is static only. The seam type cannot carry per-cell records, and widening * it is not this slice's business, so the richer type lives here instead of * the evidence being discarded to fit. */ evaluateSegment(context: XpbdIncrementalPotentialStepFilterContextN): XpbdSourceSimplexCosineBendingFamilyStepFilterEvaluationN; } /** * Compiles one source-retained cosine-bending family. * * **A discrete cosine-fold stiffness, not a continuum shell.** See * {@link XpbdSourceSimplexCosineBendingFamilyN} for the measured * non-convergence and what it means for choosing `stiffness`. * * @param options - The binding, its simplex group, a uniform positive * stiffness, the required `minimumMeasureRatio` for the paired filter, and an * optional rest coordinate. * @returns A compiled family exposing the provider and `stepFilter` together. * @throws If options are malformed, the group does not belong to the binding's * source, a simplex is degenerate, or any codimension-one face has more than * two incident cells. A non-manifold face refuses the whole compilation rather * than yielding a partial family. * * @example * A flat-rest R4 membrane strip. Note the filter travelling with the provider: * ```ts * const sheet = new CellComplex(4, Float64Array.from([ * 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0 * ]), [{ * key: 'sheet', dim: 2, verticesPerCell: 3, kind: 'simplex', * indices: Uint32Array.from([0, 1, 2, 1, 3, 2]) * }]); * const [group] = sheet.cellsOfDim(2); * if (!group) throw new Error('the sheet has no 2-cells'); * * const binding = compileXpbdParticleBindingN({ id: 'sheet', source: sheet }); * const bending = compileXpbdSourceSimplexCosineBendingFamilyN({ * id: 'sheet-bending', * binding, * simplexGroup: group, * stiffness: 12, * restCoordinate: 1, * minimumMeasureRatio: 0.05 * }); * * log(bending.hinges.length); // 1 interior hinge * log(bending.boundaryFaceCount); // 4 boundary edges * log(bending.evaluate().potentialEnergy); // 0 — the strip starts flat * ``` */ export declare function compileXpbdSourceSimplexCosineBendingFamilyN(options: CompileXpbdSourceSimplexCosineBendingFamilyNOptions): XpbdSourceSimplexCosineBendingFamilyN; export {}; //# sourceMappingURL=xpbd-source-simplex-cosine-bending.d.ts.map