14

How can I programmatically detect whether or not two triangles touch each other, given their vertices on a 2D coordinate plane? This includes touching points or edges, as well as if one triangle is completely inside the other one.

qJake
  • 16,821
  • 17
  • 83
  • 135
  • 1
    Looks close to a duplicate of this question: http://stackoverflow.com/questions/1903258/triangle-triangle-intersection-test That's 3D, not 2D, but maybe some of the answers there will help you anyway. – bcat May 06 '10 at 03:29
  • 2
    I already looked at that question, it seems to have a lot more information than I need, as it's in 3D specifically, and I don't want to over-complicate the computations here (these will be performed in a loop, and should be as cost-effective as possible). – qJake May 06 '10 at 03:35
  • 1
    Possible duplicate of [What's the most efficient way to detect triangle-triangle intersections?](http://stackoverflow.com/questions/1585459/whats-the-most-efficient-way-to-detect-triangle-triangle-intersections) – TimSC Mar 27 '16 at 20:49

3 Answers3

8

You can prove that the two triangles do not collide by finding an edge (out of the total 6 edges that make up the two triangles) that acts as a separating line where all the vertices of one triangle lie on one side and the vertices of the other triangle lie on the other side. If you can find such an edge then it means that the triangles do not intersect otherwise the triangles are colliding.

Here is a Matlab implementation of the triangle collision function. You can find the theory of the sameside function here: http://www.blackpawn.com/texts/pointinpoly/default.html

function flag = triangle_intersection(P1, P2)
% triangle_test : returns true if the triangles overlap and false otherwise

% P1, P2: a 3 by 2 array (each), describing the vertices of a triangle,
% the first column corresponds to the x coordinates while the second column
% corresponds to the y coordinates

    function flag = sameside(p1,p2,a,b)
        % sameside : returns true if the p1,p1 lie on same sides of the
        % edge ab and false otherwise

        p1(3) = 0; p2(3) = 0; a(3) = 0; b(3) = 0;
        cp1 = cross(b-a, p1-a);
        cp2 = cross(b-a, p2-a);
        if(dot(cp1, cp2) >= 0)
            flag = true;
        else
            flag = false;
        end
    end

% Repeat the vertices for the loop
P1(4:5,:) = P1(1:2,:);
P2(4:5,:) = P2(1:2,:);

flag = true;

% Testing all the edges of P1
for i=1:3
    if(~sameside(P1(i,:), P2(1,:), P1(i+1,:), P1(i+2,:)) ...
            && sameside(P2(1,:), P2(2,:), P1(i+1,:), P1(i+2,:)) ...
            && sameside(P2(2,:), P2(3,:), P1(i+1,:), P1(i+2,:)))
        flag = false; return;
    end
end

% Testing all the edges of P2
for i=1:3
    if(~sameside(P2(i,:), P1(1,:), P2(i+1,:), P2(i+2,:)) ...
            && sameside(P1(1,:), P1(2,:), P2(i+1,:), P2(i+2,:)) ...
            && sameside(P1(2,:), P1(3,:), P2(i+1,:), P2(i+2,:)))
        flag = false; return;
    end
end

end
Hassan
  • 870
  • 13
  • 25
  • You can do faster than this if you keep in mind that all triangles are convex. Because they are convex, as you go around the points, they all make turns in the same direction, so you always know on which side the triangle's point is. – Eyal May 30 '17 at 18:23
6

In short, Hassan's answer is fastest.

https://jsfiddle.net/eyal/gxw3632c/

This is the fastest code in javascript:

// check that all points of the other triangle are on the same side of the triangle after mapping to barycentric coordinates.
// returns true if all points are outside on the same side
var cross2 = function(points, triangle) {
  var pa = points.a;
  var pb = points.b;
  var pc = points.c;
  var p0 = triangle.a;
  var p1 = triangle.b;
  var p2 = triangle.c;
  var dXa = pa.x - p2.x;
  var dYa = pa.y - p2.y;
  var dXb = pb.x - p2.x;
  var dYb = pb.y - p2.y;
  var dXc = pc.x - p2.x;
  var dYc = pc.y - p2.y;
  var dX21 = p2.x - p1.x;
  var dY12 = p1.y - p2.y;
  var D = dY12 * (p0.x - p2.x) + dX21 * (p0.y - p2.y);
  var sa = dY12 * dXa + dX21 * dYa;
  var sb = dY12 * dXb + dX21 * dYb;
  var sc = dY12 * dXc + dX21 * dYc;
  var ta = (p2.y - p0.y) * dXa + (p0.x - p2.x) * dYa;
  var tb = (p2.y - p0.y) * dXb + (p0.x - p2.x) * dYb;
  var tc = (p2.y - p0.y) * dXc + (p0.x - p2.x) * dYc;
  if (D < 0) return ((sa >= 0 && sb >= 0 && sc >= 0) ||
                     (ta >= 0 && tb >= 0 && tc >= 0) ||
                     (sa+ta <= D && sb+tb <= D && sc+tc <= D));
  return ((sa <= 0 && sb <= 0 && sc <= 0) ||
          (ta <= 0 && tb <= 0 && tc <= 0) ||
          (sa+ta >= D && sb+tb >= D && sc+tc >= D));
}

var trianglesIntersect4 = function(t0, t1) {
  return !(cross2(t0,t1) ||
           cross2(t1,t0));
}

I wrote the above fiddle to test a few different techniques and compare the speed. All the techniques are based on some combination of three different tools:

  1. Barycentric point-in-triangle test: Convert a point from x,y space to u,v space where u,v are two sides of the triangle. Then test if the point is inside the triangle (0,0) (0,1) (1,0), which is easy.
  2. Same-side point-in-triangle test: This test tells you if an angle is more or less than 180 degrees. If the triangle is a,b,c and your point is p, you check if the angle pab and bac are both more or both less than 180. You need to do this for ab, bc, and ca. If any are true, the point is outside. This test is slower than barycentric for one point.
  3. Line segment intersection: Check if the line segment a,b intersects line segment c,d. To do that, you find the point where the two lines cross and then check that those lines are in the bounding box of a,b and b,c. This is about as fast as Barycentric.

Those are the tools. Now to find out if triangles intersect, there are 3 ways that I tested:

  1. 8 line intersection and 2 point-in-triangle: You only need 8 line intersection and not all 9 because there can't be just 1 intersection. After that, you need 2 point-in-triangle in case 1 triangle is entirely inside the other.
  2. 6 line intersection and 4 point-in-triangle: If you draw it out, you can see that you can completely ignore one side of one of the triangles for line intersection and then just do all the other triangles points for point-in-triangle. Because line intersection and barycentric are about the same speed, this isn't much better than #1. But if you used same-side, it will be faster because same side point-in-triangle is slower.
  3. 9 same-side point-in-triangle tests: You can use either barycentric or same side. For either, you modify the point-in-triangle to test 3 points at the same time and you don't want to just test that they are all three outside the triangle, you need to test that they are all 3 outside on the same side. Because we're re-using a lot of information, we can save time in calculations. Again, barycentric seems slightly faster than same side here, too.
Eyal
  • 5,728
  • 7
  • 43
  • 70
3

Use Line Line intersection

https://www.topcoder.com/community/data-science/data-science-tutorials/geometry-concepts-line-intersection-and-its-applications/#line_line_intersection

Also consider the possibility that some vertex might be touching one of the sides of the other triangle.

http://www.blackpawn.com/texts/pointinpoly/default.html

function SameSide(p1,p2, a,b)
    cp1 = CrossProduct(b-a, p1-a)
    cp2 = CrossProduct(b-a, p2-a)
    if DotProduct(cp1, cp2) >= 0 then return true
    else return false

function PointInTriangle(p, a,b,c)
    if SameSide(p,a, b,c) and SameSide(p,b, a,c)
        and SameSide(p,c, a,b) then return true
    else return false

Or look at this link and scroll down

http://compsci.ca/v3/viewtopic.php?t=6034

baekjoon
  • 3
  • 2
Hamish Grubijan
  • 10,562
  • 23
  • 99
  • 147
  • 5
    This doesn't check if one triangle is completely inside another one, though, does it. – qJake May 06 '10 at 03:34
  • @SpikeX: I was going to say the same thing, but I think you need to click on the link at the bottom to look for the PointInPolygon writeup (http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry3) – Dave May 06 '10 at 03:37
  • For that you need to make sure that all 3 vertices are on the edges or inside the triangle. Let me find a link. Meanwhile, feel free to upvote. – Hamish Grubijan May 06 '10 at 03:37
  • Well, it may not be pretty, but I can combine your "PointInTriangle" function with a Line-Line-detection function to get what I need. (My first comment here was before the post was edited). Thanks. – qJake May 06 '10 at 03:43
  • 1
    Cool. If you want to make this super-optimized at the expense of readability, consider writing unit tests first before hacking away at code to make it faster. – Hamish Grubijan May 06 '10 at 03:50