两个平面之间的交线

发布于 2024-11-16 08:51:10 字数 83 浏览 2 评论 0原文

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

我知道数学思想,并且我在平面法向量之间进行了叉积,

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

How can I find the line of intersection between two planes?

I know the mathematics idea, and I did the cross product between the the planes normal vectors

but how to get the line from the resulted vector programmatically

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

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

发布评论

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

评论(7

另类 2024-11-23 08:51:10

平面的方程为 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)

The equation of the plane is ax + by + cz + d = 0, where (a,b,c) is the plane's normal, and d is the distance to the origin. This means that every point (x,y,z) that satisfies that equation is a member of the plane.

Given two planes:

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

The intersection between the two is the set of points that verifies both equations. To find points along this line, you can simply pick a value for x, any value, and then solve the equations for y and z.

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

If you make x=0, this gets simpler:

y = (-c1z -d1) / b1
z = ((b2/b1)*d1 -d2)/(c2 - c1*b2/b1)
娇纵 2024-11-23 08:51:10

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

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

虽然这对于 2 个平面(其中第三个平面可以使用前两个平面的叉积来计算)效果很好,但对于 2 个平面版本可以进一步减少问题。

  • 无需使用 3x3 矩阵行列式,
    我们可以使用第一平面和第二平面之间叉积的平方长度(即第三平面的方向)
  • 无需包含第三个平面的距离,
    (计算最终位置)
  • 无需取消距离。
    通过交换叉积顺序来节省一些 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;
    }
}

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

尽管此处有其他答案 已经涵盖了原则

Finding the line between two planes can be calculated using a simplified version of the 3-plane intersection algorithm.

The 2'nd, "more robust method" from bobobobo's answer references the 3-plane intersection.

While this works well for 2 planes (where the 3rd plane can be calculated using the cross product of the first two), the problem can be further reduced for the 2-plane version.

  • No need to use a 3x3 matrix determinant,
    instead we can use the squared length of the cross product between the first and second plane (which is the direction of the 3'rd plane).
  • No need to include the 3rd planes distance,
    (calculating the final location).
  • No need to negate the distances.
    Save some cpu-cycles by swapping the cross product order instead.

Including this code-example, since it may not be immediately obvious.

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

Adding this answer for completeness, since at time of writing, none of the answers here contain a working code-example which directly addresses the question.

Though other answers here already covered the principles.

孤蝉 2024-11-23 08:51:10

查找直线上的点

要获得两个平面的交点,您需要直线上的点和该直线的方向。

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

lineDir = n1 × n2

但该线穿过原点,而沿着平面交点延伸的线可能不会。因此,Martinho的答案为寻找交线上的点提供了一个很好的开始(基本上任何 两个平面上的点)。

如果您想查看如何解决此问题的推导,请参阅其背后的数学原理:

首先让 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

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

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

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

用户 的回答来看,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 是方向向量,即两个原始平面法线的叉积。

红色和蓝色平面之间的交线看起来像这样

在此处输入图像描述

效率和稳定性

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

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

Finding a point on the line

To get the intersection of 2 planes, you need a point on the line and the direction of that line.

Finding the direction of that line is really easy, just cross the 2 normals of the 2 planes that are intersecting.

lineDir = n1 × n2

But that line passes through the origin, and the line that runs along your plane intersections might not. So, Martinho's answer provides a great start to finding a point on the line of intersection (basically any point that is on both planes).

In case you wanted to see the derivation for how to solve this, here's the math behind it:

First let x=0. Now we have 2 unknowns in 2 equations instead of 3 unknowns in 2 equations (we arbitrarily chose one of the unknowns).

Then the plane equations are (A terms were eliminated since we chose x=0):

B1y + C1z + D1 = 0

B2y + C2z + D2 = 0

We want y and z such that those equations are both solved correctly (=0) for the B1, C1 given.

So, just multiply the top eq by (-B2/B1) to get

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

B2y + C2z + D2 = 0

Add the eqs to get

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

Throw the z you find into the 1st equation now to find y as

y = (-D1 - C1z) / B1

Note the best variable to make 0 is the one with the lowest coefficients, because it carries no information anyway. So if C1 and C2 were both 0, choosing z=0 (instead of x=0) would be a better choice.

The above solution can still screw up if B1=0 (which isn't that unlikely). You could add in some if statements that check if B1=0, and if it is, be sure to solve for one of the other variables instead.

Solution using intersection of 3 planes

From user's answer, a closed form solution for the intersection of 3 planes was actually in Graphics Gems 1. The formula is:

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

Actually point_on1 • n1 = -d1 (assuming you write your planes Ax + By + Cz + D=0, and not =-D). So, you could rewrite it as:

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

A function that intersects 3 planes:

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

Proof it works (yellow dot is intersection of rgb planes here)

enter image description here

Getting the line

Once you have a point of intersection common to the 2 planes, the line just goes

P + t*d

Where P is the point of intersection, t can go from (-inf, inf), and d is the direction vector that is the cross product of the normals of the two original planes.

The line of intersection between the red and blue planes looks like this

enter image description here

Efficiency and stability

The "robust" (2nd way) takes 48 elementary ops by my count, vs the 36 elementary ops that the 1st way (isolation of x,y) uses. There is a trade off between stability and # computations between these 2 ways.

It'd be pretty catastrophic to get (0,inf,inf) back from a call to the 1st way in the case that B1 was 0 and you didn't check. So adding in if statements and making sure not to divide by 0 to the 1st way may give you the stability at the cost of code bloat, and the added branching (which might be quite expensive). The 3 plane intersection method is almost branchless and won't give you infinities.

愿得七秒忆 2024-11-23 08:51:10

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

如果这些是平面:

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

This method avoids division by zero as long as the two planes are not parallel.

If these are the planes:

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

1) Find a vector parallel to the line of intersection. This is also the normal of a 3rd plane which is perpendicular to the other two planes:

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

2) Form a system of 3 equations. These describe 3 planes which intersect at a point:

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

3) Solve them to find x1,y1,z1. This is a point on the line of intersection.

4) The parametric equations of the line of intersection are:

x = x1 + A3 * t
y = y1 + B3 * t
z = z1 + C3 * t
ι不睡觉的鱼゛ 2024-11-23 08:51:10

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

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

这个想法是首先从原点到第一个平面 (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) 应该可以很好地工作。

The determinant-based approach is neat, but it's hard to follow why it works.

Here's another way that's more intuitive.

The idea is to first go from the origin to the closest point on the first plane (p1), and then from there go to the closest point on the line of intersection of the two planes. (Along a vector that I'm calling v below.)

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)

This should give the same point as the determinant-based approach. There's almost certainly a link between the two. At least the denominator, n2 • v, is the same, if we apply the "scalar triple product" rule. So these methods are probably similar as far as condition numbers go.

Don't forget to check for (almost) parallel planes. For example: if (dir • dir < 1e-8) should work well if unit normals are used.

太傻旳人生 2024-11-23 08:51:10

您可以在此链接中找到两个平面相交线的公式。

P1: a1x + b1y + c1z  = d1
P2: a2x + b2y + c2z  = d2
n1=(a1,b1,c1); n2=(a2,b2,c2); n12=Norm[Cross[n1,n2]]^2
If n12 != 0
 a1 = (d1*Norm[n2]^2 - d2*n1.n2)/n12;  
 a2 = (d2*Norm[n1]^2 - d1*n1.n2)/n12;
 P = a1 n1 + a2 n2;
 (*formula for the intersection line*)
 Li[t_] := P + t*Cross[n1, n2]; 

You can find the formula for the intersection line of two planes in this link.

P1: a1x + b1y + c1z  = d1
P2: a2x + b2y + c2z  = d2
n1=(a1,b1,c1); n2=(a2,b2,c2); n12=Norm[Cross[n1,n2]]^2
If n12 != 0
 a1 = (d1*Norm[n2]^2 - d2*n1.n2)/n12;  
 a2 = (d2*Norm[n1]^2 - d1*n1.n2)/n12;
 P = a1 n1 + a2 n2;
 (*formula for the intersection line*)
 Li[t_] := P + t*Cross[n1, n2]; 
九厘米的零° 2024-11-23 08:51:10

线的叉积是相交线的方向。现在你需要在交叉点上有一个点。

您可以通过在叉积上取一点,然后减去平面 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

The cross product of the line is the direction of the intersection line. Now you need a point in the intersection.

You can do this by taking a point on the cross product, then subtracting Normal of plane A * distance to plane A and Normal of plane B * distance to plane b. Cleaner:

p = Point on cross product

intersection point = ([p] - ([Normal of plane A] * [distance from p to plane A]) - ([Normal of plane B] * [distance from p to plane B]))

Edit:

You have two planes with two normals:

N1 and N2

The cross product is the direction of the Intersection Line:

C = N1 x N2

The class above has a function to calculate the distance between a point and a plane. Use it to get the distance of some point p on C to both planes:

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

Intersection line:

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