collapsable toolbars
This commit is contained in:
parent
54868aeba8
commit
a6eda0226b
@ -2,7 +2,7 @@
|
|||||||
export const DISPLAY_OBJECTS_INFO = false;
|
export const DISPLAY_OBJECTS_INFO = false;
|
||||||
export const DISPLAY_CURSOR_INFO = false;
|
export const DISPLAY_CURSOR_INFO = false;
|
||||||
export const DISPLAY_CANVAS_SIZE = false;
|
export const DISPLAY_CANVAS_SIZE = false;
|
||||||
export const DISPLAY_CURRENT_SCALE = true;
|
export const DISPLAY_CURRENT_SCALE = false;
|
||||||
export const DISPLAY_CURRENT_MODE = false;
|
export const DISPLAY_CURRENT_MODE = false;
|
||||||
export const DISPLAY_VELOCITY_VECTORS = true;
|
export const DISPLAY_VELOCITY_VECTORS = true;
|
||||||
export const DISPLAY_ACCELERATION_VECTORS = true;
|
export const DISPLAY_ACCELERATION_VECTORS = true;
|
||||||
@ -21,7 +21,6 @@ export const ACCELERATION_VECTOR_WIDTH = 1.5;
|
|||||||
export const ACCELERATION_VECTOR_ARROWHEAD = true;
|
export const ACCELERATION_VECTOR_ARROWHEAD = true;
|
||||||
|
|
||||||
// PATH TRACES
|
// PATH TRACES
|
||||||
// export const PATH_TRACES_COLOR = 'rgb(128, 128, 0)'; // optionally set to 'object color'
|
|
||||||
export const PATH_TRACES_COLOR = 'object color';
|
export const PATH_TRACES_COLOR = 'object color';
|
||||||
export const PATH_TRACES_OPACITY = 0.8;
|
export const PATH_TRACES_OPACITY = 0.8;
|
||||||
export const PATH_TRACES_WIDTH = 1.5;
|
export const PATH_TRACES_WIDTH = 1.5;
|
||||||
@ -46,13 +45,15 @@ export const SCALE_POWER_MIN = -8;
|
|||||||
export const GRAVITATIONAL_CONSTANT = 1E5;
|
export const GRAVITATIONAL_CONSTANT = 1E5;
|
||||||
|
|
||||||
// CSS CLASS NAMES
|
// CSS CLASS NAMES
|
||||||
export const DRAGGABLE_ELEMENT_CLASSNAME = 'lhg-draggable-element';
|
|
||||||
export const TOOL_CLASSNAME = 'lhg-tool';
|
export const TOOL_CLASSNAME = 'lhg-tool';
|
||||||
|
export const TOOL_INFO_CLASSNAME = 'lhg-tool-info';
|
||||||
export const TOOLBAR_CLASSNAME = 'lhg-toolbar';
|
export const TOOLBAR_CLASSNAME = 'lhg-toolbar';
|
||||||
|
export const WIDE_CLASSNAME = 'lhg-wide';
|
||||||
|
|
||||||
// EVENT NAMES
|
// EVENT NAMES
|
||||||
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';
|
||||||
|
|
||||||
// MODES
|
// MODES
|
||||||
export const MODE_MASS_GENERATION = 'mass-gen';
|
export const MODE_MASS_GENERATION = 'mass-gen';
|
||||||
|
|||||||
55
index.html
55
index.html
@ -3,60 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Gravity Simulator</title>
|
<title>Gravity Simulator</title>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<style>
|
<link rel="stylesheet" href="./style.css" />
|
||||||
body {
|
|
||||||
background-color: #000;
|
|
||||||
color: #8f8;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 12pt;
|
|
||||||
overflow: hidden;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div[id=simulator] {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
width: 8em;
|
|
||||||
border-radius: 0.5EM;
|
|
||||||
border-color: #000;
|
|
||||||
border-width: 2px;
|
|
||||||
/* margin-left: 0.5em; */
|
|
||||||
/* margin-right: 0.5em; */
|
|
||||||
/* padding-top: 0; */
|
|
||||||
/* padding-bottom: 0; */
|
|
||||||
}
|
|
||||||
|
|
||||||
button.wide {
|
|
||||||
width: 16em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.lhg-tool {
|
|
||||||
position: relative;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 14EM;
|
|
||||||
|
|
||||||
/* border: 1px #0fb solid; */
|
|
||||||
/* margin: 1EM; */
|
|
||||||
padding: 1EM;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.lhg-toolbar {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 2;
|
|
||||||
width: fit-content;
|
|
||||||
/* border: 2px #00f dashed; */
|
|
||||||
/* margin: '1EM'; */
|
|
||||||
/* padding: '1EM'; */
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import { Sim } from './simulator.js';
|
import { Sim } from './simulator.js';
|
||||||
const sim = new Sim('simulator');
|
const sim = new Sim('simulator');
|
||||||
|
|||||||
24
overlay.js
24
overlay.js
@ -1,5 +1,3 @@
|
|||||||
import {DRAGGABLE_ELEMENT_CLASSNAME} from './config.js';
|
|
||||||
|
|
||||||
export class Overlay {
|
export class Overlay {
|
||||||
sim = undefined;
|
sim = undefined;
|
||||||
constructor(sim) {
|
constructor(sim) {
|
||||||
@ -12,10 +10,9 @@ export class Overlay {
|
|||||||
infoBox.style.position = 'relative';
|
infoBox.style.position = 'relative';
|
||||||
infoBox.style.display = 'inline-block';
|
infoBox.style.display = 'inline-block';
|
||||||
infoBox.style.top = 0;
|
infoBox.style.top = 0;
|
||||||
infoBox.style.left = 0;
|
infoBox.style.left = '14em';
|
||||||
infoBox.width = 'fit-content';
|
infoBox.width = 'fit-content';
|
||||||
infoBox.style.zIndex = 1;
|
infoBox.style.zIndex = 1;
|
||||||
infoBox.classList.add(DRAGGABLE_ELEMENT_CLASSNAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInfo() {
|
renderInfo() {
|
||||||
@ -36,23 +33,4 @@ export class Overlay {
|
|||||||
}
|
}
|
||||||
this.infoBox.appendChild(table);
|
this.infoBox.appendChild(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update positions of draggable items
|
|
||||||
updateDraggable() {
|
|
||||||
const elements = document.querySelectorAll(`.${DRAGGABLE_ELEMENT_CLASSNAME}`);
|
|
||||||
for (let el of elements) {
|
|
||||||
if (!el.dragging) continue;
|
|
||||||
const {
|
|
||||||
elementStart: {x: x0, y: y0},
|
|
||||||
pointerStart: {x: x1, y: y1},
|
|
||||||
pointerEnd: {x: x2, y: y2}
|
|
||||||
} = el.dragging;
|
|
||||||
const dx = x2 - x1;
|
|
||||||
const dy = y2 - y1;
|
|
||||||
const left = x0 + dx;
|
|
||||||
const top = y0 + dy;
|
|
||||||
el.style.left = `${left}px`;
|
|
||||||
el.style.top = `${top}px`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
45
pointer.js
45
pointer.js
@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
DISPLAY_CURSOR_INFO,
|
DISPLAY_CURSOR_INFO,
|
||||||
DRAGGABLE_ELEMENT_CLASSNAME,
|
|
||||||
MODE_MASS_GENERATION,
|
MODE_MASS_GENERATION,
|
||||||
MODE_OBJECT_SELECT,
|
MODE_OBJECT_SELECT,
|
||||||
MODE_PAN_VIEW,
|
MODE_PAN_VIEW,
|
||||||
@ -13,7 +12,6 @@ export class Pointer {
|
|||||||
sim = undefined;
|
sim = undefined;
|
||||||
|
|
||||||
pointerHistory = [];
|
pointerHistory = [];
|
||||||
draggingElement = undefined;
|
|
||||||
panning = undefined; // { velocity: {x and y in sim coordinates}, paused: boolean }
|
panning = undefined; // { velocity: {x and y in sim coordinates}, paused: boolean }
|
||||||
panTouchStart = undefined; // {x: undefined, y: undefined, t: undefined};
|
panTouchStart = undefined; // {x: undefined, y: undefined, t: undefined};
|
||||||
panTouchLatest = undefined; // {x: undefined, y: undefined, t: undefined};
|
panTouchLatest = undefined; // {x: undefined, y: undefined, t: undefined};
|
||||||
@ -29,53 +27,20 @@ export class Pointer {
|
|||||||
if (DISPLAY_CURSOR_INFO) {
|
if (DISPLAY_CURSOR_INFO) {
|
||||||
this.sim.info['pointermove'] = [`${e.clientX}, `, `${e.clientY}`];
|
this.sim.info['pointermove'] = [`${e.clientX}, `, `${e.clientY}`];
|
||||||
}
|
}
|
||||||
|
this.handlePointerMove({x: e.clientX, y: e.clientY});
|
||||||
if (this.draggingElement) {
|
|
||||||
this.draggingElement.dragging.pointerEnd = {
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.handlePointerMove({x: e.clientX, y: e.clientY});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('pointerdown', e => {
|
el.addEventListener('pointerdown', e => {
|
||||||
// If this is a child of a draggable element, handle dragging
|
|
||||||
let target = e.target;
|
let target = e.target;
|
||||||
while (target && !target.classList.contains(DRAGGABLE_ELEMENT_CLASSNAME)) {
|
if (target.nodeName.toLowerCase() === 'button') {
|
||||||
target = target.parentElement;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target?.classList.contains(DRAGGABLE_ELEMENT_CLASSNAME)) {
|
this.handlePointerDown({x: e.clientX, y: e.clientY});
|
||||||
this.draggingElement = target;
|
|
||||||
this.draggingElement.dragging = {
|
|
||||||
elementStart: {
|
|
||||||
x: parseInt(this.draggingElement.style.left),
|
|
||||||
y: parseInt(this.draggingElement.style.top),
|
|
||||||
},
|
|
||||||
pointerStart: {
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
},
|
|
||||||
pointerEnd: {
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.handlePointerDown({x: e.clientX, y: e.clientY});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('pointerup', e => {
|
el.addEventListener('pointerup', e => {
|
||||||
if (this.draggingElement) {
|
this.handlePointerUp({x: e.clientX, y: e.clientY});
|
||||||
this.draggingElement.dragging = undefined;
|
|
||||||
this.draggingElement = undefined;
|
|
||||||
this.lastPosition = {x: undefined, y: undefined};
|
|
||||||
} else {
|
|
||||||
this.handlePointerUp({x: e.clientX, y: e.clientY});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Monitor wheel events
|
// Monitor wheel events
|
||||||
|
|||||||
29
simulator.js
29
simulator.js
@ -13,6 +13,7 @@ import {
|
|||||||
DISPLAY_CURRENT_SCALE,
|
DISPLAY_CURRENT_SCALE,
|
||||||
DISPLAY_CURRENT_MODE,
|
DISPLAY_CURRENT_MODE,
|
||||||
MOTION_TIME_SCALE,
|
MOTION_TIME_SCALE,
|
||||||
|
EVENT_ZOOM,
|
||||||
} from './config.js';
|
} from './config.js';
|
||||||
|
|
||||||
export class Sim {
|
export class Sim {
|
||||||
@ -41,9 +42,10 @@ export class Sim {
|
|||||||
this.div = div;
|
this.div = div;
|
||||||
|
|
||||||
this.display = new Display(this);
|
this.display = new Display(this);
|
||||||
this.overlay = new Overlay(this);
|
|
||||||
this.objects = new Objects(this);
|
this.objects = new Objects(this);
|
||||||
this.toolbar = new Toolbar(this);
|
this.toolbar = new Toolbar(this, 'Tools');
|
||||||
|
this.toolbar2 = new Toolbar(this, 'Options');
|
||||||
|
this.overlay = new Overlay(this);
|
||||||
|
|
||||||
// Set up toolbar
|
// Set up toolbar
|
||||||
this.toolbar.addTool(new Zoom(this.toolbar));
|
this.toolbar.addTool(new Zoom(this.toolbar));
|
||||||
@ -51,7 +53,7 @@ export class Sim {
|
|||||||
this.toolbar.addTool(new ModeSwitch(this.toolbar));
|
this.toolbar.addTool(new ModeSwitch(this.toolbar));
|
||||||
|
|
||||||
// Set up second toolbar
|
// Set up second toolbar
|
||||||
this.toolbar2 = new Toolbar(this).topRight();
|
this.toolbar2.topRight();
|
||||||
this.toolbar2.addTool(new Options(this.toolbar));
|
this.toolbar2.addTool(new Options(this.toolbar));
|
||||||
|
|
||||||
this.pointer = new Pointer(this);
|
this.pointer = new Pointer(this);
|
||||||
@ -92,6 +94,9 @@ export class Sim {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const e = new CustomEvent(EVENT_ZOOM);
|
||||||
|
this.div.dispatchEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform display coordinates to simulator coordinates using scale and viewOrigin
|
// Transform display coordinates to simulator coordinates using scale and viewOrigin
|
||||||
@ -107,6 +112,18 @@ export class Sim {
|
|||||||
this.playing = false;
|
this.playing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getScaleDisplay() {
|
||||||
|
const scale = 2 ** Math.abs(this.display.scalePower);
|
||||||
|
return this.display.scalePower >= 0 ? `${scale}` : `1/${scale}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cb: () => undefined
|
||||||
|
onZoom(cb) {
|
||||||
|
this.div.addEventListener(EVENT_ZOOM, () => {
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
loop(currentTime) {
|
loop(currentTime) {
|
||||||
const elapsedTime = (currentTime - this.time) * MOTION_TIME_SCALE;
|
const elapsedTime = (currentTime - this.time) * MOTION_TIME_SCALE;
|
||||||
@ -122,16 +139,12 @@ export class Sim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DISPLAY_CURRENT_SCALE) {
|
if (DISPLAY_CURRENT_SCALE) {
|
||||||
const scale = 2 ** Math.abs(this.display.scalePower);
|
this.info['Scale'] = this.getScaleDisplay();
|
||||||
this.info['Scale'] = this.display.scalePower >= 0 ? `${scale}` : `1/${scale}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pointer.computeFrame(elapsedTime);
|
this.pointer.computeFrame(elapsedTime);
|
||||||
this.objects.computeFrame(elapsedTime);
|
this.objects.computeFrame(elapsedTime);
|
||||||
|
|
||||||
this.overlay.updateDraggable();
|
|
||||||
this.overlay.renderInfo();
|
this.overlay.renderInfo();
|
||||||
|
|
||||||
this.display.fillCanvas();
|
this.display.fillCanvas();
|
||||||
this.display.drawObjects();
|
this.display.drawObjects();
|
||||||
|
|
||||||
|
|||||||
91
style.css
Normal file
91
style.css
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
body {
|
||||||
|
background-color: #000;
|
||||||
|
color: #5f5;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 10pt;
|
||||||
|
overflow: hidden;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin-block-start: 4pt;
|
||||||
|
margin-block-end: 4pt;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12pt;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 16pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
div[id=simulator] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lhg-toolbar {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2;
|
||||||
|
width: fit-content;
|
||||||
|
border-radius: 0.5EM;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lhg-toolbar div.lhg-tool {
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 12EM;
|
||||||
|
padding: 0.5EM;
|
||||||
|
margin: 0.5EM;
|
||||||
|
border-radius: 0.5EM;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #282;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lhg-tool button {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 10pt;
|
||||||
|
width: 6em;
|
||||||
|
background-color: #333;
|
||||||
|
color: #5f5;
|
||||||
|
border-radius: 0.5EM;
|
||||||
|
border-color: #000;
|
||||||
|
border-width: 4px;
|
||||||
|
border-style: solid;
|
||||||
|
padding-top: 0.5EM;
|
||||||
|
padding-bottom: 0.5EM;
|
||||||
|
padding-left: 0EM;
|
||||||
|
padding-right: 0EM;
|
||||||
|
text-align: center;
|
||||||
|
/* margin-top: 0.125EM;
|
||||||
|
margin-bottom: 0.125EM; */
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lhg-tool button:hover {
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lhg-tool button:active {
|
||||||
|
background-color: #252;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lhg-tool button.lhg-tool-info {
|
||||||
|
background-color: #111;
|
||||||
|
border-color: #282;
|
||||||
|
border-width: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lhg-tool button.lhg-wide {
|
||||||
|
width: 12em;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lhg-tool > div {
|
||||||
|
/* border: 2px red solid; */
|
||||||
|
}
|
||||||
14
sync
14
sync
@ -5,8 +5,18 @@ if [[ $(hostname) != "ladd76" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rsync -ru --exclude='.git' --exclude='node_modules' ~/code/gravity-dev/ lentilz:code/gravity-dev/
|
set -eo pipefail
|
||||||
rsync -ru --exclude='.git' --exclude='node_modules' lentilz:code/gravity-dev/ ~/code/gravity-dev/
|
|
||||||
|
do_rsync() {
|
||||||
|
rsync -ru \
|
||||||
|
--exclude='.git' \
|
||||||
|
--exclude='node_modules' \
|
||||||
|
--exclude='.*.sw*' \
|
||||||
|
"$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_rsync ~/code/gravity-dev/ lentilz:code/gravity-dev/
|
||||||
|
do_rsync lentilz:code/gravity-dev/ ~/code/gravity-dev/
|
||||||
|
|
||||||
echo >&2
|
echo >&2
|
||||||
echo >&2 "Synced with https://laddhoffman.com/gravity-dev/"
|
echo >&2 "Synced with https://laddhoffman.com/gravity-dev/"
|
||||||
|
|||||||
2
tool.js
2
tool.js
@ -3,7 +3,6 @@
|
|||||||
// through toolbar can access sim
|
// through toolbar can access sim
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DRAGGABLE_ELEMENT_CLASSNAME,
|
|
||||||
TOOL_CLASSNAME,
|
TOOL_CLASSNAME,
|
||||||
} from './config.js';
|
} from './config.js';
|
||||||
|
|
||||||
@ -17,7 +16,6 @@ export class Tool {
|
|||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
this.div = div;
|
this.div = div;
|
||||||
div.classList.add(TOOL_CLASSNAME)
|
div.classList.add(TOOL_CLASSNAME)
|
||||||
div.classList.add(DRAGGABLE_ELEMENT_CLASSNAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
frame() {}
|
frame() {}
|
||||||
|
|||||||
49
tool/header.js
Normal file
49
tool/header.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { Tool } from '../tool.js';
|
||||||
|
|
||||||
|
export class Header extends Tool {
|
||||||
|
expanded = true;
|
||||||
|
|
||||||
|
constructor(toolbar, title = 'Tools') {
|
||||||
|
super(toolbar);
|
||||||
|
|
||||||
|
this.title = document.createElement('h1');
|
||||||
|
this.title.innerHTML = title;
|
||||||
|
|
||||||
|
this.toggleButton = document.createElement('button');
|
||||||
|
this.updateButton();
|
||||||
|
this.toggleButton.addEventListener('click', () => this.toggle());
|
||||||
|
|
||||||
|
this.div.appendChild(this.title);
|
||||||
|
this.div.appendChild(this.toggleButton);
|
||||||
|
|
||||||
|
this.title.style.verticalAlign = 'center';
|
||||||
|
this.toggleButton.style.width = '3EM';
|
||||||
|
this.div.style.display = 'flex';
|
||||||
|
this.div.style.justifyContent = 'space-around';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButton() {
|
||||||
|
this.toggleButton.innerHTML = this.expanded ? '˄' : '˅';
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
this.expanded = !this.expanded;
|
||||||
|
this.updateButton();
|
||||||
|
this.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
apply() {
|
||||||
|
for (const tool of this.toolbar.tools) {
|
||||||
|
if (tool === this) continue;
|
||||||
|
if (this.expanded) {
|
||||||
|
if (!this.toolbar.div.contains(tool.div)) {
|
||||||
|
this.toolbar.div.appendChild(tool.div);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.toolbar.div.contains(tool.div)) {
|
||||||
|
this.toolbar.div.removeChild(tool.div);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import {
|
|||||||
MODE_OBJECT_SELECT,
|
MODE_OBJECT_SELECT,
|
||||||
EVENT_MODE_LEAVE,
|
EVENT_MODE_LEAVE,
|
||||||
EVENT_MODE_ENTER,
|
EVENT_MODE_ENTER,
|
||||||
|
WIDE_CLASSNAME,
|
||||||
} from '../config.js';
|
} from '../config.js';
|
||||||
|
|
||||||
export class ModeSwitch extends Tool {
|
export class ModeSwitch extends Tool {
|
||||||
@ -21,7 +22,11 @@ export class ModeSwitch extends Tool {
|
|||||||
super(toolbar);
|
super(toolbar);
|
||||||
|
|
||||||
const modesDiv = document.createElement('div');
|
const modesDiv = document.createElement('div');
|
||||||
|
const heading = document.createElement('h2');
|
||||||
|
|
||||||
|
heading.innerHTML = 'Modes';
|
||||||
|
|
||||||
|
// this.div.appendChild(heading);
|
||||||
this.div.appendChild(modesDiv);
|
this.div.appendChild(modesDiv);
|
||||||
|
|
||||||
modesDiv.style.display = 'flex';
|
modesDiv.style.display = 'flex';
|
||||||
@ -32,8 +37,8 @@ export class ModeSwitch extends Tool {
|
|||||||
this.buttons.push(button);
|
this.buttons.push(button);
|
||||||
button.modeID = modeID;
|
button.modeID = modeID;
|
||||||
modesDiv.appendChild(button);
|
modesDiv.appendChild(button);
|
||||||
button.innerHTML = `<h3>${modeTitle}</h3>`;
|
button.innerHTML = modeTitle;
|
||||||
button.classList.add('wide');
|
button.classList.add(WIDE_CLASSNAME);
|
||||||
|
|
||||||
button.addEventListener('click', () => this.setCurrentMode(modeID));
|
button.addEventListener('click', () => this.setCurrentMode(modeID));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,21 +6,22 @@ import {
|
|||||||
PATH_TRACES_DASHED,
|
PATH_TRACES_DASHED,
|
||||||
PAUSE_DURING_CREATION,
|
PAUSE_DURING_CREATION,
|
||||||
PAUSE_DURING_SELECTION,
|
PAUSE_DURING_SELECTION,
|
||||||
|
WIDE_CLASSNAME,
|
||||||
} from '../config.js';
|
} from '../config.js';
|
||||||
import {Tool} from '../tool.js';
|
import {Tool} from '../tool.js';
|
||||||
|
|
||||||
export class Options extends Tool {
|
export class Options extends Tool {
|
||||||
options = [{
|
options = [{
|
||||||
type: 'group', name: 'pauseDuring', title: 'Pause During Mass',
|
type: 'group', name: 'pauseDuring', title: 'Pause During',
|
||||||
items: [
|
items: [
|
||||||
{type: 'boolean', name: 'creation', title: 'Creation', default: PAUSE_DURING_CREATION},
|
{type: 'boolean', name: 'creation', title: 'Create', default: PAUSE_DURING_CREATION},
|
||||||
{type: 'boolean', name: 'selection', title: 'Selection', default: PAUSE_DURING_SELECTION},
|
{type: 'boolean', name: 'selection', title: 'Select', default: PAUSE_DURING_SELECTION},
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
type: 'group', name: 'display', title: 'Display',
|
type: 'group', name: 'display', title: 'Display',
|
||||||
items: [
|
items: [
|
||||||
{type: 'boolean', name: 'velocity', title: 'Velocity', default: DISPLAY_VELOCITY_VECTORS},
|
{type: 'boolean', name: 'velocity', title: 'Velocity', default: DISPLAY_VELOCITY_VECTORS},
|
||||||
{type: 'boolean', name: 'acceleration', title: 'Acceleration', default: DISPLAY_ACCELERATION_VECTORS},
|
{type: 'boolean', name: 'acceleration', title: 'Accel', default: DISPLAY_ACCELERATION_VECTORS},
|
||||||
{type: 'boolean', name: 'traces', title: 'Traces', 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: 'boolean', name: 'dashedTraces', title: 'Dashed', default: PATH_TRACES_DASHED},
|
||||||
]
|
]
|
||||||
@ -29,8 +30,7 @@ export class Options extends Tool {
|
|||||||
items: [
|
items: [
|
||||||
{type: 'boolean', name: 'merge', title: 'Merge Masses', default: MERGE_ON_COLLIDE, wide: true},
|
{type: 'boolean', name: 'merge', title: 'Merge Masses', default: MERGE_ON_COLLIDE, wide: true},
|
||||||
]
|
]
|
||||||
},
|
}];
|
||||||
];
|
|
||||||
|
|
||||||
values = {};
|
values = {};
|
||||||
|
|
||||||
@ -50,9 +50,9 @@ export class Options extends Tool {
|
|||||||
}
|
}
|
||||||
case 'boolean': {
|
case 'boolean': {
|
||||||
const button = document.createElement('button');
|
const button = document.createElement('button');
|
||||||
button.innerHTML = `<h4>${item.title}</h4>`;
|
button.innerHTML = item.title;
|
||||||
if (item.wide === true) {
|
if (item.wide === true) {
|
||||||
button.classList.add('wide');
|
button.classList.add(WIDE_CLASSNAME);
|
||||||
}
|
}
|
||||||
this.setOption(path, item.default);
|
this.setOption(path, item.default);
|
||||||
button.style.opacity = this.values[path] ? '100%' : '50%';
|
button.style.opacity = this.values[path] ? '100%' : '50%';
|
||||||
@ -71,7 +71,7 @@ export class Options extends Tool {
|
|||||||
super(toolbar);
|
super(toolbar);
|
||||||
const heading = document.createElement('h2');
|
const heading = document.createElement('h2');
|
||||||
heading.innerHTML = 'Options';
|
heading.innerHTML = 'Options';
|
||||||
this.div.appendChild(heading);
|
// this.div.appendChild(heading);
|
||||||
for (const item of this.options) {
|
for (const item of this.options) {
|
||||||
const child = this.visitItem(item);
|
const child = this.visitItem(item);
|
||||||
this.div.appendChild(child);
|
this.div.appendChild(child);
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { Tool } from '../tool.js';
|
import { Tool } from '../tool.js';
|
||||||
|
|
||||||
export class PlayPause extends Tool {
|
export class PlayPause extends Tool {
|
||||||
playHTML = '<h2>Play</h2>';
|
playHTML = 'Play';
|
||||||
pauseHTML = '<h2>Pause</h2>';
|
pauseHTML = 'Pause';
|
||||||
|
|
||||||
constructor(toolbar) {
|
constructor(toolbar) {
|
||||||
super(toolbar);
|
super(toolbar);
|
||||||
|
|||||||
21
tool/zoom.js
21
tool/zoom.js
@ -1,5 +1,7 @@
|
|||||||
import { Tool } from '../tool.js';
|
import { Tool } from '../tool.js';
|
||||||
import {
|
import {
|
||||||
|
TOOL_INFO_CLASSNAME,
|
||||||
|
WIDE_CLASSNAME,
|
||||||
ZOOM_IN_FACTOR,
|
ZOOM_IN_FACTOR,
|
||||||
ZOOM_OUT_FACTOR,
|
ZOOM_OUT_FACTOR,
|
||||||
} from '../config.js';
|
} from '../config.js';
|
||||||
@ -8,21 +10,28 @@ export class Zoom extends Tool {
|
|||||||
constructor(toolbar) {
|
constructor(toolbar) {
|
||||||
super(toolbar);
|
super(toolbar);
|
||||||
|
|
||||||
|
const currentScale = document.createElement('button')
|
||||||
const zoomOut = document.createElement('button');
|
const zoomOut = document.createElement('button');
|
||||||
const zoomIn = document.createElement('button');
|
const zoomIn = document.createElement('button');
|
||||||
const zoomAll = document.createElement('button');
|
const zoomAll = document.createElement('button');
|
||||||
|
|
||||||
|
this.div.appendChild(currentScale);
|
||||||
this.div.appendChild(zoomOut);
|
this.div.appendChild(zoomOut);
|
||||||
this.div.appendChild(zoomIn);
|
this.div.appendChild(zoomIn);
|
||||||
this.div.appendChild(document.createElement('br'));
|
|
||||||
this.div.appendChild(zoomAll);
|
this.div.appendChild(zoomAll);
|
||||||
this.div.appendChild(document.createElement('br'));
|
|
||||||
|
|
||||||
zoomAll.classList.add('wide');
|
zoomAll.classList.add(WIDE_CLASSNAME);
|
||||||
|
currentScale.classList.add(WIDE_CLASSNAME);
|
||||||
|
currentScale.classList.add(TOOL_INFO_CLASSNAME);
|
||||||
|
|
||||||
zoomOut.innerHTML = '<h2>Zoom<br>Out</h2>';
|
zoomOut.innerHTML = 'Zoom<br>Out';
|
||||||
zoomIn.innerHTML = '<h2>Zoom<br>In</h2>';
|
zoomIn.innerHTML = 'Zoom<br>In';
|
||||||
zoomAll.innerHTML = '<h2>Zoom to Fit</h2>';
|
zoomAll.innerHTML = 'Zoom to Fit';
|
||||||
|
currentScale.innerHTML = `Scale: ${this.sim.getScaleDisplay()}`;
|
||||||
|
|
||||||
|
this.sim.onZoom(() => {
|
||||||
|
currentScale.innerHTML = `Scale: ${this.sim.getScaleDisplay()}`;
|
||||||
|
});
|
||||||
|
|
||||||
zoomOut.addEventListener('click', () => {
|
zoomOut.addEventListener('click', () => {
|
||||||
// Aim at center of view
|
// Aim at center of view
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
TOOLBAR_CLASSNAME,
|
TOOLBAR_CLASSNAME,
|
||||||
} from './config.js';
|
} from './config.js';
|
||||||
|
import {Header} from './tool/header.js';
|
||||||
|
|
||||||
export class Toolbar {
|
export class Toolbar {
|
||||||
sim = undefined;
|
sim = undefined;
|
||||||
tools = [];
|
tools = [];
|
||||||
|
|
||||||
constructor(sim) {
|
constructor(sim, title) {
|
||||||
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
|
||||||
@ -14,6 +15,10 @@ export class Toolbar {
|
|||||||
this.div = div;
|
this.div = div;
|
||||||
this.sim.div.appendChild(div);
|
this.sim.div.appendChild(div);
|
||||||
div.classList.add(TOOLBAR_CLASSNAME);
|
div.classList.add(TOOLBAR_CLASSNAME);
|
||||||
|
|
||||||
|
// Create a collapse/expand tool
|
||||||
|
const header = new Header(this, title);
|
||||||
|
this.addTool(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
topRight() {
|
topRight() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user