Skip to content

Commit

Permalink
Fixed duplicate edge handling (#179)
Browse files Browse the repository at this point in the history
* better manifold checks

* fixed duplicate edge handling
  • Loading branch information
elalish committed Aug 6, 2022
1 parent bf2a9a8 commit a08a9e7
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 19 deletions.
9 changes: 9 additions & 0 deletions src/manifold/src/boolean_result.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,9 @@ Manifold::Impl Boolean3::Result(Manifold::OpType op) const {

// Level 6

if (ManifoldParams().intermediateChecks)
ASSERT(outR.IsManifold(), logicErr, "polygon mesh is not manifold!");

outR.Face2Tri(faceEdge, faceRef, halfedgeBary);

#ifdef MANIFOLD_DEBUG
Expand All @@ -663,8 +666,14 @@ Manifold::Impl Boolean3::Result(Manifold::OpType op) const {
simplify.Start();
#endif

if (ManifoldParams().intermediateChecks)
ASSERT(outR.IsManifold(), logicErr, "triangulated mesh is not manifold!");

outR.SimplifyTopology();

if (ManifoldParams().intermediateChecks)
ASSERT(outR.Is2Manifold(), logicErr, "simplified mesh is not 2-manifold!");

// Index starting from 0 is chosen because I want the kernel part as simple as
// possible.
VecDH<int> meshIDs;
Expand Down
26 changes: 16 additions & 10 deletions src/manifold/src/impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,10 @@ struct Tri2Halfedges {
const int j = (i + 1) % 3;
const int edge = 3 * tri + i;
halfedges[edge] = {triVerts[i], triVerts[j], -1, tri};
edges[edge] = ((glm::uint64_t)glm::min(triVerts[i], triVerts[j])) << 32 |
// Sort the forward halfedges in front of the backward ones by setting the
// highest-order bit.
edges[edge] = glm::uint64_t(triVerts[i] < triVerts[j] ? 1 : 0) << 63 |
((glm::uint64_t)glm::min(triVerts[i], triVerts[j])) << 32 |
glm::max(triVerts[i], triVerts[j]);
}
}
Expand All @@ -124,12 +127,11 @@ struct Tri2Halfedges {
struct LinkHalfedges {
Halfedge* halfedges;
const int* ids;
const int numEdge;

__host__ __device__ void operator()(int k) {
const int i = 2 * k;
const int j = i + 1;
__host__ __device__ void operator()(int i) {
const int pair0 = ids[i];
const int pair1 = ids[j];
const int pair1 = ids[i + numEdge];
halfedges[pair0].pairedHalfedge = pair1;
halfedges[pair1].pairedHalfedge = pair0;
}
Expand Down Expand Up @@ -467,11 +469,12 @@ int Manifold::Impl::InitializeNewReference(
*/
void Manifold::Impl::CreateHalfedges(const VecDH<glm::ivec3>& triVerts) {
const int numTri = triVerts.size();
const int numEdge = 3 * numTri / 2;
// drop the old value first to avoid copy
halfedge_.resize(0);
halfedge_.resize(3 * numTri);
VecDH<uint64_t> edge(3 * numTri);
VecDH<int> ids(3 * numTri);
halfedge_.resize(2 * numEdge);
VecDH<uint64_t> edge(2 * numEdge);
VecDH<int> ids(2 * numEdge);
auto policy = autoPolicy(numTri);
sequence(policy, ids.begin(), ids.end());
for_each_n(policy, zip(countAt(0), triVerts.begin()), numTri,
Expand All @@ -482,8 +485,11 @@ void Manifold::Impl::CreateHalfedges(const VecDH<glm::ivec3>& triVerts) {
// two different faces, causing this edge to not be 2-manifold. These are
// fixed by duplicating verts in SimplifyTopology.
stable_sort_by_key(policy, edge.begin(), edge.end(), ids.begin());
for_each_n(policy, countAt(0), halfedge_.size() / 2,
LinkHalfedges({halfedge_.ptrD(), ids.ptrD()}));
// Once sorted, the first half of the range is the forward halfedges, which
// correspond to their backward pair at the same offset in the second half
// of the range.
for_each_n(policy, countAt(0), numEdge,
LinkHalfedges({halfedge_.ptrD(), ids.ptrD(), numEdge}));
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/manifold/src/impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ struct Manifold::Impl {
bool IsIndexInBounds(const VecDH<glm::ivec3>& triVerts) const;
void SetPrecision(float minPrecision = -1);
bool IsManifold() const;
bool Is2Manifold() const;
bool MatchesTriNormals() const;
int NumDegenerateTris() const;

Expand Down
2 changes: 1 addition & 1 deletion src/manifold/src/manifold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ Manifold Manifold::AsOriginal() const {
* Should always be true. Also checks saneness of the internal data structures.
*/
bool Manifold::IsManifold() const {
return GetCsgLeafNode().GetImpl()->IsManifold();
return GetCsgLeafNode().GetImpl()->Is2Manifold();
}

/**
Expand Down
26 changes: 18 additions & 8 deletions src/manifold/src/properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,22 +233,32 @@ struct CheckCCW {
namespace manifold {

/**
* Returns true if this manifold is in fact an oriented 2-manifold and all of
* Returns true if this manifold is in fact an oriented even manifold and all of
* the data structures are consistent.
*/
bool Manifold::Impl::IsManifold() const {
if (halfedge_.size() == 0) return true;
auto policy = autoPolicy(halfedge_.size());
bool isManifold = all_of(policy, countAt(0), countAt(halfedge_.size()),
CheckManifold({halfedge_.cptrD()}));
// std::cout << (isManifold ? "" : "Not ") << "Manifold" << std::endl;

return all_of(policy, countAt(0), countAt(halfedge_.size()),
CheckManifold({halfedge_.cptrD()}));
}

/**
* Returns true if this manifold is in fact an oriented 2-manifold and all of
* the data structures are consistent.
*/
bool Manifold::Impl::Is2Manifold() const {
if (halfedge_.size() == 0) return true;
auto policy = autoPolicy(halfedge_.size());

if (!IsManifold()) return false;

VecDH<Halfedge> halfedge(halfedge_);
sort(policy, halfedge.begin(), halfedge.end());
bool noDupes = all_of(policy, countAt(0), countAt(2 * NumEdge() - 1),
NoDuplicates({halfedge.cptrD()}));
// std::cout << (noDupes ? "" : "Not ") << "2-Manifold" << std::endl;
return isManifold && noDupes;

return all_of(policy, countAt(0), countAt(2 * NumEdge() - 1),
NoDuplicates({halfedge.cptrD()}));
}

/**
Expand Down
1 change: 1 addition & 0 deletions test/test_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ int main(int argc, char **argv) {
case 'v':
options.params.verbose = true;
manifold::ManifoldParams().verbose = true;
manifold::ManifoldParams().intermediateChecks = true;
break;
default:
fprintf(stderr, "Unknown option: %s\n", argv[i]);
Expand Down

0 comments on commit a08a9e7

Please sign in to comment.