diff --git a/src/manifold/src/boolean3.cpp b/src/manifold/src/boolean3.cpp index 3f6b88650..3ea2a5574 100644 --- a/src/manifold/src/boolean3.cpp +++ b/src/manifold/src/boolean3.cpp @@ -81,7 +81,7 @@ struct CopyFaceEdges { const int q1 = 3 * q2 + j; const Halfedge edge = halfedgesQ[q1]; int a = pX; - int b = edge.IsForward() ? q1 : edge.pairedHalfedge; + int b = edge.IsForward() ? q1 : edge.pairedHalfedge.ix; if (inverted) std::swap(a, b); pXq1.Set(idx + static_cast(j), a, b); } @@ -302,7 +302,7 @@ struct Kernel02 { for (const int i : {0, 1, 2}) { const int q1 = 3 * q2 + i; const Halfedge edge = halfedgeQ[q1]; - const int q1F = edge.IsForward() ? q1 : edge.pairedHalfedge; + const int q1F = edge.IsForward() ? q1 : edge.pairedHalfedge.ix; if (!forward) { const int qVert = halfedgeQ[q1F].startVert; @@ -416,7 +416,7 @@ struct Kernel12 { for (const int i : {0, 1, 2}) { const int q1 = 3 * q2 + i; const Halfedge edge = halfedgesQ[q1]; - const int q1F = edge.IsForward() ? q1 : edge.pairedHalfedge; + const int q1F = edge.IsForward() ? q1 : edge.pairedHalfedge.ix; const int64_t key = forward ? SparseIndices::EncodePQ(p1, q1F) : SparseIndices::EncodePQ(q1F, p1); const size_t idx = monobound_quaternary_search(p1q1, key); diff --git a/src/manifold/src/boolean_result.cpp b/src/manifold/src/boolean_result.cpp index c92288fec..36adbad61 100644 --- a/src/manifold/src/boolean_result.cpp +++ b/src/manifold/src/boolean_result.cpp @@ -302,9 +302,9 @@ void AppendPartialEdges(Manifold::Impl &outR, Vec &wholeHalfedgeP, // while remapping them to the output using vP2R. Use the verts position // projected along the edge vector to pair them up, then distribute these // edges to their faces. - Vec &halfedgeR = outR.halfedge_; + auto &halfedgeR = outR.halfedge_; const Vec &vertPosP = inP.vertPos_; - const Vec &halfedgeP = inP.halfedge_; + const auto &halfedgeP = inP.halfedge_; for (auto &value : edgesP) { const int edgeP = value.first; @@ -376,7 +376,7 @@ void AppendNewEdges( Vec &halfedgeRef, const Vec &facePQ2R, const int numFaceP) { ZoneScoped; // Pair up each edge's verts and distribute to faces based on indices in key. - Vec &halfedgeR = outR.halfedge_; + auto &halfedgeR = outR.halfedge_; Vec &vertPosR = outR.vertPos_; for (auto &value : edgesNew) { diff --git a/src/manifold/src/mesh_fixes.h b/src/manifold/src/mesh_fixes.h index cc2a103e6..60ae2780a 100644 --- a/src/manifold/src/mesh_fixes.h +++ b/src/manifold/src/mesh_fixes.h @@ -29,7 +29,7 @@ struct TransformTangents { void operator()(const int edgeOut) { const int edgeIn = - invert ? halfedge[FlipHalfedge(edgeOut)].pairedHalfedge : edgeOut; + invert ? halfedge[FlipHalfedge(edgeOut)].pairedHalfedge.ix : edgeOut; tangent[edgeOut + edgeOffset] = vec4(transform * vec3(oldTangents[edgeIn]), oldTangents[edgeIn].w); } diff --git a/src/manifold/src/quickhull.cpp b/src/manifold/src/quickhull.cpp index 2cec252dd..9429a76a6 100644 --- a/src/manifold/src/quickhull.cpp +++ b/src/manifold/src/quickhull.cpp @@ -193,7 +193,7 @@ HalfEdgeMesh::HalfEdgeMesh(const MeshBuilder& builderObject, i = 0; for (const auto& halfEdge : builderObject.halfedges) { - if (halfEdge.pairedHalfedge != -1) { + if (halfEdge.pairedHalfedge.IsNotNull()) { halfedges.push_back({halfEdge.endVert, halfEdge.pairedHalfedge, builderObject.halfedgeToFace[i]}); halfedgeToFace.push_back(builderObject.halfedgeToFace[i]); diff --git a/src/manifold/src/shared.h b/src/manifold/src/shared.h index a40bc04b8..b22f3b3d3 100644 --- a/src/manifold/src/shared.h +++ b/src/manifold/src/shared.h @@ -40,6 +40,55 @@ inline int NextHalfedge(int current) { return current; } +/** + * @brief Type-safety for half-edge indices. + * + */ +struct HalfedgeIndex { + int ix; + + // For now, implicit conversion from int + HalfedgeIndex(int ix) : ix(ix) {} + HalfedgeIndex(const HalfedgeIndex& other) : ix(other.ix) {} + HalfedgeIndex() : ix(-1) {} + + bool operator==(const HalfedgeIndex& rhs) const { return ix == rhs.ix; } + bool operator==(int rhs) const { return ix == rhs; } + bool operator!=(const HalfedgeIndex& rhs) const { return ix != rhs.ix; } + bool operator!=(int rhs) const { return ix != rhs; } + HalfedgeIndex operator+(int rhs) const { return {ix + rhs}; } + HalfedgeIndex operator+=(int rhs) { + ix += rhs; + return *this; + } + HalfedgeIndex operator/(int rhs) const { + DEBUG_ASSERT(rhs == 3, logicErr, "Not dividing face index by 3"); + return {ix / rhs}; + } + + operator int() const { return ix; } + + // Index of next halfedge about the triangle. + HalfedgeIndex Next() const { + HalfedgeIndex current = *this; + ++current.ix; + if (current.ix % 3 == 0) current.ix -= 3; + return current; + } + + bool IsNull() const { return ix == -1; } + bool IsNotNull() const { return ix != -1; } +}; + +inline size_t GetIndex(HalfedgeIndex ix) { return ix.ix; } + +// XXX: should be a member function on HalfedgeIndex +inline HalfedgeIndex NextHalfedge(HalfedgeIndex current) { + ++current.ix; + if (current.ix % 3 == 0) current.ix -= 3; + return current; +} + inline mat3 NormalTransform(const mat4x3& transform) { return glm::inverse(glm::transpose(mat3(transform))); } @@ -122,7 +171,7 @@ inline vec3 GetBarycentric(const vec3& v, const mat3& triPos, */ struct Halfedge { int startVert, endVert; - int pairedHalfedge; + HalfedgeIndex pairedHalfedge; bool IsForward() const { return startVert < endVert; } bool operator<(const Halfedge& other) const { return startVert == other.startVert ? endVert < other.endVert diff --git a/src/utilities/include/manifold/vec.h b/src/utilities/include/manifold/vec.h index db85d8ed3..36d16d627 100644 --- a/src/utilities/include/manifold/vec.h +++ b/src/utilities/include/manifold/vec.h @@ -28,7 +28,7 @@ namespace manifold { /** @addtogroup Private * @{ */ -template +template class Vec; /* @@ -39,8 +39,8 @@ class Vec; * implementation that did not consider things like non-trivial * constructor/destructor, please keep T trivial. */ -template -class Vec : public VecView { +template +class Vec : public VecView { public: Vec() {} diff --git a/src/utilities/include/manifold/vec_view.h b/src/utilities/include/manifold/vec_view.h index f0a310ab9..21666b976 100644 --- a/src/utilities/include/manifold/vec_view.h +++ b/src/utilities/include/manifold/vec_view.h @@ -18,12 +18,17 @@ namespace manifold { +/** + * Override this for a custom index type. e.g. HalfedgeIndex + */ +inline size_t GetIndex(size_t x) { return x; } + /** * View for Vec, can perform offset operation. * This will be invalidated when the original vector is dropped or changes * length. Roughly equivalent to std::span from c++20 */ -template +template class VecView { public: using Iter = T *; @@ -45,14 +50,14 @@ class VecView { // allows conversion to a const VecView operator VecView() const { return {ptr_, size_}; } - inline const T &operator[](size_t i) const { - ASSERT(i < size_, std::out_of_range("Vec out of range")); - return ptr_[i]; + inline const T &operator[](Ix i) const { + ASSERT(GetIndex(i) < size_, std::out_of_range("Vec out of range")); + return ptr_[GetIndex(i)]; } - inline T &operator[](size_t i) { - ASSERT(i < size_, std::out_of_range("Vec out of range")); - return ptr_[i]; + inline T &operator[](Ix i) { + ASSERT(GetIndex(i) < size_, std::out_of_range("Vec out of range")); + return ptr_[GetIndex(i)]; } IterC cbegin() const { return ptr_; }