-5

I wrote the following program for finding out whether two line segments intersect or not. The program seems to be working incorrectly for some cases. Can someone please help me rectify as to where am I going wrong?

bool FindLineIntersection(double start1[2], double end1[2], double start2[2], double end2[2])
{
    float denom = ((end1[0] - start1[0]) * (end2[1] - start2[1])) - ((end1[1] - start1[1]) * (end2[0] - start2[0]));

    //  AB & CD are parallel 
    if (denom == 0)
        return false;

    float numer = ((start1[1] - start2[1]) * (end2[0] - start2[0])) - ((start1[0] - start2[0]) * (end2[1] - start2[1]));
    float r = numer / denom;

    float numer2 = ((start1[1] - start2[1]) * (end1[0] - start1[0])) - ((start1[0] - start2[0]) * (end1[1] - start1[1]));

    float s = numer2 / denom;

    if ((r < 0 || r > 1) || (s < 0 || s > 1))
        return false;

        return true;
}
Jannat Arora
  • 2,759
  • 8
  • 44
  • 70
  • For which cases is it working incorrectly? Also, what does this have to do with Java? – Elliott Frisch Feb 18 '16 at 06:00
  • Using an OO language, think in OOP terms: use vector and point classes and express your algorithm in terms of operations on those. For example, look into the Eigen library. – iksemyonov Feb 18 '16 at 06:01
  • @ElliottFrisch Cases where two lines intersect, yet it is not detecting the intersection – Jannat Arora Feb 18 '16 at 06:04
  • 3
    Also, pick one language: you can't be using all three in the same program. – iksemyonov Feb 18 '16 at 06:05
  • You have not given us an example of failure. Is an online judge involved, perchance? – Beta Feb 18 '16 at 06:18
  • @Beta Cases where two lines intersect, yet it is not detecting the intersection – Jannat Arora Feb 18 '16 at 06:20
  • 2
    @JannatArora: Don't be thick-headed. care to show us a set of input parameters that should intersect but don't? Or vice versa? – M Oehm Feb 18 '16 at 06:22
  • Well, I can see that the algorithm posed here differs from the one here: https://www.topcoder.com/community/data-science/data-science-tutorials/geometry-concepts-line-intersection-and-its-applications/ Namely, I can't see the C coeffs being calculated and used; and the comparison for "whether the point is on the segment" is incorrect. – iksemyonov Feb 18 '16 at 06:23
  • 1
    I tried to read the code, but got lost in the redundant parentheses. – Pete Becker Feb 18 '16 at 14:18

1 Answers1

4

You want to find the intersection point [P] of the two lines a and b:

[P] = [A] + [a]*r = [B] + [b] * s

That yields the linear equations:

[a]*r - [b]*s = [S] - [R]

or

| ax   -bx |   | r |    |Sx - Rx|
|          | * |   | =  |       |
| ay   -by |   | s |    |Sy - Ry|

Judging from your determinants, that's not the equation you are trying to solve. I think your function should look like this:

bool intersection(double start1[2], double end1[2],
                  double start2[2], double end2[2])
{
    float ax = end1[0] - start1[0];     // direction of line a
    float ay = end1[1] - start1[1];     // ax and ay as above

    float bx = start2[0] - end2[0];     // direction of line b, reversed
    float by = start2[1] - end2[1];     // really -by and -by as above

    float dx = start2[0] - start1[0];   // right-hand side
    float dy = start2[1] - start1[1];

    float det = ax * by - ay * bx;

    if (det == 0) return false;

    float r = (dx * by - dy * bx) / det;
    float s = (ax * dy - ay * dx) / det;

    return !(r < 0 || r > 1 || s < 0 || s > 1);
}

Edit II: The algorithm as presented above is very coarse. It correctly calculates the intersection of two line segments that cross each other at a non-zero angle. The following issues are not resolved:

  • Collinearity. When the main determinant is zero, the direction vectors are parallel. To check whether the two line segments are collinear, test whether the cross product of one direction vector and the difference of the two starting points is zero:

        ax * dy - ay * dx == 0
    

    Collinearity doesn't necessarily mean that the line segments overlap. For that, the coefficient r for the staring and end points of the second curve must overlap with the interval [0, 1].

  • Zero-length segments. Here, the coefficient of the zero-length line segment is indeterminate and the main determinant is zero. The algoritm could check the length and determine whether the starting point of the degenerate segmant lies on the other.

  • Two zero-length segments. Test the points for equality or decide that points can coincide, but not intersect.

  • Precision. The code here just tests for zero, which usually isn't a good idea when working with floating-point coordinates. The test should probably made against a judiciously chosen epsilon:

        if (fabs(det) < epsilon) ...
    

A robust line intersection algorithm should cater for these cases. This, as they say, is left as an exercise for the reader.

Edit: I've converted the logic to Javascript in order to visually check the function. (I've found two errors in the process, which I've fixed.) For you interactive intersection pleasure, copy the web page below to an html file and run it in a browser that can interpret the HTML5 canvas. A pair of lines are generated randomly and they should be red when the intersect and grey otherwise. Reload to reroll.

<!DOCTYPE html>

<html>

<head>
<meta charset="utf-8" />
<title>Intersect!</title>
<script type="text/javascript">

    function point(cx, a)
    {
        cx.fillRect(a[0] - 3, a[1] - 3, 7, 7);
    }

    function line(cx, a, b)
    {
        cx.beginPath();
        cx.moveTo(a[0], a[1]);
        cx.lineTo(b[0], b[1]);
        cx.stroke();
    }

    function uni(x) {
        return (Math.random() * x) | 0;
    }

    window.onload = function() {
        var cv = document.getElementById("c");
        var cx = cv.getContext("2d");

        var a1 = [uni(500), uni(500)];
        var a2 = [uni(500), uni(500)];
        var b1 = [uni(500), uni(500)];
        var b2 = [uni(500), uni(500)];

        if (intersect(a1, a2, b1, b2)) {
            cx.strokeStyle = "crimson";
            cx.fillStyle = "crimson";
        } else {
            cx.strokeStyle = "slategray";
            cx.fillStyle = "slategray";
        }

        point(cx, a1);
        point(cx, a2);
        point(cx, b1);
        point(cx, b2);
        line(cx, a1, a2);
        line(cx, b1, b2);
    }

</script>
<style type="text/css">

</style>
</head>

<body>

<canvas id="c" width="500" height="500">Kann nix.</canvas>

</body>

</html>
M Oehm
  • 28,726
  • 3
  • 31
  • 42
  • 2
    Does this solve for line segments or infinitely long lines? Crazy fast answer anyway. – iksemyonov Feb 18 '16 at 06:26
  • 1
    This solves for the line segment where the coefficients `r` and `s`are between 0 and 1. For infinitely long lines in a plane the coefficients give you the intersection point, but for a simple test it would be enough to ensure that the lines aren't parallel, `det != 0`. – M Oehm Feb 18 '16 at 06:29
  • 1
    I wonder why the formulas here: https://www.topcoder.com/community/data-science/data-science-tutorials/geometry-concepts-line-intersection-and-its-applications/ are different. Are they wrong in the link or are there 2 ways to do this? (this is a real problem at school, where they expect you to use only the given formulas only) – iksemyonov Feb 18 '16 at 06:31
  • 1
    The only time your function will return the wrong answer is if the lines are colinear. See http://stackoverflow.com/a/565282/434551 for all the math. Also the math seems a little more complex than you've presented here. – R Sahu Feb 18 '16 at 06:33
  • @RSahu You are correct but how do we rectify that ? – Jannat Arora Feb 18 '16 at 06:36
  • 1
    I've just fixed a typo (`dx` twice instead of `dy`) and the logical error that the function returned the opposite. A quick conversion to Javascript, where I can check the intersection visually on a canvas seems to confirm that the function works. Note that the function calculates intersection in a 2d plane, it does not attempt to solve the problem in 3d space, where lines may be skew. – M Oehm Feb 18 '16 at 06:40
  • @MOehm Thanks. I want to find line intersection in 2-D plane itself. :) – Jannat Arora Feb 18 '16 at 06:41
  • @JannatArora: Yes, I could see that from your code, where the vectors are 2d. I've added an interactive web toy to confirm my code. – M Oehm Feb 18 '16 at 06:47
  • @RSahu: That's partly a question of whether you want to consider two collinear and coinciding lines as intersecting. You could decide not to care (what I do above :-) or add an additional check to see whether the vectors `[a]` and `[B - A]` are collinear, i which case the lines coincide at least partly. – M Oehm Feb 18 '16 at 06:52
  • @MOehm, true. I think it's worth mentioning it in the answer to let the OP, and other readers of your answer, know of the caveats. – R Sahu Feb 18 '16 at 06:54
  • @MOehm Can we also add an additionally check for collinearity. In order to make the answer complete. Also I want to consider two coinciding lines as intersecting. Although I do not want to consider two collinear (if they do not intersect anywhere) or parallel lineas as intersecting :) – Jannat Arora Feb 18 '16 at 06:56
  • @iksemyonov: The equations at the linkes site use a different representation of the line, `ax + by = c`. I've used a vector representation where a point on the first line is `[P] = [A] + r*[a]` and on the second line `[Q] = [B] + r*[b]`. The direction vectors `[a]` and `[b]` are the difference between the end and start points and hence it is easy to see why an ´r` and `s` between 0 and 1 refers to a point on the given line segment. – M Oehm Feb 18 '16 at 06:58
  • @JannatArora: I've outlined the shortcomings of the algorithm above. Your algorithm should cater for these corner cases. At least it should think about what the result should be whan the lines are degerenate or collinear. I've outlined how to check these case, but I haven't implemented it. Implementing it would be a lot of work. That's the difference between a robust, generally applicable algorithm and a quick-and-dirty functions that gets the work done is most of the cases. What I've written is clearly the latter at the moment. – M Oehm Feb 18 '16 at 07:32