为了用八进制树表示球体,我需要确定球体和轴对齐立方体之间的重叠.具体而言,有三种情况:

  • 立方体完全在球体内部.我们把它标记为满.
  • 立方体完全在球体之外.我们把它标记为空.
  • 立方体部分位于球体内部.我们将其细分并递归.

以下是我目前对如何做到这一点的最佳猜测.

I'm asking here to know if there are any simpler ways (meaning lesser complexity / fewer operations)

感谢this previous question的答案,我找到了this algorithm来检测任何重叠.然而,它不能区分立方体是完全在球体内,还是只是部分在球体内.

根据这里给出的建议,我首先通过判断立方体的最远角是否在球体内部来判断总重叠.如果不是,那么我们使用Jim Arvo的算法来确定是否有任何重叠.

以下是我的结论:(使用c23)

#include <OpenGL/OpenGL.h>
#include <math.h>

/* A 3D point with type-punning to access coordinates by name or index */
typedef union point_3d {
    struct {
        GLdouble x, y, z;
    } coord;

    GLdouble t[ 3 ];
} point_3d;

/* An axis-aligned cube */
typedef struct cube {
    /* corner with smallest coordinates */
    point_3d p_min;

    /* side length */
    GLdouble len;
} cube_t;

typedef struct sphere {
    unsigned int radius;
    point_3d center;
} sphere_t;

/* Clearly defined values for our result */
typedef enum overlap {
    no_overlap = -1,
    partial_overlap = 0,
    total_overlap = 1,
} overlap_t;

static inline GLdouble sq( GLdouble val ) {
    return val * val;
}

static bool point_in_sphere( point_3d p, struct sphere s ) {
    GLdouble dx = p.t[ 0 ] - s.center.t[ 0 ];
    GLdouble dy = p.t[ 1 ] - s.center.t[ 1 ];
    GLdouble dz = p.t[ 2 ] - s.center.t[ 2 ];
    return sq( s.radius ) >= sq( dx ) + sq( dy ) + sq( dz );
}

overlap_t cube_overlaps_sphere( cube_t c, sphere_t s ) {
    point_3d p_min = c.p_min;
    point_3d p_max = { .t= {
        p_min.t[0] + c.taille,
        p_min.t[1] + c.taille,
        p_min.t[2] + c.taille,
    } };
    point_3d c_centre = { .t = { 
        p_min.t[0] + c.taille / 2,
        p_min.t[1] + c.taille / 2, 
        p_min.t[2] + c.taille / 2,
    } };
    point_3d v = { .t = {
        c_centre.t[ 0 ] - s.centre.t[ 0 ],
        c_centre.t[ 1 ] - s.centre.t[ 1 ],
        c_centre.t[ 2 ] - s.centre.t[ 2 ],}
    };
    point_3d far_corner = { .t = { 
        v.t[ 0 ] < 0 ? p_min.t[ 0 ] : p_max.t[ 0 ],
        v.t[ 1 ] < 0 ? p_min.t[ 1 ] : p_max.t[ 1 ],
        v.t[ 2 ] < 0 ? p_min.t[ 2 ] : p_max.t[ 2 ],
    } };

    if ( point_in_sphere( far_corner, s ) ) {
        return total_overlap;
    }

    /* 算法rithm by Jim Arvo */
    GLdouble dmin = 0;
    for ( unsigned int i = 0; i < 3; i++ ) {
        if ( s.center.t[ i ] < p_min.t[ i ] ) {
            dmin += pow( s.center.t[ i ] - p_min.t[ i ], 2 );
        } else if ( s.center.t[ i ] > p_max.t[ i ] ) {
            dmin += pow( s.center.t[ i ] - p_max.t[ i ], 2 );
        }
    }
    if ( dmin <= pow( s.radius, 2 ) ) {
        return partial_overlap;
    } else {
        return no_overlap;
    }
}

推荐答案

计算重叠参数所需的操作数与空间维度的数量呈线性关系. 对于任何特定的维数,它是O(1). 对于某些球体,立方体对产生最少操作的策略将不同于其他一些对,因此对于给定应用程序来说,总体上最好的策略可能是对于最大数量的测试 case 来说最好的策略. 对于你的特定应用,我预计这将是部分重叠的情况,因为它是在接近球体表面,你将最深的分支.

您使用的Avro算法计算从球体中心到实体立方体任何部分(角、边、面或内部)的最短距离,并将其与球体半径进行比较,以确定是否存在任何重叠. 它是有效和高效的. 我看不出有什么方法可以改进它的工作.

Avro不区分立方体的部分和完全重叠,而你需要这样做. 然而,由@ EricPostpischil提供的,我们观察到,如果距离球面中心最远的立方体的角在球面内或在球面上,那么立方体就完全被包围了,否则就不是,而且我们还可以确定哪个角是从球面中心到立方体中心的矢量. 事实证明,该计算的变体可以构造成足够类似于Avro算法,以便在同一个循环中组合两者. 看起来可能是这样的:

overlap_t compute_overlap(cube_t cube, sphere_t sphere) {
    GLdouble d2min = 0;    // minimum sq distance from the center of the sphere to any part of the cube
    GLdouble d2max = 0;    // maximum sq distance from the center of the sphere to [a corner of] the cube

    for (int i = 0; i < 3; i++) {
        GLdouble delta = sphere.center[i] - cube.pmin[i];

        if (delta < cube.len / 2) {
            // the sphere center is on the decreasing side of the cube center
            d2max += sq(delta - cube.len);
            if (delta < 0) {
                 // the sphere center is on the decreasing side of the whole cube
                 d2min += sq(delta);
            }
        } else {
            // the sphere center is on the increasing side of the cube center
            d2max += sq(delta);
            if (delta > cube.len) {
                 // the sphere center is on the increasing side of the whole cube
                d2min += sq(delta - cube.len);
            }
        }
    }

    if (sq(sphere.radius) <= d2min) {
        return no_overlap;
    } else if (sq(sphere.radius) < d2max) {
        return partial_overlap;
    } else {
        return total_overlap;
    }
}

我不能证明这对您的情况是最佳的,尤其是不能证明您的特定编译器的优化器可能会发现更令人满意的小的重新安排. 然而,我确实认为,模这些微小的微优化,你将有困难做得更好的你的情况.

C++相关问答推荐

char为16位且Short也为16位的c环境合法吗

Bison解析器转移/减少冲突

编译SDL 2时缺少SDL_ttf

如何在C宏中确定Windows主目录?

我可以在C中声明不同长度数组的数组而不带变量名吗?

致命:ThreadSaniizer:在Linux内核6.6+上运行时意外的内存映射

`#if`条件中是否允许`sizeof`?

如何使用指向 struct 数组的指针并访问数组中特定索引处的 struct

错误Cygwin_Except::Open_stackdupfile:正在转储堆栈跟踪是什么?

为什么将函数名括在括号中会禁用隐式声明?

为什么我的Hello World EFI程序构建不正确?

为什么我从CSV文件中进行排序和搜索的代码没有显示数据的所有结果?

在进程之间重定向输出和输入流的问题

如果格式字符串的内存与printf的一个参数共享,会发生什么情况?

C:Assignment中的链表赋值从指针目标类型中丢弃‘const’限定符

int * 指向int的哪个字节?

问题:C#Define上的初始值设定项元素不是常量

添加/删除链表中的第一个元素

为什么实现文件中的自由函数默认没有内部链接?

段错误try 访问静态字符串,但仅有时取决于构建环境