Containers
The halfedge mesh class is equipped with a system of containers for associating data with mesh vertices, halfedges, edges, and faces. For instance, to represent a scalar value at vertices, or a vector value at faces, one can use
// on vertices VertexData<double> myVertexScalar(mesh); Vertex v = /* some vertex */; myVertexScalar[v] = 42.; // on faces FaceData<Vector3> myFaceVector(mesh); Face f = /* some face */; myFaceVector[f] = Vector3{1., 2., 3.};
A key feature of the MeshData<>
containers is that they automatically adapt to mutation of the underlying mesh. All existing MeshData<>
containers will remain valid during any sequence of mesh element insertions and deletions, adaptively and efficiently resizing themselves as needed. These containers can also be automatically written to file.
Mesh data types
The mesh data types are all templated on a common base class: MeshData<E,T>
, where E
is an element pointer type (such as Vertex
) and T
is a scalar type (such as double
). The first template argument should usually be omitted in user code; the various element containers are all typedef’d with concise names as follows:
VertexData<T>
data at verticesHalfedgeData<T>
data at (interior and exterior) halfedgesCornerData<T>
data at cornersEdgeData<T>
data at edgesFaceData<T>
data at facesBoundaryLoopData<T>
data at boundary loops
Most functionality is identical between all of these classes, so the sections below are written in terms of the generic MeshData<>
class.
Construction
MeshData<E,T>::MeshData<E,T>(HalfedgeMesh& mesh)
Construct a new container over a mesh. Elements will be defaultinitialized with T()
.
MeshData<E,T>::MeshData<E,T>(HalfedgeMesh& mesh, T initVal)
Construct a new container over a mesh.
Elements will be initialized with initVal
, and any newlycreated mesh elements will have their default values set to initVal
.
Additionally, see the vectorbased initializers in vector interoperability.
Accessors
T& MeshData<E,T>::operator[](E ptr)
Access data stored in the container with a reference to a mesh element. A const version also exists; expect semantics like std::vector<>
.
For example:
// on vertices VertexData<double> myVertexScalar(mesh); Vertex v = /* some vertex */; myVertexScalar[v] = 42.; double val = myVertexScalar[v];
T& MeshData<E,T>::operator[](size_t ind)
Access data stored in the container by the index of a mesh element. A const version also exists; expect semantics like std::vector<>
.
Only valid when the underlying mesh is compressed.
Must have 0 <= ind < N
, where N
is the number of elements of that type.
For example:
// on vertices VertexData<double> myVertexScalar(mesh); myVertexScalar[11] = 42.; double val = myVertexScalar[11]; // equivalent to: double val = myVertexScalar[mesh>vertex(11)];
void MeshData<E,T>::fill(T fillVal)
Fill all entries in the container with fillVal
.
size_t MeshData<E,T>::size()
The size of the container (equal to the number of elements of type E
, e.g. HalfedgeMesh::nVertices()
).
Vector interoperability
To support easy commoncase linear algebra operations, MeshData<>
containers support conversion to and from Eigen vector types.
The corresponding vectors are indexed according to the indices of the underlying mesh elements, or by a usersupplied index map which maps each elements to a dense set of zerobased indices.
Construct from a vector:
MeshData<E,T>::MeshData<E,T>(HalfedgeMesh& mesh Eigen::Matrix<T, Eigen::Dynamic, 1> vec)
Construct a new container over a mesh, with the contents of vec
.
MeshData<E,T>::MeshData<E,T>(HalfedgeMesh& mesh Eigen::Matrix<T, Eigen::Dynamic, 1> vec, MeshData<E, size_t>& indexer)
Construct a new container over a mesh, with the contents of vec
, indexed according to indexer
.
Fill from a vector:
void MeshData<E,T>::fromVector(Eigen::Matrix<T, Eigen::Dynamic, 1> vec)
Fill this container with the contents of vec
.
void MeshData<E,T>::fromVector(Eigen::Matrix<T, Eigen::Dynamic, 1> vec, MeshData<E, size_t>& indexer)
Fill this container with the contents of vec
, indexed according to indexer
.
Convert to a vector:
Eigen::Matrix<T, Eigen::Dynamic, 1> MeshData<E,T>::toVector()
Return a new vector which holds the contents of this container.
Eigen::Matrix<T, Eigen::Dynamic, 1> MeshData<E,T>::toVector(MeshData<E, size_t>& indexer)
Return a new vector which holds the contents of this container, indexed according to indexer
.
Transferring data
MeshData<>
containers are defined with respect to a particular mesh object. Sometimes one may need to transfer data defined on one mesh to another, for instance after making a copy of a mesh, or when reading data from file.
MeshData<E,T> MeshData<E,T>::reinterpretTo(HalfedgeMesh& target)
Map data defined on one halfedge mesh to another. The meshes must have the same number of elements, and data will be naively transferred between elements with the same index.
Requires that both meshes be compressed.
Example usage:
HalfedgeMesh meshA = /* something */; HalfedgeMesh meshB = meshA.copy(); FaceData<Vector3> myDataOnA(meshA); /* fill myDataOnA with interesting values */ FaceData<Vector3> myDataOnB = myDataOnA.reinterpretTo(meshB);
Advanced features
Underlying storage
Under the hood, all MeshData<>
types use a std::vector<T>
to store their values. This has at least two significant consquences:

For the scalar type
bool
, these containers are essentially broken, becausestd::vector<bool>
is a weird, broken special case. Usingchar
instead is usually a fine substitute: you can constructVertexData<char> flags
and setflags[vert] = true
as you would expect. 
A special allocator is needed for aligned objects, in particular fixedsize Eigen types (see gotchas). These
MeshData<>
containers all internally use the aligned allocatorstd::vector<T, Eigen::aligned_allocator<T>>
, so they can safely store fixedsized Eigen types.
Oriented edge data
Scalar values on edges often carry meaning with respect to some oriented direction along the edge— common examples include differences between values at vertices, the integral of a vector field along an edge, or more generally any 1form in discrete differential geometry. In such settings, a scalar value is concisely stored along edges, but its sign should flip when accessed “along” the opposite direction.
EdgeData<T>
containers offer a pair of special additional accessors for oriented data, which handle the sign flips automatically. Note that they cannot be instantiated unless the scalar type T
supports a unary 
operator.
T EdgeData<T>::getOriented(Halfedge he)
Access edgevalued data with sign determined by canonical halfedge orientation.
Returns edgeData[he.edge()]
if he == he.edge().halfedge()
, or edgeData[he.edge()]
otherwise.
void EdgeData<T>::setOriented(Halfedge he, T val)
Access edgevalued data with sign determined by canonical halfedge orientation.
Sets edgeData[he.edge()] = val
if he == he.edge().halfedge()
, or edgeData[he.edge()] = val
otherwise.