Common Subdivision

#include "geometrycentral/surface/common_subdivision.h"

Intuitively, the common subdivision of meshes $T_A$ and $T_B$ is the polygon mesh that you obtain if you cut $T_B$ along the edges of $T_A$. The vertices of the common subdivision correspond to intersections of simplices of $T_A$ with simplices of $T_B$ (e.g. intersections between edges).

The CommonSubdivision class represents the common subdivision of two meshes, referred to as meshA and meshB.

Note

In the common case of intrinsic triangulations, the input triangulation is meshA, and the intrinsic triangulation drawn along its surface is meshB.

Warning

Every vertex of meshA must also be a vertex of meshB.

The common subdivision is encoded by a list of CommonSubdivisionPoints, representing its vertices. In addition, it provides ordered lists indicating which points appear along each edge of meshA and meshB. Optionally, an explicit mesh of the common subdivision can be constructed via the constructMesh() function.

Example: construct an intrinsic triangulation, an build its common subdivision as a mesh.

#include "geometrycentral/surface/common_subdivision.h"
#include "geometrycentral/surface/intrinsic_triangulation.h"
#include "geometrycentral/surface/integer_coordinates_intrinsic_triangulation.h"
#include "geometrycentral/surface/vertex_position_geometry.h"

ManifoldSurfaceMesh& mesh;
VertexPositionGeometry& geometry; // your mesh and geometry

// Build an interesting intrinsic triangulation
IntegerCoordinatesIntrinsicTriangulation intTri(mesh, geometry);
intTri.delaunayRefine();

// Get the common subdivision
CommonSubdivision& cs = intTri.getCommonSubdivision();

// Build a mesh of the common subdivision, and interpolate vertex positions for it
cs.constructMesh();
ManifoldSurfaceMesh& csMesh = *cs.mesh;
VertexData<Vector3> csPositions = cs.interpolateAcrossA(geometry->vertexPositions);


Members

ManifoldSurfaceMesh& CommonSubdivision::meshA
ManifoldSurfaceMesh& CommonSubdivision::meshB

The two meshes whose common subdivision this object encodes.

std::deque<CommonSubdivisionPoint> CommonSubdivision::subdivisionPoints

A list of the vertices (subdivision points) of the common subdivision. See documentation of CommonSubdivisionPoint below.

EdgeData<std::vector<CommonSubdivisionPoint*>> CommonSubdivision::pointsAlongA
EdgeData<std::vector<CommonSubdivisionPoint*>> CommonSubdivision::pointsAlongB

Stores the list of the vertices of the common subdivision along each edge of meshA (resp. meshB), represented as pointers to elements of subdivisionPoints. These lists are ordered according to the edge’s orientation and include the start and end points of the edges.

If edge eA of meshA and eB of meshB run parallel to each other, then pointsAlongA[eA] will store an “intersection” of type CSIntersectionType::EDGE_PARALLEL to record the fact that it runs parallel to edge eB, and vice versa.

Queries and Accessors

size_t CommonSubdivision::nVertices() const

Counts the number of vertices of the common subdivision. Works even if an explicit mesh of the common subdivision has not been constructed yet.

std::tuple<size_t, size_t, size_t> CommonSubdivision::elementCounts() const

Counts the number of vertices, edges, and faces of the common subdivision. Works even if an explicit mesh of the common subdivision has not been constructed yet.

size_t CommonSubdivision::intersectionsA(Edge eA) const
size_t CommonSubdivison::intersectionsB(Edge eB) const

Counts the number of common subdivision vertices (not including endpoints) along edge eA of meshA (resp. eB of meshB). These intersection counts can be thought of as normal coordinates.

std::unique_ptr<SimplePolygonMesh> CommonSubdivision::buildSimpleMesh()

Construct and return a SimplePolygonMesh of the common subdivision.

In cases where there are some errors in intersection data defining the common subvidision, it may not be possible to construct the ManifoldSurfaceMesh with constructMesh(), and this is the only option.

Common Subdivision Points

A CommonSubdivisionPoint is a struct representing a vertex of the common subdivision. Each vertex of the common subdivision is the intersection of a mesh element from meshA with a mesh element of meshB.

CSIntersectionType CommonSubdivisionPoint::intersectionType

The type of elements intersecting (e.g. edge-edge intersection, or face-vertex intersection).

SurfacePoint CommonSubdivisionPoint::posA
SurfacePoint CommonSubdivisionPoint::posB

The location of this intersection on meshA (resp. meshB), represented as a SurfacePoint.

bool CommonSubdivisionPoint::orientation

For edge-edge intersections, this stores the orientation of the intersection. It is set to true for positive intersections, meaning that when traveling along the edge of meshA, the intersecting edge of meshB points to the left.

Intersection Types

enum class CSIntersectionType {
VERTEX_VERTEX,
EDGE_TRANSVERSE,
EDGE_PARALLEL,
FACE_VERTEX, // Face of mesh A, Vertex of mesh B
EDGE_VERTEX  // Edge of mesh A, Vertex of mesh B
};

‘Parallel’ intersections

In addition to storing points representing transverse intersections of mesh elements, we also store points which represent edges of meshA and meshB that run parallel to each other. These points are not really vertices of the common subdivision, but provide a convenient way of encoding the fact that edges run parallel to each other. These points are tagged with type CSIntersectionType::EDGE_PARALLEL.

Mesh connectivity

Optionally, the raw intersections stored in the common subdivision can be used to explicitly construct a SurfaceMesh (with all the usual halfedge connectivity) of the common subdivision; this mesh can then be used for many higher-level geometric operations.

The routines and members in this section all require that constructMesh() has been called first.

Note that in cases where there are some errors in intersection data defining the common subdivision, it may not be possible to construct a manifold mesh of the common subdivision. constructMesh() will fail with an exception in this case. Note that buildSimpleMesh() above can be used to build a plain old vertex-face adjacency list representation of the mesh, although it cannot be used for the various routines in this section.

By default, the common subdivision mesh is connectivity-only; there is no geometry associated with. Geometry can be recovered by either interpolating vertex positions from a mesh sitting in space (like interpolateAcrossA(geometry->vertexPositions)), or intrinsically via (like interpolationEdgeLengthsA()).

Mesh Members

std::unique_ptr<ManifoldSurfaceMesh> CommonSubdivision::mesh

An explicit mesh of the common subdivision.

VertexData<CommonSubdivisionPoint*> CommonSubdivision::sourcePoints

Defined per-vertex of the mesh CommonSubdivision::mesh, associates each vertex of mesh with the corresponding element of subdivisionPoints.

FaceData<Face> CommonSubdivision::sourceFaceA
FaceData<Face> CommonSubdivision::sourceFaceB

Defined per-face of the mesh CommonSubdivision::mesh, associates each face of mesh with the corresponding face of meshA (resp. meshB) which contains it.

Mesh Constructors

void CommonSubdivision::constructMesh(bool triangulate = true, bool skipIfAlreadyConstructed = true)

Compute an explicit mesh of the common subdivision, storing it in the mesh member.

Initially, the faces of the common subdivision are polygons. If triangulate is true, then these faces are immediately triangulated internally.

If skipIfAlreadyConstructed is true, this function does nothing when called multiple times. Otherwise, it deletes and reconstructs on subsequent calls.

void CommonSubdivision::triangulateMesh() const

Triangulate the mesh member. Internally, the sourceFaceA and sourceFaceB are also updated to reflect the triangulation.

This is already called be default in constructMesh() if triangulate=true.

Mesh Utilities

Interpolate values defined at vertices from either meshA or meshB to the vertices of the common subdivision mesh.

VertexData<T> CommonSubdivision::interpolateAcrossA(const VertexData<T>& dataA)
VertexData<T> CommonSubdivision::interpolateAcrossB(const VertexData<T>& dataB)

Linearly interpolates data on meshA (resp. meshB) to the common subdivision.

FaceData<T> CommonSubdivision::copyFromA(const FaceData<T>& dataA)
FaceData<T> CommonSubdivision::copyFromB(const FaceData<T>& dataB)

Copy data at faces from one of the meshes to the common subdivision. Each face of the common subdivision gets the value from the face which contains it. The return value is defined per-face of the common subdivision mesh.

SparseMatrix<double> CommonSubdivison::interpolationMatrixA()
SparseMatrix<double> CommonSubdivision::interpolationMatrixB()

Yields a |V| x |V_A| matrix (resp. |V| x |V_B|) which linearly interpolates data on meshA (resp. meshB) to the common subdivision. Here |V| denotes the number of vertices in the common subdivision.

EdgeData<double> CommonSubdivision::interpolateEdgeLengthsA(const EdgeData<double>& lengthA)
EdgeData<double> CommonSubdivision::interpolateEdgeLengthsB(const EdgeData<double>& lengthB)

Takes in edge lengths for meshA (resp. meshB) and computes the edge lengths of the common subdivision.

Note that in the standard case of an intrinsic triangulation with Euclidean metric-preserving edge flips, calling either of these methods with the edge lengths from the respective triangulation will produce identical outputs (up to floating-point error).

SparseMatrix<double> CommonSubdivision::vertexGalerkinMassMatrixFromPositionsA(const VertexData<Vector3>& positionsA)
SparseMatrix<double> CommonSubdivision::vertexGalerkinMassMatrixFromPositionsB(const VertexData<Vector3>& positionsB)

Takes in vertex positions for meshA (resp. meshB) and computes the Galerkin mass matrix of the common subdivision.

SparseMatrix<double> CommonSubdivision::vertexGalerkinMassMatrixFromLengthsA(const EdgeData<Vector3>& lengthsA)
SparseMatrix<double> CommonSubdivision::vertexGalerkinMassMatrixFromLengthsB(const EdgeData<Vector3>& lengthsB)

Takes in edge lengths for meshA (resp. meshB) and computes the Galerkin mass matrix of the common subdivision.

FaceData<double> niceColors(ManifoldSurfaceMesh& mesh, int kColors=7)
Take kColors evenly spaced values on [0,1] and treat them as categorical labels. These labels are assigned to mesh faces in the sense of a graph coloring, with further heuristics to try to avoid neighbors-of-neighbors at vertices.