C# - Mesh.Vertices and Mesh.TopologyVertices not in Sync

OK you mesh traversal experts out there. I thought I had nailed it, then I closed the GH definition, then reopened it and… nope. Not at all in sync.

So what I’ve been trying to do is to make a component which traverses a mesh (mesh.Vertices[i]) and picks up nearby vertices (around the “center vertex” i) and output all of them nicely in datatrees.

However, I also want the data indexes in the branches to be in sync with the DeconstructMesh component so I can pair the datatree with other additional data. Well, that was the idea. But I don’t seem to be able to get the indexes correctly in sync. That is my problem.

Pictured is the collected data with index offset -1 and +1 (yellow balls, centered index is the red ball in the middle) plus the same index(es), the blue balls, picked from the std DeconstructMesh component.

Clue: The indexes are obviously not in sync (the red and blue balls should be the same vertices). But what am I doing wrong in the code? (C#). I tried to comment so that the code should be readable.

(The code is structured such that I fist collect the vertices into arrays in order to enable using Parallel.For (using arrays is a simple way to avoid data races) and then shuffle the resulting arrays into a Datatree at the end of the method. An option is to expand the vertice-group (“Expand Star” input) from connected vertices into yet another level of connected vertices (the yellow ones pictured).

The gh definition:
Mesh Topology - Simplified.gh (890.7 KB)

// --------------------

Edit: Ops, forgot to say that all the relevant code is in one single method:

private DataTree<GH_Point> GetConnectedTopologyVertices()

// Rolf

Before looking at the code, topological vertices are sorted by ascending x coordinate. Furthermore any coincident vertices in the mesh end up associated with the same topological vertex. So it’s not just that the ordering of vertices is not the same as the ordering of topological vertices, there may be different amounts of them.

Topologically speaking, all meshes are fully welded.

Each topological vertex has a list of vertex indices it is associated with.

Edit2: Corrected a potential bug (see Edit2 below).

Yup, all that is perfectly clear to me. Therefore I also “translated” the mesh.TopologyVertices back to its mesh.Vertex index like so:

    var ix = mesh.TopologyVertices.MeshVertexIndices(connectedTopoV_indices[j])[0];
    blah_array[ ] = mesh.Vertices[ix];`

Which is why I don’t understand where it all went wrong anyway. Must be some silly mistake which I simply don’t see myself. “Code-blind” looking on my own code, I guess.

Edit1: The method concerned:
Edit2: One little bug discovered, and corrected belwo (see #):

  private DataTree<GH_Point> GetConnectedTopologyVertices()
    // local vars are faster ( -> heap )
    var mesh_topovert = m_mesh.TopologyVertices;
    var expand_star = m_expand_star; // local var is faster, especially in a loop

    // connected topology vertices (ctv) for each topology vertex (tv)
    var mv_arr = new GH_Point[m_mesh_vertices.Count][][];

    //for (var v_i = 0; v_i < m_mesh_vertices.Count; v_i++)
    System.Threading.Tasks.Parallel.For(0, m_mesh_vertices.Count, v_i =>
        // get the index of the Topology Vertex for this MESH vertex;
        var tv_i = mesh_topovert.TopologyVertexIndex(v_i);
        // sort all connected vertices around this single topo vertex
        // array of connected neighbour vertice indices
        var ctv_indices = mesh_topovert.ConnectedTopologyVertices(tv_i);
        // all connected vertices per tv, allow for indefinite star-depth
        var ctv_arr = new GH_Point[ctv_indices.Length + 1][];
        ctv_arr[0] = new GH_Point[ctv_indices.Length + 1];

        // current star
        ctv_arr[0][0] = new GH_Point(m_mesh_vertices[v_i]); // # correction: tv_i -> v_i, a potential bug
        for (var j = 0; j < ctv_indices.Length; j++)
          var ix = mesh_topovert.MeshVertexIndices(ctv_indices[j])[0];
          ctv_arr[0][j + 1] = new GH_Point(m_mesh_vertices[ix]);

        // .....................
        // Epand to a wider star
        // .....................
        if (expand_star)
          // what has already been visited?
          var visited_indices = new List<int>();

          // add one level to the star
          for (var j = 0; j < ctv_indices.Length; j++)
            var star_indices = mesh_topovert.ConnectedTopologyVertices(ctv_indices[j]);
            ctv_arr[j + 1] = new GH_Point[star_indices.Length];

            for (var t = 0; t < star_indices.Length; t++)
              var already_visited = false;

              // check if the last star if this item already exist (probability is more than 50%...
              // if (visited_indices.IndexOf(star_indices[t]) > -1) { already_visited = true; }
              for (var u = 0; u < visited_indices.Count; u++)
                if (star_indices[t] == visited_indices[u])
                  already_visited = true;
              if (already_visited) continue; // next k

              // not already collected, so add it now
                var ix = mesh_topovert.MeshVertexIndices(star_indices[t])[0];
                ctv_arr[j + 1][t] = new GH_Point(m_mesh_vertices[ix]);
                // record visited
            } // star_indices level+
        } // expand star

        mv_arr[v_i] = ctv_arr;

    return Point3dArrayToDataTree(ref mv_arr);

// Rolf

I found the problem.

The original mesh and the mesh.DuplicateMesh() doesn’t retain identical indexes. Sigh.

So, the problem disappeared when using the mesh.DuplicateMesh to feed both the internal logic in my component and the “external” DeconstructMesh component. Only then they will have identical indexes.

Fig. Red and blue balls (center vertices) are now the same.

I had used the “original mesh” as the soruce for the DeconstructMesh, and that was not a good idea…

I mean, how could it have been a bug in my code? Of course not! :wink:

// Rolf

1 Like