import {add, copy, div, mult, sub, zero} from "./vector.js"; export class Panning { sim = undefined; touchStart = undefined; touchLatest = undefined; paused = false; velocity = zero; constructor(sim) { this.sim = sim; } handlePointerDown({x, y}) { this.initializeTouch({x, y}); } initializeTouch({x, y}) { this.touchStart = { x, y, t: this.sim.rawTime, viewOrigin: copy(this.sim.display.viewOrigin), }; this.touchLatest = { ...this.touchStart, dx: 0, dy: 0, dt: 0, }; } // With fast panning, panning velocity calculation happens every move; // With normal panning, calculation only happens at pointer up. handlePointerMove({x, y}) { if (this.touchStart) { this.touchLatest = { x, y, t: this.sim.rawTime, dx: x - this.touchStart.x, dy: x - this.touchStart.y, dt: this.sim.rawTime - this.touchStart.t, }; if (this.sim.getOption('compensate.fastPanning')) { this.updateVelocity(); } } } handlePointerUp() { if (this.touchStart && this.touchLatest) { if (this.touchLatest.dt === 0) { this.velocity = zero; } this.touchStart = undefined; if (this.sim.getOption('compensate.fastPanning')) { this.velocity = zero; } else { this.updateVelocity(); } } } frame(elapsedTime) { const {touchStart: start, touchLatest: latest} = this; const {display} = this.sim; // Direct translate, unless using fast panning if (start && latest && !this.sim.getOption('compensate.fastPanning')) { // start and latest are in screen coordinates, need to convert to sim scale const delta = div(sub(latest, start), display.scale); display.viewOrigin = sub(start.viewOrigin, delta); } // Apply update to viewOrigin based on panning if (!this.paused) { // elapsedTime is scaled by time scale, is that what we want? // Yes because if panning.velocity == obj.velocity, object should stay in view const delta = mult(this.velocity, elapsedTime); display.viewOrigin = add(display.viewOrigin, delta); } // Update what's considered start if (start && latest) { this.initializeTouch(this.touchLatest); } if (this.sim.getOption('debug.panningInfo')) { const {x, y} = this.sim.panning?.velocity ?? {}; this.sim.info['Panning Velocity'] = [`${x?.toPrecision(6)}, `, y?.toPrecision(6)]; const {centerOfMass} = this.sim.system.computeSystemCenter(); this.sim.info['Center of Mass'] = [`${centerOfMass.x.toPrecision(6)}, `, centerOfMass.y.toPrecision(6)]; this.sim.info['Net Angular Momentum'] = this.sim.system.computeSystemAngularMomentum().toPrecision(6); } } updateVelocity() { // Convert pointer velocity to simulation scale, and multiply by -1 // because the camera is panning opposite to the pointer velocity. let velocity = div(this.sim.pointer.latestVelocity, -this.sim.display.scale); if (this.sim.getOption('compensate.timeScale')) { velocity = div(velocity, this.sim.timeScale); } // Also add current panning velocity = add(velocity, this.velocity); this.velocity = velocity; } setVelocity(velocity) { this.velocity = velocity; if (!this.sim.playing) { this.paused = true; } } }