142 lines
4.3 KiB
JavaScript
142 lines
4.3 KiB
JavaScript
import {Tool} from '../tool.js';
|
|
import {
|
|
TOOL_INFO_CLASSNAME,
|
|
WIDE_CLASSNAME,
|
|
} from '../config.js';
|
|
|
|
export class UtilityTool extends Tool {
|
|
currentTimeEl = undefined;
|
|
|
|
get displayScaleText() {
|
|
return `Scale: ${this.sim.getScaleDisplay()}`;
|
|
}
|
|
|
|
get timeText() {
|
|
let time = this.sim.time;
|
|
// Time in milliseconds
|
|
const ms = Math.floor(time % 1000);
|
|
time = (time - ms) / 1000;
|
|
const s = Math.floor(time % 60);
|
|
time = (time - s) / 60;
|
|
const m = Math.floor(time % 60);
|
|
time = (time - m) / 60;
|
|
const h = Math.floor(time % 24);
|
|
time = (time - h) / 24;
|
|
const d = Math.floor(time);
|
|
return [
|
|
d || undefined,
|
|
h.toString().padStart(2, '0'),
|
|
m.toString().padStart(2, '0'),
|
|
[
|
|
s.toString().padStart(2, '0'),
|
|
ms.toString().padStart(3, '0'),
|
|
].join('.')
|
|
].filter(x => x !== undefined).join(':');
|
|
}
|
|
|
|
frame() {
|
|
if (this.currentTimeEl) {
|
|
this.currentTimeEl.innerHTML = this.timeText;
|
|
}
|
|
}
|
|
|
|
constructor(container) {
|
|
super(container);
|
|
|
|
const zeroVelocity = document.createElement('button');
|
|
const clearTraces = document.createElement('button');
|
|
const zoomAll = document.createElement('button');
|
|
const currentScale = document.createElement('button')
|
|
const currentTime = document.createElement('button');
|
|
const clearDebug = document.createElement('button');
|
|
|
|
this.currentTimeEl = currentTime;
|
|
|
|
this.div.appendChild(currentTime);
|
|
this.div.appendChild(currentScale);
|
|
this.div.appendChild(zoomAll);
|
|
this.div.appendChild(zeroVelocity);
|
|
this.div.appendChild(clearTraces);
|
|
this.div.appendChild(clearDebug);
|
|
|
|
zeroVelocity.classList.add(WIDE_CLASSNAME);
|
|
clearTraces.classList.add(WIDE_CLASSNAME);
|
|
zoomAll.classList.add(WIDE_CLASSNAME);
|
|
currentScale.classList.add(WIDE_CLASSNAME);
|
|
currentScale.classList.add(TOOL_INFO_CLASSNAME);
|
|
currentTime.classList.add(TOOL_INFO_CLASSNAME);
|
|
currentTime.classList.add(WIDE_CLASSNAME);
|
|
clearDebug.classList.add(WIDE_CLASSNAME);
|
|
|
|
zeroVelocity.innerHTML = 'Zero Momentum';
|
|
clearTraces.innerHTML = 'Clear Traces';
|
|
zoomAll.innerHTML = 'Zoom to Fit';
|
|
currentScale.innerHTML = this.displayScaleText;
|
|
currentTime.innerHTML = this.timeText;
|
|
clearDebug.innerHTML = 'Clear Debug';
|
|
|
|
this.sim.onZoom(() => {
|
|
currentScale.innerHTML = this.displayScaleText;
|
|
});
|
|
|
|
zeroVelocity.addEventListener('click', () => {
|
|
// Determine center of mass and average momentum
|
|
const { totalMass, netMomentum } = this.sim.objects.computeSystemCenter();
|
|
const netVelocity = {
|
|
x: netMomentum.x / totalMass,
|
|
y: netMomentum.y / totalMass,
|
|
};
|
|
|
|
// Apply offset to all object velocities
|
|
this.sim.objects.forEachObject(obj => {
|
|
obj.velocity.x -= netVelocity.x;
|
|
obj.velocity.y -= netVelocity.y;
|
|
});
|
|
|
|
// Cancel panning
|
|
this.sim.panning = undefined;
|
|
});
|
|
|
|
clearTraces.addEventListener('click', () => {
|
|
// Obliterate object histories
|
|
this.sim.objects.forEachObject(obj => {
|
|
obj.history = [];
|
|
}, {alive: null});
|
|
});
|
|
|
|
zoomAll.addEventListener('click', () => {
|
|
// Determine bounding box
|
|
const box = this.sim.objects.boundingBox;
|
|
const x = (box.start.x + box.end.x) / 2;
|
|
const y = (box.start.y + box.end.y) / 2;
|
|
const widthRatio = Math.abs(box.start.x - box.end.x) / this.sim.display.width;
|
|
const heightRatio = Math.abs(box.start.y - box.end.y) / this.sim.display.height;
|
|
const biggerRatio = Math.max(widthRatio, heightRatio);
|
|
const base2factor = Math.log(1/biggerRatio) / Math.log(2) - 0.5;
|
|
const factor = Math.floor(base2factor);
|
|
// Determine average momentum and set panning velocity to match
|
|
const netMomentum = {x: 0, y: 0};
|
|
let totalMass = 0;
|
|
let count = 0;
|
|
this.sim.objects.forEachObject(obj => {
|
|
count++;
|
|
netMomentum.x += obj.mass * obj.velocity.x;
|
|
netMomentum.y += obj.mass * obj.velocity.y;
|
|
totalMass += obj.mass;
|
|
});
|
|
if (!count) {
|
|
return;
|
|
}
|
|
const netVelocity = {
|
|
x: netMomentum.x / totalMass,
|
|
y: netMomentum.y / totalMass,
|
|
};
|
|
this.sim.scheduleZoom({x, y}, factor, netVelocity)
|
|
});
|
|
|
|
clearDebug.addEventListener('click', () => {
|
|
this.sim.info = {};
|
|
});
|
|
}
|
|
}
|