C++ 快速四面体-四面体相交

C++ 快速四面体-四面体相交,c++,3d,geometry,vectormath,tetrahedra,C++,3d,Geometry,Vectormath,Tetrahedra,对于矿山的一个项目,我需要在三维空间中可靠地检测两个四面体之间的交点。我不需要点/线/面来知道是否存在交点。接触也被视为相交,但公共三角形面不被视为相交。在尽可能快地实现这一目标的艰难斗争之后,我的解决方案变成了这样一个可怕的问题: 设四面体v0,v1 每个四面体有4个三角形t[4],其中每个三角形有3个点p0、p1、p2和法向量n 计算两个四面体的所有四条边的平面 所以平面上的任何点p都由方程给出 dot(p,n) + d = 0 其中n是平面的法线。众所周知,这归结于计算d D0[i] =

对于矿山的一个项目,我需要在三维空间中可靠地检测两个四面体之间的交点。我不需要点/线/面来知道是否存在交点。接触也被视为相交,但公共三角形面不被视为相交。在尽可能快地实现这一目标的艰难斗争之后,我的解决方案变成了这样一个可怕的问题:

  • 设四面体
    v0,v1

    每个四面体有4个三角形
    t[4]
    ,其中每个三角形有3个点
    p0、p1、p2
    和法向量
    n

  • 计算两个四面体的所有四条边的平面

    所以平面上的任何点
    p
    都由方程给出

    dot(p,n) + d = 0
    
    其中
    n
    是平面的法线。众所周知,这归结于计算
    d

    D0[i] = -dot(v0.t[i].p0,v0.t[i].n)
    D1[i] = -dot(v1.t[i].p0,v1.t[i].n)
    i = { 0,1,2,3 }
    
    对于每个四面体的每个三角形

  • 测试v0、v1之间三角形与三角形交点的任意组合

    所以,只要在所有16个组合之间循环,并使用三角形与三角形相交

  • 三角形
    v0.t[i]
    vs三角形
    v1.t[j]
    交点归结为:

  • 计算平面之间的交点

    这显然是光线(对于非平行平面),所以平面法线的简单叉积将给出光线方向

    dir = cross(v0.t[i].n,v1.t[j].n)
    
    现在只需要找到属于两个平面的交点。直接利用法线的叉积进行行列式计算,光线计算如下:

    // determinants
    det=vector_len2(dir);
    d0=D0[i]/det;
    d1=D1[j]/det;
    // position
    pos = d0*cross(dir,v1.t[j].n) + d1*cross(v0.t[i].n,dir);
    
    intersecting        5
    non intersecting 1766
    all tests        1771
    
    有关更多信息,请参阅:

  • 为每个三角形编译三角形光线相交的有符号距离间隔

    所以只需计算光线和三角形每条边线的交点,记住距离
    pos
    的最小和最大距离。我们不需要实际点,只需要距
    pos
    的标量距离,这是线/射线相交返回的参数

  • 检查两个三角形的范围是否重叠

    如果重叠超过
    v0,v1
    相交。。。如果所有16个测试均未发生重叠,则
    v0,v1
    不相交

  • 正如你所看到的,要计算的东西很多。我的线性代数和向量数学知识仅限于我使用的东西,所以很有可能有更好的方法来解决这个问题。我尝试了很多方法来缓解这种情况,但没有任何运气(比如使用bbox、bsphere,使用更简单的测试,利用光线和三角形边都在同一平面上等),但结果要么较慢,甚至错误(没有正确计算边情况)

    这里是我的实际C++实现:

    //---------------------------------------------------------------------------
    bool tetrahedrons::intersect_lin_ray(double *a0,double *a1,double *b0,double *db,double &tb)
        {
        int i0,i1;
        double da[3],ta,q;
        vector_sub(da,a1,a0); ta=0.0; tb=0.0; i0=0; i1=1;
             if (fabs(da[i0])+fabs(db[i0])<=_zero) i0=2;
        else if (fabs(da[i1])+fabs(db[i1])<=_zero) i1=2;
        q=(da[i0]*db[i1])-(db[i0]*da[i1]);
        if (fabs(q)<=_zero) return 0;       // no intersection
        // intersection ta,tb parameters
        ta=divide(db[i0]*(a0[i1]-b0[i1])+db[i1]*(b0[i0]-a0[i0]),q);
        tb=divide(da[i0]*(a0[i1]-b0[i1])+da[i1]*(b0[i0]-a0[i0]),q);
        if ((ta<0.0)||(ta>1.0)) return 0;   // inside line check
        return 1;
        }
    //---------------------------------------------------------------------------
    bool tetrahedrons::intersect_vol_vol(_vol4 &v0,_vol4 &v1)   // tetrahedron v0 intersect tetrahedron v1 ?
        {
        int i,j,_ti,_tj;
        _fac3 *f0,*f1;
        double pos[3],dir[3],p[3],det,D0[4],D1[4],d0,d1,t,ti0,ti1,tj0,tj1;
        // planes offset:  dot(p,v0.t[i].n)+D0[i] = 0  ,  dot(p,v1.t[j].n)+D1[j] = 0
        for (i=0;i<4;i++)
            {
            D0[i]=-vector_mul(pnt.pnt.dat+fac.dat[v0.t[i]].p0,fac.dat[v0.t[i]].n);
            D1[i]=-vector_mul(pnt.pnt.dat+fac.dat[v1.t[i]].p0,fac.dat[v1.t[i]].n);
            }
        // plane plane intersection -> ray
        for (i=0;i<4;i++)
         for (j=0;j<4;j++)
            {
            f0=fac.dat+v0.t[i];
            f1=fac.dat+v1.t[j];
            // no common vertex
            if ((f0->p0==f1->p0)||(f0->p0==f1->p1)||(f0->p0==f1->p2)) continue;
            if ((f0->p1==f1->p0)||(f0->p1==f1->p1)||(f0->p1==f1->p2)) continue;
            if ((f0->p2==f1->p0)||(f0->p2==f1->p1)||(f0->p2==f1->p2)) continue;
            // direction
            vector_mul(dir,f0->n,f1->n);
            det=vector_len2(dir);
            if (fabs(det)<=_zero) continue; // parallel planes?
            d0=D0[i]/det;
            d1=D1[j]/det;
            // position
            vector_mul(p,dir,f1->n); vector_mul(pos,p,d0);
            vector_mul(p,f0->n,dir); vector_mul(p,p,d1);
            vector_add(pos,pos,p);
            // compute intersection edge points
            _ti=1; _tj=1;
            if (intersect_lin_ray(pnt.pnt.dat+f0->p0,pnt.pnt.dat+f0->p1,pos,dir,t)){ if (_ti) { _ti=0; ti0=t; ti1=t; } if (ti0>t) ti0=t; if (ti1<t) ti1=t; }
            if (intersect_lin_ray(pnt.pnt.dat+f0->p1,pnt.pnt.dat+f0->p2,pos,dir,t)){ if (_ti) { _ti=0; ti0=t; ti1=t; } if (ti0>t) ti0=t; if (ti1<t) ti1=t; }
            if (intersect_lin_ray(pnt.pnt.dat+f0->p2,pnt.pnt.dat+f0->p0,pos,dir,t)){ if (_ti) { _ti=0; ti0=t; ti1=t; } if (ti0>t) ti0=t; if (ti1<t) ti1=t; }
            if (intersect_lin_ray(pnt.pnt.dat+f1->p0,pnt.pnt.dat+f1->p1,pos,dir,t)){ if (_tj) { _tj=0; tj0=t; tj1=t; } if (tj0>t) tj0=t; if (tj1<t) tj1=t; }
            if (intersect_lin_ray(pnt.pnt.dat+f1->p1,pnt.pnt.dat+f1->p2,pos,dir,t)){ if (_tj) { _tj=0; tj0=t; tj1=t; } if (tj0>t) tj0=t; if (tj1<t) tj1=t; }
            if (intersect_lin_ray(pnt.pnt.dat+f1->p2,pnt.pnt.dat+f1->p0,pos,dir,t)){ if (_tj) { _tj=0; tj0=t; tj1=t; } if (tj0>t) tj0=t; if (tj1<t) tj1=t; }
            if ((_ti)||(_tj)) continue;
            if ((ti0>=tj0)&&(ti0<=tj1)) return 1;
            if ((ti1>=tj0)&&(ti1<=tj1)) return 1;
            if ((tj0>=ti0)&&(tj0<=ti1)) return 1;
            if ((tj1>=ti0)&&(tj1<=ti1)) return 1;
            }
        return 0;
        };
    //---------------------------------------------------------------------------
    
    p
    pnt
    中的点索引
    0,3,6,9…
    n
    是正常的
    s
    是正常的符号(如果三角形共享,那么法线指向相同的方向),
    t[4]
    fac
    中三角形的索引
    0,1,2,3,
    。 下面是一个样本测试:

    bool tetrahedrons::vols_intersect()                     // test if vol[] intersects each other
        {
        int i,j;
        for (i=0;i<vol.num;i++)
         for (j=i+1;j<vol.num;j++,dbg_cnt++)
          if (intersect_vol_vol(vol.dat[i],vol.dat[j]))
            {
            linc=0x800000FF;
            if (intersect_vol_vol(vol.dat[j],vol.dat[i])) linc=0x8000FFFF;
            lin_add_vol(vol.dat[i]);
            lin_add_vol(vol.dat[j]);
            return 1;
            }
        return 0;
        }
    
    我调用了
    vols\u intersect
    测试10次,以使测量足够长。此数据集中所有放置的四面体都不会相交(导致时间最长)。在导致该网格的实际过程中(太大而无法发布),计数如下所示:

    // determinants
    det=vector_len2(dir);
    d0=D0[i]/det;
    d1=D1[j]/det;
    // position
    pos = d0*cross(dir,v1.t[j].n) + d1*cross(v0.t[i].n,dir);
    
    intersecting        5
    non intersecting 1766
    all tests        1771
    

    你试过分离轴的方法吗?它相当快(您只有8个轴)。它用于类似于quick hull的东西,因此任何新添加的四面体都不能与已放置的部分甚至所有四面体相交。但是没有一个四面体是相交的这仅仅是一种预感-但是你考虑过德克·格雷戈里厄斯吗?上有一个示例实现。@Spektre您确定您的算法覆盖了所有的角点情况(例如平行面和一个四面体完全封闭在另一个四面体中的情况)?我有点怀疑。你的方法已经非常类似于MBo建议的分离轴测试,我建议你去那里。然而…@MBo。。。SAT测试两个四面体需要24个测试:第一个四面体的4个法线,第二个四面体的4个法线,以及两个四面体的所有法线对的16个交叉积,这说明了边的分离。