Finding the Branch of an Item

Hi All, I have a component into which I have fed a 3 dimensional DataTree with a single item at each branch.

How do I find the branch of an item in C#? I need this to find the adjacent branch in each dimension.

In other words, if I have an item - in this case a Point3d at branch {nX; nY; nZ}, how do I find the branch it’s on? Then, with that, my intent is to find {nX+1; nY; nZ} and so forth.

Thank you in advance for your help.

Duane

( p.s. Note that I am not implementing GH_Structure. If this is the solution because it allows access to other properties or methods, does anyone have a good resource to understand how to implement this - or good code examples? I have been trying to understand the concepts here and how to implement them for months, and I just can’t get my head around it. This might be a good example for which to show specific code, how to convert my data that’s in DataTrees to the corresponding GH_Structure type, etc… your help in this area would be appreciated. -d )

Hi Duane,

My best definition of GH_Structure is that it is dictionary<path,list<GH_type>> that you can enumerate through.
The structure contains branches (entries) that are lists of data in the following types:
https://developer.rhino3d.com/api/grasshopper/html/N_Grasshopper_Kernel_Types.htm

So since you have a point:
GH_Structure< GH_Point>

The code should look something like the following:

GH_Path Path = new GH_Path();
for(int i = 0; i < structure.Branches.Count; i++)
{
    if(structure.Branch[i][0].value == poi){
        Path = structure.Path[i]
        break;
    }
}
Path[0] = Path[0] + 1;
Structure.Branch[Path]

In the for loop, we iterate through the branches and get the first item in the branch and compare that with your point of interest. (I’m not too sure if it is Branch[i] or get_Branch(i), can’t remember off the top of my head.)

We then query the structure with the path for the data we want.

1 Like

Hi @christopher.ho, thank you so much for the speedy response!
My code currently looks a lot like yours. However, using GH_Path, there’s no way to access a data tree two branches deep, as your

structure.Branch[i][0]...

seems to be doing. I’m trying to be able to extract branches [i][j] and [k] separately.

I’ll give you an example, in a previous thread about the same component library, I hadn’t worked out the DataTree construction logic yet, so I was expressing them as Vectors. One advantage of this was vectors can be deconstructed into their x,y, and z components.

I’m trying to extract and use the properties of the point itself to 1. keep from having to pass the extra vector info from the component that constructs the points (and vectors). 2. use a data type - vectors - which may be confusing to the end user.

Thanks for any additional input you may have.

(And to all - my apologies I’m not posting more code here, it’s actually split between a few class files and would be hard to gather together and format clearly. If we get further into the issue, I’ll do so.)

As far as I know there is no direct conversion method between DataTree and GH_Structure. They are relatively similar, as pointed out it’s basically a dictionary with a GH_Path as key and a List as value. GH_Structure has the constraint that T needs to be of type IGH_Goo. I have not tested this, but maybe that’s something for you:

public GH_Structure<GH_Point> toStructure(DataTree<Point3d> tree)
{ 
   GH_Structure<GH_Point> structure = new  GH_Structure<GH_Point>();

   foreach(var path in tree.Paths)
   {
     List<Point3d> branch = tree.Branch(path);
     foreach (Point3d pt in branch)
     {
       structure.Append(new GH_Point(pt), path);
     }
   }
   return structure;
}
1 Like

As for the initial question, maybe something like this will work:

public GH_Path findPath(DataTree<Point3d> tree, Point3d pt, double tol)
{
  foreach (var path in tree.Paths)
  {
    List<Point3d> branch = tree.Branch(path);
    if (branch.Count != 1)
      throw new ArgumentException("More than 1 item per branch, aborted.");

    Point3d current = branch[0];
    if (current.DistanceTo(pt) < tol)
    {
      return path;
    }
  }
  return null;
}

You can get nx, ny and nz by doing for example this:

var path = findPath(tree, pt, 1.0E-3);

if (path != null && path.Indices.Length >= 3)
{
  int nx = path.Indices[0];
  int ny = path.Indices[1];
  int nz = path.Indices[2];
}

I haven’t tested this.

2 Likes

As iterating over the entire structure is not super fast, in case you do those queries a lot, you might want to consider an RTree or something similar. See the attached example, has potential for improvement…

test_search.gh (33.6 KB)

1 Like

Hi @duanemclemore

In my opinion is best practice to create a platform independent library. This means that it is much better to use .NET’s native data structures like array, multidimensional array, jagged array, stacks, hash tables etc… This will make your code be easily ported to other software’s outside of the Rhino ecosystem. All the data tree stuff related to “depth” or “paths” just gets to freeky in my opinion. If you want something that behaves similar to a DataTree or a GH_Structure you can use a Jagged Array which is basically an array of arrays. This type of data structure is best used when your data is not rectangular. This means that each array will have a different length, like DataTrees usually have

Jagged arrays are also very quick to access by index, because the C# runtime is highly optimized for arrays. Do note that they will consume more memory to allocate because each array will occupy its own memory space in the heap. Making it more time consuming for the Garbage Collector to clear out memory.

For example, if your input will be a DataTree from Grasshopper you can convert it easily to a Jagged Array as such


     /// <summary>
        /// Convert Data Tree to Jagged Array data structure
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">Data tree to convert</param>
        /// <returns></returns>
        public static T[][] ToJaggedArray<T>( DataTree<T> data)
        {
            // formula for getting items in branches = total number of elements / branch count
            int totalElementsPerArray = data.DataCount / data.BranchCount;
            int numElements = data.BranchCount;

            T[][] observations = new T[numElements][];
        
            for (int i = 0; i < observations.Length; i++)
            {
                T[] sub = new T[totalElementsPerArray];
             
                for (int j = 0; j < totalElementsPerArray; j++)
                {
                    sub[j] = data.Branch(i)[j];
                }
                observations[i] = sub;
            }
            

            return observations;
        }

And when you are ready to display some stuff in Rhino :

        /// <summary>
        /// Convert Jagged Array to a Data Tree
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data">Jagged Array to manipulate </param>
        /// <returns> A Grasshopper Data Tree matching the same data structure as the input 
        /// Jagged Array </returns>
        public static DataTree<T> ToDataTree<T>(T[][] data)
        {
            DataTree<T> dataTree = new DataTree<T>();

            int totalBranches = data.Length;

            for (int i = 0; i < data.Length; i++)
            {
                
                for (int j = 0; j < data[i].Length; j++)
                {
                    dataTree.Add(data[i][j], new GH_Path(i));
                }
            }
        
            return dataTree;
        }

Note: I am using something called Generics in C#, that is what the T is basically telling the compiler. This means that your data structures can hold anything you need.

I hope, this helps.

PS: I think eventually you will need to start posting some code, so we can see what you are trying to do, this can be more efficient in how people can help you as well. There can be many misunderstandings sometimes. So its better to be as explicit as possible with the problem you are having and need help with.

PSS:

Here you can see what I was referring to about memory allocation differences

Jagged Array

 long t1 = GC.GetTotalMemory(true);

    int[][] jaggedArr = new int[1000][];
    for (int i = 0; i < 1000; i++)
    {
     //Notice how each element of jaggedArr is an array that will hold 250 elements.
     // Or a "Branch" with 250 elements
      jaggedArr[i] = new int[250];
    }
    long t2 = GC.GetTotalMemory(true);

this will occupy:

2D Array


    long t3 = GC.GetTotalMemory(true);

    int[,] multArr = new int[1000, 250];

    long t4 = GC.GetTotalMemory(true);

this will occupy:

So the Jagged Array will occupy 32,392 + bytes !

2 Likes

the function was get_Branch(i)

so you have your paths:
{0;0;0}
{0;0;1}
{0;1;0}
{0;1;1}
{1;0;0;}
and so on.

get_Branch(i) would get the data on the branch with index i, so if i is 2, it would get the data on branch:
{0;1;0}
I used an if statement to check if it matches the item you are looking for and return from the list of paths, the path at index of i.

So it would return the following path:
[0,1,0]
Explained in dsonntag’s post.

You can treat this path as an integer array and modify it.
Or if you know what path the data is on specifically.
You can use:
structure.get_Branch(new GH_Path(new int{0,1,0}));