快速检测空间三角形相交算法的代码实现(Devillers & Guigue算法)

    Devillers & Guigue算法( 简称Devillers 算法) 通过三角形各顶点构成的行列式正负的几何意义来判断三角形中点、线、面之间的相对位置关系,从而判断两三角形是否相交。其基本原理如下:给定空间四个点:a(ax,ay,az),b=(bx,by,bz),c=(cx,cy,cz),d=(dx,dy,dz),定义行列式如下:

    [ a, b, c, d] 采用右手螺旋法则定义了四个空间点的位置关系。[ a, b, c, d] > 0 表示 d 在 a、b、c 按逆时针顺序所组成的三角形的正法线方向( 即上方) ; [ a, b, c, d] < 0 表示 d 在 △abc的下方; [ a, b, c, d] = 0 表示四点共面。

    设两个三角形T1和T2,顶点分别为:V10,V11,V12和V20,V21,V22,三角形所在的平面分别为π1和π2,其法向量分别为N1和N2.算法先判别三角形和另一个三角形所在的平面的相互位置关系,提前排除不相交的情况。通过计算[V20,V21,V22,V1i].(i=0,1,2)来判断T1和π2的关系:如果所有的行列式的值都不为零且同号,则T1和T2不相交;否则T1和π2相交。相交又分为如下几种情况:

     a)如果所有的行列式的值为零,则T1和T2共面,转化为共面的线段相交问题。

     b)如果其中一个行列式的值为零,而其他两个行列式同号,则只有一个点在平面内,测试顶点是否则T2内部,是则相交,否则不相交;

     c)否则T1的顶点位于平面π2两侧(包含T1的一条边在平面π2中的情况)。

     再按照类似的方法对 T 2 和 π 1 作进一步的测试。如果通过测试, 则每个三角形必有确定的一点位于另一个三角形所在平面的一侧, 而另外两点位于其另一侧。算法分别循环置换每个三角形的顶点, 以使V10(V20)位于π2(π1)的一侧,另两个点位于其另一侧;同时对顶点V21,V22(V11,V12)进行交换操作,以确保V10(V20)位于π2(π1)的上方,即正法线方向。经过以上的预排除和置换操作,V10的邻边V10V11,V10V12和V20的邻边V20V21和V20V22与两平面的交线L相交于固定形式的点上,分别记为i,j,k,l(i<j,k<l),如图:

    这些点在L上形成的封闭区间为i1=[i,j],i2=[k,l].至此,两个三角形的相交测试问题转换为封闭区间i1,i2的重叠问题。若重叠则相交,否则不相交。由于交点形式固定,只需满足条件k<=j且i<=l即表明区间重叠,条件还可进一步缩减为判别式(1)是否成立:

    [V10,V11,V20,V21]<=0 && [V10,V12,V22,V20]<=0                                                    (1)

以上资料来自《计算机应用研究》第25卷第10期,邹益胜等的《空间三角形快速相交检测算法》下面是具体代码实现。

 

typedef float float3[3];

enum TopologicalStructure 
{ 
	INTERSECT, NONINTERSECT 
};

struct Triangle
{
	float3 Normal_0; 
	float3 Vertex_1, Vertex_2, Vertex_3;
};

struct point
{
	float x, y;
};


//三维点拷贝为二维点
static void copy_point( point& p, float3 f )
{
	p.x = f[0];
	p.y = f[1];
}

//四点行列式
inline float get_vector4_det( float3 v1, float3 v2, float3 v3, float3 v4 )
{
	float a[3][3];
	for ( int i = 0; i != 3; ++i )
	{
		a[0][i] = v1[i] - v4[i];
		a[1][i] = v2[i] - v4[i];
		a[2][i] = v3[i] - v4[i];
	}

	return a[0][0] * a[1][1] * a[2][2] 
	+ a[0][1] * a[1][2] * a[2][0] 
	+ a[0][2] * a[1][0] * a[2][1] 
	- a[0][2] * a[1][1] * a[2][0] 
	- a[0][1] * a[1][0] * a[2][2] 
	- a[0][0] * a[1][2] * a[2][1];
}


//利用叉积计算点p相对线段p1p2的方位
inline double direction( point p1, point p2, point p ){
	return ( p.x - p1.x ) * ( p2.y - p1.y ) - ( p2.x - p1.x ) * ( p.y - p1.y )   ;
}


//确定与线段p1p2共线的点p是否在线段p1p2上
inline int on_segment( point p1, point p2, point p ){
	double max = p1.x > p2.x ? p1.x : p2.x ;
	double min = p1.x < p2.x ? p1.x : p2.x ;
	double max1 = p1.y > p2.y ? p1.y : p2.y ;
	double min1 = p1.y < p2.y ? p1.y : p2.y ;
	if ( p.x >= min && p.x <= max && p.y >= min1 && p.y <= max1 )
	{
		return 1;
	}
	else
	{
		return 0;
	}
}


//判断线段p1p2与线段p3p4是否相交的主函数
inline int segments_intersert( point p1, point p2, point p3, point p4 ){
	double d1, d2, d3, d4;
	d1 = direction( p3, p4, p1 );
	d2 = direction( p3, p4, p2 );
	d3 = direction( p1, p2, p3 );
	d4 = direction( p1, p2, p4 );
	if ( d1 * d2 < 0 && d3 * d4 < 0 )
	{
		return 1;
	}
	else if ( d1 == 0 && on_segment( p3, p4, p1 ) == 1 )
	{
		return 1;
	}
	else if ( d2 == 0 && on_segment( p3, p4, p2 ) == 1 )
	{
		return 1;
	}
	else if ( d3 == 0 && on_segment( p1, p2, p3 ) == 1 )
	{
		return 1;
	}
	else if ( d4 == 0 && on_segment( p1, p2, p4 ) == 1 )
	{
		return 1;
	}
	return 0;
}


//判断同一平面的直线和三角形是否相交
inline bool line_triangle_intersert_inSamePlane( Triangle* tri, float3 f1, float3 f2 )
{
	point p1, p2, p3, p4;

	copy_point( p1, f1 );

	copy_point( p2, f2 );

	copy_point( p3, tri->Vertex_1 );

	copy_point( p4, tri->Vertex_2 );

	if ( segments_intersert( p1, p2, p3, p4 ) )
	{
		return true;
	}

	copy_point( p3, tri->Vertex_2 );

	copy_point( p4, tri->Vertex_3 );

	if ( segments_intersert( p1, p2, p3, p4 ) )
	{
		return true;
	}

	copy_point( p3, tri->Vertex_1 );

	copy_point( p4, tri->Vertex_3 );

	if ( segments_intersert( p1, p2, p3, p4 ) )
	{
		return true;
	}

	return false;
} 


inline void get_central_point(float3 ¢ralPoint,Triangle* tri)
{
	centralPoint[0]=(tri->Vertex_1[0]+tri->Vertex_2[0]+tri->Vertex_3[0])/3;

	centralPoint[1]=(tri->Vertex_1[1]+tri->Vertex_2[1]+tri->Vertex_3[1])/3;

	centralPoint[2]=(tri->Vertex_1[2]+tri->Vertex_2[2]+tri->Vertex_3[2])/3;
}




//向量之差
inline void get_vector_diff( float3& aimV, const float3 a, const float3 b )
{
	aimV[0] = b[0] - a[0];

	aimV[1] = b[1] - a[1];

	aimV[2] = b[2] - a[2];
} 

//向量内积
inline float Dot( const float3& v1, const float3& v2 )
{    
	return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] ;
} 

//重心法判断点是否在三角形内部
inline bool is_point_within_triangle( Triangle* tri, float3 point )
{
	float3 v0;   
	get_vector_diff( v0, tri->Vertex_1, tri->Vertex_3 );
	float3 v1;   
	get_vector_diff( v1, tri->Vertex_1, tri->Vertex_2 );
	float3 v2; 
	get_vector_diff( v2, tri->Vertex_1, point );
	float dot00 = Dot( v0, v0 ) ;   
	float dot01 = Dot( v0, v1 ) ;    
	float dot02 = Dot( v0, v2 ) ;    
	float dot11 = Dot( v1, v1 ) ;   
	float dot12 = Dot( v1, v2 ) ;    
	float inverDeno = 1 / ( dot00* dot11 - dot01* dot01 ) ;    
	float u = ( dot11* dot02 - dot01* dot12 ) * inverDeno ;    
	if ( u < 0 || u > 1 ) // if u out of range, return directly
	{
		return false ;
	}    
	float v = ( dot00* dot12 - dot01* dot02 ) * inverDeno ;    
	if ( v < 0 || v > 1 ) // if v out of range, return directly
	{
		return false ;
	}    
	return u + v <= 1 ;
} 


//判断同一平面内的三角形是否相交
inline bool triangle_intersert_inSamePlane( Triangle* tri1, Triangle* tri2 )
{
	if ( line_triangle_intersert_inSamePlane( tri2, tri1->Vertex_1, tri1->Vertex_2 ) )
	{
		return true;
	}
	else if ( line_triangle_intersert_inSamePlane( tri2, tri1->Vertex_2, tri1->Vertex_3 ) )
	{
		return true;
	}
	else if ( line_triangle_intersert_inSamePlane( tri2, tri1->Vertex_1, tri1->Vertex_3 ) )
	{
		return true;
	}
	else
	{
		float3 centralPoint1,centralPoint2;

		get_central_point(centralPoint1,tri1);

		get_central_point(centralPoint2,tri2);

		if(is_point_within_triangle( tri2, centralPoint1 ) || is_point_within_triangle( tri1, centralPoint2 ) )
		{
			return true;
		}

		return false;
	}
} 

//Devillers算法主函数
inline TopologicalStructure judge_triangle_topologicalStructure( Triangle* tri1, Triangle* tri2 )
{
	//设tri1所在的平面为p1,tri2所在的平面为p2
	float p1_tri2_vertex1 = get_vector4_det( tri1->Vertex_1, tri1->Vertex_2, tri1->Vertex_3, tri2->Vertex_1 );

	float p1_tri2_vertex2 = get_vector4_det( tri1->Vertex_1, tri1->Vertex_2, tri1->Vertex_3, tri2->Vertex_2 );

	float p1_tri2_vertex3 = get_vector4_det( tri1->Vertex_1, tri1->Vertex_2, tri1->Vertex_3, tri2->Vertex_3 );


	if ( p1_tri2_vertex1 > 0 && p1_tri2_vertex2 > 0 && p1_tri2_vertex3 > 0 )
	{
		return NONINTERSECT;
	}

	if ( p1_tri2_vertex1 < 0 && p1_tri2_vertex2 < 0 && p1_tri2_vertex3 < 0 )
	{
		return NONINTERSECT;
	}


	if ( p1_tri2_vertex1 == 0 && p1_tri2_vertex2 == 0 && p1_tri2_vertex3 == 0 )
	{
		if ( triangle_intersert_inSamePlane( tri1, tri2 ) )
		{
			return INTERSECT;
		}
		else
		{
			return NONINTERSECT;
		}
	}


	if ( p1_tri2_vertex1 == 0 && p1_tri2_vertex2 * p1_tri2_vertex3 > 0 )
	{
		if ( is_point_within_triangle( tri1, tri2->Vertex_1 ) )
		{
			return INTERSECT;
		}
		else
		{
			return NONINTERSECT;
		}
	}
	else if ( p1_tri2_vertex2 == 0 && p1_tri2_vertex1 * p1_tri2_vertex3 > 0 )
	{
		if ( is_point_within_triangle( tri1, tri2->Vertex_2 ) )
		{
			return INTERSECT;
		}
		else
		{
			return NONINTERSECT;
		}
	}
	else if ( p1_tri2_vertex3 == 0 && p1_tri2_vertex1 * p1_tri2_vertex2 > 0 )
	{
		if ( is_point_within_triangle( tri1, tri2->Vertex_3 ) )
		{
			return INTERSECT;
		}
		else
		{
			return NONINTERSECT;
		}
	}



	float p2_tri1_vertex1 = get_vector4_det( tri2->Vertex_1, tri2->Vertex_2, tri2->Vertex_3, tri1->Vertex_1 );

	float p2_tri1_vertex2 = get_vector4_det( tri2->Vertex_1, tri2->Vertex_2, tri2->Vertex_3, tri1->Vertex_2 );

	float p2_tri1_vertex3 = get_vector4_det( tri2->Vertex_1, tri2->Vertex_2, tri2->Vertex_3, tri1->Vertex_3 );


	if ( p2_tri1_vertex1 > 0 && p2_tri1_vertex2 > 0 && p2_tri1_vertex3 > 0 )
	{
		return NONINTERSECT;
	}

	if ( p2_tri1_vertex1 < 0 && p2_tri1_vertex2 < 0 && p2_tri1_vertex3 < 0 )
	{
		return NONINTERSECT;
	}


	if ( p2_tri1_vertex1 == 0 && p2_tri1_vertex2 * p2_tri1_vertex3 > 0 )
	{
		if ( is_point_within_triangle( tri2, tri1->Vertex_1 ) )
		{
			return INTERSECT;
		}
		else
		{
			return NONINTERSECT;
		}
	}

	if ( p2_tri1_vertex2 == 0 && p2_tri1_vertex1 * p2_tri1_vertex3 > 0 )
	{
		if ( is_point_within_triangle( tri2, tri1->Vertex_2 ) )
		{
			return INTERSECT;
		}
		else
		{
			return NONINTERSECT;
		}
	}

	if ( p2_tri1_vertex3 == 0 && p2_tri1_vertex1 * p2_tri1_vertex2 > 0 )
	{
		if ( is_point_within_triangle( tri2, tri1->Vertex_3 ) )
		{
			return INTERSECT;
		}
		else
		{
			return NONINTERSECT;
		}
	}



	float* tri1_a = tri1->Vertex_1,* tri1_b = tri1->Vertex_2,* tri1_c = tri1->Vertex_3
		,* tri2_a = tri2->Vertex_1,* tri2_b = tri2->Vertex_2,* tri2_c = tri2->Vertex_3;

	float* m;

	float im;

	if ( p2_tri1_vertex2 * p2_tri1_vertex3 >= 0 && p2_tri1_vertex1 != 0 )
	{
		if ( p2_tri1_vertex1 < 0 )
		{
			m = tri2_b;
			tri2_b = tri2_c;
			tri2_c = m;

			im = p1_tri2_vertex2;
			p1_tri2_vertex2 = p1_tri2_vertex3;
			p1_tri2_vertex3 = im;
		}
	}
	else if ( p2_tri1_vertex1 * p2_tri1_vertex3 >= 0 && p2_tri1_vertex2 != 0 )
	{
		m = tri1_a;
		tri1_a = tri1_b;
		tri1_b = tri1_c;
		tri1_c = m;

		if ( p2_tri1_vertex2 < 0 )
		{
			m = tri2_b;
			tri2_b = tri2_c;
			tri2_c = m;

			im = p1_tri2_vertex2;
			p1_tri2_vertex2 = p1_tri2_vertex3;
			p1_tri2_vertex3 = im;
		}
	}
	else if ( p2_tri1_vertex1 * p2_tri1_vertex2 >= 0 && p2_tri1_vertex3 != 0 )
	{
		m = tri1_a;

		tri1_a = tri1_c;

		tri1_c = tri1_b;

		tri1_b = m;

		if ( p2_tri1_vertex3 < 0 )
		{
			m = tri2_b;
			tri2_b = tri2_c;
			tri2_c = m;

			im = p1_tri2_vertex2;
			p1_tri2_vertex2 = p1_tri2_vertex3;
			p1_tri2_vertex3 = im;
		}
	}

	if ( p1_tri2_vertex2 * p1_tri2_vertex3 >= 0 && p1_tri2_vertex1 != 0 )
	{
		if ( p1_tri2_vertex1 < 0 )
		{
			m = tri1_b;
			tri1_b = tri1_c;
			tri1_c = m;
		}
	}
	else if ( p1_tri2_vertex1 * p1_tri2_vertex3 >= 0 && p1_tri2_vertex2 != 0 )
	{
		m = tri2_a;

		tri2_a = tri2_b;

		tri2_b = tri2_c;

		tri2_c = m;

		if ( p1_tri2_vertex2 < 0 )
		{
			m = tri1_b;
			tri1_b = tri1_c;
			tri1_c = m;
		}
	}
	else if ( p1_tri2_vertex1 * p1_tri2_vertex2 >= 0 && p1_tri2_vertex3 != 0 )
	{
		m = tri2_a;

		tri2_a = tri2_c;

		tri2_c = tri2_b;

		tri2_b = m;

		if ( p1_tri2_vertex3 < 0 )
		{
			m = tri1_b;
			tri1_b = tri1_c;
			tri1_c = m;
		}
	}

	if ( get_vector4_det( tri1_a, tri1_b, tri2_a, tri2_b ) <= 0 && get_vector4_det( tri1_a, tri1_c, tri2_c, tri2_a ) <= 0 )
	{
		return INTERSECT;
	}
	else
	{
		return NONINTERSECT;
	}
}


参考资料:

[1]     空间三角形快速相交检测算法     邹益胜, 丁国富, 何邕, 许明恒( 西南交通大学 机械工程学院)

[2]     http://www.cnblogs.com/graphics/archive/2010/08/05/1793393.html

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值