Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose mesh indexSize() functions for maximum value of raw index #196

Merged
merged 2 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/docs/surface/surface_mesh/indexing.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,13 @@ size_t i = e.getIndex();

??? func "`#!cpp size_t Corner::getIndex() const`"

Returns an index between 0 and _C_-1, where _C_ is the number of corners in the mesh.
Returns an index between 0 and _H_-1, where _H_ is the number of halfedges in the mesh.

!!! warning

**NOTE:** Due to implementation constraints, raw corner indices behave differently from all other indices. Even when a mesh is compressed, `corner.getIndex()` will NOT necessarily be a dense enumeration from [0,C). There will be some unsued indices, and the indices will range up to `H`. Currently, this occurs only on a manifold mesh with boundary; the gaps occur because corners really just syntactic sugar for halfedges, but exterior halfedges are also enumerated.

The `cornerIndices` containers above do not have this drawback.

??? func "`#!cpp size_t Vertex::getIndex() const`"

Expand Down
15 changes: 15 additions & 0 deletions include/geometrycentral/surface/surface_mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,26 @@ class SurfaceMesh {
// Check capacity. Needed when implementing expandable containers for mutable meshes to ensure the contain can
// hold a sufficient number of elements before the next resize event.
size_t nHalfedgesCapacity() const;
size_t nCornersCapacity() const;
size_t nVerticesCapacity() const;
size_t nEdgesCapacity() const;
size_t nFacesCapacity() const;
size_t nBoundaryLoopsCapacity() const;

// Return the size corresponding to the largest raw index in the mesh (except corners, see below). That is, the
// maximum value of he.getIndex()+1 for all halfedges, etc. This may differ from `nHalfedges()` or
// `nHalfedgesCapacity()` for non-compressed meshes. It also may differ for corners even in the case of a compressed
// mesh. These values may change after any mutation, not just on resize events.
//
// Due to implementaiton constraints, for corners the guarantee is slightly weaker: this value may be one greater than
// the largest index of any corner.
size_t halfedgeIndexSize() const;
size_t cornerIndexSize() const; // NOTE: this one is not tight like the others
size_t vertexIndexSize() const;
size_t edgeIndexSize() const;
size_t faceIndexSize() const;
size_t boundaryLoopIndexSize() const;

// == Debugging, etc

// Performs a sanity checks on halfedge structure; throws on fail
Expand Down
9 changes: 9 additions & 0 deletions include/geometrycentral/surface/surface_mesh.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@ inline size_t SurfaceMesh::nBoundaryLoops() const { return nBoundaryLoopsCou

// Capacities
inline size_t SurfaceMesh::nHalfedgesCapacity() const { return nHalfedgesCapacityCount; }
inline size_t SurfaceMesh::nCornersCapacity() const { return nHalfedgesCapacityCount; }
inline size_t SurfaceMesh::nVerticesCapacity() const { return nVerticesCapacityCount; }
inline size_t SurfaceMesh::nEdgesCapacity() const { return nEdgesCapacityCount; }
inline size_t SurfaceMesh::nFacesCapacity() const { return nFacesCapacityCount - nBoundaryLoopsFillCount; }
inline size_t SurfaceMesh::nBoundaryLoopsCapacity() const { return nFacesCapacityCount - nFacesFillCount; }

// Index size
inline size_t SurfaceMesh::halfedgeIndexSize() const { return nHalfedgesFillCount; }
inline size_t SurfaceMesh::cornerIndexSize() const { return nHalfedgesFillCount; }
inline size_t SurfaceMesh::vertexIndexSize() const { return nVerticesFillCount; }
inline size_t SurfaceMesh::edgeIndexSize() const { return nEdgesFillCount; }
inline size_t SurfaceMesh::faceIndexSize() const { return nFacesFillCount; }
inline size_t SurfaceMesh::boundaryLoopIndexSize() const { return nBoundaryLoopsFillCount; }

// Connectivity
inline size_t SurfaceMesh::heNext(size_t iHe) const { return heNextArr[iHe]; }
inline size_t SurfaceMesh::heTwin(size_t iHe) const { if(usesImplicitTwin()) return heTwinImplicit(iHe);
Expand Down
62 changes: 60 additions & 2 deletions test/src/halfedge_mesh_test.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#include "geometrycentral/surface/manifold_surface_mesh.h"
#include "geometrycentral/surface/meshio.h"
#include "geometrycentral/surface/rich_surface_mesh_data.h"
Expand Down Expand Up @@ -237,6 +236,66 @@ TEST_F(HalfedgeMeshSuite, IterateBoundaryLoopsTest) {
}


// ============================================================
// =============== Counts and internals
// ============================================================

TEST_F(HalfedgeMeshSuite, IndexCountTest) {
for (MeshAsset& a : allMeshes()) {
a.printThyName();

{ // Vertices
bool maxSeen = false;
for (Vertex v : a.mesh->vertices()) {
EXPECT_LT(v.getIndex(), a.mesh->vertexIndexSize());
if ((v.getIndex() + 1) == a.mesh->vertexIndexSize()) maxSeen = true;
}
EXPECT_TRUE(maxSeen);
}

{ // Halfedges
bool maxSeen = false;
for (Halfedge he : a.mesh->halfedges()) {
EXPECT_LT(he.getIndex(), a.mesh->halfedgeIndexSize());
if ((he.getIndex() + 1) == a.mesh->halfedgeIndexSize()) maxSeen = true;
}
EXPECT_TRUE(maxSeen);
}

{ // Corners
bool maxSeen = false;
for (Corner c : a.mesh->corners()) {
EXPECT_LT(c.getIndex(), a.mesh->cornerIndexSize());

// special case for corners, they have a weaker guarantee and
// cornerIndexSize() may be not-tight by 1
if ((c.getIndex() + 1) == a.mesh->cornerIndexSize()) maxSeen = true;
if ((c.getIndex() + 2) == a.mesh->cornerIndexSize()) maxSeen = true;
}
EXPECT_TRUE(maxSeen);
}

{ // Edges
bool maxSeen = false;
for (Edge e : a.mesh->edges()) {
EXPECT_LT(e.getIndex(), a.mesh->edgeIndexSize());
if ((e.getIndex() + 1) == a.mesh->edgeIndexSize()) maxSeen = true;
}
EXPECT_TRUE(maxSeen);
}

{ // Faces
bool maxSeen = false;
for (Face f : a.mesh->faces()) {
EXPECT_LT(f.getIndex(), a.mesh->faceIndexSize());
if ((f.getIndex() + 1) == a.mesh->faceIndexSize()) maxSeen = true;
}
EXPECT_TRUE(maxSeen);
}
}
}


// ============================================================
// =============== Utility and status functions
// ============================================================
Expand Down Expand Up @@ -764,7 +823,6 @@ TEST_F(HalfedgeMeshSuite, EdgeDiamondNavigator) {
}
}


// ============================================================
// =============== Utilities
// ============================================================
Expand Down