/// <summary> /// Populates |EditVertex.AdjacentVertices|. /// </summary> private void ComputeAdjacentVertices() { // Put all vertices into a KDTree. try { KDTree <EditVertex> tree = new KDTree <EditVertex>(3); foreach (var face in Faces) { foreach (var vert in face.Vertices) { var pos = vert.Position; tree.AddPoint(new double[] { pos.X, pos.Y, pos.Z }, vert); } } // For each vertices, retrieve adjacent/identical vertices by looking // up neighbors within a very small (epsilon'ish) radius. Faces.ParallelDo( face => { foreach (var vert in face.Vertices) { var pos = vert.Position; var neighbors = tree.NearestNeighbors(new double[] { pos.X, pos.Y, pos.Z }, new SquareEuclideanDistanceFunction(), int.MaxValue, 1e-5f); foreach (var neighbor in neighbors) { vert.AdjacentVertices.Add(neighbor); } } }); } catch (Exception ex) { // TODO: Log. // Unfortunately, KDTree rarely crashes. // Long-term: fix KDTree. // Short-term: O(n^2) brute force workaround. Faces.ParallelDo( face => { foreach (var vert in face.Vertices) { var pos = vert.Position; foreach (var otherVert in Vertices) { if (vert.IsApproximatelySamePosition(otherVert)) { vert.AdjacentVertices.Add(otherVert); } } } }); } // Vertices being adjacent should be a transitive relation, yet this is // not guaranteed if implemented through a search radius. HashSet <EditVertex> seen = new HashSet <EditVertex>(); foreach (var face in Faces) { foreach (var vert in face.Vertices) { if (seen.Contains(vert)) { continue; } foreach (var adjacentVert in vert.AdjacentVertices) { vert.AdjacentVertices.UnionWith(vert.AdjacentVertices); } foreach (var adjacentVert in vert.AdjacentVertices) { adjacentVert.AdjacentVertices = vert.AdjacentVertices; seen.Add(adjacentVert); } } } }