Working with GH_Structure


#1

I ran across a problem working with GH_Structure.

Since I am working inside VS2017, rhino6 beta and acording grasshopper, I should work with GH_Structure instead of Grasshopper.DataTree what I found out so far.

I have a problem accessing the GH_Structure again after appending data.
According to the SDK, I should be able to access a given path by

Branch(GH_Path) --> Gets a limited access pointer to the data list associated with a certain path.

but VS2017 Intellisense only gives my
.Branches and
.get_Branch

here is the general code

// create new Tree
GH_Structure<GH_Curve> LinesTree = new GH_Structure<GH_Curve>();

//Loop through lines
for (int i = 0; i < Counter; i++)
{
GH_Path path = new GH_Path(i);
// …
PolylineCurve polylinecurve = new PolylineCurve(polyline3dPoints);
GH_Curve ghPolylineCurve = new GH_Curve(polylinecurve);
LinesTree.Append(ghPolylineCurve, path);
// …
// —> PROBLEM calling .Branch || only .Branches or .get_Branch available in Intellisense VS 2017
List<GH_Curve> ghCurveList = LinesTree.Branch(path);
// …
}

I have the same problem in VS2015 and rhino5 and according grasshopper.

Thanks for your help.

Udo


(David Rutten) #2

This is weird, .NET internally replaces getter and setter properties with methods using a get_ or set_ prefix. However this little lie is supposed to be undone when Visual Studio references an assembly and provides intellisense for it. Unless you are dealing with the IntermediateLanguage or Reflection directly, you should never see those modified method names.

OH WAIT! I remember. Grasshopper is written in VB.NET which supports indexed properties. C# does not and therefore must expose these as the underlying methods. So yeah, it’s because there isn’t 100% overlap between VB and C#. You’ll just have to use get_Branch() instead, it eventually calls the same method in the assembly.


#3

Thanks David,

if i use .get_Branch() VS2017 give me this error.

List<GH_Curve> ghCurveList = LinesTree.get_Branch(path);

Fehler CS0266 Der Typ “System.Collections.IList” kann nicht implizit in “System.Collections.Generic.List<Grasshopper.Kernel.Types.GH_Curve>” konvertiert werden. Es ist bereits eine explizite Konvertierung vorhanden (möglicherweise fehlt eine Umwandlung).

Hope you understand enough german.

Thx


#4

Try this:

List<GH_Curve> ghCurveList = LinesTree.get_Branch(path)[0];

(index)

// Rolf


(David Rutten) #5

Keine probleme, ich wohne seit 4 Jahren in Tirol.

The Branch[path] or get_Branch(GH_Path) method implements a method defined in the non-generic interface IGH_Structure. As such, it cannot return a type-safe collection because IGH_Structure is not type-aware. It just returns an IList. You can perform the casting yourself like so:

List<GH_Curve> curves = LinesTree.get_Branch(path) as List<GH_Curve>;

Or you can use DataList instead of get_Branch, which is a type-safe re-implementation of the non-typesafe Branch property specifically added to GH_Structure<T>. DataList is also marked as the default property, so it may even work as intended in C# (this is untested, but I’m hopeful):

List<GH_Curve> curves = LinesTree[path];

#6

Hi David,

both solutions work fine up to my breakingpoint.
I will test it further.

I would favour the DataList so far, due to its type-saftey.

Thx,

Udo


(David Rutten) #7

Yup, type-safe, readable, no downsides.


#8

Hi David,

following up our discussion from yesterday, I have got a question how to handle Datatypes like
Curve vs. GH_Curve, etc. best.

I have to wrap and unrwap (is this the right term?) geometries like points, curves, breps and so on.
The GH_Curve class provides certain methods like tramsform, whereas GH_point doesn´t provide things, like set x-Value, y-Value etc.

My direct question is, how do I unwrap GH_structures best?

anything.Value

So far I am working in one component passing data back and forth through several methods. Initially I started of with Grasshopper.DataTree and it works fine. Right now I am rewriting everything to work with GH_Structure.

A further goal is to pass Data through multiple “customized” components, which should be able to handle data of multiple data types, since I read out / (write in, future topic) Data from anothers software API.
Kangaroo is doing similar things as I am aware, handling custom data streams?

I will dive into the more complex things of coding like Interfaces etc. anyway, but for the time being this would help me out.

Thx,

Udo


(David Rutten) #9

If you’re building GH_Structure<T> instances yourself you cannot escape the IGH_Goo wrapper types, as you can when you just write code inside a SolveInstance() method where the automatic casting will do all the wrapping and unwrapping for you.

Most wrapper types are really quite thin and do not do much apart from providing some very basic functionality (formatting, parsing text back into the local type, conversion into other types, (de)serialization, validity testing, and, in the case of ‘IGH_GeometricGoo’ some ways to transform, spacemorph and boundingbox the wrapped geometry).

There is no standard way to get the wrapped data out of an IGH_Goo instance, but all the native implementations provide a Value property that allows you to set and get the data. So if you have IGH_Goo you are out of luck because it could be anything. If however you specifically have GH_Curve then you can use the Value property to access the Rhino.Geometry.Curve inside.

Similarly, if you have a Rhino.Geometry.Curve and you want to store is into a GH_Structure<T> you will have to make a new GH_Curve and pass your curve shape to the constructor.

Mostly you will be working with specific types (only curves, or only lines, or only planes) and the conversion will be obvious. At other times you will be working with generic types and you don’t care about the actual data, in which case you never have to unwrap the goo to begin with. However sometimes you will be working with curves and lines and arcs and circles so you can’t use type-safety to make your life easier. In these cases you will either have to write quite a lot of conversion and type-checking code yourself, or you can rely on the utility methods in the GH_Convert class.


#10

Thanks David,

the Value Property works fine for me at the moment.
I will have a deeper look into the GH_Convert class.

Udo


#11

Hi David,

I have another question handling data, especially own classes through different grasshopper components.

I wrote my own class, lets call it myCustomClass, basically consisting of types like double, int, string AND some specific data type from another API.
There is no geometry.

I implemented it into the
public class myCustomClassGoo : Grasshopper.Kernel.Types.GH_Goo<myCustomClass>

The component outputs data of IGH_Goo, so there is another wrapping when outputting, since the new input data stream has to be of IGH_Goo.

How do I get the data unwrapped in another component from
IGH_Goo -> myCustomClassGoo
myCustomClassGoo -> myCustomClass
in the most useful way.

Is it possible to stream data from one component to the next in a treestructure where I have multiple myCustomClassesGoos, like a train with multiple different wagons, seating different people.

for instance
<myCustomClassGermanPeopleGoo(of GermanPeople)>,<myCustomClassItalianPeopleGoo(of ItalianPeople)>, <myCustomClassEnglishPeopleGoo(of EnglishPeople)>
and so on

Thx

Udo


#12

Addendum:

There was something wrong with the implementation .CastTo in myCustomClassGoo

now it works as followed:

private GH_Structure<GH_Point> gh_Point3D(GH_Structure<IGH_Goo> inputTree)
{
        GH_Structure<GH_Point> Point3DTree = new GH_Structure<GH_Point>();

        for (int i = 0; i < inputTree.Branches.Count; i++)
        {
            GH_Path path = new GH_Path(i);

            List<IGH_Goo> myList = inputTree[path];
            myList[0].CastTo<myCustomClass>(out myCustomClass myCC);

            if (myCC != null)
            {
                Point3d pt = new Point3d
                {
                    X = myCC.x,
                    Y = myCC.y,
                    Z = myCC.z
                };

                GH_Point ghPoint = new GH_Point(pt);
                Point3DTree.Append(ghPoint, path);
            }
           
        }

        return Point3DTree;
}

thx,

Udo


(David Rutten) #13

If you store your myCustomClassGoo inside an IGH_Goo parameter/gh_structure, then it just remains that type. It is only stored under a less specific type constraint. That is to say no actual conversion takes place. All you have to do to get the data back into a variable of your specific type is:

IGH_Goo goo = GoGetThatGoo();
myCustomClassGoo myGoo = goo as myCustomClassGoo;
if (myGoo == null)
  // Oh noes! Somebody stored the wrong kind of data.

The as keyword works really well for casting instances to more derived types. It will not perform any conversion. It differs from the casting operator (i.e. myCustomClassGoo myGoo = (myCustomClassGoo)goo;) in that it will not throw an exception when the cast fails, it’ll just return null. However the downside of this is that you cannot use as on structs, because structs cannot be null. If you have -say- an integer stored inside a System.Object and you want to get it out, you’ll have to use the is keyword and the casting operator:

object myValue;
if (myValue is int)
  int myInteger = (int)myValue;

(David Rutten) #14

.NET OOP rules say that if you store different types (that includes different flavours of a generic type) you can at best store them under the first type they all derive from.

For example if you have a generic base class Person<T> which does not specifically inherit from another class or implement some sort of interface, then the only way to store various flavours of person in a list is to use a List<object>:

public abstract class Person<T>{}
...
List<object> list = new List<object>();
list.Add(new Person<German>());
list.Add(new Person<Italian>());
list.Add(new Person<Aleut>());

If however Person<T> inherits from a non-generic class or implements a non-generic interface, then you can use that common ancestor as a type constraint in your collection class.

It sounds as though IGH_Goo may be the most recent common ancestor of all your classes though. You can of course still create a custom parameter type which is based on IGH_Goo but which allows only specific types to be stored. This will prevent some git from sending a bunch of GH_Point or GH_Integer instances into your component when you least expect it.