是否有一种优化的方法可以在3D中找到立方体的最远角

时间:2017-07-04 15:00:09

标签: algorithm geometry

如果一个立方体 C 有8个角(不是轴对齐)和一个随机点 P ,我可以做的不仅仅是检查所有角落并选择角落距离 P 最远的距离是否可以通过考虑立方体的对称性来减少检查次数?

4 个答案:

答案 0 :(得分:4)

  1. 将点P转换为立方体的原理基础,即它在轴向上对齐(调用新点Q),原点是世界空间中立方体的中心

  2. 要查找此框架中的最远点F:以X轴为例,如果

    • Q.x > 0然后F.x = -L,其中L是多维数据集的边长
    • Q.x < 0然后F.x = L
    • Q.x == 0(严格abs(Q.x) < EPSILON),然后是F.x = 0
  3. 对其他两个轴也执行步骤2

  4. 通过在步骤1中执行的逆转换,将结果点F 转换回到世界空间。这将在您想要的立方体上给出最远点。 / LI>

    编辑:让我们看看为什么这会更有效率。

    • 原始方法:8 X平方距离计算= 8 X(3次浮点乘法+ 2次加法)

    • 新方法:2次矩阵乘法= 2次X(9次乘法+ 6次加法)

    如您所见,新方法使用较少的乘法和加法。

答案 1 :(得分:2)

将随机点的坐标转换为与立方体对齐的局部坐标系。这需要9次乘法和9次加法。

如果本地系统以立方体为中心,则三个符号测试将告诉您最远的角落。

通过 locus 方法可以很好地理解这个问题。

如果考虑随机点的所有位置导致相同的答案(即相同的角指数;这是顶点的最远Voronoi图),则在由立方体的三个二等分平面定义的八个八分圆中划分空间。在八个区域中说明一点需要三次二进制测试(Lg(8))。有问题的测试告诉了这个点位于哪一侧,这需要评估仿射表达式aX+bY+cZ+d

在&#34;代数决策树&#34;模型,这可能是最佳的。

<强>更新

由于只有标志很重要,你可以对系数进行标准化,使其中一个系数为单位,并且备用乘法(aX+bY+cZ+d>0 -> X+b'Y+c'Z+d>0')。您还可以交换添加项以进行比较(X+bY+cZ+d>0 -> X+bY+cZ>d')。因此,共有6次乘法,6次加法和3次比较。

[对于数值稳定性,应除以最大系数。不幸的是,这导致表达式依赖于它。这可以通过编写二十七个(!)索引计算函数(见下文)并通过指针调用正确的函数来规避。]

假设系数已经预先计算,下面的表达式给出了角落索引,范围为[0,7]

(X + b*Y + c*Z > d) + ((X + b'*Y + c'*Z > d') << 1) + ((X + b"*Y + c"*Z > d") << 2)

幸运的是,编译器可以通过条件赋值以无分支方式计算它,需要15个触发器和5个操作。

答案 2 :(得分:0)

最远的角落满足|xc-xp||yc-yp||zc-zp|的至少一个“delta”最大值。

测试每个“delta”,存储“候选人”。如果一个角的delta大于或等于另一个候选者的相同delta,则该角是“候选者”。

如果先前的候选人在其“最大条件”中被击败,则将其从候选人名单中删除。例如,如果角落4成为候选者,因为其delta-y是最大值,但现在角落6具有更大的delta-y(其他增量不是角落4的原因),则角落4不再是候选者。
第一个测试角有三个最大值,因此需要击败三次(在x,y和z上)才能离开候选列表。

请注意,成为候选人的考试是d1>=d2而不只是d1>d2

现在只为候选人测试距离(平方距离足够)。

请注意,如果所有角落位于立方体的中心,则它们距离点的距离可以相同。解决方案不仅可以是一个角落,而且可以是两个,四个或八个。

答案 3 :(得分:0)

@spug触发了我的认识,我已经拥有了我需要的大部分代码。

我以前开发过一种解决立方体上最近点(不一定是角落)的最佳方法。该代码只需要进行少量修改即可返回多维数据集中最近的角落。从逻辑上讲,立方体上最远的点是最近角落的对角。

原理是将点投影到三个主轴上并测试投影沿每个轴的距离。如果它大于一半,则将该轴的索引设置为false,否则将其设置为true。在为三个轴执行此操作后,您可以使用三个布尔变量来选择角点。

在分析超过1000万个随机点后,可以获得以下数据。

朴素线性搜索算法 - 643ms 快速算法 - 481ms

我最终使用的C#代码是

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public Vector3 FurthestPointFromFast(Vector3 q)
    {
        //BasisVectors( TransformMatrix, out var vx, out var vy, out var vz );
        var origin = BoxCorners.V000;

        // Get the pre-calculated principle
        // axes of the box with origin at V000
        Vector3 vx = BoxCorners.Px;
        Vector3 vy = BoxCorners.Py;
        Vector3 vz = BoxCorners.Pz;

        // Get the pre-calculated prinicple
        // axes lengths squared of the box at V000
        double lx = BoxCorners.Lx2;
        double ly = BoxCorners.Ly2;
        double lz = BoxCorners.Lz2;

        var d = q - origin;
        double tx = Vector3.Dot( d, vx );
        double ty = Vector3.Dot( d, vy );
        double tz = Vector3.Dot( d, vz );

        bool ix = tx < lx / 2;
        bool iy = ty < ly / 2;
        bool iz = tz < lz / 2;

        return ix
            ? (iy
                ? (iz
                    ? BoxCorners.V111
                    : BoxCorners.V110)
                : (iz
                    ? BoxCorners.V101
                    : BoxCorners.V100))
            : (iy
                ? (iz
                    ? BoxCorners.V011
                    : BoxCorners.V010)
                : (iz
                    ? BoxCorners.V001
                    : BoxCorners.V000));
    }

其中BoxCorners的实例来自以下类。假设一个盒子相对于对它进行的检查的数量是相对静态的,因此计算出的值(例如盒子的边长)与每个角的坐标一起存储。

public class BoxCorners
{
    public Vector3 V111;
    public Vector3 V110;
    public Vector3 V101;
    public Vector3 V100;
    public Vector3 V011;
    public Vector3 V010;
    public Vector3 V001;
    public Vector3 V000;

    // These are the vectors starting at the box origin (V000)
    // They are precalculated and usefull in other algorithms
    #region Principle Axis
    public Vector3 Origin; 
    public Vector3 Px;
    public Vector3 Py;
    public Vector3 Pz;
    /// <summary>
    /// Px length squared
    /// </summary>
    public double Lx2;
    /// <summary>
    /// Py length squared
    /// </summary>
    public double Ly2;
    /// <summary>
    /// Pz length squared
    /// </summary>
    public double Lz2;
    #endregion


    public BoxCorners(Vector3 v000, Vector3 v001, Vector3 v010, Vector3 v011, Vector3 v100, Vector3 v101, Vector3 v110, Vector3 v111)
    {
        V000 = v000;
        V001 = v001;
        V010 = v010;
        V011 = v011;
        V100 = v100;
        V101 = v101;
        V110 = v110;
        V111 = v111;

        Origin = V000;

        Px = (V100 - Origin);
        Py = (V010 - Origin);
        Pz = (V001 - Origin);

        Lx2 = Px.LengthSquared();
        Ly2 = Py.LengthSquared();
        Lz2 = Py.LengthSquared();
    }

}

原始的朴素算法在

之下
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    void Acc(ref Vector3 v, ref Vector3 q, ref double max, ref Vector3 maxV)
    {
        var m = Vector3.DistanceSquared(v, q);
        if (!(m > max))
            return;
        max = m;
        maxV = v;
    }

    /// <summary>
    /// Return the furthest point on the box from the point
    /// </summary>
    /// <param name="q"></param>
    /// <returns></returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public Vector3 FurthestPointFrom(Vector3 q)
    {

        var max = 0.0;
        var bc = BoxCorners;
        var r = Vector3.Zero;

        Acc(ref bc.V111, ref q, ref max, ref r);
        Acc(ref bc.V110, ref q, ref max, ref r);
        Acc(ref bc.V101, ref q, ref max, ref r);
        Acc(ref bc.V100, ref q, ref max, ref r);
        Acc(ref bc.V011, ref q, ref max, ref r);
        Acc(ref bc.V010, ref q, ref max, ref r);
        Acc(ref bc.V001, ref q, ref max, ref r);
        Acc(ref bc.V000, ref q, ref max, ref r);

        return r;
    }

double numerics nuget包用于3D矢量图元。