我正在寻找一种方法来尝试执行此更改:
A 和 C 是固定的 3D 点。然后应移动 B,以便 AtoB 和 CtoB 具有它们以前的平均距离。
听起来微不足道,但到目前为止我得到的最好的结果是,如果执行多次,最终会翻转点并且有些平均。
Vector3 dir_a = B - A;
Vector3 dir_b = B - C;
float averageDistance = (dir_a.magnitude + dir_b.magnitude) / 2;
dir_a.Normalize();
dir_b.Normalize();
B -= (averageDistance) * (dir_a + dir_b);
有人有正确方向的提示吗?
这有点模棱两可,因为通常有一整套有效的解决方案,但我们可以选择一个接近原始 B 的点(其中有一个问题,我在最后解决)。一种方法是在 A 和 C 之间创建一个垂直于 A 和 C 之间的线的平面,然后从该线与该平面的交点开始,朝 B 移动,同时停留在平面上 直到距离是对的。顺便说一句,没有搜索,我们只需计算所需的半径即可:我们将有一个直角三角形,其中给定了底边,我们需要选择一个高度,使斜边具有所需的长度。 像这样:
Vector3 dir_a = B - A;
Vector3 dir_b = B - C;
float avg_d = (dir_a.Length() + dir_b.Length()) / 2;
// axis is the normal vector of the plane between A and C
// perpendicular to the line between A and C
Vector3 axis = Vector3.Normalize(C - A);
// point halfway between A and C,
// will be the origin of the circle on which the result lies,
// and it will be where the right angle of the right angle triangle is
Vector3 midpoint = (A + C) / 2;
Vector3 mid_to_b = B - midpoint;
// project mid_to_B onto the plane and normalize,
// forming the direction of the height of the triangle (radius of the circle)
Vector3 dir = Vector3.Normalize(mid_to_b - Vector3.Dot(axis, mid_to_b) * axis);
// the triangle should satisfy:
// height² + base² = avg_d²
// where the base is the distance between the midpoint and C (or A, same thing)
// solving for height:
// height² = avg_d² - base²
// height = sqrt(avg_d² - base²)
float axis_length2 = (C - midpoint).LengthSquared();
float height = MathF.Sqrt(avg_d * avg_d - axis_length2);
// go from the midpoint in the right direction for the distance we just calculated:
Vector3 result = midpoint + dir * height;
请注意,如果 A、B 和 C 共线,则此方法不起作用。如果您关心这一点,您可以检测到这一点,并在这种情况下尝试使用另一个向量而不是
mid_to_b
。由于此方法尝试沿 B 方向行进,因此如果该方向垂直于平面,则不可能进行任何移动。
dir
可能会变成 NaN 或(由于浮点的东西使得向量被归一化为 几乎零而不是实际零)一些不是特别有用的方向。 您可以使用与
axis
正交的任何方向,即
Vector3 dir = Vector3.Normalize(Orthogonal(axis))
,您可以获得像这样的一些正交向量:(如果您对获得的方向不是非常挑剔,只是它是正交的,在这里我选择最长的选项主要是为了避免意外得到零向量) static Vector3 Orthogonal(Vector3 dir)
{
Vector3 A = new Vector3(0, dir.Z, -dir.Y);
Vector3 B = new Vector3(-dir.Z, 0, dir.X);
Vector3 C = new Vector3(-dir.Y, dir.X, 0);
float lenA = A.LengthSquared();
float lenB = B.LengthSquared();
float lenC = C.LengthSquared();
if (lenA > lenB)
{
// make B the largest vector among A and B
B = A;
lenB = lenA;
}
// now one of B or C must be the longest
if (lenB > lenC)
return B;
else
return C;
}
使用它更稳健,但是:
这个方向与 B 无关,将新点发送到与 B 无关的某个方向,B 唯一的用途就是最初找到
avg_d