Javascript, Canvas: Calculating the angle from a flying bubble - javascript

What I have:
A lot of bubbles. But to make it more simple, let's say I have two. When they meet each other they collide and change the direction.
var xVelocityBubble1 = Math.random();
var yVelocityBubble1 = Math.random();
var xVelocityBubble2 = Math.random();
var yVelocityBubble2 = Math.random();
moveBubbles = function() {
xbubble1 += xVelocityBubble1;
ybubble1 += yVelocityBubble1;
xbubble2 -= xVelocityBubble2;
xbubble2 -= yVelocityBubble2;
if (Math.sqrt(Math.pow(xbubble1 - xbubble2, 2) + Math.pow(ybubble1 - ybubble2, 2)) < radius * 2) {
xVelocityBubble1 *= -1;
yVelocityBubble1 *= -1;
xVelocityBubble2 *= -1;
yVelocityBubble2 *= -1;
}
}
What I want:
I do not want the circles to simply change the direction, because that looks strange and boring. So I want to calculate the angle where the circle meet, and from that I need to calculate how much momentum they exchange and how that affects each circle.
My problem:
I really do not know how to calculate the angle and the momentum! Any hints?

To get the angle between those two bubbles if they collide do as follows:
get the direction vector in which one of those bubbles were moving
direction = {x: Math.abs(xVelocityBubble1), y: Math.abs(yVelocityBubble1)};
Then normalize that vector (divide it's x and y components by it's length)
After doing that you'll have the cosine of the angle as the x component and the sine as the y, just use any of them in Math.acos or Math.asin and you'll have the angle in which they collided.

This code shows collision of asteroids:
for (var i = 0; i < asteroidsLength; i++) {
var tmpAsteroid = asteroids[i];
for (var j = i + 1; j < asteroidsLength; j++) {
var tmpAsteroidB = asteroids[j];
var dX = tmpAsteroidB.x - tmpAsteroid.x;
var dY = tmpAsteroidB.y - tmpAsteroid.y;
var distance = Math.sqrt((dX * dX) + (dY * dY));
if (distance < tmpAsteroid.radius + tmpAsteroidB.radius) {
var angle = Math.atan2(dY, dX);
var sine = Math.sin(angle);
var cosine = Math.cos(angle);
// Rotate asteroid position
var x = 0;
var y = 0;
// Rotate asteroidB position
var xB = dX * cosine + dY * sine;
var yB = dY * cosine - dX * sine;
// Rotate asteroid velocity
var vX = tmpAsteroid.vX * cosine + tmpAsteroid.vY * sine;
var vY = tmpAsteroid.vY * cosine - tmpAsteroid.vX * sine;
// Rotate asteroidB velocity
var vXb = tmpAsteroidB.vX * cosine + tmpAsteroidB.vY * sine;
var vYb = tmpAsteroidB.vY * cosine - tmpAsteroidB.vX * sine;
// Conserve momentum
var vTotal = vX - vXb;
vX = ((tmpAsteroid.mass - tmpAsteroidB.mass) * vX + 2 * tmpAsteroidB.mass * vXb) / (tmpAsteroid.mass + tmpAsteroidB.mass);
vXb = vTotal + vX;
// Move asteroids apart
xB = x + (tmpAsteroid.radius + tmpAsteroidB.radius);
// Rotate asteroid positions back
tmpAsteroid.x = tmpAsteroid.x + (x * cosine - y * sine);
tmpAsteroid.y = tmpAsteroid.y + (y * cosine + x * sine);
tmpAsteroidB.x = tmpAsteroid.x + (xB * cosine - yB * sine);
tmpAsteroidB.y = tmpAsteroid.y + (yB * cosine + xB * sine);
// Rotate asteroid velocities back
tmpAsteroid.vX = vX * cosine - vY * sine;
tmpAsteroid.vY = vY * cosine + vX * sine;
tmpAsteroidB.vX = vXb * cosine - vYb * sine;
tmpAsteroidB.vY = vYb * cosine + vXb * sine;
};
};

Related

3D Rotation where the z-axis remains vertical

This codepen has a small rotation function that accepts theta and phi values. The formula of the rotation can be found anywhere on the internet and is fairly simple:
function rotate(P, C, theta, phi) {
let ct = Math.cos(theta);
let st = Math.sin(theta);
let cp = Math.cos(phi);
let sp = Math.sin(phi);
let x = P.x - C.x;
let y = P.y - C.y;
let z = P.z - C.z;
P.x = ct * x - st * cp * y + st * sp * z + C.x;
P.y = st * x + ct * cp * y - ct * sp * z + C.y;
P.z = sp * y + cp * z + C.z;
}
This rotation works as a demonstration, but It's easy to loose track of which face of the cube is the initially bottom face.
openSCAD on the other hand has a very nice rotation where the z-axis of the object always appears as a vertical line:
The above function would behave the same, if you'd only change the theta value. As soon as you change phi value the cube stars to fall over.
How would I write such a rotation in JavaScript?

How to properly execute collision detection between two spheres?

Hello world :) Working on a small animation in which a bunch of spheres jump around the canvas, bouncing off of eachother, floors, walls, and ceilings.
Now, this works perfectly with no failures, until I add more spheres than can fit in the area in one level. At this point, the spheres sink to one flat level of spheres, coinjoining into eachother and pushing others through wall boundaries, which doesn't normally happen.
However, it should be stated I don't fully understand the function I am using to achieve the outcome, so that likely has something to do with it. Will post my collision detection functions below, and hopefully one of you can help me get a bit farther :)
Borrowed Functions:
/**
* Rotates coordinate system for velocities
*
* Takes velocities and alters them as if the coordinate system they're on was rotated
*
* #param Object | velocity | The velocity of an individual particle
* #param Float | angle | The angle of collision between two objects in radians
* #return Object | The altered x and y velocities after the coordinate system has been rotated
*/
function rotate(velocity, angle) {
const rotatedVelocities = {
x: velocity.x * Math.cos(angle) - velocity.y * Math.sin(angle),
y: velocity.x * Math.sin(angle) + velocity.y * Math.cos(angle)
};
return rotatedVelocities;
}
/**
* Swaps out two colliding particles' x and y velocities after running through
* an elastic collision reaction equation
*
* #param Object | particle | A particle object with x and y coordinates, plus velocity
* #param Object | otherParticle | A particle object with x and y coordinates, plus velocity
* #return Null | Does not return a value
*/
function resolveCollision(particle, otherParticle) {
const xVelocityDiff = particle.velocity.x - otherParticle.velocity.x;
const yVelocityDiff = particle.velocity.y - otherParticle.velocity.y;
console.log("Resolving");
const xDist = otherParticle.x - particle.x;
const yDist = otherParticle.y - particle.y;
// alert(xVelocityDiff * xDist + yVelocityDiff * yDist)
// Prevent accidental overlap of particles
if (xVelocityDiff * xDist + yVelocityDiff * yDist >= 0) {
console.log("Resolving IF");
// Grab angle between the two colliding particles
const angle = -Math.atan2(otherParticle.y - particle.y, otherParticle.x - particle.x);
// Store mass in var for better readability in collision equation
const m1 = particle.mass;
const m2 = otherParticle.mass;
// Velocity before equation
const u1 = rotate(particle.velocity, angle);
const u2 = rotate(otherParticle.velocity, angle);
// Velocity after 1d collision equation
const v1 = { x: u1.x * (m1 - m2) / (m1 + m2) + u2.x * 2 * m2 / (m1 + m2), y: u1.y };
const v2 = { x: u2.x * (m1 - m2) / (m1 + m2) + u1.x * 2 * m2 / (m1 + m2), y: u2.y };
// Final velocity after rotating axis back to original location
const vFinal1 = rotate(v1, -angle);
const vFinal2 = rotate(v2, -angle);
// Swap particle velocities for realistic bounce effect
particle.velocity.x = vFinal1.x;
particle.velocity.y = vFinal1.y;
otherParticle.velocity.x = vFinal2.x;
otherParticle.velocity.y = vFinal2.y;
}
}
Ball Properties:
// Objects
function Ball(x, y, dy, dx, radius, color) {
this.x = x
this.y = y
// this.dy = dy;
// this.dx = dx;
this.velocity = {
x:dx,
y:dy
}
this.radius = radius
this.color = color
this.mass = 1;
this.collision = ()=> {
for (var index = 0; index < objects.length; index++) {
var coin = objects[index];
if (this === coin) {
continue;
}
if (getDistance(this.x, this.y, coin.x, coin.y) - (this.radius + coin.radius) < 0) {
// alert('hi');
console.log("collision:");
resolveCollision(this, coin)
}
}
}
}
Ball.prototype.update = function() {
if (this.y + this.radius + this.velocity.y > canvas.height) {
this.velocity.y = (-this.velocity.y * parseFloat(0.85));
}else {
this.velocity.y += gravity;
}
this.y += this.velocity.y;
this.x += this.velocity.x;
if (this.x + this.radius + this.velocity.x > canvas.width) {
this.velocity.x = -this.velocity.x;
}
if (Math.sign(this.velocity.x) === 1) {
this.velocity.x -= 0.01;
} else if (Math.sign(this.velocity.x) === -1) {
this.velocity.x += 0.01;
}
if (this.x - this.radius - this.velocity.x < 0) {
this.velocity.x = Math.abs(this.velocity.x);
}
this.draw()
}
Any help here really would be most appreciated, I am trying to improve my skills in this field, and feel a strong need to go back and read my geometry books again :D

Inelastic collision only moving object to the right

My Rocket is hitting this Inertia object, as defined in handleCollision. I'm passing in a rocket which has a .r value for its theta and .power for its magnitude.
I'm wanting to update my .rotation & .magnitude according to an inelastic collision as defined by Wikipedia
When colliding from the left, my Inertia moves to the right.
But when colliding from the right it errors and moves exactly 180 degrees off. So if the rocket is up and right at a 45 degree angle from the inertia object, the object will move up and right at a 45 degree angle.
What am I missing here? I thought it might be an issue with the atan function so I converted by the y component & x component of the vector to radians first, same issue.
handleCollision(rocket) {
var angle = rocket.r * Math.PI / 180.0;
var rr = this.rotation * Math.PI / 180;
var rocketVector = {'x' : r.power * Math.cos(angle), 'y' : r.power * Math.sin(angle)};
var inertiaVector = {'x' : this.magnitude * Math.cos(rr), 'y' : this.magnitude * Math.sin(rr)};
var rMass = 10;
var shipMass = 10;
var x = (rMass * rocketVector.x) + (shipMass * inertiaVector.x);
var y = (rMass * rocketVector.y) + (shipMass * inertiaVector.y);
var xDividedByMass = x / (rMass + shipMass);
var yDividedByMass = y / (rMass + shipMass);
var yRadians = (yDividedByMass * Math.PI / 180);
var xRadians = (xDividedByMass * Math.PI / 180);
var theta = Math.atan( yRadians / xRadians);
theta = theta * 180 / Math.PI;
console.log(theta);
var hypotenuse = Math.sqrt((xDividedByMass * xDividedByMass) + (yDividedByMass * yDividedByMass));
this.magnitude = hypotenuse;
this.rotation = theta;
if (this.rotation < 0) {
this.rotation += 360;
} else if (this.rotation > 360) {
this.rotation -= 360;
}
}
If xDividedbyMass>0, you are great because you are quadrant I or IV where arctangent kicks out its values. If you do not like the negative angle, okay add 360 like you did.
But if x<0 and y>0, you will get a negative angle and want to add 180 to get to Q II (tangent has a period of 180). And if x<0, and y<0, you are in QIII and again arctan gives you something in Q1 to which you must add 180.
The logic will look something like this.
if ((x > 0) && (y<0)) {
this.rotation += 360;
} else if (x<0) {
this.rotation += 180;
}

Rotating a point in HTML canvas

I'm trying to learn some basic vector math, but I can't seem to get this method for rotating a point to work. The magnitude of the rotated vectors is scaling up and I don't know what's up with the angle.
Here's the relevant function. I'm working in javascript/HTML canvas.
function rotate(point, center, angle) {
var theta = (Math.PI / 180) * angle,
cX = center.pos.x,
cY = center.pos.y,
pX = point.pos.x,
pY = point.pos.y,
pCos = Math.cos(theta),
pSin = Math.sin(theta),
x = pX - cX,
y = pY - cY;
x = (x * pCos - y * pSin) + cX;
y = (x * pSin + y * pCos) + cY;
return {x: Math.floor(x), y: Math.floor(y)};
}
Here's a jsbin of the weird result
The function is almost right but you are just using the modified x value to calculate y
function rotate(point, center, angle) {
var theta = (Math.PI / 180) * angle,
cX = center.pos.x,
cY = center.pos.y,
pX = point.pos.x,
pY = point.pos.y,
pCos = Math.cos(theta),
pSin = Math.sin(theta),
x = pX - cX,
y = pY - cY;
/* You had
x = (x * pCos - y * pSin) + cX; // you change x on this line
y = (x * pSin + y * pCos) + cY; /// then used the modified x to get y
*/
// this will fix the problem
var xx = (x * pCos - y * pSin) + cX;
var yy = (x * pSin + y * pCos) + cY;
return {x: Math.floor(xx), y: Math.floor(yy)};
}

Learning the math behind WebGL

Like the masochistic I am, I'm trying to learn all the matrix math behind creating modelview and perspective matrices so that I can write my own functions for generating them without the use of JS libraries.
I understand the concept of the matrices, but not how to actually generate them. I've been looking very closely at the glMatrix library, and I have the following questions:
1) What is going on in the following mat4.perspecive method?
/**
* Generates a perspective projection matrix with the given bounds
*
* #param {mat4} out mat4 frustum matrix will be written into
* #param {number} fovy Vertical field of view in radians
* #param {number} aspect Aspect ratio. typically viewport width/height
* #param {number} near Near bound of the frustum
* #param {number} far Far bound of the frustum
* #returns {mat4} out
*/
mat4.perspective = function (out, fovy, aspect, near, far) {
var f = 1.0 / Math.tan(fovy / 2),
nf = 1 / (near - far);
out[0] = f / aspect;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = f;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = (far + near) * nf;
out[11] = -1;
out[12] = 0;
out[13] = 0;
out[14] = (2 * far * near) * nf;
out[15] = 0;
return out;
};
Specifically, I get what Math.tan(fovy / 2) is calculating, but why take the inverse of it? Likewise, why take the inverse of the difference between the near boundary and the far boundary? Also, why is out[11] set to -1 and what is the value stored in out[14] for?
2) The following mat4.lookAt method in the library is also confusing me:
/**
* Generates a look-at matrix with the given eye position, focal point,
* and up axis
*
* #param {mat4} out mat4 frustum matrix will be written into
* #param {vec3} eye Position of the viewer
* #param {vec3} center Point the viewer is looking at
* #param {vec3} up vec3 pointing up
* #returns {mat4} out
*/
mat4.lookAt = function (out, eye, center, up) {
var x0, x1, x2, y0, y1, y2, z0, z1, z2, len,
eyex = eye[0],
eyey = eye[1],
eyez = eye[2],
upx = up[0],
upy = up[1],
upz = up[2],
centerx = center[0],
centery = center[1],
centerz = center[2];
if (Math.abs(eyex - centerx) < GLMAT_EPSILON &&
Math.abs(eyey - centery) < GLMAT_EPSILON &&
Math.abs(eyez - centerz) < GLMAT_EPSILON) {
return mat4.identity(out);
}
z0 = eyex - centerx;
z1 = eyey - centery;
z2 = eyez - centerz;
len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2);
z0 *= len;
z1 *= len;
z2 *= len;
x0 = upy * z2 - upz * z1;
x1 = upz * z0 - upx * z2;
x2 = upx * z1 - upy * z0;
len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2);
if (!len) {
x0 = 0;
x1 = 0;
x2 = 0;
} else {
len = 1 / len;
x0 *= len;
x1 *= len;
x2 *= len;
}
y0 = z1 * x2 - z2 * x1;
y1 = z2 * x0 - z0 * x2;
y2 = z0 * x1 - z1 * x0;
len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2);
if (!len) {
y0 = 0;
y1 = 0;
y2 = 0;
} else {
len = 1 / len;
y0 *= len;
y1 *= len;
y2 *= len;
}
out[0] = x0;
out[1] = y0;
out[2] = z0;
out[3] = 0;
out[4] = x1;
out[5] = y1;
out[6] = z1;
out[7] = 0;
out[8] = x2;
out[9] = y2;
out[10] = z2;
out[11] = 0;
out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez);
out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez);
out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez);
out[15] = 1;
return out;
};
Similar to the mat4.perspecive method, why is the inverse of the length of the vector being calculated? Also, why is that value then multiplied by the z0, z1 and z2 values? The same thing is being done for the x0-x2 variables and the y0-y2 variables. Why? Lastly, what is the meaning of the values set for out[12]-out[14]?
3) Lastly, I have a few questions about the mat4.translate method. Specifically, I bought the book Professional WebGL Programming: Developing 3D Graphics for the Web, and it says that the following 4x4 matrix is used to translate a vertex:
1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 1
However, when I look at the following mat4.translate method in the glMatrix library, I see that out[12]-out[15] are set via some complex equations. Why are these values set at all?
/**
* Translate a mat4 by the given vector
*
* #param {mat4} out the receiving matrix
* #param {mat4} a the matrix to translate
* #param {vec3} v vector to translate by
* #returns {mat4} out
*/
mat4.translate = function (out, a, v) {
var x = v[0], y = v[1], z = v[2],
a00, a01, a02, a03,
a10, a11, a12, a13,
a20, a21, a22, a23;
if (a === out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
} else {
a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;
out[12] = a00 * x + a10 * y + a20 * z + a[12];
out[13] = a01 * x + a11 * y + a21 * z + a[13];
out[14] = a02 * x + a12 * y + a22 * z + a[14];
out[15] = a03 * x + a13 * y + a23 * z + a[15];
}
return out;
};
Thank you all for your time, and sorry for all the questions. I come from a JS background, not an OpenGL/3D programming background, so it's hard for me to understand the math behind all the matrices.
If there are any great resources out there that explain the math used for these equations/methods, then that would be great too. Thanks.
Specifically, I get what Math.tan(fovy / 2) is calculating, but why
take the inverse of it?
Because the focal distance d comes from the formula
Math.tan(fovy / 2) = y / d
to get the focal length you need to multiply by
1 / Math.tan(fovy / 2)
why take the inverse of the difference between the near boundary and
the far boundary? Also, why is out[11] set to -1 and what is the value
stored in out[14] for?
You can project (x,y,z) into (x*d/z, y*d/z) using the focal distance d. This is enough but OpenGL requires a linear transformation to (x,y,z) such as the projection gives coordinates in [-1,1]. Such normalized coordinates simplify clipping and retain the z information used to remove hidden surfaces.
out[11] is set to -1 because there's no linear transformation that gives normalized coordinates unless a reflection is applied. This -1 causes the handedness of the system to be switched with normalized coordinates.
out[14] is used with out[10] to transform z from [-n -f] to [-1 1] after projection.
Similar to the mat4.perspecive method, why is the inverse of the
length of the vector being calculated? Also, why is that value then
multiplied by the z0, z1 and z2 values? The same thing is being done
for the x0-x2 variables and the y0-y2 variables. Why?
To normalize the vectors x, y and z
what is the meaning of the values set for out[12]-out[14]?
A camera is composed of a base of vectors and a position.
out[12]-out[14] apply an inverse translation to set the camera position.
However, when I look at the following mat4.translate method in the
glMatrix library, I see that out[12]-out[15] are set via some complex
equations. Why are these values set at all?
The equations look complex because it's a product of a translation matrix and an existing matrix a.
Professional WebGL Programming: Developing 3D Graphics for the Web
I don't know this book, it might explain some math but if you need detailed explanation you should consider Eric Lengyel's book that explains and derivates the important math used in 3d raster graphics.

Categories