查找 3D 点和 3D 中的定向椭圆之间的距离 space (C++)
Find the distance between a 3D point and an Orientated Ellipse in 3D space (C++)
为了给这个问题一些背景知识,我正在创建一个游戏,它需要知道一个物体的 'Orbit' 是否在另一个轨道的容差范围内。为了展示这一点,我使用目标轨道绘制了一个具有给定半径(公差)的圆环形状,现在我需要检查椭圆是否在该圆环内。
我在 Math/Stack 交换的方程式中迷路了,所以寻求更具体的解决方案。为了澄清起见,这是一张带有圆环和轨道(红线)的游戏图像。很简单,我想检查那个红色轨道是否在那个圆环形状内。
我认为我需要做的是在其中一个轨道上绘制 World-Space 中的四个点(很容易做到)。然后我需要计算该点与其他轨道椭圆之间的最短距离。这是困难的部分。有几个例子可以找到一个点到一个椭圆的最短距离,但都是二维的,很难理解。
如果该距离小于所有四个点的公差,则认为这等同于目标环面内的轨道。
为简单起见,所有这些轨道的原点始终位于世界原点 (0, 0, 0) - 我的坐标系是 Z-Up。每个轨道都有一系列定义它的参数(轨道元素)。
这里简单的做法:
对每个轨道进行采样以设置 N
个点。
设第一个轨道的点为 A
,第二个轨道的点为 B
。
const int N=36;
float A[N][3],B[N][3];
找到2个最近的点
所以 d=|A[i]-B[i]|
是最小的。如果 d
小于或等于你的 margin/treshold 那么轨道彼此太近了。
速度与准确度
除非你对 #2 使用一些高级方法,否则它的计算将是 O(N^2)
,这有点可怕。 N
越大,结果的准确性越高,但计算时间也越长。有两种方法可以补救。例如:
第一个小样本 N
当找到最近的点时再次采样两个轨道
但仅在相关点附近(更高 N
)。
您可以通过循环 #2 递归地提高准确性,直到您达到所需的精度
测试d
椭圆之间是否太接近
我想我可能有新的解决办法。
- 绘制当前轨道上的四个点(椭圆)。
- 将这些点投影到目标轨道(环面)的平面上。
- 使用目标轨道倾角作为平面的法线,计算每个(归一化)点与近点角的夹角
在目标轨道上。
- 以此角度作为平均距平,计算等效偏心距平
- 使用这些偏心异常绘制目标轨道上的四个点 - 这应该是离另一个轨道最近的点。
- 检查这些点之间的距离。
这里的难点在于计算角度并将其转换为另一个轨道上的异常。不过,这应该比递归函数更准确、更快。当我尝试这个时会更新。
编辑:
是的,这行得通!
// 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);}
我希望评论描述这是如何工作的。折腾了几个月终于解决了。谢谢大家!
为了给这个问题一些背景知识,我正在创建一个游戏,它需要知道一个物体的 'Orbit' 是否在另一个轨道的容差范围内。为了展示这一点,我使用目标轨道绘制了一个具有给定半径(公差)的圆环形状,现在我需要检查椭圆是否在该圆环内。
我在 Math/Stack 交换的方程式中迷路了,所以寻求更具体的解决方案。为了澄清起见,这是一张带有圆环和轨道(红线)的游戏图像。很简单,我想检查那个红色轨道是否在那个圆环形状内。
我认为我需要做的是在其中一个轨道上绘制 World-Space 中的四个点(很容易做到)。然后我需要计算该点与其他轨道椭圆之间的最短距离。这是困难的部分。有几个例子可以找到一个点到一个椭圆的最短距离,但都是二维的,很难理解。
如果该距离小于所有四个点的公差,则认为这等同于目标环面内的轨道。
为简单起见,所有这些轨道的原点始终位于世界原点 (0, 0, 0) - 我的坐标系是 Z-Up。每个轨道都有一系列定义它的参数(轨道元素)。
这里简单的做法:
对每个轨道进行采样以设置
N
个点。设第一个轨道的点为
A
,第二个轨道的点为B
。const int N=36; float A[N][3],B[N][3];
找到2个最近的点
所以
d=|A[i]-B[i]|
是最小的。如果d
小于或等于你的 margin/treshold 那么轨道彼此太近了。速度与准确度
除非你对 #2 使用一些高级方法,否则它的计算将是
O(N^2)
,这有点可怕。N
越大,结果的准确性越高,但计算时间也越长。有两种方法可以补救。例如:第一个小样本
N
当找到最近的点时再次采样两个轨道
但仅在相关点附近(更高
N
)。您可以通过循环 #2 递归地提高准确性,直到您达到所需的精度
测试
d
椭圆之间是否太接近
我想我可能有新的解决办法。
- 绘制当前轨道上的四个点(椭圆)。
- 将这些点投影到目标轨道(环面)的平面上。
- 使用目标轨道倾角作为平面的法线,计算每个(归一化)点与近点角的夹角 在目标轨道上。
- 以此角度作为平均距平,计算等效偏心距平
- 使用这些偏心异常绘制目标轨道上的四个点 - 这应该是离另一个轨道最近的点。
- 检查这些点之间的距离。
这里的难点在于计算角度并将其转换为另一个轨道上的异常。不过,这应该比递归函数更准确、更快。当我尝试这个时会更新。
编辑:
是的,这行得通!
// 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);}
我希望评论描述这是如何工作的。折腾了几个月终于解决了。谢谢大家!