Skip to content

Instantly share code, notes, and snippets.

@lucas-jones
Created October 2, 2024 15:53
Show Gist options
  • Save lucas-jones/d42187cb62b93b1b20077134abbadd75 to your computer and use it in GitHub Desktop.
Save lucas-jones/d42187cb62b93b1b20077134abbadd75 to your computer and use it in GitHub Desktop.
Typescript Spring
export class Spring {
target: number = 0;
position: number = 0;
velocity: number = 0;
frequency: number = 0.2;
damping: number = 1;
constructor(frequency: number = 0.2, damping: number = 1) {
this.frequency = frequency;
this.damping = damping;
}
update(deltaTime: number) {
var parms = CalcDampedSpringMotionParams(deltaTime, this.frequency, this.damping);
UpdateDampedSpringMotion(this, this.target, parms);
}
}
export const UpdateDampedSpringMotion = (spring: Spring, equilibriumPos: number, params: SpringParams) => {
const oldPos = spring.position - equilibriumPos;
const oldVel = spring.velocity;
spring.position = oldPos * params.posPosCoef + oldVel * params.positionVelCoef + equilibriumPos;
spring.velocity = oldPos * params.velPosCoef + oldVel * params.velocityVelCoef;
};
export type SpringParams = {
posPosCoef: number;
positionVelCoef: number;
velPosCoef: number;
velocityVelCoef: number;
};
export const CalcDampedSpringMotionParams = (deltaTime: number, angularFrequency: number, dampingRatio: number) => {
const epsilon = 0.0001;
var springParams: SpringParams = {
posPosCoef: 0,
positionVelCoef: 0,
velPosCoef: 0,
velocityVelCoef: 0,
};
if (dampingRatio < 0.0) dampingRatio = 0.0;
if (angularFrequency < 0.0) angularFrequency = 0.0;
if (angularFrequency < epsilon) {
springParams.posPosCoef = 1.0;
springParams.positionVelCoef = 0.0;
springParams.velPosCoef = 0.0;
springParams.velocityVelCoef = 1.0;
return;
}
if (dampingRatio > 1.0 + epsilon) {
var za = -angularFrequency * dampingRatio;
var zb = angularFrequency * Math.sqrt(dampingRatio * dampingRatio - 1.0);
var z1 = za - zb;
var z2 = za + zb;
var e1 = Math.exp(z1 * deltaTime);
var e2 = Math.exp(z2 * deltaTime);
var invTwoZb = 1.0 / (2.0 * zb);
var e1_Over_TwoZb = e1 * invTwoZb;
var e2_Over_TwoZb = e2 * invTwoZb;
var z1e1_Over_TwoZb = z1 * e1_Over_TwoZb;
var z2e2_Over_TwoZb = z2 * e2_Over_TwoZb;
springParams.posPosCoef = e1_Over_TwoZb * z2 - z2e2_Over_TwoZb + e2;
springParams.positionVelCoef = -e1_Over_TwoZb + e2_Over_TwoZb;
springParams.velPosCoef = (z1e1_Over_TwoZb - z2e2_Over_TwoZb + e2) * z2;
springParams.velocityVelCoef = -z1e1_Over_TwoZb + z2e2_Over_TwoZb;
} else if (dampingRatio < 1.0 - epsilon) {
var omegaZeta = angularFrequency * dampingRatio;
var alpha = angularFrequency * Math.sqrt(1.0 - dampingRatio * dampingRatio);
var expTerm = Math.exp(-omegaZeta * deltaTime);
var cosTerm = Math.cos(alpha * deltaTime);
var sinTerm = Math.sin(alpha * deltaTime);
var invAlpha = 1.0 / alpha;
var expSin = expTerm * sinTerm;
var expCos = expTerm * cosTerm;
var expOmegaZetaSin_Over_Alpha = expTerm * omegaZeta * sinTerm * invAlpha;
springParams.posPosCoef = expCos + expOmegaZetaSin_Over_Alpha;
springParams.positionVelCoef = expSin * invAlpha;
springParams.velPosCoef = -expSin * alpha - omegaZeta * expOmegaZetaSin_Over_Alpha;
springParams.velocityVelCoef = expCos - expOmegaZetaSin_Over_Alpha;
} else {
var expTerm = Math.exp(-angularFrequency * deltaTime);
var timeExp = deltaTime * expTerm;
var timeExpFreq = timeExp * angularFrequency;
springParams.posPosCoef = timeExpFreq + expTerm;
springParams.positionVelCoef = timeExp;
springParams.velPosCoef = -angularFrequency * timeExpFreq;
springParams.velocityVelCoef = -timeExpFreq + expTerm;
}
return springParams;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment