UNPKG

nbody

Version:

Configure, simulate and visualize n-body interactions

200 lines (199 loc) 6.89 kB
import { RealTimeVisualizer, RealTimeVisualizer3D, RecordingVisualizer, RecordingVisualizer3D, } from './library/Visualizer'; /** * A Simulation object that contains Universes and a Visualizer. * @category Building blocks */ export class Simulation { /** * Create a new Simulation object with the provided Universes and visualization config. * @param universes array of Universes. * @param visType visualization type. * @param record whether to record the simulation. * @param looped whether to loop the recorded simulation. * @param controller controller type. * @param showTrails whether to show trails in the visualization. * @param showDebugInfo whether to show debug info in the visualization. * @param maxFrameRate maximum frame rate of the visualization. * @param maxTrailLength maximum trail for each universe. */ constructor(universes, { visType = '2D', record = false, looped = true, controller = 'none', showTrails = false, showDebugInfo = false, maxFrameRate = -1, maxTrailLength = 100, }) { /** * Controls object used to control the simulation. * @hidden */ this.controls = { speed: 1, paused: false, showTrails: false, showUniverse: {}, }; this.universes = Array.isArray(universes) ? universes : [universes]; if (this.universes.length > 10) { throw new Error('Too many universes'); } const uniqueLabels = new Set(this.universes.map((u) => u.label)); if (uniqueLabels.size !== this.universes.length) { throw new Error('Duplicate label in universes'); } this.controller = controller; this.universes.forEach((u) => { this.controls.showUniverse[u.label] = true; }); this.controls.showTrails = showTrails; this.showDebugInfo = showDebugInfo; this.maxFrameRate = maxFrameRate; this.maxTrailLength = maxTrailLength; this.looped = looped; if (record) { // if (this.universes.length > 1) { // throw new Error("Cannot record multiple universes"); // } this.maxFrameRate = 60; this.visualizer = visType === '2D' ? new RecordingVisualizer(this) : new RecordingVisualizer3D(this); } else { this.visualizer = visType === '2D' ? new RealTimeVisualizer(this) : new RealTimeVisualizer3D(this); } } /** * Get the speed of the simulation. * @returns speed of the simulation as a scale of normal time. */ getSpeed() { return this.controls.speed; } /** * Set the speed of the simulation. Only works if the controller is 'code'. * @param speed speed of the simulation as a scale of normal time. */ setSpeed(speed) { if (this.controller === 'code') { this.controls.speed = speed; } } /** * Get whether the simulation is playing. * @returns true if the simulation is playing. */ isPlaying() { return !this.controls.paused; } /** * Pause the simulation. Only works if the controller is 'code'. */ pause() { if (this.controller === 'code') { this.controls.paused = true; } } /** * Resume the simulation. Only works if the controller is 'code'. */ resume() { if (this.controller === 'code') { this.controls.paused = false; } } /** * Get whether trails are shown in the visualization. * @returns true if trails are shown. */ getShowTrails() { return this.controls.showTrails; } /** * Set whether to show trails in the visualization. Only works if the controller is 'code'. * @param showTrails true to show trails. */ setShowTrails(showTrails) { if (this.controller === 'code') { this.controls.showTrails = showTrails; if (!showTrails) { this.visualizer.clearTrails(); } } } /** * True if the universe with the given label is shown. * @param label universe label. * @returns whether the universe is shown. */ getShowUniverse(label) { return this.controls.showUniverse[label]; } /** * Set whether to show the universe with the given label. Only works if the controller is 'code'. * @param label universe label. * @param show true to show the universe. */ setShowUniverse(label, show) { if (this.controller !== 'code') { return; } for (let i = 0; i < this.universes.length; i++) { if (this.universes[i].label === label) { this.controls.showUniverse[label] = show; if (!show) { this.visualizer.clearTrails(i); } break; } } } /** * Get the maximum trail length used in the visualization. * @returns maximum trail length. */ getMaxTrailLength() { return this.maxTrailLength; } /** * Set the maximum trail length used in the visualization. Changes only apply on the next Simulation.play() call. * @param maxTrailLength maximum trail length. */ setMaxTrailLength(maxTrailLength) { if (this.controller === 'code') { this.maxTrailLength = maxTrailLength; } } /** * Simulates a single step in this simulation. * @param deltaT time step to simulate. * @hidden */ simulateStep(deltaT) { this.universes.forEach((universe) => { universe.simulateStep(deltaT); }); } /** * Insert the simulation visualization in the div with the given id. * @param divId div id. * @param width width of the visualization. * @param height height of the visualization. * @param playSpeed initial time scale. * @param startPaused whether to start the simulation paused. * @param recordFor number of seconds to record for, only used if in record mode. * @param recordSpeed speed of the recording, only used if in record mode. */ start(divId, width, height, playSpeed = 1, startPaused = false, recordFor = 1, recordSpeed = 1) { if (recordFor === undefined) { throw new Error('recordFor must be defined if record is true'); } this.controls.paused = startPaused; this.controls.speed = playSpeed; this.visualizer.start(divId, width, height, recordFor, recordSpeed); } /** * Stop and clear the simulation. */ stop() { this.visualizer.stop(); } }