如何检测两个片段(在3d空间中)是否相交?

时间:2019-03-18 11:29:56

标签: python 3d intersect segment

仅检查,无需查找要点。  坐标z不为0。 堆栈溢出中的旧问题都是二维的。 预先感谢

2 个答案:

答案 0 :(得分:1)

有几种方法可以进行线-线相交测试。经典方式是使用线性代数(即求解线性矩阵系统),但从软件开发的角度来看,我更喜欢采用Plucker坐标形式的Geometric-Algebra方式,该方式仅需要实现矢量代数运算(即,叉积和点积),比求解线性系统的矩阵运算更容易编码。

矢量代数解决方案

给出由点P1和P2限制的线段P和由点Q1和Q2限制的线段Q。

线的参数形式如下:

P(t)= P1 + t(P2-P1)

Q(t)= Q1 + t(Q2-Q1)

其中t是区间[0 1]中的实数。

如果两条线相交,则以下等式成立:

P(t0)= Q(t1)

提供两个未知数字t0和t1存在。扩展上面的等式,我们得到:

t0(P2-P1)-t1(Q2-Q1)= Q1-P1

为避免处理矩阵代数,我们可以尝试使用向量代数和替换方法求解系统:

t0(P2-P1)-t1(Q2-Q1)= Q1-P1

t0 = a + t1 b

t1 = C•(Q1-(1-a)P1-a P2)/ | C | ^ 2

位置:

a =(P2-P1)•(Q1-P1)/ | P2-P1 | ^ 2

b =(P2-P1)•(Q2-Q1)/ | P2-P1 | ^ 2

C = b(P2-P1)-(Q2-Q1)

其中(•)是点积。如果t0和t1都在间隔[0 1]中,则交点P(t0)(或Q(t1))存在。

Plucker坐标方式

给出由点P1和P2限制的线段P和由点Q1和Q2限制的线段Q。

P线的Plucker坐标由一对3d向量(Pd,Pm)给出:

Pd = P2-P1

Pm = P1×P2

其中Pm是P1和P2的叉积。可以以完全相同的方式计算Q行的Plucker坐标(Qd,Qm)。

P和Q线只有在共面的情况下才能相交。 P和Q线是共面iif:

Pd•Qm + Qd•Pm = 0

其中(•)是点积。由于机器具有有限的精度,因此可靠的测试不应检查零,而是检查一小部分。如果Pd×Qd = 0,则线是平行的(此处0是零向量)。再次应进行可靠的测试,以确保(Pd×Qd)的平方长度小。

如果线不平行,则它们是共面的,并且它们的交点(在Plucker的行话中称为“相遇”)将成为重点:

x =((Pm•N)Qd-(Qm•N)Pd-(Pm•Qd)N)/(Pd×Qd)•N

其中N是任何坐标轴矢量(即(1,0,0)或(0,1,0)或(0,0,1)),使得(Pd×Qd)•N不为零。

如果P和Q都不通过原点,则它们的Plucker坐标Pm和Qm将分别为非零,并且下面的sinpler公式将起作用

x = Pm×Qm / Pd•Qm

有关Plucker坐标的介绍,请参见:

https://en.m.wikipedia.org/wiki/Plücker_coordinates

http://www.realtimerendering.com/resources/RTNews/html/rtnv11n1.html#art3

有关一般交集公式,请参阅:“推论6”:

http://web.cs.iastate.edu/~cs577/handouts/plucker-coordinates.pdf

线性代数方式

给出由点P1和P2限制的线段P和由点Q1和Q2限制的线段Q。

线的参数形式如下:

P(t)= P1 + t(P2-P1)

Q(t)= Q1 + t(Q2-Q1)

其中t是区间[0 1]中的实数。

如果两条线相交,则以下等式成立:

P(t0)= Q(t1)

提供两个未知数字t0和t1存在。扩展上面的等式,我们得到:

t0(P2-P1)-t1(Q2-Q1)= Q1-P1

我们可以通过在矩阵代数中表达上述方程来求解t0和t1:

A x = B

其中,A是一个3x2矩阵,第一列的向量(P2-P1)坐标,第二列的向量(Q2-Q1)坐标; x是未知数t0和t1的2x1列向量,B是坐标为(Q1-P1)的3x1列向量。

通常,可以通过计算矩阵A的伪逆来解决该系统,用A ^ +表示:

A ^ + =(A ^ T A)^-1 A ^ T

请参阅: https://en.m.wikipedia.org/wiki/Generalized_inverse

幸运的是,Python中的任何矩阵包都应该能够非常轻松地并且也许也非常高效地计算上述计算。

如果将A乘以其伪逆A ^ +等于单位矩阵I,即(AA ^ +)== I,则存在唯一的答案(交集),您可以通过以下计算得到它产品:

x = A ^ + B

当然,如果您不能首先计算伪逆,例如因为(A ^ T A)是奇数(即行列式为零),则不存在交集。

因为我们正在处理线段,所以交点在点P(x0)或Q(x1)处,如果x0和x1都在[0 1]区间内。

答案 1 :(得分:0)

我可以告诉您可以使用的数学(尽管有些复杂)。 您以参数形式为每行写方程式。然后找到每行的参数。 请参见下面的示例:

(a1,a2,a3)________________(b1,b2,b3)
A                           B 


and second line 
(c1,c2,c3)________________(d1,d2,d3)
C                            D

所以等式为 第一行的A + t(B-A)(实际上代表线段上t介于0和1之间的点) 和 第二行C + s(D-C) 其中t和s是参数 谁的值应在0到1之间,以使点位于线段上。 (0是A,1是B) 这里A表示(a1,a2,a3) 因此,您可以将两个等式等同于交点(如果有的话),并找到满足三个等式的t和s:

  a1+t(b1-a1)=c1+s(d1-c1)
  a2+t(b2-a2)=c2+s(d2-c2)
  a3+t(b3-a3)=c3+s(d3-c3)

t和s应该在0到1之间,以使该点真正位于线段上。所以我希望你明白了:) 代码:

#input a1,a2,a3 and all the other components here. 

#define all constants required for solving t and s
A=b1-a1
B=c1-d1
C=c1-a1
D=b2-a2
E=c2-d2
F=c2-a2

#find t and s using formula
t=(C*E-F*B)/(E*A-B*D)
s=(D*C-A*F)/(D*B-A*E)

#check if third equation is also satisfied(we have 3 equations and 2 variable
if ((t*(b3-a3)+s*(c3-d3))==c3-a3):
    if(0<=t<=1 and 0<=s<=1):
       print('line segments intersect')
    else:
       print ('line segments intersect on being produced')

这是代码段。希望您终于明白了:)