UNPKG

@dominicstop/utils

Version:

Yet another event emitter written in typescript.

139 lines (109 loc) 3.53 kB
import { Rect } from "src/geometry"; import { CircleParticle } from "./ParticleShapes"; import { PhysicsEngine } from "./PhysicsEngine"; import { SomeOfflineSimulation } from "./SomeOfflineSimulation"; import { SomeParticleForce } from "./SomeParticleForce"; import { SomeSystemForce } from "./SomeSystemForce"; import { CentralAttractionForce } from "./CentralAttractionForce"; export class CirclePackingOfflineSimulation implements SomeOfflineSimulation<CircleParticle> { engine: PhysicsEngine<CircleParticle>; isRunning: boolean = false; shouldLog = false; private _totalSimulatedTime: number = 0; private _iterationCount: number = 0; readonly fixedDeltaTimeSeconds: number; minIterations: number = 50; maxIterations: number = 250; onPreStep: ((deltaTime: number) => void) | null = null; onPostStep: ((deltaTime: number) => void) | null = null; onUpdate: (() => void) | null = null; get totalSimulatedTime(): number { return this._totalSimulatedTime; } get iterationCount(): number { return this._iterationCount; }; get frameTimeMS(): number { return this.minIterations * 1000; }; constructor(args: { frame: Rect; centralAttractionStrength?: number; timeStep?: number }) { this.fixedDeltaTimeSeconds = args.timeStep ?? (1 / 60); const engine = new PhysicsEngine<CircleParticle>(); this.engine = engine; const centerForce = new CentralAttractionForce( args.frame.center.asVector, args.centralAttractionStrength ?? 8, true ); engine.addForce(centerForce); } run() { this.isRunning = true; this._iterationCount = 0; while (true) { const didReachMaxIterations = this._iterationCount >= this.maxIterations; if(!this.isRunning || didReachMaxIterations){ this.isRunning = false; break; }; this.step(); this._iterationCount++; this.onUpdate?.(); const didReachMinIterations = this._iterationCount > this.minIterations; const shouldStop = didReachMinIterations && this.isStable(); if (shouldStop) { this.isRunning = false; } } }; step() { this.onPreStep?.(this.fixedDeltaTimeSeconds); this.engine.update(this.fixedDeltaTimeSeconds); this.onPostStep?.(this.fixedDeltaTimeSeconds); this._totalSimulatedTime += this.fixedDeltaTimeSeconds; if(this.shouldLog){ console.log({ 'isRunning': this.isRunning, 'iterationCount': this._iterationCount, 'totalSimulatedTime': this._totalSimulatedTime, 'getTotalKineticEnergy': this.engine.getTotalKineticEnergy(), 'isKineticEnergyNegligible': this.isKineticEnergyNegligible(), 'checkIfAllParticlesAtRest': this.engine.checkIfAllParticlesAtRest(), 'this.isStable': this.isStable(), }); this.engine.logState(); }; }; reset() { this.isRunning = false; this.engine.clear(); this._totalSimulatedTime = 0; this._iterationCount = 0; }; addParticle(particle: CircleParticle) { this.engine.addParticle(particle); }; addForce(force: SomeParticleForce | SomeSystemForce) { this.engine.addForce(force); }; isKineticEnergyNegligible(): boolean { const energyThreshold = 0.01; return this.engine.getTotalKineticEnergy() < energyThreshold; }; isStable(): boolean { return ( this.isKineticEnergyNegligible() || this.engine.checkIfAllParticlesAtRest() ); }; };