I wrote an implementation of circle - line segment collision detection algorithm. Here is quick demonstration how it works:
And here is my implementation in Javascript using HTML canvas:
index.html
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<canvas id="canvas" width="600" height="400" style="background-color: #EEE"></canvas>
<script src="vec2.js"></script>
<script src="script.js"></script>
</body>
</html>
vec2.js - contains simple 2D vector implementation
let Vec2 = {
x: 0,
y: 0,
project(normal)
{
let projected = Vec2.create();
let dotProd = this.dot(normal);
projected.x = dotProd * normal.x;
projected.y = dotProd * normal.y;
return projected;
},
//return normalized version of vector
normalize()
{
let len = this.getLen();
let tmp = Vec2.create(this.x / len, this.y / len);
return tmp;
},
setAngle(rad)
{
let len = this.getLen();
this.x = Math.cos(rad) * len;
this.y = Math.sin(rad) * len;
return this;
},
getAngle()
{
return Math.atan2(this.y, this.x);
},
//returns length/magnitude
getLen()
{
return Math.sqrt(this.x * this.x + this.y * this.y);
},
//returns square length/magnitude
getSqrLen()
{
return this.x * this.x + this.y * this.y;
},
setLen(len)
{
let normalized = this.normalize();
normalized.x *= len;
normalized.y *= len;
this.x = normalized.x;
this.y = normalized.y;
},
multiply(scalar)
{
this.x *= scalar;
this.y *= scalar;
return this;
},
dot(vec)
{
return this.x * vec.x + this.y * vec.y;
},
create(x = 0, y = 0, angle = null)
{
let obj = Object.create(Vec2);
if(angle == null)
{
obj.x = x;
obj.y = y;
}
else
{
obj.x = 1.0; //unit vector
obj.setAngle(angle);
}
return obj;
},
};
script.js
let canvas = document.getElementById("canvas");
let context = canvas.getContext('2d');
//mouse coords
let mx = 0;
let my = 0;
//circle radius
let r = 30;
//rotation
let t = 0.0;
let speed = 0.01;
window.onload = function()
{
update();
}
window.onmousemove = function(e)
{
mx = (e.clientX - canvas.width / 2.0) - 5.0;
my = -(e.clientY - canvas.height / 2.0) + 5.0;
}
function clamp(min, max, val)
{
return val < min ? min : val > max ? max : val;
}
function getClosestPointToLine(A, B, circleCenter)
{
//vector from circle center to point B
let d = Vec2.create(B.x - circleCenter.x, B.y - circleCenter.y);
//normalized vector from A to B
let ab = Vec2.create(B.x - A.x, B.y - A.y).normalize();
//d projected to ab
let proj = d.project(ab);
//get closest point to circle
let closest = Vec2.create(B.x - proj.x, B.y - proj.y);
return closest;
}
function clampToLine(A, B, point)
{
//define segment bounding box
let minX = Math.min(A.x, B.x);
let minY = Math.min(A.y, B.y);
let maxX = Math.max(A.x, B.x);
let maxY = Math.max(A.y, B.y);
let inBoundingBox = point.x > minX && point.x < maxX &&
point.y > minY && point.y < maxY;
if(!inBoundingBox)
{
let distA = Vec2.create(point.x - A.x, point.y - A.y).getSqrLen();
let distB = Vec2.create(point.x - B.x, point.y - B.y).getSqrLen();
//if point is not in bounding box, set it as A or B depending on distances from both
point = distA < distB ? A : B;
}
return point;
}
function pointInCircle(circleCenter, r, point)
{
//create vector from circle center to closest point
let sqrDist = Vec2.create(point.x - circleCenter.x, point.y - circleCenter.y).getSqrLen();
//circle intersects when sqrDist is lower or equal to square radius
let intersects = sqrDist <= r * r;
return intersects;
}
function update()
{
t += speed;
let sr = 125; //line segment radius
//get segment points from rotation around center
let A = Vec2.create(Math.cos(t) * sr, Math.sin(t) * sr);
let B = Vec2.create(Math.cos(t + Math.PI) * sr, Math.sin(t + Math.PI) * sr);
//circle center position is mouse position
let circleCenter = Vec2.create(mx, my);
//get closest point to circle
let closest = getClosestPointToLine(A, B, circleCenter);
//clamp it to line segment
closest = clampToLine(A, B, closest);
//check that it's in the circle
let intersects = pointInCircle(circleCenter, r, closest);
//drawing
context.clearRect(0, 0, canvas.width, canvas.height);
context.save();
//center canvas, and invert y axis
context.translate(canvas.width / 2.0, canvas.height / 2.0);
context.scale(1.0, -1.0);
//circle
context.beginPath();
context.arc(mx, my, r, 0, Math.PI * 2);
context.lineWidth = 2.0;
if(intersects) context.fillStyle = "#900";
context.fill();
//segment
context.beginPath();
context.moveTo(A.x, A.y);
context.lineTo(B.x, B.y);
context.strokeStyle = "#000";
context.lineWidth = 1.0;
context.stroke();
context.restore();
requestAnimationFrame(update);
}