I’m creating a library so it’s easier for me to create HTML5 canvas games. I’m currently working on the collision detection. This is the code I wrote for line/circle collisions is below. object1
is an object containing the circle’s x, y, and radius. object2
is an object containing both points of a line segment.
const point1 = object2.point1;
const point2 = object2.point2;
let newPoint1X = point1.x - object1.x;
let newPoint1Y = point1.y - object1.y;
let newPoint2X = point2.x - object1.x;
let newPoint2Y = point2.y - object1.y;
let lineSlope = (newPoint2Y - newPoint1Y) / (newPoint2X - newPoint1X);
let circleSlope;
if (lineSlope != 0) circleSlope = lineSlope / -1;
else circleSlope = 65535;
let closestX = (newPoint1Y - lineSlope * newPoint1X) / (circleSlope - lineSlope);
let closestY = closestX * circleSlope;
if ((closestX - newPoint1X) * (closestX - newPoint2X) >= 0 && (closestY - newPoint1Y) * (closestY - newPoint2Y) >= 0) {
if ((closestX - newPoint1X) * (closestX - newPoint2X) > 0) {
if (Math.abs(closestX - newPoint1X) > Math.abs(closestX - newPoint2X)) {
closestX = newPoint2X;
closestY = newPoint2Y;
}
else {
closestX = newPoint1X;
closestY = newPoint1Y;
}
}
else {
if (Math.abs(closestY - newPoint1Y) > Math.abs(closestY - newPoint2Y)) {
closestX = newPoint2X;
closestY = newPoint2Y;
}
else {
closestX = newPoint1X;
closestY = newPoint1Y;
}
}
}
return closestX * closestX + closestY * closestY < object1.radius * object1.radius;
Here is an example of object1
and object2
:
let object1 = {
type: "circle",
x: 100,
y: 100,
radius: 50,
color: "#90fcff"
}
let object2 = {
type: "line",
point1: {
x: 30,
y: 20
},
point2: {
x: 360,
y: 310
},
color: "#000000",
lineWidth: 1
}
I tested this code, and it doesn’t detect intersections at the right points. Any help with this?
2
Answers
I would suggest writing separate functions for the basic vector operations, like adding them, getting their size, performing a dot product, …etc.
You could use the Vector formulation of how to get the point on a given line that is closest to another point (the center of your circle).
This also allows to know how far that point is from one end of the line segment and whether that point is on the segment or outside of it.
With that information you can then determine whether the segment and the circle collide. They collide when either:
Here is an interactive OOP implementation: move the mouse to change one endpoint of a segment; the circle’s interior color will reflect whether there is a collision or not:
The given answer can be improved.
Avoid the square root
In the example below the function
rayInterceptsCircle
returnstrue
orfalse
depending on the intercept of a line segment (ray) and circle by using the distance from the line segment to the circle center.This is similar to the existing answer however it avoids the need to calculate a expensive square root
On circle perimeter
The function
rayDist2Circle
returns the distance along the line to the point where it intersects the circle, if there is no intercept then the distance is returned as Infinity. It does require up to 2 square roots.If you have many circles that you must test the line against this function can find the first circle the line intercepts by finding the minimum distance
Demo
Use mouse to move line segment endpoint. If line intercepts circle it is rendered in red to the point of intercept.