possible wide toolbar groups
This commit is contained in:
parent
debf31e9b0
commit
fe7e9f43ad
@ -10,6 +10,8 @@ Screenshots
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -35,3 +37,5 @@ TODO
|
|||||||
- [ ] Track farthest reaches, min/max in each dimension (x, y)
|
- [ ] Track farthest reaches, min/max in each dimension (x, y)
|
||||||
- [ ] Calculate Work as FxD as measure of energy flux
|
- [ ] Calculate Work as FxD as measure of energy flux
|
||||||
- [ ] Option to automatically slow time when energy flux is greater
|
- [ ] Option to automatically slow time when energy flux is greater
|
||||||
|
- [ ] Handle pointerleave or other mechanism when window loses focus
|
||||||
|
- [ ] Verify stationary pointer leads to zero pointer velocity
|
||||||
|
|||||||
@ -16,6 +16,7 @@ export const PATH_TRACES_DASHED_OPACITY = 1.0;
|
|||||||
|
|
||||||
// SIZES
|
// SIZES
|
||||||
export const POINTER_HISTORY_SIZE = 20;
|
export const POINTER_HISTORY_SIZE = 20;
|
||||||
|
export const OBJECT_HISTORY_SIZE = 1e5;
|
||||||
export const FRAMERATE_SAMPLE_DURATION = 2.0; // Seconds
|
export const FRAMERATE_SAMPLE_DURATION = 2.0; // Seconds
|
||||||
export const POINTER_DOWN_HISTORY_SIZE = 5;
|
export const POINTER_DOWN_HISTORY_SIZE = 5;
|
||||||
export const ARROWHEAD_LENGTH = 7;
|
export const ARROWHEAD_LENGTH = 7;
|
||||||
@ -37,6 +38,7 @@ 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';
|
export const OPTION_GROUP_CLASSNAME = 'lhg-option-group';
|
||||||
|
export const TOOLBAR_GROUP_CLASSNAME = 'lhg-toolbar-group';
|
||||||
|
|
||||||
// EVENT NAMES
|
// EVENT NAMES
|
||||||
export const EVENT_MODE_LEAVE = 'lhg-mode-leave';
|
export const EVENT_MODE_LEAVE = 'lhg-mode-leave';
|
||||||
|
|||||||
BIN
gravity-simulator-5.png
Normal file
BIN
gravity-simulator-5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
@ -183,8 +183,10 @@ export class MassObject {
|
|||||||
// const panning = this.sim.panning?.velocity ?? {x: 0, y: 0};
|
// const panning = this.sim.panning?.velocity ?? {x: 0, y: 0};
|
||||||
// velocity.x = vx + (pointerV.x + panning.x) * scale;
|
// velocity.x = vx + (pointerV.x + panning.x) * scale;
|
||||||
// velocity.y = vy + (pointerV.y + panning.y) * scale;
|
// velocity.y = vy + (pointerV.y + panning.y) * scale;
|
||||||
velocity.x = vx + pointerV.x / this.sim.timeScale;
|
if (this.sim.getOption('compensate.timeScale')) {
|
||||||
velocity.y = vy + pointerV.y / this.sim.timeScale;
|
velocity.x = vx + pointerV.x / this.sim.timeScale;
|
||||||
|
velocity.y = vy + pointerV.y / this.sim.timeScale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const speed = Math.sqrt(velocity.x ** 2, velocity.y ** 2) / this.sim.display.scale;
|
const speed = Math.sqrt(velocity.x ** 2, velocity.y ** 2) / this.sim.display.scale;
|
||||||
const arrowDirection = Math.atan2(velocity.y, velocity.x);
|
const arrowDirection = Math.atan2(velocity.y, velocity.x);
|
||||||
|
|||||||
@ -69,7 +69,14 @@ export class Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getOption(path) {
|
getOption(path) {
|
||||||
return this.values[path];
|
const [group, name] = path.split('.');
|
||||||
|
const {type} = this.options[group][name];
|
||||||
|
const value = this.values[path];
|
||||||
|
switch (type) {
|
||||||
|
case 'number': return Number(value);
|
||||||
|
case 'boolean': return value === true || value === 'true';
|
||||||
|
default: return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setOption(path, value) {
|
setOption(path, value) {
|
||||||
|
|||||||
@ -205,10 +205,10 @@ export class Pointer {
|
|||||||
|
|
||||||
frame() {
|
frame() {
|
||||||
// Add another entry for the current pointer position
|
// Add another entry for the current pointer position
|
||||||
const {pointerHistory} = this.sim.pointer ?? {};
|
const { pointerHistory } = this;
|
||||||
if (pointerHistory?.length) {
|
if (pointerHistory.length) {
|
||||||
const currentPointer = pointerHistory[pointerHistory.length - 1];
|
const currentPointer = pointerHistory[pointerHistory.length - 1];
|
||||||
this.sim.pointer.updatePointer(currentPointer);
|
this.updatePointer(currentPointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,10 +10,10 @@ export const simOptions = {
|
|||||||
acceleration: ['Accel Vectors', 'boolean', true],
|
acceleration: ['Accel Vectors', 'boolean', true],
|
||||||
velocityScale: ['Velocity<br>Vec Scale', 'number', 80, {hideUnless: 'display.velocity'}],
|
velocityScale: ['Velocity<br>Vec Scale', 'number', 80, {hideUnless: 'display.velocity'}],
|
||||||
accelerationScale: ['Accel<br>Vec Scale', 'number', 800, {hideUnless: 'display.acceleration'}],
|
accelerationScale: ['Accel<br>Vec Scale', 'number', 800, {hideUnless: 'display.acceleration'}],
|
||||||
targetFrameRate: ['Target Frame Rate', 'number', 60],
|
targetFrameRate: ['Frame Rate', 'number', 60],
|
||||||
},
|
},
|
||||||
collision: {
|
collision: {
|
||||||
merge: ['Merge Masses<br>on Collision', 'boolean', true, {wide: true}],
|
merge: ['Merge Masses on Collision', 'boolean', true, {wide: true}],
|
||||||
},
|
},
|
||||||
compensate: {
|
compensate: {
|
||||||
timeScale: ['Time Scale Compensator', 'boolean', false, {wide: true}],
|
timeScale: ['Time Scale Compensator', 'boolean', false, {wide: true}],
|
||||||
|
|||||||
68
simulator.js
68
simulator.js
@ -52,7 +52,38 @@ export class Sim {
|
|||||||
// Initiate main loop
|
// Initiate main loop
|
||||||
this.rawTime = document.timeline.currentTime;
|
this.rawTime = document.timeline.currentTime;
|
||||||
this.time = 0;
|
this.time = 0;
|
||||||
requestAnimationFrame(t => this.loop(t));
|
requestAnimationFrame(t => this.frame(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
frame(currentTime) {
|
||||||
|
const early = this.markFrame(currentTime);
|
||||||
|
if (early) {
|
||||||
|
// Slow down :)
|
||||||
|
requestAnimationFrame(t => this.frame(t));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.timeScale = this.getOption('param.timeScale');
|
||||||
|
const elapsedTime = (currentTime - this.rawTime) * this.timeScale;
|
||||||
|
this.rawTime = currentTime;
|
||||||
|
if (this.playing) {
|
||||||
|
this.time += elapsedTime;
|
||||||
|
}
|
||||||
|
if (this.getOption('debug.currentMode')) {
|
||||||
|
this.info['Mode'] = this.getCurrentMode();
|
||||||
|
}
|
||||||
|
if (this.getOption('debug.frameRate')) {
|
||||||
|
this.info['Frame Rate'] = this.frameRate.toPrecision(3);
|
||||||
|
}
|
||||||
|
this.zoom.frame();
|
||||||
|
this.pointer.frame();
|
||||||
|
this.display.frame(elapsedTime);
|
||||||
|
this.system.frame(elapsedTime);
|
||||||
|
this.overlay.frame();
|
||||||
|
for (const group in this.toolbarGroups) {
|
||||||
|
this.toolbarGroups[group].frame();
|
||||||
|
}
|
||||||
|
requestAnimationFrame(t => this.frame(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
markFrame(t) {
|
markFrame(t) {
|
||||||
@ -110,39 +141,4 @@ export class Sim {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main loop
|
|
||||||
loop(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;
|
|
||||||
this.rawTime = currentTime;
|
|
||||||
|
|
||||||
if (this.playing) {
|
|
||||||
this.time += elapsedTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.getOption('debug.currentMode')) {
|
|
||||||
this.info['Mode'] = this.getCurrentMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.getOption('debug.frameRate')) {
|
|
||||||
this.info['Frame Rate'] = this.frameRate.toPrecision(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.zoom.frame();
|
|
||||||
this.display.frame(elapsedTime);
|
|
||||||
this.system.frame(elapsedTime);
|
|
||||||
this.overlay.frame();
|
|
||||||
for (const group in this.toolbarGroups) {
|
|
||||||
this.toolbarGroups[group].frame();
|
|
||||||
}
|
|
||||||
|
|
||||||
requestAnimationFrame(t => this.loop(t));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
42
style.css
42
style.css
@ -27,6 +27,31 @@ div[id=simulator] {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* normal toolbar group */
|
||||||
|
div.lhg-toolbar-group div.lhg-tool {
|
||||||
|
width: 12em;
|
||||||
|
}
|
||||||
|
div.lhg-toolbar-group div.lhg-tool button, div.lhg-toolbar-group div.lhg-tool input {
|
||||||
|
width: 6em;
|
||||||
|
}
|
||||||
|
div.lhg-toolbar-group div.lhg-tool .lhg-wide {
|
||||||
|
width: 12em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* wide toolbar group */
|
||||||
|
div.lhg-toolbar-group.lhg-wide div.lhg-tool {
|
||||||
|
width: 16em;
|
||||||
|
}
|
||||||
|
div.lhg-toolbar-group.lhg-wide div.lhg-tool button, div.lhg-toolbar-group.lhg-wide div.lhg-tool input {
|
||||||
|
width: 8em;
|
||||||
|
}
|
||||||
|
div.lhg-toolbar-group.lhg-wide div.lhg-tool .lhg-wide {
|
||||||
|
width: 16em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of toolbar group section */
|
||||||
|
|
||||||
div.lhg-toolbar {
|
div.lhg-toolbar {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
@ -42,7 +67,6 @@ div.lhg-tool {
|
|||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 12em;
|
|
||||||
/* padding: 0.5em; */
|
/* padding: 0.5em; */
|
||||||
/* margin: 0.5em; */
|
/* margin: 0.5em; */
|
||||||
text-align: middle;
|
text-align: middle;
|
||||||
@ -56,7 +80,6 @@ div.lhg-tool div.lhg-wide {
|
|||||||
div.lhg-tool button, div.lhg-tool input {
|
div.lhg-tool button, div.lhg-tool input {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
width: 6em;
|
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
color: #5f5;
|
color: #5f5;
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
@ -73,10 +96,6 @@ div.lhg-tool button, div.lhg-tool input {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.lhg-tool input {
|
|
||||||
width: 6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.lhg-tool button:hover {
|
div.lhg-tool button:hover {
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
}
|
}
|
||||||
@ -103,10 +122,6 @@ div.lhg-tool .lhg-tool-info {
|
|||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.lhg-tool .lhg-wide {
|
|
||||||
width: 12em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.lhg-tool .lhg-tall {
|
div.lhg-tool .lhg-tall {
|
||||||
height: 3.666em;
|
height: 3.666em;
|
||||||
}
|
}
|
||||||
@ -119,7 +134,14 @@ div.lhg-overlay-info-box {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.lhg-option-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
div.lhg-option-group > * {
|
div.lhg-option-group > * {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
72
system.js
72
system.js
@ -1,4 +1,5 @@
|
|||||||
import { MassObject } from './object.js';
|
import {OBJECT_HISTORY_SIZE} from './config.js';
|
||||||
|
import {MassObject} from './object.js';
|
||||||
|
|
||||||
export class System {
|
export class System {
|
||||||
objects = [];
|
objects = [];
|
||||||
@ -88,10 +89,10 @@ export class System {
|
|||||||
|
|
||||||
get boundingBox() {
|
get boundingBox() {
|
||||||
const box = this.reduce(({start, end}, obj) => {
|
const box = this.reduce(({start, end}, obj) => {
|
||||||
const lx = obj.position.x - obj.radius;
|
const lx = obj.position.x - obj.radius;
|
||||||
const gx = obj.position.x + obj.radius;
|
const gx = obj.position.x + obj.radius;
|
||||||
const ly = obj.position.y - obj.radius;
|
const ly = obj.position.y - obj.radius;
|
||||||
const gy = obj.position.y + obj.radius;
|
const gy = obj.position.y + obj.radius;
|
||||||
let ret;
|
let ret;
|
||||||
if (start.x === undefined) {
|
if (start.x === undefined) {
|
||||||
ret = {
|
ret = {
|
||||||
@ -112,7 +113,7 @@ export class System {
|
|||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
}, {
|
}, {
|
||||||
start: {x: undefined, y: undefined},
|
start: {x: undefined, y: undefined},
|
||||||
end: {x: undefined, y: undefined},
|
end: {x: undefined, y: undefined},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -128,7 +129,7 @@ export class System {
|
|||||||
this.selectedObjectStart = undefined;
|
this.selectedObjectStart = undefined;
|
||||||
this.forEachObject((obj, i) => {
|
this.forEachObject((obj, i) => {
|
||||||
// If distance to object is less than object's radius, we are touching the object
|
// 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);
|
const dist = Math.pow((obj.position.x - x) ** 2 + (obj.position.y - y) ** 2, 1 / 2);
|
||||||
if (dist <= obj.radius) {
|
if (dist <= obj.radius) {
|
||||||
idx = i;
|
idx = i;
|
||||||
return null;
|
return null;
|
||||||
@ -140,7 +141,7 @@ export class System {
|
|||||||
handlePointerDown({x, y}) {
|
handlePointerDown({x, y}) {
|
||||||
// If pointer is touching an object, select the object
|
// If pointer is touching an object, select the object
|
||||||
const touchingObject = this.objectAtLocation(x, y);
|
const touchingObject = this.objectAtLocation(x, y);
|
||||||
|
|
||||||
if (touchingObject !== undefined) {
|
if (touchingObject !== undefined) {
|
||||||
this.selectObject(touchingObject, {x, y});
|
this.selectObject(touchingObject, {x, y});
|
||||||
} else {
|
} else {
|
||||||
@ -226,8 +227,8 @@ export class System {
|
|||||||
const F = gravity * A.mass * B.mass / dSquared;
|
const F = gravity * A.mass * B.mass / dSquared;
|
||||||
const Fx = F * dx / d;
|
const Fx = F * dx / d;
|
||||||
const Fy = F * dy / d;
|
const Fy = F * dy / d;
|
||||||
A.forces.push({ x: Fx, y: Fy });
|
A.forces.push({x: Fx, y: Fy});
|
||||||
B.forces.push({ x: -Fx, y: -Fy });
|
B.forces.push({x: -Fx, y: -Fy});
|
||||||
}, {alive: true, startWith: i + 1});
|
}, {alive: true, startWith: i + 1});
|
||||||
});
|
});
|
||||||
// Also compute acceleration
|
// Also compute acceleration
|
||||||
@ -245,9 +246,13 @@ export class System {
|
|||||||
|
|
||||||
if (this.creatingObject !== undefined) {
|
if (this.creatingObject !== undefined) {
|
||||||
const obj = this.objects[this.creatingObject];
|
const obj = this.objects[this.creatingObject];
|
||||||
let massCreationRate = this.sim.getOption('param.massCreationRate') / this.sim.display.scale;
|
let massCreationRate = this.sim.getOption('param.massCreationRate');
|
||||||
|
massCreationRate /= this.sim.display.scale;
|
||||||
// Keep consistent time scale
|
// Keep consistent time scale
|
||||||
obj.mass += massCreationRate * elapsedTime / this.sim.timeScale;
|
if (this.sim.getOption('compensate.timeScale')) {
|
||||||
|
massCreationRate /= this.sim.timeScale;
|
||||||
|
}
|
||||||
|
obj.mass += massCreationRate * elapsedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate forces due to gravity.
|
// Calculate forces due to gravity.
|
||||||
@ -259,9 +264,9 @@ export class System {
|
|||||||
obj.currentAcceleration = {...obj.acceleration};
|
obj.currentAcceleration = {...obj.acceleration};
|
||||||
|
|
||||||
obj.position.x += elapsedTime *
|
obj.position.x += elapsedTime *
|
||||||
(obj.velocity.x + 1/2 * obj.currentAcceleration.x * elapsedTime);
|
(obj.velocity.x + 1 / 2 * obj.currentAcceleration.x * elapsedTime);
|
||||||
obj.position.y += elapsedTime *
|
obj.position.y += elapsedTime *
|
||||||
(obj.velocity.y + 1/2 * obj.currentAcceleration.y * elapsedTime);
|
(obj.velocity.y + 1 / 2 * obj.currentAcceleration.y * elapsedTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Collisions
|
// Collisions
|
||||||
@ -313,7 +318,7 @@ export class System {
|
|||||||
}, {alive: true, startWith: i + 1});
|
}, {alive: true, startWith: i + 1});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recompute forces
|
// Recompute forces
|
||||||
this.computeForces();
|
this.computeForces();
|
||||||
|
|
||||||
@ -328,36 +333,41 @@ export class System {
|
|||||||
obj.velocity.y += obj.acceleration.y * elapsedTime;
|
obj.velocity.y += obj.acceleration.y * elapsedTime;
|
||||||
|
|
||||||
// Append to object history
|
// Append to object history
|
||||||
// TODO: enforce object history length
|
|
||||||
// TODO: store object color changes in history
|
|
||||||
obj.history.push({position: {...obj.position}});
|
obj.history.push({position: {...obj.position}});
|
||||||
|
|
||||||
|
// TODO: store object color changes in history
|
||||||
|
|
||||||
|
// Enforce object history length
|
||||||
|
while (obj.history.length > OBJECT_HISTORY_SIZE) {
|
||||||
|
obj.history.shift();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display objects info
|
// Display objects info
|
||||||
// First clear info from previous frame
|
// First clear info from previous frame
|
||||||
this.forEachObject((_obj, i) => {
|
this.forEachObject((_obj, i) => {
|
||||||
delete this.sim.info[`Object ${i}`];
|
delete this.sim.info[`Object ${i}`];
|
||||||
}, { alive: null });
|
}, {alive: null});
|
||||||
if (this.sim.getOption('debug.objectsInfo')) {
|
if (this.sim.getOption('debug.objectsInfo')) {
|
||||||
const aliveOnly = this.sim.getOption('debug.aliveObjects');
|
const aliveOnly = this.sim.getOption('debug.aliveObjects');
|
||||||
this.forEachObject((obj, i) => {
|
this.forEachObject((obj, i) => {
|
||||||
const speed = Math.pow(obj.velocity.x ** 2 + obj.velocity.y ** 2, 1/2);
|
const speed = Math.pow(obj.velocity.x ** 2 + obj.velocity.y ** 2, 1 / 2);
|
||||||
const accel = Math.pow(obj.acceleration.x ** 2 + obj.acceleration.y ** 2, 1/2);
|
const accel = Math.pow(obj.acceleration.x ** 2 + obj.acceleration.y ** 2, 1 / 2);
|
||||||
// Invert y so that the angle is counterclockwise from x-axis
|
// Invert y so that the angle is counterclockwise from x-axis
|
||||||
const direction = Math.atan2(-obj.velocity.y, obj.velocity.x) * 180 / Math.PI;
|
const direction = Math.atan2(-obj.velocity.y, obj.velocity.x) * 180 / Math.PI;
|
||||||
const accelDir = Math.atan2(-obj.acceleration.y, obj.acceleration.x) * 180 / Math.PI;
|
const accelDir = Math.atan2(-obj.acceleration.y, obj.acceleration.x) * 180 / Math.PI;
|
||||||
const {r, g, b} = obj.color;
|
const {r, g, b} = obj.color;
|
||||||
this.sim.info[`Object ${i}`] = [
|
this.sim.info[`Object ${i}`] = [
|
||||||
`<span style="background-color: rgb(${r},${g},${b});"> </span>`,
|
`<span style="background-color: rgb(${r},${g},${b});"> </span>`,
|
||||||
`${obj.position.x.toPrecision(4)}, `,
|
`${obj.position.x.toPrecision(4)}, `,
|
||||||
`${obj.position.y.toPrecision(4)}, `,
|
`${obj.position.y.toPrecision(4)}, `,
|
||||||
`${obj.mass.toPrecision(4)} kg, `,
|
`${obj.mass.toPrecision(4)} kg, `,
|
||||||
`${speed.toPrecision(2)} m/s, ${direction.toPrecision(2)}°`,
|
`${speed.toPrecision(2)} m/s, ${direction.toPrecision(2)}°`,
|
||||||
`${accel.toPrecision(2)} m/s<sup>2</sup>, ${accelDir.toPrecision(2)}°`,
|
`${accel.toPrecision(2)} m/s<sup>2</sup>, ${accelDir.toPrecision(2)}°`,
|
||||||
`Alive: ${obj.alive}`,
|
`Alive: ${obj.alive}`,
|
||||||
];
|
];
|
||||||
}, { alive: aliveOnly || null });
|
}, {alive: aliveOnly || null});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the objects
|
// Render the objects
|
||||||
@ -366,7 +376,7 @@ export class System {
|
|||||||
|
|
||||||
computeSystemCenter() {
|
computeSystemCenter() {
|
||||||
// Determine center of mass
|
// Determine center of mass
|
||||||
const { totalMass, count, totalMassLocation } =
|
const {totalMass, count, totalMassLocation} =
|
||||||
this.reduce((acc, obj) => ({
|
this.reduce((acc, obj) => ({
|
||||||
count: acc.count + 1,
|
count: acc.count + 1,
|
||||||
totalMass: acc.totalMass + obj.mass,
|
totalMass: acc.totalMass + obj.mass,
|
||||||
@ -387,11 +397,11 @@ export class System {
|
|||||||
|
|
||||||
// Determine average momentum
|
// Determine average momentum
|
||||||
const netMomentum = this.reduce((acc, obj) => ({
|
const netMomentum = this.reduce((acc, obj) => ({
|
||||||
x: acc.x + obj.mass * obj.velocity.x,
|
x: acc.x + obj.mass * obj.velocity.x,
|
||||||
y: acc.y + obj.mass * obj.velocity.y,
|
y: acc.y + obj.mass * obj.velocity.y,
|
||||||
}), { x: 0, y: 0 });
|
}), {x: 0, y: 0});
|
||||||
|
|
||||||
return { totalMass, count, totalMassLocation, centerOfMass, netMomentum };
|
return {totalMass, count, totalMassLocation, centerOfMass, netMomentum};
|
||||||
}
|
}
|
||||||
|
|
||||||
computeSystemAngularMomentum(centerOfMass) {
|
computeSystemAngularMomentum(centerOfMass) {
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
|
import {TOOLBAR_GROUP_CLASSNAME, WIDE_CLASSNAME} from './config.js';
|
||||||
export class ToolbarGroup {
|
export class ToolbarGroup {
|
||||||
sim = undefined;
|
sim = undefined;
|
||||||
toolbars = [];
|
toolbars = [];
|
||||||
|
|
||||||
constructor(sim) {
|
constructor(sim) {
|
||||||
this.sim = sim;
|
this.sim = sim;
|
||||||
const div = document.createElement('div');
|
this.div = document.createElement('div');
|
||||||
this.div = div;
|
this.sim.div.appendChild(this.div);
|
||||||
this.sim.div.appendChild(div);
|
this.div.classList.add(TOOLBAR_GROUP_CLASSNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
topRight() {
|
topRight() {
|
||||||
@ -16,6 +17,11 @@ export class ToolbarGroup {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wide() {
|
||||||
|
this.div.classList.add(WIDE_CLASSNAME);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
addToolbar(toolbar) {
|
addToolbar(toolbar) {
|
||||||
this.div.appendChild(toolbar.div);
|
this.div.appendChild(toolbar.div);
|
||||||
this.toolbars.push(toolbar);
|
this.toolbars.push(toolbar);
|
||||||
|
|||||||
3
zoom.js
3
zoom.js
@ -45,7 +45,8 @@ export class Zoom {
|
|||||||
// this.pointer.clearPointerHistory();
|
// this.pointer.clearPointerHistory();
|
||||||
|
|
||||||
// TODO: If paused, set panning velocity on resume
|
// TODO: If paused, set panning velocity on resume
|
||||||
if (this.sim.playing && velocity) {
|
// if (this.sim.playing && velocity) {
|
||||||
|
if (velocity) {
|
||||||
this.sim.panning = {
|
this.sim.panning = {
|
||||||
velocity: { ...velocity }
|
velocity: { ...velocity }
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user