import { TOOL_INFO_CLASSNAME, WIDE_CLASSNAME, ZOOM_IN_FACTOR, ZOOM_OUT_FACTOR, } from '../config.js'; import {Tool} from '../tool.js'; import {zero} from '../vector.js'; export class Zoom extends Tool { currentScaleEl = undefined; get displayScaleText() { return `Scale: ${this.sim.getScaleDisplay()}`; } setContainer(container) { super.setContainer(container); this.currentScaleEl.innerHTML = this.displayScaleText; this.sim.onZoom(() => { this.currentScaleEl.innerHTML = this.displayScaleText; }); } constructor() { super(); const currentScale = document.createElement('button') const zoomOut = document.createElement('button'); const zoomIn = document.createElement('button'); const zoomAll = document.createElement('button'); const zeroVelocity = document.createElement('button'); this.currentScaleEl = currentScale; this.div.appendChild(currentScale); this.div.appendChild(zoomOut); this.div.appendChild(zoomIn); this.div.appendChild(zoomAll); this.div.appendChild(zeroVelocity); currentScale.classList.add(WIDE_CLASSNAME); currentScale.classList.add(TOOL_INFO_CLASSNAME); zoomOut.innerHTML = 'Zoom
Out'; zoomIn.innerHTML = 'Zoom
In'; zoomAll.innerHTML = 'Zoom to Fit'; zeroVelocity.innerHTML = 'Zero Momentum'; zoomOut.addEventListener('click', () => { // Aim at center of view const x = this.sim.display.width * this.sim.display.scale / 2; const y = this.sim.display.height * this.sim.display.scale / 2; this.sim.scheduleZoom(this.sim.screenToSim(x, y), ZOOM_OUT_FACTOR); }); zoomIn.addEventListener('click', () => { // Aim at center of view const x = this.sim.display.width * this.sim.display.scale / 2; const y = this.sim.display.height * this.sim.display.scale / 2; this.sim.scheduleZoom(this.sim.screenToSim(x, y), ZOOM_IN_FACTOR); }); zoomAll.addEventListener('click', () => { // Determine bounding box const box = this.sim.system.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 ratio = Math.max(widthRatio, heightRatio) * 4; const factor = Math.ceil(Math.log2(1 / ratio)); // Determine average momentum and set panning velocity to match const {netMomentum, totalMass} = this.sim.system.computeSystemCenter(); const netVelocity = { x: netMomentum.x / totalMass, y: netMomentum.y / totalMass, }; this.sim.scheduleZoom({x, y}, factor, netVelocity) }); zeroVelocity.addEventListener('click', () => { // Determine center of mass and average momentum const {totalMass, netMomentum} = this.sim.system.computeSystemCenter(); const netVelocity = { x: netMomentum.x / totalMass, y: netMomentum.y / totalMass, }; // Apply offset to all object velocities this.sim.system.forEachObject(obj => { obj.velocity.x -= netVelocity.x; obj.velocity.y -= netVelocity.y; }); // Cancel panning this.sim.panning.setVelocity(zero); }); } }