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

发布于 2024-09-01 04:45:41 字数 73 浏览 6 评论 0 原文

给定二维坐标平面上的顶点,如何以编程方式检测两个三角形是否相互接触?这包括接触点或边缘,以及一个三角形是否完全位于另一个三角形内部。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蓝戈者 2024-09-08 04:45:41

您可以通过找到一条边(在构成两个三角形的总共 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

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
初吻给了烟 2024-09-08 04:45:41

总之,哈桑的回答是最快的。

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 的边界框中。这大约与重心一样快。

这些就是工具。现在要确定三角形是否相交,我测试了 3 种方法:

  1. 8 条线相交和 2 个三角形内的点:您只需要 8 条线相交,而不是全部 9 条线相交,因为不可能只有1个路口。之后,您需要 2 个三角形内点,以防 1 个三角形完全位于另一个三角形内部。
  2. 6条线相交和4个三角形内点:如果你把它画出来,你会发现你可以完全忽略其中一个三角形的一条边来进行线相交,然后只处理所有其他三角形三角形内的点。因为线相交和重心速度大致相同,所以这并不比#1 好多少。但如果你使用同边,它会更快,因为同边三角形点更慢。
  3. 9 个同边三角形点测试:您可以使用重心或同边。对于任一情况,您修改三角形中的点以同时测试 3 个点,并且您不想只测试它们全部三个在三角形之外,您需要测试它们全部三个在 之外在同一侧。由于我们重复使用大量信息,因此可以节省计算时间。同样,重心似乎也比同侧快一些。

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.
平定天下 2024-09-08 04:45:41

使用线 线交集

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文