gravity/panning.js
Ladd 68464b0460 Implemented adjustable elasticity and adjustable merge threshold
This parameter represents the percentage of energy that will be
conserved in collisions.
0 -> Perfectly inelastic
1 -> Perfectly elastic
2026-01-31 18:22:56 -06:00

112 lines
2.9 KiB
JavaScript

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;
}
}
}