public virtual bool Apply() { Removed = 0; double merge_r2 = VertexTolerance * VertexTolerance; // construct hash table for edge midpoints var pointset = new TriCentroids() { Mesh = this.Mesh }; var hash = new PointSetHashtable(pointset); int hashN = (Mesh.TriangleCount > 100000) ? 128 : 64; hash.Build(hashN); Vector3d a = Vector3d.Zero, b = Vector3d.Zero, c = Vector3d.Zero; Vector3d x = Vector3d.Zero, y = Vector3d.Zero, z = Vector3d.Zero; int MaxTriID = Mesh.MaxTriangleID; // remove duplicate triangles int[] buffer = new int[1024]; for (int tid = 0; tid < MaxTriID; ++tid) { if (!Mesh.IsTriangle(tid)) { continue; } Vector3d centroid = Mesh.GetTriCentroid(tid); int N; while (hash.FindInBall(centroid, VertexTolerance, buffer, out N) == false) { buffer = new int[buffer.Length]; } if (N == 1 && buffer[0] != tid) { throw new Exception("RemoveDuplicateTriangles.Apply: how could this happen?!"); } if (N <= 1) { continue; // unique edge } Mesh.GetTriVertices(tid, ref a, ref b, ref c); Vector3d n = MathUtil.Normal(a, b, c); for (int i = 0; i < N; ++i) { if (buffer[i] != tid) { Mesh.GetTriVertices(buffer[i], ref x, ref y, ref z); if (is_same_triangle(ref a, ref b, ref c, ref x, ref y, ref z, merge_r2) == false) { continue; } if (CheckOrientation) { Vector3d n2 = MathUtil.Normal(x, y, z); if (n.Dot(n2) < 0.99) { continue; } } MeshResult result = Mesh.RemoveTriangle(buffer[i], true, false); if (result == MeshResult.Ok) { ++Removed; } } } } return(true); }
public virtual bool Apply() { merge_r2 = MergeDistance * MergeDistance; // construct hash table for edge midpoints MeshBoundaryEdgeMidpoints pointset = new MeshBoundaryEdgeMidpoints(this.Mesh); PointSetHashtable hash = new PointSetHashtable(pointset); int hashN = 64; if (Mesh.TriangleCount > 100000) { hashN = 128; } if (Mesh.TriangleCount > 1000000) { hashN = 256; } hash.Build(hashN); Vector3d a = Vector3d.Zero, b = Vector3d.Zero; Vector3d c = Vector3d.Zero, d = Vector3d.Zero; // find edge equivalence sets. First we find all other edges with same // midpoint, and then we check if endpoints are the same in second loop int[] buffer = new int[1024]; List <int>[] EquivSets = new List <int> [Mesh.MaxEdgeID]; HashSet <int> remaining = new HashSet <int>(); foreach (int eid in Mesh.BoundaryEdgeIndices()) { Vector3d midpt = Mesh.GetEdgePoint(eid, 0.5); int N; while (hash.FindInBall(midpt, MergeDistance, buffer, out N) == false) { buffer = new int[buffer.Length]; } if (N == 1 && buffer[0] != eid) { throw new Exception("MergeCoincidentEdges.Apply: how could this happen?!"); } if (N <= 1) { continue; // unique edge } Mesh.GetEdgeV(eid, ref a, ref b); // if same endpoints, add to equivalence set List <int> equiv = new List <int>(N - 1); for (int i = 0; i < N; ++i) { if (buffer[i] != eid) { Mesh.GetEdgeV(buffer[i], ref c, ref d); if (is_same_edge(ref a, ref b, ref c, ref d)) { equiv.Add(buffer[i]); } } } if (equiv.Count > 0) { EquivSets[eid] = equiv; remaining.Add(eid); } } // [TODO] could replace remaining hashset w/ PQ, and use conservative count? // add potential duplicate edges to priority queue, sorted by // number of possible matches. // [TODO] Does this need to be a PQ? Not updating PQ below anyway... DynamicPriorityQueue <DuplicateEdge> Q = new DynamicPriorityQueue <DuplicateEdge>(); foreach (int i in remaining) { if (OnlyUniquePairs) { if (EquivSets[i].Count != 1) { continue; } foreach (int j in EquivSets[i]) { if (EquivSets[j].Count != 1 || EquivSets[j][0] != i) { continue; } } } Q.Enqueue(new DuplicateEdge() { eid = i }, EquivSets[i].Count); } while (Q.Count > 0) { DuplicateEdge e = Q.Dequeue(); if (Mesh.IsEdge(e.eid) == false || EquivSets[e.eid] == null || remaining.Contains(e.eid) == false) { continue; // dealt with this edge already } if (Mesh.IsBoundaryEdge(e.eid) == false) { continue; } List <int> equiv = EquivSets[e.eid]; // find viable match // [TODO] how to make good decisions here? prefer planarity? bool merged = false; int failed = 0; for (int i = 0; i < equiv.Count && merged == false; ++i) { int other_eid = equiv[i]; if (Mesh.IsEdge(other_eid) == false || Mesh.IsBoundaryEdge(other_eid) == false) { continue; } DMesh3.MergeEdgesInfo info; MeshResult result = Mesh.MergeEdges(e.eid, other_eid, out info); if (result != MeshResult.Ok) { equiv.RemoveAt(i); i--; EquivSets[other_eid].Remove(e.eid); //Q.UpdatePriority(...); // how need ref to queue node to do this...?? // maybe equiv set is queue node?? failed++; } else { // ok we merged, other edge is no longer free merged = true; EquivSets[other_eid] = null; remaining.Remove(other_eid); } } if (merged) { EquivSets[e.eid] = null; remaining.Remove(e.eid); } else { // should we do something else here? doesn't make sense to put // back into Q, as it should be at the top, right? EquivSets[e.eid] = null; remaining.Remove(e.eid); } } return(true); }