布尔运算介绍
布尔运算是英国数学家布尔在1847年发明的处理二值关系的逻辑数学计算法,主要包括联合(Union),相交(Intersection),相减(subtraction)。直接用图说明,及时逻辑运算并交差。对于程序自动生成的数据比如小球,圆柱等布尔运算有着比较好的结果,但是对于自己要用到的实验数据,那么问题就很多了,比如两个数据的原始位置不一样,而布尔运算读取的是数据的原始位置。
比如还有就是布尔运算是对两个封闭的曲面进行操作的,当出现开放曲面以及不规则曲面的时候还需要涉及到曲面插值等,还有一个需要注意的点就是一定要搞清楚vtkTransform和actor->setPosition的关系,这个在我后面的博客里面有讲到。下面贴的代码就是两个stl数据进行布尔运算的代码,网上很少有,自己也是弄了好长时间,希望能给你们一些帮助啦!!!
布尔运算的步骤:
布尔运算的实现步骤主要如下:
(1)求取两个模型的相交线;
(2)根据交线分割含有交线的三角形;
(3)根据布尔运算的种类(并集,差集,或者交集),取舍两个模型上的三
角面元,形成最终的结果模型。
(1)求相交线
第一步需要求两个模型的相交线。这涉及到求一个三角面片模型上每一个的三角面元与另一个模型上所有面元的交线。对meshA上的一个三角面片来说,meshB上一般只有非常少量的面片可能与它相交,所以为了剔除许多不必要的三角面片的相交判断,VTK对每个三角面片模型均建立一个空间复合OBBTree[301。OBB(Oriented Bounding Box)是指层次包围盒,他由Gottschalk,Lin:和Manocha在计算层级表示算法中用到‘14]。它把空间分为大小不同的层次包围盒,通过建立OBB Tree,可以实现空间中点、面元的快速定位。使用VTK中提供的vtkOBBTree对象,对meshA和meshB各建立一个vtkOBBTree对象。在做三角面元的相交判断时,只对有相交部分的包围盒中的三角面元做相交测试,这样就去除了很多不必要的判断。
第一步:计算T1和T2的法向量,在封闭模型中,法向所指向的是模型的外部。
第二步:判断T1上所有顶点与T2所在平面的位置关系:假设p1是T1上的一点,p2是T2上的一点,计算从pl指向p2的向量面,计算这个向量与T2的法向量砸的关系,如果p21与菘的角度大于90度,则定义pl在T2的外部,p1到T2的有符号距离为负值,反之如果面与菘的角度小于90度,则定义pl在T2的内部,到T2的有符号距离为正值。最后,如果面与冱垂直,则p2在T2所在平面内,距离为0。同样的方法计算T2上所有顶点到Tl所在平面的有符号距离。
第三步:如果Tl或者T2的所有顶点的有符号距离都是同号,则一个三角形
在另一个三角形的同侧,不存在相交。
第四步:计算Tl上顶点到T2平面的有符号距离的准确值
第五步:取出一对有符号距离的符号不同的点
第六步:在计算出三条边与另一个三角形的相交情况后,我们会得到两个交点。把这两个点相连,截取这个连线在三角形内部的位置。
(2)重新剖分三角面元
在计算出交线后,根据交线重新分割有交线的三角形。这一步可以利用
Delaunay三角剖分来实现。前苏联数学家Delaunay在1934年提出Delaunay三角
剖分,它是平面上点集的最优三角剖分,尽量避免了过于狭长和尖锐的三角形的
出现,即所有的三角形都满足“最大一最小角”优化准则。标准的Delaunay三角剖
分算法已经发展很成熟,代表性的有:分割一归并算法,三角网格生长算法等等。
在本文的应用情形下,需要带有约束边的Delaunay三角网格剖分。
VTK提供vtkDelaunay2D实现三角形的重新剖分。首先将一个三角面元
vtkCell旋转到XY平面,然后利用一个vtkDelaunay2D对象进行处理
(3)三角面元的取舍
在经过前面的步骤处理后,对于meshA或者meshB上的任何三角面元,它
要么完全在另一个模型的内部,要么完全在外部。根据布尔操作的类型,要过滤
出最终结果需要的三角面元,组成一个新的模型。
#include <vtkActor.h>
#include <vtkBooleanOperationPolyDataFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkSTLReader.h>
vtkActor* GetBooleanOperationActor( double x, int operation )
{
//读取stl数据
std::string inputFilename1 = "a.stl";
vtkSmartPointer<vtkSTLReader> reader1 = vtkSmartPointer<vtkSTLReader>::New();
reader1->SetFileName(inputFilename1.c_str());
reader1->Update();
std::string inputFilename2 = "b.stl";
vtkSmartPointer<vtkSTLReader> reader2 = vtkSmartPointer<vtkSTLReader>::New();
reader2->SetFileName(inputFilename2.c_str());
reader2->Update();
vtkSmartPointer<vtkPolyDataMapper> mapper1 = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper1->SetInputConnection(reader2->GetOutputPort());
vtkSmartPointer<vtkActor> actor1 = vtkSmartPointer<vtkActor>::New();
actor1->SetMapper(mapper1);
actor1->SetPosition(-19, + 3.5, - 19);
vtkSmartPointer<vtkBooleanOperationPolyDataFilter> boolFilter = vtkSmartPointer<vtkBooleanOperationPolyDataFilter>::New();
boolFilter->SetOperation(operation);
boolFilter->SetInputConnection(0, reader1->GetOutputPort());//boolFilter的输入是reader1和reader2的输出
boolFilter->SetInputConnection(1, reader2->GetOutputPort());
/*
double centerSeparation = 0.20;
vtkSmartPointer<vtkSphereSource> sphere1 = vtkSmartPointer<vtkSphereSource>::New();//数据1
sphere1->SetCenter(-centerSeparation + x, 0.0, 0.0);//设置位置
vtkSmartPointer<vtkSphereSource> sphere2 = vtkSmartPointer<vtkSphereSource>::New();//数据2
sphere2->SetCenter( centerSeparation + x, 0.0, 0.0);
vtkSmartPointer<vtkBooleanOperationPolyDataFilter> boolFilter =vtkSmartPointer<vtkBooleanOperationPolyDataFilter>::New();
boolFilter->SetOperation( operation );
boolFilter->SetInputConnection( 0, sphere1->GetOutputPort() );//boolFilter的输入是sphere1和sphere2的输出
boolFilter->SetInputConnection( 1, sphere2->GetOutputPort() );
*/
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();//将输入数据渲染多边形的几何数据
mapper->SetInputConnection( boolFilter->GetOutputPort( 0 ) );//mapper的输入是boolFilter的输出
mapper->ScalarVisibilityOff();
vtkActor *actor = vtkActor::New();
actor->SetMapper( mapper );
return actor;
}
int main(int argc, char* argv[])
{
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();//管理渲染过程
vtkSmartPointer<vtkRenderWindow> renWin = vtkSmartPointer<vtkRenderWindow>::New();//设置窗体
renWin->AddRenderer( renderer );
vtkSmartPointer<vtkRenderWindowInteractor> renWinInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renWinInteractor->SetRenderWindow( renWin );//设置鼠标,键盘监听
vtkActor *unionActor = GetBooleanOperationActor(-2.0, vtkBooleanOperationPolyDataFilter::VTK_UNION);//并
renderer->AddActor( unionActor );//窗体增加并集对象
unionActor->Delete();
vtkActor *intersectionActor = GetBooleanOperationActor(0.0, vtkBooleanOperationPolyDataFilter::VTK_INTERSECTION);//交
renderer->AddActor( intersectionActor );//窗体增加交集对象
intersectionActor->Delete();
vtkActor *differenceActor = GetBooleanOperationActor(2.0, vtkBooleanOperationPolyDataFilter::VTK_DIFFERENCE);//差
renderer->AddActor( differenceActor );//窗体增加差集对象
differenceActor->Delete();
renWin->Render();
renWinInteractor->Start();//进入事件循环响应
return EXIT_SUCCESS;
}