如何找到一条线与一个轴对齐的框相交的位置,其中该线的至少一个点始终在该框内



我有一个数据类型,表示在我制作的游戏中,每个轴可以施加到机器上的最大力。此数据类型分别存储六个轴——X、Y和Z,分别为正轴和负轴。这用于定义设备在3D空间中给定方向上可以施加的最大力。由于所有可以对这台机器施加力的零件都是轴对齐的(扩展到只能对一个或多个世界空间法线施加力(,因此这可以完美地表示其极限。

利用这些信息,我构建了一种边界框。此边界框旨在将移动机器所需的力限制为一个较小的值,该值表示机器实际输出的力。

我目前的方法是根据这些数据创建这个虚拟边界框,并查看我所需的力(可视化为线段,其中第一点是3D空间的原点,第二点是所需力向量本身(与这个虚拟矩形棱镜上的至少一个面相交的位置。我还没有完全使用这种方法,因为我在理解如何在我这边解决这个问题方面存在一些问题,这也是我提出这个问题的目的之一。边界框始终包含三维空间的原点,但不能保证框本身的原点等于三维空间的起点。

我认为这个盒子的类比是迄今为止最好的方法,因为我的有限力最好用一条线与这个虚拟盒子相交的地方来表示。有没有更好的方法可以按照我需要的的方式约束框内的点,而不必简单地按轴约束它?以下是问题的常数:

  • 表示力的线段的原点将始终等于三维空间的原点。因此,表示力的线段的组合方向*大小将始终等于力本身
  • 三维空间的原点将永远不在此虚拟框之外
  • 我认为我的想法是正确的,但感觉太复杂了,我认为有很多东西我可以删掉,让计算更容易,但问题是,我不太确定从哪里开始,让它尽可能干净高效,更不用说真正解决这个问题的最佳方法了。

    这是我试过的代码。

    public Vector3f ConstrainedWithinScaled(Vector3f value) {
    // Vector3f is a simple class that contains x, y, z, basic arithmetic operators (*/+-), Magnitude/Normalized properties, and Dot/Cross methods.
    // The code that this method exists in is a class called DualVector3f which is the class described above. It contains properties PosX, PosY, PosZ, NegX, NegY, and NegZ -- All of these properties have positive values as they describe magnitude on that specific face.
    if (IsInBounds(value)) {
    // The input value is already within the constraints of the virtual box.
    return value;
    }
    // Get all intersections
    // The first returned intersection that resides on this box's surface is correct.
    // Only in cases where the point reside on a corner or edge will result in multiple of these conditions being true, however in these cases the returned point will be identical in all 2 or 3 cases.
    Vector3f center = Center;
    Vector3f size = Size;
    // Cache these so that I don't calculate them every single time.
    // These are calculated from the minimum/maximum coordinates to create the center of the virtual box and the size of the virtual box respectively.
    // public Vector3f Size => Negative + Positive; (Negative and Positive both only have positive components, as outlined up top)
    // public Vector3f Center => Positive - (Size / 2); (Positive is always the maximum)
    // As should be evident, Positive is composed of PosX, PosY, and PosZ
    // Likewise, Negative is composed of NegX, NegY, and NegZ
    Vector3f topIntersection = IntersectPoint(value, new Vector3f(0, 1, 0), center + new Vector3f(0, size.y, 0));
    if (topIntersection.y == PosY) return topIntersection;
    Vector3f bottomIntersection = IntersectPoint(value, new Vector3f(0, 1, 0), center - new Vector3f(0, size.y, 0));
    if (bottomIntersection.y == -NegY) return bottomIntersection;
    Vector3f leftIntersection = IntersectPoint(value, new Vector3f(-1, 0, 0), center - new Vector3f(size.x, 0, 0));
    if (leftIntersection.x == -NegX) return leftIntersection;
    Vector3f rightIntersection = IntersectPoint(value, new Vector3f(1, 0, 0), center + new Vector3f(size.x, 0, 0));
    if (rightIntersection.x == PosX) return rightIntersection;
    Vector3f frontIntersection = IntersectPoint(value, new Vector3f(0, 0, 1), center + new Vector3f(0, 0, size.z));
    if (frontIntersection.z == PosZ) return frontIntersection;
    Vector3f backIntersection = IntersectPoint(value, new Vector3f(0, 0, -1), center - new Vector3f(0, 0, size.z));
    if (backIntersection.z == -NegZ) return backIntersection;
    return new Vector3f(); // Fallback. This should theoretically never occur under any condition, so this simply satisfies the need to return.
    }
    // This was derived from https://rosettacode.org/wiki/Find_the_intersection_of_a_line_with_a_plane#C.23 and omits the "rayOrigin" parameter as this is always a zero vector.
    private static Vector3f IntersectPoint(Vector3f dirWithMag, Vector3f planeNormal, Vector3f planeCenter) {
    Vector3f diff = -planeCenter;
    float prod1 = diff.Dot(planeNormal);
    float prod2 = dirWithMag.Dot(planeNormal);
    float prod3 = prod1 / prod2;
    return dirWithMag * -prod3;
    }
    

    谢谢。

    我发现解决这个问题的最好方法是取向量,找出哪个分量离边界最远。在我找到这个组件后,我将所有组件除以表示其越界程度的百分比,这会将所有组件缩小到我的框内。这满足了解决方案,因为这将点放置在我的长方体的边界上。

    这是实现它的代码:

    public Vector3f ConstrainedWithinScaled(Vector3f value) {
    if (IsInBounds(value)) {
    // The input value is already within the constraints of the virtual box.
    return value;
    }
    float factorX = 1;
    float factorY = 1;
    float factorZ = 1;
    if (value.x > PosX) {
    factorX = value.x / PosX;
    } else if (value.x < -NegX) {
    factorX = value.x / -NegX;
    }
    if (value.y > PosY) {
    factorY = value.y / PosY;
    } else if (value.y < -NegY) {
    factorY = value.y / -NegY;
    }
    if (value.z > PosZ) {
    factorZ = value.z / PosZ;
    } else if (value.z < -NegZ) {
    factorZ = value.z / -NegZ;
    }
    float largestFactor = Mathf.Max(factorX, factorY, factorZ);
    // Catch case: Box has zero size.
    if (largestFactor == 0) return Vector3f.Zero;
    return value / largestFactor;
    }
    

    相关内容

    最新更新