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.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
不相交//---------------------------------------------------------------------------
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个交叉积,这说明了边的分离。