How can I extract the angle from a function that determines if a circle is colliding with a line? - math

So I have the following code that works fine
I've taken it from https://codereview.stackexchange.com/questions/192477/circle-line-segment-collision
I am now trying to figure out where along this function I can determine the angle in which the circle is hitting the line. Instead of returning true I'd like it to return the angle, since I didn't write the function myself going through it to try and figure this out myself has been a bit of a struggle. Wondering if someone excellent at math can help me figure this out at first glance. Thank you!
// Function to check intercept of line seg and circle
// A,B end points of line segment
// C center of circle
// radius of circle
// returns true if touching or crossing else false
function doesLineInterceptCircle(A, B, C, radius) {
var dist;
const v1x = B.x - A.x;
const v1y = B.y - A.y;
const v2x = C.x - A.x;
const v2y = C.y - A.y;
// get the unit distance along the line of the closest point to
// circle center
const u = (v2x * v1x + v2y * v1y) / (v1y * v1y + v1x * v1x);
// if the point is on the line segment get the distance squared
// from that point to the circle center
if(u >= 0 && u <= 1){
dist = (A.x + v1x * u - C.x) ** 2 + (A.y + v1y * u - C.y) ** 2;
} else {
// if closest point not on the line segment
// use the unit distance to determine which end is closest
// and get dist square to circle
dist = u < 0 ?
(A.x - C.x) ** 2 + (A.y - C.y) ** 2 :
(B.x - C.x) ** 2 + (B.y - C.y) ** 2;
}
return dist < radius * radius;
}

Related

My raycaster renders walls in a really weird way depending on the map size I guess

I've been writing a raycaster in C++ and to render stuff I use GDI/GDI+. I know that using WGDI to render graphics is not the best idea in the world and I should probably use OpenGL, SFML and etc. but this raycaster does not involve any super-high-level real-time graphics, so in this case WGDI does the job. Besides I probably will be showing this in my school and installing OpenGL there would be a huge pain.
Okay, so the actual problem I wanted to talk about is that whenever I change the map grid from 8x8 to e.g. 8x16, the way that some walls are rendered is pretty bizzarre:
If someone can explain why such issue occurrs I would be very happy to discover what's wrong with my code.
main.cpp
/*
* Pseudo-code of the void renderer():
* Horizontal gridline check:
* Set horizontal distance to a pretty high value, horizontal coordinates to camera coordinates
* Calculate negative inverse of tangent
* Set DOF variable to 0
* If ray angle is bigger than PI calculate ray Y-coordinate to be as close as possible to the gridline position and subtract 0.0001 for precision, calculate ray X-coordinate and offset coordinates for the ray moovement over the gridline
* If ray angle is smaller than PI do the same as if ray angle < PI but add whatever the size of the map is to ray Y-coordinate
* If ray angle is straight up or down set ray coordinates to camera coordinates and DOF to map size
* Loop only if DOF is smaller than map size:
* Calculate actual gridline coordinates
* If the grid cell at [X, Y] is a wall break out from the loop, save the current ray coordinates, calculate the distance between the camera and the wall
* Else update ray coordinates with the earlier calculated offsets
*
* Vertical gridline check:
* Set vertical distance to a pretty high value, vertical coordinates to camera coordinates
* Calculate inverse of tangent
* Set DOF variable to 0
* If ray angle is bigger than PI / 2 and smaller than 3 * PI / 2 calculate ray X-coordinate to be as close as possible to the gridline position and subtract 0.0001 for precision, calculate ray Y-coordinate and offset coordinates for the ray moovement over the gridline
* If ray angle is smaller than PI / 2 or bigger than 3 * PI / 2 do the same as if ray angle > PI / 2 && < 3 * PI / 2 but add whatever the size of the map is to ray X-coordinate
* If ray angle is straight left or right set ray coordinates to camera coordinates and DOF to map size
* Loop only if DOF is smaller than map size:
* Calculate actual gridline coordinates
* If the grid cell at [X, Y] is a wall break out from the loop, save the current ray coordinates, calculate the distance between the camera and the wall
* Else update ray coordinates with the earlier calculated offsets
*
* If the vertical distance is smaller than the horizontal one update ray coordinates to the horizontal ones and set final distance to the horizontal one
* Else update ray coordinates to the vertical ones and set final distance to the vertical one
* Fix fisheye effect
* Add one radian to the ray angle
* Calculate line height by multiplying constant integer 400 by the map size and dividing that by the final distance
* Calculate line offset (to make it more centered) by subtracting half of the line height from constant integer 400
* Draw 8-pixels wide column at [ray index * 8, camera Z-offset + line offset] and [ray index * 8, camera Z-offset + line offset + line height] (the color doesn't matter i think)
*/
#include "../../LIB/wsgl.hpp"
#include "res/maths.hpp"
#include <memory>
using namespace std;
const int window_x = 640, window_y = 640;
float camera_x = 256, camera_y = 256, camera_z = 75;
float camera_a = 0.001;
int camera_fov = 80;
int map_x;
int map_y;
int map_s;
shared_ptr<int[]> map_w;
void controls()
{
if(wsgl::is_key_down(wsgl::key::w))
{
int mx = (camera_x + 30 * cos(camera_a)) / map_s;
int my = (camera_y + 30 * sin(camera_a)) / map_s;
int mp = my * map_x + mx;
if(mp >= 0 && mp < map_s && !map_w[mp])
{camera_x += 15 * cos(camera_a); camera_y += 15 * sin(camera_a);}
}
if(wsgl::is_key_down(wsgl::key::s))
{
int mx = (camera_x - 30 * cos(camera_a)) / map_s;
int my = (camera_y - 30 * sin(camera_a)) / map_s;
int mp = my * map_x + mx;
if(mp >= 0 && mp < map_s && !map_w[mp])
{camera_x -= 5 * cos(camera_a); camera_y -= 5 * sin(camera_a);}
}
if(wsgl::is_key_down(wsgl::key::a_left))
{camera_a = reset_ang(camera_a - 5 * RAD);}
if(wsgl::is_key_down(wsgl::key::a_right))
{camera_a = reset_ang(camera_a + 5 * RAD);}
if(wsgl::is_key_down(wsgl::key::a_up))
{camera_z += 15;}
if(wsgl::is_key_down(wsgl::key::a_down))
{camera_z -= 15;}
}
void renderer()
{
int map_x_pos, map_y_pos, map_cell, dof;
float ray_x, ray_y, ray_a = reset_ang(camera_a - deg_to_rad(camera_fov / 2));
float x_offset, y_offset, tangent, distance_h, distance_v, h_x, h_y, v_x, v_y;
float final_distance, line_height, line_offset;
wsgl::clear_window();
for(int i = 0; i < camera_fov; i++)
{
distance_h = 1000000, h_x = camera_x, h_y = camera_y;
tangent = -1 / tan(ray_a);
dof = 0;
if(ray_a > PI)
{ray_y = (((int)camera_y / map_s) * map_s) - 0.0001; ray_x = (camera_y - ray_y) * tangent + camera_x; y_offset = -map_s; x_offset = -y_offset * tangent;}
if(ray_a < PI)
{ray_y = (((int)camera_y / map_s) * map_s) + map_s; ray_x = (camera_y - ray_y) * tangent + camera_x; y_offset = map_s; x_offset = -y_offset * tangent;}
if(ray_a == 0 || ray_a == PI)
{ray_x = camera_x; ray_y = camera_y; dof = map_s;}
for(dof; dof < map_s; dof++)
{
map_x_pos = (int)(ray_x) / map_s;
map_y_pos = (int)(ray_y) / map_s;
map_cell = map_y_pos * map_x + map_x_pos;
if(map_cell >= 0 && map_cell < map_s && map_w[map_cell])
{dof = map_s; h_x = ray_x; h_y = ray_y; distance_h = distance(camera_x, camera_y, h_x, h_y);}
else
{ray_x += x_offset; ray_y += y_offset;}
}
distance_v = 1000000, v_x = camera_x, v_y = camera_y;
tangent = -tan(ray_a);
dof = 0;
if(ray_a > PI2 && ray_a < PI3)
{ray_x = (((int)camera_x / map_s) * map_s) - 0.0001; ray_y = (camera_x - ray_x) * tangent + camera_y; x_offset = -map_s; y_offset = -x_offset * tangent;}
if(ray_a < PI2 || ray_a > PI3)
{ray_x = (((int)camera_x / map_s) * map_s) + map_s; ray_y = (camera_x - ray_x) * tangent + camera_y; x_offset = map_s; y_offset = -x_offset * tangent;}
if(ray_a == PI2 || ray_a == PI3)
{ray_x = camera_x; ray_y = camera_y; dof = map_s;}
for(dof; dof < map_s; dof++)
{
map_x_pos = (int)(ray_x) / map_s;
map_y_pos = (int)(ray_y) / map_s;
map_cell = map_y_pos * map_x + map_x_pos;
if(map_cell >= 0 && map_cell < map_s && map_w[map_cell])
{dof = map_s; v_x = ray_x; v_y = ray_y; distance_v = distance(camera_x, camera_y, v_x, v_y);}
else
{ray_x += x_offset; ray_y += y_offset;}
}
if(distance_v < distance_h)
{ray_x = v_x; ray_y = v_y; final_distance = distance_v;}
else
{ray_x = h_x; ray_y = h_y; final_distance = distance_h;}
final_distance *= cos(reset_ang(camera_a - ray_a));
ray_a = reset_ang(ray_a + RAD);
line_height = (map_s * 400) / final_distance;
line_offset = 200 - line_height / 2;
wsgl::draw_line({i * 8, camera_z + line_offset}, {i * 8, camera_z + line_offset + line_height}, {0, 255 / (final_distance / 250 + 1), 0}, 8);
if(i == camera_fov / 2)
{wsgl::draw_text({0, 0}, {255, 255, 255}, L"Final distance: " + to_wstring(final_distance) + L" Line height: " + to_wstring(line_height) + L" X: " + to_wstring(camera_x) + L" Y: " + to_wstring(camera_y));}
}
wsgl::render_frame();
}
void load_map(wsgl::wide_str wstr, int cell_size = 1)
{
shared_ptr<wsgl::bmp> map = shared_ptr<wsgl::bmp>(wsgl::bmp::FromFile(wstr.c_str(), true));
map_x = map->GetWidth();
map_y = map->GetHeight();
map_s = map_x * map_y;
map_w = shared_ptr<int[]>(new int[map_s]);
wsgl::color color;
for(int y = 0; y < map_y; y += cell_size)
{
for(int x = 0; x < map_x; x += cell_size)
{
map->GetPixel(x, y, &color);
if(color.GetR() == 255 && color.GetG() == 255 && color.GetB() == 255)
{*(map_w.get() + ((y / cell_size) * map_x + (x / cell_size))) = 0;}
else
{*(map_w.get() + ((y / cell_size) * map_x + (x / cell_size))) = 1;}
}
}
}
int main()
{
wsgl::session sess = wsgl::startup(L"raycaster", {window_x, window_y});
load_map(L"res/map.png");
while(true)
{controls(); renderer();}
}
maths.hpp
#include <cmath>
const float PI = 3.14159265359;
const float PI2 = PI / 2;
const float PI3 = 3 * PI2;
const float RAD = PI / 180;
float deg_to_rad(float deg)
{return deg * RAD;}
float distance(float ax, float ay, float bx, float by)
{
float dx = bx - ax;
float dy = by - ay;
return sqrt(dx * dx + dy * dy);
}
float reset_ang(float ang)
{
if(ang < 0)
{ang += 2 * PI;}
if(ang > 2 * PI)
{ang -= 2 * PI;}
return ang;
}
If someone asks whats wsgl.hpp thats just my wrapper library over some WGDI routines and etc.
I think the problem lies here:
map_x_pos = (int)(ray_x) / map_s;
map_y_pos = (int)(ray_y) / map_s;
map_cell = map_y_pos * map_x + map_x_pos;
You need to change the order of operations:
map_x_pos = (int)(ray_x / map_s);
map_y_pos = (int)(ray_y / map_s);
map_cell = map_y_pos * map_x + map_x_pos;
With your current implementation, you first truncate ray_x and ray_y, then divide by map_s (which should probably be a floating point value, but is an integer in your current implementation), then truncate again to integer values. Your current implementation needlessly sacrifices precision and will be unpredictable for small map_s values.
Additionally, map_s seems incorrect. You set map_s to represent the total area of your map, but in the above code, you use it like it was the side length of the map.
To be correct, you would need something like
#include <cmath>
map_x_pos = (int)(ray_x / sqrtf(map_s));
map_y_pos = (int)(ray_y / sqrtf(map_s));
map_cell = map_y_pos * map_x + map_x_pos;

How to calculate the start point and the end point of a circle based on points

I want bevel the sides of a rectangle in order to do so i want to draw a circle from the start point to the end point as shown in the image
while drawing a complete circle this is how i do it
float angle = 2.0f * M_PI * i / iSegments;
// vertex data
float x, y, z ,tx,ty ,tz;
x = cos(angle) * 50.0;
y = sin(angle) * 50.0;
z = 0.0;
How do we calculate the vertices for the circle in the given case ?
The center of the circle is radius away from the corner point, both in x and in y directions. In your example the radius is 30. To draw an arc of 90° (π/2 radians), you can start with the first angle and divide π/2 into as many segments as you want. The first angle depends on the which corner of the rectangle is treated. In the example of the upper right corner, the starting angle would be 0°.
Here is some code to illustrate the concept:
// draw a circular bevel to a rectangle, given are
// p_x, p_y: the coordinates of the corner, e.g. 50, 50
// rad: the radius of the bevel, e.g. 30
// dir_x: the direction to indicate whether the center of
// the circle lies lef (dir_y=-1) or right (dir_x=+1) of the corner
// dir_y: the direction to indicate whether the center of
// the circle lies lower (dir_y=-1) or higher (dir_y=+1) than the corner
void draw_circular_bevel (float p_x, float p_y, float rad, int dir_x, int dir_y)
{
float c_x, c_y; // the center of the circle
float start_angle; // the angle where to start the arc
c_x = p_x + rad * dir_x;
c_y = p_y + rad * dir_y;
if (dir_x == 1 and dir_y == 1)
start_angle = 0.0;
else if (dir_x == 1 and dir_y == -1)
start_angle = - M_PI * 0.5f;
else if (dir_x == -1 and dir_y == 1)
start_angle = M_PI * 0.5f;
else if (dir_x == -1 and dir_y == -1)
start_angle = - 2.0f * M_PI;
for (int i=0; i <= iSegments; ++i) {
float x, y;
float angle = start_angle + 0.5f * M_PI * i / (float)iSegments;
x = c_x + cos(angle) * rad;
y = c_y + sin(angle) * rad;
// here call code to draw a point or a segment at position x,y
}
}

Calculate min distance between a "line" and one "point"

I have a "linestring" (with init and end points) and a single "point" (two coordinates).
And I have implemented the following ActionSctipt code to use "haversine formula" to calculate the distance between two points (each point has x & y coordinates); this function can return the "distance" in "kms", "meters", "feets" or "miles":
private function distanceBetweenCoordinates(lat1:Number, lon1:Number, lat2:Number, lon2:Number, units:String = "miles"):Number {
var R:int = RADIUS_OF_EARTH_IN_MILES;
if (units == "km") {
R = RADIUS_OF_EARTH_IN_KM;
}
if (units == "meters") {
R = RADIUS_OF_EARTH_IN_M;
}
if (units == "feet") {
R = RADIUS_OF_EARTH_IN_FEET;
}
var dLat:Number = (lat2 - lat1) * Math.PI / 180;
var dLon:Number = (lon2 - lon1) * Math.PI / 180;
var lat1inRadians:Number = lat1 * Math.PI / 180;
var lat2inRadians:Number = lat2 * Math.PI / 180;
var a:Number = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1inRadians) * Math.cos(lat2inRadians);
var c:Number = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d:Number = R * c;
return d;
}
This code is functioning well. But I need to improving this code to allow calculate the minimum distance between a "single point" and one "linestring" (with 2 points).
How can I do?
I thought this solution:
* Divide the "linesting" for each point (Init and end)... and for each of these calculate the distance to the "single point"... and after I getting both "distances" return the minimum distance.
This solution is not the better, this is explained in the following image:
"d1" and "d2" distances are invalid... because only "d0" is the valid distance.
Please! help me!!! How can I improve the haversine formula to calculate the distance between a line and a single point in kilometres?
Thanks!!!!
In your case d0 distance is a height of triangle. It's Hb=2*A/b where A- Area & b-length of the base side (your linestring).
If given 3 points you can calculate the the distances between them (sides a, b, c of triangle). It will allow you to calculate triangle Area: A=sqrt(p*(p-a)*(p-b)*(p-c)) where p is half perimeter: p=(a+b+c)/2. So, now u have all variables u need to calculate the distance Hb (your "d0").

Intersection between a line and a sphere

I'm trying to find the point of intersection between a sphere and a line but honestly, I don't have any idea of how to do so.
Could anyone help me on this one ?
Express the line as an function of t:
{ x(t) = x0*(1-t) + t*x1
{ y(t) = y0*(1-t) + t*y1
{ z(t) = z0*(1-t) + t*z1
When t = 0, it will be at one end-point (x0,y0,z0). When t = 1, it will be at the other end-point (x1,y1,z1).
Write a formula for the distance to the center of the sphere (squared) in t (where (xc,yc,zc) is the center of the sphere):
f(t) = (x(t) - xc)^2 + (y(t) - yc)^2 + (z(t) - zc)^2
Solve for t when f(t) equals R^2 (R being the radius of the sphere):
(x(t) - xc)^2 + (y(t) - yc)^2 + (z(t) - zc)^2 = R^2
A = (x0-xc)^2 + (y0-yc)^2 + (z0-zc)^2 - R^2
B = (x1-xc)^2 + (y1-yc)^2 + (z1-zc)^2 - A - C - R^2
C = (x0-x1)^2 + (y0-y1)^2 + (z0-z1)^2
Solve A + B*t + C*t^2 = 0 for t. This is a normal quadratic equation.
You can get up to two solutions. Any solution where t lies between 0 and 1 are valid.
If you got a valid solution for t, plug it in the first equations to get the point of intersection.
I assumed you meant a line segment (two end-points). If you instead want a full line (infinite length), then you could pick two points along the line (not too close), and use them. Also let t be any real value, not just between 0 and 1.
Edit: I fixed the formula for B. I was mixing up the signs. Thanks M Katz, for mentioning that it didn't work.
I believe there is an inaccuracy in the solution by Markus Jarderot. Not sure what the problem is, but I'm pretty sure I translated it faithfully to code, and when I tried to find the intersection of a line segment known to cross into a sphere, I got a negative discriminant (no solutions).
I found this: http://www.codeproject.com/Articles/19799/Simple-Ray-Tracing-in-C-Part-II-Triangles-Intersec, which gives a similar but slightly different derivation.
I turned that into the following C# code and it works for me:
public static Point3D[] FindLineSphereIntersections( Point3D linePoint0, Point3D linePoint1, Point3D circleCenter, double circleRadius )
{
// http://www.codeproject.com/Articles/19799/Simple-Ray-Tracing-in-C-Part-II-Triangles-Intersec
double cx = circleCenter.X;
double cy = circleCenter.Y;
double cz = circleCenter.Z;
double px = linePoint0.X;
double py = linePoint0.Y;
double pz = linePoint0.Z;
double vx = linePoint1.X - px;
double vy = linePoint1.Y - py;
double vz = linePoint1.Z - pz;
double A = vx * vx + vy * vy + vz * vz;
double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy +
pz * pz - 2 * pz * cz + cz * cz - circleRadius * circleRadius;
// discriminant
double D = B * B - 4 * A * C;
if ( D < 0 )
{
return new Point3D[ 0 ];
}
double t1 = ( -B - Math.Sqrt ( D ) ) / ( 2.0 * A );
Point3D solution1 = new Point3D( linePoint0.X * ( 1 - t1 ) + t1 * linePoint1.X,
linePoint0.Y * ( 1 - t1 ) + t1 * linePoint1.Y,
linePoint0.Z * ( 1 - t1 ) + t1 * linePoint1.Z );
if ( D == 0 )
{
return new Point3D[] { solution1 };
}
double t2 = ( -B + Math.Sqrt( D ) ) / ( 2.0 * A );
Point3D solution2 = new Point3D( linePoint0.X * ( 1 - t2 ) + t2 * linePoint1.X,
linePoint0.Y * ( 1 - t2 ) + t2 * linePoint1.Y,
linePoint0.Z * ( 1 - t2 ) + t2 * linePoint1.Z );
// prefer a solution that's on the line segment itself
if ( Math.Abs( t1 - 0.5 ) < Math.Abs( t2 - 0.5 ) )
{
return new Point3D[] { solution1, solution2 };
}
return new Point3D[] { solution2, solution1 };
}
Don't have enough reputation to comment on M Katz answer, but his answer assumes that the line can go on infinitely in each direction. If you need only the line SEGMENT's intersection points, you need t1 and t2 to be less than one (based on the definition of a parameterized equation). Please see my answer in C# below:
public static Point3D[] FindLineSphereIntersections(Point3D linePoint0, Point3D linePoint1, Point3D circleCenter, double circleRadius)
{
double cx = circleCenter.X;
double cy = circleCenter.Y;
double cz = circleCenter.Z;
double px = linePoint0.X;
double py = linePoint0.Y;
double pz = linePoint0.Z;
double vx = linePoint1.X - px;
double vy = linePoint1.Y - py;
double vz = linePoint1.Z - pz;
double A = vx * vx + vy * vy + vz * vz;
double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy +
pz * pz - 2 * pz * cz + cz * cz - circleRadius * circleRadius;
// discriminant
double D = B * B - 4 * A * C;
double t1 = (-B - Math.Sqrt(D)) / (2.0 * A);
Point3D solution1 = new Point3D(linePoint0.X * (1 - t1) + t1 * linePoint1.X,
linePoint0.Y * (1 - t1) + t1 * linePoint1.Y,
linePoint0.Z * (1 - t1) + t1 * linePoint1.Z);
double t2 = (-B + Math.Sqrt(D)) / (2.0 * A);
Point3D solution2 = new Point3D(linePoint0.X * (1 - t2) + t2 * linePoint1.X,
linePoint0.Y * (1 - t2) + t2 * linePoint1.Y,
linePoint0.Z * (1 - t2) + t2 * linePoint1.Z);
if (D < 0 || t1 > 1 || t2 >1)
{
return new Point3D[0];
}
else if (D == 0)
{
return new [] { solution1 };
}
else
{
return new [] { solution1, solution2 };
}
}
You may use Wolfram Alpha to solve it in the coordinate system where the sphere is centered.
In this system, the equations are:
Sphere:
x^2 + y^2 + z^2 = r^2
Straight line:
x = x0 + Cos[x1] t
y = y0 + Cos[y1] t
z = z0 + Cos[z1] t
Then we ask Wolfram Alpha to solve for t: (Try it!)
and after that you may change again to your original coordinate system (a simple translation)
Find the solution of the two equations in (x,y,z) describing the line and the sphere.
There may be 0, 1 or 2 solutions.
0 implies they don't intersect
1 implies the line is a tangent to the sphere
2 implies the line passes through the sphere.
Here's a more concise formulation using inner products, less than 100 LOCs, and no external links. Also, the question was asked for a line, not a line segment.
Assume that the sphere is centered at C with radius r. The line is described by P+l*D where D*D=1. P and C are points, D is a vector, l is a number.
We set PC = P-C, pd = PC*D and s = pd*pd - PC*PC + r*r. If s < 0 there are no solutions, if s == 0 there is just one, otherwise there are two. For the solutions we set l = -pd +- sqrt(s), then plug into P+l*D.
Or you can just find the formula of both:
line: (x-x0)/a=(y-y0)/b=(z-z0)/c, which are symmetric equations of the line segment between the points you can find.
sphere: (x-xc)^2+(y-yc)^2+(z-zc)^2 = R^2.
Use the symmetric equation to find relationship between x and y, and x and z.
Then plug in y and z in terms of x into the equation of the sphere.
Then find x, and then you can find y and z.
If x gives you an imaginary result, that means the line and the sphere doesn't intersect.
I don't have the reputation to comment on Ashavsky's solution, but the check at the end needed a bit more tweaking.
if (D < 0)
return new Point3D[0];
else if ((t1 > 1 || t1 < 0) && (t2 > 1 || t2 < 0))
return new Point3D[0];
else if (!(t1 > 1 || t1 < 0) && (t2 > 1 || t2 < 0))
return new [] { solution1 };
else if ((t1 > 1 || t1 < 0) && !(t2 > 1 || t2 < 0))
return new [] { solution2 };
else if (D == 0)
return new [] { solution1 };
else
return new [] { solution1, solution2 };

Circle collision response

I'm working on an Android game and I need to bounce 2 circles of each other (like 2 pool balls bouncing off each other). The collision is an elastic collision, and I need to calculate only 1 circles (called a Particle in my code) new velocity after the collision (the other circle, called a Barrier in my code will remain stationary and will not move because of a collision).
I am using a formula that I found on Wikipedia (http://en.wikipedia.org/wiki/Elastic_collision), but my end result for the new velocity of the particle after the collision is exactly the same as the velocity before the collision?
This is def wrong but I cant see where I am going wrong. Can anyone spot where I am going wrong?
I have just used a Java program to simulate my velocities and locations for the 2 circles as I dont wanna try it in my main Android game at the moment for fear of "breaking something"
Here is what I have so far (like I mentioned, this is just a simulation in NetBeans for the moment and I will use a menthod in my Android game to keep things a bit tidier):
double randomNextDouble = (new Random()).nextDouble();
System.out.println("Random.nextDouble: " + randomNextDouble);
double mathPI = Math.PI * 2;
System.out.println("Math PI: " + mathPI);
// get a random direction for the Particle to move towards
double direction = (new Random()).nextDouble() * Math.PI * 2;
System.out.println("Direction: " + direction);
// Then set the Particle's velocity - Increase to make Particles move faster
int velocity = 10;
System.out.println("Velocity: " + velocity);
// Then calculate the xv and the yv
// Velocity value on the x and y axis
double xv = (velocity * Math.cos(direction));
double yv = (velocity * Math.sin(direction));
System.out.println("\nXV: " + xv + "\nYV: " + yv);
// Genareting a random number for the Particle and Barrier's positions on screen
double Xmin = 0;
double Xmax = 300;
double Ymin = 0;
double Ymax = 300;
double randomNumber1 = Xmin + (int)(Math.random() * ((Xmax - Xmin) + 1));
double randomNumber2 = Ymin + (int)(Math.random() * ((Ymax - Ymin) + 1));
double randomNumber3 = Xmin + (int)(Math.random() * ((Xmax - Xmin) + 1));
double randomNumber4 = Ymin + (int)(Math.random() * ((Ymax - Ymin) + 1));
// Setting the Particle and Barrier's radius
double particleRadius = 8;
double barrierRadius = 16;
// Setting up the Particle and Barrier's mass
double particleMass = 100;
double barrierMass = 200;
// Assigning a random number to the Particle to simulate its position on screen
double particleX = randomNumber1;
double particleY = randomNumber2;
System.out.println("\nParticle X: " + particleX + " Particle Y: " + particleY);
// Assigning a random number to the Barrier to simulate its position on screen
double barrierX = randomNumber3;
double barrierY = randomNumber4;
System.out.println("Barrier X: " + barrierX + " Barrier Y: " + barrierY);
double distanceXToBarrier = barrierX - particleX;
System.out.println("\nBarrier X - Particle X: " + distanceXToBarrier);
double distanceYToBarrier = barrierY - particleY;
System.out.println("Barrier Y - Particle Y: " + distanceYToBarrier);
// Get the distance between the Particle and the Barrier
// Used for collision detection
double distance = Math.sqrt((distanceXToBarrier * distanceXToBarrier) + (distanceYToBarrier * distanceYToBarrier));
System.out.println("\nDistance: " + distance);
// Check to see if the Particle and Barrier has collided
if (distance <= particleRadius + barrierRadius)
{
System.out.println("Distance is less than 2 Radii");
}
else
System.out.println("Distance is NOT less than 2 Radii");
// Velx = (v1.u) * u + (v1 - (v1.u) * u)
// Vely = (v1.u) * u + (v1 - (v1.u) * u)
// Where v1 = xv and yv respectively
// Break it into 2 equations
// (v1.u) * u AND
// (v1 - (v1.u) * u)
//
// u = normalised Vector
// To normalize you just devide the x, y, z coords by the length of the vector.
// This then gives you the Unit Vector.
//
//Normalize the vector
double particleXNormalized = particleX * (1.0 / distance);
double particleYNormalized = particleY * (1.0 / distance);
System.out.println("\nParticle X Normalised: " + particleXNormalized +
"\nParticle Y Normalised: " + particleYNormalized);
// Calculating the first part of the eqaution
// (v1.u)
double v1DotUForX = xv * particleXNormalized;
double v1DotUForY = yv * particleYNormalized;
System.out.println("\nv1.u for X: " + v1DotUForX +
"\nv1.u for Y: " + v1DotUForY);
// The first part of the equation
// (v1.u) * u
double part1X = v1DotUForX * particleXNormalized;
double part1Y = v1DotUForY * particleYNormalized;
System.out.println("\nPart 1 for X: " + part1X +
"\nPart 1 for Y: " + part1Y);
// The second part of the equation
// (v1 - (v1.u) * u)
double part2X = (xv - (v1DotUForX) * particleXNormalized);
double part2Y = (yv - (v1DotUForY) * particleYNormalized);
System.out.println("\nPart 2 for X: " + part2X +
"\nPart 2 for Y: " + part2Y);
// Solving for:
// (((mass 1 - mass2) / (mass1 + mass2) * (v1.u) * u + ((2mass2) / (mass1 + mass2) * ((v1.u) * u))) +
// (v1 - (v1.u) * u))
double newXV = ((((particleMass - barrierMass) / (particleMass + barrierMass)) * part1X) + (((2 * barrierMass) / (particleMass + barrierMass)) * part1X) + part2X);
double newYV = ((((particleMass - barrierMass) / (particleMass + barrierMass)) * part1Y) + (((2 * barrierMass) / (particleMass + barrierMass)) * part1Y) + part2Y);
System.out.println("\nNew XV: " + newXV + "\nNew YV: " + newYV);
Looking at your algorithm, you appear to have made errors in the implementation. Why are you normalizing the coordinates of the particle? Shouldn't you be doing that to the velocity? In the usual equations, u is velocity, not position.
And why do you give the particle a random velocity (xv, yv) that has nothing to do with the two random coordinates you set up for the particle and barrier? (Surely the velocity should be some multiple of (barrier - particle) vector?)

Resources