确定两条光线是否相交

时间:2010-05-28 18:30:55

标签: algorithm math geometry intersection

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

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

9 个答案:

答案 0 :(得分:27)

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

如果存在交点p:

,则两条线相交
p = as + ad * u
p = bs + bd * v

如果这个方程系统有一个u> = 0且v> = 0的解(正方向就是它们的光线),则光线相交。

对于2d向量的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

插入并解决你的问题:

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,如果两者都是正数,则光线相交,否则不计算。

答案 1 :(得分:25)

我很遗憾不同意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< 0或(sign(num)!= sign(denom))代替,取决于目标计算机上哪些更有效。

请注意,极少数情况下det == 0表示光线不相交(另外一个比较)。

答案 2 :(得分:3)

光线可以用点集A + Vt表示,其中A是起点,V是指示光线方向的矢量,{{1}是参数。因此,要确定两条光线是否相交,请执行以下操作:

t >= 0

答案 3 :(得分:1)

GeomAlgorithms.com有一些很好的算法可以处理3D中的线条...但一般来说,两条线在3D空间中交叉的概率确实非常低。

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

答案 4 :(得分:1)

行由 p 点和向量 v 表示:

  

line = p + a * v (适用于所有人)

光线是该行的(正面)一半:

  

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

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

  

交叉点发生在 p 1 + a 1 * v 1 = p 2 + a 2 * v 2
  (注意有两个未知数,一个 1 和一个 2 ,以及两个方程式,因为 p v < / strong>是多维的)

求解 1 2 - 如果它们都是非负的,则它们相交。如果一个是负数,则它们不相交。

答案 5 :(得分: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

答案 6 :(得分:0)

我只想检查两条光线是否相交。我将通过计算从两条射线产生的两个“三角形”的旋转方向来解决这个问题。它们实际上不是三角形,但从数学的角度来看,如果我只想计算三角形的旋转,我只需要两个具有共同起点的矢量,其余的并不重要。

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

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

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

这是一些psudo代码:

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

它可以进行五次乘法,六次减法和一次比较。

答案 7 :(得分:0)

C ++ for Guntners解决方案

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;
}

答案 8 :(得分:-1)

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