fixed display vectors
This commit is contained in:
parent
bfaa2b2437
commit
d010d19495
@ -8,8 +8,7 @@ Uses `npm` for `eslint`.
|
|||||||
Screenshots
|
Screenshots
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||

|

|
||||||

|
|
||||||
|
|
||||||
TODO
|
TODO
|
||||||
----
|
----
|
||||||
@ -31,10 +30,7 @@ TODO
|
|||||||
- [ ] Undo "Clear Traces" Action
|
- [ ] Undo "Clear Traces" Action
|
||||||
- [ ] Undo "Reset
|
- [ ] Undo "Reset
|
||||||
- [ ] Time Control: Reverse Time
|
- [ ] Time Control: Reverse Time
|
||||||
- [x] 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)
|
||||||
- [x] Compute Net Angular Momentum
|
|
||||||
- [ ] Display Net Angular Momentum
|
|
||||||
- [ ] 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
|
||||||
|
|||||||
BIN
gravity-simulator-4.png
Normal file
BIN
gravity-simulator-4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
23
object.js
23
object.js
@ -180,22 +180,19 @@ export class MassObject {
|
|||||||
let velocity = {x: vx, y: vy};
|
let velocity = {x: vx, y: vy};
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
const pointerV = this.sim.pointer.latestVelocity;
|
const pointerV = this.sim.pointer.latestVelocity;
|
||||||
const scale = this.sim.display.scale;
|
|
||||||
// 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 * scale;
|
velocity.x = vx + pointerV.x / this.sim.timeScale;
|
||||||
velocity.y = vy + pointerV.y * scale;
|
velocity.y = vy + pointerV.y / this.sim.timeScale;
|
||||||
}
|
}
|
||||||
const speed = Math.sqrt(velocity.x ** 2, velocity.y ** 2);
|
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);
|
||||||
// Prevent negative numbers by adding 1
|
// Prevent negative numbers by adding 1
|
||||||
const arrowLength = Math.log(speed + 1) * vecScale;
|
// TODO: Make logarithmic vector length scale optional
|
||||||
|
const arrowLength = Math.log10(speed + 1) * vecScale + radius;
|
||||||
const endVx = x + arrowLength * Math.cos(arrowDirection);
|
const endVx = x + arrowLength * Math.cos(arrowDirection);
|
||||||
const endVy = y + arrowLength * Math.sin(arrowDirection);
|
const endVy = y + arrowLength * Math.sin(arrowDirection);
|
||||||
if (sim.getOption('debug.panningInfo')) {
|
|
||||||
console.log('velocity', {vecScale, isSelected, velocity, speed, arrowDirection, arrowLength, endVx, endVy});
|
|
||||||
}
|
|
||||||
const style = VELOCITY_VECTOR_COLOR === 'object color' ?
|
const style = VELOCITY_VECTOR_COLOR === 'object color' ?
|
||||||
`rgb(${r}, ${g}, ${b})` : VELOCITY_VECTOR_COLOR;
|
`rgb(${r}, ${g}, ${b})` : VELOCITY_VECTOR_COLOR;
|
||||||
sim.display.drawArrow(x, y, endVx, endVy, {
|
sim.display.drawArrow(x, y, endVx, endVy, {
|
||||||
@ -209,11 +206,13 @@ export class MassObject {
|
|||||||
|
|
||||||
// Draw arrow for acceleration
|
// Draw arrow for acceleration
|
||||||
if (sim.getOption('display.acceleration')) {
|
if (sim.getOption('display.acceleration')) {
|
||||||
const vecScale = this.sim.getOption('param.accelerationScale');
|
const vecScale = this.sim.getOption('display.accelerationScale');
|
||||||
const accelerationMagnitude = Math.sqrt(acceleration.x ** 2 + acceleration.y ** 2);
|
const accelerationMagnitude = Math.sqrt(acceleration.x ** 2 + acceleration.y ** 2) /
|
||||||
|
this.sim.display.scale;
|
||||||
const arrowDirection = Math.atan2(acceleration.y, acceleration.x);
|
const arrowDirection = Math.atan2(acceleration.y, acceleration.x);
|
||||||
// Prevent negative numbers by adding e
|
// Prevent negative numbers by adding 1
|
||||||
const arrowLength = Math.log(accelerationMagnitude + 1) * vecScale / this.sim.display.scale;
|
const arrowLength = Math.log10(accelerationMagnitude + 1) * vecScale + radius;
|
||||||
|
//const arrowLength = accelerationMagnitude * vecScale;
|
||||||
const endAx = x + arrowLength * Math.cos(arrowDirection);
|
const endAx = x + arrowLength * Math.cos(arrowDirection);
|
||||||
const endAy = y + arrowLength * Math.sin(arrowDirection);
|
const endAy = y + arrowLength * Math.sin(arrowDirection);
|
||||||
const style = ACCELERATION_VECTOR_COLOR === 'object color' ?
|
const style = ACCELERATION_VECTOR_COLOR === 'object color' ?
|
||||||
|
|||||||
@ -4,12 +4,12 @@ export const simOptions = {
|
|||||||
selection: ['Pause While Selecting', 'boolean', true],
|
selection: ['Pause While Selecting', 'boolean', true],
|
||||||
},
|
},
|
||||||
display: {
|
display: {
|
||||||
traces: ['Path Trace', 'boolean', true],
|
traces: ['Path Traces', 'boolean', true],
|
||||||
dashedTraces: ['Dashed', 'boolean', false, {tall: true}],
|
dashedTraces: ['Dashed', 'boolean', false, {tall: true}],
|
||||||
velocity: ['Velocity Vector', 'boolean', true],
|
velocity: ['Velocity Vectors', 'boolean', true],
|
||||||
acceleration: ['Accel Vector', 'boolean', true],
|
acceleration: ['Accel Vectors', 'boolean', true],
|
||||||
velocityScale: ['Velocity<br>Vec Scale', 'number', 20],
|
velocityScale: ['Velocity<br>Vec Scale', 'number', 80],
|
||||||
accelerationScale: ['Accel<br>Vec Scale', 'number', 20],
|
accelerationScale: ['Accel<br>Vec Scale', 'number', 800],
|
||||||
},
|
},
|
||||||
collision: {
|
collision: {
|
||||||
merge: ['Merge Masses<br>on Collision', 'boolean', true, {wide: true}],
|
merge: ['Merge Masses<br>on Collision', 'boolean', true, {wide: true}],
|
||||||
@ -18,7 +18,6 @@ export const simOptions = {
|
|||||||
gravity: ['Gravity', 'number', 1],
|
gravity: ['Gravity', 'number', 1],
|
||||||
timeScale: ['Time Scale', 'number', 0.1],
|
timeScale: ['Time Scale', 'number', 0.1],
|
||||||
massCreationRate: ['Mass Creation Rate', 'number', 1],
|
massCreationRate: ['Mass Creation Rate', 'number', 1],
|
||||||
massAcceleration: ['Mass Rate Accel', 'boolean', false, {wide: true}],
|
|
||||||
},
|
},
|
||||||
debug: {
|
debug: {
|
||||||
objectsInfo: ['Objects Info', 'boolean', false],
|
objectsInfo: ['Objects Info', 'boolean', false],
|
||||||
|
|||||||
11
system.js
11
system.js
@ -157,8 +157,8 @@ export class System {
|
|||||||
// Convert pointer velocity to simulation scale
|
// Convert pointer velocity to simulation scale
|
||||||
// Including time scale - if time is slow, our motion is relatively faster
|
// Including time scale - if time is slow, our motion is relatively faster
|
||||||
const pointer = {...this.sim.pointer.latestVelocity};
|
const pointer = {...this.sim.pointer.latestVelocity};
|
||||||
obj.velocity.x = pointer.x / this.sim.display.scale;
|
obj.velocity.x = pointer.x / this.sim.display.scale / this.sim.timeScale;
|
||||||
obj.velocity.y = pointer.y / this.sim.display.scale;
|
obj.velocity.y = pointer.y / this.sim.display.scale / this.sim.timeScale;
|
||||||
if (this.sim.panning?.velocity) {
|
if (this.sim.panning?.velocity) {
|
||||||
obj.velocity.x += this.sim.panning.velocity.x;
|
obj.velocity.x += this.sim.panning.velocity.x;
|
||||||
obj.velocity.y += this.sim.panning.velocity.y;
|
obj.velocity.y += this.sim.panning.velocity.y;
|
||||||
@ -239,12 +239,7 @@ 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');
|
let massCreationRate = this.sim.getOption('param.massCreationRate') / this.sim.display.scale;
|
||||||
// Mass creation rate acceleration
|
|
||||||
if (this.sim.getOption('param.massAcceleration')) {
|
|
||||||
// TODO: Separate parameter for mass creation acceleration rate
|
|
||||||
massCreationRate *= obj.rawAge;
|
|
||||||
}
|
|
||||||
// Keep consistent time scale
|
// Keep consistent time scale
|
||||||
obj.mass += massCreationRate * elapsedTime / this.sim.timeScale;
|
obj.mass += massCreationRate * elapsedTime / this.sim.timeScale;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,6 @@ export class UtilityTool extends Tool {
|
|||||||
constructor(container) {
|
constructor(container) {
|
||||||
super(container);
|
super(container);
|
||||||
|
|
||||||
const zeroVelocity = document.createElement('button');
|
|
||||||
const clearTraces = document.createElement('button');
|
const clearTraces = document.createElement('button');
|
||||||
const currentTime = document.createElement('button');
|
const currentTime = document.createElement('button');
|
||||||
const clearDebug = document.createElement('button');
|
const clearDebug = document.createElement('button');
|
||||||
@ -47,39 +46,18 @@ export class UtilityTool extends Tool {
|
|||||||
this.currentTimeEl = currentTime;
|
this.currentTimeEl = currentTime;
|
||||||
|
|
||||||
this.div.appendChild(currentTime);
|
this.div.appendChild(currentTime);
|
||||||
this.div.appendChild(zeroVelocity);
|
|
||||||
this.div.appendChild(clearTraces);
|
this.div.appendChild(clearTraces);
|
||||||
this.div.appendChild(clearDebug);
|
this.div.appendChild(clearDebug);
|
||||||
|
|
||||||
zeroVelocity.classList.add(WIDE_CLASSNAME);
|
|
||||||
clearTraces.classList.add(WIDE_CLASSNAME);
|
clearTraces.classList.add(WIDE_CLASSNAME);
|
||||||
currentTime.classList.add(TOOL_INFO_CLASSNAME);
|
currentTime.classList.add(TOOL_INFO_CLASSNAME);
|
||||||
currentTime.classList.add(WIDE_CLASSNAME);
|
currentTime.classList.add(WIDE_CLASSNAME);
|
||||||
clearDebug.classList.add(WIDE_CLASSNAME);
|
clearDebug.classList.add(WIDE_CLASSNAME);
|
||||||
|
|
||||||
zeroVelocity.innerHTML = 'Zero Momentum';
|
|
||||||
clearTraces.innerHTML = 'Clear Traces';
|
clearTraces.innerHTML = 'Clear Traces';
|
||||||
currentTime.innerHTML = this.timeText;
|
currentTime.innerHTML = this.timeText;
|
||||||
clearDebug.innerHTML = 'Clear Debug';
|
clearDebug.innerHTML = 'Clear Debug';
|
||||||
|
|
||||||
zeroVelocity.addEventListener('click', () => {
|
|
||||||
// Determine center of mass and average momentum
|
|
||||||
const { totalMass, netMomentum } = this.sim.system.computeSystemCenter();
|
|
||||||
const netVelocity = {
|
|
||||||
x: netMomentum.x / totalMass,
|
|
||||||
y: netMomentum.y / totalMass,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Apply offset to all object velocities
|
|
||||||
this.sim.system.forEachObject(obj => {
|
|
||||||
obj.velocity.x -= netVelocity.x;
|
|
||||||
obj.velocity.y -= netVelocity.y;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Cancel panning
|
|
||||||
this.sim.panning = undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
clearTraces.addEventListener('click', () => {
|
clearTraces.addEventListener('click', () => {
|
||||||
// Obliterate object histories
|
// Obliterate object histories
|
||||||
this.sim.system.forEachObject(obj => {
|
this.sim.system.forEachObject(obj => {
|
||||||
|
|||||||
27
tool/zoom.js
27
tool/zoom.js
@ -3,7 +3,6 @@ import {
|
|||||||
ZOOM_IN_FACTOR,
|
ZOOM_IN_FACTOR,
|
||||||
ZOOM_OUT_FACTOR,
|
ZOOM_OUT_FACTOR,
|
||||||
WIDE_CLASSNAME,
|
WIDE_CLASSNAME,
|
||||||
TALL_CLASSNAME,
|
|
||||||
TOOL_INFO_CLASSNAME,
|
TOOL_INFO_CLASSNAME,
|
||||||
} from '../config.js';
|
} from '../config.js';
|
||||||
|
|
||||||
@ -19,21 +18,22 @@ export class Zoom extends Tool {
|
|||||||
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');
|
||||||
|
const zeroVelocity = document.createElement('button');
|
||||||
|
|
||||||
this.div.appendChild(currentScale);
|
this.div.appendChild(currentScale);
|
||||||
this.div.appendChild(zoomOut);
|
this.div.appendChild(zoomOut);
|
||||||
this.div.appendChild(zoomIn);
|
this.div.appendChild(zoomIn);
|
||||||
this.div.appendChild(zoomAll);
|
this.div.appendChild(zoomAll);
|
||||||
|
this.div.appendChild(zeroVelocity);
|
||||||
|
|
||||||
currentScale.classList.add(WIDE_CLASSNAME);
|
currentScale.classList.add(WIDE_CLASSNAME);
|
||||||
currentScale.classList.add(TOOL_INFO_CLASSNAME);
|
currentScale.classList.add(TOOL_INFO_CLASSNAME);
|
||||||
zoomAll.classList.add(WIDE_CLASSNAME);
|
|
||||||
zoomAll.classList.add(TALL_CLASSNAME);
|
|
||||||
|
|
||||||
currentScale.innerHTML = this.displayScaleText;
|
currentScale.innerHTML = this.displayScaleText;
|
||||||
zoomOut.innerHTML = 'Zoom<br>Out';
|
zoomOut.innerHTML = 'Zoom<br>Out';
|
||||||
zoomIn.innerHTML = 'Zoom<br>In';
|
zoomIn.innerHTML = 'Zoom<br>In';
|
||||||
zoomAll.innerHTML = 'Zoom to Fit';
|
zoomAll.innerHTML = 'Zoom to Fit';
|
||||||
|
zeroVelocity.innerHTML = 'Zero Momentum';
|
||||||
|
|
||||||
this.sim.onZoom(() => {
|
this.sim.onZoom(() => {
|
||||||
currentScale.innerHTML = this.displayScaleText;
|
currentScale.innerHTML = this.displayScaleText;
|
||||||
@ -60,7 +60,7 @@ export class Zoom extends Tool {
|
|||||||
const y = (box.start.y + box.end.y) / 2;
|
const y = (box.start.y + box.end.y) / 2;
|
||||||
const widthRatio = Math.abs(box.start.x - box.end.x) / this.sim.display.width;
|
const widthRatio = Math.abs(box.start.x - box.end.x) / this.sim.display.width;
|
||||||
const heightRatio = Math.abs(box.start.y - box.end.y) / this.sim.display.height;
|
const heightRatio = Math.abs(box.start.y - box.end.y) / this.sim.display.height;
|
||||||
const ratio = Math.max(widthRatio, heightRatio) * 2.5;
|
const ratio = Math.max(widthRatio, heightRatio) * 4;
|
||||||
const factor = Math.ceil(Math.log2(1 / ratio));
|
const factor = Math.ceil(Math.log2(1 / ratio));
|
||||||
|
|
||||||
// Determine average momentum and set panning velocity to match
|
// Determine average momentum and set panning velocity to match
|
||||||
@ -71,5 +71,24 @@ export class Zoom extends Tool {
|
|||||||
};
|
};
|
||||||
this.sim.scheduleZoom({x, y}, factor, netVelocity)
|
this.sim.scheduleZoom({x, y}, factor, netVelocity)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
zeroVelocity.addEventListener('click', () => {
|
||||||
|
// Determine center of mass and average momentum
|
||||||
|
const { totalMass, netMomentum } = this.sim.system.computeSystemCenter();
|
||||||
|
const netVelocity = {
|
||||||
|
x: netMomentum.x / totalMass,
|
||||||
|
y: netMomentum.y / totalMass,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apply offset to all object velocities
|
||||||
|
this.sim.system.forEachObject(obj => {
|
||||||
|
obj.velocity.x -= netVelocity.x;
|
||||||
|
obj.velocity.y -= netVelocity.y;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel panning
|
||||||
|
this.sim.panning = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user