Implemented adjustable elasticity and adjustable merge threshold
This parameter represents the percentage of energy that will be conserved in collisions. 0 -> Perfectly inelastic 1 -> Perfectly elastic
@ -8,11 +8,11 @@ Uses `npm` for `eslint`.
|
||||
Screenshots
|
||||
-----------
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
TODO
|
||||
|
||||
|
Before Width: | Height: | Size: 152 KiB |
@ -69,8 +69,6 @@ export class Panning {
|
||||
// Additional scaling factor
|
||||
velocity = mult(velocity, this.sim.getOption('display.panningSpeed'));
|
||||
|
||||
// TODO: Make it easier to slow down the camera
|
||||
|
||||
// Add pointer velocity to current panning velocity
|
||||
this.velocity = add(this.velocity, velocity);
|
||||
}
|
||||
@ -82,10 +80,6 @@ export class Panning {
|
||||
this.velocity = zero;
|
||||
}
|
||||
this.touchStart = undefined;
|
||||
|
||||
if (this.sim.getOption('compensate.fastPanning')) {
|
||||
this.velocity = zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
BIN
screenshots/gravity-simulator-5.png
Normal file
|
After Width: | Height: | Size: 266 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
@ -10,18 +10,19 @@ export const simOptions = {
|
||||
traces: ['Path Traces', 'boolean', true],
|
||||
dashedTraces: ['Dashed', 'boolean', false, {tall: true, showIf: 'display.traces'}],
|
||||
velocityScale: ['Velocity<br>Vec Scale', 'number', 80, {showIf: 'display.velocity'}],
|
||||
accelerationScale: ['Accel<br>Vec Scale', 'number', 800, {showIf: 'display.acceleration'}],
|
||||
accelerationScale: ['Accel<br>Vec Scale', 'number', 80, {showIf: 'display.acceleration'}],
|
||||
zoomVectors: ['Zoom Vectors', 'boolean', true],
|
||||
panningSpeed: ['Pan<br>Speed', 'number', 0.1],
|
||||
},
|
||||
compensate: {
|
||||
timeScale: ['Time Scale Compensator', 'boolean', false, {wide: true}],
|
||||
fastPanning: ['Fast Panning', 'boolean', false],
|
||||
},
|
||||
param: {
|
||||
gravity: ['Gravity', 'number', 1],
|
||||
timeScale: ['Time Scale', 'number', 0.1],
|
||||
massCreationRate: ['Mass Creation Rate', 'number', 1],
|
||||
elasticity: ['Elasticity', 'number', 0.7, {min: 0, max: 1}],
|
||||
mergeThreshold: ['Merge Threshold', 'number', 0.1, {min: 0, max: 1}],
|
||||
},
|
||||
debug: {
|
||||
objectsInfo: ['Objects Info', 'boolean', false],
|
||||
|
||||
34
system.js
@ -400,10 +400,13 @@ export class System {
|
||||
bounceObjects(i, j) {
|
||||
const A = this.object(i);
|
||||
const B = this.object(j);
|
||||
const totalMass = A.mass + B.mass;
|
||||
|
||||
// TODO: Handle scenario where an object overlaps more than one other object
|
||||
|
||||
// const elasticity = 1;
|
||||
const autoMerge = true;
|
||||
|
||||
const Z = this.sim.getOption('param.elasticity'); // Elasticity: 0 = inelastic, 1 = elastic
|
||||
const r = sub(A.position, B.position);
|
||||
const normal = div(r, magnitude(r));
|
||||
const tangent = {x: -normal.y, y: normal.x};
|
||||
@ -421,21 +424,30 @@ export class System {
|
||||
// Are these objects sticking together?
|
||||
// One way to determine this is,
|
||||
// does either object have enough KE to escape their gravitational potential?
|
||||
// const vRel = sub(A.velocity, B.velocity);
|
||||
// const G = this.sim.getOption('param.gravity');
|
||||
// if (square(vRel) < 0.1 * G * (A.mass + B.mass) / magnitude(r)) {
|
||||
// // Neither object looks like it can escape!
|
||||
// this.mergeObjects(i, j);
|
||||
// return;
|
||||
// }
|
||||
if (autoMerge) {
|
||||
// const vSquared = Math.abs(vAn - vBn) ** 2;
|
||||
const vSquared = square(sub(A.velocity, B.velocity));
|
||||
const G = this.sim.getOption('param.gravity');
|
||||
const mergeThreshold = this.sim.getOption('param.mergeThreshold');
|
||||
if (vSquared < mergeThreshold * G * totalMass / magnitude(r)) {
|
||||
// Neither object looks like it can escape!
|
||||
this.mergeObjects(i, j);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the collision
|
||||
const Zscaled = Math.sqrt(Z);
|
||||
const vAnNew = (vAn * (A.mass - Zscaled * B.mass) + vBn * (1 + Zscaled) * B.mass) / totalMass;
|
||||
|
||||
// Continue with rebound calculations
|
||||
const vAnNew = (vAn * (A.mass - B.mass) + vBn * 2 * B.mass) / (A.mass + B.mass);
|
||||
const vA = add(mult(tangent, vAt), mult(normal, vAnNew));
|
||||
const vB = div(add(mult(A.velocity, A.mass), mult(B.velocity, B.mass), mult(vA, -1 * A.mass)), B.mass);
|
||||
const vB = div(add(mult(A.velocity, A.mass), mult(B.velocity, B.mass), mult(vA, -A.mass)), B.mass);
|
||||
|
||||
// const Ki = A.kineticEnergy + B.kineticEnergy;
|
||||
A.velocity = vA;
|
||||
B.velocity = vB;
|
||||
// const Kf = A.kineticEnergy + B.kineticEnergy;
|
||||
// console.log('Collision: Zscaled', Zscaled, 'Energy before', Ki, 'Energy after', Kf, 'Fraction change', (Kf - Ki) / Ki);
|
||||
}
|
||||
|
||||
mergeObjects(i, j) {
|
||||
|
||||
@ -126,6 +126,13 @@ export class OptionsTool extends Tool {
|
||||
|
||||
input.addEventListener('change', () => {
|
||||
this.sim.setOption(path, input.value);
|
||||
// Enforce min/max if provided
|
||||
if (item.max !== undefined && this.sim.getOption(path) > item.max) {
|
||||
this.sim.setOption(path, item.max);
|
||||
}
|
||||
if (item.min !== undefined && this.sim.getOption(path) < item.min) {
|
||||
this.sim.setOption(path, item.min);
|
||||
}
|
||||
});
|
||||
|
||||
this.sim.onOptionSet(path, ({value}) => {
|
||||
|
||||