【类简介】
该类主要是计算两个输入面定义的容器的并、交、差运算的边缘,需要输入面是流形的,不然结果不可预料。
第一个输出是结果面
第二个输出是两个输入面的交线
具体内容可参考: "Boolean Operations on Surfaces in VTK Without External Libraries" by Cory Quammen, Chris Weigle C., Russ Taylor
【操作类型】
VTK_UNION | |
VTK_INTERSECTION | |
VTK_DIFFERENCE |
【缺陷 limitations】
该类不能很好的处理三角面片共面的情况,应对复杂场景,不适用。
【示例】
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
vtkSmartPointer input1;
vtkSmartPointer input2;
std::string operation("intersection");
if (argc == 4)
{
vtkSmartPointer reader1 =
vtkSmartPointer::New();
reader1->SetFileName(argv[1]);
reader1->Update();
input1 = reader1->GetOutput();
vtkSmartPointer reader2 =
vtkSmartPointer::New();
reader2->SetFileName(argv[3]);
reader2->Update();
input2 = reader2->GetOutput();
operation = argv[2];
}
else
{
vtkSmartPointer sphereSource1 =
vtkSmartPointer::New();
sphereSource1->SetCenter(.25, 0, 0);
sphereSource1->Update();
input1 = sphereSource1->GetOutput();
vtkSmartPointer sphereSource2 =
vtkSmartPointer::New();
sphereSource2->Update();
input2 = sphereSource2->GetOutput();
if (argc == 2)
{
operation = argv[1];
}
}
vtkSmartPointer input1Mapper =
vtkSmartPointer::New();
#if VTK_MAJOR_VERSION <= 5
input1Mapper->SetInputConnection( input1->GetProducerPort() );
#else
input1Mapper->SetInputData( input1 );
#endif
input1Mapper->ScalarVisibilityOff();
vtkSmartPointer input1Actor =
vtkSmartPointer::New();
input1Actor->SetMapper( input1Mapper );
input1Actor->GetProperty()->SetColor(1,0,0);
input1Actor->SetPosition(
input1->GetBounds()[1]-input1->GetBounds()[0],
0, 0);
vtkSmartPointer input2Mapper =
vtkSmartPointer::New();
#if VTK_MAJOR_VERSION <= 5
input2Mapper->SetInputConnection( input2->GetProducerPort() );
#else
input2Mapper->SetInputData( input2 );
#endif
input2Mapper->ScalarVisibilityOff();
vtkSmartPointer input2Actor =
vtkSmartPointer::New();
input2Actor->SetMapper( input2Mapper );
input2Actor->GetProperty()->SetColor(0,1,0);
input2Actor->SetPosition(
-(input2->GetBounds()[1]-input2->GetBounds()[0]),
0, 0);
vtkSmartPointer booleanOperation =
vtkSmartPointer::New();
if (operation == "union")
{
booleanOperation->SetOperationToUnion();
}
else if (operation == "intersection")
{
booleanOperation->SetOperationToIntersection();
}
else if (operation == "difference")
{
booleanOperation->SetOperationToDifference();
}
else
{
std::cout << "Unknown operation: " << operation << std::endl;
return EXIT_FAILURE;
}
#if VTK_MAJOR_VERSION <= 5
booleanOperation->SetInputConnection( 0, input1->GetProducerPort() );
booleanOperation->SetInputConnection( 1, input2->GetProducerPort() );
#else
booleanOperation->SetInputData( 0, input1 );
booleanOperation->SetInputData( 1, input2 );
#endif
vtkSmartPointer booleanOperationMapper =
vtkSmartPointer::New();
booleanOperationMapper->SetInputConnection( booleanOperation->GetOutputPort() );
booleanOperationMapper->ScalarVisibilityOff();
vtkSmartPointer booleanOperationActor =
vtkSmartPointer::New();
booleanOperationActor->SetMapper( booleanOperationMapper );
vtkSmartPointer renderer =
vtkSmartPointer::New();
renderer->AddViewProp(input1Actor);
renderer->AddViewProp(input2Actor);
renderer->AddViewProp(booleanOperationActor);
renderer->SetBackground(.1, .2, .3);
vtkSmartPointer renderWindow =
vtkSmartPointer::New();
renderWindow->AddRenderer( renderer );
vtkSmartPointer renWinInteractor =
vtkSmartPointer::New();
renWinInteractor->SetRenderWindow( renderWindow );
renderWindow->Render();
renWinInteractor->Start();
return EXIT_SUCCESS;
}
【类内部实现分析】
int vtkBooleanOperationPolyDataFilter::RequestData(vtkInformation* vtkNotUsed(request),
vtkInformationVector** inputVector,
vtkInformationVector* outputVector)
{
vtkInformation* inInfo0 = inputVector[0]->GetInformationObject(0);
vtkInformation* inInfo1 = inputVector[1]->GetInformationObject(0);
vtkInformation* outInfo0 = outputVector->GetInformationObject(0);
vtkInformation* outInfo1 = outputVector->GetInformationObject(1);
if (!inInfo0 || !inInfo1 || !outInfo0 || !outInfo1)
{
return 0;
}
vtkPolyData* input0 =
vtkPolyData::SafeDownCast(inInfo0->Get(vtkDataObject::DATA_OBJECT()));
vtkPolyData* input1 =
vtkPolyData::SafeDownCast(inInfo1->Get(vtkDataObject::DATA_OBJECT()));
vtkPolyData* outputSurface =
vtkPolyData::SafeDownCast(outInfo0->Get(vtkDataObject::DATA_OBJECT())); // 输出1: 输出面
vtkPolyData* outputIntersection =
vtkPolyData::SafeDownCast(outInfo1->Get(vtkDataObject::DATA_OBJECT())); // 输出2; 相交线
if (!input0 || !input1 || !outputSurface || !outputIntersection) // 无效指针的检查
{
return 0;
}
// Get intersected versions
vtkSmartPointer PolyDataIntersection =
vtkSmartPointer::New();
PolyDataIntersection->SetInputConnection
(0, this->GetInputConnection(0, 0));
PolyDataIntersection->SetInputConnection
(1, this->GetInputConnection(1, 0));
PolyDataIntersection->SplitFirstOutputOn();
PolyDataIntersection->SplitSecondOutputOn();
PolyDataIntersection->Update();
outputIntersection->CopyStructure(PolyDataIntersection->GetOutput());
outputIntersection->GetPointData()->PassData(PolyDataIntersection->GetOutput()->GetPointData());
outputIntersection->GetCellData()->PassData(PolyDataIntersection->GetOutput()->GetCellData());
// Compute distances
vtkSmartPointer PolyDataDistance =
vtkSmartPointer::New();
PolyDataDistance->SetInputConnection
(0, PolyDataIntersection->GetOutputPort( 1 )); // 求相交线的第二个输出,即第一个输入
PolyDataDistance->SetInputConnection
(1, PolyDataIntersection->GetOutputPort( 2 )); // 求相交线的第三个输出,即第二个输入
PolyDataDistance->ComputeSecondDistanceOn(); // 计算第二个输入上每个点到第一个输入的有向距离
PolyDataDistance->Update();
vtkPolyData* pd0 = PolyDataDistance->GetOutput(); // 第一个输入和对应的scalar
vtkPolyData* pd1 = PolyDataDistance->GetSecondDistanceOutput(); // a copy of the second input with an additional distance scalar field
pd0->BuildCells();
pd0->BuildLinks();
pd1->BuildCells();
pd1->BuildLinks();
// Set up field lists of both points and cells that are shared by
// the input data sets.
vtkDataSetAttributes::FieldList pointFields(2);
pointFields.InitializeFieldList( pd0->GetPointData() );
pointFields.IntersectFieldList( pd1->GetPointData() );
vtkDataSetAttributes::FieldList cellFields(2);
cellFields.InitializeFieldList( pd0->GetCellData() );
cellFields.IntersectFieldList( pd1->GetCellData() );
// Sort union/intersection.
vtkSmartPointer< vtkIdList > interList = vtkSmartPointer< vtkIdList >::New();
vtkSmartPointer< vtkIdList > unionList = vtkSmartPointer< vtkIdList >::New();
this->SortPolyData(pd0, interList, unionList); // 面1上的点在面2内, 面1上的点在面2外
outputSurface->Allocate(pd0);
outputSurface->GetPointData()->CopyAllocate(pointFields);
outputSurface->GetCellData()->CopyAllocate(cellFields);
if ( this->Operation == VTK_UNION || this->Operation == VTK_DIFFERENCE )
{
this->CopyCells(pd0, outputSurface, 0, pointFields, cellFields, unionList, false); // 面1在面2外的点
}
else if ( this->Operation == VTK_INTERSECTION )
{
this->CopyCells(pd0, outputSurface, 0, pointFields, cellFields, interList, false); // 面1在面2内的点
}
// Label sources for each point and cell.
vtkSmartPointer< vtkIntArray > pointSourceLabel = vtkSmartPointer< vtkIntArray >::New();
pointSourceLabel->SetNumberOfComponents(1);
pointSourceLabel->SetName("PointSource");
pointSourceLabel->SetNumberOfTuples(outputSurface->GetNumberOfPoints());
for (vtkIdType ii = 0; ii < outputSurface->GetNumberOfPoints(); ii++)
{
pointSourceLabel->InsertValue(ii, 0);
}
vtkSmartPointer< vtkIntArray > cellSourceLabel = vtkSmartPointer< vtkIntArray >::New();
cellSourceLabel->SetNumberOfComponents(1);
cellSourceLabel->SetName("CellSource");
cellSourceLabel->SetNumberOfValues(outputSurface->GetNumberOfCells());
for (vtkIdType ii = 0; ii < outputSurface->GetNumberOfCells(); ii++)
{
cellSourceLabel->InsertValue(ii, 0);
}
interList->Reset();
unionList->Reset();
this->SortPolyData(pd1, interList, unionList); // 面2上的点在面1内, 面2上的点在面1外
if ( this->Operation == VTK_UNION )
{
this->CopyCells(pd1, outputSurface, 1, pointFields, cellFields, unionList, false); // 面2上的点在面1外
}
else if ( this->Operation == VTK_INTERSECTION || this->Operation == VTK_DIFFERENCE ) // 面2上的点在面1内,如果是差会有不同的情况
{
this->CopyCells(pd1, outputSurface, 1, pointFields, cellFields, interList,
(this->ReorientDifferenceCells == 1 &&
this->Operation == VTK_DIFFERENCE));
}
vtkIdType i;
i = pointSourceLabel->GetNumberOfTuples();
pointSourceLabel->Resize(outputSurface->GetNumberOfPoints());
for ( ; i < outputSurface->GetNumberOfPoints(); i++)
{
pointSourceLabel->InsertValue(i, 1);
}
i = cellSourceLabel->GetNumberOfTuples();
cellSourceLabel->Resize(outputSurface->GetNumberOfCells());
for ( ; i < outputSurface->GetNumberOfCells(); i++)
{
cellSourceLabel->InsertValue(i, 1);
}
outputSurface->GetPointData()->AddArray(pointSourceLabel);
outputSurface->GetCellData()->AddArray(cellSourceLabel);
outputSurface->Squeeze();
outputSurface->GetPointData()->Squeeze();
outputSurface->GetCellData()->Squeeze();
return 1;
}
// Labels triangles in mesh as part of the intersection or union surface.
// 标签化 相交或者并面的部分三角形
void vtkBooleanOperationPolyDataFilter::SortPolyData(vtkPolyData* input,
vtkIdList* interList,
vtkIdList* unionList)
{
int numCells = input->GetNumberOfCells();
vtkDoubleArray *distArray = vtkDoubleArray::SafeDownCast
( input->GetCellData()->GetArray("Distance") );
for (int cid = 0; cid < numCells; cid++)
{
if ( distArray->GetValue( cid ) > this->Tolerance )
{
unionList->InsertNextId( cid ); // 点在面外
}
else
{
interList->InsertNextId( cid ); // 点在面内
}
}
}