Grasshopper c# - set object attributes

Hi!

I was wondering how to set object attributes (properties) in rhino grasshopper c# script.
Elefront plugin would do the job, unfortunately it does not support Mac OS.

I wish to set key and value string on curves to use object information for rhino inside Revit.

here… where you can check properties/attributes in rhino

I tried this method ObjectAttributes.SetUserString
https://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_DocObjects_ObjectAttributes_SetUserString.htm

and wrote short script below :

ObjectAttributes attri = new ObjectAttributes();
attri.SetUserString(key, value);

foreach(GH_Path path in crvs.Paths){
foreach(Curve cry ln in crvs.Branch(path)){
crv.Attributes = attri;
}
}

and it returns an error message says

  1. Error (CS1656): Cannot assign to ‘Attributes’ because it is a ‘method group’ (line 74)

Could you give some advice how to make this script work?
Thanks! :slight_smile:

something like this may help:

private void RunScript(GeometryBase geo, object y, ref object A)
  {
    var oa = new Rhino.DocObjects.ObjectAttributes();
    oa.SetUserString("test", "value");
    Rhino.DocObjects.Tables.ObjectTable ot = Rhino.RhinoDoc.ActiveDoc.Objects;
    ot.Add(geo, oa);
  }
1 Like

Thanks! your suggestion worked as I expected.
Could you help me one more question?

Here is the screenshot of my script

when I bake the result, it creates duplicated line (one with attributes, the other without attributes) and I assume it is because I created new Rhino.DocObjects.Tables.ObjectTable class.
Screenshot 2021-08-08 at 16.31.37

Is there any way to cull duplicated line (the line without attributes)?

Here attached .gh script just in case.
duplicatedResult.gh (18.1 KB)

Thanks for your help!

What is very strange is that you have 420 lines going into your C# script and 420 lines coming out. Yet baking the output Line gives you 840 lines while baking the input Line gives only 420, as expected.

missing

P.S. Oh, I see what’s happening by toggling the ‘run’ input. The lines are already “baked” by just running the script so there is no need to bake the output?

“Just in case”? It’s always valuable to post code!

Not sure why you are looping in that (strange?) manner through the tree, but this way seems to work.

 if(run)
    {
      ObjectAttributes attr = new ObjectAttributes();
      attr.SetUserString(key, value);
      for (int i = 0; i < baselines.BranchCount; i++)
      {
        for (int j = 0; j < baselines.Branch(i).Count; j++)
        {
          Rhino.DocObjects.Tables.ObjectTable ot = Rhino.RhinoDoc.ActiveDoc.Objects;
          ot.AddLine(baselines.Branch(i)[j], attr);
        }
      }
      A = baselines;
    }
1 Like

It works the same way as what @Jane.Cho already had. The ‘AddLine()’ method “Adds a line object to Rhino”, effectively “baking it”, so baking the output is what causes the duplication.

https://developer.rhino3d.com/api/RhinoCommon/html/Methods_T_Rhino_DocObjects_Tables_ObjectTable.htm

2 Likes

Not sure, can you attach these properties without baking?
how would you do it in elefront?

1 Like

In elefront component, if user update/change attribute value (key or value string) and click ‘bake’ button it automatically update attributes without baking new object.
However in my script, if I change the attribute value, the component bake new object with updated input value. Thus I get two objects with different attributes in Rhino.
Anyway thanks a lot for the comments! :slight_smile:

So it just works for rhino objects as I understand for now, not for gh geometry.

To be a little more precise about this, your C# code “bakes” your lines with assigned attribute (key, value) due to ‘ot.AddLine()’ but the attribute is not added to the ‘A’ output.

And adding a ‘nl’ array doesn’t help (I just checked):

private void RunScript(bool run, DataTree<Line> baselines, string key, string value, ref object A)
{
    if(run){
      ObjectAttributes attr = new ObjectAttributes();
      attr.SetUserString(key, value);
      Line[] nl = new Line[baselines.DataCount];
      int i = 0;
      foreach(GH_Path path in baselines.Paths){
        foreach(Line ln in baselines.Branch(path)){
          Rhino.DocObjects.Tables.ObjectTable ot = Rhino.RhinoDoc.ActiveDoc.Objects;
          ot.AddLine(ln, attr);
          nl[i] = ln;
          i = i + 1;
        }
      }
      A = nl;
    }
}

duplicatedResult_2021_Aug9a.gh (22.7 KB)

Neither does this:

private void RunScript(bool run, DataTree<Line> baselines, string key, string value, ref object A)
{
    if(run){
      ObjectAttributes attr = new ObjectAttributes();
      attr.SetUserString(key, value);
      List<Line> nl = new List<Line>();
      foreach(GH_Path path in baselines.Paths){
        foreach(Line ln in baselines.Branch(path)){
          Rhino.DocObjects.Tables.ObjectTable ot = Rhino.RhinoDoc.ActiveDoc.Objects;
          ot.AddLine(ln, attr);
          nl.Add(ln);
        }
      }
      A = nl;
    }
}

duplicatedResult_2021_Aug9b.gh (22.4 KB)

1 Like

Thanks for the comment! You are correct, by using AddLine method it automatically bake lines in Rhino.

Here I figured out the script that works for my situation. It is no professional code, but hopefully this will help someone who has similar question :slight_smile:

if(bake){
    ObjectAttributes attr = new ObjectAttributes();
    Rhino.DocObjects.Tables.ObjectTable ot = Rhino.RhinoDoc.ActiveDoc.Objects;
    attr.Name = bakeName;

    //delete previous table by filtering 'bakeName'
    Rhino.DocObjects.ObjectEnumeratorSettings settings = new Rhino.DocObjects.ObjectEnumeratorSettings();
    settings.NameFilter = bakeName;
    System.Collections.Generic.List<Guid> ids = new System.Collections.Generic.List<Guid>();
    foreach (Rhino.DocObjects.RhinoObject rhObj in doc.Objects.GetObjectList(settings)){
        ids.Add(rhObj.Id);
    }

    if (ids.Count == 0){
        Print("No objects with the name " + bakeName);
    }else{
        //delete previous object table by ID in Rhino
        foreach (Guid id in ids) {
            ot.Delete(id, true);
        }
    }

//add new attributes in object table
    foreach(GH_Path path in baselines.Paths){
        int level = path.Indices[0];
        for(int i = 0; i < baselines.Branch(path).Count; i++){
            //bakeName
            attr.SetUserString(attri[0], bakeName);
            string floorLevel = "Level_" + level.ToString();
            attr.SetUserString("FloorLevel", floorLevel);
            ot.AddCurve(baselines.Branch(path)[i], attr);
        }
    }
}