如何找到两个平面之间的交线?
我知道数学思想,并且我在平面法向量之间做了交叉乘积
但如何以编程方式从结果矢量中获取该行
答案 0 :(得分:17)
平面的方程是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)
答案 1 :(得分:12)
要获得2个平面的交点,您需要一条线和该线的方向。
找到该线的方向非常简单,只需穿过相交的2个平面的2个法线。
lineDir = n1 × n2
但是该线穿过原点,沿着平面交叉点的线可能不会。因此,Martinho's回答为在交叉线上找到一个点提供了一个很好的开始(基本上任意点在两个平面上)。
如果你想看看如何解决这个问题的推导,这里有数学背后的数学:
首先让x = 0。现在我们在2个方程中有2个未知数而不是2个方程中的3个未知数(我们任意选择了一个未知数)。
然后平面方程是(由于我们选择x = 0,因此消除了一个项):
B 1 y + C 1 z + D 1 = 0
B 2 y + C 2 z + D 2 = 0
我们想要y和z,使得这些方程式对于B 1 ,C 1 给出正确解决(= 0)。
所以,只需将顶部eq乘以(-B 2 / B 1 )即可获得
-B 2 y +( - B 2 / B 1 )* C 1 z +( -B 2 / B 1 )* D 1 = 0
B 2 y + C 2 z + D 2 = 0
添加eqs以获取
z =(( - B 2 / B 1 )* D 1 - D 2 )/ (C 2 * B 2 / B 1 )* C 1 )
现在将你找到的z扔到第一个等式中,找到y为
y =( - D 1 - C 1 z)/ B 1
注意 best 变量使0变为具有最低系数的变量,因为它无论如何都不携带任何信息。因此,如果C 1 且C 2 都为0,则选择z = 0(而不是x = 0)将是更好的选择。
如果B 1 = 0,上述解决方案仍然会搞砸(这不太可能)。您可以添加一些检查B 1 = 0的if语句,如果是,请确保替换其中一个变量。
从user's回答,3个平面交叉的封闭式解决方案实际上是在图形宝石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个基本操作。这两种方式之间在稳定性和#computing之间存在折衷。
在B 1 为0并且你没有检查的情况下,从第一路的调用中获取(0,inf,inf)是非常灾难性的。因此,添加if
语句并确保不将0除以第一种方式可能会以代码膨胀和增加的分支(可能非常昂贵)为代价来提供稳定性。 3平面交叉法几乎是无分支的,不会给你无穷大。
答案 2 :(得分:12)
为了完整性添加此答案,因为在撰写本文时,这里的答案都没有包含直接解决问题的有效代码示例。
虽然其他答案在already covered the principles。
可以使用3平面交叉算法的简化版本计算两个平面之间的线。
2'更强大的方法"从bobobobo's回答参考3平面交叉点。
虽然这适用于2个平面(其中第3个平面可以使用前两个平面的叉积计算),但对于2平面版本,问题可以进一步减少。
包括这个代码示例,因为它可能不会立即显而易见。
// 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;
}
}
答案 3 :(得分:6)
只要两个平面不平行,此方法就可以避免除零。
如果这些是飞机:
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
答案 4 :(得分:0)
基于行列式的方法很简洁,但很难理解它的工作原理。
这是另一种更直观的方式。
这个想法首先从原点到第一个平面上的最近点(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)
应该可以正常工作。
答案 5 :(得分:-2)
线的叉积是交线的方向。现在你需要在交叉点有一个点。
您可以通过在十字产品上取一个点,然后减去平面A的法线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