diff --git a/mesh.cpp b/mesh.cpp index cea9847..f8772b5 100644 --- a/mesh.cpp +++ b/mesh.cpp @@ -476,19 +476,19 @@ void SKdNode::MakeMeshInto(SMesh *m) { } } -void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt) { +void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt, bool *inter) { if(gt && lt) { double ac = a.Element(which), bc = b.Element(which); if(ac < c + KDTREE_EPS || bc < c + KDTREE_EPS) { - lt->FindEdgeOn(a, b, n, cnt); + lt->FindEdgeOn(a, b, n, cnt, inter); } if(ac > c - KDTREE_EPS || bc > c - KDTREE_EPS) { - gt->FindEdgeOn(a, b, n, cnt); + gt->FindEdgeOn(a, b, n, cnt, inter); } } else { STriangleLl *ll; @@ -497,12 +497,37 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt) { if(tr->tag == cnt) continue; + // Test if this triangle matches up with the given edge if((a.Equals(tr->b) && b.Equals(tr->a)) || (a.Equals(tr->c) && b.Equals(tr->b)) || (a.Equals(tr->a) && b.Equals(tr->c))) { (*n)++; + } else if(((a.Equals(tr->a) && b.Equals(tr->b)) || + (a.Equals(tr->b) && b.Equals(tr->c)) || + (a.Equals(tr->c) && b.Equals(tr->a)))) + { + // It's an edge of this triangle, okay. + } else { + // Check for self-intersection + Vector n = (tr->Normal()).WithMagnitude(1); + double d = (tr->a).Dot(n); + double pa = a.Dot(n) - d, pb = b.Dot(n) - d; + // It's an intersection if neither point lies in-plane, + // and the edge crosses the plane (should handle in-plane + // intersections separately but don't yet). + if((pa < -LENGTH_EPS || pa > LENGTH_EPS) && + (pb < -LENGTH_EPS || pb > LENGTH_EPS) && + (pa*pb < 0)) + { + // The edge crosses the plane of the triangle; now see if + // it crosses inside the triangle. + if(tr->ContainsPointProjd(b.Minus(a), a)) { + *inter = true; + } + } } + // Ensure that we don't count this triangle twice if it appears // in two buckets of the kd tree. tr->tag = cnt; @@ -510,7 +535,10 @@ void SKdNode::FindEdgeOn(Vector a, Vector b, int *n, int cnt) { } } -void SKdNode::MakeNakedEdgesInto(SEdgeList *sel) { +void SKdNode::MakeNakedEdgesInto(SEdgeList *sel, bool *inter, bool *leaky) { + if(inter) *inter = false; + if(leaky) *leaky = false; + SMesh m; ZERO(&m); ClearTags(); @@ -526,10 +554,17 @@ void SKdNode::MakeNakedEdgesInto(SEdgeList *sel) { Vector b = (j == 0) ? tr->b : ((j == 1) ? tr->c : tr->a); int n = 0, nOther = 0; - FindEdgeOn(a, b, &n, cnt); + bool thisIntersects = false; + FindEdgeOn(a, b, &n, cnt, &thisIntersects); if(n != 1) { sel->AddEdge(a, b); + if(leaky) *leaky = true; } + if(thisIntersects) { + sel->AddEdge(a, b); + if(inter) *inter = true; + } + cnt++; } } diff --git a/polygon.h b/polygon.h index d8d0a01..feb102a 100644 --- a/polygon.h +++ b/polygon.h @@ -219,8 +219,8 @@ public: void MakeMeshInto(SMesh *m); void ClearTags(void); - void FindEdgeOn(Vector a, Vector b, int *n, int cnt); - void MakeNakedEdgesInto(SEdgeList *sel); + void FindEdgeOn(Vector a, Vector b, int *n, int cnt, bool *inter); + void MakeNakedEdgesInto(SEdgeList *sel, bool *inter=NULL, bool *leaky=NULL); }; #endif diff --git a/solvespace.cpp b/solvespace.cpp index 8ff4f6f..23b9a98 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -406,15 +406,23 @@ void SolveSpace::MenuAnalyze(int id) { SMesh *m = &(SS.GetGroup(SS.GW.activeGroup)->runningMesh); SKdNode *root = SKdNode::From(m); - root->MakeNakedEdgesInto(&(SS.nakedEdges)); + bool inters, leaks; + root->MakeNakedEdgesInto(&(SS.nakedEdges), &inters, &leaks); InvalidateGraphics(); + char *intersMsg = inters ? + "The mesh is self-intersecting (NOT okay, invalid)." : + "The mesh is not self-intersecting (okay, valid)."; + char *leaksMsg = leaks ? + "The mesh has naked edges (NOT okay, invalid)." : + "The mesh is watertight (okay, valid)."; + if(SS.nakedEdges.l.n == 0) { - Error("Zero naked edges; the model is watertight. " - "An exported STL file will be valid."); + Message("%s\r\n\r\n%s\r\n\r\nZero problematic edges, good.", + intersMsg, leaksMsg); } else { - Error("Found %d naked edges, now highlighted.", - SS.nakedEdges.l.n); + Error("%s\r\n\r\n%s\r\n\r\n%d problematic edges, bad.", + intersMsg, leaksMsg, SS.nakedEdges.l.n); } break; }