physics/constraints/springConstraint.js

import { Constraint } from "./constraint.js";
import { Vector2 } from "../../math/index.js"

let tmp1 = new Vector2(),
  tmp2 = new Vector2(),
  tmp3 = new Vector2(),
  tmp4 = new Vector2(),
  tmp5 = new Vector2(),
  zero = new Vector2()
 /**
  * A constraint that acts like a spring between two bodies
 */
export class SpringConstraint extends Constraint {
  /**
   * @param {Body2D} body1
   * @param {Body2D} body2
   * @param { Vector2} localA
   * @param { Vector2} localB
   */
  constructor(body1, body2, localA, localB) {
    super(body1, body2)
    this.localA = new Vector2().copy(localA || zero)
    this.localB = new Vector2().copy(localB || zero)
    this.fixed = !body1.mass || !body2.mass
    this.dampening = 1
    this.maxDistance = 100
    this.stiffness = 1
  }
    /**
   * @inheritdoc
   * 
   * @param {Body2D} body1
   * @param {Body2D} body2
   * @param {number} dt
  */
  behavior(body1, body2, dt) {
    let arm1 = tmp1.copy(this.localA),
      arm2 = tmp2.copy(this.localB),
      pos1 = tmp3.copy(body1.position).add(arm1),
      pos2 = tmp4.copy(body2.position).add(arm2),
      dist = pos1.sub(pos2),
      magnitude = dist.magnitude()

    if (magnitude === 0) {
      return
    }
    let difference = (magnitude - this.maxDistance) / magnitude,
      force = dist.multiply(difference * this.stiffness * this.dampeninging),
      massTotal = body1.inv_mass + body2.inv_mass,
      inertiaTotal = body1.inv_inertia + body2.inv_inertia
      force.divide(massTotal + inertiaTotal)
      body1.velocity.add(tmp5.copy(force).multiply(-body1.inv_mass))
      body2.velocity.add(tmp5.copy(force).multiply(body2.inv_mass))
      
      body1.rotation.value += force.cross(arm1) * body1.inv_inertia
      body2.rotation.value += force.cross(arm2) * -body2.inv_inertia
  }
}