refactoring
This commit is contained in:
parent
dcc630a2e6
commit
9070617bf4
@ -8,6 +8,7 @@ Uses `npm` for `eslint`.
|
|||||||
TODO
|
TODO
|
||||||
----
|
----
|
||||||
|
|
||||||
|
- [ ] Parameter Slider
|
||||||
- [ ] Selection Box
|
- [ ] Selection Box
|
||||||
- [ ] Object List
|
- [ ] Object List
|
||||||
- [ ] Object Detail
|
- [ ] Object Detail
|
||||||
@ -17,11 +18,17 @@ TODO
|
|||||||
- [ ] Zoom Easing
|
- [ ] Zoom Easing
|
||||||
- [ ] 2-touch Pan & Zoom
|
- [ ] 2-touch Pan & Zoom
|
||||||
- [ ] Multi-touch Mass Create
|
- [ ] Multi-touch Mass Create
|
||||||
|
- [ ] Camera Rotation
|
||||||
|
- [ ] 2-touch Rotation
|
||||||
|
- [ ] Spinning Frame
|
||||||
- [ ] Undo feature:
|
- [ ] Undo feature:
|
||||||
- [ ] Undo "Clear Traces" Action
|
- [ ] Undo "Clear Traces" Action
|
||||||
- [ ] Undo "Reset
|
- [ ] Undo "Reset
|
||||||
|
- [ ] Time Control: Reverse Time
|
||||||
- [ ] 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)
|
- [ ] Track farthest reaches, min/max in each dimension (x, y)
|
||||||
- [ ] Enabling Zoom to Fit Traces
|
- [ ] Enabling Zoom to Fit Traces
|
||||||
- [ ] Tool: Zero Angular Momentum
|
- [x] Compute Net Angular Momentum
|
||||||
|
- [ ] Display Net Angular Momentum
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
// DISPLAY
|
// DISPLAY
|
||||||
export const DISPLAY_OBJECTS_INFO = false;
|
export const DISPLAY_OBJECTS_INFO = false;
|
||||||
export const DISPLAY_CURSOR_INFO = false;
|
export const DISPLAY_CURSOR_INFO = true;
|
||||||
export const DISPLAY_CANVAS_SIZE = false;
|
export const DISPLAY_CANVAS_SIZE = false;
|
||||||
export const DISPLAY_CURRENT_SCALE = false;
|
export const DISPLAY_CURRENT_SCALE = false;
|
||||||
export const DISPLAY_CURRENT_MODE = false;
|
export const DISPLAY_CURRENT_MODE = false;
|
||||||
|
export const DISPLAY_PANNING_INFO = true;
|
||||||
|
|
||||||
// VELOCITY
|
// VELOCITY
|
||||||
export const VELOCITY_VECTOR_SCALE = 8E0;
|
export const VELOCITY_VECTOR_SCALE = 8E0;
|
||||||
@ -43,6 +44,7 @@ export const TOOL_INFO_CLASSNAME = 'lhg-tool-info';
|
|||||||
export const TOOLBAR_CLASSNAME = 'lhg-toolbar';
|
export const TOOLBAR_CLASSNAME = 'lhg-toolbar';
|
||||||
export const TOOLBAR_HEADER_CLASSNAME = 'lhg-toolbar-header';
|
export const TOOLBAR_HEADER_CLASSNAME = 'lhg-toolbar-header';
|
||||||
export const WIDE_CLASSNAME = 'lhg-wide';
|
export const WIDE_CLASSNAME = 'lhg-wide';
|
||||||
|
export const OVERLAY_INFO_BOX_CLASSNAME = 'lhg-overlay-info-box';
|
||||||
|
|
||||||
// EVENT NAMES
|
// EVENT NAMES
|
||||||
export const EVENT_MODE_LEAVE = 'lhg-mode-leave';
|
export const EVENT_MODE_LEAVE = 'lhg-mode-leave';
|
||||||
|
|||||||
42
objects.js
42
objects.js
@ -313,4 +313,46 @@ export class Objects {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
computeSystemCenter() {
|
||||||
|
// Determine center of mass
|
||||||
|
const { totalMass, count, totalMassLocation } =
|
||||||
|
this.sim.objects.reduce((acc, obj) => ({
|
||||||
|
count: acc.count + 1,
|
||||||
|
totalMass: acc.totalMass + obj.mass,
|
||||||
|
totalMassLocation: {
|
||||||
|
x: acc.totalMassLocation.x + obj.position.x * obj.mass,
|
||||||
|
y: acc.totalMassLocation.y + obj.position.y * obj.mass,
|
||||||
|
},
|
||||||
|
}), {
|
||||||
|
totalMassLocation: {x: 0, y: 0},
|
||||||
|
totalMass: 0,
|
||||||
|
count: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!count) return;
|
||||||
|
|
||||||
|
const centerOfMass = {
|
||||||
|
x: totalMassLocation.x / totalMass,
|
||||||
|
y: totalMassLocation.y / totalMass,
|
||||||
|
};
|
||||||
|
|
||||||
|
return { totalMass, count, totalMassLocation, centerOfMass };
|
||||||
|
}
|
||||||
|
|
||||||
|
computeSystemAngularMomentum(centerOfMass) {
|
||||||
|
return this.reduce((acc, obj) => {
|
||||||
|
// Angular momentum for each object is m * s / d
|
||||||
|
// where d is the distance of the object from the global center of mass
|
||||||
|
// and s is the magnitude of the cross product of v and r
|
||||||
|
const r = {
|
||||||
|
x: obj.position.x - centerOfMass.x,
|
||||||
|
y: obj.position.y - centerOfMass.y,
|
||||||
|
};
|
||||||
|
const v = obj.velocity;
|
||||||
|
const s = v.x * r.y - v.y * r.x;
|
||||||
|
const d = Math.sqrt(r.x ** 2 + r.y ** 2);
|
||||||
|
return acc + obj.mass * s / d;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { OVERLAY_INFO_BOX_CLASSNAME } from './config.js';
|
||||||
|
|
||||||
export class Overlay {
|
export class Overlay {
|
||||||
sim = undefined;
|
sim = undefined;
|
||||||
constructor(sim) {
|
constructor(sim) {
|
||||||
@ -7,12 +9,7 @@ export class Overlay {
|
|||||||
const infoBox = document.createElement('div');
|
const infoBox = document.createElement('div');
|
||||||
this.sim.div.appendChild(infoBox);
|
this.sim.div.appendChild(infoBox);
|
||||||
this.infoBox = infoBox;
|
this.infoBox = infoBox;
|
||||||
infoBox.style.position = 'relative';
|
infoBox.classList.add(OVERLAY_INFO_BOX_CLASSNAME);
|
||||||
infoBox.style.display = 'inline-block';
|
|
||||||
infoBox.style.top = 0;
|
|
||||||
infoBox.style.left = '14em';
|
|
||||||
infoBox.width = 'fit-content';
|
|
||||||
infoBox.style.zIndex = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInfo() {
|
renderInfo() {
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export class Pointer {
|
|||||||
|
|
||||||
el.addEventListener('pointermove', e => {
|
el.addEventListener('pointermove', e => {
|
||||||
if (DISPLAY_CURSOR_INFO) {
|
if (DISPLAY_CURSOR_INFO) {
|
||||||
this.sim.info['pointermove'] = [`${e.clientX}, `, `${e.clientY}`];
|
this.sim.info['pointermove'] = [`${e.clientX.toPrecision(6)}, `, `${e.clientY.toPrecision(6)}`];
|
||||||
}
|
}
|
||||||
this.handlePointerMove({x: e.clientX, y: e.clientY});
|
this.handlePointerMove({x: e.clientX, y: e.clientY});
|
||||||
});
|
});
|
||||||
|
|||||||
10
simulator.js
10
simulator.js
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
DISPLAY_CURRENT_MODE,
|
DISPLAY_CURRENT_MODE,
|
||||||
DISPLAY_CURRENT_SCALE,
|
DISPLAY_CURRENT_SCALE,
|
||||||
|
DISPLAY_PANNING_INFO,
|
||||||
EVENT_ZOOM,
|
EVENT_ZOOM,
|
||||||
SCALE_POWER_MAX,
|
SCALE_POWER_MAX,
|
||||||
SCALE_POWER_MIN,
|
SCALE_POWER_MIN,
|
||||||
@ -151,7 +152,9 @@ export class Sim {
|
|||||||
const timeScale = this.getOption('param.timeScale');
|
const timeScale = this.getOption('param.timeScale');
|
||||||
const elapsedTime = (currentTime - this.rawTime) * timeScale;
|
const elapsedTime = (currentTime - this.rawTime) * timeScale;
|
||||||
this.rawTime = currentTime;
|
this.rawTime = currentTime;
|
||||||
this.time += elapsedTime;
|
if (this.playing) {
|
||||||
|
this.time += elapsedTime;
|
||||||
|
}
|
||||||
|
|
||||||
if (DISPLAY_CURRENT_MODE) {
|
if (DISPLAY_CURRENT_MODE) {
|
||||||
this.info['Mode'] = this.getCurrentMode();
|
this.info['Mode'] = this.getCurrentMode();
|
||||||
@ -166,6 +169,11 @@ export class Sim {
|
|||||||
this.info['Scale'] = this.getScaleDisplay();
|
this.info['Scale'] = this.getScaleDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DISPLAY_PANNING_INFO) {
|
||||||
|
const {x, y} = this.panning?.velocity ?? {};
|
||||||
|
this.info['Panning'] = [`${x?.toPrecision(6)}, `, y?.toPrecision(6)];
|
||||||
|
}
|
||||||
|
|
||||||
this.display.computePanning(elapsedTime);
|
this.display.computePanning(elapsedTime);
|
||||||
this.objects.computeFrame(elapsedTime);
|
this.objects.computeFrame(elapsedTime);
|
||||||
this.overlay.renderInfo();
|
this.overlay.renderInfo();
|
||||||
|
|||||||
29
style.css
29
style.css
@ -31,8 +31,8 @@ div.lhg-toolbar {
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
margin: 0.5EM;
|
margin: 0.5em;
|
||||||
border-radius: 0.5EM;
|
border-radius: 0.5em;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-color: #282;
|
border-color: #282;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
@ -42,9 +42,9 @@ div.lhg-tool {
|
|||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 12EM;
|
width: 12em;
|
||||||
/* padding: 0.5EM; */
|
/* padding: 0.5em; */
|
||||||
margin: 0.5EM;
|
margin: 0.5em;
|
||||||
text-align: middle;
|
text-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,14 +59,14 @@ div.lhg-tool button, div.lhg-tool input {
|
|||||||
width: 6em;
|
width: 6em;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
color: #5f5;
|
color: #5f5;
|
||||||
border-radius: 0.5EM;
|
border-radius: 0.5em;
|
||||||
border-color: #000;
|
border-color: #000;
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
padding-top: 0.5EM;
|
padding-top: 0.5em;
|
||||||
padding-bottom: 0.5EM;
|
padding-bottom: 0.5em;
|
||||||
padding-left: 0EM;
|
padding-left: 0em;
|
||||||
padding-right: 0EM;
|
padding-right: 0em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
@ -74,7 +74,7 @@ div.lhg-tool button, div.lhg-tool input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.lhg-tool input {
|
div.lhg-tool input {
|
||||||
width: 6EM;
|
width: 6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.lhg-tool button:hover {
|
div.lhg-tool button:hover {
|
||||||
@ -107,3 +107,10 @@ div.lhg-tool .lhg-wide {
|
|||||||
width: 12em;
|
width: 12em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.lhg-overlay-info-box {
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
left: 14em;
|
||||||
|
width: fit-content;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|||||||
43
tool/zoom.js
43
tool/zoom.js
@ -92,50 +92,17 @@ export class Zoom extends Tool {
|
|||||||
zeroVelocity.addEventListener('click', () => {
|
zeroVelocity.addEventListener('click', () => {
|
||||||
// TODO: Zero net angular momentum
|
// TODO: Zero net angular momentum
|
||||||
// Determine center of mass
|
// Determine center of mass
|
||||||
const { totalMass, count, totalMassLocation } =
|
const { totalMass, centerOfMass } =
|
||||||
this.sim.objects.reduce((acc, obj) => ({
|
this.sim.objects.computeSystemCenter();
|
||||||
count: acc.count + 1,
|
|
||||||
totalMass: acc.totalMass + obj.mass,
|
|
||||||
totalMassLocation: {
|
|
||||||
x: acc.totalMassLocation.x + obj.position.x * obj.mass,
|
|
||||||
y: acc.totalMassLocation.y + obj.position.y * obj.mass,
|
|
||||||
},
|
|
||||||
}), {
|
|
||||||
totalMassLocation: {x: 0, y: 0},
|
|
||||||
totalMass: 0,
|
|
||||||
count: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!count) return;
|
|
||||||
|
|
||||||
const centerOfMass = {
|
|
||||||
x: totalMassLocation.x / totalMass,
|
|
||||||
y: totalMassLocation.y / totalMass,
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log({totalMass, count, totalMassLocation, centerOfMass});
|
|
||||||
|
|
||||||
// Determine total angular momentum
|
// Determine total angular momentum
|
||||||
const netAngularMomentum = this.sim.objects.reduce((acc, obj, idx) => {
|
const netAngularMomentum = this.sim.objects
|
||||||
// Angular momentum for each object is m * s / d
|
.computeSystemAngularMomentum(centerOfMass);
|
||||||
// where d is the distance of the object from the global center of mass
|
|
||||||
// and s is the magnitude of the cross product of v and r
|
|
||||||
const r = {
|
|
||||||
x: obj.position.x - centerOfMass.x,
|
|
||||||
y: obj.position.y - centerOfMass.y,
|
|
||||||
};
|
|
||||||
const v = obj.velocity;
|
|
||||||
const s = v.x * r.y - v.y * r.x;
|
|
||||||
const d = Math.sqrt(r.x ** 2 + r.y ** 2);
|
|
||||||
console.log(`obj ${idx} s`, s, 'd', d);
|
|
||||||
return acc + obj.mass * s / d;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
console.log('net angular momentum', netAngularMomentum);
|
console.log('net angular momentum', netAngularMomentum);
|
||||||
const netAngularVelocity = netAngularMomentum / totalMass;
|
const netAngularVelocity = netAngularMomentum / totalMass;
|
||||||
console.log('net angular velocity', netAngularVelocity);
|
console.log('net angular velocity', netAngularVelocity);
|
||||||
|
|
||||||
// TODO: Apply rotation...
|
// TODO: Camera rotation
|
||||||
|
|
||||||
// Determine average momentum
|
// Determine average momentum
|
||||||
const netMomentum = this.sim.objects.reduce((acc, obj) => ({
|
const netMomentum = this.sim.objects.reduce((acc, obj) => ({
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user