@dominicstop/utils
Version:
Yet another event emitter written in typescript.
117 lines (116 loc) • 3.63 kB
JavaScript
import { Vector2D } from "../geometry";
export class Particle {
get position() {
return this._position;
}
;
set position(newValue) {
this._position = newValue;
this.shape.center = newValue.asPoint;
}
;
constructor(args) {
this.shape = args.shape;
this.id = args.id;
this.position = args.position.clone();
this.previousPosition = args.position.clone();
this.mass = args.mass;
this.inverseMass = (args.mass === 0
? 0
: 1 / args.mass);
this.velocity = args.initialVelocity ?? Vector2D.zero;
this.acceleration = Vector2D.zero;
this.isStatic = args.mass === 0;
}
;
clone() {
const clone = new Particle({
id: this.id,
position: this.position.clone(),
shape: this.shape.clone(),
mass: this.mass,
initialVelocity: this.velocity.clone(),
});
clone.previousPosition = this.previousPosition.clone();
return clone;
}
;
applyForce(force) {
if (this.isStatic)
return;
const accelerationFromForce = force.multipliedByScalar(this.inverseMass);
this.acceleration = this.acceleration.addedWithOther(accelerationFromForce);
}
;
update(deltaTime) {
if (this.isStatic)
return;
this.previousPosition = this.position.clone();
// get change in velocity due to acceleration over the time step.
const velocityDelta = this.acceleration.multipliedByScalar(deltaTime);
this.velocity = this.velocity.addedWithOther(velocityDelta);
// get change in position due to velocity over the time step.
const positionDelta = this.velocity.multipliedByScalar(deltaTime);
this.position = this.position.addedWithOther(positionDelta);
}
;
resetAcceleration() {
this.acceleration = Vector2D.zero;
}
;
getSpeed() {
return this.velocity.magnitude;
}
;
getKineticEnergy() {
return 0.5 * this.mass * this.getSpeed() ** 2;
}
;
setPosition(newPosition) {
this.previousPosition = this.position.clone();
this.position = newPosition;
}
;
setVelocity(newVelocity) {
this.velocity = newVelocity;
}
;
setMass(newMass) {
this.mass = newMass;
this.inverseMass = ((newMass === 0)
? 0
: 1 / newMass);
this.isStatic = newMass === 0;
}
;
isCollidingWithOther(other) {
return this.shape.isCollidingWithOther(other.shape);
}
;
computeDistanceToOther(other) {
return this.shape.computeDistanceToOther(other.shape);
}
;
computeOverlapVectorWith(other) {
if (!this.isCollidingWithOther(other)) {
return Vector2D.zero;
}
// Vector from other to this
const direction = this.position.subtractedWithOther(other.position).normalized;
// overlap depth (negative distance means overlap)
const distance = this.computeDistanceToOther(other);
const overlapDepth = -distance;
return direction.multipliedByScalar(overlapDepth);
}
checkIsAtRest() {
const restThreshold = 1e-5;
return this.velocity.magnitude < restThreshold;
}
;
isEdgeToEdgeWithOther(other) {
const distance = this.computeDistanceToOther(other);
const epsilon = 1e-8; // Small tolerance for floating-point precision
return Math.abs(distance) < epsilon && !this.isCollidingWithOther(other);
}
}
;