Using Arrays in C#

Hi All,

I am developing a component, but am still new to C#. We can get in to the specific code of my question a little later, but let me explain my purpose first and there may be a general answer.

I am trying to generate lattices of 3d points. The eventual goal is to generate very large lattices, so I am trying to use the fastest calculation methods I can. One of my requirements is to be able to select or describe the position of any single item within this lattice as minimally as possible. A one-dimensional array with three elements for each point would be ideal, with an element for the x, y, and z position within the lattice.

I have had no problems using simple nested for loops to generate the x,y, and z indices as separate lists. However, Iā€™ve tried everything I possibly could to get an array out, to no avail.

The VS build is always successful, but when I get to the Grasshopper canvas, thereā€™s usually an error that it could not convert from int32[] to GH_Integer.

Iā€™ve also tried to build Data Trees using the same loops, unsuccessfully.

Iā€™ve gotten output that Grasshopper will actually recognize from using a one-dimensional Rhino.Geometry.Matrix. However, it never recognizes the looping and only outputs one matrix with the largest index in each direction in that column. And Iā€™m also afraid that since thatā€™s a ā€œgeometryā€ type, it will increase the calculation time once Iā€™m up into the very, very many lattice points.

And on the note of a matrix, a single 3d matrix would be a swell way to describe the lattice point positions, but I fear that it would be the most time consuming addressing method possible.

Anyway - thatā€™s the whole background. If anyone has a way to solve my int32[] to GH_Integer problem, that would solve it in a way I could move on with. But if you all need more code to understand whatā€™s actually going wrong, we can get into that.

Thanks in advance for any help!

Duane

Arrays are reference types and include a length integer. If all your arrays are always the same size, a struct with n fields is probably more efficient. Or otherwise a single array containing all the values, i.e. (x_0, y_0, z_0, x_1, y_1, z_1, ā€¦, x_n, y_n, z_n)

1 Like

In general Grasshopper adds plenty of overhead, so if ultra efficiency is your goal it may not be the best platform for you.

There are some things you can do however:

  1. If you make your components in VisualStudio, create your own lattice type. This will avoid GH trying to iterate over your data seeing if it can interpret any of the elements.
  2. If your code runs inside a Script component, try outputting the lattice (which seems to be made up of nested arrays) wrapped inside a GH_ObjectWrapper. Any data coming out of a script needs to be converted into some sort of IGH_Goo before it can be stored inside a parameter. The object wrapper does this with minimal overhead.
1 Like

Hi David,

Thank you for the speedy and clear replies. I am indeed using VS - I tried initially to implement the lattice point as a type, but the learning curve is still steep. Iā€™ll have to dig further into the subject to understand it better and try again, as it did seem the most logical way initially.

I literally just been trying to declare the arrays of a length set prior to running, but which was variable based on what I am inputting for the number of points in that direction. That part seemed to work - I got it to where that was at least returning a 0 for each of the values. But trying to do a jagged list where each of the indices on the array returned a 3-element array was running into int to GH_Int conversion problems as well. I think Iā€™m going to try to go back to making a lattice type.

The underlying for-loop logic works. As I think I mentioned above, I am able to generate a list of the index in the given dimension for any point, and a list of the indices sequentially (as if the trees of trees had been flattened to a single list). If there was a way to use that index to grab the matching element from each of the three lists and compile them into an array that could be recognized by Grasshopper, that would be ideal. Heck - thinking out loud here - I could just treat them as vectors. But since thatā€™s a ā€œgeometricā€ object, is there a computational hit?

Thanks again,

Duane

p.s. Yes, Grasshopper isnā€™t the best for this kind of work. The intent of doing it in C# is to write external libraries for the geometries these are eventually controlling, potentially to be applied within other software. But Iā€™m writing it for GH because Iā€™m very familiar with it, use it as a teaching tool, and I am implementing some interesting math thatā€™s been missing from architectural computation for a long time that I think would be good to add to the Grasshopper toolkit.

OK, apologies if there are a couple update posts as I try to work through the issues. This is more thinking out loud, but if you see something I can work with, just point it out.

Using Matrices
I am one error away, in a couple different ways in VS. And when I get it to zero errors in VS, similar type conversion issues just pop up when I place the component.

  1. Basically, if I try to assign the int values from my iterator loops - or those same values converted to GH_Integers - straight into the GH_Matrix, it gives me

"Cannot implicitly convert type ā€˜Grasshopper.Kernel.Types.GH_Integerā€™ to ā€˜Grasshopper.Kernel.Types.GH_Matrixā€™ "

Fair enough.

Although I canā€™t figure out whatā€™s going wrong, as Iā€™m not trying to ā€œconvertā€ rather just to assign GH_Integer values to individual Matrix elements such as [0,0] and [0,1]

  1. So Iā€™ve tried adding the GH_Integer values to a GH_Integer[] array. Iā€™ve tried both a single-dimension array [3] and a two dimension array [1,3].
    I call this lpArray2, and Iā€™m trying to convert it to type GH_Matrix[ , ], called lpArray1.

Using GH_Convert.ToGHMatrix_Secondary(lpArray2, ref lpArray1); I get the error:

Argument 2: cannot convert from ā€˜ref Grasshopper.Kernel.Types.GH_Matrix[,]ā€™ to ā€˜ref Grasshopper.Kernel.Types.GH_Matrixā€™

How do I either a. get the ā€œpManager.AddMatrixParameterā€ to recognize an output of GH_Matrix[*,*] or convert from GH_Matrix[*,*] to GH_Matrix[ ] ?

Note that when I donā€™t try to convert, I get the same error when placing the component on the Grasshopper canvas.

I have to step out, but Iā€™m sure Iā€™ll have more to say when I return. Iā€™d also love any advice on the things I mentioned in my first post, ie using the lists of the index in the X, Y, and Z that Iā€™ve already successfully generated to construct vectors or data trees. Thanks in advance for any input

SOLUTION:

Describing these as vectors works almost perfectly. Iā€™ve saved all the other forks of my code, but this is clean and works great for my purposes, both short and mid-term.

The only issue is that no matter what it describes them with decimal numbers. I tried feeding it system integers and GH_Integers and it didnā€™t like either. Since I am using these to describe graphs (lattices), this is extra resource usage I donā€™t need. If anyone knows a way in C# to implement integer-only vectors for use in graph theory applications, that would be helpful to me and I think it has great potential for future use in Grasshopper.

(Maybe as my understanding of how to code grows I could write it, but thatā€™s a long way off.)

Looking forward to sharing what Iā€™m doing with the community once itā€™s more mature.

And thank you very much, @DavidRutten for the input!

Just make your own type. A class which wraps an int[,] and provides easy access methods. Or maybe even just an int[], it doesnā€™t really matter and as long as you provide a proper abstraction you can always swap it out behind the scenes.

Donā€™t use nested array, i.e. int[][]. Although that is the recommended approach, it means you waste an array instance per coordinate.

Once you have your own type, youā€™ll need to make a goo wrapper and possibly a parameter for it. I can help out with that if need be.

1 Like

Hi David, thanks yet again. Iā€™ve tried making the class, using int [] and this seems to work.

However, I am still running into the problem getting these out into Grasshopper due to problems converting from int to GH_Integer or int[] to GH_Integer[].

I think at least the logic is still working, as it outputs as many nulls from this output as there are points in the lattice.

Could I get your assistance in making a goo wrapper? You suggested a parameter as well, which would be awesome. Let me know how to take you up on this. And thanks again.

If you post a (possibly somewhat simplified) version of your custom type, I can try and create goo and a parameter for it.

Iā€™m somewhat confused about the casting to GH_Integer though. Surely the whole point of this exercise is to avoid getting data trees and GH_Integer involved due to their inefficiency?

Hi David

Iā€™m somewhat confused about the casting to GH_Integer though. Surely the whole point of this exercise is to avoid getting data trees and GH_Integer involved due to their inefficiency?

Maybe we can start over on that one - I donā€™t want GH_Integers. But every time I place the compiled component on the canvas when it was passing out System intā€™s or system int[]'s, it gives me a warning that it was unable to convert int[] to GH_Integer[]. So I assumed I had no option, even though Iā€™ve written plenty of simpler C# components which passed out integers.

If I could use System classes, that would be far preferable - especially if as you say thereā€™s an inefficiency there (which I also didnā€™t know about).

Regarding your first question - Iā€™m new to writing in OOP, so Iā€™m still getting my head around types and classes, etc. My custom class is just inside my component.

(I played around with saving them as separate cs files, which the SolveInstance didnā€™t like. I assume that has to do with setting proper access, but Iā€™m still really, really a noob at that.)

The class itself is simply:

    public class Lattice
    {
        public int[] LatticeVector = new int[3] { X, Y, Z };
        public static int X { get; set; }
        public static int Y { get; set; }
        public static int Z { get; set; }

    }

[edited to add: is this not returning my int[3] because LatticeVector is the 3-element array? I tried defining x, y, and z as sub-methods of LatticeVector (or whatever you call that), but it threw a TON of errors. It looks like the data is coming out of the Solve Instance as a 3 element array when it doesnā€™t convert to GH_Integer, so if I can solve that, I can answer my own questionā€¦]

Then down in the solve instance, I have my iterators in the x, y, and z direction which are called i, j, and k.
(I know these could have been the class fields X, Y, and Z but I am assuming itā€™s simpler not to call the class every time and just assign the value later? Or is it quicker to combine the two operations?)

So down in my solve instance I just define these, add them to the object instance, LatticeArray, and then add that to the list for output, latticeArrayList

                Lattice.X = i;
                Lattice.Y = j;
                Lattice.Z = k;

                Lattice LatticeArray = new Lattice();
                
                latticeArrayList.Add(LatticeArray);

Iā€™d like to eventually make a class for latticeArrayList, so that I could start handling these as parameters within Grasshopper.

OK, so this is all now of type ā€œLattice,ā€ rather than of type int or GH_Integer.

When I set this to be output from the component at the end of the solve instance, I simply use

               DA.SetDataList(2, latticeArrayList);

And then back up in Register Output Parameters - and not this seems like it might be the problem - I try to send it out of the component with

pManager.AddIntegerParameter("XYZ Array", "XYZ", " X, Y, and Z Indices of Vertex", GH_ParamAccess.list);

Now, I donā€™t know how to set a custom parameter for passing the information out. But it does seem that if I understood how to interpret the documentation, I could make a custom parameter with AddParameter to send out a three element arrayā€¦

OK, I hope thatā€™s enough to go onā€¦ Thanks again for your help!

Hi @duanemclemore

Itā€™s not exactly clear to me what you are after. But I will take it that you want to implement graph theory in C# in that regard I think your least worry should be to implement the IGH_Goo interface and to think in how to communicate with grasshopper, there are more pressing concerns you need to consider. Graph theory as you know is a very important part of computer science and has been researched very widely. So you have 2 options, either find a C# library that implements graph theory and itā€™s various algorithms (best idea) or code your own from scratch (cooler idea in terms of learning)

Now your problem is threefold here :

  1. You need to learn how to program in general and get a good grasp of the programming language which you will choose to start with, which seems to be C# (good idea)

  2. If you write your own Graph Theory library in the process of learning to program be prepared to spend a long time doing this, depending also in how detailed you would like to do your library.

  3. If you use another library developed by someone else you would still need to learn how to program and the main principal about Graph Theory to be able to properly implement that library in to your workflow.

Assuming you want to start to do your own here is a small example of how all this can look. Bear in mind I myself am not an expert either hehehe

Firstly a base Node class can be created. Depending on your data structure, for example a Tree, nodes will have different characteristics, for example nodes in trees can only have a maximum of 2 neighbours

 public class Node<T>
    {
        private T m_value;
        private NodeContainer<T> m_neigbhbours;
        private string m_name;


        public Node() { }

        /// <summary>
        /// Constructs a Node with a value
        /// </summary>
        /// <param name="val">A generic value</param>
        /// 
        public Node(T val)
        {
            m_value = val;
            m_neigbhbours = new NodeContainer<T>();
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
        public Node(T val, string name)
        {
            m_value = val;
            m_name = name;
            m_neigbhbours = new NodeContainer<T>();
        }

        /// <summary>
        /// Constructs a Node with a value and a collection of neighbors;
        /// </summary>
        /// <param name="val">A generic value</param>
        /// <param name="neighbours">A NodeContainer object </param>
        public Node(T val, NodeContainer<T> neighbours)
        {
            m_value = val;
            m_neigbhbours = neighbours;
        }


        /// <summary>
        /// 
        /// </summary>
        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }


        /// <summary>
        /// Get all neighbors of node
        /// </summary>
        public NodeContainer<T> Neighbors
        {
            get { return m_neigbhbours; }
            set { m_neigbhbours = value; }
        }

        /// <summary>
        /// Get value stored in node
        /// </summary>
        public T Value
        {
            get { return m_value; }
            set { m_value = value; }

        }




    }

A GraphNode object inheriting from a base Node class. Nodes in graphs can have N amounts of neighbors and other properties so it seems like a good idea to extend the Node class functionality.

public class GraphNode<T>: Node<T>
    {
        private List<double> m_weights;
       
        public GraphNode():base()
        {
            m_weights = new List<double>();
        }

        /// <summary>
        /// Construct a node from a generic value
        /// </summary>
        /// <param name="value"></param>
        public GraphNode(T value):base(value)
        {
            m_weights = new List<double>();
        }


        public GraphNode(T value,string name) : base(value,name)
        {
            m_weights = new List<double>();
        }


        /// <summary>
        /// Construct node from a generic value and a collection of neighbors
        /// </summary>
        /// <param name="value"></param>
        /// <param name="neighbours"></param>
        public GraphNode(T value, NodeContainer<T> neighbours) : base(value, neighbours)
        {

            m_weights = new List<double>();
        }

        /// <summary>
        /// Get a list of weights from current node
        /// to all neighbors
        /// </summary>
        public List<double> Weights
        {
            get { return m_weights; }
            set {  m_weights = value; }
        }


        /// <summary>
        /// Adds an unweighted directed edge from this node to a given node
        /// </summary>
        /// <param name="node"></param>
        /// <param name="weight"></param>
        public void AddUnWeightedDirectedEdge(GraphNode<T> node)
        {
            this.Neighbors.Add(node);
        
        }


        
        /// <summary>
        /// Adds an a weighted directed edge from this node to a given node
        /// </summary>
        /// <param name="node"></param>
        /// <param name="weight"></param>
        public void AddWeightedDirectedEdge(GraphNode<T> node, double weight)
        {
            this.Neighbors.Add(node);
            this.m_weights.Add(weight);
        }





    }

Not sure if this is really needed, but maybe an idea? it could also be even a batter idea to make it inherit from a Dictionary insteadā€¦

 public class NodeContainer<T>: Collection<Node<T>>
    {
        public NodeContainer() : base()
        { }

        public NodeContainer(int numOfItems)
        {
            for (int i = 0; i < numOfItems; i++)
            {
                base.Items.Add(default( Node<T>));
            }
        }


        /// <summary>
        /// Tries to find a node in the collection by a unique value
        /// </summary>
        /// <param name="value">Value to search for</param>
        /// <param name="node">the returned Node on success,</param>
        /// <returns>true on success false on failure</returns>
        public bool TryFindByValue(T value, out Node<T> node)
        {
            bool result = false;
            node = null;
            foreach (Node<T> n in base.Items)
            {
                if (n.Value.Equals(value))
                {
                    result = true;
                    node = n;
                    break;
                }
            }
                                
            return result;
        }
    }

And finally the Graph classā€¦

public class Graph<T>
    {
        private readonly NodeContainer<T> m_nodes;


        /// <summary>
        /// default constructor
        /// </summary>
        public Graph()
        {
            m_nodes = new NodeContainer<T>();
        }

        /// <summary>
        /// Initialize a graph from an existing node collection
        /// </summary>
        /// <param name="nodeCollection"></param>
        public Graph(NodeContainer<T> nodeCollection)
        {
            m_nodes = nodeCollection;
        }


        /// <summary>
        /// Get node Collection
        /// </summary>
        public NodeContainer<T> Nodes
        {
            get { return m_nodes; }
        }


        /// <summary>
        /// Get total number of nodes in Graph
        /// </summary>
        public int TotalNodes
        {
            get { return m_nodes.Count; }
        }



        /// <summary>
        /// Display Adjacency information of a graph
        /// </summary>
        /// <param name="graph"></param>
        /// <returns></returns>
        public static DataTree<T> DisplayAdjacencyList(Graph<T> graph)
        {
            DataTree<T> d = new DataTree<T>();
            for (int i = 0; i < graph.Nodes.Count; i++)
            {
                GH_Path p = new GH_Path(i);
                var neighbours = graph.Nodes[i].Neighbors;

                for (int j = 0; j < neighbours.Count; j++)
                {
                    d.Add(neighbours[j].Value, p);
                }
            }

            return d;
        }


        /// <summary>
        /// Display Adjacency weights of a graph
        /// </summary>
        /// <param name="graph"></param>
        /// <returns></returns>
        public static DataTree<double> AdjacencyListWeights(Graph<T> graph)
        {
            DataTree<double> d = new DataTree<double>();
            for (int i = 0; i < graph.Nodes.Count; i++)
            {

                GraphNode<T> node = graph.Nodes[i] as GraphNode<T>;
                List<double> weights = node.Weights;

                for (int j = 0; j < weights.Count; j++)
                {
                    GH_Path p = new GH_Path(i);
                    d.Add(weights[j], p);
                }


            }

            return d;
        }



        /// <summary>
        /// Adds a Node to the graph
        /// </summary>
        /// <param name="node"></param>
        public void AddNode(GraphNode<T> node)
        {
            if (!m_nodes.Contains(node))
            {
                m_nodes.Add(node);
            }

        }


        /// <summary>
        /// Check if graph contains a node by specifying a unique value
        /// </summary>
        /// <param name="value"></param>
        /// <param name="node"></param>
        /// <returns></returns>
        public bool Contains(T value, out GraphNode<T> node)
        {
            bool result = false;
            Node<T> _node = null;

            if (m_nodes.TryFindByValue(value, out _node))
            {

                result = true;
            }

            node = _node as GraphNode<T>;
            return result;

        }


  


        public void Remove(T value)
        {


            //nodeToRemove = null;
            if (!Contains(value, out GraphNode<T> nodeToRemove))
            {
                throw new ArgumentNullException("The node you are trying to remove does not exist!");
            }

            else
            {
                // Remove node form node collection
                m_nodes.Remove(nodeToRemove);


                // loop through all nodes an remove the desired node from all the other nodes
                // neighbors and corresponding weights 
                for (int i = 0; i < m_nodes.Count; i++)
                {
                    GraphNode<T> graphNode = m_nodes[i] as GraphNode<T>;

                    // Get index of node to remove
                    int index = graphNode.Neighbors.IndexOf(nodeToRemove);


                    graphNode.Neighbors.RemoveAt(index);
                    graphNode.Weights.RemoveAt(index);
                }
            }



        }

And a terrible example of how to use this ā€¦ lol

 private void RunScript(List<string> z, List<Point3d> x, ref object A, ref object B)
  {

    Graph<string> graph = new Graph<string>();

    for (int i = 0; i < z.Count; i++)
    {
      graph.AddNode(new GraphNode<string>(z[i]));
    }

    GraphNode<string> n_A = graph.Nodes[0] as GraphNode<string>;
    GraphNode<string> n_B = graph.Nodes[1] as GraphNode<string>;
    GraphNode<string> n_C = graph.Nodes[2] as GraphNode<string>;
    GraphNode<string> n_D = graph.Nodes[3] as GraphNode<string>;
    GraphNode<string> n_E = graph.Nodes[4] as GraphNode<string>;
    GraphNode<string> n_F = graph.Nodes[5] as GraphNode<string>;

    n_A.AddWeightedDirectedEdge(n_B, 3);
    n_A.AddWeightedDirectedEdge(n_C, 3);

    n_B.AddWeightedDirectedEdge(n_A, 1);
    n_B.AddWeightedDirectedEdge(n_D, 3);
    n_B.AddWeightedDirectedEdge(n_F, 6);
    n_B.AddWeightedDirectedEdge(n_E, 2);

    n_C.AddWeightedDirectedEdge(n_A, 1);
    n_C.AddWeightedDirectedEdge(n_E, 3);

    n_D.AddWeightedDirectedEdge(n_B, 3);

    n_E.AddWeightedDirectedEdge(n_B, 9);
    n_E.AddWeightedDirectedEdge(n_C, 7);

    n_F.AddWeightedDirectedEdge(n_B, 10);


  
    A = Graph<string>.DisplayAdjacencyList(graph);

    B = Graph<string>.AdjacencyListWeights(graph);
  }

So this is just the ā€œdata structure partā€ there is a whole sea of algorithms out there to traverse graphs and also in my version I am not adding the functionality to make it undirected or directed

4 Likes

@duanemclemore, ok I also just saw your code.

I think you are doing some very weird stuff. What is the purpose of of the Lattice class? it cant just be a 1D array with 3 elements, it has no functionality for graph theory applications as you can see on my above examples there are many more things to add.

In my head the Lattice class you are trying to do is more like a " Field " of data or, better yet a Graph which in your case it will store a N points. So I ask you again, what are you trying to achieve?

If you just want to store some objects with properties in a Graph class you can do something like this to start with

public class Node<T>
    {
        private T m_value;
        private Point3d m_position
       


        public Node() { }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="val"></param>
        public Node( Point3d position, T val)
        {
            m_value = val;
            m_position = position;

        }



        /// <summary>
        /// Get value stored in node
        /// </summary>
        public T Value
        {
            get { return m_value; }
            set { m_value = value; }

        }
		
		
		public Point3d Position
		{
			get { return m_position; }
            set { m_position = value; }
		}




    }

public class Graph
	
	{
		private int m_columns;
        private int m_rows;
		private int m_height;
        private double m_resolution;
        private Node<T>[,,] m_Field;
		
		/// Create Graph Object
		public Graph(int columns, int rows,int height, double resolution, T [,,]data)
        {
            m_columns = columns;
            m_rows = rows;
			m_height = height
            m_resolution = resolution;

            m_Field = new Node<T>[m_columns, m_rows,m_height];


            /// Initialize  nodes
            for (int i = 0; i < m_columns; i++)
            {
                for (int j = 0; j < m_rows; j++)
                {
					
					for (int j = 0; j < m_rows; j++)
					{
					
						m_Field[i, j,k] = new Node<T>( new Vec3(i * m_resolution, j * m_resolution, 0), data[i, j,k]);

					}

                }
            }



        }
		
		
		
		
		
		  public int Columns
        {
            get { return m_columns; }
            set{m_columns = value;}

        }




        public Node<T>[,,] Field
        {
            get { return m_Field; }
            set{ m_Field = value;}

        }




        public Point3d [,] Grid
        {
            get { return DisplayField(); }
        }



        public double Resolution
        {
            get { return m_resolution; }
            set{m_resolution = value}

        }

        public int Rows
        {
            get { return m_rows; }
            set{m_rows = value}

        }
		
		#region METHODS
		
		// Here you can all all your methods
		
		vecs[i, j] = (Point3d)m_Field[i, j].Position;
		 private Point3d[,] DisplayField()
        {
            Point3d[,,] pts = new Point3d[m_columns, m_rows,m_height];

            for (int i = 0; i < m_columns; i++)
            {
                for (int j = 0; j < m_rows; j++)
                {
                      for (int k = 0; k < m_height; k++)
					{
						pts[i, j,k] = m_Field[i, j,k].Position;
					}
                }
            }

           
            return pts;
        }
		
		
		#enregion
		
		
		
	}

In your grasshopper component:


	 protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
        {


            pManager.AddIntegerParameter("columns", "columns", "", GH_ParamAccess.item, 50);
            pManager.AddIntegerParameter("rows", "rows", "", GH_ParamAccess.item, 50);
			pManager.AddIntegerParameter("height", "height", "", GH_ParamAccess.item, 50);
            pManager.AddNumberParameter("resolution", "resolution", "", GH_ParamAccess.item, 1);
            pManager.AddNumberParameter("values", "", "", GH_ParamAccess.list);



        }

        /// <summary>
        /// Registers all the output parameters for this component.
        /// </summary>
        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
            pManager.AddGenericParameter("Graph", "Graph", "", GH_ParamAccess.item);
			pManager.AddPointParameter("Graph", "Graph", "", GH_ParamAccess.item);
        }

        /// <summary>
        /// This is the method that actually does the work.
        /// </summary>
        /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
        protected override void SolveInstance(IGH_DataAccess DA)
        {

 
            int _columns = 0;
            int _rows = 0;
			int _height = 0;
            double _resolution = 0;
			
			/// just assuming its double but data is supposed to be generic 
			List<double> _data = new List<double>();


            DA.GetData(0, ref _columns);
            DA.GetData(1, ref _rows);
			DA.GetData(2, ref _height);
            DA.GetData(3, ref _resolution);
			DA.GetDataList(4, ref _data);
			
			
				int var index = 0;
				
				double [,,] data3D = new double [_columns,_rows,_height];
				
			for (int i = 0; i < _columns; i++)
            {
                for (int j = 0; j < _rows; j++)
                {
                      for (int k = 0; k < _height; k++)
					{
						data3D[i, j,k] = _data[index++];
					}
                }
            }

			 Graph g = new Graph<double>(_columns, _rows, _resolution, data3D);
			 
			 // send to ouptut
			 DA.SetData(0, g);
			 
			 // This will show all the points in grasshopper
			 DA.SetDataList(1, g.Grid);
		}

Hi Nicholas,

Thanks for all the excellent advice. I havenā€™t read it all yet, but I wanted to be timely in my response. To clarify, that was only the relevant portion of the class. Now that the logic works, it will be folded back in to the class LatticePoint, which is a class containing many other properties for an individual point within a lattice.

The component this is part of is for generating lattices. However, as it gets more defined it is clear that I should implement LatticePoint (including the x,y,z array above) as a -sub-class- within a new one called Lattice (not to be confused with the old lazyname ā€œLatticeā€). That way I could define the properties of the lattice as a whole and pass them in to the sub-class.

Anyway - when I started writing this component, I knew it would hinge on the mathematics of graphs, which seems a bit under-implemented in Grasshopper (probably simply because there are better tools for it). But the graph theory thus far has been a means to an end. The idea of writing components for this - even if Iā€™m simply applying the extremely well developed algorithms from CS - is really exciting. But the one Iā€™m developing is actually to implement another related field of mathematics, and right now I just need to be able to define and access positions within the graph.

But what I skimmed of your commentary and code looks really helpful, and it sounds like youā€™re at least curious about whatever ā€œweirdnessā€ Iā€™m up to LOL. Iā€™ll respond to your messages more fully soon, etc. Iā€™ll also share more as I go with the forums here. (as I come up with better and better problems :wink: )

Thanks again for your time and advice!

D

Bit behind the curve here, but maybe this will help you out somewhat anyway. I attached some code which shows how to implement a heavy data type efficiently, along with all type levels (goo, parameter, components).

The idea here is that thereā€™s a Node type (only used temporarily as a handy container for related data) and a Lattice type which goes to some lengths to minimise its memory footprint.

Both the Node and Lattice types are immutable, this makes it fully thread-safe. You may not want this depending on your use-case though, you may want to be able to modify small portions of the Lattice type specifically. However the way its set up now any ā€˜array-basedā€™ operations can be done exceedingly fast, because the data is stored in arrays. So appending more items, re-ordering, getting sub-sections, ā€¦, itā€™s all really efficient.

I chose not to draw any of the nodes in the preview as that would require you to maintain a separate array of Point3d structures, which would more double your memory usage, but itā€™s something you may want to consider. For example you could choose to cache every 10th or 100th point to be used in the preview only.

Lattice.cs (16.4 KB)

I didnā€™t really test much of the code, itā€™s pretty much boiler-plate-stream-of-consciousness stuff, so beware.

Hi David and @rawitscher-torres, thanks again to both of you for all the input. I am in the thick of the end of the semester at my University, so I have been unable to get back around to this thread, but before it sat too long, I wanted to send my gratitude for your generous help.

Hi All, thanks again for all of your help to date. I wanted to revisit this one. I found an alternate method for the integers - I constructed paths from them and used it to organize the other data.

[[EDITED TO ADD: Iā€™m also going to try just passing the x,y, and z values as a list and see if that solves it.]]

Iā€™ve run into another similar issue though. The data describing each of the locations in Cartesian space of the Nodes. I have already built this component to create points and send them downstream, but I am trying to defer the creation of geometric point objects until later components and instead just pass the location data of where they will be when they are created.

I understand enough about passing data now that I was able to get the output component to pass my point location data as Grasshopper.Data Tree<double[]> by following the tutorial here. This Tree is constructed from the {x,y,z} indices we discussed before.

I can receive this data it in the downstream component, but the only type that will work in getting it out is GH_Structure<IGH_Goo>.

My issue is that I canā€™t seem to find any conversion / casting methods from GH_Structure<IGH_Goo> to DataTree<double[]>, even in multiple steps. Every different method I try seems to have a different insurmountable issue. Iā€™d be fine casting / converting it to any type that works.

Thanks in advance for your help, apologies for my ineptitude.

UPDATE:

In the end, I just decided to fight my battles elsewhere. Maybe some other day Iā€™ll refactor this entire thing to be class based and wrapped in GH_ObjectWrapper but for my current purposes, the easiest way was just to output each set of Cartesian coordinates as a list - converting them to GH_Numbers and putting them on a GH_Structure<GH_Number> before sending them on.

Hi David, I had made my whole Lattice Generator working by other means, but the code was getting unwieldy, so I am looping back around to this concept. I would love your assistance, as you offered, on writing a custom parameter for passing a lattice. Or even just getting double[3] to be able to be passed by GH_Number

The sum total of my problem is that right now, handling everything with the class Lattice works, and I can pass the double[3] from one component to the next, but when it comes time to output it from the 2nd file with DA.SetData, there are no issues in VS, but I get the error

One of the supplied items could not be converted parameter Type: GH_Number supplied type: double

I canā€™t post my code at the moment, but in case you need it, could post a minimal sample laterā€¦ Thanks in advance for any assistance you can offer.

(edited to add - I can also share the github with you if you want to see the issue and also get some laughs)

Alternately, @DavidRutten I can create a GH_Number[3] but canā€™t pass it out of this component because of the error:

The type ā€˜Grasshopper.Kernel.Types.GH_Number[]ā€™ cannot be used as type parameter ā€˜Tā€™ in the generic type or method ā€˜GH_Structureā€™. There is no implicit reference conversion from ā€˜Grasshopper.Kernel.Types.GH_Number[]ā€™ to ā€˜Grasshopper.Kernel.Types.IGH_Gooā€™.

It is a bit unclear, what youā€™re trying to achieve. But if I understand correctly you have a double or GH_Number and want to output it via DA.SetData? Why donā€™t you use DA.SetDataList()?

Or you could sort the arrays into tree branches and output the DataTree? Or you could cast the arrays to Vector3d structs and output those? How do you expect the output to look like?

DA.SetData expects a type derived from IGH_Goo. An array of IGH_Goo wonā€™t work. The errors are coming from that, the type of your output is not derived from IGH_Goo, so Grasshopper doesnā€™t understand what you want to do.