refactoring
This commit is contained in:
parent
dcc630a2e6
commit
9070617bf4
@ -8,6 +8,7 @@ Uses `npm` for `eslint`.
|
||||
TODO
|
||||
----
|
||||
|
||||
- [ ] Parameter Slider
|
||||
- [ ] Selection Box
|
||||
- [ ] Object List
|
||||
- [ ] Object Detail
|
||||
@ -17,11 +18,17 @@ TODO
|
||||
- [ ] Zoom Easing
|
||||
- [ ] 2-touch Pan & Zoom
|
||||
- [ ] Multi-touch Mass Create
|
||||
- [ ] Camera Rotation
|
||||
- [ ] 2-touch Rotation
|
||||
- [ ] Spinning Frame
|
||||
- [ ] Undo feature:
|
||||
- [ ] Undo "Clear Traces" Action
|
||||
- [ ] Undo "Reset
|
||||
- [ ] Time Control: Reverse Time
|
||||
- [ ] Save to LocalStorage
|
||||
- [ ] 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
|
||||
- [x] Compute Net Angular Momentum
|
||||
- [ ] Display Net Angular Momentum
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
// DISPLAY
|
||||
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_CURRENT_SCALE = false;
|
||||
export const DISPLAY_CURRENT_MODE = false;
|
||||
export const DISPLAY_PANNING_INFO = true;
|
||||
|
||||
// VELOCITY
|
||||
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_HEADER_CLASSNAME = 'lhg-toolbar-header';
|
||||
export const WIDE_CLASSNAME = 'lhg-wide';
|
||||
export const OVERLAY_INFO_BOX_CLASSNAME = 'lhg-overlay-info-box';
|
||||
|
||||
// EVENT NAMES
|
||||
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 {
|
||||
sim = undefined;
|
||||
constructor(sim) {
|
||||
@ -7,12 +9,7 @@ export class Overlay {
|
||||
const infoBox = document.createElement('div');
|
||||
this.sim.div.appendChild(infoBox);
|
||||
this.infoBox = infoBox;
|
||||
infoBox.style.position = 'relative';
|
||||
infoBox.style.display = 'inline-block';
|
||||
infoBox.style.top = 0;
|
||||
infoBox.style.left = '14em';
|
||||
infoBox.width = 'fit-content';
|
||||
infoBox.style.zIndex = 1;
|
||||
infoBox.classList.add(OVERLAY_INFO_BOX_CLASSNAME);
|
||||
}
|
||||
|
||||
renderInfo() {
|
||||
|
||||
@ -26,7 +26,7 @@ export class Pointer {
|
||||
|
||||
el.addEventListener('pointermove', e => {
|
||||
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});
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import {
|
||||
DISPLAY_CURRENT_MODE,
|
||||
DISPLAY_CURRENT_SCALE,
|
||||
DISPLAY_PANNING_INFO,
|
||||
EVENT_ZOOM,
|
||||
SCALE_POWER_MAX,
|
||||
SCALE_POWER_MIN,
|
||||
@ -151,7 +152,9 @@ export class Sim {
|
||||
const timeScale = this.getOption('param.timeScale');
|
||||
const elapsedTime = (currentTime - this.rawTime) * timeScale;
|
||||
this.rawTime = currentTime;
|
||||
if (this.playing) {
|
||||
this.time += elapsedTime;
|
||||
}
|
||||
|
||||
if (DISPLAY_CURRENT_MODE) {
|
||||
this.info['Mode'] = this.getCurrentMode();
|
||||
@ -166,6 +169,11 @@ export class Sim {
|
||||
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.objects.computeFrame(elapsedTime);
|
||||
this.overlay.renderInfo();
|
||||
|
||||
29
style.css
29
style.css
@ -31,8 +31,8 @@ div.lhg-toolbar {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: fit-content;
|
||||
margin: 0.5EM;
|
||||
border-radius: 0.5EM;
|
||||
margin: 0.5em;
|
||||
border-radius: 0.5em;
|
||||
border-width: 1px;
|
||||
border-color: #282;
|
||||
border-style: solid;
|
||||
@ -42,9 +42,9 @@ div.lhg-tool {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 12EM;
|
||||
/* padding: 0.5EM; */
|
||||
margin: 0.5EM;
|
||||
width: 12em;
|
||||
/* padding: 0.5em; */
|
||||
margin: 0.5em;
|
||||
text-align: middle;
|
||||
}
|
||||
|
||||
@ -59,14 +59,14 @@ div.lhg-tool button, div.lhg-tool input {
|
||||
width: 6em;
|
||||
background-color: #333;
|
||||
color: #5f5;
|
||||
border-radius: 0.5EM;
|
||||
border-radius: 0.5em;
|
||||
border-color: #000;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
padding-top: 0.5EM;
|
||||
padding-bottom: 0.5EM;
|
||||
padding-left: 0EM;
|
||||
padding-right: 0EM;
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
padding-left: 0em;
|
||||
padding-right: 0em;
|
||||
text-align: center;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
@ -74,7 +74,7 @@ div.lhg-tool button, div.lhg-tool input {
|
||||
}
|
||||
|
||||
div.lhg-tool input {
|
||||
width: 6EM;
|
||||
width: 6em;
|
||||
}
|
||||
|
||||
div.lhg-tool button:hover {
|
||||
@ -107,3 +107,10 @@ div.lhg-tool .lhg-wide {
|
||||
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', () => {
|
||||
// TODO: Zero net angular momentum
|
||||
// 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,
|
||||
};
|
||||
|
||||
console.log({totalMass, count, totalMassLocation, centerOfMass});
|
||||
const { totalMass, centerOfMass } =
|
||||
this.sim.objects.computeSystemCenter();
|
||||
|
||||
// Determine total angular momentum
|
||||
const netAngularMomentum = this.sim.objects.reduce((acc, obj, idx) => {
|
||||
// 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);
|
||||
console.log(`obj ${idx} s`, s, 'd', d);
|
||||
return acc + obj.mass * s / d;
|
||||
}, 0);
|
||||
|
||||
const netAngularMomentum = this.sim.objects
|
||||
.computeSystemAngularMomentum(centerOfMass);
|
||||
console.log('net angular momentum', netAngularMomentum);
|
||||
const netAngularVelocity = netAngularMomentum / totalMass;
|
||||
console.log('net angular velocity', netAngularVelocity);
|
||||
|
||||
// TODO: Apply rotation...
|
||||
// TODO: Camera rotation
|
||||
|
||||
// Determine average momentum
|
||||
const netMomentum = this.sim.objects.reduce((acc, obj) => ({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user