有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java:转换为3d空间的两个椭圆段的交点

我将直线和椭圆(而非平面和椭球)的段转换为三维空间,并需要计算两个给定段是否相交以及在哪里相交

我正在使用Java,但还没有找到一个库来解决我的问题,也没有找到一些可以用于我自己实现的算法


共 (1) 个答案

  1. # 1 楼答案

    对于直线相交测试,有几种方法可以解决。经典的方法是使用线性代数(即求解线性矩阵系统),但从软件开发的角度来看,我更喜欢几何代数的方法,以普莱克坐标的形式,它只需要实现向量代数运算(即叉积和点积),这比求解线性系统的矩阵运算更容易编码

    为了比较起见,我将两者都展示出来,然后你决定:

    线性代数方式

    给定线段p受点P1和P2限制,线段Q受点Q1和Q2限制

    线条的参数化形式如下所示:

    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

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

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

    x=A^+B

    当然,如果你首先不能计算伪逆,例如,因为(A^ta)是奇异的(即行列式为零),那么就不存在交集

    因为我们处理的是线段,所以当x0和x1都在区间[01]内时,交点在点p(x0)或Q(x1)处

    其他解决方法

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

    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)

    我还不能提供上述结果的几何直观

    采摘器协调方式

    给定线段p受点P1和P2限制,线段Q受点Q1和Q2限制

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

    Pd=P2-P1

    Pm=P1 x P2

    式中,Pm是P1和P2的叉积。可以用完全相同的方法计算线Q的采摘器坐标(Qd,Qm)

    线p和Q只有在共面时才能相交。第P线和第Q线为共平面iif:

    Pd•Qm+Qd•Pm=0

    其中(•)是点积。由于机器的精度有限,稳健测试不应检查零,而应检查少量。如果Pd x Qd=0,那么直线是平行的(这里0是零向量)。对于instamce,应该再次进行稳健测试,以确定(Pd x Qd)的平方长度很小

    如果两条线不平行,那么它们是共面的,它们的交点(用普莱克的术语称为“相交”)将是点:

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

    其中N是任何坐标轴向量(即,(1,0,0)或(0,1,0)或(0,0,1)),如at(Pd x Qd)•N为非零

    如果p和Q都不通过原点,那么它们的采摘器坐标Pm和Qm将分别为非零,并且以下sinpler公式将起作用

    x=Pm x Qm/Pd•Qm

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

    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

    将椭圆转换为前后旋转的圆

    我们总是可以把椭圆变成圆。椭圆有两个“半径”,称为半轴,你可以把它们想象成两个正交向量,一个大的称为大半轴,一个小的称为小半轴。可以对两个半轴向量应用非均匀缩放变换,使其大小相等,从而得到一个圆

    我们定义一个椭圆“E”,它的中心O是一个3d点,它的两个半轴A1和A2也是3d向量。椭圆平面的法向量N可以通过其半轴N=A1 x A2的叉积来计算,然后将其归一化为单位长度

    现在假设有一个线性函数M,你可以用它把椭圆E变换成一个圆C,半径等于短半轴,把它应用到椭圆的半轴A1和A2,以及椭圆的中心O

    请注意,Elipse的圆心O和椭圆的法向量N不会因M而改变。因此M(N)=N和M(O)=O。这意味着圆位于同一平面上,并且与椭圆具有相同的位置C。线性函数M有一个对应的反函数M^-1,所以我们可以将圆的向量变换回原始椭圆E

    当然,我们也可以使用函数M来变换直线p和Q的端点,将它们发送到“圆空间”,我们可以使用M^-1将它们发送回“椭圆空间”

    利用M,我们可以计算圆空间中的直线p和Q与椭圆E的交点。现在我们可以把重点放在直线和圆的交点上

    线平面相交

    给定一个具有法向量N和距离D的平面:

    N•x+D=0

    对于平面中的每个点x。然后,与线P的交点与Pucker坐标(Pd,Pm)由下式给出:

    x=(nx Pm-D Pd)/N•Pd

    这仅在线p不在平面内时有效,即:

    (N•P1+D)!=0和(N•P2+D)!=0

    对于椭圆E,我们有:

    N=(A1 x A2)/|A1 x A2 |

    D=-N•O

    直线圆和点圆交点

    现在有了x,圆内点检查很容易:

    |O-x |<;=|A2|

    只有当x在圆边界内时,等式才成立

    如果p线在圆的平面内,那么下面的单次检查将给出答案:

    https://stackoverflow.com/a/1079478/9147444

    如何计算线性函数M

    计算M的简单方法如下。使用椭圆的法向量N以及半轴A1和A2创建一个3x3矩阵U。这样,U将向量A1、A2和N作为列

    然后缩放长半轴A1,使其与短半轴A2具有相同的长度。然后创建矩阵V auch,该矩阵V将新向量A1、A2和N作为列

    然后我们定义线性矩阵系统:

    RU=V

    其中R是一个3x3(非均匀)标度旋转矩阵

    我们可以通过将方程的两边乘以U的倒数来求解R,U的倒数用U^-1表示

    ru^-1=vu^-1

    因为U^-1是我们得到的单位矩阵:

    R=vu^+

    利用R定义仿射变换

    M(x)=R(x-O)+O

    我们可以使用M将点转换为圆空间,例如O、P1、P2、Q1和Q2。但是如果我们需要变换向量,比如A1,A2,N,Pd和Qd。我们需要e简单的M:

    M(x)=rx

    因为A1、A2和N是R的特征向量,所以R不是奇异的,并且有一个逆。我们将逆M定义为:

    M^-1(x)=R^-1(x-O)+O

    对于向量:

    M^-1(x)=R^-1 x

    更新:椭圆-椭圆相交

    两个相交的非共面三维椭圆的交点位于其平面相交形成的线上。所以你首先找到包含椭圆的平面相交形成的线(如果平面不相交,也就是说,它们是平行的,那么两个椭圆都不相交)

    交线属于两个平面,因此它垂直于两条法线。方向向量V是平面法线的叉积:

    V=N1×N2

    为了完全确定直线,我们还需要在直线上找到一个点。我们可以通过解平面的线性方程来实现。给定2x3矩阵N=[N1^T N2^T],法线N1和N2为行,2x1列向量b=[N1•C1,N2•C2],其中C1和C2是椭圆的中心,我们可以形成线性矩阵系统:

    nx=b

    其中X是满足两个平面方程的点。由于满足系统的直线上有无限多个点,因此系统被低估。我们仍然可以通过使用矩阵N的伪逆(用N^+表示)找到更接近原点的特定解

    X=N^+b

    直线方程是

    L(t)=X+tV

    对于一些标量t

    然后可以应用前面描述的方法来测试线-椭圆相交,即将椭圆缩放为一个圆,并与共面线相交。如果两个椭圆在同一点与直线相交,则它们相交

    现在,椭圆实际上位于同一平面上的情况更加复杂。你可以在Eberly博士的优秀著作《几何工具》(Geometric Tools,也可在网上找到)中看到他的方法:

    https://www.geometrictools.com/Documentation/IntersectionOfEllipses.pdf

    ,也可以检查开源的C++源代码:

    https://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrEllipse2Ellipse2.h