I’m trying to Get Generic Data Tree inside Visual Studio. Currently I’m doing it in a very hacky way by using GH_ObjectWrapper then cast this to System.Object for a method that takes in Generic Type input. I refereed to this forum post too: https://www.grasshopper3d.com/forum/topics/datatree-of-object-gh-structure-of
I have two questions
The component gives a complaint “The parameter at index 0 is of IGH_Goo. Your requested type GH_ObjectWrapper”. From my understanding GH_ObjectWrapper already implement IGH_Goo. Why does grasshopper still give this complaint?
Is there a better way to do this?
Code is below:
/// <summary>
/// Registers all the input parameters for this component.
/// </summary>
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddGenericParameter("Tree", "Tree", "Tree that has empty branches", GH_ParamAccess.tree);
}
/// <summary>
/// Registers all the output parameters for this component.
/// </summary>
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("CollaspeTree", "Tree", "Tree without empty branches", GH_ParamAccess.tree);
}
/// <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)
{
GH_Structure<GH_ObjectWrapper> tree ;
if (!DA.GetDataTree(0, out tree)) return;
var newTree = new DataTree<System.Object>();
for (int i = 0; i < tree.Branches.Count; i++)
{
GH_Path pth = new GH_Path(i);
for (int j = 0; j < tree.Branches[i].Count; j++)
{
if (tree.Branches[i].Count > 0)
{
Object o = tree.Branches[i][j].Value as System.Object;
newTree.Add(o, pth);
}
}
}
// SomeMethod(DataTree<T> newTree)
}
From my understanding IGH_Goo is an implementation not an object. I tried, it does not seem like I can manipulate the data I got by this settings further.
IGH_Goo is an interface. Any data which exists inside parameters must implement this interface so that Grasshopper knows how to display it, convert it, check it for validity etc.
Because although GH_ObjectWrapper implements IGH_Goo, many other types do so as well. The data inside your input might be GH_Number, GH_String, GH_Brep, or a myriad other types.
You need to declare the tree you’re getting using the IGH_Goo constraint, as @Will_Wang said:
GH_Structure<IGH_Goo> tree ;
if (!DA.GetDataTree(0, out tree)) return;
I assume the input would be of different types and that’s why the IGH_Goo was necessary. Not sure what “manipulation” you must do to a group of objects that can be double, Curve, or anything that really doesn’t share the same properties/methods…
yeah sorry I mean interface. I have a data tree that has empty branches, after cleaning, however, it gives back a tree that has branches which still numbered according to the old tree. Instead, I want to fully clean this tree. That means the branch 8, 20, 24, 32, 55 will be 0,1,2,3,4. The example I posted here has curve as data input but in reality I want them to be as generic as possible, including receiving custom object. I think I need to implement T : IGH_Goo but not sure where the implementation should be. (i.e where should that line of code be)
I include both cs file and gh file here. Please pardon me for not using datatree directly, I converted to nested list instead as the two convert methods were already written prior to this. @Will_Wang
I think this is what you’re after, but I’m still testing:
protected override void SolveInstance(IGH_DataAccess access)
{
access.GetDataTree(0, out GH_Structure<IGH_Goo> tree);
tree = CleanTree(tree);
access.SetDataTree(0, tree);
}
public GH_Structure<IGH_Goo> CleanTree(GH_Structure<IGH_Goo> tree)
{
// We are not allowed to modify the tree that comes out of the input.
// So we'll just create a new one and populate it over time.
var clean = new GH_Structure<IGH_Goo>();
int k = 0;
for (int p = 0; p < tree.PathCount; p++)
{
var list = tree.Branches[p];
// Ignore empty branches.
if (list.Count == 0)
continue;
// Remove nulls and invalids from the branch,
// this means make a copy of it as we are going to change it.
list = new List<IGH_Goo>(list);
list.RemoveAll(goo => goo is null);
list.RemoveAll(goo => !goo.IsValid);
// The list is empty after removing all nulls and invalids.
if (list.Count == 0)
continue;
clean.AppendRange(list, new GH_Path(k++));
}
return clean;
}
If you want the clean method to be generic, slight changes are needed:
public GH_Structure<T> CleanTree<T>(GH_Structure<T> tree) where T : IGH_Goo
{
// We are not allowed to modify the tree that comes out of the input.
// So we'll just create a new one and populate it over time.
var clean = new GH_Structure<T>();
int k = 0;
for (int p = 0; p < tree.PathCount; p++)
{
var list = tree.Branches[p];
// Ignore empty branches.
if (list.Count == 0)
continue;
// Remove nulls and invalids from the branch,
// this means make a copy of it as we are going to change it.
list = new List<T>(list);
list.RemoveAll(goo => goo == null);
list.RemoveAll(goo => !goo.IsValid);
// The list is empty after removing all nulls and invalids.
if (list.Count == 0)
continue;
clean.AppendRange(list, new GH_Path(k++));
}
return clean;
}