objects now have velocity
This commit is contained in:
parent
f3c8fc85fa
commit
08c3657baf
@ -10,6 +10,8 @@ body {
|
||||
font-family: monospace;
|
||||
font-size: 16pt;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
div[id=simulator] {
|
||||
|
||||
125
simulator.js
125
simulator.js
@ -2,6 +2,7 @@ export class MassObject {
|
||||
mass = 0;
|
||||
density = 1;
|
||||
position = {x: undefined, y: undefined};
|
||||
velocity = {x: 0, y: 0};
|
||||
color = {r: undefined, g: undefined, b: undefined};
|
||||
created = undefined;
|
||||
|
||||
@ -17,6 +18,11 @@ export class MassObject {
|
||||
get age() {
|
||||
return document.timeline.currentTime - this.created;
|
||||
}
|
||||
|
||||
get radius() {
|
||||
// radius should be proportional to cube root of mass
|
||||
return Math.pow(this.mass / this.density, 1/3);
|
||||
}
|
||||
}
|
||||
|
||||
export class Sim {
|
||||
@ -24,10 +30,14 @@ export class Sim {
|
||||
objects = [];
|
||||
frame = 0;
|
||||
time = undefined;
|
||||
pointerHistory = [];
|
||||
|
||||
POINTER_HISTORY_SIZE = 20;
|
||||
MASS_CREATION_RATE = 0.001;
|
||||
DISPLAY_OBJECTS_INFO = false;
|
||||
DISPLAY_CURSOR_INFO = false;
|
||||
DISPLAY_VELOCITY_VECTORS = true;
|
||||
VELOCITY_VECTOR_SCALE = 0.2;
|
||||
|
||||
fullscreen() {
|
||||
this.canvas.width = document.documentElement.clientWidth;
|
||||
@ -52,7 +62,7 @@ export class Sim {
|
||||
keyCell.innerHTML = `${k}: `;
|
||||
row.appendChild(keyCell);
|
||||
let vs = Array.isArray(v) ? v : [v];
|
||||
for (let x of v) {
|
||||
for (let x of vs) {
|
||||
let valueCell = document.createElement('td');
|
||||
valueCell.innerHTML = x;
|
||||
row.appendChild(valueCell);
|
||||
@ -96,7 +106,7 @@ export class Sim {
|
||||
this.info['Mouse move'] = [`${e.clientX}, `, `${e.clientY}`];
|
||||
this.renderInfo();
|
||||
}
|
||||
this.handleCursorMove(e.clientX, e.clientY);
|
||||
this.handlePointerMove(e.clientX, e.clientY);
|
||||
});
|
||||
|
||||
// Monitor touch events
|
||||
@ -105,8 +115,7 @@ export class Sim {
|
||||
this.info['Touch move'] = [`${e.touches[0].pageX}, `, `${e.touches[0].pageY}`];
|
||||
this.renderInfo();
|
||||
}
|
||||
// TODO: If e.touches.length > 1, user may be engaging pinch to zoom
|
||||
this.handleCursorMove(e.touches[0].pageX, e.touches[0].pageY);
|
||||
this.handlePointerMove(e.touches[0].pageX, e.touches[0].pageY);
|
||||
});
|
||||
|
||||
el.addEventListener('pointerdown', e => {
|
||||
@ -114,7 +123,7 @@ export class Sim {
|
||||
this.info['Pointer down'] = [`${e.clientX}, `, `${e.clientY}`];
|
||||
this.renderInfo();
|
||||
}
|
||||
this.createObject(e.clientX, e.clientY);
|
||||
this.handlePointerDown(e.clientX, e.clientY);
|
||||
});
|
||||
|
||||
el.addEventListener('pointerup', e => {
|
||||
@ -122,7 +131,7 @@ export class Sim {
|
||||
this.info['Pointer up'] = [`${e.clientX}, `, `${e.clientY}`];
|
||||
this.renderInfo();
|
||||
}
|
||||
this.doneCreatingObject();
|
||||
this.handlePointerUp(e.clientX, e.clientY);
|
||||
});
|
||||
|
||||
el.addEventListener('click', e => {
|
||||
@ -137,13 +146,77 @@ export class Sim {
|
||||
requestAnimationFrame(t => this.loop(t));
|
||||
}
|
||||
|
||||
clearPointerHistory() {
|
||||
this.pointerHistory = [];
|
||||
}
|
||||
|
||||
updatePointer(x, y) {
|
||||
const t = document.timeline.currentTime;
|
||||
this.pointerHistory.push({x, y, t});
|
||||
if (this.pointerHistory.length > this.POINTER_HISTORY_SIZE) {
|
||||
this.pointerHistory.shift();
|
||||
}
|
||||
}
|
||||
|
||||
getPointerVelocity() {
|
||||
// Average over pointer history
|
||||
if (this.pointerHistory.length < 2) {
|
||||
return {x: 0, y: 0};
|
||||
}
|
||||
const start = this.pointerHistory[0];
|
||||
const end = this.pointerHistory[this.pointerHistory.length - 1];
|
||||
const dt = (end.t - start.t) / 1000;
|
||||
this.renderInfo();
|
||||
return {
|
||||
x: (end.x - start.x) / dt,
|
||||
y: (end.y - start.y) / dt,
|
||||
};
|
||||
}
|
||||
|
||||
handlePointerDown(x, y) {
|
||||
this.clearPointerHistory();
|
||||
this.updatePointer(x, y);
|
||||
|
||||
// If pointer is touching an object, select the object
|
||||
let touchingObject = undefined;
|
||||
for (let i = 0; i < this.objects.length; i++) {
|
||||
const obj = this.objects[i];
|
||||
// If distance to object is less than object's radius, we are touching the object
|
||||
const dist = Math.pow((obj.position.x - x)**2 + (obj.position.y - y)**2, 1/2);
|
||||
if (dist <= obj.radius) {
|
||||
touchingObject = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (touchingObject !== undefined) {
|
||||
this.selectedObject = touchingObject;
|
||||
} else {
|
||||
// Otherwise, create a new object
|
||||
this.createObject(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
handlePointerUp(x, y) {
|
||||
this.creatingObject = undefined;
|
||||
this.selectedObject = undefined;
|
||||
}
|
||||
|
||||
// Handle cursor (mouse or touch) movement
|
||||
handleCursorMove(x, y) {
|
||||
// If the cursor moves while creating an object, update the position of the object
|
||||
if (this.creatingObject !== undefined) {
|
||||
const obj = this.objects[this.creatingObject];
|
||||
handlePointerMove(x, y) {
|
||||
// TODO: If e.touches.length > 1, user may be engaging pinch to zoom
|
||||
|
||||
// If the cursor moves while creating an object, or if an object is selected,
|
||||
// update the position and velocity of the object
|
||||
let selectedObject = this.creatingObject ?? this.selectedObject;
|
||||
if (selectedObject !== undefined) {
|
||||
const obj = this.objects[selectedObject];
|
||||
this.updatePointer(x, y);
|
||||
const {x: vx, y: vy} = this.getPointerVelocity();
|
||||
obj.position.x = x;
|
||||
obj.position.y = y;
|
||||
obj.velocity.x = vx;
|
||||
obj.velocity.y = vy;
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,11 +227,6 @@ export class Sim {
|
||||
this.creatingObject = idx;
|
||||
this.objects.push(obj);
|
||||
}
|
||||
|
||||
// Done creating object
|
||||
doneCreatingObject() {
|
||||
this.creatingObject = undefined;
|
||||
}
|
||||
|
||||
// Main loop
|
||||
loop(currentTime) {
|
||||
@ -177,7 +245,15 @@ export class Sim {
|
||||
if (this.DISPLAY_OBJECTS_INFO) {
|
||||
for (let i = 0; i < this.objects.length; i++) {
|
||||
const obj = this.objects[i];
|
||||
this.info[`Object ${i}`] = [`${obj.position.x}, `, `${obj.position.y}, `, `${obj.mass.toPrecision(6)} kg`];
|
||||
const speed = Math.pow(obj.velocity.x ** 2 + obj.velocity.y ** 2, 1/2);
|
||||
// Invert y so that the angle is counterclockwise from x-axis
|
||||
const direction = Math.atan2(-obj.velocity.y, obj.velocity.x) * 180 / Math.PI;
|
||||
this.info[`Object ${i}`] = [
|
||||
`${obj.position.x}, `,
|
||||
`${obj.position.y}, `,
|
||||
`${obj.mass.toPrecision(6)} kg, `,
|
||||
`${speed.toPrecision(2)} m/s, ${direction.toPrecision(2)}°`,
|
||||
];
|
||||
this.renderInfo();
|
||||
}
|
||||
}
|
||||
@ -191,14 +267,27 @@ export class Sim {
|
||||
renderObject(idx) {
|
||||
const obj = this.objects[idx];
|
||||
const ctx = this.ctx;
|
||||
// radius should be proportional to cube root of mass
|
||||
const radius = Math.pow(obj.mass / obj.density, 1/3);
|
||||
const {r, g, b} = obj.color;
|
||||
const {x, y} = obj.position;
|
||||
const {x: vx, y: vy} = obj.velocity;
|
||||
const radius = obj.radius;
|
||||
|
||||
// Draw filled circle for the object
|
||||
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, 2*Math.PI);
|
||||
ctx.fill();
|
||||
|
||||
// Draw line for the velocity
|
||||
// TODO: Arrow
|
||||
ctx.strokeStyle = ctx.fillStyle;
|
||||
ctx.lineWidth = 2.0;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(x + this.VELOCITY_VECTOR_SCALE * vx, y + this.VELOCITY_VECTOR_SCALE * vy);
|
||||
ctx.stroke();
|
||||
|
||||
// TODO: Draw line for acceleration
|
||||
}
|
||||
|
||||
renderObjects() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user