separate parameters section
This commit is contained in:
parent
56414a7ca1
commit
1b14843070
@ -8,8 +8,6 @@ Uses `npm` for `eslint`.
|
|||||||
TODO
|
TODO
|
||||||
----
|
----
|
||||||
|
|
||||||
- [x] Numeric Option Type
|
|
||||||
- [x] Time Indicator
|
|
||||||
- [ ] Selection Box
|
- [ ] Selection Box
|
||||||
- [ ] Object List
|
- [ ] Object List
|
||||||
- [ ] Object Detail
|
- [ ] Object Detail
|
||||||
@ -19,9 +17,11 @@ TODO
|
|||||||
- [ ] Zoom Easing
|
- [ ] Zoom Easing
|
||||||
- [ ] 2-touch Pan & Zoom
|
- [ ] 2-touch Pan & Zoom
|
||||||
- [ ] Multi-touch Mass Create
|
- [ ] Multi-touch Mass Create
|
||||||
- [x] Tool to Clear Traces
|
|
||||||
- [ ] Undo feature:
|
- [ ] Undo feature:
|
||||||
- [ ] Undo "Clear Traces" Action
|
- [ ] Undo "Clear Traces" Action
|
||||||
- [ ] Undo "Reset
|
- [ ] Undo "Reset
|
||||||
- [ ] Save to LocalStorage
|
- [ ] Save to LocalStorage
|
||||||
- [ ] Lossy Rescaling To Widen Zoom (Handling overflow/underflow)
|
- [ ] Lossy Rescaling To Widen Zoom (Handling overflow/underflow)
|
||||||
|
- [ ] Track farthest reaches, min/max in each dimension (x, y)
|
||||||
|
- [ ] Enabling Zoom to Fit Traces
|
||||||
|
- [ ] Tool: Zero Angular Momentum
|
||||||
|
|||||||
10
commit
10
commit
@ -1,15 +1,11 @@
|
|||||||
#!/bin/env bash
|
#!/bin/env bash
|
||||||
|
|
||||||
if [[ $(hostname) != "ladd76" ]]; then
|
|
||||||
echo >&2 "host $(hostname) != 'ladd76'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
git checkout dev
|
git checkout dev
|
||||||
|
|
||||||
./sync
|
if [[ $(hostname) == "ladd76" ]]; then
|
||||||
|
./sync
|
||||||
|
fi
|
||||||
|
|
||||||
if git diff; then
|
if git diff; then
|
||||||
git add .
|
git add .
|
||||||
|
|||||||
@ -56,6 +56,7 @@ export const WIDE_CLASSNAME = 'lhg-wide';
|
|||||||
export const EVENT_MODE_LEAVE = 'lhg-mode-leave';
|
export const EVENT_MODE_LEAVE = 'lhg-mode-leave';
|
||||||
export const EVENT_MODE_ENTER = 'lhg-mode-enter';
|
export const EVENT_MODE_ENTER = 'lhg-mode-enter';
|
||||||
export const EVENT_ZOOM = 'lhg-zoom-event';
|
export const EVENT_ZOOM = 'lhg-zoom-event';
|
||||||
|
export const EVENT_OPTION_SET = 'lhg-option-set';
|
||||||
|
|
||||||
// MODES
|
// MODES
|
||||||
export const MODE_MASS_GENERATION = 'mass-gen';
|
export const MODE_MASS_GENERATION = 'mass-gen';
|
||||||
|
|||||||
90
options.js
Normal file
90
options.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import {
|
||||||
|
DISPLAY_ACCELERATION_VECTORS,
|
||||||
|
DISPLAY_VELOCITY_VECTORS,
|
||||||
|
GRAVITATIONAL_CONSTANT,
|
||||||
|
MASS_CREATION_RATE,
|
||||||
|
MERGE_ON_COLLIDE,
|
||||||
|
MOTION_TIME_SCALE,
|
||||||
|
PATH_TRACES_DASHED,
|
||||||
|
PAUSE_DURING_CREATION,
|
||||||
|
PAUSE_DURING_SELECTION,
|
||||||
|
EVENT_OPTION_SET,
|
||||||
|
} from './config.js';
|
||||||
|
|
||||||
|
export const optionsLayout = {
|
||||||
|
pauseDuring: {
|
||||||
|
creation: ['Pause While Creating', 'boolean', PAUSE_DURING_CREATION],
|
||||||
|
creation2: ['Pause While Creating', 'boolean', PAUSE_DURING_CREATION],
|
||||||
|
selection: ['Pause While Selecting', 'boolean', PAUSE_DURING_SELECTION],
|
||||||
|
},
|
||||||
|
display: {
|
||||||
|
velocity: ['Velocity Vectors', 'boolean', DISPLAY_VELOCITY_VECTORS],
|
||||||
|
acceleration: ['Accel. Vectors', 'boolean', DISPLAY_ACCELERATION_VECTORS],
|
||||||
|
traces: ['Path Traces', 'boolean', DISPLAY_ACCELERATION_VECTORS],
|
||||||
|
dashedTraces: ['Dashed Traces', 'boolean', PATH_TRACES_DASHED],
|
||||||
|
},
|
||||||
|
collision: {
|
||||||
|
merge: ['Merge Masses<br>on Collision', 'boolean', MERGE_ON_COLLIDE, {wide: true}],
|
||||||
|
},
|
||||||
|
param: {
|
||||||
|
gravity: ['Gravity', 'number', GRAVITATIONAL_CONSTANT],
|
||||||
|
timeScale: ['Time Scale', 'number', MOTION_TIME_SCALE],
|
||||||
|
massCreationRate: ['Mass Creation Rate', 'number', MASS_CREATION_RATE],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Options {
|
||||||
|
sim = undefined;
|
||||||
|
values = {};
|
||||||
|
|
||||||
|
constructor(sim) {
|
||||||
|
this.sim = sim;
|
||||||
|
|
||||||
|
// Global methods to get/set current option values
|
||||||
|
this.sim.getOption = (path) => this.getOption(path);
|
||||||
|
this.sim.setOption = (path, value) => this.setOption(path, value);
|
||||||
|
this.sim.onOptionSet = (path, cb) => this.onOptionSet(path, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
getOption(path) {
|
||||||
|
const val = this.values[path];
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOption(path, value) {
|
||||||
|
this.values[path] = value;
|
||||||
|
const e = new CustomEvent(EVENT_OPTION_SET, {detail: {path, value}});
|
||||||
|
this.sim.div.dispatchEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cb: (value) => undefined
|
||||||
|
onOptionSet(path, cb) {
|
||||||
|
this.sim.div.addEventListener(EVENT_OPTION_SET, (e) => {
|
||||||
|
if (path === e.detail.path) {
|
||||||
|
cb(e.detail.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSection(layout, sectionName) {
|
||||||
|
const section = layout[sectionName];
|
||||||
|
const group = {
|
||||||
|
type: 'group',
|
||||||
|
name: sectionName,
|
||||||
|
title: section._title,
|
||||||
|
items: [],
|
||||||
|
};
|
||||||
|
for (const name in section) {
|
||||||
|
if (name.startsWith('_')) continue;
|
||||||
|
const [title, type, defaultValue, opts] = section[name];
|
||||||
|
group.items.push({
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
title,
|
||||||
|
default: defaultValue,
|
||||||
|
...opts
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ import {
|
|||||||
MODE_OBJECT_SELECT,
|
MODE_OBJECT_SELECT,
|
||||||
MODE_PAN_VIEW,
|
MODE_PAN_VIEW,
|
||||||
POINTER_HISTORY_SIZE,
|
POINTER_HISTORY_SIZE,
|
||||||
TOOL_CLASSNAME,
|
TOOLBAR_CLASSNAME,
|
||||||
ZOOM_IN_FACTOR,
|
ZOOM_IN_FACTOR,
|
||||||
ZOOM_OUT_FACTOR,
|
ZOOM_OUT_FACTOR,
|
||||||
} from './config.js';
|
} from './config.js';
|
||||||
@ -33,7 +33,7 @@ export class Pointer {
|
|||||||
|
|
||||||
el.addEventListener('pointerdown', e => {
|
el.addEventListener('pointerdown', e => {
|
||||||
let target = e.target;
|
let target = e.target;
|
||||||
while (target && !target.classList?.contains(TOOL_CLASSNAME)) {
|
while (target && !target.classList?.contains(TOOLBAR_CLASSNAME)) {
|
||||||
target = target.parentNode;
|
target = target.parentNode;
|
||||||
}
|
}
|
||||||
if (target) {
|
if (target) {
|
||||||
|
|||||||
66
simulator.js
66
simulator.js
@ -5,15 +5,17 @@ import {
|
|||||||
SCALE_POWER_MAX,
|
SCALE_POWER_MAX,
|
||||||
SCALE_POWER_MIN,
|
SCALE_POWER_MIN,
|
||||||
} from './config.js';
|
} from './config.js';
|
||||||
import {Display} from './display.js';
|
import { Display } from './display.js';
|
||||||
import {Objects} from './objects.js';
|
import { Objects } from './objects.js';
|
||||||
import {Overlay} from './overlay.js';
|
import { Overlay } from './overlay.js';
|
||||||
import {Pointer} from './pointer.js';
|
import { Pointer } from './pointer.js';
|
||||||
import {ModeSwitch} from './tool/modes.js';
|
import { Options } from './options.js';
|
||||||
import {Options} from './tool/options.js';
|
import { ModeSwitch } from './tool/modes.js';
|
||||||
import {PlayPause} from './tool/play-pause.js';
|
import { OptionsTool } from './tool/options.js';
|
||||||
import {Zoom} from './tool/zoom.js';
|
import { PlayPause } from './tool/play-pause.js';
|
||||||
import {Toolbar} from './toolbar.js';
|
import { Zoom } from './tool/zoom.js';
|
||||||
|
import { Toolbar } from './toolbar.js';
|
||||||
|
import { ToolbarGroup } from './toolbar-group.js';
|
||||||
|
|
||||||
export class Sim {
|
export class Sim {
|
||||||
info = {};
|
info = {};
|
||||||
@ -26,8 +28,8 @@ export class Sim {
|
|||||||
overlay = undefined;
|
overlay = undefined;
|
||||||
pointer = undefined;
|
pointer = undefined;
|
||||||
objects = undefined;
|
objects = undefined;
|
||||||
toolbar = undefined;
|
toolbars = {};
|
||||||
toolbar2 = undefined;
|
toolbarGroups = {};
|
||||||
|
|
||||||
isCurrentMode = () => undefined;
|
isCurrentMode = () => undefined;
|
||||||
getCurrentMode = () => undefined;
|
getCurrentMode = () => undefined;
|
||||||
@ -41,23 +43,37 @@ export class Sim {
|
|||||||
const div = document.getElementById(this.divId);
|
const div = document.getElementById(this.divId);
|
||||||
this.div = div;
|
this.div = div;
|
||||||
|
|
||||||
|
this.options = new Options(this);
|
||||||
this.display = new Display(this);
|
this.display = new Display(this);
|
||||||
this.objects = new Objects(this);
|
this.objects = new Objects(this);
|
||||||
this.toolbar = new Toolbar(this, 'Tools');
|
this.toolbarGroups = {
|
||||||
this.toolbar2 = new Toolbar(this, 'Options');
|
left: new ToolbarGroup(this),
|
||||||
|
right: new ToolbarGroup(this).topRight(),
|
||||||
|
};
|
||||||
|
this.toolbars = {
|
||||||
|
tools: new Toolbar(this, 'Tools', this.toolbarGroups.left),
|
||||||
|
modes: new Toolbar(this, 'Modes', this.toolbarGroups.left),
|
||||||
|
options: new Toolbar(this, 'Options', this.toolbarGroups.right),
|
||||||
|
params: new Toolbar(this, 'Parameters', this.toolbarGroups.right),
|
||||||
|
}
|
||||||
this.overlay = new Overlay(this);
|
this.overlay = new Overlay(this);
|
||||||
|
|
||||||
// Set up toolbar
|
|
||||||
this.toolbar.addTool(new Zoom(this.toolbar));
|
|
||||||
this.toolbar.addTool(new PlayPause(this.toolbar));
|
|
||||||
this.toolbar.addTool(new ModeSwitch(this.toolbar));
|
|
||||||
|
|
||||||
// Set up second toolbar
|
|
||||||
this.toolbar2.topRight();
|
|
||||||
this.toolbar2.addTool(new Options(this.toolbar));
|
|
||||||
|
|
||||||
this.pointer = new Pointer(this);
|
this.pointer = new Pointer(this);
|
||||||
|
|
||||||
|
// Primary Toolbar
|
||||||
|
this.toolbars.tools.addTool(new Zoom(this.toolbars.tools));
|
||||||
|
this.toolbars.tools.addTool(new PlayPause(this.toolbars.tools));
|
||||||
|
|
||||||
|
// Secondary Toolbar; Mode Switches
|
||||||
|
this.toolbars.modes.addTool(new ModeSwitch(this.toolbars.modes));
|
||||||
|
|
||||||
|
// Options Toolbar
|
||||||
|
this.toolbars.options.addTool(new OptionsTool(this.toolbars.options,
|
||||||
|
['pauseDuring', 'display', 'collision']));
|
||||||
|
|
||||||
|
// Parameters Toolbar
|
||||||
|
this.toolbars.params.addTool(new OptionsTool(this.toolbars.options,
|
||||||
|
['param']));
|
||||||
|
|
||||||
// Initiate main loop
|
// Initiate main loop
|
||||||
this.rawTime = document.timeline.currentTime / 1000;
|
this.rawTime = document.timeline.currentTime / 1000;
|
||||||
this.time = 0;
|
this.time = 0;
|
||||||
@ -151,7 +167,9 @@ export class Sim {
|
|||||||
this.overlay.renderInfo();
|
this.overlay.renderInfo();
|
||||||
this.display.fillCanvas();
|
this.display.fillCanvas();
|
||||||
this.display.drawObjects();
|
this.display.drawObjects();
|
||||||
this.toolbar.frame();
|
for (const group in this.toolbarGroups) {
|
||||||
|
this.toolbarGroups[group].frame();
|
||||||
|
}
|
||||||
|
|
||||||
requestAnimationFrame(t => this.loop(t));
|
requestAnimationFrame(t => this.loop(t));
|
||||||
}
|
}
|
||||||
|
|||||||
12
style.css
12
style.css
@ -28,10 +28,14 @@ div[id=simulator] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.lhg-toolbar {
|
div.lhg-toolbar {
|
||||||
position: fixed;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
margin: 0.5EM;
|
||||||
border-radius: 0.5EM;
|
border-radius: 0.5EM;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: #282;
|
||||||
|
border-style: solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.lhg-tool {
|
div.lhg-tool {
|
||||||
@ -39,12 +43,8 @@ div.lhg-tool {
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 12EM;
|
width: 12EM;
|
||||||
padding: 0.5EM;
|
/* padding: 0.5EM; */
|
||||||
margin: 0.5EM;
|
margin: 0.5EM;
|
||||||
border-radius: 0.5EM;
|
|
||||||
border-width: 1px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: #282;
|
|
||||||
text-align: middle;
|
text-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,58 +1,22 @@
|
|||||||
// Options picker
|
// Options picker
|
||||||
import {
|
import {
|
||||||
DISPLAY_ACCELERATION_VECTORS,
|
|
||||||
DISPLAY_VELOCITY_VECTORS,
|
|
||||||
GRAVITATIONAL_CONSTANT,
|
|
||||||
MASS_CREATION_RATE,
|
|
||||||
MERGE_ON_COLLIDE,
|
|
||||||
MOTION_TIME_SCALE,
|
|
||||||
PATH_TRACES_DASHED,
|
|
||||||
PAUSE_DURING_CREATION,
|
|
||||||
PAUSE_DURING_SELECTION,
|
|
||||||
TOOL_INFO_CLASSNAME,
|
TOOL_INFO_CLASSNAME,
|
||||||
WIDE_CLASSNAME,
|
WIDE_CLASSNAME,
|
||||||
} from '../config.js';
|
} from '../config.js';
|
||||||
import {Tool} from '../tool.js';
|
import { Tool } from '../tool.js';
|
||||||
|
import { Options, optionsLayout } from '../options.js';
|
||||||
export class Options extends Tool {
|
|
||||||
options = [{
|
|
||||||
type: 'group', name: 'pauseDuring', title: 'Pause During',
|
|
||||||
items: [
|
|
||||||
{type: 'boolean', name: 'creation', title: 'Create', default: PAUSE_DURING_CREATION},
|
|
||||||
{type: 'boolean', name: 'selection', title: 'Select', default: PAUSE_DURING_SELECTION},
|
|
||||||
]
|
|
||||||
}, {
|
|
||||||
type: 'group', name: 'display', title: 'Display',
|
|
||||||
items: [
|
|
||||||
{type: 'boolean', name: 'velocity', title: 'Velocity', default: DISPLAY_VELOCITY_VECTORS},
|
|
||||||
{type: 'boolean', name: 'acceleration', title: 'Accel', default: DISPLAY_ACCELERATION_VECTORS},
|
|
||||||
{type: 'boolean', name: 'traces', title: 'Traces', default: DISPLAY_ACCELERATION_VECTORS},
|
|
||||||
{type: 'boolean', name: 'dashedTraces', title: 'Dashed', default: PATH_TRACES_DASHED},
|
|
||||||
]
|
|
||||||
}, {
|
|
||||||
type: 'group', name: 'collision', title: 'Collision',
|
|
||||||
items: [
|
|
||||||
{type: 'boolean', name: 'merge', title: 'Merge Masses', default: MERGE_ON_COLLIDE, wide: true},
|
|
||||||
]
|
|
||||||
}, {
|
|
||||||
type: 'group', name: 'param', title: 'Parameters',
|
|
||||||
items: [
|
|
||||||
{type: 'number', name: 'gravity', title: 'Gravity', default: GRAVITATIONAL_CONSTANT},
|
|
||||||
{type: 'number', name: 'timeScale', title: 'Time Scale', default: MOTION_TIME_SCALE},
|
|
||||||
{type: 'number', name: 'massCreationRate', title: 'Mass Creation Rate', default: MASS_CREATION_RATE},
|
|
||||||
]
|
|
||||||
}];
|
|
||||||
|
|
||||||
values = {};
|
|
||||||
|
|
||||||
|
export class OptionsTool extends Tool {
|
||||||
visitItem(item, path) {
|
visitItem(item, path) {
|
||||||
path = [path, item.name].filter(x => !!x).join('.');
|
path = [path, item.name].filter(x => !!x).join('.');
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case 'group': {
|
case 'group': {
|
||||||
const group = document.createElement('div');
|
const group = document.createElement('div');
|
||||||
const heading = document.createElement('h3');
|
if (item.title) {
|
||||||
heading.innerHTML = item.title;
|
const heading = document.createElement('h3');
|
||||||
group.appendChild(heading);
|
heading.innerHTML = item.title;
|
||||||
|
group.appendChild(heading);
|
||||||
|
}
|
||||||
for (const next of item.items) {
|
for (const next of item.items) {
|
||||||
const child = this.visitItem(next, path);
|
const child = this.visitItem(next, path);
|
||||||
group.appendChild(child);
|
group.appendChild(child);
|
||||||
@ -65,11 +29,14 @@ export class Options extends Tool {
|
|||||||
if (item.wide === true) {
|
if (item.wide === true) {
|
||||||
button.classList.add(WIDE_CLASSNAME);
|
button.classList.add(WIDE_CLASSNAME);
|
||||||
}
|
}
|
||||||
this.setOption(path, item.default);
|
this.sim.setOption(path, item.default);
|
||||||
button.style.opacity = this.values[path] ? '100%' : '50%';
|
button.style.opacity = this.sim.getOption(path) ? '100%' : '50%';
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
this.setOption(path, !this.getOption(path));
|
this.sim.setOption(path, !this.sim.getOption(path));
|
||||||
button.style.opacity = this.values[path] ? '100%' : '50%';
|
button.style.opacity = this.sim.getOption(path) ? '100%' : '50%';
|
||||||
|
});
|
||||||
|
this.sim.onOptionSet(path, value => {
|
||||||
|
button.style.opacity = value ? '100%' : '50%';
|
||||||
});
|
});
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
@ -88,44 +55,35 @@ export class Options extends Tool {
|
|||||||
}
|
}
|
||||||
title.innerHTML = item.title;
|
title.innerHTML = item.title;
|
||||||
input.value = item.default;
|
input.value = item.default;
|
||||||
this.setOption(path, item.default);
|
this.sim.setOption(path, item.default);
|
||||||
|
|
||||||
input.addEventListener('input', () => {
|
input.addEventListener('input', () => {
|
||||||
input.value = input.value.slice(0, maxLength);
|
input.value = input.value.slice(0, maxLength);
|
||||||
});
|
});
|
||||||
|
|
||||||
input.addEventListener('change', () => {
|
input.addEventListener('change', () => {
|
||||||
this.setOption(path, input.value);
|
this.sim.setOption(path, input.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.sim.onOptionSet(path, value => {
|
||||||
|
input.value = value;
|
||||||
|
});
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error('unknown option type');
|
console.error('Unknown option type', item);
|
||||||
|
throw new Error('Unknown option type');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(toolbar) {
|
constructor(toolbar, sections) {
|
||||||
super(toolbar);
|
super(toolbar);
|
||||||
const heading = document.createElement('h2');
|
|
||||||
heading.innerHTML = 'Options';
|
for (const sectionName of sections) {
|
||||||
// this.div.appendChild(heading);
|
const item = Options.getSection(optionsLayout, sectionName);
|
||||||
for (const item of this.options) {
|
|
||||||
const child = this.visitItem(item);
|
const child = this.visitItem(item);
|
||||||
this.div.appendChild(child);
|
this.div.appendChild(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global methods to get/set current option values
|
|
||||||
this.sim.getOption = (path) => this.getOption(path);
|
|
||||||
this.sim.setOption = (path, value) => this.setOption(path, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
getOption(path) {
|
|
||||||
const val = this.values[path];
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
setOption(path, value) {
|
|
||||||
this.values[path] = value;
|
|
||||||
// TODO Update button opacity in case this is called from elsewhere
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
toolbar-group.js
Normal file
31
toolbar-group.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
export class ToolbarGroup {
|
||||||
|
sim = undefined;
|
||||||
|
toolbars = [];
|
||||||
|
|
||||||
|
constructor(sim) {
|
||||||
|
this.sim = sim;
|
||||||
|
const div = document.createElement('div');
|
||||||
|
this.div = div;
|
||||||
|
this.sim.div.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
topRight() {
|
||||||
|
this.div.style.position = 'fixed';
|
||||||
|
this.div.style.top = '0px';
|
||||||
|
this.div.style.right = '0px';
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addToolbar(toolbar) {
|
||||||
|
this.div.appendChild(toolbar.div);
|
||||||
|
this.toolbars.push(toolbar);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame() {
|
||||||
|
for (let toolbar of this.toolbars) {
|
||||||
|
toolbar.frame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
15
toolbar.js
15
toolbar.js
@ -7,13 +7,17 @@ export class Toolbar {
|
|||||||
sim = undefined;
|
sim = undefined;
|
||||||
tools = [];
|
tools = [];
|
||||||
|
|
||||||
constructor(sim, title) {
|
constructor(sim, title, group) {
|
||||||
this.sim = sim;
|
this.sim = sim;
|
||||||
|
|
||||||
// Create ourselves a div, as child of sim's div
|
// Create ourselves a div, as child of sim's div
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
this.div = div;
|
this.div = div;
|
||||||
this.sim.div.appendChild(div);
|
if (group) {
|
||||||
|
group.addToolbar(this);
|
||||||
|
} else {
|
||||||
|
this.sim.div.appendChild(div);
|
||||||
|
}
|
||||||
div.classList.add(TOOLBAR_CLASSNAME);
|
div.classList.add(TOOLBAR_CLASSNAME);
|
||||||
|
|
||||||
// Create a collapse/expand tool
|
// Create a collapse/expand tool
|
||||||
@ -21,16 +25,11 @@ export class Toolbar {
|
|||||||
this.addTool(header);
|
this.addTool(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
topRight() {
|
|
||||||
this.div.style.top = '0px';
|
|
||||||
this.div.style.right = '0px';
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tool: instance of Tool
|
// tool: instance of Tool
|
||||||
addTool(tool) {
|
addTool(tool) {
|
||||||
this.div.appendChild(tool.div);
|
this.div.appendChild(tool.div);
|
||||||
this.tools.push(tool);
|
this.tools.push(tool);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame() {
|
frame() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user