Line Circle intersection for Vertical and Horizontal Lines
Sebastian Wright
I'm trying to detect when a line intersects a circle in javascript. I found a function that works almost perfectly but I recently noticed that it does not work when the intersecting line is perfectly horizontal or vertical. Since I don't have a great understanding of how this function actually works, I'm not sure how to edit it to get the results I'd like.
function lineCircleCollision(circleX,circleY,radius,lineX1,lineY1,lineX2,lineY2) { var d1 = pDist(lineX1,lineY1,circleX,circleY); var d2 = pDist(lineX2,lineY2,circleX,circleY); if (d1<=radius || d2<=radius) { return true; } var k1 = ((lineY2-lineY1)/(lineX2-lineX1)); var k2 = lineY1; var k3 = -1/k1; var k4 = circleY; var xx = (k1*lineX1-k2-k3*circleX+k4)/(k1-k3); var yy = k1*(xx-lineX1)+lineY1; var allow = true; if (lineX2>lineX1) { if (xx>=lineX1 && xx<=lineX2) {} else {allow = false;} } else { if (xx>=lineX2 && xx<=lineX1) {} else {allow = false;} } if (lineY2>lineY1) { if (yy>=lineY1 && yy<=lineY2) {} else {allow = false;} } else { if (yy>=lineY2 && yy<=lineY1) {} else {allow = false;} } if (allow) { if (pDist(circleX,circleY,xx,yy)<radius) { return true; } else { return false; } } else { return false; }
}
function pDist(x1,y1,x2,y2) { var xd = x2-x1; var yd = y2-y1; return Math.sqrt(xd*xd+yd*yd);
} 2 3 Answers
You can express the line as two relations:
x = x1 + k * (x2 - x1) = x1 + k * dx
y = y1 + k * (y2 - y1) = y1 + k * dywith 0 < k < 1. A point on the circle satisfies the equation:
(x - Cx)² + (y - Cy)² = r²Replace x and y by the line equations and you'll get a quadratic equation:
a*k² + b*k + c = 0
a = dx² + dy²
b = 2*dx*(x1 - Cx) + s*dy*(y1 - Cy)
c = (x1 - Cx)² + (y1 - Cy)² - r²Solve that and if any of the two possible solutions for k lies in the range between 0 and 1, you have a hit. This method checks real intersections and misses the case where the line is entirely contained in the circle, so an additional check whether the line's end points lie within the circle is necessary.
Here's the code:
function collision_circle_line(Cx, Cy, r, x1, y1, x2, y2) { var dx = x2 - x1; var dy = y2 - y1; var sx = x1 - Cx; var sy = y1 - Cy; var tx = x2 - Cx; var ty = y2 - Cy; if (tx*tx + ty*ty < r*r) return true; var c = sx*sx + sy*sy - r*r; if (c < 0) return true; var b = 2 * (dx * sx + dy * sy); var a = dx*dx + dy*dy; if (Math.abs(a) < 1.0e-12) return false; var discr = b*b - 4*a*c; if (discr < 0) return false; discr = Math.sqrt(discr); var k1 = (-b - discr) / (2 * a); if (k1 >= 0 && k1 <= 1) return true; var k2 = (-b + discr) / (2 * a); if (k2 >= 0 && k2 <= 1) return true; return false;
} 0 Another way to view the intersection check is that we're finding the point on the line segment closest to the circle center and then determining whether it's close enough. Since distance to the circle center is a convex function, there are three possibilities: the two endpoints of the segment, and the closest point on the line, assuming that it's on the segment.
To find the closest point on the line, we have an overdetermined linear system
(1 - t) lineX1 + t lineX2 = circleX
(1 - t) lineY1 + t lineY2 = circleY,expressed as a matrix:
[lineX2 - lineX1] [t] = [circleX - lineX1]
[lineY2 - lineY1] [circleY - lineY1].The closest point can be found by solving the normal equation
[(lineX2 - lineX1) (lineY2 - lineY1)] [lineX2 - lineX1] [t] = [lineY2 - lineY1]
[(lineX2 - lineX1) (lineY2 - lineY1)] [circleX - lineX1] [circleY - lineY1],expressed alternatively as
((lineX2 - lineX1)^2 + (lineY2 - lineY1)^2) t =
(lineX2 - lineX1) (circleX - lineX1) + (lineY2 - lineY1) (circleY - lineY1),and solved for t:
(lineX2 - lineX1) (circleX - lineX1) + (lineY2 - lineY1) (circleY - lineY1)
t = ---------------------------------------------------------------------------. ((lineX2 - lineX1)^2 + (lineY2 - lineY1)^2)Assuming that t is between 0 and 1, we can plug it in and check the distance. When t is out of range, we can clamp it and check only that endpoint.
Untested code:
function lineCircleCollision(circleX, circleY, radius, lineX1, lineY1, lineX2, lineY2) { circleX -= lineX1; circleY -= lineY1; lineX2 -= lineX1; lineY2 -= lineY1; var t = (lineX2 * circleX + lineY2 * circleY) / (lineX2 * lineX2 + lineY2 * lineY2); if (t < 0) t = 0; else if (t > 1) t = 1; var deltaX = lineX2 * t - circleX; var deltaY = lineY2 * t - circleY; return deltaX * deltaX + deltaY * deltaY <= radius * radius;
} 1 If you do not need the point just want to know if line intersects then:
compute distance from circle center P0(x0,y0) and line endpoints P1(x1,y1),P2(x2,y2)
double d1=|P1-P0|=sqrt((x1-x0)*(x1-x0)+(y1-x0)*(y1-x0));double d2=|P2-P0|=sqrt((x2-x0)*(x2-x0)+(y2-x0)*(y2-x0));
order d1,d2 ascending
if (d1>d2) { double d=d1; d1=d2; d2=d; }
check intersection
if ((d1<=r)&&(d2>=r)) return true; else return false;ris circle radius
[notes]
- you do not need sqrt distances
- if you leave them un-sqrted then just compare them to
r*rinstead ofr