elastic 2d ball collision using angles - javascript

Yes theres a few threads on this, but not many using angles and I'm really trying to figure it out this way,
I'm now stuck on setting the new velocity angles for the circles. I have been looking at:
http://www.hoomanr.com/Demos/Elastic2/
as a reference to it, but I'm stuck now.
Can anybody shed some light?
cx/cy/cx2/cy2 = center x/y for balls 1 and 2.
vx/vy/vx2/vy2 = velocities for x/y for balls 1 and 2
function checkCollision() {
var dx = cx2 - cx; //distance between x
var dy = cy2 - cy; // distance between y
var distance = Math.sqrt(dx * dx + dy * dy);
var ang = Math.atan2(cy - cy2, cx - cx2);
// was displaying these in a div to check
var d1 = Math.atan2(vx, vy); //ball 1 direction
var d2 = Math.atan2(vx2, vy2); //ball 2 direction
// this is where I am stuck, and i've worked out this is completely wrong now
// how do i set up the new velocities for
var newvx = vx * Math.cos(d1 - ang);
var newvy = vy * Math.sin(d1 - ang);
var newvx2 = vx2 * Math.cos(d2 - ang);
var newvy2 = vy2 * Math.sin(d2 - ang);
if (distance <= (radius1 + radius2)) {
//Set new velocity angles here at collision..
}
Heres a codepen link:
http://codepen.io/anon/pen/MwbMxX

A few directions :
• As mentioned in the comments, use only radians (no more *180/PI).
• atan2 takes y as first param, x as second param.
var d1 = Math.atan2(vy, vx); //ball 1 direction in angles
var d2 = Math.atan2(vy2, vx2); //ball 2 direction in angles
• to rotate a vector, compute first its norm, then only project it with the new angle :
var v1 = Math.sqrt(vx*vx+vy*vy);
var v2 = Math.sqrt(vx2*vx2+vy2*vy2);
var newvx = v1 * Math.cos(d1 - ang);
var newvy = v1 * Math.sin(d1 - ang);
var newvx2 = v2 * Math.cos(d2 - ang);
var newvy2 = v2 * Math.sin(d2 - ang);
• You are detecting the collision when it already happened, so both circles overlap, but you do NOT solve the collision, meaning the circles might still overlap on next iteration, leading to a new collision and a new direction taken, ... not solved, etc..
-->> You need to ensure both circles are not colliding any more after you solved the collision.
• Last issue, but not a small one, is how you compute the angle. No more time for you sorry, but it would be helpful both for you and us to build one (several) scheme showing how you compute the angles.
Updated (but not working) codepen here :
http://codepen.io/anon/pen/eNgmaY
Good luck.
Edit :
Your code at codepen.io/anon/pen/oXZvoe simplify to this :
var angle = Math.atan2(dy, dx),
spread = minDistance - distance,
ax = spread * Math.cos(angle),
ay = spread * Math.sin(angle);
vx -= ax;
vy -= ay;
vx2 += ax;
vy2 += ay;
You are substracting the gap between both circles from the speed. Since later you add the speed to the position, that will do the spatial separation (=> no more collision).
I think to understand what vx-=ax means, we have to remember newton : v = a*t, where a is the acceleration, so basically doing vx=-ax means applying a force having the direction between both centers as direction, and the amount by which both circle collided (spread) as intensity. That amount is obviously quite random, hence the numerical instability that you see : sometimes a small effect, sometimes a big one.
look here for a constant punch version :
http://codepen.io/anon/pen/WvpjeK
var angle = Math.atan2(dy, dx),
spread = minDistance - distance,
ax = spread * Math.cos(angle),
ay = spread * Math.sin(angle);
// solve collision (separation)
cx -= ax;
cy -= ay;
// give a punch to the speed
var punch = 2;
vx -= punch*Math.cos(angle);
vy -= punch*Math.sin(angle);
vx2 += punch*Math.cos(angle);
vy2 += punch*Math.sin(angle);

Related

Calculating the angle between a velocity (particles motion) and a line

So I am creating a simulation of a bouncing ball, and the user can place lines the ball can collide with on the canvas by dragging from one point to another. There are essentially four lines that can be created:
So the object that stores a line is defined as such:
export interface pathSection {
xfrom: number;
yfrom: number;
xto: number;
yto: number;
length: number;
}
The first and third lines in the image for example dont give the same value from
Math.atan2(yto - yfrom, xto - from);
So given the (relative) complexity of the surfaces, I need to find the angle between a moving object and that surface at the point of collision:
The ball strikes the surface at an angle a, which is what I want!
However I am having trouble finding the angle between the two vectors. This is what I understood would work:
var dx = this.path[index_for_path_section].xfrom - this.path[index_for_path_section].xto;
var dy = this.path[index_for_path_section].yfrom - this.path[index_for_path_section].yto;
var posX = this.particle.pos.x;
var posY = this.particle.pos.y;
var posNextX = posX + this.particle.v.x;
var posNextY = posY + this.particle.v.y;
var angleOfRamp = Math.atan2(dy, dx);
var angleOfvelocity = Math.atan2(posNextY - posY, posNextX - posX);
var angleBetween = angleOfRamp - angleOfvelocity;
This is then used to calculate the speed of the object after the collision:
var spd = Math.sqrt(this.particle.v.x * this.particle.v.x + this.particle.v.y * this.particle.v.y);
var restitution = this.elasticity / 100;
this.particle.v.x = restitution * spd * Math.cos(angleBetween);
this.particle.v.y = restitution * spd * Math.sin(angleBetween);
However the angle calculated is around -4.5 Pi, about -90 degrees for the object directly down and the surface at what looks to be around 45-60 degrees…
The red arrow shows the path of the object moving through the surface - the white dots show where a collision has been detected between the surface and the object.
Any help on how to get the correct and usable angle between the two velocity and the line would be appreciated!
Note I have tried utilizing this solution, but have struggled to adapt it to my own work.
So it took me some time, and I am not 100% sure still of why it works because I think im finding the JavaScript angles system a bit tricky, but:
var dx = this.path[collided].xfrom - this.path[collided].xto;
var dy = this.path[collided].yfrom - this.path[collided].yto;
var spd = Math.sqrt(this.particle.v.x * this.particle.v.x + this.particle.v.y * this.particle.v.y);
var angleOfRamp = Math.atan2(dy, dx);
var angleOfvelocity = Math.atan2(this.particle.v.y, this.particle.v.x);
var angleBetween = angleOfRamp * 2 - angleOfvelocity; // not sure why :)
if (angleBetween < 0) { angleBetween += 2*Math.PI; } // not sure why :)
const restitution = this.elasticity / 100;
this.particle.v.x = restitution * spd * Math.cos(angleBetween);
this.particle.v.y = restitution * spd * Math.sin(angleBetween);
Thanks to all who looked :)

Calculating a circle's velocity

I've been working on this problem for a bit, and it doesn't seem too hard, but I'm getting tired and it seems more and more complicated the more I try (but it's probably really easy).
My goal is to have a ball bounce off another ball. Seems easy enough.
Ball 2 is controlled by the user's mouse (so far it's sort of like single player pong, but it's a circle instead of a rectangle) so its velocity doesn't matter.
Ball 1 has a few attributes, including dx (the x distance it moves every frame) and dy (dx, but for the y coordinate)
The problem with what I have so far is that you don't know what values will be positive and what will be negative (so the speed can severely increase or decrease instantly), you might be able to fix this using many else if's, but I'm too confused to think right now.
Here is the important part of this function. Also, I've tried to set it up so that dx + dy is always the same, even when the numbers change, so that it looks more natural.
if (collision(ball, paddle)) {
diffX = paddle.x-ball.x;
diffY = paddle.y-ball.y;
totalVel = ball.dx+ball.dy;
dir = {
x : diffX/(diffX+diffY)*-totalVel,
y : diffY/(diffX+diffY)*-totalVel
};
ball.dx = dir.x;
ball.dy = dir.y;
}
Here is a JSFiddle with the full code
https://jsfiddle.net/a2prr0uw/1/
So firstly let's start by defining what a "bounce" is - the speed is the same, but the direction (on both axis) will be inverted. If we treat dx and dy like a vector, then we can first get the incoming speed of the ball like this:
var ballSpeed = Math.sqrt((ball.dx * ball.dx) + (ball.dy * ball.dy));
The above value will always be positive, regardless of what dx and dy are doing.
Next, we'll need the incoming direction of the ball - that bit is the same as what you've currently got:
diffX = paddle.x-ball.x;
diffY = paddle.y-ball.y;
However if we treat this as a vector too, it essentially has a totally unknown length. So, let's normalise it so it's a direction vector with a length of 1:
var distanceBetweenPaddleAndBall = Math.sqrt((diffX * diffX) + (diffY * diffY));
diffX /= distanceBetweenPaddleAndBall;
diffY /= distanceBetweenPaddleAndBall;
diffX and diffY is now a normalised direction vector - the direction the ball is currently going in - and ballSpeed is the speed we'd like it to go.
So now we'll apply our bounce - flip the direction and retain the speed. That becomes this:
dir = {
x : -diffX * ballSpeed,
y : -diffY * ballSpeed
};
Put it all together and we end up with this:
if (collision(ball, paddle)) {
diffX = paddle.x-ball.x;
diffY = paddle.y-ball.y;
// How fast is the ball coming in?
var ballSpeed = Math.sqrt((ball.dx * ball.dx) + (ball.dy * ball.dy));
// How far is the ball from the paddle?
var distanceBetweenPaddleAndBall = Math.sqrt((diffX * diffX) + (diffY * diffY));
// Normalise diffX and diffY so we have a direction vector:
diffX /= distanceBetweenPaddleAndBall;
diffY /= distanceBetweenPaddleAndBall;
// Apply the bounce and the original ball speed:
dir = {
x : -diffX * ballSpeed,
y : -diffY * ballSpeed
};
ball.dx = dir.x;
ball.dy = dir.y;
}
And here it is as a fork of your fiddle too.
not an answer but some considerations on your bouncing logic:
you have to calculate the balls direction (dy/dx)
the collision has also a direction (angle beween both centers = b.x-p.x / b.y-p.y)
the angle after bouncing has to be calculated based on these two angles: using ther 2nd for mirroring
to calculate the new dx & dy after collision you will need the original velocity Math.abs(Math.sqrt(Math.pow(dx)+Math.pow(dy))) of the ball
based on this velocity and the new direction you can calc the new dx & dy

dda algorithm - raycasting

I started a project using the raycasting technique GitHub Project
To find the length of the ray (distance from players pos to wall) I just increment by one. But there are several problems with that, its time consuming, inaccurate & will be difficult for texturing.
I tried to implement the daa algorithm, which doesnt just increments by 1 -> he goes through the grids and returns exact positions.
http://www.geeksforgeeks.org/dda-line-generation-algorithm-computer-graphics/
Has anyone experience with that or any tips?
No algorithm way:
for(let resolution = 0; resolution < display.width / 2; resolution++){ //every 2nd px gets scanned
let ray = this.pov + (-this.fov / 2 + this.fov / (display.width / 2) * resolution);
let distance = 0, hit = false;
/*ugly way of raycasting!*/
do{
let x = this.x + distance * Math.cos(ray * (Math.PI / 180));
let y = this.y + distance * Math.sin(ray * (Math.PI / 180));
if(map[Math.floor(x / block)][Math.floor(y / block)]){
distance = Math.sqrt(Math.pow(this.x - x, 2) + Math.pow(this.y - y, 2));
hit = true
}
distance += 1;
}while(!hit);
distance = convert / distance;
canvas.fillStyle = "#fff";
canvas.fillRect(resolution * 2, display.height / 2 - distance / 2, 2, distance);
}
You don't need DDA or Bresenham algorithm to find intersections of the ray with walls.
If you need one intersection with given border (or box edges) - just calculate it with ray equation and border position.
If you want to get intersections with grid cells - use voxelization algorithm like Amanatides-Woo

Connect two circles with a line (with DOM elements)

I am struggling with connecting two circles with a line. I am using the famo.us library.
DEMO on Codepen
a.k.a. "Two balls, one line."
The Problem
Angle and length of the line are correct, but the position is wrong.
First attempt
The important part should be lines 114-116:
connection.origin = [.5, .5];
connection.align = [.5, .5];
connection.body.setPosition([
Math.min(sourcePos.x, targetPos.x),
Math.min(sourcePos.y, targetPos.y)
]);
Appearently i am doing something wrong with the math. Playing around with those values gives me all kinds of results, but nothing is close to correct.
Intended solution
(1) The minimal solution would be to connect the centres of the circles with the line.
(2) The better solution would be a line that is only touching the surface of both circles instead of going to the center.
(3) The ideal solution would have arrows on each end of the line to look like a directed graph.
This fixes it :
connection.body.setPosition([
sourcePos.x * Math.cos(angle) + sourcePos.y * Math.sin(angle),
sourcePos.x * Math.sin(-angle)+ sourcePos.y * Math.cos(angle)
]);
Your segment is defined by its extrimity in sourceand the angle and distance to target, thus you have to set its origin to be that of source
The rotation seems to not only rotate the object, but also rotate the coordinates around the origin, so I rotated them by -angle to compensate.
There might be a more famo.usesque way to do it (maybe you can get it to rotate before setting the position, or have the position be 0,0 and add the coordinates as a translation in the transformation).
To get your better solution, still with mostly math, you may keep the same code but
with r the radius of the source ball, remove [r * distX / distance, r * distY / distance] to the coordinates of the segment, to put it in contact with the outer part of the ball
remove both balls' radius from the distance
With that, we get :
var distX = sourcePos.x - targetPos.x;
var distY = sourcePos.y - targetPos.y;
var norm = Math.sqrt(distX * distX + distY * distY);
var distance = norm - (source.size[0]+target.size[0])/2;
var angle = -Math.atan2(-distY, distX);
connection.angle = angle;
connection.size = [distance, 2, 0];
connection.align = [.5, .5];
connection.origin = [.5, .5];
var posX = sourcePos.x - source.size[0]/2 * (distX / norm);
var posY = sourcePos.y - source.size[0]/2 * (distY / norm);
connection.body.setPosition([
posX * Math.cos(angle) + posY * Math.sin(angle),
posX * Math.sin(-angle)+ posY * Math.cos(angle)
]);
result on this fork : http://codepen.io/anon/pen/qEjPLg
I think the fact that the line length is off when the balls go fast is a timing issue. Most probably you compute the segment's length and position at a moment when the ball's centres are not yet updated for that frame.

Bezier Handles Keep Radius and Symmetry but not length

I'm Having a Bezier Curve in Javascript built with a few bezier Curves.
I can move handles and they keep the symmetry. I'm doing that by first calculating
the distance between Handle and Point on Beziér. Then I compare the distances
of the two handles, calculate a multiplier and apply it to the not dragged
handle. This works for keeping Symmetry.
But I want to achieve that the length of the not dragged handle stays the same.
http://cl.ly/image/0c1z00131m2y (a little picture explaining what i mean).
The code, i currently use to calculate the movement is this:
dx = Math.abs(drag.x - point.p[(draggedItemIndex)/2].x);
dy = Math.abs(drag.y - point.p[(draggedItemIndex)/2].y);
dx2 = Math.abs(point.cp[draggedItemIndex-1].x - point.p[draggedItemIndex/2].x);
dy2 = Math.abs(point.cp[draggedItemIndex-1].y - point.p[draggedItemIndex/2].y);
dxdx = dx2/dx;
dydy = dy2/dy;
point.cp[draggedItemIndex-1].x -= dragX*dxdx;
point.cp[draggedItemIndex-1].y -= dragY*dydy;
Thank you for your answer.
I'm now doing it with ciruclar calculations.
//Circle Center Point
cx = point.p[(draggedItemIndex)/2].x;
cy = point.p[(draggedItemIndex)/2].y;
//Dragged Point Position (To Circle Origin)
x1 = drag.x - cx;
y1 = drag.y - cy;
//Mirrored Point Position (To Circle Origin)
x2 = point.cp[draggedItemIndex-1].x - cx;
y2 = point.cp[draggedItemIndex-1].y - cy;
//Angle Dragged Point
a1 = Math.atan2(-y1,x1)*(180/Math.PI);
//Mirrored Angle
a2 = (a1-180)*(Math.PI/180)*(-1);
//Mirrored Point Radius
r = Math.sqrt(Math.pow(x2, 2)+Math.pow(y2, 2));
//Apply new Position to Point
point.cp[draggedItemIndex-1].x = cx + r * Math.cos(a2);
point.cp[draggedItemIndex-1].y = cy + r * Math.sin(a2);

Categories

Resources