将B点移动到A和C之间,同时保持距离

问题描述 投票:0回答:1

我正在寻找一种方法来尝试执行此更改:

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);

有人有正确方向的提示吗?

c# euclidean-distance
1个回答
0
投票

这有点模棱两可,因为通常有一整套有效的解决方案,但我们可以选择一个接近原始 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
  • 这会花费更多的代码/复杂性/可能还需要时间。
© www.soinside.com 2019 - 2024. All rights reserved.