3D打印技术之切片引擎(3)

版权声明:转载本博客原创文章,请标明出处并附上链接。 https://blog.csdn.net/fourierFeng/article/details/44004459

【此系列文章基于熔融沉积( fused depostion modeling, FDM )成形工艺

从这一篇文章开始,我讲一下实体切片方面的一些技术。

切片引擎,实体部分大致包括:

提取边界矢量——>添加多边——>生成填充矢量集合。

其中生成填充矢量集合是整个切片引擎技术的核心,因为衡量一款引擎的好坏的四个核心要素——稳固性;与原始模型的相似度;用了多少材料;打印快慢,都是主要取决于填充矢量的生成技术。目前生成填充矢量的算法还有很多未知的问题有待攻克,对于这一块技术我将在最后与大家讨论,在这一篇文章里,我主要的说一下提取边界矢量的技术。

提取边界矢量,就是在对模型分层的时候,获得模型与某一层(z平面)相交的矢量集合,并且以特定顺序(一般规定顺着Z轴负方向看为正向)首尾相接。

其中难点主要是要所有的层都以特定顺序首尾相接,看下图:


这是某一模型在skeinforge下生成的某一层的矢量集合。看它的边界矢量,就是如上所述,按照某一特定顺序,首尾相接。

求三角面片和z平面的交线是高中解析几何的知识,这里只强调一点(很重要的一点):当切到某个三角面片与z轴垂直的时候(也就是三角面片与z平面重合的情况),该如何办?尤其是有些模型同一个z平面上会有很多个三角面片。用算法也可以解决,但是很麻烦,而且增加了很多逻辑上不好的代码,降低了代码的可读性。这时候一个较好的解决方案就是避开,因为这种情况毕竟是极少数,也就是说机器在对模型分层的时候,恰巧切到了这一含有三角面片的层。我们需要假设模型是光滑的,即三角面片之间是近似于光滑过渡的(趋近于光滑流形),也就是说,每个互连的三角面片的夹角尽可能的接近于180度,这是一个良好的3D模型所必须具备的。应用微分学原理,可以在这一层沿z轴方向的上面或者下面很小的距离取一层来近似的代替它。这样这个棘手的问题就得到了较好的解决。

首尾相接这一块只需略懂数据结构就非常容易做到的。在这里就不赘述了。

接下来我就重点说一下如何让所有的交线按照某一特定方向来生成边界矢量

算法步骤如下:

第一步,求出来的三角面片和z平面交线段的两个端点:beginPoint和endPoint,把它假设成为我们想要的符合特定顺序的矢量:phasor。开始节点:beginPoint,结束节点:endPoint。

第二步,就是判断该矢量是否符合我们的要求的,如果不符合就对它做反转。

步骤如下:

1,做beginPoint和endPoint的差vectorDiff。

2,对vectorDiff和该三角面片的法向量normal做叉积cross。

3,beginPoint和cross相加得到vectorAdd。

4,做三个向量(1,1,z),(1,0,z),(0,1,z).这三个向量的第三个元素都是z,要保证线性无关(三个线性无关的向量确定一个平面)。

5,对这三个向量以及vectorAdd做四点行列式,如果行列式的值小于0,反转,否则不反转。

可能对于初学者,四点行列式有些难以理解,对此,请参看我的另一篇博文: Devillers & Guigue算法

为什么要这样做,有一点点算法功底的同学容易理解,这样做的目的主要是充分利用法向量来判断二维矢量的合理方向,因为对传统的多边形面片离散化的3D模型,法向量唯一标识3D模型面的方向,要使得提取的边界矢量方向规范就必须充分利用法向量信息。

至于我所做的三个向量,是为了标定z平面,只需找到这个平面三个不相关的向量就可以了。

讲这么多,上述算法应该就不难理解了。

下面呈上该算法的代码。

//矢量
struct Phasor
{
	float3 beginPoint,endPoint;

	int status;
	
	int material;

	int tri_index;

	void reversal()
	{
		float3 temp;

		memcpy(temp,beginPoint,sizeof(float)*3);

		memcpy(beginPoint,endPoint,sizeof(float)*3);

		memcpy(endPoint,temp,sizeof(float)*3);
	}

	void copy(Phasor *p)
	{
		memcpy(beginPoint,p->beginPoint,sizeof(float)*3);

		memcpy(endPoint,p->endPoint,sizeof(float)*3);

		status=p->status;

		material=p->material;

		tri_index=p->tri_index;
	}
};

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];
}

void cross_product(float3 &result,float3 a, float3 b)
{
	result[0] = a[1]*b[2] - a[2]*b[1];
	result[1] = a[2]*b[0] - a[0]*b[2];
	result[2] = a[0]*b[1] - a[1]*b[0];
}


void get_vector_sum( float3& aimV, const float3 a, const float3 b )
{
    aimV[0] = a[0] + b[0];
	
    aimV[1] = a[1] + b[1];
	
    aimV[2] = a[2] + b[2];
}

//获得四点的行列式
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];
}




/// <summary>   
/// 如果边界矢量不符合要求,改变矢量方向
/// </summary>     
/// <param name="phasor">矢量</param> 
/// <param name="normal">法向</param>
/// <param name="z">z坐标</param>
void STLDelamination::rectifyDirection(Phasor *phasor,float3 normal,float z)
{
	float3 cross;

	float3 vectorDiff;

	float3 vectorAdd;

	float3 a,b,c,temp;

	a[0]=1;
	a[1]=1;
	a[2]=z;

	b[0]=1;
	b[1]=0;
	b[2]=z;

	c[0]=0;
	c[1]=1;
	c[2]=z;

	get_vector_diff(vectorDiff,phasor->beginPoint,phasor->endPoint);

	cross_product(cross,normal,vectorDiff);

	get_vector_sum(vectorAdd,phasor->beginPoint,cross);


	if(get_vector4_det(a, b, c, vectorAdd )<0)
	{
		memcpy(temp,phasor->beginPoint,sizeof(float)*3);
		
		memcpy(phasor->beginPoint,phasor->endPoint,sizeof(float)*3);
		
		memcpy(phasor->endPoint,temp,sizeof(float)*3);
	}
}






猜你喜欢

转载自blog.csdn.net/fourierFeng/article/details/44004459