How to get ray to segment distance

1.7k Views Asked by At

For collision detection i used simple point QP to line segment S0-S1 closest distance test, with means of ortohonal projection like this:

s0s1 = s[1] - s[0];
s0qp = qp - s[0];
len2 = dot(s0s1, s0s1);
t = max(0, min(len2, dot(s0s1, s0qp))) / len2; // t is a number in [0,1] describing the closest point on the line segment s, as a blend of endpoints
cp = s[0] + s0s1 * t; // cp is the position (actual coordinates) of the closest point on the segment s
dv = cp - qp;
return dot(dv, dv);

But now i need a proximity sensor emulation. So how do i cast a ray from that point QP, but now given (unit) direction vector D? Could you point me a bit, please? :)

enter image description here

2

There are 2 best solutions below

0
On BEST ANSWER

This problem is called ray-segment intersection if you wish to search for more answers online.

So you have your ray:

$$R(t) = Q + t D \quad\quad t \in [0,\infty)$$

and your segment:

$$S(s) = S_0 + s (S_1 - S_0) \quad\quad s \in [0,1]$$

and they intersect when:

$$R(t) = S(s) \wedge t \in [0,\infty) \wedge s \in [0,1]$$

In 2D, $R(t) = S(s)$ gives you 2 linear equations (for $x$ and $y$ coordinates) in 2 unknowns ($s$ and $t$), which you can solve(*). Then you can check if they intersect, by seeing if $t \in [0,\infty)$ and $s \in [0,1]$ (if not then they don't intersect), and you can find the point of intersection by $P = R(t) = S(s)$.

(*) for example see:

but note the caveats in comments about not taking the absolute value of the cross-product.

0
On

Thanks to all of our efforts! Here is working solution for what i need:

// convenient mapping for neural network distance sensor;
scalar get_distance_inverse_squared(const point& qp, const point& d, const segment& s)  {
    auto s0s1 = s[1] - s[0];
    auto s0qp = qp - s[0];
    auto dd = d[0] * s0s1[1] - d[1] * s0s1[0];
    if (dd != 0) {
        auto r = (s0qp[1] * s0s1[0] - s0qp[0] * s0s1[1]) / dd;
        auto s = (s0qp[1] * d[0] - s0qp[0] * d[1]) / dd;
        if (r >= 0 && s >= 0 && s <= 1) {
            // inverse square of distance, always less than 1.0
            return scalar(1) / (r*r + 1);
        }
    }
    return 0; // infinitely far, parallel, no signal, etc
}