Related
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.
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.
The following is what I am trying to figure out.
Question - Explain how you can project a 2D point onto a plane to create a 3D point.
I want to know how I would go about figuring this out. I have looked through a computational geometry book and looked for anything that may relate to what I'm trying to figure out. There was no information given along with the question about the computational geometry problem. The thing is I don't know anything about computational geometry< so figuring this out is beyond my knowledge.
Can anyone point me in the right direction?
If I understood this correctly you want to project points on the 2D plane onto a plane with a different orientation. I’m also going to assume that you are looking for the orthogonal projection (i.e. all points from the xy-plane are to be projected onto the closest point on the target plane).
So we have the equations of the two planes and the point we want to project:
The original 2D plane: z = 0, with the normal vector n1 = (0, 0, 1)
The target plane: ax + by + cz + d = 0 with the normal vector n2 = (a, b, c)
Point P: (e, f, 0) which obviously lies in the xy-plane
Now, we want to travel from point P in the direction of the normal of the target plane (because this will give us the closest point on the target plane). Hence we form an equation for the line which starts at point P, and which is parallel to the normal vector of the target plane.
Line L: (x,y,z) = (e,f,0) + t(a,b,c) = (e+ta, f+tb, tc) , where t is a real valued parameter
Next, we want to find a point on the line L which also lies on the target plane. Hence, we plug the line equation into the equation for the target plane and receive:
a(e+ta) + b(f+tb) + c * tc + d= 0
ae + bf + d + t(a2 + b2 + c 2) = 0
t = - (ae + bf + d) / (a2 + b2 + c 2)
hence the projected point will be:
Pprojected = (e + ka, f + kb, kc), where k = - (ae + bf + d) / (a2 + b2 + c 2)
With all the variables in the solution about, it might be a bit hard to grasp if you are new to the area. But really, it is rather simple. The things you have to learn are:
The standard equation for a plane: ax + by + cz + d = 0
How to extract the normal vector from the equation of the plane
What the normal vector is (a vector which is perpendicular to all position vectors in the plane)
The parametric representation of a line: (x, y, z) = u + t*v* , where u is a point which lies on the line, t is a real valued parameter and v is a vector parallel to the line.
The understanding that the closest path between a point and a plane will be parallel to the normal of the plane
If you grasped the above concepts, computing the projection is simple:
Compute the normal vector of the target plane. Starting from the point that you want to project, travel parallel to the computed normal vector until you reach the plane.
I want to improve a collision system.
Right now I detect if 2 irregular objects collide if their bounding rectangles collide.
I want to obtain the for rectangle the corresponding ellipse while for the other one to use a circle. I found a method to obtain the ellipse coordinates but I have a problem when I try to detect if it intersects the circle.
Do you know a algorithm to test if a circle intersects an ellipse?
Short answer: Solving exactly for whether the two objects intersect is complicated enough to be infeasible for the purpose of collision detection. Discretize your ellipse as an n-sided polygon for some n (depending on how accurate you need to be) and do collision detection with that polygon.
Long answer: If you insist on determining if the smooth ellipse and circle intersect, there are two main approaches. Both involve solving first for the closest point to the circle's center on the ellipse, and then comparing that distance to the circle's radius.
Approach 1: Use a parametrization of the ellipse. Transform your coordinates so that the ellipse is at the origin, with its axes aligned to the x-y axes. That is:
Center of ellipse: (0,0)
Center of circle: c = (cx, cy)
Radius of circle: r
Radius of x-aligned axis of ellipse: a
Radius of y-aligned axis of ellipse: b.
The equation of the ellipse is then given by a cos(t), b sin(t). To find the closest point, we want to minimize the square distance
|| (a cos t, b sin t) - c ||^2. As Jean points out, this is "just calculus": take a derivative, and set it equal to 0. Unless I'm missing something, though, solving the resulting (quite nasty) equation for t is not possible analytically, and must be approximated using e.g. Newton's Method.
Plug in the t you find into the parametric equation to get the closest point.
Pro: Numerical solve is only in one variable, t.
Con: You must be able to write down a parametrization of the ellipse, or transform your coordinates so that you can. This shouldn't be too hard for any reasonable representation you have of the ellipse. However, I'm going to show you a second method, which is much more general and might be useful if you have to generalize your problem to, say, 3D.
Approach 2: Use multidimensional calculus. No change of coordinates is necessary.
Center of circle: c = (cx, cy)
Radius of cirlce: r
Ellipse is given by g(x, y) = 0 for a function g. For instance, per Curd's answer you might use g(x,y) = distance of (x,y) from focus 1 + distance of (x,y) from focus 2 - e.
Finding the point on the ellipse closest to the center of the circle can then be phrased as a constrained minimization problem:
Minimize ||(x,y) - c||^2 subject to g(x,y) = 0
(Minimizing the square distance is equivalent to minimizing the distance, and much more pleasant to deal with since it's a quadratic polynomial in x,y.)
To solve the constrained minimization problem, we introduce Lagrange multiplier lambda, and solve the system of equations
2 * [ (x,y) -c ] + lambda * Jg(x,y) = 0
g(x,y) = 0
Here Jg is the gradient of g. This is a system of three (nonlinear) equations in three unknowns: x, y, and lambda. We can solve this system using Newton's Method, and the (x,y) we get is the closest point to the circle's center.
Pro: No parametrization needs to be found
Pro: Method is very general, and works well whenever writing g is easier than finding a parametric equation (such as in 3D)
Con: Requires a multivariable Newton solve, which is very hairy if you don't have access to a numerical method package.
Caveat: both of these approaches technically solve for the point which extremizes the distance to the circle's center. Thus the point found might be the furthest point from the circle, and not the closest. For both methods, seeding your solve with a good initial guess (the center of the circle works well for Method 2; you're on your own for Method 1) will reduce this danger.
Potential Third Approach?: It may be possible to directly solve for the roots of the system of two quadratic equations in two variables representing the circle and ellipse. If a real root exists, the objects intersect. The most direct way of solving this system, again using a numerical algorithm like Newton's Method, won't help because lack of convergence does not necessary imply nonexistence of a real root. For two quadratic equations in two variables, however, there may exist a specialized method that's guaranteed to find real roots, if they exist. I myself can't think of a way of doing this, but you may want to research it yourself (or see if someone on stackoverflow can elaborate.)
An ellipse is defined a the set of points whose
sum of the distance to point A and the distance to point B is constant e.
(A and B are called the foci of the ellipse).
All Points P, whose sum AP + BP is less than e, lie within the ellipse.
A circle is defined as the set of points whose
distance to point C is r.
A simple test for intersection of circle and ellipse is following:
Find
P as the intersection of the circle and the line AC and
Q as the intersection of the circle and the line BC.
Circle and ellipse intersect (or the circle lies completely within the ellipse) if
AP + BP <= e or AQ + BQ <= e
EDIT:
After the comment of Martin DeMello and adapting my answer accordingly I thought more about the problem and found that the answer (with the 2nd check) still doesn't detect all intersections:
If circle and ellipse are intersecting only very scarcely (just a little more than being tangent) P and Q will not lie within the ellipse:
So the test described above detects collision only if the overlap is "big enough".
Maybe it is good enough for your practical purposes, although mathematically it is not perfect.
I know that it's too late but I hope it would help somebody. My approach to solve this problem was to interpolate the ellipse into an n-dimensions polygon, then to construct a line between every 2 points and find whether the circle intersects with any of the lines or not. This doesn't provide the best performance, but it is handy and easy to implement.
To interpolate the ellipse to an n-dimensions polygon, you can use:
float delta = (2 * PI) / n;
std::vector<Point*> interpolation;
for(float t = 0; t < (2 * PI); t += delta) {
float x = rx * cos(t) + c->get_x();
float y = ry * sin(t) + c->get_y();
interpolation.push_back(new Point(x, y));
}
c: The center of the ellipse.
rx: The radius of x-aligned axis of the ellipse.
ry: The radius of y-aligned axis of the ellipse.
Now we have the interpolation points, we can find the intersection between the circle and the lines between every 2 points.
One way to find the line-cricle intersection is described here,
an intersection occurs if an intersection occurred between any of the lines and the circle.
Hope this helps anybody.
find the point on the ellipse closest to the center of the circle
and then check if the distance from this point is smaller than the radius of the circle
if you need help doing this just comment, but it's simply calculus
edit: here's a ways towards the solution, since there is something wrong with curds
given center α β on the ellipse
and (for lack of remembering the term) x radius a, y radius b
the parametrization is
r(Θ) = (ab)/( ( (BcosΘ)^2 + (asinΘ)^2 )^.5)
x(Θ) = α + sin(Θ)r(Θ)
y(Θ) = β + cos(Θ)r(Θ)
and then just take the circle with center at (φ, ψ) and radius r
then the distance d(Θ) = ( (φ - x(Θ))^2 + (ψ - y(Θ) )^2)^.5
the minimum of this distance is when d'(Θ) = 0 (' for the derivative)
d'(Θ) = 1/d(Θ) * (-φx'(Θ) + x(Θ)x'(Θ) - ψy'(Θ) + y(Θ)y'(Θ) )
==>
x'(Θ) * (-φ + x(Θ)) = y'(Θ) * (ψ - y(Θ))
and keep going and going and hopefully you can solve for Θ
The framework you're working in might have things to help you solve this, and you could always take the easy way out and approximate roots via Newton's Method
if a circle and an ellipse collide, then either their boundaries intersect 1, 2, 3, or 4 times(or infinitely many in the case of a circular ellipse that coincides with the circle), or the circle is within the ellipse or vice versa.
I'm assuming the circle has an equation of (x - a)^2 + (y - b)^2 <= r^2 (1) and the ellipse has an equation of [(x - c)^2]/[d^2] + [(y - e)^2]/[f^2] <= 1 (2)
To check whether one of them is inside the other, you can evaluate the equation of the circle at the coordinates of the center of the ellipse(x=c, y=e), or vice versa, and see if the inequality holds.
to check the other cases in which their boundaries intersect, you have to check whether the system of equations described by (1) and (2) has any solutions.
you can do this by adding (1) and (2), giving you
(x - a)^2 + (y - b)^2 + [(x - c)^2]/[d^2] + [(y - e)^2]/[f^2] = r^2 + 1
next you multiply out the terms, giving
x^2 - 2ax + a^2 + y^2 - 2by + b^2 + x^2/d^2 - 2cx/d^2 + c^2/d^2 + y^2/f^2 - 2ey/f^2 + e^2/f^2 = r^2 + 1
collecting like terms, we get
(1 + 1/d^2)x^2 - (2a + 2c/d^2)x + (1 + 1/f^2)y^2 - (2b + 2e/f^2)y = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
now let m = (1 + 1/d^2), n = -(2a + 2c/d^2), o = (1 + 1/f^2), and p = -(2b + 2e/f^2)
the equation is now mx^2 + nx + oy^2 + py = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
now we need to complete the squares on the left hand side
m[x^2 + (n/m)x] + o[y^2 + (p/o)y] = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
m[x^2 + (n/m)x + (n/2m)^2 - (n/2m)^2] + o[y^2 + (p/o)y + (p/2o)^2 - (p/2o)^2] = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
m[(x + n/2m)^2 - (n/2m)^2] + o[(y + p/2o)^2 - (p/2o)^2] = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
m(x + n/2m)^2 - m(n/2m)^2 + o(y + p/2o)^2 - o(p/2o)^2 = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2
m(x + n/2m)^2 + o(y + p/2o)^2 = 1 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2 + m(n/2m)^2 + o(p/2o)^2
this system has a solution iff 11 + r^2 - a^2 - b^2 - c^2/d^2 - e^2/f^2 + m(n/2m)^2 + o(p/2o)^2 >= 0
There you have it, if I didn't make any algebraic mistakes. I don't know how much you can simplify the resulting expression, so this solution might be quite computationally expensive if you're going to check for many circles/ellipses
Enlarge the ellipse's major and minor radii by the radius of the circle. Then test if the center of the given circle is within this new larger ellipse by summing the distances to the foci of the enlarged ellipse.
This algorithm is quite efficient. You can early-out if the given circle doesn't intersect a circle which circumscribes the ellipse. This is slower than a bounding box test, but finding the bounding box of a non-axis-aligned ellipse is tricky.
Forget about a mathematical solution. As you can easily see by drawing, you can have up to four solutions, and thus likely a fourth grade polynomial.
Instead just do a binary search along the edge of one of the figures. It is easy to determine if a point lies within an ellipse and even more so in a circle (just see if distance is shorter than radius).
If you really want to go for the maths, Wolfram MathWorld has a nice article here: http://mathworld.wolfram.com/Circle-EllipseIntersection.html but be warned, you'll still have to write a polynomial equation solver, probably using something like binary search.
Supposing:
the ellipse is centred at the origin and with the semi-major
axis (of length a) oriented along the x axis, and with a semi-minor
axis of length b; E2 is the eccentricity squared, ie (aa-bb)/(a*a);
the circle is centred at X,Y and of radius r.
The easy cases are:
the circle centre is inside the ellipse (ie hypot(X/a, Y/b) <= 1)
so there is an intersection;
the circle centre is outside a circle centred at 0 of radius a+r
(ie hypot(X,Y) > a+r) so there isn't an intersection.
One approach for the other cases is to compute the geodetic
coordinates (latitude, height) of the circle centre. The circle
intersects the ellipse if and only if the height is less than the radius.
The geodetic latitude of a point on an ellipse is the angle
the normal to the ellipse at the point makes with the x axis, and
the height of a point outside the ellipse is the distance of the
point from the point on the ellipse closest to it. Note the geodetic latitude is not same as the polar angle from the ellipse centre to the point unless the
ellipse is in fact circular.
In formulae the conversion from geodetic coordinates lat,ht to
cartesian coordinates X,Y is
X = (nu+ht)*cos(lat), Y = (nu * (1-E2) + ht)*sin(lat)
where nu = a/sqrt( 1 - E2*sin(lat)sin(lat)).
The point on the ellipse closest to X,Y is the point
with the same latitude, but zero height, ie x = nucos(lat),
y = nu * (1-E2) * sin(lat).
Note that nu is a function of latitude.
Unfortunately the process of finding lat,ht from X,Y is an
iterative one. One approach is to first find the latitude, and then
the height.
A little algebra shows that the latitude satisfies
lat = atan2( Y+ E2*nusin(lat), X)
which can be used to compute successive approximations to the latitude,
starting at lat = atan2( Y, X(1.0-E2)), or (more efficiently) can be
solved using newton's method.
The larger E2 is, ie the flatter the ellipse is, the more
iterations will be required. For example if the ellipse is nearly
circular (say E2<0.1) then five iterations will get x,y below
to within a*1e-12, but if the ellipse is very flat, e.g. E2=0.999
you'll need around 300 iterations to get the same accuracy!
Finally, given the latitude, the height can be computed
by computing (x,y):
x = nucos(lat), y = nu(1-E2)*sin(lat)
and then h is the distance from x,y to the circle centre,
h = hypot( X-x, Y-y)
This isn't that hard. user168715's answer is generally right, but doing calculus isn't necessary. Just trigonometry.
Find the angle between the center of the two objects. Using this you can find the closest point to the circle's center on the ellipse using the polar-form:
(Taken from Wikipedia article on Ellipses)
Now compare the distance between the two object centers, subtracting the ellipse radius and circle radius.
Maybe I'm missing something; maybe ArcTan/Cos/Sin are slow -- but I don't think so, and there should be fast-approximations if needed.
I wanted to provide some input into the more general problem involving contact between two ellipses. Calculating the distance of closest approach of two ellipses was a long standing problem and was only solved analytically within the last ten years-it is by no means simple. The solution to the problem may be found here http://www.e-lc.org/docs/2007_01_17_00_46_52/.
The general method to determine if there is contact between two ellipses is to first calculate the distance of closest approach of the ellipses in their current configuration and then subtract this from their current magnitude of separation. If this result is less than or equal to 0, then they are in contact.
If anyone is interested I can post code that calculates the distance of closest approach--it's in C++. The code is for the general case of two arbitrary ellipses, but you can obviously do it for a circle and ellipse, since a circle is an ellipse with equal minor and major axes.
I am trying to determine whether a line segment (i.e. between two points) intersects a sphere. I am not interested in the position of the intersection, just whether or not the segment intersects the sphere surface. Does anyone have any suggestions as to what the most efficient algorithm for this would be? (I'm wondering if there are any algorithms that are simpler than the usual ray-sphere intersection algorithms, since I'm not interested in the intersection position)
If you are only interested if knowing if it intersects or not then your basic algorithm will look like this...
Consider you have the vector of your ray line, A -> B.
You know that the shortest distance between this vector and the centre of the sphere occurs at the intersection of your ray vector and a vector which is at 90 degrees to this which passes through the centre of the sphere.
You hence have two vectors, the equations of which fully completely defined. You can work out the intersection point of the vectors using linear algebra, and hence the length of the line (or more efficiently the square of the length of the line) and test if this is less than the radius (or the square of the radius) of your sphere.
I don't know what the standard way of doing it is, but if you only want to know IF it intersects, here is what I would do.
General rule ... avoid doing sqrt() or other costly operations. When possible, deal with the square of the radius.
Determine if the starting point is inside the radius of the sphere. If you know that this is never the case, then skip this step. If you are inside, your ray will intersect the sphere.
From here on, your starting point is outside the sphere.
Now, imagine the small box that will fit sphere. If you are outside that box, check the x-direction, y-direction and z-direction of the ray to see if it will intersect the side of the box that your ray starts at. This should be a simple sign check, or comparison against zero. If you are outside the and moving away from it, you will never intersect it.
From here on, you are in the more complicated phase. Your starting point is between the imaginary box and the sphere. You can get a simplified expression using calculus and geometry.
The gist of what you want to do is determine if the shortest distance between your ray and the sphere is less than radius of the sphere.
Let your ray be represented by (x0 + it, y0 + jt, z0 + kt), and the centre of your sphere be at (xS, yS, zS). So, we want to find t such that it would give the shortest of (xS - x0 - it, yS - y0 - jt, zS - z0 - kt).
Let x = xS - x0, y = yX - y0, z = zS - z0, D = magnitude of the vector squared
D = x^2 -2*xit + (i*t)^2 + y^2 - 2*yjt + (j*t)^2 + z^2 - 2*zkt + (k*t)^2
D = (i^2 + j^2 + k^2)t^2 - (xi + yj + zk)*2*t + (x^2 + y^2 + z^2)
dD/dt = 0 = 2*t*(i^2 + j^2 + k^2) - 2*(xi + yj + z*k)
t = (xi + yj + z*k) / (i^2 + j^2 + k^2)
Plug t back into the equation for D = .... If the result is less than or equal the square of the sphere's radius, you have an intersection. If it is greater, then there is no intersection.
This page has an exact solution for this problem. Essentially, you are substituting the equation for the line into the equation for the sphere, then computes the discriminant of the resulting quadratic. The values of the discriminant indicate intersection.
Are you still looking for an answer 13 years later? Here is a complete and simple solution
Assume the following:
the line segment is defined by endpoints as 3D vectors v1 and v2
the sphere is centered at vc with radius r
Ne define the three side lengths of a triangle ABC as:
A = v1-vc
B = v2-vc
C = v1-v2
If |A| < r or |B| < r, then we're done; the line segment intersects the sphere
After doing the check above, if the angle between A and B is acute, then we're done; the line segment does not intersect the sphere.
If neither of these conditions are met, then the line segment may or may not intersect the sphere. To find out, we just need to find H, which is the height of the triangle ABC taking C as the base. First we need φ, the angle between A and C:
φ = arccos( dot(A,C) / (|A||C|) )
and then solve for H:
sin(φ) = H/|A|
===> H = |A|sin(φ) = |A| sqrt(1 - (dot(A,C) / (|A||C|))^2)
and we are done. The result is
if H < r, then the line segment intersects the sphere
if H = r, then the line segment is tangent to the sphere
if H > r, then the line segment does not intersect the sphere
Here that is in Python:
import numpy as np
def unit_projection(v1, v2):
'''takes the dot product between v1, v2 after normalization'''
u1 = v1 / np.linalg.norm(v1)
u2 = v2 / np.linalg.norm(v2)
return np.dot(u1, u2)
def angle_between(v1, v2):
'''computes the angle between vectors v1 and v2'''
return np.arccos(np.clip(unit_projection(v1, v2), -1, 1))
def check_intersects_sphere(xa, ya, za, xb, yb, zb, xc, yc, zc, radius):
'''checks if a line segment intersects a sphere'''
v1 = np.array([xa, ya, za])
v2 = np.array([xb, yb, zb])
vc = np.array([xc, yc, zc])
A = v1 - vc
B = v2 - vc
C = v1 - v2
if(np.linalg.norm(A) < radius or np.linalg.norm(B) < radius):
return True
if(angle_between(A, B) < np.pi/2):
return False
H = np.linalg.norm(A) * np.sqrt(1 - unit_projection(A, C)**2)
if(H < radius):
return True
if(H >= radius):
return False
Note that I have written this so that it returns False when either endpoint is on the surface of the sphere, or when the line segment is tangent to the sphere, because it serves my purposes better.
This might be essentially what user Cruachan suggested. A comment there suggests that other answers are "too elaborate". There might be a more elegant way to implement this that uses more compact linear algebra operations and identities, but I suspect that the amount of actual compute required boils down to something like this. If someone sees somewhere to save some effort please do let us know.
Here is a test of the code. The figure below shows several trial line segments originating from a position (-1, 1, 1) , with a unit sphere at (1,1,1). Blue line segments have intersected, red have not.
And here is another figure which verifies that line segments that stop just short of the sphere's surface do not intersect, even if the infinite ray that they belong to does:
Here is the code that generates the image:
import matplotlib.pyplot as plt
radius = 1
xc, yc, zc = 1, 1, 1
xa, ya, za = xc-2, yc, zc
nx, ny, nz = 4, 4, 4
xx = np.linspace(xc-2, xc+2, nx)
yy = np.linspace(yc-2, yc+2, ny)
zz = np.linspace(zc-2, zc+2, nz)
n = nx * ny * nz
XX, YY, ZZ = np.meshgrid(xx, yy, zz)
xb, yb, zb = np.ravel(XX), np.ravel(YY), np.ravel(ZZ)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(n):
if(xb[i] == xa): continue
intersects = check_intersects_sphere(xa, ya, za, xb[i], yb[i], zb[i], xc, yc, zc, radius)
color = ['r', 'b'][int(intersects)]
s = [0.3, 0.7][int(intersects)]
ax.plot([xa, xb[i]], [ya, yb[i]], [za, zb[i]], '-o', color=color, ms=s, lw=s, alpha=s/0.7)
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = np.outer(np.cos(u), np.sin(v)) + xc
y = np.outer(np.sin(u), np.sin(v)) + yc
z = np.outer(np.ones(np.size(u)), np.cos(v)) + zc
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='k', linewidth=0, alpha=0.25, zorder=0)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.tight_layout()
plt.show()
you sorta have to work that the position anyway if you want accuracy. The only way to improve speed algorithmically is to switch from ray-sphere intersection to ray-bounding-box intersection.
Or you could go deeper and try and improve sqrt and other inner function calls
http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection