Intersection of two Moving Objects with Latitude/Longitude Coordinates - algorithm

This issue I can't really solve till now although I read through several articles already - hope somebody can help here.
Facts (know variables):
Two moving objects on earth surface, both with current know latitude/longitude coordinates.
The speed of both objects is know as well (in m/s).
The direction (angle) of one object is know.
Now I want to calculate the direction (angle) of the second moving object needed to intersect with (hit) the other moving object.
As the distance between the objects is small (in the range of only 5-20 km) and no very high accuracy is needed, it is OK to consider the earth surface as plane.
Therefore I already tried working with this great library:
http://www.codeproject.com/Articles/990452/Interception-of-Two-Moving-Objects-in-D-Space
But I don't really get that to work as I don't know how to convert speed in m/s back and forth to latitude/longitude velocity vectors.
To better understand the problem here an example with values:
Moving object 1 (runner):
Current location: latitude: 38.565, longitude: -98.513
Speed: 100 m/s
Direction: 270°
Moving object 2 (chaser):
Current location: latitude: 38.724, longitude: -98.449
Speed: 150 m/s
Direction: To be calculated
Any help on that would be highly appreciated, thanks in advance!

If the distances are small and you convert the latitude/longitude coordinates to x,y coordinates on a flat plane as you suggest, using e.g. the answers to this question, then the math needed to solve this problem is quite straightforward.
The image below shows the location of chaser and target at time 0 (red and blue dot), their velocity (the circles show their range after time 1, 2, 3...) and the trajectory of the target (blue ray).
The green curve contains all locations where target and chaser could be at the same moment if they keep moving at their current velocity. The intersection of this curve with the target's trajectory is the interception point (pink dot).
If the interception happens after time t, then the distance travelled by chaser and target is t.vc and t.vt. We also know the distance d between the chaser and target at time 0, and the angle α between the line segment connecting chaser and target and the target's trajectory. If we enter these into the cosine rule we get:
(t . vc)2 = (t . vt)2 + d2 - 2 . d . t . vt . cos(α)
Which transforms to this quadratic equation when we solve for time t:
(vc2 - vt2) . t2 + (2 . d . vt . cos(α)) . t - d2 = 0
If we solve this using the quadratic formula:
t = (- b ± √(b2 - 4 . a . c)) / (2 . a)
where:
a = vc2 - vt2
b = 2 . d . vt . cos(α)
c = - d2
a non-negative discriminant means interception is possible, and the root obtained by using addition in the quadratic formula is the first or only time of interception.
As you can see in the image above, if the chaser is slower than the target, there are potentially two interception points if the target moves towards the chaser (blue ray intersects with green curve), but none if the target moves away from the chaser. Using addition in the quadratic formula gives the first interception point (using subtraction would give the second).
We can then calculate the position of the target after time t, which is the interception point, and the direction from the chaser to this point.
The JavaScript code snippet below demonstrates this method, with the values from both images. It uses angles in radians and the orientation: 0 = right, π/2 = up, π = left, -π/2 = down. The case where the chaser and target have equal velocity (and the curve is a straight line) is solved using the isosceles triangle theorem, because otherwise it would lead to a division by zero in the quadratic equation.
function intercept(chaser, target) {
var dist = distance(chaser, target);
var dir = direction(chaser, target);
var alpha = Math.PI + dir - target.dir;
// EQUAL VELOCITY CASE: SOLVE BY ISOSCELES TRIANGLE THEOREM
if (chaser.vel == target.vel) {
if (Math.cos(alpha) < 0) return NaN; // INTERCEPTION IMPOSSIBLE
return (dir + alpha) % (Math.PI * 2);
}
// GENERAL CASE: SOLVE BY COSINE RULE AND QUADRATIC EQUATION
var a = Math.pow(chaser.vel, 2) - Math.pow(target.vel, 2);
var b = 2 * dist * target.vel * Math.cos(alpha);
var c = -Math.pow(dist, 2);
var disc = Math.pow(b, 2) - 4 * a * c;
if (disc < 0) return NaN; // INTERCEPTION IMPOSSIBLE
var time = (Math.sqrt(disc) - b) / (2 * a);
var x = target.x + target.vel * time * Math.cos(target.dir);
var y = target.y + target.vel * time * Math.sin(target.dir);
return direction(chaser, {x: x, y: y});
function distance(p, q) {
return Math.sqrt(Math.pow(p.x - q.x, 2) + Math.pow(p.y - q.y, 2));
}
function direction(p, q) {
return Math.atan2(q.y - p.y, q.x - p.x);
}
}
var chaser = {x: 196, y: -45, vel: 100};
var target = {x: 139, y: -312, vel: 75, dir: 0.1815142422};
document.write(intercept(chaser, target) + "<br>"); // -1.015 rad = -58.17°
var chaser = {x: 369, y: -235, vel: 37.5};
var target = {x: 139, y: -376, vel: 75, dir: 0.1815142422};
document.write(intercept(chaser, target) + "<br>"); // -1.787 rad = -102.4°
other interception points
The green curve effectively divides the 2D plane into a zone where the target will arrive first, and a zone where the chaser will arrive first. If you want the chaser and the target to move at constant speed and collide (imagine e.g. a torpedo being fired at a moving ship) then you'd aim for a point on the curve, where the two will arrive at the same time, as explained above.
However, if the chaser can go to a point and wait there for the target to arrive (imagine e.g. a person trying to catch a bus), then every point on the target's trajectory that is within the "chaser's zone" could be the interception point.
In the first image (slower target) the curve is a circle around the target, and once the target moves out of this circle (to the right of the interception point indicated in pink), the chaser could always get there first and wait for the target. This could be useful if you want a safety margin in case the chaser's or target's speed isn't constant.
In the second image (faster target) the curve is a circle around the chaser, and every point on the target's trajectory inside this circle could be the interception point. The chaser could e.g. move perpendicular to the target's trajectory, in order to minimise the distance travelled, or aim at a point halfway between the first and last interception point to maximise the waiting time.

Related

Calculate points on an arc of a circle using center, radius and 3 points on the circle

Given the center, radius and and 3 points on a circle, I want to draw an arc that starts at the first point, passing through the second and ends at the third by specifying the angle to start drawing and the amount of angle to rotate. To do this, I need to calculate the points on the arc. I want the number of points calculated to be variable so I can adjust the accuracy of the calculated arc, so this means I probably need a loop that calculates each point by rotating a little after it has calculated a point. I've read the answer to this question Draw arc with 2 points and center of the circle but it only solves the problem of calculating the angles because I don't know how 'canvas.drawArc' is implemented.
This question has two parts:
How to find the arc between two points that passes a third point?
How to generate a set of points on the found arc?
Let's start with first part. Given three points A, B and C on the (O, r) circle we want to find the arc between A and C that passes through B. To find the internal angle of the arc we need to calculate the oriented angles of AB and AC arcs. If angle of AB was greater than AC, we are in wrong direction:
Va.x = A.x - O.x;
Va.y = A.y - O.y;
Vb.x = B.x - O.x;
Vb.y = B.y - O.y;
Vc.x = C.x - O.x;
Vc.y = C.y - O.y;
tb = orientedAngle(Va.x, Va.y, Vb.x, Vb.y);
tc = orientedAngle(Va.x, Va.y, Vc.x, Vc.y);
if tc<tb
tc = tc - 2 * pi;
end
function t = orientedAngle(x1, y1, x2, y2)
t = atan2(x1*y2 - y1*x2, x1*x2 + y1*y2);
if t<0
t = t + 2 * pi;
end
end
Now the second part. You said:
I probably need a loop that calculates each point by rotating a little
after it has calculated a point.
But the question is, how little? Since the perimeter of the circle increases as its radius increase, you cannot reach a fixed accuracy with a fixed angle. In other words, to draw two arcs with the same angle and different radii, we need a different number of points. What we can assume to be [almost] constant is the distance between these points, or the length of the segments we draw to simulate the arc:
segLen = someConstantLength;
arcLen = abs(tc)*r;
segNum = ceil(arcLen/segLen);
segAngle = tc / segNum;
t = atan2(Va.y, Va.x);
for i from 0 to segNum
P[i].x = O.x + r * cos(t);
P[i].y = O.y + r * sin(t);
t = t + segAngle;
end
Note that although in this method A and C will certainly be created, but point B will not necessarily be one of the points created. However, the distance of this point from the nearest segment will be very small.

Find tangent points in a circle from a point

Circle center : Cx,Cy
Circle radius : a
Point from which we need to draw a tangent line : Px,Py
I need the formula to find the two tangents (t1x, t1y) and (t2x,t2y) given all the above.
Edit: Is there any simpler solution using vector algebra or something, rather than finding the equation of two lines and then solving equation of two straight lines to find the two tangents separately? Also this question is not off-topic because I need to write a code to find this optimally
Here is one way using trigonometry. If you understand trig, this method is easy to understand, though it may not give the exact correct answer when one is possible, due to the lack of exactness in trig functions.
The points C = (Cx, Cy) and P = (Px, Py) are given, as well as the radius a. The radius is shown twice in my diagram, as a1 and a2. You can easily calculate the distance b between points P and C, and you can see that segment b forms the hypotenuse of two right triangles with side a. The angle theta (also shown twice in my diagram) is between the hypotenuse and adjacent side a so it can be calculated with an arccosine. The direction angle of the vector from point C to point P is also easily found by an arctangent. The direction angles of the tangency points are the sum and difference of the original direction angle and the calculated triangle angle. Finally, we can use those direction angles and the distance a to find the coordinates of those tangency points.
Here is code in Python 3.
# Example values
(Px, Py) = (5, 2)
(Cx, Cy) = (1, 1)
a = 2
from math import sqrt, acos, atan2, sin, cos
b = sqrt((Px - Cx)**2 + (Py - Cy)**2) # hypot() also works here
th = acos(a / b) # angle theta
d = atan2(Py - Cy, Px - Cx) # direction angle of point P from C
d1 = d + th # direction angle of point T1 from C
d2 = d - th # direction angle of point T2 from C
T1x = Cx + a * cos(d1)
T1y = Cy + a * sin(d1)
T2x = Cx + a * cos(d2)
T2y = Cy + a * sin(d2)
There are obvious ways to combine those calculations and make them a little more optimized, but I'll leave that to you. It is also possible to use the angle addition and subtraction formulas of trigonometry with a few other identities to completely remove the trig functions from the calculations. However, the result is more complicated and difficult to understand. Without testing I do not know which approach is more "optimized" but that depends on your purposes anyway. Let me know if you need this other approach, but the other answers here give you other approaches anyway.
Note that if a > b then acos(a / b) will throw an exception, but this means that point P is inside the circle and there is no tangency point. If a == b then point P is on the circle and there is only one tangency point, namely point P itself. My code is for the case a < b. I'll leave it to you to code the other cases and to decide the needed precision to decide if a and b are equal.
Here's another way using complex numbers.
If a is the direction (a complex number of length 1) of the tangent point on the circle from the centre c, and d is the (real) length along the tangent to get to p, then (because the direction of the tangent is I*a)
p = c + r*a + d*I*a
rearranging
(r+I*d)*a = p-c
But a has length 1 so taking the length we get
|r+I*d| = |p-c|
We know everything but d, so we can solve for d:
d = +- sqrt( |p-c|*|p-c| - r*r)
and then find the a's and the points on the circle, one of each for each value of d above:
a = (p-c)/(r+I*d)
q = c + r*a
Hmm not really an algorithm question (people tend to mistake algorithm and equation) If you want to write a code then do (you did not specify language nor what prevents you from doing this which is the reason of close votes)... Without this info your OP is just asking for math equation which is indeed off-topic here and by answering this I risk (right-full) down-votes too (but this is/was asked a lot here with much less info and 4 reopen votes against 1 close put my decision weight on reopen and answering this anyway).
You can exploit the fact that you are in 2D as in 2D perpendicular vectors to vector a(x,y) are computed like this:
c = (-y, x)
d = ( y,-x)
c = -d
so you swap x,y and negate one (which one determines if the perpendicular vector is CW or CCW). It is really a rotation formula but as we rotate by 90deg the cos,sin are just +1 and -1.
Now normal n to any circumference point on circle lies in the line going through that point and circles center. So putting all this together your tangents are:
// normal
nx = Px-Cx
ny = Py-Cy
// tangent 1
tx = -ny
ty = +nx
// tangent 2
tx = +ny
ty = -nx
If you want unit vectors than just divide by radius a (not sure why you do not call it r like the rest of the math world) so:
// normal
nx = (Px-Cx)/a
ny = (Py-Cy)/a
// tangent 1
tx = -ny
ty = +nx
// tangent 2
tx = +ny
ty = -nx
Let's go through derivation process:
As you can see, if the interior of the square is < 0 it's because the point is interior to the circumferemce. When the point is outside of the circumference there are two solutions, depending on the sign of the square.
The rest is easy. Take atan(solution) and be carefull here with the signs, you may better do some checks.
Use (2) and then undo (1) transformations and that's all.
c# implementation of dmuir's answer:
static void FindTangents(Vector2 point, Vector2 circle, float r, out Line l1, out Line l2)
{
var p = new Complex(point.x, point.y);
var c = new Complex(circle.x, circle.y);
var cp = p - c;
var d = Math.Sqrt(cp.Real * cp.Real + cp.Imaginary * cp.Imaginary - r * r);
var q = GetQ(r, cp, d, c);
var q2 = GetQ(r, cp, -d, c);
l1 = new Line(point, new Vector2((float) q.Real, (float) q.Imaginary));
l2 = new Line(point, new Vector2((float) q2.Real, (float) q2.Imaginary));
}
static Complex GetQ(float r, Complex cp, double d, Complex c)
{
return c + r * (cp / (r + Complex.ImaginaryOne * d));
}
Move the circle to the origin, rotate to bring the point on X and downscale by R to obtain a unit circle.
Now tangency is achieved when the origin (0, 0), the (reduced) given point (d, 0) and an arbitrary point on the unit circle (cos t, sin t) form a right triangle.
cos t (cos t - d) + sin t sin t = 1 - d cos t = 0
From this, you draw
cos t = 1 / d
and
sin t = ±√(1-1/d²).
To get the tangency points in the initial geometry, upscale, unrotate and untranslate. (These are simple linear algebra operations.) Notice that there is no need to perform the direct transform explicitly. All you need is d, ratio of the distance center-point over the radius.

Algorithm to predict if trajectory of a line will come in contact with a rectangle?

I am unsure of what math to use here, as I am very inexperienced in the area of using math along with coding to solve problems such as this, so I was wondering if anyone here could either give me some pointers or give me somewhere to look at this specific problem. I have the x and y trajectory of the single point (or ball) and when it moves, it acts like a line, moving from one place in which is has to stop, then bouncing off of it (reflecting the trajectory), and going in that bounced trajectory. I just need the algorithm to give a true/false (boolean) to whether the current slope will come in contact with the rectangle. I have the 4 edge points of the rectangle and the middle point of the rectangle if needed.
After some back-and-forth in the comments, here is a function that may better suit OP's purpose. The is_intersect() function takes as input the position of the point on the trajectory, its direction, and a quadrilateral, and returns true if the point is on an intersecting trajectory, false otherwise.
pos is a table containing the position of the point, of the form:
pos = { x=x1, y=y1 }
dir is a number containing a positive angle in radians (0 <= θ < 2π) with respect to the positive x-axis, representing the direction of travel of the point.
quad is a table representing a quadrilateral, of the form:
quad = {{x=x1, y=y1}, {x=x2, y=y2}, {x=x3, y=y3}, {x=x4, y=y4}}
Note that it would be a simple matter, and perhaps desirable, to adapt the code to use integer-indexed tables instead, such as pos = {x1, y1} and quad = {{x1, y1}, {x2, y2},...}.
This function works for quadrilaterals situated anywhere in the plane, and for trajectory points situated anywhere in the plane. It works by finding the positive angles with respect to the positive x-axis of a line through the trajectory point and each of the corners of the quadrilateral. The function returns true if the direction angle is in this range.
function is_intersect(pos, dir, quad)
local theta_corner, theta_min, theta_max
for i = 1, 4 do
local x, y = quad[i].x, quad[i].y
-- Find angle of line from pos to corner
theta_corner = math.atan((y - pos.y) / (x - pos.x))
-- Adjust angle to quadrant
if x < pos.x then
theta_corner = theta_corner + math.pi
elseif y < pos.y then
theta_corner = theta_corner + 2*math.pi
end
-- Keep max and min angles
if (not theta_min) or theta_corner < theta_min then
theta_min = theta_corner
end
if (not theta_max) or theta_corner > theta_max then
theta_max = theta_corner
end
end
-- Compare direction angle with max and min angles
return theta_min <= dir and dir <= theta_max
end
Here is a sample interaction:
> test = {{x = 1, y = 1}, {x = 1, y = 2}, {x = 2, y = 2}, {x = 2, y = 1}}
> position = {x = 3, y = 3}
> pi = math.pi
> is_intersect(position, 5*pi/4, test)
true
> angle = math.atan(.5)
> -- checking corners
> is_intersect(position, pi + angle, test)
true
> is_intersect(position, 3*pi/2 - angle, test)
true
> -- checking slightly inside corners
> is_intersect(position, 1.01*(pi + angle), test)
true
> is_intersect(position, .99*(3*pi/2 - angle), test)
true
> -- checking slightly outside corners
> is_intersect(position, .99*(pi + angle), test)
false
> is_intersect(position, 1.01*(3*pi/2 - angle), test)
false
How it Works
This section is for OP's benefit. Read no further if you do not want a Trigonometry refresher.
In this diagram, there are two points with directions represented by a green and a yellow arrow. The red lines connect the points with the corners of the rectangle. The is_intersect() function works by calculating the angles, measured from the positive x-axis, as in the diagram, to the lines connecting the point to each of the corners of the rectangle. You can see that there will be four such lines for each point, but only two are marked in the diagram. One of these is the largest such angle, and the other is the smallest. The direction of travel for the point is specified by an angle, also measured from the positive x-axis. If this angle is between the angles to the two red lines, then the point is on an intersecting trajectory with the rectangle. The green point is on an intersecting trajectory: the angle from the positive x-axis to the line of travel for the green point (what we might call its velocity vector) is between the other two angles for this point. But the yellow point is not on an intersecting trajectory: the angle from the positive x-axis to the line of travel for the yellow point is larger than both of the other two angles.
Now, in this diagram there are four triangles. Each of the triangles has an angle at the origin of the coordinate system. We define the tangent of this angle to be the ratio of the length of the side opposite the angle (the vertical leg of the triangle) to the length of the side adjacent to the angle (the horizontal leg). That is:
tan(A) = y/x
Furthermore, the angle A is the arctangent of y/x:
A = atan(y/x)
From this diagram, it can be seen that to find the direction angle of the line connecting the point on the trajectory to the corner of the rectangle, we can calculate the angle A from the triangle, and add 270°. We really add 3π/2 radians. For a variety of reasons which I will not go into now, radians are better. Once you get used to them, you will never use degrees for any sort of calculations ever again. If you ever study Calculus, you will have to use radians. But the practical issue at the moment is that Lua's trig functions take arguments in radians, and report angles in radians.
So, the angle A is atan(x/y). How to find x and y? By subtracting the value of the x-coordinate for the point from the x-coordinate of the rectangle corner we can find x. Similarly, by subtracting the value of the y-coordinate of the point from the y-coordinate of the rectangle corner, we can find y.
We still need to know which quadrant the angle is in so that we know how much to add to A. The quadrants are traditionally labelled with the Roman numerals from Ⅰ to Ⅳ, starting from the upper right quadrant of the x-y axis. The angle in the diagram is in quadrant Ⅳ. We can tell which quadrant the angle is in by checking to see if the point is to the left or right of the corner of the rectangle, and above or below the corner of the rectangle.
With this information, the direction angles to each of the corners from the point can be found. The largest and smallest are kept and compared with the direction angle for the trajectory of the point to see if the trajectory will intersect the rectangle.
The above exposition is a pretty good description of what the code in the is_intersect() function is doing. There is a subtlety in that the subtraction to find the sides of the triangles gives negative side-lengths in some cases. This is a normal issue in trigonometric calculations; the code needs to know how the atan() function being used handles negative values. The code under the comment -- Adjust angle to quadrant takes this into account.
For the case of a coordinate system with the origin at the upper left, you simply need to measure angles in a clockwise fashion instead. This is analagous to flipping the original coordinate system upside down. By the way, the original coordinate system (with the origin at the lower-left) is the one usually found in Mathematics, and is called a right-handed coordinate system. The other system, with the origin at the upper-left, is called a left-handed coordinate system.
Addendum
In an attempt to help OP understand these functions, I provided this simple testing code. What follows is a further attempt to explicate these tests.
The above diagram shows in part the situation represented in the tests. There is a 1X1 rectangle located in quadrant I. Note that these are screen coordinates, with the origin at the upper left. All angles are measured in a clockwise direction from the positive x-axis.
In the tests you will find:
pos1 = { x = 0, y = 0 }
pos3 = { x = 3, y = 3 }
These are two positions from which we wish to test the functions. The first position is at the origin, the second is at the location (3, 3). The red lines in the diagram help us to see the direction angles from the test points to the corners of the rectangle.
First note that A = atan(1 / 2), or A = atan(.5). This angle will show up again, so I have defined the constants:
angle = math.atan(.5)
pi = math.pi
Next notice that the angle B is (π/2 - A) radians. Convince yourself that this is true by looking at the symmetry of the situation. There is no need to convert any of these angles to degrees, in fact do not do this! This will only cause problems; the usual trig functions expect arguments in radians, and return angles in radians.
Continuing, if you look closely you should be able to see that the angle C is (π + A) radians, and the angle D is (3π/2 - A) radians.
So we have, for example, these tests:
{ pass = true, args = { pos1, pi/4, rect } },
{ pass = true, args = { pos3, 5*pi/4, rect } },
The first of these tests the trajectory from position pos1, (0, 0), with a direction angle of π/4 radians. This should intersect the corner nearest pos1. The second tests the trajectory from position pos3, (3, 3), with a direction angle of 5π/4 radians. This should intersect the corner nearest pos3. So both tests should return true.
There are also tests like:
{ pass = true, args = { pos3, pi + angle, rect } },
{ pass = true, args = { pos3, 3*pi/2 - angle, rect } },
{ pass = true, args = { pos3, 1.1*(pi + angle), rect} },
{ pass = false, args = { pos3, 1.1*(3*pi/2 - angle), rect } },
{ pass = false, args = { pos3, .99*(pi + angle), rect } },
{ pass = true, args = { pos3, .99*(3*pi/2 - angle), rect } },
Now pi + angle and 3*pi/2 - angle are the direction angles to the outside corners of the rectangle from pos3, as previously noted. So the first two tests should return true, since we expect trajectories directed at the corners to intersect the rectangle. The remaining tests make the direction angles larger or smaller. Looking back at the diagram, note that if the direction angle of the trajectory from pos3 is made larger than C, there should be an intersection, but if it is made smaller than C, there should not be an intersection. Similarly, if the direction angle of the trajectory from pos3 is made larger than D, there should be no intersection, while if it is is made smaller, there should be an intersection.
All of this is just to explain what is happening in the tests, so that you can understand them and write tests of your own. In practice, you only need to specify the quadrilateral with the quad table, a position with the pos table, and a direction with dir, which is a number in radians. You specified straight-line motion; curvilinear motion is more involved.
After all of this, I still strongly suggest, as I did earlier in the comments, that you consider using #Gene's function. To use this function you specify a direction vector instead of an angle, using a table: dir = {x = x1, y = y1). This will save you many troubles, and as you mentioned earlier, you already have x- and y-components for the direction of the trajectory. Converting this information to an angle will require you to understand how to make the correct adjustments for the quadrant of the angle. Don't mess with this right now.
Here are the tests of position pos3 for Gene's function:
{ pass = true, args = { pos3, {x=-1.01, y=-2}, rect} },
{ pass = false, args = { pos3, {x=-0.99, y=-2}, rect } },
{ pass = false, args = { pos3, {x=-2.01, y=-1}, rect } },
{ pass = true, args = { pos3, {x=-1.99, y=-1}, rect } },
Instead of worrying about angles, and referring back to the last diagram, note that arrows from pos3 to the outside corners of the rectangle can be drawn by going 1 in the negative x-direction, and 2 in the negative y-direction, or by going 2 in the negative x-direction and 1 in the negative y-direction. So to test for slightly off-corner intersections, as before, the x-components of the trajectory direction vectors are modified. For example, from pos3, the trajectory {-2, -1} should intersect at the corner nearest the y-axis. So, the trajectory {-2.01, -1} should not intersect with this corner, and the trajectory {-1.99, -1} should intersect.
One caveat. Notice that there are four tests, and not six as before. Gene's function will report that there is no intersection from pos3 with the trajectory vector {-2, -1}. This is not incorrect, but just a difference in the way his code handles corner cases. In fact, I should probably remove the tests that directly test at the corners of the rectangle for my function, and just test near the corners, which is what really matters. Both functions report the same results for all other cases. The tiniest bit away from a corner, and both functions report false; the tiniest bit inside the corners, and both functions report true.
IMO atan is a bad idea. It's expensive, and the quadrant adjustment is way too hard.
You can simplify the accepted code by using atan2 rather than atan, but that's still too expensive.
You don't really care about the angle. You care about the signs of the angles between the direction vector and the extreme vectors from current position to box corners.
For this, cross-products are cheaper, simpler, and better-suited. If you have two vectors a and b, then the quantity:
s = a.x * b.y - a.y * b.x
is positive if the acute angle from a to b is positive (counter-clockwise) and vice versa.
You need only to verify that the acute angles formed by the direction vector and lines from the current point to each of the box corners include at least one positive and one negative result.
function is_intersect(pos, dir, quad)
local n_pos, n_neg = 0, 0
for i = 1, 4 do
local dx, dy = quad[i].x - pos.x, quad[i].y - pos.y
local s = dir.x * dy - dir.y * dx
if s > 0 then n_pos = n_pos + 1 end
if s < 0 then n_neg = n_neg + 1 end
end
return n_pos > 0 and n_neg > 0
end
Sorry I'm not a Lua programmer. This is a best guess at Lua syntax using the already-accepted answer as a pattern.

Polygon Algorithm

I'm trying to code a general algorithm that can find a polygon from the area swept out by a circle (red line) that follows some known path (green line), and where the circle gets bigger as it moves further down the known path. Basically, can anyone point me down a direction to solve this, please? I can't seem to nail down which tangent points are part of the polygon for any point (and thus circle) on the path.
Any help is appreciated.
Well, the easiest is to approximate your path by small segments on which your path is linear, and your circle grows linearly.
Your segments and angles will likely be small, but for the sake of the example, let's take bigger (and more obvious) angles.
Going through the geometry
Good lines for the edges of your polygon are the tangents to both circles. Note that there aren't always close to the lines defined by the intersections between the circles and the orthogonal line to the path, especially with stronger growth speeds. See the figure below, where (AB) is the path, we want the (OE) and (OF) lines, but not the (MN) one for example :
The first step is to identify the point O. It is the only point that defines a homothetic transformation between both circles, with a positive ratio.
Thus ratio = OA/OB = (radius C) / (radius C') and O = A + AB/(1-ratio)
Now let u be the vector from O to A normalized, and v a vector orthogonal to u (let us take it in the direction from A to M).
Let us call a the vector from O to E normalized, and beta the angle EOA. Then, since (OE) and (AE) are perpendicular, sin(beta) = (radius C) / OA. We also have the scalar product a.u = cos(beta) and since the norm of a is 1, a = u * cos(beta) + v * sin(beta)
Then it comes easily that with b the vector from O to F normalized, b = u * cos(beta) - v * sin(beta)
Since beta is an angle less than 90° (otherwise the growth of the circle would be so much faster than it going forward, that the second circle contains the first completely), we know that cos(beta) > 0.
Pseudo-code-ish solution
For the first and last circles you can do something closer to them -- fort the sake of simplicity, I'm just going to use the intersection between the lines I'm building and the tangent to the circle that's orthogonal to the first (or last) path, as illustrated in the first figure of this post.
Along the path, you can make your polygon arbitrarily close to the real swept area by making the segments smaller.
Also, I assume you have a function find_intersection that, given two parametric equations of two lines, returns the point of intersection between them. First of all, it makes it trivial to see if they are parallel (which they should never be), and it allows to easily represent vertical lines.
w = 1; // width of the first circle
C = {x : 0, y : 0}; // first circle center
while( (new_C, new_w) = next_step )
{
// the vector (seg_x, seg_y) is directing the segment
seg = new_C - C;
norm_seg = sqrt( seg.x * seg.x + seg.y * seg.y );
// the vector (ortho_x, ortho_y) is orthogonal to the segment, with same norm
ortho = { x = -seg.y, y = seg.x };
// apply the formulas we devised : get ratio-1
fact = new_w / w - 1;
O = new_C - seg / fact;
sin_beta = w * fact / norm_seg;
cos_beta = sqrt(1 - sin_beta * sin_beta);
// here you know the two lines, parametric equations are O+t*a and O+t*b
a = cos_beta * seg + sin_beta * ortho;
b = cos_beta * seg - sin_beta * ortho;
if( first iteration )
{
// initialize both "old lines" to a line perpendicular to the first segment
// that passes through the opposite side of the circle
old_a = ortho;
old_b = -ortho;
old_O = C - seg * (w / norm_seg);
}
P = find_intersection(old_O, old_a, O, a);
// add P to polygon construction clockwise
Q = find_intersection(old_O, old_b, O, b);
// add Q to polygon construction clockwise
old_a = a;
old_b = b;
old_O = O;
w = new_w;
C = new_C;
}
// Similarly, finish with line orthogonal to last direction, that is tangent to last circle
O = C + seg * (w / norm_seg);
a = ortho;
b = -ortho;
P = find_intersection(old_O, old_a, O, a);
// add P to polygon construction clockwise
Q = find_intersection(old_O, old_b, O, b);
// add Q to polygon construction clockwise
Let's suppose the centers are along the positive x-axis, and the lines in the envelope are y=mx and y=-mx for some m>0. The distance from (x,0) to y=mx is mx/sqrt(1+m^2). So, if the radius is increasing at a rate of m/sqrt(1+m^2) times the distance moved along the x-axis, the enveloping lines are y=mx and y=-mx.
Inverting this, if you put a circle of radius cx at the center of (x,0), then c=m/sqrt(1+m^2) so
m = c/sqrt(1-c^2).
If c=1 then you get a vertical line, and if c>1 then every point in the plane is included in some circle.
This is how you can tell how much faster than sound a supersonic object is moving from the Mach angle of the envelope of the disturbed medium.
You can rotate this to nonhorizontal lines. It may help to use the angle formulation mu = arcsin(c), where mu is the angle between the envelope and the path, and the Mach number is 1/c.

circle-circle collision

I am going to develop a 2-d ball game where two balls (circles) collide. Now I have the problem with determining the colliding point (in fact, determining whether they are colliding in x-axis/y-axis). I have an idea that when the difference between the y coordinate of 2 balls is greater than the x coordinate difference then they collide in their y axis, otherwise, they collide in their x axis. Is my idea correct? I implemented this thing in my games. Normally it works well, but sometimes, it fails. Can anyone tell me whether my idea is right? If not, then why, and is any better way?
By collision in the x axis, I mean the circle's 1st, 4th, 5th, or 8th octant, y axis means the circle's 2nd, 3rd, 6th, or 7th octant.
Thanks in advance!
Collision between circles is easy. Imagine there are two circles:
C1 with center (x1,y1) and radius r1;
C2 with center (x2,y2) and radius r2.
Imagine there is a line running between those two center points. The distance from the center points to the edge of either circle is, by definition, equal to their respective radii. So:
if the edges of the circles touch, the distance between the centers is r1+r2;
any greater distance and the circles don't touch or collide; and
any less and then do collide.
So you can detect collision if:
(x2-x1)^2 + (y2-y1)^2 <= (r1+r2)^2
meaning the distance between the center points is less than the sum of the radii.
The same principle can be applied to detecting collisions between spheres in three dimensions.
Edit: if you want to calculate the point of collision, some basic trigonometry can do that. You have a triangle:
(x1,y1)
|\
| \
| \ sqrt((x2-x1)^2 + (y2-y1)^2) = r1+r2
|y2-y1| | \
| \
| X \
(x1,y2) +------+ (x2,y2)
|x2-x1|
The expressions |x2-x1| and |y2-y1| are absolute values. So for the angle X:
|y2 - y1|
sin X = -------
r1 + r2
|x2 - x1|
cos X = -------
r1 + r2
|y2 - y1|
tan X = -------
|x2 - x1|
Once you have the angle you can calculate the point of intersection by applying them to a new triangle:
+
|\
| \
b | \ r2
| \
| X \
+-----+
a
where:
a
cos X = --
r2
so
a = r2 cos X
From the previous formulae:
|x2 - x1|
a = r2 -------
r1 + r2
Once you have a and b you can calculate the collision point in terms of (x2,y2) offset by (a,b) as appropriate. You don't even need to calculate any sines, cosines or inverse sines or cosines for this. Or any square roots for that matter. So it's fast.
But if you don't need an exact angle or point of collision and just want the octant you can optimize this further by understanding something about tangents, which is:
0 <= tan X <= 1 for 0 <= X <= 45 degrees;
tan X >= 1 for 45 <= X <= 90
0 >= tan X >= -1 for 0 >= X => -45;
tan X <= -1 for -45 >= X => -90; and
tan X = tan (X+180) = tan (X-180).
Those four degree ranges correspond to four octants of the cirlce. The other four are offset by 180 degrees. As demonstrated above, the tangent can be calculated simply as:
|y2 - y1|
tan X = -------
|x2 - x1|
Lose the absolute values and this ratio will tell you which of the four octants the collision is in (by the above tangent ranges). To work out the exact octant just compare x1 and x2 to determine which is leftmost.
The octant of the collision on the other single is offset (octant 1 on C1 means octant 5 on C2, 2 and 6, 3 and 7, 4 and 8, etc).
As cletus says, you want to use the sum of the radii of the two balls. You want to compute the total distance between the centers of the balls, as follows:
Ball 1: center: p1=(x1,y1) radius: r1
Ball 2: center: p2=(x2,y2) radius: r2
collision distance: R= r1 + r2
actual distance: r12= sqrt( (x2-x1)^2 + (y2-y1)^2 )
A collision will happen whenever (r12 < R). As Artelius says, they shouldn't actually collide on the x/y axes, they collide at a particular angle. Except, you don't actually want that angle; you want the collision vector. This is the difference between the centers of the two circles when they collide:
collision vector: d12= (x2-x1,y2-y1) = (dx,dy)
actual distance: r12= sqrt( dx*dx + dy*dy )
Note that you have already computed dx and dy above when figuring the actual distance, so you might as well keep track of them for purposes like this. You can use this collision vector for determining the new velocity of the balls -- you're going to end up scaling the collision vector by some factors, and adding that to the old velocities... but, to get back to the actual collision point:
collision point: pcollision= ( (x1*r2+x2*r1)/(r1+r2), (y1*r2+y2*r1)/(r1+r2) )
To figure out how to find the new velocity of the balls (and in general to make more sense out of the whole situation), you should probably find a high school physics book, or the equivalent. Unfortunately, I don't know of a good web tutorial -- suggestions, anyone?
Oh, and if still want to stick with the x/y axis thing, I think you've got it right with:
if( abs(dx) > abs(dy) ) then { x-axis } else { y-axis }
As for why it might fail, it's hard to tell without more information, but you might have a problem with your balls moving too fast, and passing right by each other in a single timestep. There are ways to fix this problem, but the simplest way is to make sure they don't move too fast...
This site explains the physics, derives the algorithm, and provides code for collisions of 2D balls.
Calculate the octant after this function calculates the following: position of collision point relative to centre of mass of body a; position of collision point relative to centre of mass of body a
/**
This function calulates the velocities after a 2D collision vaf, vbf, waf and wbf from information about the colliding bodies
#param double e coefficient of restitution which depends on the nature of the two colliding materials
#param double ma total mass of body a
#param double mb total mass of body b
#param double Ia inertia for body a.
#param double Ib inertia for body b.
#param vector ra position of collision point relative to centre of mass of body a in absolute coordinates (if this is
known in local body coordinates it must be converted before this is called).
#param vector rb position of collision point relative to centre of mass of body b in absolute coordinates (if this is
known in local body coordinates it must be converted before this is called).
#param vector n normal to collision point, the line along which the impulse acts.
#param vector vai initial velocity of centre of mass on object a
#param vector vbi initial velocity of centre of mass on object b
#param vector wai initial angular velocity of object a
#param vector wbi initial angular velocity of object b
#param vector vaf final velocity of centre of mass on object a
#param vector vbf final velocity of centre of mass on object a
#param vector waf final angular velocity of object a
#param vector wbf final angular velocity of object b
*/
CollisionResponce(double e,double ma,double mb,matrix Ia,matrix Ib,vector ra,vector rb,vector n,
vector vai, vector vbi, vector wai, vector wbi, vector vaf, vector vbf, vector waf, vector wbf) {
double k=1/(ma*ma)+ 2/(ma*mb) +1/(mb*mb) - ra.x*ra.x/(ma*Ia) - rb.x*rb.x/(ma*Ib) - ra.y*ra.y/(ma*Ia)
- ra.y*ra.y/(mb*Ia) - ra.x*ra.x/(mb*Ia) - rb.x*rb.x/(mb*Ib) - rb.y*rb.y/(ma*Ib)
- rb.y*rb.y/(mb*Ib) + ra.y*ra.y*rb.x*rb.x/(Ia*Ib) + ra.x*ra.x*rb.y*rb.y/(Ia*Ib) - 2*ra.x*ra.y*rb.x*rb.y/(Ia*Ib);
double Jx = (e+1)/k * (Vai.x - Vbi.x)( 1/ma - ra.x*ra.x/Ia + 1/mb - rb.x*rb.x/Ib)
- (e+1)/k * (Vai.y - Vbi.y) (ra.x*ra.y / Ia + rb.x*rb.y / Ib);
double Jy = - (e+1)/k * (Vai.x - Vbi.x) (ra.x*ra.y / Ia + rb.x*rb.y / Ib)
+ (e+1)/k * (Vai.y - Vbi.y) ( 1/ma - ra.y*ra.y/Ia + 1/mb - rb.y*rb.y/Ib);
Vaf.x = Vai.x - Jx/Ma;
Vaf.y = Vai.y - Jy/Ma;
Vbf.x = Vbi.x - Jx/Mb;
Vbf.y = Vbi.y - Jy/Mb;
waf.x = wai.x - (Jx*ra.y - Jy*ra.x) /Ia;
waf.y = wai.y - (Jx*ra.y - Jy*ra.x) /Ia;
wbf.x = wbi.x - (Jx*rb.y - Jy*rb.x) /Ib;
wbf.y = wbi.y - (Jx*rb.y - Jy*rb.x) /Ib;
}
I agree with provided answers, they are very good.
I just want to point you a small pitfall: if the speed of balls is high, you can just miss the collision, because circles never intersect for given steps.
The solution is to solve the equation on the movement and to find the correct moment of the collision.
Anyway, if you would implement your solution (comparisons on X and Y axes) you'd get the good old ping pong! http://en.wikipedia.org/wiki/Pong
:)
The point at which they collide is on the line between the midpoints of the two circles, and its distance from either midpoint is the radius of that respective circle.

Resources