objects reducer, and some angular momentum calculations

This commit is contained in:
Lentil Hoffman 2025-12-29 13:39:23 -06:00
parent bb331a8a40
commit e434726be9
Signed by: lentil
GPG Key ID: 0F5B99F3F4D0C087
5 changed files with 89 additions and 37 deletions

View File

@ -72,7 +72,7 @@ export class Display {
} }
drawObjects() { drawObjects() {
this.sim.objects.forEachObject(obj => obj.drawObject(this.sim), null); this.sim.objects.forEachObject(obj => obj.drawObject(this.sim), {alive: null});
} }
drawArrow(startX, startY, endX, endY, {style, width, arrowhead, arrowheadLength, fill, ifShort}) { drawArrow(startX, startY, endX, endY, {style, width, arrowhead, arrowheadLength, fill, ifShort}) {

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -86,25 +86,25 @@ export class Objects {
} }
get boundingBox() { get boundingBox() {
const box = { const box = this.reduce((acc, obj) => {
if (acc.start.x === undefined) {
acc.start = {...obj.position};
acc.end = {...obj.position};
} else {
if (obj.position.x < acc.start.x) acc.start.x = obj.position.x;
if (obj.position.x > acc.end.x) acc.end.x = obj.position.x;
if (obj.position.y < acc.start.y) acc.start.y = obj.position.y;
if (obj.position.y > acc.end.y) acc.end.y = obj.position.y;
}
}, {
start: {x: undefined, y: undefined}, start: {x: undefined, y: undefined},
end: {x: undefined, y: undefined}, end: {x: undefined, y: undefined},
};
this.forEachObject(obj => {
if (box.start.x === undefined) {
box.start = {...obj.position};
box.end = {...obj.position};
} else {
if (obj.position.x < box.start.x) box.start.x = obj.position.x;
if (obj.position.x > box.end.x) box.end.x = obj.position.x;
if (obj.position.y < box.start.y) box.start.y = obj.position.y;
if (obj.position.y > box.end.y) box.end.y = obj.position.y;
}
}); });
box.start.x -= ZOOM_TO_FIT_PADDING;
box.start.y -= ZOOM_TO_FIT_PADDING; box.start.x = (box.start.x ?? 0) - ZOOM_TO_FIT_PADDING;
box.end.x += ZOOM_TO_FIT_PADDING; box.start.y = (box.start.y ?? 0) - ZOOM_TO_FIT_PADDING;
box.end.y += ZOOM_TO_FIT_PADDING; box.end.x = (box.end.x ?? 0) + ZOOM_TO_FIT_PADDING;
box.end.y = (box.end.y ?? 0) + ZOOM_TO_FIT_PADDING;
return box; return box;
} }
@ -155,9 +155,9 @@ export class Objects {
} }
// cb: (obj, idx) => {} // cb: (obj, idx) => {}
// TODO: Reducer forEachObject(cb, {alive, startWith} = {}) {
forEachObject(cb, alive = true, startWith = 0) { if (alive === undefined) alive = true;
for (let i = startWith; i < this.objects.length; i++) { for (let i = startWith ?? 0; i < this.objects.length; i++) {
const obj = this.objects[i]; const obj = this.objects[i];
if (alive === null || alive == obj.alive) { if (alive === null || alive == obj.alive) {
const ret = cb(obj, i); const ret = cb(obj, i);
@ -166,6 +166,19 @@ export class Objects {
} }
} }
// cb: (acc, obj, idx) => {}
reduce(cb, initial, opts) {
let acc = initial;
console.log('reduce, initial', acc);
this.forEachObject((obj, idx) => {
const ret = cb(acc, obj, idx);
if (ret !== undefined) {
acc = ret;
}
}, opts);
return acc;
}
computeForces() { computeForces() {
const gravity = this.sim.getOption('param.gravity'); const gravity = this.sim.getOption('param.gravity');
if (this.objects.length < 2) return; if (this.objects.length < 2) return;
@ -183,7 +196,7 @@ export class Objects {
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 });
}, true, i + 1); }, {alive: true, startWith: i + 1});
}); });
// Also compute acceleration // Also compute acceleration
this.forEachObject(obj => { this.forEachObject(obj => {
@ -262,7 +275,7 @@ export class Objects {
T.alive = false; T.alive = false;
T.forces = []; T.forces = [];
} }
}, true, i + 1); }, {alive: true, startWith: i + 1});
}); });
} }

View File

@ -92,7 +92,7 @@ export class PlayPause extends Tool {
// Obliterate object histories // Obliterate object histories
this.sim.objects.forEachObject(obj => { this.sim.objects.forEachObject(obj => {
obj.history = []; obj.history = [];
}, null); }, {alive: null});
}); });
} }
} }

View File

@ -90,31 +90,70 @@ export class Zoom extends Tool {
}); });
zeroVelocity.addEventListener('click', () => { 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});
// 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);
console.log('net angular momentum', netAngularMomentum);
const netAngularVelocity = netAngularMomentum / totalMass;
console.log('net angular velocity', netAngularVelocity);
// TODO: Apply rotation...
// Determine average momentum // Determine average momentum
const netMomentum = {x: 0, y: 0}; const netMomentum = this.sim.objects.reduce((acc, obj) => ({
let totalMass = 0; x: acc.x + obj.mass * obj.velocity.x,
let count = 0; y: acc.y + obj.mass * obj.velocity.y,
this.sim.objects.forEachObject(obj => { }), { x: 0, y: 0 });
count++;
netMomentum.x += obj.mass * obj.velocity.x;
netMomentum.y += obj.mass * obj.velocity.y;
totalMass += obj.mass;
});
if (!count) {
return;
}
const netVelocity = { const netVelocity = {
x: netMomentum.x / totalMass, x: netMomentum.x / totalMass,
y: netMomentum.y / totalMass, y: netMomentum.y / totalMass,
}; };
// Apply offset to all object velocities // Apply offset to all object velocities
this.sim.objects.forEachObject(obj => { this.sim.objects.forEachObject(obj => {
obj.velocity.x -= netVelocity.x; obj.velocity.x -= netVelocity.x;
obj.velocity.y -= netVelocity.y; obj.velocity.y -= netVelocity.y;
}); });
// TODO: Zero net angular momentum
// Cancel panning // Cancel panning
this.sim.pointer.panning = undefined; this.sim.pointer.panning = undefined;
}); });