target frame rate
This commit is contained in:
parent
d010d19495
commit
debf31e9b0
@ -13,15 +13,16 @@ Screenshots
|
||||
TODO
|
||||
----
|
||||
|
||||
- [ ] Parameter Slider (Invisible, mouse/touch drag)
|
||||
- [ ] Selection Box
|
||||
- [ ] Object List
|
||||
- [ ] Object Detail
|
||||
- [ ] Left Button Panning
|
||||
- [ ] Middle Button Pause
|
||||
- [ ] Parameter Slider (Invisible, mouse/touch drag)
|
||||
- [ ] Grid
|
||||
- [ ] Zoom Easing
|
||||
- [ ] 2-touch Pan & Zoom
|
||||
- Either continuous zoom, or discrete with animation of bounding box proposed changes
|
||||
- [ ] Multi-touch Mass Create
|
||||
- [ ] Camera Rotation
|
||||
- [ ] 2-touch Rotation
|
||||
|
||||
@ -152,6 +152,7 @@ export class Display {
|
||||
|
||||
if (this.sim.panning && !this.sim.panning.paused) {
|
||||
// Apply update to viewOrigin based on panning
|
||||
// TODO: elapsedTime is scaled by time scale, is that what we want?
|
||||
pdx = this.sim.panning.velocity.x * elapsedTime;
|
||||
pdy = this.sim.panning.velocity.y * elapsedTime;
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ export class Options {
|
||||
|
||||
toStored(value) {
|
||||
if (value === undefined) {
|
||||
// TODO: Do we want to interpret this as removing from storage?
|
||||
// Do we want to interpret this as removing from storage?
|
||||
// Let's just treat it as a value for now;
|
||||
// Semantically it works because when retrieved, it will return undefined,
|
||||
// which is the same result you get if the key is not set
|
||||
|
||||
170
pointer.js
170
pointer.js
@ -3,7 +3,6 @@ import {
|
||||
MODE_OBJECT_SELECT,
|
||||
MODE_PAN_VIEW,
|
||||
POINTER_HISTORY_SIZE,
|
||||
POINTER_DOWN_HISTORY_SIZE,
|
||||
TOOLBAR_CLASSNAME,
|
||||
ZOOM_IN_FACTOR,
|
||||
ZOOM_OUT_FACTOR,
|
||||
@ -61,7 +60,7 @@ export class Pointer {
|
||||
getPointerVelocity(points = POINTER_HISTORY_SIZE) {
|
||||
// Average over pointer history
|
||||
if (this.pointerHistory.length < 2) {
|
||||
return this.latestVelocity ?? {x: 0, y: 0, dt: 1};
|
||||
return this.latestVelocity;
|
||||
}
|
||||
points = Math.min(points, POINTER_HISTORY_SIZE, this.pointerHistory.length);
|
||||
const start = this.pointerHistory[this.pointerHistory.length - points];
|
||||
@ -74,8 +73,9 @@ export class Pointer {
|
||||
};
|
||||
}
|
||||
|
||||
// Keep the specified number of entries at the end of the array (most recent)
|
||||
clearPointerHistory(keep = 0) {
|
||||
this.pointerHistory.splice(0, this.pointerHistory.length - keep)
|
||||
this.pointerHistory.splice(keep, this.pointerHistory.length - keep)
|
||||
}
|
||||
|
||||
updatePointer({x, y}) {
|
||||
@ -89,91 +89,123 @@ export class Pointer {
|
||||
|
||||
get latestVelocity() {
|
||||
const latestPointer = this.pointerHistory[this.pointerHistory.length - 1];
|
||||
return latestPointer?.v;
|
||||
}
|
||||
|
||||
handlePointerDown({x: clientX, y: clientY}) {
|
||||
this.clearPointerHistory(POINTER_DOWN_HISTORY_SIZE);
|
||||
this.updatePointer({x: clientX, y: clientY});
|
||||
|
||||
if (this.sim.isCurrentMode(MODE_MASS_GENERATION)) {
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY)
|
||||
this.sim.system.handlePointerDown({x, y});
|
||||
|
||||
} else if (this.sim.isCurrentMode(MODE_PAN_VIEW)) {
|
||||
this.touchStart = {
|
||||
x: clientX,
|
||||
y: clientY,
|
||||
t: this.sim.rawTime,
|
||||
viewOrigin: {...this.sim.display.viewOrigin},
|
||||
};
|
||||
this.touchLatest = {
|
||||
...this.touchStart,
|
||||
dx: 0,
|
||||
dy: 0,
|
||||
dt: 0,
|
||||
};
|
||||
|
||||
} else if (this.sim.isCurrentMode(MODE_OBJECT_SELECT)) {
|
||||
// TODO: Start a selection box
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
...latestPointer?.v
|
||||
}
|
||||
}
|
||||
|
||||
handlePointerUp({x: clientX, y: clientY}) {
|
||||
if (this.sim.isCurrentMode(MODE_MASS_GENERATION)) {
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY);
|
||||
this.sim.system.handlePointerUp({x, y});
|
||||
handlePointerDown({x: clientX, y: clientY}) {
|
||||
// this.clearPointerHistory(POINTER_DOWN_HISTORY_SIZE);
|
||||
this.updatePointer({x: clientX, y: clientY});
|
||||
|
||||
} else if (this.sim.isCurrentMode(MODE_PAN_VIEW)) {
|
||||
// Set panning velocity
|
||||
if (this.touchStart && this.touchLatest) {
|
||||
if (!this.touchLatest.dt) {
|
||||
this.sim.panning = undefined;
|
||||
} else {
|
||||
const v = {...this.latestVelocity};
|
||||
// Convert pointer velocity to simulation scale
|
||||
// Also multiply by -1 because the camera is panning opposite to
|
||||
// the pointer velocity
|
||||
v.x /= -this.sim.display.scale;
|
||||
v.y /= -this.sim.display.scale;
|
||||
|
||||
this.sim.panning = {
|
||||
velocity: v
|
||||
};
|
||||
}
|
||||
|
||||
this.touchStart = undefined;
|
||||
switch (this.sim.getCurrentMode()) {
|
||||
case MODE_MASS_GENERATION: {
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY)
|
||||
this.sim.system.handlePointerDown({x, y});
|
||||
break;
|
||||
}
|
||||
case MODE_PAN_VIEW: {
|
||||
this.touchStart = {
|
||||
x: clientX,
|
||||
y: clientY,
|
||||
t: this.sim.rawTime,
|
||||
viewOrigin: {...this.sim.display.viewOrigin},
|
||||
};
|
||||
// Since we've processed this increment, reset
|
||||
this.touchLatest = {
|
||||
...this.touchStart,
|
||||
dx: 0,
|
||||
dy: 0,
|
||||
dt: 0,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case MODE_OBJECT_SELECT: {
|
||||
// TODO: Start a selection box
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle cursor (mouse or touch) movement
|
||||
// TODO: If e.touches.length > 1, user may be engaging pinch to zoom
|
||||
handlePointerMove({x: clientX, y: clientY}) {
|
||||
// TODO: If e.touches.length > 1, user may be engaging pinch to zoom
|
||||
this.updatePointer({x: clientX, y: clientY});
|
||||
|
||||
if (this.sim.isCurrentMode(MODE_MASS_GENERATION)) {
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY);
|
||||
this.sim.system.handlePointerMove({x, y});
|
||||
switch (this.sim.getCurrentMode()) {
|
||||
case MODE_MASS_GENERATION: {
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY);
|
||||
this.sim.system.handlePointerMove({x, y});
|
||||
break;
|
||||
}
|
||||
case MODE_PAN_VIEW: {
|
||||
if (this.touchStart) {
|
||||
// Event loop should be able to read
|
||||
this.touchLatest = {
|
||||
x: clientX,
|
||||
y: clientY,
|
||||
t: this.sim.rawTime,
|
||||
dx: clientX - this.touchStart.x,
|
||||
dy: clientY - this.touchStart.y,
|
||||
dt: this.sim.rawTime - this.touchStart.t,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MODE_OBJECT_SELECT: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (this.sim.isCurrentMode(MODE_PAN_VIEW)) {
|
||||
if (this.touchStart) {
|
||||
// Event loop should be able to read
|
||||
this.touchLatest = {
|
||||
x: clientX,
|
||||
y: clientY,
|
||||
t: this.sim.rawTime,
|
||||
dx: clientX - this.touchStart.x,
|
||||
dy: clientY - this.touchStart.y,
|
||||
dt: this.sim.rawTime - this.touchStart.t,
|
||||
};
|
||||
handlePointerUp({x: clientX, y: clientY}) {
|
||||
switch (this.sim.getCurrentMode()) {
|
||||
case MODE_MASS_GENERATION: {
|
||||
const {x, y} = this.sim.screenToSim(clientX, clientY);
|
||||
this.sim.system.handlePointerUp({x, y});
|
||||
break;
|
||||
}
|
||||
case MODE_PAN_VIEW: {
|
||||
// Set panning velocity
|
||||
if (this.touchStart && this.touchLatest) {
|
||||
if (!this.touchLatest.dt) {
|
||||
this.sim.panning = undefined;
|
||||
} else {
|
||||
const panning = {...this.latestVelocity};
|
||||
// Convert pointer velocity to simulation scale.
|
||||
// Also multiply by -1 because the camera is
|
||||
// panning opposite to the pointer velocity.
|
||||
panning.x /= -this.sim.display.scale;
|
||||
panning.y /= -this.sim.display.scale;
|
||||
|
||||
if (this.sim.getOption('compensate.timeScale')) {
|
||||
panning.x /= this.sim.timeScale;
|
||||
panning.y /= this.sim.timeScale;
|
||||
}
|
||||
|
||||
// Also add current panning
|
||||
panning.x += this.sim.panning?.velocity.x ?? 0;
|
||||
panning.y += this.sim.panning?.velocity.y ?? 0;
|
||||
|
||||
this.sim.panning = {
|
||||
velocity: panning
|
||||
};
|
||||
}
|
||||
this.touchStart = undefined;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MODE_OBJECT_SELECT: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frame() {
|
||||
// Add another entry for the current pointer position
|
||||
const { pointerHistory } = this.sim.pointer ?? {};
|
||||
const {pointerHistory} = this.sim.pointer ?? {};
|
||||
if (pointerHistory?.length) {
|
||||
const currentPointer = pointerHistory[pointerHistory.length - 1];
|
||||
this.sim.pointer.updatePointer(currentPointer);
|
||||
|
||||
@ -8,12 +8,16 @@ export const simOptions = {
|
||||
dashedTraces: ['Dashed', 'boolean', false, {tall: true}],
|
||||
velocity: ['Velocity Vectors', 'boolean', true],
|
||||
acceleration: ['Accel Vectors', 'boolean', true],
|
||||
velocityScale: ['Velocity<br>Vec Scale', 'number', 80],
|
||||
accelerationScale: ['Accel<br>Vec Scale', 'number', 800],
|
||||
velocityScale: ['Velocity<br>Vec Scale', 'number', 80, {hideUnless: 'display.velocity'}],
|
||||
accelerationScale: ['Accel<br>Vec Scale', 'number', 800, {hideUnless: 'display.acceleration'}],
|
||||
targetFrameRate: ['Target Frame Rate', 'number', 60],
|
||||
},
|
||||
collision: {
|
||||
merge: ['Merge Masses<br>on Collision', 'boolean', true, {wide: true}],
|
||||
},
|
||||
compensate: {
|
||||
timeScale: ['Time Scale Compensator', 'boolean', false, {wide: true}],
|
||||
},
|
||||
param: {
|
||||
gravity: ['Gravity', 'number', 1],
|
||||
timeScale: ['Time Scale', 'number', 0.1],
|
||||
|
||||
77
sim-tools.js
77
sim-tools.js
@ -1,23 +1,39 @@
|
||||
import { Overlay } from './overlay.js';
|
||||
import { Pointer } from './pointer.js';
|
||||
import { ModeSwitch } from './tool/modes.js';
|
||||
import { OptionsTool } from './tool/options.js';
|
||||
import { PlayPause } from './tool/play-pause.js';
|
||||
import { Zoom } from './tool/zoom.js';
|
||||
import { UtilityTool } from './tool/utility.js';
|
||||
import { Toolbar } from './toolbar.js';
|
||||
import { ToolbarGroup } from './toolbar-group.js';
|
||||
import {Overlay} from './overlay.js';
|
||||
import {Pointer} from './pointer.js';
|
||||
import {ModeSwitch} from './tool/modes.js';
|
||||
import {OptionsTool} from './tool/options.js';
|
||||
import {PlayPause} from './tool/play-pause.js';
|
||||
import {UtilityTool} from './tool/utility.js';
|
||||
import {Zoom} from './tool/zoom.js';
|
||||
import {ToolbarGroup} from './toolbar-group.js';
|
||||
import {Toolbar} from './toolbar.js';
|
||||
|
||||
export function initializeTools(sim) {
|
||||
sim.toolbars = {
|
||||
tools: new Toolbar(sim, 'Tools'),
|
||||
modes: new Toolbar(sim, 'Modes'),
|
||||
utils: new Toolbar(sim, 'Utils', { expanded: false }),
|
||||
options: new Toolbar(sim, 'Options'),
|
||||
params: new Toolbar(sim, 'Params'),
|
||||
debug: new Toolbar(sim, 'Debug', { expanded: false }),
|
||||
tools: new Toolbar(sim, 'Tools')
|
||||
.addTool(new Zoom())
|
||||
.addTool(new PlayPause()),
|
||||
modes: new Toolbar(sim, 'Modes')
|
||||
.addTool(new ModeSwitch()),
|
||||
utils: new Toolbar(sim, 'Utils', {expanded: false})
|
||||
.addTool(new UtilityTool()),
|
||||
options: new Toolbar(sim, 'Options')
|
||||
.addTool(new OptionsTool([
|
||||
'pauseDuring',
|
||||
'display',
|
||||
'collision',
|
||||
'compensate',
|
||||
])),
|
||||
params: new Toolbar(sim, 'Params')
|
||||
.addTool(new OptionsTool([
|
||||
'param'
|
||||
])),
|
||||
debug: new Toolbar(sim, 'Debug', {expanded: false})
|
||||
.addTool(new OptionsTool([
|
||||
'debug'
|
||||
])),
|
||||
};
|
||||
const { tools, modes, options, params, debug, utils } = sim.toolbars;
|
||||
const {tools, modes, options, params, debug, utils} = sim.toolbars;
|
||||
sim.toolbarGroups = {
|
||||
left: new ToolbarGroup(sim)
|
||||
.addToolbar(tools)
|
||||
@ -31,35 +47,6 @@ export function initializeTools(sim) {
|
||||
sim.overlay = new Overlay(sim);
|
||||
sim.pointer = new Pointer(sim);
|
||||
|
||||
// Configure toolbars
|
||||
|
||||
// Primary
|
||||
tools.addTool(new Zoom(tools));
|
||||
tools.addTool(new PlayPause(tools));
|
||||
|
||||
// Secondary
|
||||
modes.addTool(new ModeSwitch(modes));
|
||||
|
||||
// Utility
|
||||
utils.addTool(new UtilityTool(utils));
|
||||
|
||||
// Options
|
||||
options.addTool(new OptionsTool(options, [
|
||||
'pauseDuring',
|
||||
'display',
|
||||
'collision'
|
||||
]));
|
||||
|
||||
// Parameters
|
||||
params.addTool(new OptionsTool(params, [
|
||||
'param'
|
||||
]));
|
||||
|
||||
// Debug
|
||||
debug.addTool(new OptionsTool(debug, [
|
||||
'debug'
|
||||
]));
|
||||
|
||||
for (const id in sim.toolbars) {
|
||||
const toolbar = sim.toolbars[id];
|
||||
toolbar.applyExpanded();
|
||||
|
||||
39
simulator.js
39
simulator.js
@ -17,8 +17,8 @@ export class Sim {
|
||||
nextZoom = undefined;
|
||||
playing = true;
|
||||
recentFrames = [];
|
||||
frameRate = 0;
|
||||
panning = undefined;
|
||||
frameRate = 0;
|
||||
|
||||
system = undefined;
|
||||
display = undefined;
|
||||
@ -57,16 +57,26 @@ export class Sim {
|
||||
|
||||
markFrame(t) {
|
||||
const { recentFrames: rfs } = this;
|
||||
rfs.push(t);
|
||||
if (rfs.length < 2) return;
|
||||
const oldest = rfs[0];
|
||||
const newest = rfs[rfs.length - 1];
|
||||
const count = rfs.length;
|
||||
const duration = (newest - oldest);
|
||||
this.frameRate = 1000 * count / duration; // Converting from ms to s
|
||||
if (duration >= FRAMERATE_SAMPLE_DURATION) {
|
||||
rfs.shift();
|
||||
if (!rfs.length) {
|
||||
rfs.push(t);
|
||||
return;
|
||||
}
|
||||
let oldest = rfs[0];
|
||||
let duration = t - oldest;
|
||||
const count = rfs.length + 1;
|
||||
const frameRate = 1000 * count / duration;
|
||||
const targetFrameRate = parseInt(this.getOption('display.targetFrameRate'));
|
||||
if (frameRate > targetFrameRate + 1) {
|
||||
return true;
|
||||
}
|
||||
this.frameRate = frameRate;
|
||||
rfs.push(t);
|
||||
while (duration >= FRAMERATE_SAMPLE_DURATION) {
|
||||
rfs.shift();
|
||||
oldest = rfs[0];
|
||||
duration = t - oldest;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// velocity should be in Sim coordinate scale
|
||||
@ -102,7 +112,12 @@ export class Sim {
|
||||
|
||||
// Main loop
|
||||
loop(currentTime) {
|
||||
this.markFrame(currentTime);
|
||||
const early = this.markFrame(currentTime);
|
||||
if (early) {
|
||||
// Slow down :)
|
||||
requestAnimationFrame(t => this.loop(t));
|
||||
return;
|
||||
}
|
||||
this.timeScale = this.getOption('param.timeScale');
|
||||
|
||||
const elapsedTime = (currentTime - this.rawTime) * this.timeScale;
|
||||
@ -117,7 +132,7 @@ export class Sim {
|
||||
}
|
||||
|
||||
if (this.getOption('debug.frameRate')) {
|
||||
this.info['Frame Rate'] = this.frameRate?.toPrecision(3);
|
||||
this.info['Frame Rate'] = this.frameRate.toPrecision(3);
|
||||
}
|
||||
|
||||
this.zoom.frame();
|
||||
|
||||
12
system.js
12
system.js
@ -155,10 +155,16 @@ export class System {
|
||||
this.doneCreatingObject();
|
||||
this.deselect();
|
||||
// Convert pointer velocity to simulation scale
|
||||
// Including time scale - if time is slow, our motion is relatively faster
|
||||
const pointer = {...this.sim.pointer.latestVelocity};
|
||||
obj.velocity.x = pointer.x / this.sim.display.scale / this.sim.timeScale;
|
||||
obj.velocity.y = pointer.y / this.sim.display.scale / this.sim.timeScale;
|
||||
obj.velocity.x = pointer.x / this.sim.display.scale;
|
||||
obj.velocity.y = pointer.y / this.sim.display.scale;
|
||||
|
||||
// Including time scale - if time is slow, our motion is relatively faster
|
||||
if (this.sim.getOption('compensate.timeScale')) {
|
||||
obj.velocity.x /= this.sim.timeScale;
|
||||
obj.velocity.y /= this.sim.timeScale;
|
||||
}
|
||||
|
||||
if (this.sim.panning?.velocity) {
|
||||
obj.velocity.x += this.sim.panning.velocity.x;
|
||||
obj.velocity.y += this.sim.panning.velocity.y;
|
||||
|
||||
9
tool.js
9
tool.js
@ -10,13 +10,16 @@ export class Tool {
|
||||
container = undefined;
|
||||
sim = undefined;
|
||||
|
||||
constructor(container) {
|
||||
this.container = container;
|
||||
this.sim = this.container.sim;
|
||||
constructor() {
|
||||
const div = document.createElement('div');
|
||||
this.div = div;
|
||||
div.classList.add(TOOL_CLASSNAME)
|
||||
}
|
||||
|
||||
setContainer(container) {
|
||||
this.container = container;
|
||||
this.sim = this.container.sim;
|
||||
}
|
||||
|
||||
frame() {}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ import {TOOLBAR_HEADER_CLASSNAME} from '../config.js';
|
||||
import { Tool } from '../tool.js';
|
||||
|
||||
export class Header extends Tool {
|
||||
|
||||
constructor(container, title = 'Tools') {
|
||||
super(container);
|
||||
this.title = document.createElement('h1');
|
||||
@ -10,7 +9,6 @@ export class Header extends Tool {
|
||||
|
||||
this.toggleButton = document.createElement('button');
|
||||
this.div.addEventListener('click', () => this.toggle());
|
||||
this.updateButton();
|
||||
|
||||
this.div.appendChild(this.title);
|
||||
this.div.appendChild(this.toggleButton);
|
||||
@ -22,6 +20,11 @@ export class Header extends Tool {
|
||||
this.div.classList.add(TOOLBAR_HEADER_CLASSNAME);
|
||||
}
|
||||
|
||||
setContainer(container) {
|
||||
super.setContainer(container);
|
||||
this.updateButton();
|
||||
}
|
||||
|
||||
updateButton() {
|
||||
this.toggleButton.innerHTML = this.container.expanded ? '˅' : '˄';
|
||||
}
|
||||
|
||||
@ -18,6 +18,21 @@ export class ModeSwitch extends Tool {
|
||||
];
|
||||
buttons = [];
|
||||
|
||||
setContainer(container) {
|
||||
super.setContainer(container);
|
||||
|
||||
// First listed mode is the default
|
||||
const [[currentModeID, ]] = this.modes;
|
||||
this.setCurrentMode(currentModeID);
|
||||
|
||||
// Add global method to set/get current mode
|
||||
this.sim.setCurrentMode = (modeID) => this.setCurrentMode(modeID);
|
||||
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);
|
||||
}
|
||||
|
||||
constructor(container) {
|
||||
super(container);
|
||||
|
||||
@ -42,17 +57,6 @@ export class ModeSwitch extends Tool {
|
||||
|
||||
button.addEventListener('click', () => this.setCurrentMode(modeID));
|
||||
}
|
||||
|
||||
// First listed mode is the default
|
||||
const [[currentModeID, ]] = this.modes;
|
||||
this.setCurrentMode(currentModeID);
|
||||
|
||||
// Add global method to set/get current mode
|
||||
this.sim.setCurrentMode = (modeID) => this.setCurrentMode(modeID);
|
||||
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() {
|
||||
|
||||
@ -8,10 +8,16 @@ import {
|
||||
import { Tool } from '../tool.js';
|
||||
|
||||
export class OptionsTool extends Tool {
|
||||
constructor(container, sections) {
|
||||
super(container);
|
||||
sections = undefined;
|
||||
|
||||
for (const sectionName of sections) {
|
||||
constructor(sections) {
|
||||
super();
|
||||
this.sections = sections;
|
||||
}
|
||||
|
||||
setContainer(container) {
|
||||
super.setContainer(container);
|
||||
for (const sectionName of this.sections) {
|
||||
const option = this.sim.options.getSection(sectionName);
|
||||
const child = this.visitItem(option);
|
||||
this.div.appendChild(child);
|
||||
|
||||
@ -14,6 +14,11 @@ export class PlayPause extends Tool {
|
||||
this.playButton.style.opacity = this.sim.playing ? '50%' : '100%';
|
||||
}
|
||||
|
||||
setContainer(container) {
|
||||
super.setContainer(container);
|
||||
this.updateButtons();
|
||||
}
|
||||
|
||||
constructor(container) {
|
||||
super(container);
|
||||
|
||||
@ -32,8 +37,6 @@ export class PlayPause extends Tool {
|
||||
playButton.classList.add(TALL_CLASSNAME);
|
||||
pauseButton.classList.add(TALL_CLASSNAME);
|
||||
|
||||
this.updateButtons();
|
||||
|
||||
pauseButton.addEventListener('click', () => {
|
||||
this.sim.panning = undefined;
|
||||
if (this.sim.playing) {
|
||||
|
||||
@ -36,6 +36,11 @@ export class UtilityTool extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
setContainer(container) {
|
||||
super.setContainer(container);
|
||||
this.currentTimeEl.innerHTML = this.timeText;
|
||||
}
|
||||
|
||||
constructor(container) {
|
||||
super(container);
|
||||
|
||||
@ -55,7 +60,6 @@ export class UtilityTool extends Tool {
|
||||
clearDebug.classList.add(WIDE_CLASSNAME);
|
||||
|
||||
clearTraces.innerHTML = 'Clear Traces';
|
||||
currentTime.innerHTML = this.timeText;
|
||||
clearDebug.innerHTML = 'Clear Debug';
|
||||
|
||||
clearTraces.addEventListener('click', () => {
|
||||
|
||||
19
tool/zoom.js
19
tool/zoom.js
@ -7,10 +7,22 @@ import {
|
||||
} from '../config.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(container) {
|
||||
super(container);
|
||||
|
||||
@ -20,6 +32,8 @@ export class Zoom extends Tool {
|
||||
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);
|
||||
@ -29,16 +43,11 @@ export class Zoom extends Tool {
|
||||
currentScale.classList.add(WIDE_CLASSNAME);
|
||||
currentScale.classList.add(TOOL_INFO_CLASSNAME);
|
||||
|
||||
currentScale.innerHTML = this.displayScaleText;
|
||||
zoomOut.innerHTML = 'Zoom<br>Out';
|
||||
zoomIn.innerHTML = 'Zoom<br>In';
|
||||
zoomAll.innerHTML = 'Zoom to Fit';
|
||||
zeroVelocity.innerHTML = 'Zero Momentum';
|
||||
|
||||
this.sim.onZoom(() => {
|
||||
currentScale.innerHTML = this.displayScaleText;
|
||||
});
|
||||
|
||||
zoomOut.addEventListener('click', () => {
|
||||
// Aim at center of view
|
||||
const x = this.sim.display.width * this.sim.display.scale / 2;
|
||||
|
||||
@ -38,6 +38,7 @@ export class Toolbar {
|
||||
// tool: instance of Tool
|
||||
addTool(tool) {
|
||||
this.div.appendChild(tool.div);
|
||||
tool.setContainer(this);
|
||||
this.tools.push(tool);
|
||||
return this;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user