mode enter/leave callbacks

This commit is contained in:
Ladd 2025-12-27 12:45:42 -06:00
parent 4f88d404d6
commit 28909a0c82
7 changed files with 68 additions and 20 deletions

View File

@ -44,6 +44,10 @@ export const GRAVITATIONAL_CONSTANT = 1E5;
// CSS CLASS NAMES
export const DRAGGABLE_ELEMENT_CLASSNAME = 'lhg-draggable-element';
// EVENT NAMES
export const EVENT_MODE_LEAVE = 'lhg-mode-leave';
export const EVENT_MODE_ENTER = 'lhg-mode-enter';
// MODES
export const MODE_MASS_GENERATION = 'mass-gen';
export const MODE_PAN_VIEW = 'pan-view';

View File

@ -194,7 +194,6 @@ export class Display {
// Draw arrow for the velocity
if (this.sim.getOption('display.velocity')) {
console.log('velocity vector');
const speed = Math.sqrt(vx ** 2 + vy ** 2);
const endVx = x + VELOCITY_VECTOR_SCALE * vx / speed * Math.log(speed);
const endVy = y + VELOCITY_VECTOR_SCALE * vy / speed * Math.log(speed);

View File

@ -98,11 +98,13 @@ export class Objects {
}
objectAtLocation(x, y) {
this.forEachObject(obj => {
let idx = undefined;
this.forEachObject((obj, i) => {
// If distance to object is less than object's radius, we are touching the object
const dist = Math.pow((obj.position.x - x)**2 + (obj.position.y - y)**2, 1/2);
if (dist <= obj.radius) {
return i;
idx = i;
return null;
}
});
}
@ -137,11 +139,13 @@ export class Objects {
}
// cb: (obj, idx) => {}
// TODO: Reducer
forEachObject(cb, alive = true, startWith = 0) {
for (let i = startWith; i < this.objects.length; i++) {
const obj = this.objects[i];
if (alive === null || alive == obj.alive) {
cb(obj, i);
const ret = cb(obj, i);
if (ret === null) break;
}
}
}

View File

@ -90,6 +90,11 @@ export class Pointer {
this.sim.scheduleZoom({x, y}, factor);
});
// When leaving panning mode, clear panning
this.sim.onModeLeave(MODE_PAN_VIEW, () => {
this.panning = undefined;
});
}
getPointerVelocity() {
@ -179,10 +184,6 @@ export class Pointer {
}
// Apply update to viewOrigin based on panning
if (!this.sim.isCurrentMode(MODE_PAN_VIEW)) {
this.panning = undefined;
return;
}
if (this.panning) {
const {pointerStart, pointerCurrent, viewOriginStart, velocity} = this.sim.pointer.panning;
// Convert pointer velocity to sim internal scale

View File

@ -36,7 +36,6 @@ export class Sim {
this.display = new Display(this);
this.overlay = new Overlay(this);
this.objects = new Objects(this);
this.pointer = new Pointer(this);
this.toolbar = new Toolbar(this);
// Set up toolbar
@ -45,6 +44,8 @@ export class Sim {
this.toolbar.addTool(new ModeSwitch(this.toolbar));
this.toolbar.addTool(new Options(this.toolbar));
this.pointer = new Pointer(this);
// Initiate main loop
this.time = document.timeline.currentTime;
requestAnimationFrame(t => this.loop(t));

View File

@ -3,6 +3,8 @@ import { Tool } from '../tool.js';
import {
MODE_MASS_GENERATION,
MODE_PAN_VIEW,
EVENT_MODE_LEAVE,
EVENT_MODE_ENTER,
} from '../config.js';
export class ModeSwitch extends Tool {
@ -16,9 +18,6 @@ export class ModeSwitch extends Tool {
constructor(toolbar) {
super(toolbar);
const [[currentModeID, _]] = this.modes;
this.currentMode = currentModeID;
const modesDiv = document.createElement('div');
const titleDiv = document.createElement('div');
@ -41,18 +40,18 @@ export class ModeSwitch extends Tool {
button.innerHTML = `<h3>${modeTitle}</h3>`;
button.classList.add('wide');
button.addEventListener('click', (e) => {
if (this.currentMode !== modeID) {
this.currentMode = modeID;
this.setModesOpacity();
}
});
button.addEventListener('click', (e) => this.setMode(modeID));
}
this.setModesOpacity();
// First listed mode is the default
const [[currentModeID, _]] = this.modes;
this.setMode(currentModeID);
// Add global method to get current mode / check mode
this.sim.getCurrentMode = () => this.currentMode;
this.sim.isCurrentMode = (modeID) => modeID === this.currentMode;
this.sim.onModeLeave = (modeID, cb) => this.onModeLeave(modeID, cb);
this.sim.onModeEnter = (modeID, cb) => this.onModeEnter(modeID, cb);
}
setModesOpacity() {
@ -61,6 +60,32 @@ export class ModeSwitch extends Tool {
}
}
// TODO: on enter / on leave mode / some sort of callbacks on mode transitions
setMode(modeID) {
if (modeID === this.currentMode) return;
const leave = new CustomEvent(EVENT_MODE_LEAVE, {detail: {modeID: this.currentMode}});
const enter = new CustomEvent(EVENT_MODE_LEAVE, {detail: {modeID}});
this.currentMode = modeID;
this.setModesOpacity();
this.div.dispatchEvent(leave);
this.div.dispatchEvent(enter);
}
// cb: () => {}
onModeLeave(modeID, cb) {
this.div.addEventListener(EVENT_MODE_LEAVE, (e) => {
if (e.detail?.modeID === modeID) {
cb();
}
});
}
// cb: () => {}
onModeEnter(modeID, cb) {
this.div.addEventListener(EVENT_MODE_ENTER, (e) => {
if (e.detail?.modeID === modeID) {
cb();
}
});
}
}

View File

@ -51,6 +51,20 @@ export class Zoom extends Tool {
const factor = Math.floor(base2factor) - 1;
this.sim.scheduleZoom({x, y}, factor);
}
// Determine average velocity and set panning velocity to match
const totalVelocity = {x: 0, y: 0};
let count = 0;
this.sim.objects.forEachObject(obj => {
count++;
totalVelocity.x += obj.velocity.x;
totalVelocity.y += obj.velocity.y;
});
const vx = totalVelocity.x / count;
const vy = totalVelocity.y / count;
console.log('zoom, pan', vx, vy);
this.sim.pointer.panning = {
velocity: {x: vx, y: vy},
};
});
}
}