private static void BuildNeighborsLists(AdjacencySets adjSets, List <Vector3> neighbors, List <int> neighborIndices, int i, Vector3[] vertices) { HashSet <int> aSet = adjSets.GetAdjacencySet(i); foreach (int index in aSet) { if (i != index) { neighbors.Add(vertices[index]); neighborIndices.Add(index); } } }
public static void AdjSetsHCSmoother(Mesh mesh, AdjacencySets adjacencySets) { Vector3[] ov = mesh.vertices; // original vertices Vector3[] wv = mesh.vertices; // will contain the Laplacian smoothing Vector3[] bv = new Vector3[mesh.vertices.Length]; bool[] canBeMoved = new bool[mesh.vertices.Length]; List <Vector3> neighbors = new List <Vector3>(); List <int> neighborIndices = new List <int>(); for (int i = 0; i < wv.Length; i++) { neighbors.Clear(); // must exist but be empty for each new vertex neighborIndices.Clear(); BuildNeighborsLists(adjacencySets, neighbors, neighborIndices, i, ov); // True if the vertex is not on the edge of the mesh. canBeMoved[i] = IsInsideVertex(neighbors, neighborIndices, adjacencySets); if (canBeMoved[i]) { wv[i] = TrueAverageVertex(neighbors, ov[i]); } // Compute the differences bv[i] = wv[i] - ov[i]; } // We let the first loop complete because bv must be filled for the next step Vector3 average; Vector3 move; for (int i = 0; i < bv.Length; i++) { // We can't reuse the same neighbor list, because we get it from bv this time neighbors.Clear(); neighborIndices.Clear(); BuildNeighborsLists(adjacencySets, neighbors, neighborIndices, i, bv); average = AverageOfNeighbors(neighbors); // Final computation if (canBeMoved[i]) { move = BETA * bv[i] + ONE_MINUS_BETA * average; wv[i] += HC_CORRECT * move; } } mesh.vertices = wv; }
public static void AdjSetsSmoother(MeshData mData, AdjacencySets adjacencySets) { Vector3[] vertices = mData.vertices; Vector3[] ov = mData.vertices; // original vertices List <Vector3> neighbors = new List <Vector3>(); List <int> neighborIndices = new List <int>(); for (int i = 0; i < vertices.Length; i++) { neighbors.Clear(); // must exist but be empty for each new vertex neighborIndices.Clear(); BuildNeighborsLists(adjacencySets, neighbors, neighborIndices, i, ov); vertices[i] = TrueAverageVertex(neighbors, ov[i]); } mData.vertices = vertices; }
/// <summary> /// Creates the surface objects. /// </summary> /// <param name='voxels'> /// Voxels, i.e. the scalar field used to compute the surface. /// </param> /// <param name='threshold'> /// The threshold on which the isosurface is based. /// </param> /// <param name='delta'> /// Delta parameter from the grid, basically the size of each cell. /// </param> /// <param name='origin'> /// Origin of the grid. /// </param> /// <param name='colors'> /// Colors. Kept from previous implementation, but doesn't do anything here. I'm only /// keeping it because I'm not sure what it was used for. --- Alexandre /// </param> /// <param name='tag'> /// Tag for the objects to be created. /// </param> /// <param name='electro'> /// True if this is an electrostatic field isosurface. /// </param> public static void CreateSurfaceObjects(float[,,] voxels, float threshold, Vector3 delta, Vector3 origin, Color[] colors, string tag = "SurfaceManager", bool electro = false) { ELECTRO = electro; Debug.Log(ELECTRO.ToString()); if (ELECTRO) { ReadDX readDX = UI.GUIMoleculeController.readdx; origin = readDX.GetOrigin(); delta = readDX.GetDelta(); } InitGenMesh(voxels, threshold, delta, origin, tag); SetDims(); float bMCTime = Time.realtimeSinceStartup; MeshData mData = MarchingCubes.CreateMesh(VOXELS, 0, XDIM, 0, YDIM, 0, ZDIM); Debug.Log("Entire surface contains " + mData.vertices.Length.ToString() + " vertices."); float elapsed = 10f * (Time.realtimeSinceStartup - bMCTime); Debug.Log("GenerateMesh::MarchingCubes time: " + elapsed.ToString()); OffsetVertices(mData); float bSmooth = Time.realtimeSinceStartup; AdjacencySets adjacencySets = new AdjacencySets(mData.triangles.Length); adjacencySets.AddAllTriangles(mData.triangles); SmoothFilter.AdjSetsSmoother(mData, adjacencySets); elapsed = Time.realtimeSinceStartup - bSmooth; Debug.Log("Smoothing time: " + elapsed.ToString()); ProperlyCalculateNormals(mData); // Necessary for electrostatic fields isosurfaces Debug.Log(threshold.ToString()); if (threshold < 0) { FlipTriangles(mData); } Splitting splitting = new Splitting(); List <Mesh> meshes = splitting.Split(mData); CreateSurfaceObjects(meshes); }
private static bool IsInsideVertex(List <Vector3> neighbors, List <int> nIndices, AdjacencySets aSets) { int nbNeighbors; // number of neighbors that each neighbor possesses HashSet <int> aSet; // First we iterate over every neighbor of the "central" vertex we consider for (int nIndex = 0; nIndex < neighbors.Count; nIndex++) { // We then get the adjacency set containing *its* neighbors aSet = aSets.GetAdjacencySet(nIndices[nIndex]); nbNeighbors = 0; // We now iterate over this set. If any value in it is also in the nIndices list, // i.e. in the list of neighbors of our "central" vertex, we increment the number // of neighbors. foreach (int a in aSet) { if (nIndices.Contains(a)) { nbNeighbors++; } } // If this number is less than two, this means one of the neighbors of the central // vertex has less than two neighbors that are also a neighbor of the central vertex. // In other words, the central vertex is on an edge of the mesh. if (nbNeighbors < 2) { return(false); } } // If the function has never returned false, then the vertex is not on an edge. // It can be moved. return(true); }