fixed option save/load
This commit is contained in:
parent
d6c3db8e45
commit
49feb0c106
@ -39,6 +39,7 @@ export const TOOLBAR_HEADER_CLASSNAME = 'lhg-toolbar-header';
|
|||||||
export const WIDE_CLASSNAME = 'lhg-wide';
|
export const WIDE_CLASSNAME = 'lhg-wide';
|
||||||
export const TALL_CLASSNAME = 'lhg-tall';
|
export const TALL_CLASSNAME = 'lhg-tall';
|
||||||
export const OVERLAY_INFO_BOX_CLASSNAME = 'lhg-overlay-info-box';
|
export const OVERLAY_INFO_BOX_CLASSNAME = 'lhg-overlay-info-box';
|
||||||
|
export const OPTION_GROUP_CLASSNAME = 'lhg-option-group';
|
||||||
|
|
||||||
// EVENT NAMES
|
// EVENT NAMES
|
||||||
export const EVENT_MODE_LEAVE = 'lhg-mode-leave';
|
export const EVENT_MODE_LEAVE = 'lhg-mode-leave';
|
||||||
|
|||||||
12
objects.js
12
objects.js
@ -208,14 +208,16 @@ export class Objects {
|
|||||||
// with the mass creation rate accelerating over time
|
// with the mass creation rate accelerating over time
|
||||||
|
|
||||||
// Scaling this parameter because of millisecond conversion
|
// Scaling this parameter because of millisecond conversion
|
||||||
const massCreationRate = this.sim.getOption('param.massCreationRate') / 1000;
|
|
||||||
|
|
||||||
if (this.creatingObject !== undefined) {
|
if (this.creatingObject !== undefined) {
|
||||||
const obj = this.objects[this.creatingObject];
|
const obj = this.objects[this.creatingObject];
|
||||||
const rate = massCreationRate * obj.age;
|
// Putting in a somewhat arbitrary scaling factor here
|
||||||
console.log('obj.age', obj.age, 'mass creation rate', rate, 'elapsedTime', elapsedTime);
|
let massCreationRate = this.sim.getOption('param.massCreationRate') / 1000;
|
||||||
// TODO: After objects merge during creation, mass creation rate can accelerate
|
// Mass creation rate acceleration
|
||||||
obj.mass += rate * elapsedTime;
|
if (this.sim.getOption('param.massAcceleration')) {
|
||||||
|
massCreationRate *= obj.age;
|
||||||
|
}
|
||||||
|
obj.mass += massCreationRate * elapsedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate forces due to gravity.
|
// Calculate forces due to gravity.
|
||||||
|
|||||||
29
options.js
29
options.js
@ -24,29 +24,38 @@ export class Options {
|
|||||||
for (const groupName of Object.keys(options)) {
|
for (const groupName of Object.keys(options)) {
|
||||||
for (const [name, [, , defaultValue]] of Object.entries(this.options[groupName])) {
|
for (const [name, [, , defaultValue]] of Object.entries(this.options[groupName])) {
|
||||||
const path = [groupName, name].join('.');
|
const path = [groupName, name].join('.');
|
||||||
let value = this.getOption(path)
|
let value = this.getFromLocalStorage(path);
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
value = defaultValue;
|
value = defaultValue;
|
||||||
this.setOption(path, value);
|
|
||||||
}
|
}
|
||||||
|
this.values[path] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getOption(path) {
|
toStored(value) {
|
||||||
let value = this.values[path];
|
return JSON.stringify(value);
|
||||||
if (value === undefined) {
|
|
||||||
value = localStorage.getItem(this.getStorageKey(path));
|
|
||||||
if (value === 'false') value = false;
|
|
||||||
else if (value === 'true') value = true;
|
|
||||||
this.values[path] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fromStored(value) {
|
||||||
|
return JSON.parse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFromLocalStorage(path) {
|
||||||
|
const storageKey = this.getStorageKey(path);
|
||||||
|
const value = this.fromStored(window.localStorage.getItem(storageKey));
|
||||||
|
this.values[path] = value;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOption(path) {
|
||||||
|
return this.values[path];
|
||||||
|
}
|
||||||
|
|
||||||
setOption(path, value) {
|
setOption(path, value) {
|
||||||
this.values[path] = value;
|
this.values[path] = value;
|
||||||
window.localStorage.setItem(this.getStorageKey(path), value);
|
const storageKey = this.getStorageKey(path);
|
||||||
|
window.localStorage.setItem(storageKey, this.toStored(value));
|
||||||
const e = new CustomEvent(EVENT_OPTION_SET, {detail: {path, value}});
|
const e = new CustomEvent(EVENT_OPTION_SET, {detail: {path, value}});
|
||||||
this.sim.div.dispatchEvent(e);
|
this.sim.div.dispatchEvent(e);
|
||||||
}
|
}
|
||||||
|
|||||||
29
sim-options.js
Normal file
29
sim-options.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export const simOptions = {
|
||||||
|
pauseDuring: {
|
||||||
|
creation: ['Pause While Creating', 'boolean', true],
|
||||||
|
selection: ['Pause While Selecting', 'boolean', true],
|
||||||
|
},
|
||||||
|
display: {
|
||||||
|
velocity: ['Velocity Vector', 'boolean', true],
|
||||||
|
acceleration: ['Accel Vector', 'boolean', true],
|
||||||
|
traces: ['Path Trace', 'boolean', true],
|
||||||
|
dashedTraces: ['Dashed', 'boolean', false, {tall: true}],
|
||||||
|
},
|
||||||
|
collision: {
|
||||||
|
merge: ['Merge Masses<br>on Collision', 'boolean', true, {wide: true}],
|
||||||
|
},
|
||||||
|
param: {
|
||||||
|
gravity: ['Gravity', 'number', 4E4],
|
||||||
|
timeScale: ['Time Scale', 'number', 0.2],
|
||||||
|
massCreationRate: ['Mass Creation Rate', 'number', 10],
|
||||||
|
massAcceleration: ['Mass Rate Accel', 'boolean', true, {wide: true}],
|
||||||
|
},
|
||||||
|
debug: {
|
||||||
|
objectsInfo: ['Objects Info', 'boolean', false],
|
||||||
|
cursorInfo: ['Cursor Info', 'boolean', false],
|
||||||
|
frameRate: ['Frame Rate', 'boolean', false, {wide: true}],
|
||||||
|
currentMode: ['Current Mode', 'boolean', false],
|
||||||
|
panningInfo: ['Panning Info', 'boolean', false],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
67
sim-tools.js
Normal file
67
sim-tools.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
export function initializeTools(sim) {
|
||||||
|
sim.toolbars = {
|
||||||
|
tools: new Toolbar(sim, 'Tools'),
|
||||||
|
modes: new Toolbar(sim, 'Modes'),
|
||||||
|
utils: new Toolbar(sim, 'Utility', { expanded: false }),
|
||||||
|
options: new Toolbar(sim, 'Options'),
|
||||||
|
params: new Toolbar(sim, 'Parameters'),
|
||||||
|
debug: new Toolbar(sim, 'Debug', { expanded: false }),
|
||||||
|
};
|
||||||
|
const { tools, modes, options, params, debug, utils } = sim.toolbars;
|
||||||
|
sim.toolbarGroups = {
|
||||||
|
left: new ToolbarGroup(sim)
|
||||||
|
.addToolbar(tools)
|
||||||
|
.addToolbar(modes)
|
||||||
|
.addToolbar(utils),
|
||||||
|
right: new ToolbarGroup(sim).topRight()
|
||||||
|
.addToolbar(options)
|
||||||
|
.addToolbar(params)
|
||||||
|
.addToolbar(debug),
|
||||||
|
};
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
58
simulator.js
58
simulator.js
@ -6,16 +6,9 @@ import {
|
|||||||
} 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 { Pointer } from './pointer.js';
|
|
||||||
import { Options } from './options.js';
|
import { Options } from './options.js';
|
||||||
import { ModeSwitch } from './tool/modes.js';
|
import { simOptions } from './sim-options.js';
|
||||||
import { OptionsTool } from './tool/options.js';
|
import { initializeTools } from './sim-tools.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';
|
|
||||||
|
|
||||||
const simOptions = {
|
const simOptions = {
|
||||||
pauseDuring: {
|
pauseDuring: {
|
||||||
@ -91,53 +84,8 @@ export class Sim {
|
|||||||
this.options = new Options(this, simOptions);
|
this.options = new Options(this, simOptions);
|
||||||
this.display = new Display(this);
|
this.display = new Display(this);
|
||||||
this.objects = new Objects(this);
|
this.objects = new Objects(this);
|
||||||
this.toolbars = {
|
|
||||||
tools: new Toolbar(this, 'Tools'),
|
|
||||||
modes: new Toolbar(this, 'Modes'),
|
|
||||||
utils: new Toolbar(this, 'Utility', { expanded: false }),
|
|
||||||
options: new Toolbar(this, 'Options'),
|
|
||||||
params: new Toolbar(this, 'Parameters'),
|
|
||||||
debug: new Toolbar(this, 'Debug', { expanded: false }),
|
|
||||||
};
|
|
||||||
const { tools, modes, options, params, debug, utils } = this.toolbars;
|
|
||||||
this.toolbarGroups = {
|
|
||||||
left: new ToolbarGroup(this)
|
|
||||||
.addToolbar(tools)
|
|
||||||
.addToolbar(modes)
|
|
||||||
.addToolbar(utils),
|
|
||||||
right: new ToolbarGroup(this).topRight()
|
|
||||||
.addToolbar(options)
|
|
||||||
.addToolbar(params)
|
|
||||||
.addToolbar(debug),
|
|
||||||
};
|
|
||||||
this.overlay = new Overlay(this);
|
|
||||||
this.pointer = new Pointer(this);
|
|
||||||
|
|
||||||
// Configure toolbars
|
initializeTools(this);
|
||||||
|
|
||||||
// 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 this.toolbars) {
|
|
||||||
const toolbar = this.toolbars[id];
|
|
||||||
toolbar.applyExpanded();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initiate main loop
|
// Initiate main loop
|
||||||
this.rawTime = document.timeline.currentTime;
|
this.rawTime = document.timeline.currentTime;
|
||||||
|
|||||||
@ -108,8 +108,7 @@ div.lhg-tool .lhg-wide {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.lhg-tool .lhg-tall {
|
div.lhg-tool .lhg-tall {
|
||||||
padding-top: 1em;
|
height: 3.666em;
|
||||||
padding-bottom: 1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.lhg-overlay-info-box {
|
div.lhg-overlay-info-box {
|
||||||
@ -119,3 +118,8 @@ div.lhg-overlay-info-box {
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.lhg-option-group > * {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,16 +1,29 @@
|
|||||||
// Options picker
|
// Options picker
|
||||||
import {
|
import {
|
||||||
TOOL_INFO_CLASSNAME,
|
TOOL_INFO_CLASSNAME,
|
||||||
|
OPTION_GROUP_CLASSNAME,
|
||||||
WIDE_CLASSNAME,
|
WIDE_CLASSNAME,
|
||||||
|
TALL_CLASSNAME,
|
||||||
} from '../config.js';
|
} from '../config.js';
|
||||||
import { Tool } from '../tool.js';
|
import { Tool } from '../tool.js';
|
||||||
|
|
||||||
export class OptionsTool extends Tool {
|
export class OptionsTool extends Tool {
|
||||||
|
constructor(container, sections) {
|
||||||
|
super(container);
|
||||||
|
|
||||||
|
for (const sectionName of sections) {
|
||||||
|
const option = this.sim.options.getSection(sectionName);
|
||||||
|
const child = this.visitItem(option);
|
||||||
|
this.div.appendChild(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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');
|
||||||
|
group.classList.add(OPTION_GROUP_CLASSNAME);
|
||||||
if (item.title) {
|
if (item.title) {
|
||||||
const heading = document.createElement('h3');
|
const heading = document.createElement('h3');
|
||||||
heading.innerHTML = item.title;
|
heading.innerHTML = item.title;
|
||||||
@ -25,13 +38,9 @@ export class OptionsTool extends Tool {
|
|||||||
case 'boolean': {
|
case 'boolean': {
|
||||||
const button = document.createElement('button');
|
const button = document.createElement('button');
|
||||||
button.innerHTML = item.title;
|
button.innerHTML = item.title;
|
||||||
if (item.wide === true) {
|
if (item.wide === true) button.classList.add(WIDE_CLASSNAME);
|
||||||
button.classList.add(WIDE_CLASSNAME);
|
if (item.tall === true) button.classList.add(TALL_CLASSNAME);
|
||||||
}
|
|
||||||
const value = this.sim.getOption(path);
|
const value = this.sim.getOption(path);
|
||||||
if (value === undefined) {
|
|
||||||
this.sim.setOption(path, item.default);
|
|
||||||
}
|
|
||||||
button.style.opacity = value ? '100%' : '50%';
|
button.style.opacity = value ? '100%' : '50%';
|
||||||
this.sim.onOptionSet(path, value => {
|
this.sim.onOptionSet(path, value => {
|
||||||
console.log('option set cb', path, value);
|
console.log('option set cb', path, value);
|
||||||
@ -43,6 +52,10 @@ export class OptionsTool extends Tool {
|
|||||||
console.log('click, option value', value);
|
console.log('click, option value', value);
|
||||||
this.sim.setOption(path, !value);
|
this.sim.setOption(path, !value);
|
||||||
});
|
});
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
const value = this.sim.getOption(path);
|
||||||
|
this.setOption(path, !value);
|
||||||
|
});
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
case 'number': {
|
case 'number': {
|
||||||
@ -59,8 +72,7 @@ export class OptionsTool extends Tool {
|
|||||||
input.classList.add(WIDE_CLASSNAME);
|
input.classList.add(WIDE_CLASSNAME);
|
||||||
}
|
}
|
||||||
title.innerHTML = item.title;
|
title.innerHTML = item.title;
|
||||||
input.value = item.default;
|
input.value = this.sim.getOption(path);
|
||||||
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);
|
||||||
@ -77,18 +89,7 @@ export class OptionsTool extends Tool {
|
|||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
console.error('Unknown option type', item);
|
|
||||||
throw new Error('Unknown option type');
|
throw new Error('Unknown option type');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(container, sections) {
|
|
||||||
super(container);
|
|
||||||
|
|
||||||
for (const sectionName of sections) {
|
|
||||||
const option = this.sim.options.getSection(sectionName);
|
|
||||||
const child = this.visitItem(option);
|
|
||||||
this.div.appendChild(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user