在3D空间中找到3D点和定向椭圆之间的距离(C ++)

时间:2016-04-01 11:04:07

标签: c++ math 3d unreal-engine4 orbital-mechanics

为了给出这个问题的一些背景知识,我创造了一个需要知道“Orbit”的游戏。一个物体在另一个轨道的容忍范围内。为了说明这一点,我使用目标轨道绘制了一个具有给定半径(公差)的圆环形状,现在我需要检查椭圆是否在该圆环内。

我迷失在数学/堆栈交换的方程中,所以要求更具体的解决方案。为了澄清,这里是Torus和Orbit(红线)游戏的图像。很简单,我想检查那个红色轨道是否在Torus形状内。

enter image description here

我认为我需要做的是在其中一个轨道上绘制世界空间中的四个点(很容易做到)。然后,我需要计算该点与其他轨道之间的最短距离。椭圆。这是困难的部分。有几个例子可以找到一个点到椭圆的最短距离,但都是2D并且很难遵循。

如果该距离小于所有四个点的容差,则认为等于轨道位于目标环面内。

为简单起见,所有这些轨道的原点始终位于世界Origin(0,0,0) - 我的坐标系为Z-Up。每个轨道都有一系列定义它的参数(轨道元素)。

2 个答案:

答案 0 :(得分:1)

这里简单的方法:

  1. 将每个轨道样本设置为N点。

    让第一个轨道上的点为A和第二个轨道B

    const int N=36;
    float A[N][3],B[N][3];
    
  2. 找到2个最近点

    所以d=|A[i]-B[i]|是最小的。如果d小于或等于您的保证金/门槛,则轨道彼此距离太近。

  3. 速度与准确度

    除非您使用某种高级方法#2 ,否则其计算将为O(N^2),这有点可怕。 N越大,结果的准确性越高,但计算时间也越来越多。有两种方法可以解决这两个问题。例如:

    1. N的第一个样本

    2. 当发现最近的点再次对两个轨道进行采样时

      但只接近相关点(较高N)。

      enter image description here

    3. 您可以通过循环#2来递归提高准确度,直到达到所需的精度

    4. 测试d如果省略号太靠近

答案 1 :(得分:0)

我想我可能有一个新的解决方案。

  1. 绘制当前轨道上的四个点(椭圆)。
  2. 将这些点投射到目标轨道(圆环面)的平面上。
  3. 使用目标轨道倾角作为平面的法线,计算每个(标准化)点与周围参数之间的角度 在目标轨道上。
  4. 使用此角度作为平均异常,并计算等效的偏心异常。
  5. 使用那些偏心异常来绘制目标轨道上的四个点 - 这应该是距离另一个轨道最近的点。
  6. 检查这些点之间的距离。
  7. 这里的困难来自于计算角度并将其转换为另一个轨道上的异常。这应该比递归函数更准确和更快。我试过这个时会更新。

    编辑:

    是的,这有效!

        // The Four Locations we will use for the checks
    TArray<FVector> CurrentOrbit_CheckPositions;
    TArray<FVector> TargetOrbit_ProjectedPositions;
    CurrentOrbit_CheckPositions.SetNum(4);
    TargetOrbit_ProjectedPositions.SetNum(4);
    
    // We first work out the plane of the target orbit.
    const FVector Target_LANVector = FVector::ForwardVector.RotateAngleAxis(TargetOrbit.LongitudeAscendingNode, FVector::UpVector); // Vector pointing to Longitude of Ascending Node
    const FVector Target_INCVector = FVector::UpVector.RotateAngleAxis(TargetOrbit.Inclination, Target_LANVector);                  // Vector pointing up the inclination axis (orbit normal)
    const FVector Target_AOPVector = Target_LANVector.RotateAngleAxis(TargetOrbit.ArgumentOfPeriapsis, Target_INCVector);           // Vector pointing towards the periapse (closest approach)
    
    // Geometric plane of the orbit, using the inclination vector as the normal.
    const FPlane ProjectionPlane = FPlane(Target_INCVector, 0.f);   // Plane of the orbit. We only need the 'normal', and the plane origin is the Earths core (periapse focal point)
    
    // Plot four points on the current orbit, using an equally-divided eccentric anomaly.
    const float ECCAngle = PI / 2.f;
    for (int32 i = 0; i < 4; i++)
    {
        // Plot the point, then project it onto the plane
        CurrentOrbit_CheckPositions[i] = PosFromEccAnomaly(i * ECCAngle, CurrentOrbit);
        CurrentOrbit_CheckPositions[i] = FVector::PointPlaneProject(CurrentOrbit_CheckPositions[i], ProjectionPlane);
    
        // TODO: Distance from the plane is the 'Depth'. If the Depth is > Acceptance Radius, we are outside the torus and can early-out here
    
        // Normalize the point to find it's direction in world-space (origin in our case is always 0,0,0)
        const FVector PositionDirectionWS = CurrentOrbit_CheckPositions[i].GetSafeNormal();
    
        // Using the Inclination as the comparison plane - find the angle between the direction of this vector, and the Argument of Periapse vector of the Target orbit
        // TODO: we can probably compute this angle once, using the Periapse vectors from each orbit, and just multiply it by the Index 'I'
        float Angle = FMath::Acos(FVector::DotProduct(PositionDirectionWS, Target_AOPVector));
    
        // Compute the 'Sign' of the Angle (-180.f - 180.f), using the Cross Product
        const FVector Cross = FVector::CrossProduct(PositionDirectionWS, Target_AOPVector);
        if (FVector::DotProduct(Cross, Target_INCVector) > 0)
        {
            Angle = -Angle;
        }
    
        // Using the angle directly will give us the position at th eccentric anomaly. We want to take advantage of the Mean Anomaly, and use it as the ecc anomaly
        // We can use this to plot a point on the target orbit, as if it was the eccentric anomaly.
        Angle = Angle - TargetOrbit.Eccentricity * FMathD::Sin(Angle);
        TargetOrbit_ProjectedPositions[i] = PosFromEccAnomaly(Angle, TargetOrbit);}
    

    我希望评论描述这是如何运作的。经过几个月的搔痒,终于解决了。谢谢大家!