【问题标题】:Line of intersection between two planes两个平面之间的交线
【发布时间】:2011-09-18 12:23:45
【问题描述】:

如何找到两个平面之间的交线?

我知道数学思想,我做了平面法向量之间的叉积

但是如何以编程方式从结果向量中获取线

【问题讨论】:

标签: math language-agnostic plane


【解决方案1】:

线的叉积是相交线的方向。现在您需要在交叉点上找到一个点。

您可以通过在叉积上取一个点,然后减去平面 A 的法线 * 到平面 A 的距离和平面 B 的法线 * 到平面 b 的距离来做到这一点。清洁工:

p = 叉积上的点

交点 = ([p] - ([平面 A 的法线] * [p 到平面 A 的距离]) - ([平面 B 的法线] * [p 到平面 B 的距离]))

编辑:

你有两个平面和两个法线:

N1 and N2

叉积是相交线的方向:

C = N1 x N2

上面的类有一个计算点到平面距离的函数。用它来获取 C 上某个点 p 到两个平面的距离:

p = C //p = 1 times C to get a point on C
d1 = plane1.getDistance(p)
d2 = plane2.getDistance(p)

相交线:

resultPoint1 = (p - (d1 * N1) - (d2 * N2))
resultPoint2 = resultPoint1 + C

【讨论】:

  • 我会告诉你我的理解 1. 得到叉积 2. 得到这个叉积中的点,然后得到交点 [p] 表示幅度是这样的,我有一个问题我画线需要两点,第二点怎么取
  • 交点是一个点。叉积是线的方向,所以第二个点就是交点+x*叉积,例如交点+1*叉积
  • 请问什么 p = C //p = 1 次 C 在 C 上得到一个点,好的,我使用 n1 和 n2 的叉积得到 C,为什么我使用 p = c
  • 我创建了一个平面结构,通过法线和与点的距离定义平面,以及定义点的 vector3 结构,叉积是矢量 x,y,z 。所以 c 将是 x,y 和 z 的向量,如何继续
  • 我试过你的算法,效果很好,但是x值不正确
【解决方案2】:

平面的方程是ax + by + cz + d = 0,其中(a,b,c)是平面的法线,d是到原点的距离。这意味着满足该方程的每个点 (x,y,z) 都是平面的成员。

给定两个平面:

P1: a1x + b1y + c1z + d1 = 0
P2: a2x + b2y + c2z + d2 = 0

两者之间的交集是验证两个方程的点集。要沿着这条线找到点,您可以简单地为 x 选择一个值,任何值,然后求解 y 和 z 的方程。

y = (-c1z -a1x -d1) / b1
z = ((b2/b1)*(a1x+d1) -a2x -d2)/(c2 - c1*b2/b1)

如果你创建x=0,这会变得更简单:

y = (-c1z -d1) / b1
z = ((b2/b1)*d1 -d2)/(c2 - c1*b2/b1)

【讨论】:

  • 如何获取行的开始和结束,以及第二个点
  • x=0 可能并不总是有效。例如。如果生成的线平行于 y-z 平面。
  • b1、b2、c1、c2的值代表什么?
  • 这种方法需要做更多的工作才能变得健壮。但并不多!只需重复上述步骤,从y 开始,然后从z 开始。然后选择具有最低范数的解决方案。 (注意b1 为零的情况等)这使得它很健壮,因为任何线(我们平面的交点)必须与 x=0、y=0 或 z=0 平面中的至少一个相交一个不是无穷大的点——它可能出现在条件不佳的情况下(这种情况并不罕见,但一直发生,带有轴对齐的平面和舍入误差)。
【解决方案3】:

只要两个平面不平行,这种方法就可以避免除以零。

如果这些是飞机:

A1*x + B1*y + C1*z + D1 = 0
A2*x + B2*y + C2*z + D2 = 0

1) 找到一个平行于交线的向量。这也是垂直于其他两个平面的第三个平面的法线:

(A3,B3,C3) = (A1,B1,C1) cross (A2,B2,C2)

2) 形成一个由 3 个方程组成的系统。这些描述了在一点相交的 3 个平面:

A1*x1 + B1*y1 + C1*z1 + D1 = 0
A2*x1 + B2*y1 + C2*z1 + D2 = 0
A3*x1 + B3*y1 + C3*z1 = 0

3) 求解它们以找到 x1,y1,z1。这是相交线上的一个点。

4) 相交线的参数方程为:

x = x1 + A3 * t
y = y1 + B3 * t
z = z1 + C3 * t

【讨论】:

  • 好答案。 3 个平面相交的封闭形式解决方案实际上在 Graphics Gems 1, pg 305 中。
【解决方案4】:

在线上找一点

要获得 2 个平面的交点,您需要在线上的一个点以及该线的方向。

找到那条线的方向真的很容易,只需穿过相交的 2 个平面的 2 个法线即可。

lineDir = n1 × n2

但是那条线穿过原点,沿着你的平面交叉点延伸的线可能不会。因此,Martinho's 的回答为在相交线上找到一个点(基本上是两个平面上的任何点)提供了一个很好的开始。

如果您想了解如何解决这个问题的推导,这里是它背后的数学:

首先让 x=0。现在我们在 2 个方程中有 2 个未知数,而不是在 2 个方程中有 3 个未知数(我们任意选择了一个未知数)。

那么平面方程是(由于我们选择 x=0,所以消除了 A 项):

B1y + C1z + D1 = 0

B2y + C2z + D2 = 0

我们希望 y 和 z 使得对于给定的 B1、C1,这些方程都能正确求解 (=0)。

所以,只需将顶部 eq 乘以 (-B2/B1) 即可得到

-B2y + (-B2/B1)*C1z + ( -B2/B1)*D1 = 0

B2y + C2z + D2 = 0

添加等式得到

z = ( (-B2/B1)*D1 - D2 ) / (C2 * B2/B1)*C1)

现在将您找到的 z 放入第一个等式中以找到 y

y = (-D1 - C1z) / B1

注意 best 变量使 0 是具有 最低 系数的变量,因为它无论如何都不携带任何信息。因此,如果 C1 和 C2 都为 0,则选择 z=0(而不是 x=0)会是更好的选择。

如果 B1=0(这不太可能),上述解决方案仍然会出错。您可以添加一些 if 语句来检查 B1=0,如果是,请务必改为求解其他变量之一。

使用 3 个平面相交的解决方案

来自user's的回答,3个平面相交的封闭形式解决方案实际上在Graphics Gems 1中。公式为:

P_intersection = (( point_on1 • n1 )( n2 × n3 ) + ( point_on2 • n2 )( n3 × n1 ) + ( point_on3 • n3 )( n1 × n2 )) / det(n1,n2,n3)

实际上point_on1 • n1 = -d1(假设你写你的平面Ax + By + Cz + D=0,而不是=-D)。因此,您可以将其重写为:

P_intersection = (( -d1 )( n2 × n3 ) + ( -d2 )( n3 × n1 ) + ( -d3 )( n1 × n2 )) / det(n1,n2,n3)

一个与 3 个平面相交的函数:

// Intersection of 3 planes, Graphics Gems 1 pg 305
static Vector3f getIntersection( const Plane& plane1, const Plane& plane2, const Plane& plane3 )
{
  float det = Matrix3f::det( plane1.normal, plane2.normal, plane3.normal ) ;
    
  // If the determinant is 0, that means parallel planes, no intn.
  if( det == 0.f ) return 0 ; //could return inf or whatever
  
  return ( plane2.normal.cross( plane3.normal )*-plane1.d +
           plane3.normal.cross( plane1.normal )*-plane2.d + 
           plane1.normal.cross( plane2.normal )*-plane3.d ) / det ;            
}

证明它有效(黄点是 rgb 平面的交点)

获取线路

一旦你有了两个平面的共同交点,这条线就可以了

P + t*d

其中P是交点,t可以从(-inf, inf)出发,d是方向向量,是两个原始平面法线的叉积。

红色和蓝色平面的交线如下所示

高效稳定

据我统计,“稳健”(第二种方式)需要 48 个基本操作,而第一种方式(x,y 的隔离)使用 36 个基本操作。这两种方式之间需要在稳定性和 # 计算之间进行权衡。

在 B1 为 0 并且您没有检查的情况下,从第一种方式的调用中返回 (0,inf,inf) 将是非常灾难性的。因此,添加if 语句并确保第一种方式不除以0 可能会以代码膨胀和添加的分支(这可能非常昂贵)为代价来为您提供稳定性。 3 平面相交法几乎是无分支的,不会给你无穷大。

【讨论】:

  • 注意:公式z = ( (-B2/B1)*D1 - D2 ) / (C2 * B2/B1)*C1) 不正确 - 它应该是z = ((B2/B1)*D1 - D2)/(C2 - B2/B1*C1)
【解决方案5】:

为了完整起见,添加此答案,因为在撰写本文时,此处的答案均不包含直接解决该问题的工作代码示例。

虽然这里有其他答案already covered the principles


可以使用 3 平面相交算法的简化版本来计算两个平面之间的线。

bobobobo's 答案中的第二个“更稳健的方法”引用了 3 平面相交。

虽然这适用于 2 个平面 (其中第 3 个平面可以使用前两个平面的叉积计算),但对于 2 个平面版本,该问题可以进一步减少。 p>

  • 不需要使用 3x3 矩阵行列式,
    我们可以使用第一个和第二个平面之间的叉积的平方长度(这是第 3 个平面的方向).
  • 无需包含第 3 个平面的距离,
    (计算最终位置)
  • 无需否定距离。
    通过交换叉积顺序来节省一些 CPU 周期。

包括这个代码示例,因为它可能不是很明显。

// Intersection of 2-planes: a variation based on the 3-plane version.
// see: Graphics Gems 1 pg 305
//
// Note that the 'normal' components of the planes need not be unit length
bool isect_plane_plane_to_normal_ray(
        const Plane& p1, const Plane& p2,
        // output args
        Vector3f& r_point, Vector3f& r_normal)
{
    // logically the 3rd plane, but we only use the normal component.
    const Vector3f p3_normal = p1.normal.cross(p2.normal);
    const float det = p3_normal.length_squared();

    // If the determinant is 0, that means parallel planes, no intersection.
    // note: you may want to check against an epsilon value here.
    if (det != 0.0) {
        // calculate the final (point, normal)
        r_point = ((p3_normal.cross(p2.normal) * p1.d) +
                   (p1.normal.cross(p3_normal) * p2.d)) / det;
        r_normal = p3_normal;
        return true;
    }
    else {
        return false;
    }
}

【讨论】:

  • 当然!以这种方式形成的矩阵中的第三个向量与其他两个向量正交,因此行列式是棱镜的面积,其底面是由两个平面的法线形成的平行四边形。该平行四边形的面积是叉积的大小,而该棱柱的高度也是叉积的大小。因此,体积是叉积的平方。但是,if (det != 0) 不是检查平行平面的方法,因为由于舍入错误,它很可能类似于 1.1212e-12。
  • @EvgeniSergeev,对 - 在实践中使用这些类型的计算,您需要与 epsilon 进行比较,在答案中注明。
  • 你为什么要做叉积来计算 r_point ?由于我们定义了垂直于平面 1 和 2 的第三个平面不是 p3.normal.cross(p2.normal) = p1.normalp1.normal.cross(p3_normal) = p2.normal 吗?
  • 也从bobobobo's answer 得到交叉点的公式否定d 像这样:plane2.normal.cross( plane3.normal )* - plane1.d 第二个问题,也许是相关的,你怎么能从公式中删除plane1.normal.cross( plane2.normal )*-plane3.d?我知道我们没有p3.d,所以这将无法计算,但删除它应该会使公式无效。
  • re: p3.normal.cross(p2.normal) = p1.normalp1.normal.cross(p3_normal) = p2.normal,方向将匹配,但向量长度不匹配。回复:plane2.normal.cross( plane3.normal )* - plane1.d,这与我所拥有的相同,只是交换了叉积并添加了否定。 (但结果相同)。不知道你最后一点的要求是什么。 - 但这很容易检查 - 随时链接到一些 sn-p。
【解决方案6】:

基于行列式的方法很简洁,但很难理解它为什么有效。

这是另一种更直观的方式。

这个想法是首先从原点到第一个平面上的最近点(p1),然后从那里到两个平面相交线上的最近点。 (沿着我在下面称为v 的向量。)

Given
=====
 First plane: n1 • r = k1
Second plane: n2 • r = k2

Working
=======
dir = n1 × n2
p1 = (k1 / (n1 • n1)) * n1
v = n1 × dir
pt = LineIntersectPlane(line = (p1, v), plane = (n2, k2))

LineIntersectPlane
==================
#We have n2 • (p1 + lambda * v) = k2
lambda = (k2 - n2 • p1) / (n2 • v)
Return p1 + lambda * v

Output
======
Line where two planes intersect: (pt, dir)

这应该与基于行列式的方法给出相同的观点。几乎可以肯定两者之间存在联系。如果我们应用“标量三重积”规则,至少分母n2 • v 是相同的。因此,就条件数而言,这些方法可能是相似的。

不要忘记检查(几乎)平行平面。例如:如果使用单位法线,if (dir • dir < 1e-8) 应该可以正常工作。

【讨论】:

    猜你喜欢
    • 2017-12-20
    • 1970-01-01
    • 2016-04-20
    • 1970-01-01
    • 1970-01-01
    • 2013-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多