二维空间中三角形碰撞的检测

时间:2010-05-06 03:27:49

标签: math geometry collision-detection

如果两个三角形在2D坐标平面上的顶点,我怎么能以编程方式检测两个三角形是否相互接触?这包括触摸点或边缘,以及一个三角形是否完全在另一个内部。

3 个答案:

答案 0 :(得分:4)

你可以通过找到一条边(构成两个三角形的6条边中的一条边)来证明这两个三角形没有碰撞,这条边充当一条三角形的所有顶点位于一侧的分离线。另一个三角形的顶点位于另一侧。如果你能找到这样的边,则意味着三角形不相交,否则三角形会发生碰撞。

这是三角形碰撞函数的Matlab实现。您可以在此处找到sameside函数的理论: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

答案 1 :(得分:2)

使用直线交点

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

还要考虑一些顶点可能触及另一个三角形的一侧的可能性。

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

或者查看此链接并向下滚动

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

答案 2 :(得分:2)

简而言之,哈桑的答案最快。

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

这是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));
}

我写了上面的小提琴来测试一些不同的技术并比较速度。所有技术都基于三种不同工具的组合:

  1. 重心三角测试:将一个点从x,y空间转换为u,v空间,其中u,v是三角形的两边。然后测试该点是否在三角形(0,0)(0,1)(1,0)内,这很容易。
  2. 同侧三角测试:此测试会告诉您角度是大于还是小于180度。如果三角形是a,b,c并且您的点是p,则检查角度pab和bac是否大于或小于180.您需要对ab,bc和ca执行此操作。如果有的话,这一点就在外面。这个测试比重心一点。
  3. 线段交点:检查线段a,b是否与线段c,d相交。为此,您可以找到两条线交叉的点,然后检查这些线是否位于a,b和b,c的边界框中。这和Barycentric一样快。
  4. 这些是工具。现在要找出三角形是否相交,我测试了3种方法:

    1. 8行交叉点和2点三角形:您只需要8行交叉而不是全部9行,因为不能只有1个交叉点。之后,如果1个三角形完全位于另一个内,则需要2个三角形点。
    2. 6行交叉点和4点三角形:如果你绘制它,你可以看到你可以完全忽略其中一个三角形的一边进行直线交叉然后只做全部其他三角形指向三角形点。因为线交叉和重心速度大致相同,所以这并不比#1好多少。但是如果你使用同一侧,它会更快,因为同一侧三角形点更慢。
    3. 9个同侧三角测试:您可以使用重心或同侧。对于其中任何一个,你修改三角形点以同时测试3个点并且你不想只测试它们在三角形之外都是三个,你需要测试它们都是3个以外的在同一侧。因为我们重新使用了大量信息,所以我们可以节省计算时间。同样,重心似乎也比同侧快一点。