ITK  5.4.0
Insight Toolkit
Examples/DataRepresentation/Mesh/MeshKComplex.cxx
/*=========================================================================
*
* Copyright NumFOCUS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*=========================================================================*/
// Software Guide : BeginLatex
//
// The \doxygen{Mesh} class supports the representation of formal
// topologies. In particular the concept of \emph{K-Complex} can be
// correctly represented in the Mesh. An informal definition of K-Complex
// may be as follows: a K-Complex is a topological structure in which for
// every cell of dimension $N$, its boundary faces (which are cells of
// dimension $N-1$) also belong to the structure.
//
// This section illustrates how to instantiate a K-Complex structure using
// the mesh. The example structure is composed of one tetrahedron, its four
// triangle faces, its six line edges and its four vertices.
//
// \index{itk::Mesh!K-Complex}
//
// Software Guide : EndLatex
// Software Guide : BeginLatex
//
// The header files of all the cell types involved should be loaded along
// with the header file of the mesh class.
//
// \index{itk::LineCell!header}
// \index{itk::VertexCell!header}
// \index{itk::TriangleCell!header}
// \index{itk::TetrahedronCell!header}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
#include "itkMesh.h"
#include "itkLineCell.h"
// Software Guide : EndCodeSnippet
int
main(int, char *[])
{
// Software Guide : BeginLatex
//
// Then the PixelType is defined and the mesh type is instantiated with it.
// Note that the dimension of the space is three in this case.
//
// \index{itk::Mesh!Instantiation}
// \index{itk::Mesh!PixelType}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
using PixelType = float;
using MeshType = itk::Mesh<PixelType, 3>;
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// The cell type can now be instantiated using the traits
// taken from the Mesh.
//
// \index{itk::LineCell!Instantiation}
// \index{itk::VertexCell!Instantiation}
// \index{itk::TriangleCell!Instantiation}
// \index{itk::TetrahedronCell!Instantiation}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
using CellType = MeshType::CellType;
using VertexType = itk::VertexCell<CellType>;
using LineType = itk::LineCell<CellType>;
using TriangleType = itk::TriangleCell<CellType>;
using TetrahedronType = itk::TetrahedronCell<CellType>;
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// The mesh is created and the points associated with the vertices are
// inserted. Note that there is an important distinction between the
// points in the mesh and the \doxygen{VertexCell} concept. A VertexCell
// is a cell of dimension zero. Its main difference as compared to a point
// is that the cell can be aware of neighborhood relationships with other
// cells. Points are not aware of the existence of cells. In fact, from
// the pure topological point of view, the coordinates of points in the
// mesh are completely irrelevant. They may as well be absent from the
// mesh structure altogether. VertexCells on the other hand are necessary
// to represent the full set of neighborhood relationships on the
// K-Complex.
//
// The geometrical coordinates of the nodes of a regular tetrahedron can be
// obtained by taking every other node from a regular cube.
//
// \index{itk::Mesh!New()}
// \index{itk::Mesh!SetPoint()}
// \index{itk::Mesh!PointType}
// \index{itk::Mesh!Pointer}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
auto mesh = MeshType::New();
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);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// We proceed now to create the cells, associate them with the points and
// insert them on the mesh. Starting with the tetrahedron we write the
// following code.
//
// \index{itk::AutoPointer!TakeOwnership()}
// \index{CellAutoPointer!TakeOwnership()}
// \index{CellType!creation}
// \index{itk::Mesh!SetCell()}
// \index{itk::TetrahedronCell!Instantiation}
// \index{itk::TetrahedronCell!SetPointId()}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
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);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// Four triangular faces are created and associated with the mesh now.
// The first triangle connects points {0,1,2}.
//
// \index{itk::TriangleCell!Instantiation}
// \index{itk::TriangleCell!SetPointId()}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
cellpointer.TakeOwnership(new TriangleType);
cellpointer->SetPointId(0, 0);
cellpointer->SetPointId(1, 1);
cellpointer->SetPointId(2, 2);
mesh->SetCell(1, cellpointer);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// The second triangle connects points { 0, 2, 3 }.
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
cellpointer.TakeOwnership(new TriangleType);
cellpointer->SetPointId(0, 0);
cellpointer->SetPointId(1, 2);
cellpointer->SetPointId(2, 3);
mesh->SetCell(2, cellpointer);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// The third triangle connects points { 0, 3, 1 }.
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
cellpointer.TakeOwnership(new TriangleType);
cellpointer->SetPointId(0, 0);
cellpointer->SetPointId(1, 3);
cellpointer->SetPointId(2, 1);
mesh->SetCell(3, cellpointer);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// The fourth triangle connects points { 3, 2, 1 }.
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
cellpointer.TakeOwnership(new TriangleType);
cellpointer->SetPointId(0, 3);
cellpointer->SetPointId(1, 2);
cellpointer->SetPointId(2, 1);
mesh->SetCell(4, cellpointer);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// Note how the \code{CellAutoPointer} is reused every time. Reminder: the
// \doxygen{AutoPointer} loses ownership of the cell when it is passed as
// an argument of the \code{SetCell()} method. The AutoPointer is attached
// to a new cell by using the \code{TakeOwnership()} method.
//
// The construction of the K-Complex continues now with the creation of the
// six lines on the tetrahedron edges.
//
// \index{itk::LineCell!Instantiation}
// \index{itk::LineCell!SetPointId()}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
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);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// Finally the zero dimensional cells represented by the
// \doxygen{VertexCell} are created and inserted in the mesh.
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
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);
// Software Guide : EndCodeSnippet
// Print out the number of points and the number of cells.
std::cout << "# Points= " << mesh->GetNumberOfPoints() << std::endl;
std::cout << "# Cell = " << mesh->GetNumberOfCells() << std::endl;
// Software Guide : BeginLatex
//
// At this point the Mesh contains four points and fifteen cells enumerated
// from 0 to 14. The points can be visited using PointContainer iterators.
//
// \index{itk::Mesh!PointsContainer}
// \index{itk::Mesh!PointsIterators}
// \index{itk::Mesh!GetPoints()}
// \index{PointsContainer!Begin()}
// \index{PointsContainer!End()}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
using PointIterator = MeshType::PointsContainer::ConstIterator;
PointIterator pointIterator = mesh->GetPoints()->Begin();
PointIterator pointEnd = mesh->GetPoints()->End();
while (pointIterator != pointEnd)
{
std::cout << pointIterator.Value() << std::endl;
++pointIterator;
}
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// The cells can be visited using CellsContainer iterators.
//
// \index{itk::Mesh!CellsContainer}
// \index{itk::Mesh!CellsIterators}
// \index{itk::Mesh!GetCells()}
// \index{CellsContainer!Begin()}
// \index{CellsContainer!End()}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
using CellIterator = MeshType::CellsContainer::ConstIterator;
CellIterator cellIterator = mesh->GetCells()->Begin();
CellIterator cellEnd = mesh->GetCells()->End();
while (cellIterator != cellEnd)
{
CellType * cell = cellIterator.Value();
std::cout << cell->GetNumberOfPoints() << std::endl;
++cellIterator;
}
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// Note that cells are stored as pointer to a generic cell type that is the
// base class of all the specific cell classes. This means that at this
// level we can only have access to the virtual methods defined in the
// \code{CellType}.
//
// The point identifiers to which the cells have been associated can be
// visited using iterators defined in the \code{CellType} trait. The
// following code illustrates the use of the PointIdIterators. The
// \code{PointIdsBegin()} method returns the iterator to the first
// point-identifier in the cell. The \code{PointIdsEnd()} method returns
// the iterator to the past-end point-identifier in the cell.
//
// \index{CellType!PointIdsBegin()}
// \index{CellType!PointIdsEnd()}
// \index{CellType!PointIdIterator}
// \index{PointIdIterator}
// \index{PointIdsBegin()}
// \index{PointIdsEnd()}
//
// Software Guide : EndLatex
cellIterator = mesh->GetCells()->Begin();
cellEnd = mesh->GetCells()->End();
while (cellIterator != cellEnd)
{
CellType * cell = cellIterator.Value();
std::cout << "cell with " << cell->GetNumberOfPoints();
std::cout << " points " << std::endl;
// Software Guide : BeginCodeSnippet
using PointIdIterator = CellType::PointIdIterator;
PointIdIterator pointIditer = cell->PointIdsBegin();
PointIdIterator pointIdend = cell->PointIdsEnd();
while (pointIditer != pointIdend)
{
std::cout << *pointIditer << std::endl;
++pointIditer;
}
// Software Guide : EndCodeSnippet
++cellIterator;
}
// Software Guide : BeginLatex
//
// Note that the point-identifier is obtained from the iterator using the
// more traditional \code{*iterator} notation instead the \code{Value()}
// notation used by cell-iterators.
//
// Software Guide : EndLatex
// Software Guide : BeginLatex
//
// Up to here, the topology of the K-Complex is not completely defined
// since we have only introduced the cells. ITK allows the user to define
// explicitly the neighborhood relationships between cells. It is clear
// that a clever exploration of the point identifiers could have allowed a
// user to figure out the neighborhood relationships. For example, two
// triangle cells sharing the same two point identifiers will probably be
// neighbor cells. Some of the drawbacks on this implicit discovery of
// neighborhood relationships is that it takes computing time and that some
// applications may not accept the same assumptions. A specific case is
// surgery simulation. This application typically simulates bistoury cuts
// in a mesh representing an organ. A small cut in the surface may be made
// by specifying that two triangles are not considered to be neighbors any
// more.
//
// Neighborhood relationships are represented in the mesh by the
// notion of \emph{BoundaryFeature}. Every cell has an internal list of
// cell-identifiers pointing to other cells that are considered to be its
// neighbors. Boundary features are classified by dimension. For example, a
// line will have two boundary features of dimension zero corresponding to
// its two vertices. A tetrahedron will have boundary features of dimension
// zero, one and two, corresponding to its four vertices, six edges and
// four triangular faces. It is up to the user to specify the connections
// between the cells.
//
// \index{BoundaryFeature}
// \index{CellBoundaryFeature}
// \index{itk::Mesh!BoundaryFeature}
//
// Let's take in our current example the tetrahedron cell that was
// associated with the cell-identifier \code{0} and assign to it the four
// vertices as boundaries of dimension zero. This is done by invoking the
// \code{SetBoundaryAssignment()} method on the Mesh class.
//
// \index{itk::Mesh!SetBoundaryAssignment()}
// \index{SetBoundaryAssignment()!itk::Mesh}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
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);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// The \code{featureId} is simply a number associated with the sequence of
// the boundary cells of the same dimension in a specific cell. For
// example, the zero-dimensional features of a tetrahedron are its four
// vertices. Then the zero-dimensional feature-Ids for this cell will range
// from zero to three. The one-dimensional features of the tetrahedron are
// its six edges, hence its one-dimensional feature-Ids will range from
// zero to five. The two-dimensional features of the tetrahedron are its
// four triangular faces. The two-dimensional feature ids will then range
// from zero to three. The following table summarizes the use on indices
// for boundary assignments.
//
// \begin{center}
// \begin{tabular}{ | c || c | c | c | }
// \hline
// Dimension & CellType & FeatureId range & Cell Ids \\ \hline\hline
// 0 & VertexCell & [0:3] & \{11,12,13,14\} \\ \hline
// 1 & LineCell & [0:5] & \{5,6,7,8,9,10\} \\ \hline
// 2 & TriangleCell & [0:3] & \{1,2,3,4\} \\ \hline
// \end{tabular}
// \end{center}
//
// In the code example above, the values of featureId range from zero to
// three. The cell identifiers of the triangle cells in this example are
// the numbers \{1,2,3,4\}, while the cell identifiers of the vertex cells
// are the numbers \{11,12,13,14\}.
//
// Let's now assign one-dimensional boundary features of the tetrahedron.
// Those are the line cells with identifiers \{5,6,7,8,9,10\}. Note that
// the feature identifier is reinitialized to zero since the count is
// independent for each dimension.
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
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);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// Finally we assign the two-dimensional boundary features of the
// tetrahedron. These are the four triangular cells with identifiers
// \{1,2,3,4\}. The featureId is reset to zero since feature-Ids are
// independent on each dimension.
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
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);
// Software Guide : EndCodeSnippet
// Software Guide : BeginLatex
//
// At this point we can query the tetrahedron cell for information about
// its boundary features. For example, the number of boundary features of
// each dimension can be obtained with the method
// \code{GetNumberOfBoundaryFeatures()}.
//
// \index{itk::Mesh!CellFeatureCount}
// \index{itk::Mesh!GetNumberOfBoundaryFeatures()}
// \index{GetNumberOfBoundaryFeatures()!itk::Mesh}
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
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);
// Software Guide : EndCodeSnippet
std::cout << "Number of boundary features of the cellId= " << cellId
<< std::endl;
std::cout << "Dimension 0 = " << n0 << std::endl;
std::cout << "Dimension 1 = " << n1 << std::endl;
std::cout << "Dimension 2 = " << n2 << std::endl;
// Software Guide : BeginLatex
//
// The boundary assignments can be recovered with the method
// \code{GetBoundaryAssignment()}. For example, the zero-dimensional
// features of the tetrahedron can be obtained with the following code.
//
// \index{itk::Mesh!GetBoundaryAssignment()}
// \index{GetBoundaryAssignment()!itk::Mesh}
//
// Software Guide : EndLatex
std::cout << "Boundary features of dimension 0 " << std::endl;
// Software Guide : BeginCodeSnippet
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;
}
// Software Guide : EndCodeSnippet
dimension = 1;
std::cout << "Boundary features of dimension " << dimension << std::endl;
for (unsigned int b1 = 0; b1 < n1; ++b1)
{
MeshType::CellIdentifier id;
bool found = mesh->GetBoundaryAssignment(dimension, cellId, b1, &id);
if (found)
{
std::cout << id << std::endl;
}
}
dimension = 2;
std::cout << "Boundary features of dimension " << dimension << std::endl;
for (unsigned int b2 = 0; b2 < n2; ++b2)
{
MeshType::CellIdentifier id;
bool found = mesh->GetBoundaryAssignment(dimension, cellId, b2, &id);
if (found)
{
std::cout << id << std::endl;
}
}
// Software Guide : BeginLatex
//
// The following code illustrates how to set the edge boundaries for one of
// the triangular faces.
//
// Software Guide : EndLatex
// Software Guide : BeginCodeSnippet
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);
// Software Guide : EndCodeSnippet
std::cout << "In cell Id = " << cellId << std::endl;
std::cout << "Boundary features of dimension " << dimension;
n1 = mesh->GetNumberOfCellBoundaryFeatures(dimension, cellId);
std::cout << " = " << n1 << std::endl;
for (unsigned int b1 = 0; b1 < n1; ++b1)
{
MeshType::CellIdentifier id;
bool found = mesh->GetBoundaryAssignment(dimension, cellId, b1, &id);
if (found)
{
std::cout << id << std::endl;
}
}
return EXIT_SUCCESS;
}
itk::VertexCell
Represents a single vertex for a Mesh.
Definition: itkVertexCell.h:42
itk::GTest::TypedefsAndConstructors::Dimension2::PointType
ImageBaseType::PointType PointType
Definition: itkGTestTypedefsAndConstructors.h:51
itk::FixedArray::Begin
Iterator Begin()
itkLineCell.h
itk::TetrahedronCell
TetrahedronCell represents a tetrahedron for a Mesh.
Definition: itkTetrahedronCell.h:40
itkMesh.h
itk::TriangleCell
Definition: itkTriangleCell.h:46
itk::LineCell
Represents a line segment for a Mesh.
Definition: itkLineCell.h:45
itk::Mesh
Implements the N-dimensional mesh structure.
Definition: itkMesh.h:126
New
static Pointer New()
itkTetrahedronCell.h