【问题标题】:Determining if two rays intersect判断两条射线是否相交
【发布时间】:2011-02-25 06:30:30
【问题描述】:

我在 2D 平面上有两条延伸到无穷远的射线,但它们都有一个起点。它们都由一个起点和一个延伸到无穷远的射线方向上的向量来描述。我想知道两条射线是否相交,但我不需要知道它们相交的位置(这是碰撞检测算法的一部分)。

到目前为止,我所看到的所有内容都描述了找到两条线或线段的交点。有没有快速的算法来解决这个问题?

【问题讨论】:

  • 2D 还是 3D?如果前者只是检查并查看两者的斜率是否相同:如果是,它们是平行的或同一条线。否则它们会相交。
  • 这些是光线,不是线,那么?所有线都在二维上相交,除非它们是平行的。
  • @fbereto:抱歉,二维平面。编辑以反映这一点。
  • @Carl Norum:是的,你的权利。对不起,你是对的
  • 乍一看,寻找矢量产品和比较角度的一些奇特用途很诱人,但考虑获得这些产品所需的计算并查看亚当或彼得的解决方案。计算方程组的行列式与计算向量积几乎相同

标签: algorithm math geometry intersection


【解决方案1】:

根据此处的其他答案,我在尝试找到两条光线之间的交点时发现了这篇文章。以防万一其他人来到这里寻找相同的答案,这里有一个 TypeScript / JavaScript 的答案。

/**
 * Get the intersection of two rays, with origin points p0 and p1, and direction vectors n0 and n1.
 * @param p0 The origin point of the first ray
 * @param n0 The direction vector of the first ray
 * @param p1 The origin point of the second ray
 * @param n1 The direction vector of the second ray
 * @returns
 */
export function getRaysIntersection(
  p0: number[],
  n0: number[],
  p1: number[],
  n1: number[]
): number[] | undefined {
  const dx = p1[0] - p0[0];
  const dy = p1[1] - p0[1];
  const det = n1[0] * n0[1] - n1[1] * n0[0];
  const u = (dy * n1[0] - dx * n1[1]) / det;
  const v = (dy * n0[0] - dx * n0[1]) / det;
  if (u < 0 || v < 0) return undefined; // Might intersect as lines, but as rays.

  const m0 = n0[1] / n0[0];
  const m1 = n1[1] / n1[0];
  const b0 = p0[1] - m0 * p0[0];
  const b1 = p1[1] - m1 * p1[0];
  const x = (b1 - b0) / (m0 - m1);
  const y = m0 * x + b0;

  return Number.isFinite(x) ? [x, y] : undefined;
}

在这里演示:https://codesandbox.io/s/intersection-of-two-rays-mcwst

【讨论】:

    【解决方案2】:

    用于 Guntners 解决方案的 c++

    bool RaysIntersection(const Point& as, const Point& ad, const Point& bs, const Point& bd, Point& result)
    {
        if (as == bs) {
            result = as;
            return true;
        }
        auto dx = bs.X - as.X;
        auto dy = bs.Y - as.Y;
        auto det = bd.X * ad.Y - bd.Y * ad.X;
        if (det != 0) { // near parallel line will yield noisy results
            double u = (dy * bd.X - dx * bd.Y) / (double)det;
            double v = (dy * ad.X - dx * ad.Y) / (double)det;
            if (u >= 0 && v >= 0) {
                result = as + ad * u;
                return true;
            }
        }
        return false;
    }
    

    【讨论】:

      【解决方案3】:

      我很抱歉不同意 Peter Walser 的回答。在我的桌子上求解方程:

      u = ((bs.y - as.y) * bd.x - (bs.x - as.x) * bd.y) / (bd.x * ad.y - bd.y * ad.x)
      v = ((bs.y - as.y) * ad.x - (bs.x - as.x) * ad.y) / (bd.x * ad.y - bd.y * ad.x)
      

      考虑到常用术语,这是:

      dx = bs.x - as.x
      dy = bs.y - as.y
      det = bd.x * ad.y - bd.y * ad.x
      u = (dy * bd.x - dx * bd.y) / det
      v = (dy * ad.x - dx * ad.y) / det
      

      五次减法,六次乘法和两次除法。

      如果只需要知道射线是否相交,u 和 v 的符号就足够了,这两个除法可以用 num*denom

      请注意,det==0 的罕见情况意味着光线不相交(另外一个比较)。

      【讨论】:

      • 对于那些对推导感到困惑的人,这里是:math.stackexchange.com/questions/2788943
      • 链接已过期。
      • 小尼特:对于det==0的情况,可能意味着没有交集,也可能意味着到处都有交集(输入线重叠)。也许它可以更准确地表述为“光线没有独特的交点”。
      【解决方案4】:

      我只想检查两条射线是否相交。我将通过计算从两条射线创建的两个“三角形”的旋转方向来解决这个问题。它们并不是真正的三角形,但从数学的角度来看,如果我只想计算三角形的旋转,我只需要两个具有共同起点的向量,其余的都无所谓。

      第一个三角形将由两个向量和一个起点组成。起点将是第一条射线的起点。第一个向量将是第一条光线的方向向量。第二个向量将是从第一条射线的起点到第二条射线的起点的向量。从这里我们取两个向量的叉积并记下符号。

      我们对第二个三角形再次这样做。同样,起点是第二条射线的起点。第一个向量是第二条射线的方向,第二个向量是从第二条射线的起点到第一条射线的起点。我们再次取向量的叉积并记下符号。

      现在我们只需要两个标志并检查它们是否相同。如果它们相同,我们就没有交集。如果它们不同,我们就有一个交叉点。就是这样!

      这是一些伪代码:

      sign1 = cross(vector1, point1 - point2)
      sign2 = cross(vector2, point2 - point1)
      
      if (sign1 * sign2 < 0) // If signs are mismatched, they will multiply to be negative
          return intersection
      

      结果是五次乘法、六次减法和一次比较。

      【讨论】:

      • 啊,你明白了。没看到。
      • 不知道是否有必要考虑“边缘情况”,但我编辑了我的答案来描述它们。
      • 我的应用程序中出现边缘情况的可能性很小,即使是误报也不会真正伤害到我。我需要原始速度,因为我正在迭代可能数十亿个这样的案例。我只是简单地说,如果有什么共线的,我们就称它为交集,而不是添加一些额外的语句来确定到底发生了什么。
      • 不,这是错误的。请参阅我在 CashCommons 的同样不正确的解决方案上对 John 的评论。
      【解决方案5】:

      GeomAlgorithms.com 有一些非常棒的算法来处理 3D 中的线...不过一般来说,两条线在 3D 空间中相交的概率确实很低。

      在 2D 中,您必须检查斜率。如果斜率不相等,则它们相交。如果斜率相等,则如果它们上的一个点具有相同的 x 坐标或相同的 y 坐标,则它们相交。

      【讨论】:

      • 该问题明确指出它仅限于 2d。
      • @glowcoder 在我最初回答的时候并没有说明,编辑帖子来描述二维算法。
      • @glowcoder:没关系,如果它是 3D 的,我需要做的就是将所有 z 分量设置为零并简化方程,它应该可以工作。谢谢vicatcu,我去看看这个网站。
      【解决方案6】:

      如果线是无限长的,那么它们将总是相交,除非它们是平行的。要检查它们是否平行,请找到每条线的斜率并进行比较。斜率将是 (y2-y1)/(x2-x1)。

      【讨论】:

      • 线不是射线。平行线也可以相交(如果它们是同一条线)。
      • 另外,如果二维空间是弯曲的,平行线可以相交(例如纬度和经度)。
      • 对不起,我编辑了我的帖子以反映我真正的要求。它们的线实际上是有起点但没有终点的射线。检查斜率不会给我答案,因为“线”有可能在点的“错误”一侧相交,这被视为未命中。
      • 我相信这个问题暗示它们在两个方向上都不是无限的:它们有一个起始向量和一个方向向量。因此,从 (1,2) 开始并在 (0,1) 开始的线不会与从 (2,1) 开始并在 (1,0) 开始的线相交。如果它们是线条而不是“射线”,那么您的答案当然是正确的。
      【解决方案7】:

      给定:两条射线 a、b,起点(原矢量)as、bs 和方向矢量 ad、bd。

      如果有交点p,两条线相交:

      p = as + ad * u
      p = bs + bd * v
      

      如果此方程组有 u>=0 和 v>=0 的解(正方向使它们成为射线),则射线相交。

      对于二维向量的 x/y 坐标,这意味着:

      p.x = as.x + ad.x * u
      p.y = as.y + ad.y * u
      p.x = bs.x + bd.x * v
      p.y = bs.y + bd.y * v
      

      进一步的步骤:

      as.x + ad.x * u = bs.x + bd.x * v
      as.y + ad.y * u = bs.y + bd.y * v
      

      解决 v:

      v := (as.x + ad.x * u - bs.x) / bd.x
      

      对 u 进行插入和求解:

      as.y + ad.y * u = bs.y + bd.y * ((as.x + ad.x * u - bs.x) / bd.x) 
      u := (as.y*bd.x + bd.y*bs.x - bs.y*bd.x - bd.y*as.x ) / (ad.x*bd.y - ad.y*bd.x)
      

      计算u,然后计算v,如果两者都为正则射线相交,否则不相交。

      【讨论】:

      • 你如何从最后一个公式中解出你?它包括自己。
      • 哎呀,我在错误的方程式中插入了“v”。现在已经修好了。
      • 我知道这篇文章很旧,我不知道为什么或如何,但是当向量 a 和 be 切换时,这个解决方案会导致不同的解决方案,特别是当解决方案应该是 v = 2 但是它等于 1。我使用的点是:对于第一个向量(7.64475393f,5.59931898f),(6.30824f,4.91833f)对于第二个向量(6.43122959f,7.98099709f),(6.43122864f,6.48099709f)
      • 你如何从as.y + ad.y * u = bs.y + bd.y * ((as.x + ad.x * u - bs.x) / bd.x) u := (as.y*bd.x + bd.y*bs.x - bs.y*bd.x - bd.y*as.x ) / (ad.x*bd.y - ad.y*bd.x)
      【解决方案8】:

      线由点p和向量v表示:

      line = p + a * v(对于所有 a)

      光线是那条线的(正)一半:

      ray = p + a * v(对于所有 a >= 0)

      要确定两条线是否相交,请将它们设置为相等并求解:

      相交发生在 p1 + a1 * v1 = p2 + a2 * v2
      (请注意,有两个未知数,a1 和 a2,以及两个方程,因为 pv 是多维的)

      求解 a1 和 a2 - 如果它们都是非负数,则它们相交。如果一个是负数,它们就不会相交。

      【讨论】:

        【解决方案9】:

        一条射线可以用点集A + Vt来表示,其中A是起点,V是表示射线方向的向量,t &gt;= 0是参数。因此,要确定两条射线是否相交,请执行以下操作:

        bool DoRaysIntersect(Ray r1, Ray r2)
        {
            // Solve the following equations for t1 and t2:
            //   r1.A.x + r1.V.x * t1 == r2.A.x + r2.V.x * t2
            //   r1.A.y + r1.V.y * t1 == r2.A.y + r2.V.y * t2
            if(no solution)  // (e.g. parallel lines)
            {
                if(r1 == r2)  // same ray?
                    return true;
                else
                    return false;  // parallel, non-intersecting
            }
            else  // unique solution
            {
                if(t1 >= 0 && t2 >= 0)
                    return true;
                else
                    return false;  // they would intersect if they are lines, but they are not lines
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-05-19
          • 1970-01-01
          • 2011-10-30
          • 1970-01-01
          • 1970-01-01
          • 2011-04-06
          • 1970-01-01
          相关资源
          最近更新 更多