This issue is relatively easy to solve, if formulated appropriately. It seems a simple case of collision detection, where you're seeking to adjust the object's position after detecting the collision. The intuitive way to frame it is: you have an event telling you your object crossed a boundary. Its last motion vector is (dirX, dirY)
and you're given the delta
your object crossed beyond the boundary. All you need to do is to move your object backwards, the same delta it crossed beyond the boundary, along the same motion vector.
And instead of reasoning about the problem in terms of tangent points, you can simply reason about it in terms of the center coordinates of the object itself.
Now, the following parameters are given input:
- let
x, y
be the last coordinates of your object's center and r
its radius
- let
(dirX, dirY)
be your motion vector
- let
alpha
be the angle of your motion vector to the x axis (you can compute it from the motion vector itself)
- let
delta
be the distance crossed beyond the boundary
The problem's resolution reduces to the following:

And here's an executable code sample in python 2 that implements and tests that solution:
from math import sin, cos, atan, fabs as abs, sqrt, pow
from collections import namedtuple
Disk = namedtuple('Disk', 'x y radius')
MotionVector = namedtuple('MotionVector', 'x y')
def sqr(d):
return pow(d, 2)
def reposition_disk(disk, delta, motion_vector):
alpha = atan(motion_vector.y / motion_vector.x)
dX = cos(alpha) * delta
dY = sin(alpha) * delta
return Disk(disk.x - dX, disk.y - dY, disk.radius)
def test_reposition_disk():
disk = Disk(80, 70, 40)
motion_vector = MotionVector(10, -5)
delta = 5
updated = reposition_disk(disk, delta, motion_vector)
alpha = atan(motion_vector.y / motion_vector.x)
bX = disk.x + (disk.radius - delta) * cos(alpha)
bY = disk.y + (disk.radius - delta) * sin(alpha)
distance = sqrt(sqr(updated.x - bX ) + sqr(updated.y - bY))
print('Distance to boundary : %f' % distance)
assert abs(distance - disk.radius) < 0.01
test_reposition_disk()
Addendum
I went through the code you pasted and unless I am mistaken, there is a problem with the logic through which, you are checking for collision and computing the distance from the center of the circle to the intersecting line. An accurate implementation of that logic implies solving a quadratic equation (check this link for future reference) to find out whether the line intersects the circle. Then to compute the distance you'll have to solve an equation based on the dot product of the direction vector of the intersecting line, and the orthogonal line passing through the center of the circle.
Here's a python snippet illustrating the first part (figuring out whether or not the line intersects the circle) and loosely following the steps detailed in the above link:
Line = namedtuple('Line', 'x1 y1 x2 y2')
def sqr(d):
return pow(d, 2)
def coords_to_affine(line):
h = (line.y2 - line.y1) / (line.x2 - line.x1)
j = y1 - (h * x1)
return (h, j)
def compute_quadratic_params(disk, line):
(h, j) = coords_to_affine(line)
(p, q, r) = (disk.x, disk.y, disk.r)
a = sqr(h) + 1
b = 2 * ((h * j) - (h * q) - p)
c = sqr(q) - sqr(r) + sqr(p) - (2 * (j * q)) + sqr(j)
return (a, b, c)
def disk_intersects_line(a, b, c):
return (sqr(b) - (4 * a * c)) >= 0