import {PANNING_ZERO_TOUCH_THRESHOLD} from "./config.js"; import {add, copy, div, mult, zero} from "./vector.js"; export class Panning { sim = undefined; touchStart = undefined; touchLatest = undefined; paused = false; velocity = zero; constructor(sim) { this.sim = sim; } 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, }; } toJSON() { return { velocity: this.velocity, }; } fromJSON({velocity}) { this.velocity = copy(velocity); this.paused = true; } handlePointerDown({x, y}) { this.initializeTouch({x, y}); if (this.paused) { this.paused = false; } } // 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, }; // Convert pointer velocity to simulation scale let velocity = div(this.sim.pointer.latestVelocity, this.sim.display.scale); // Optional time scale compensation if (this.sim.getOption('compensate.timeScale')) { velocity = div(velocity, this.sim.timeScale); } // Additional scaling factor velocity = mult(velocity, this.sim.getOption('display.panningSpeed')); // Add pointer velocity to current panning velocity this.velocity = add(this.velocity, velocity); } } handlePointerUp() { if (this.touchStart && this.touchLatest) { if (this.touchLatest.dt < PANNING_ZERO_TOUCH_THRESHOLD) { this.velocity = zero; } this.touchStart = undefined; } } frame(elapsedTime) { const {display} = this.sim; // 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 display.viewOrigin = add(display.viewOrigin, mult(this.velocity, elapsedTime)); } 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); } } setVelocity(velocity) { this.velocity = velocity; if (!this.sim.playing) { this.paused = true; } } }