Mesh类支持形式化拓扑的表示。特别地,k复形的概念可以在网格中正确地表示。
k复形是一种拓扑结构,其中对于每一个维数N的单元格,其边界面为维数N−1的单元格也属于该结构。
下面演示如何使用网格实例化K-Complex结构。
- 该实例结构由一个四面体、四个三角形面、六个线边和四个顶点组成
#include "itkMesh.h"
#include "itkVertexCell.h"
#include "itkLineCell.h"
#include "itkTriangleCell.h"
#include "itkTetrahedronCell.h"
//定义PixelType并实例化网格类型。
//注意,在这种情况下,空间的维度是3。
typedef float PixelType;
typedef itk::Mesh< PixelType, 3 > MeshType;
//现在可以使用从网格中获取的特征实例化单元格类型
typedef MeshType::CellType CellType;
typedef itk::VertexCell< CellType > VertexType;
typedef itk::LineCell< CellType > LineType;
typedef itk::TriangleCell< CellType > TriangleType;
typedef itk::TetrahedronCell< CellType > TetrahedronType;
//网格被创建,与顶点相关联的点被插入。
//正四面体节点的几何坐标可以通过从正立方体中取出每一个节点来得到。
MeshType::Pointer mesh = MeshType::New();
MeshType::PointType point0;
MeshType::PointType point1;
MeshType::PointType point2;
MeshType::PointType point3;
point0[0] = -1; point0[1] = -1; point0[2] = -1;
point1[0] = 1; point1[1] = 1; point1[2] = -1;
point2[0] = 1; point2[1] = -1; point2[2] = 1;
point3[0] = -1; point3[1] = 1; point3[2] = 1;
mesh->SetPoint( 0, point0 ); mesh->SetPoint( 1, point1 ); mesh->SetPoint( 2, point2 ); mesh->SetPoint( 3, point3 );
/*
注意,网格中的点和itk::VertexCell概念之间有一个重要的区别。
VertexCell是维度为零的单元格。它与点的主要区别在于,cell可以感知到与其他cell的邻域关系。
而点不知道cell的存在。事实上,从纯粹的拓扑角度来看,网格中点的坐标是完全无关的。它们也可能完全不存在于网格结构中。
VertexCells对于表示k -复形上有邻域关系的完整集合是必要的。
*/
//现在我们继续创建单元格,将它们与点关联起来,并将它们插入网格中。
//从四面体开始,我们编写以下代码
CellType::CellAutoPointer cellpointer;
cellpointer.TakeOwnership( new TetrahedronType );
cellpointer->SetPointId( 0, 0 );
cellpointer->SetPointId( 1, 1 );
cellpointer->SetPointId( 2, 2 );
cellpointer->SetPointId( 3, 3 );
mesh->SetCell( 0, cellpointer );
//现在创建了四个三角形面并与网格相关联。第一个三角形连接点0,1,2。
cellpointer.TakeOwnership( new TriangleType );
cellpointer->SetPointId( 0, 0 );
cellpointer->SetPointId( 1, 1 );
cellpointer->SetPointId( 2, 2 );
mesh->SetCell( 1, cellpointer );
//第二个三角形连接点0,2,3
cellpointer.TakeOwnership( new TriangleType );
cellpointer->SetPointId( 0, 0 );
cellpointer->SetPointId( 1, 2 );
cellpointer->SetPointId( 2, 3 );
mesh->SetCell( 2, cellpointer );
//第三个
cellpointer.TakeOwnership( new TriangleType );
cellpointer->SetPointId( 0, 0 );
cellpointer->SetPointId( 1, 3 );
cellpointer->SetPointId( 2, 1 );
mesh->SetCell( 3, cellpointer );
//第四个三角形连接点3,2,1
cellpointer.TakeOwnership( new TriangleType );
cellpointer->SetPointId( 0, 3 );
cellpointer->SetPointId( 1, 2 );
cellpointer->SetPointId( 2, 1 );
mesh->SetCell( 4, cellpointer );
/*注意每次CellAutoPointer是如何重用的。
提醒:当itk::AutoPointer作为SetCell()方法的参数传递时,它失去了对单元格的所有权。
AutoPointer通过使用TakeOwnership()方法连接到一个新的单元格。*/
//现在继续构建K-Complex,在四面体的边缘上创建六条线。
cellpointer.TakeOwnership( new LineType );
cellpointer->SetPointId( 0, 0 );
cellpointer->SetPointId( 1, 1 );
mesh->SetCell( 5, cellpointer );
cellpointer.TakeOwnership( new LineType );
cellpointer->SetPointId( 0, 1 );
cellpointer->SetPointId( 1, 2 );
mesh->SetCell( 6, cellpointer );
cellpointer.TakeOwnership( new LineType );
cellpointer->SetPointId( 0, 2 );
cellpointer->SetPointId( 1, 0 );
mesh->SetCell( 7, cellpointer );
cellpointer.TakeOwnership( new LineType );
cellpointer->SetPointId( 0, 1 );
cellpointer->SetPointId( 1, 3 );
mesh->SetCell( 8, cellpointer );
cellpointer.TakeOwnership( new LineType );
cellpointer->SetPointId( 0, 3 );
cellpointer->SetPointId( 1, 2 );
mesh->SetCell( 9, cellpointer );
cellpointer.TakeOwnership( new LineType );
cellpointer->SetPointId( 0, 3 );
cellpointer->SetPointId( 1, 0 );
mesh->SetCell( 10, cellpointer );
//最后,创建并插入由itk::VertexCell表示的零维单元格
cellpointer.TakeOwnership( new VertexType );
cellpointer->SetPointId( 0, 0 );
mesh->SetCell( 11, cellpointer );
cellpointer.TakeOwnership( new VertexType );
cellpointer->SetPointId( 0, 1 );
mesh->SetCell( 12, cellpointer );
cellpointer.TakeOwnership( new VertexType );
cellpointer->SetPointId( 0, 2 );
mesh->SetCell( 13, cellpointer );
cellpointer.TakeOwnership( new VertexType );
cellpointer->SetPointId( 0, 3 );
mesh->SetCell( 14, cellpointer );
此时网格包含4个点和从0到14枚举的15个单元格。
- 可以使用PointContainer迭代器访问这些点
typedef MeshType::PointsContainer::ConstIterator PointIterator;
PointIterator pointIterator = mesh->GetPoints()->Begin();
PointIterator pointEnd = mesh->GetPoints()->End();
while( pointIterator != pointEnd )
{
std::cout << pointIterator.Value() << std::endl;
++pointIterator;
}
- 可以使用CellsContainer迭代器访问单元格
typedef MeshType::CellsContainer::ConstIterator CellIterator;
CellIterator cellIterator = mesh->GetCells()->Begin();
CellIterator cellEnd = mesh->GetCells()->End();
while( cellIterator != cellEnd )
{
CellType * cell = cellIterator.Value();
std::cout << cell->GetNumberOfPoints() << std::endl;
++cellIterator;
}
请注意,单元格存储为指向泛型单元格类型的指针,泛型单元格类型是所有特定单元格类的基类。
- 这意味着在这个级别上我们只能访问CellType中定义的虚拟方法。
可以使用CellType特征中定义的迭代器访问与单元格关联的点标识符。
下面的代码演示了PointIdIterators的使用。PointIdsBegin()方法将迭代器返回到单元格中的第一个点标识符。PointIdsEnd()方法将迭代器返回到单元格中的过期结束点标识符:
typedef CellType::PointIdIterator PointIdIterator;
PointIdIterator pointIditer = cell->PointIdsBegin();
PointIdIterator pointIdend = cell->PointIdsEnd();
while( pointIditer != pointIdend )
{
std::cout << *pointIditer << std::endl;
++pointIditer;
}
到这里为止,k复形的拓扑还没有完全定义,因为我们只引入了单元格。ITK允许用户显式地定义单元格之间的睦邻关系。很明显,对点标识符的巧妙探索可以让用户找出邻居关系。
- 例如,共享相同的两个点标识符的两个三角形单元格可能是相邻单元格。这种邻域关系的隐式发现的一些缺点是,它需要计算时间,而且一些应用程序可能不接受同样的假设。
在网格中,邻域关系用边界特征的概念表示。
每个cell都有一个单元标识符的内部列表,指向被认为是它的邻居的其他cell。边界特征按维数进行分类。
- 例如,一条线有两个维度为零的边界特征,对应于它的两个顶点。一个四面体将具有维度为0、1和2的边界特征,对应于它的四个顶点、六个边和四个三角形面。由用户指定单元格之间的连接。
我们以当前的例子为例,将与单元标识符0相关联的四面体单元格分配给它四个顶点作为零维度的边界。这是通过调用网格类上的SetBoundaryAssignment(()方法来完成的:
MeshType::CellIdentifier cellId = 0; // the tetrahedron
int dimension = 0; // vertices
MeshType::CellFeatureIdentifier featureId = 0;
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 11 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 12 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 13 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 14 );
featureId只是一个与特定单元中相同维度的边界单元序列相关联的数字。
- 例如,一个四面体的零维特征是它的四个顶点。然后,此单元格的零维特性id的范围将从0到3。四面体的一维特征是它的六个边,因此它的一维特征id将从0到5。四面体的二维特征是它的四个三角形面。二维特征id的范围将从0到3。
下表总结:
现在让我们指定这个四面体的一维边界特征。
-
这些是具有标识符{5、6、7、8、9、10}的行单元格。
-
注意,特性标识符被重新初始化为零,因为计数独立于每个维度。
cellId = 0; // still the tetrahedron
dimension = 1; // one-dimensional features = edges
featureId = 0; // reinitialize the count
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 5 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 6 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 7 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 8 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 9 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 10 );
最后确定四面体的二维边界特征。
- 这是四个三角形单元格,标识符为{1,2,3,4}。featureId被重置为零,因为特性id独立于每个维度。
cellId = 0; // still the tetrahedron
dimension = 2; // two-dimensional features = triangles
featureId = 0; // reinitialize the count
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 1 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 2 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 3 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 4 );
此时,我们可以查询四面体单元格的边界特征信息。例如,可以通过getnumberofborderyfeatures()方法得到每个维度的边界特征个数:
cellId = 0; // still the tetrahedron
MeshType::CellFeatureCount n0; // number of zero-dimensional features
MeshType::CellFeatureCount n1; // number of one-dimensional features
MeshType::CellFeatureCount n2; // number of two-dimensional features
n0 = mesh->GetNumberOfCellBoundaryFeatures( 0, cellId );
n1 = mesh->GetNumberOfCellBoundaryFeatures( 1, cellId );
n2 = mesh->GetNumberOfCellBoundaryFeatures( 2, cellId );
边界分配可以通过 GetBoundaryAssigment()方法重新获得。
例如,可以通过以下代码获得四面体的零维特征:
dimension = 0;
for(unsigned int b0=0; b0 < n0; b0++)
{
MeshType::CellIdentifier id;
bool found = mesh->GetBoundaryAssignment( dimension, cellId, b0, &id );
if( found ) std::cout << id << std::endl; }
//下面的代码演示了如何为一个三角形面设置边缘边界。
cellId = 2; // one of the triangles
dimension = 1; // boundary edges
featureId = 0; // start the count of features
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 7 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 9 );
mesh->SetBoundaryAssignment( dimension, cellId, featureId++, 10 );