【问题标题】:Find whether two triangles intersect or not判断两个三角形是否相交
【发布时间】:2011-10-30 02:41:02
【问题描述】:

给定 2 组点

((x1,y1,z1),(x2,y2,z2),(x3,y3,z3)) 和

((p1,q1,r1),(p2,q2,r2),(p3,q3,r3)) 每个在 3D 空间中形成一个三角形。

如何判断这些三角形是否相交?

这个问题的一个明显解决方案是找到每个三角形形成的平面的方程。如果平面平行,则它们不相交。

否则,使用这些平面的法向量找出这些平面相交形成的直线方程。

现在,如果这条线位于两个三角形区域中,那么这两个三角形相交,否则不相交。

trianglesIntersect(Triangle T1, Triangle T2)
{
   if(trianglesOnParallelPlanes(T1, T2))
   {
      return false
   }
   Line L1 = lineFromPlanes(planeFromTriangle(T1), planeFromTriangle(T2))
   if(lineOnTriangle(T1, L1) AND lineOnTriangle(T2, L1))
   {
      return true
   }
   return false
}

鉴于我知道如何编写上述函数,我应该考虑其他哪些 trianglesIntersect 实现?

有没有更快的算法可以解决这个问题?

【问题讨论】:

  • 尝试在math.stackexchange.com 上提问。 SO 用于编程问题。
  • 我很失望这个问题被关闭了。这是一个众所周知的编程问题,出现在计算机图形、光线追踪、视频游戏中。我不止一次自己编程过。这里怎么会跑题?
  • @gareth 这是一道数学题,不是编程题。如果您正在制作烹饪应用程序,您是否会考虑是否在倒入牛奶之前先搅打鸡蛋,反之亦然?
  • 来自常见问题解答:涉及“特定编程问题”或“软件算法”的问题。这不是一个“数学”问题,它更像是计算几何,并且一直出现在图形、游戏、模拟中。
  • @Lasse V. Karlsen :所以你的意思是编程问题和数学问题是两个不相交的集合?

标签: algorithm language-agnostic geometry collision-detection


【解决方案1】:

访问this table of geometric intersection algorithms@realtimerendering.com 提供,查看三角形/三角形交点的条目,然后按照参考资料进行操作,例如 Christer Ericson,Real-Time Collision Detection,第 172 页。(我强烈推荐的一本书。)

基本思想很简单。如果两个三角形相交,则要么一个三角形的两条边与另一条相交(下图中的左侧配置),要么每个三角形的一条边与另一条相交(右侧配置)。

所以执行六个线段-三角形相交测试,看看是否找到了这些配置中的任何一个。

现在,您问,如何进行线段/三角形相交测试?嗯,这很容易。访问this table of geometric intersection algorithms,查看线段(射线)/三角形交点的条目,并按照参考...

(重要的是要提到上面概述的简单测试不能正确处理共面三角形。对于许多应用程序来说,这并不重要:例如,当检测三角形网格之间的碰撞时,共面情况是不明确的,所以它确实不管返回哪个结果。但是如果您的应用程序是例外之一,您需要将其作为特殊情况进行检查,或者在 Ericson 中继续阅读以了解其他一些方法,例如,separating-axis method 或 Tomas Möller 的interval overlap method。)

【讨论】:

  • 共面三角形(使用平面方程很容易检测,使用 normal1==normal2 and d1==d2)可以在所有三角形角上使用 ptInPoly tests using barycentric coordinates 轻松测试.
  • 顺便说一下Moller's interval overlap method is here的C代码。
  • 共面重叠并不像角测试那么简单,因为仅靠这些无法检测到大卫之星的配置。如果需要检测相对三角形之间的交点,则相等性测试也可能需要考虑否定平面。
【解决方案2】:

我实现了Dynamic Collision Detection using Oriented Bounding Boxes中描述的算法仅适用于非共面三角形,它基于分离轴定理。

这是我的 GitHub 存储库,其中包含代码(在文件 collision-tests.js 中)和演示/测试页面:

特意编写代码以匹配“动态碰撞”文档的相关部分。下面是交集测试函数(以及collision-tests.js 文件的全部内容)。请注意,three.js 三角形对象具有三角形三个顶点的abc 属性。

/**
 * @function
 * @param {THREE.Triangle} t1 - Triangular face
 * @param {THREE.Triangle} t2 - Triangular face
 * @returns {boolean} Whether the two triangles intersect
 */
function doTrianglesIntersect(t1, t2) {

  /*
  Adapated from section "4.1 Separation of Triangles" of:

   - [Dynamic Collision Detection using Oriented Bounding Boxes](https://www.geometrictools.com/Documentation/DynamicCollisionDetection.pdf)
  */


  // Triangle 1:

  var A0 = t1.a;
  var A1 = t1.b;
  var A2 = t1.c;

  var E0 = A1.clone().sub(A0);
  var E1 = A2.clone().sub(A0);

  var E2 = E1.clone().sub(E0);

  var N = E0.clone().cross(E1);


  // Triangle 2:

  var B0 = t2.a;
  var B1 = t2.b;
  var B2 = t2.c;

  var F0 = B1.clone().sub(B0);
  var F1 = B2.clone().sub(B0);

  var F2 = F1.clone().sub(F0);

  var M = F0.clone().cross(F1);


  var D = B0.clone().sub(A0);


  function areProjectionsSeparated(p0, p1, p2, q0, q1, q2) {
    var min_p = Math.min(p0, p1, p2),
        max_p = Math.max(p0, p1, p2),
        min_q = Math.min(q0, q1, q2),
        max_q = Math.max(q0, q1, q2);

    return ((min_p > max_q) || (max_p < min_q));
  }


  // Only potential separating axes for non-parallel and non-coplanar triangles are tested.


  // Seperating axis: N

  {
    var p0 = 0,
        p1 = 0,
        p2 = 0,
        q0 = N.dot(D),
        q1 = q0 + N.dot(F0),
        q2 = q0 + N.dot(F1);

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Separating axis: M

  {
    var p0 = 0,
        p1 = M.dot(E0),
        p2 = M.dot(E1),
        q0 = M.dot(D),
        q1 = q0,
        q2 = q0;

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Seperating axis: E0 × F0

  {
    var p0 = 0,
        p1 = 0,
        p2 = -(N.dot(F0)),
        q0 = E0.clone().cross(F0).dot(D),
        q1 = q0,
        q2 = q0 + M.dot(E0);

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Seperating axis: E0 × F1

  {
    var p0 = 0,
        p1 = 0,
        p2 = -(N.dot(F1)),
        q0 = E0.clone().cross(F1).dot(D),
        q1 = q0 - M.dot(E0),
        q2 = q0;

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Seperating axis: E0 × F2

  {
    var p0 = 0,
        p1 = 0,
        p2 = -(N.dot(F2)),
        q0 = E0.clone().cross(F2).dot(D),
        q1 = q0 - M.dot(E0),
        q2 = q1;

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Seperating axis: E1 × F0

  {
    var p0 = 0,
        p1 = N.dot(F0),
        p2 = 0,
        q0 = E1.clone().cross(F0).dot(D),
        q1 = q0,
        q2 = q0 + M.dot(E1);

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Seperating axis: E1 × F1

  {
    var p0 = 0,
        p1 = N.dot(F1),
        p2 = 0,
        q0 = E1.clone().cross(F1).dot(D),
        q1 = q0 - M.dot(E1),
        q2 = q0;

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Seperating axis: E1 × F2

  {
    var p0 = 0,
        p1 = N.dot(F2),
        p2 = 0,
        q0 = E1.clone().cross(F2).dot(D),
        q1 = q0 - M.dot(E1),
        q2 = q1;

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Seperating axis: E2 × F0

  {
    var p0 = 0,
        p1 = N.dot(F0),
        p2 = p1,
        q0 = E2.clone().cross(F0).dot(D),
        q1 = q0,
        q2 = q0 + M.dot(E2);

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Seperating axis: E2 × F1

  {
    var p0 = 0,
        p1 = N.dot(F1),
        p2 = p1,
        q0 = E2.clone().cross(F1).dot(D),
        q1 = q0 - M.dot(E2),
        q2 = q0;

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  // Seperating axis: E2 × F2

  {
    var p0 = 0,
        p1 = N.dot(F2),
        p2 = p1,
        q0 = E2.clone().cross(F2).dot(D),
        q1 = q0 - M.dot(E2),
        q2 = q1;

    if (areProjectionsSeparated(p0, p1, p2, q0, q1, q2))
      return false;
  }


  return true;
}

【讨论】:

    猜你喜欢
    • 2014-08-01
    • 1970-01-01
    • 2011-04-06
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多