@ferret/physics-stretch
springs, ropes and bands that stretch and tear; cloth and soft bodies by position-based dynamics.
Read the guide: how to use it →
Classes
Cloth class
class Clothreadonly n: number | |
readonly x: Float64Array | |
readonly prev: Float64Array | |
readonly inv: Float64Array | |
readonly links: Link[] | |
readonly colliders: ClothCollider[] | |
gravity: Vec3 | |
wind: ((p: Vec3) => Vec3) | null | wind velocity at a point |
readonly def: Required<Omit<ClothDef, 'origin' | 'u' | 'v' | 'pinned' | 'tearStrain'>> & { origin: Vec3; u: Vec3; v: Vec3; tearStrain?: number } | |
onTear: ((l: { i: number; j: number; kind: string }) => void) | null | called for each link that tears, after the step |
torn: number | links torn so far |
time: number | |
constructor(d: ClothDef) | |
position(i: number): Vec3 | |
pin(i: number) | |
release(i: number) | |
move(i: number, p: Vec3) | Move a pinned particle (a hand holding a flag). |
maxStrain(): number | The largest stretch of any weave link above its rest length (0.1 = 10 %). |
step(dt: number) |
packages/physics-stretch/src/cloth.ts:50
Link class
class Linkreadonly a: Body | |
readonly b: Body | |
readonly L0: number | |
readonly k: number | |
readonly damping: number | |
readonly rope: boolean | |
readonly yieldStrain: number | |
readonly hardening: number | |
readonly breakStrain: number | |
readonly name?: string | |
plastic: number | permanent strain and the accumulated plastic strain (they differ only after a reversal) |
alpha: number | |
broken: boolean | |
c: Constraint | null | |
constructor(private readonly kernel: PhysicsKernel, d: LinkDef) | |
get distance(): number | |
get strain(): number | (L − L0)/L0 |
get elastic(): number | the elastic part of the strain |
get force(): number | tension, N (negative in compression) |
get rest(): number | the length the link now rests at, m |
update(): boolean | Called after the kernel step. Returns true on the step the link breaks. |
break() | |
remove() |
packages/physics-stretch/src/links.ts:32
SoftBody class
class SoftBodyA soft body by position-based dynamics: a closed skin of particles held by its edges and by one constraint on the volume it encloses (Müller's balloon), so a ball of jelly or a cushion squashes on the ground and bulges at the sides instead of losing its volume. Collides with planes (floor); deterministic.
readonly n: number | |
readonly x: Float64Array | |
readonly prev: Float64Array | |
readonly inv: number | |
readonly tris: number[] | |
readonly edges: Array<{ i: number; j: number; rest: number }> | |
readonly rest: number | |
gravity: Vec3 | |
floors: Array<{ normal: Vec3; offset: number }> | |
readonly def: Required<Omit<SoftBodyDef, 'centre' | 'radius' | 'rings' | 'segments'>> | |
constructor(d: SoftBodyDef) | |
volume(): number | Enclosed volume, m³ (the divergence theorem over the skin). |
centre(): Vec3 | |
extent() | Lowest point and widest extent across x, m. |
step(dt: number) |
packages/physics-stretch/src/soft.ts:27
Functions
makeRope function
function makeRope(kernel: PhysicsKernel, d: RopeDef): RopeResultA chain of beads joined by links; the stiffness of the whole is E·A/L, so each of n segments is n·E·A/L.
packages/physics-stretch/src/links.ts:122
stretch function
function stretch(): StretchCapabilitypackages/physics-stretch/src/index.ts:30
Interfaces
ClothDef interface
interface ClothDefnu: number | particles along the two directions |
nv: number | |
spacing: number | distance between neighbouring particles, m |
origin?: Vec3 | position of particle (0, 0), and the directions the grid runs in |
u?: Vec3 | |
v?: Vec3 | |
mass: number | total mass, kg |
stiffness?: number | 0..1: how hard the weave links are corrected each iteration |
bending?: number | 0..1 for the bending links (0 = a flexible sheet, 1 = a stiff plate) |
strainLimit?: number | a link may not be longer than its rest length times this |
damping?: number | velocity lost per second, 0..1 |
thickness?: number | half the thickness: particles stay this far outside colliders, m |
friction?: number | |
iterations?: number | |
substeps?: number | |
pinned?: 'top' | 'corners' | 'none' | number[] | particles held where they are: 'top' (the first row of v), 'corners', or indices |
drag?: number | air drag coefficient, kg/s per particle against the wind |
tearStrain?: number | a weave or shear link still stretched past this strain at the end of a sub-step tears (0.15 = 15 %); never when missing. The strain of the solved sheet, so it is a dial of this model: a sheet of 12 rows hangs at about 5 % under its weight |
packages/physics-stretch/src/cloth.ts:14
LinkDef interface
interface LinkDefSprings, ropes and bands between two kernel bodies. A link is a kernel spring whose rest length and stiffness this layer keeps up to date: elastic first, then plastic past the yield strain (a permanent set, with linear hardening), then broken past the tensile strain. A rope-like link does not push: when the two ends are closer than the rest length it carries no force.
a: Body | |
b: Body | |
length?: number | natural length, m (the present distance when missing) |
k?: number | stiffness N/m; or give E (Pa) and area (m²) and it is E·A/L |
E?: number | |
area?: number | |
damping?: number | N·s/m |
rope?: boolean | true: no force when slack |
yieldStrain?: number | strain (ΔL/L) where the link stops being elastic; never when missing |
hardening?: number | plastic hardening: the yield strain grows by this ratio of the plastic strain (0 = perfectly plastic) |
breakStrain?: number | strain where the link breaks; never when missing |
name?: string |
packages/physics-stretch/src/links.ts:8
RopeDef interface
interface RopeDeffrom: Vec3 | |
to: Vec3 | |
segments: number | |
mass: number | total mass, kg |
E: number | string area and Young's modulus, for the stiffness of the whole rope: k_total = E·A/L |
area: number | |
damping?: number | |
anchorA?: Body | body of the end(s): a static or kinematic body to hang from; otherwise a free end |
anchorB?: Body | |
yieldStrain?: number | |
hardening?: number | |
breakStrain?: number | |
radius?: number | radius of the beads, m |
rope?: boolean |
packages/physics-stretch/src/links.ts:98
RopeResult interface
interface RopeResultpackages/physics-stretch/src/links.ts:119
SoftBodyDef interface
interface SoftBodyDefcentre: Vec3 | centre and radius of the ball the body starts as |
radius: number | |
rings?: number | rings of latitude and segments of longitude of its surface |
segments?: number | |
mass: number | |
stiffness?: number | 0..1: how hard the skin's edges are corrected each iteration |
pressure?: number | the volume it keeps, as a share of the one it starts with (1 = a closed ball of water; above 1 = blown up) |
iterations?: number | |
substeps?: number | |
damping?: number | velocity lost per second, 0..1 |
friction?: number |
packages/physics-stretch/src/soft.ts:3
StretchCapability interface
interface StretchCapability extends Capabilityreadonly links: Set<Link> | |
readonly cloths: Map<string, Cloth> | |
link(d: LinkDef): Link | |
rope(d: RopeDef): RopeResult | |
addCloth(name: string, d: ClothDef): Cloth | |
particles(): { asked: number; stepped: number } | Particles that cloths asked for, and the part the budget lets through. |
packages/physics-stretch/src/index.ts:20
Types
ClothCollider type
type ClothCollider = | { type: 'sphere'; center: Vec3; radius: number } | { type: 'plane'; normal: Vec3; offset: number } | { type: 'box'; center: Vec3; half: Vec3 } | { type: 'body'; body: Body }Cloth and soft sheets by position-based dynamics: distance constraints along the weave and across it, bending constraints between next-but-one neighbours, a hard limit on how far any link may stretch, and collisions with spheres, planes, boxes and the bodies of the scene. Verlet integration at a fixed step with a fixed number of iterations, so a replay is identical.
packages/physics-stretch/src/cloth.ts:8
Constants
STRETCH_DEFAULTS const
const STRETCH_DEFAULTS: { particleBudget: number; wind: [number, number, number]; }things that stretch. Links (springs, ropes, bands) are kept elastic, plastic or broken by their strain; cloth is stepped by position-based dynamics, with a particle budget so a scene cannot ask for more than it can pay.
values (each at world / level / zone / entity scope):
particleBudget most cloth particles stepped per frame, all cloths together (4000)
wind wind velocity on cloth, [x, y, z] m/s ([0, 0, 0])events: stretch:yield { link }, stretch:break { link }.
packages/physics-stretch/src/index.ts:18