How to get the values of a Rhino Object using C#

Hello guys,

I’m trying to get the keys and values of the Rhino object using the C# script and I am now managed to extract the keys: PTCODE, PTELEV, PTNAME.
However, I couldn’t find the corresponding values by accessing the attributes properties.
If there is someone who can help me with it, I’d appreciate it!

Thanks a lot!

attached below is my code which managed to extract the keys:

Rhino.RhinoDoc doc = Rhino.RhinoDoc.ActiveDoc;
Rhino.DocObjects.RhinoObject[] rhobjs = doc.Objects.FindByLayer(name);

var keys = rhobjs[2].Attributes.GetUserStrings();
B = keys;



map.3dm (428.8 KB)
RhinoDocLayerFinder2.gh (3.1 KB)

Use the attached as a start - add anything more.

LayersAndRObjects_EntryLevel_V1.gh (7.2 KB)

Thanks for your reply,
I’m also wondering where can I get the values (12.38, 15) that are displayed on the image.
or which property I can apply in order to get them?

Thank you!

Found it, it’s the “.Attributes.GetUserString()” method.!
Thanks!

Found a couple of minutes more for the trad update.

LayersAndRObjects_EntryLevel_V1A.gh (117.2 KB)

BTW: The proper way to deal with some general case is to define a suitable class (see a simple demo one inside C#) and then do P/LINQ (flat of nested) queries: GroupBy, OrderBy and the likes.

1 Like

Thanks for your demonstration!
Got another question if you don’t mind.
I am trying to extract the key, values, and the corresponded geometry (in this case, it’s a 3dpoint) from the InstanceObject by using C#; I’m wondering which method or property I could apply in order to get the 3dpoint geometry?

Thanks

You mean that you have Instance Definitions to play with? (Blocks) - if so the Find should be a bit more focused.

Yeah, but I still can’t get it right…
Initially, I was planning to get the geometry by using the “.Explode” method; However, this strategy seems only applicable in InstanceObject…

The data type I got from the "doc.Objects.FindByLayer(name) " method is a “RhinoObject”, so
I think this idea can’t work…
map.3dm (441.8 KB)
RhinoDocLayerFinder2.gh (4.8 KB)

You date the wrong girl (an InstanceReference is the Definition placed in the doc, while the Definition itself is the “template”):

Anyway … if you can’t do it (it’s that easy, mind) … I’ll provide a full Instance Definition related thingy later on (how to find, how to get things, how to do chaos, how to do bad things … etc etc).

BTW: are you familiar with classes and LINQ? (in order to properly query the Instance Definitions found).

BTW: Obviously you can take the long way home: Find by Layer and then query by ObjectType:

In the mean time I have a small C# challenge for you: Assume that you have nested Layers(real-life and the likes) .By what means the path {3;7} would become the correct one {3;6;7}? Is it a small Recursion the answer? Is it Karma? Is it something else?

Hi @PeterFotiadis,
I am a complete novice with code, but I am surfing this AI wave to start doing things myself! I have been creating some nodes in Python and C# to create small utilities for my team and me, streamline some repetitive tasks, and learn something in the meantime.

I am experimenting with ClaudeAI, creating utility nodes to speed up our work in a large masterplan project. I am working in Rhino7 for Mac, as we need the connection with Speckle (only available for R7) and using the latest version of elefront.

I have reference from Speckle geometry coming from Archicad, and use elefront to bake slab outlines, with user attributes with the idea of being able to group or work with those attributes downstream.

I have created 2 utility nodes with C#:

*The second one, splits the tree, in 3 separate trees (harcoded number) based on some criteria.

Both nodes in terms of functionality work well, BUT I found that the attributes attached to the referenced curves are lost after passing by the custom nodes. I have made some research and me and Claude tried to resolve it, with no luck.

Would you be able to point me in the right direction or check my code? Happy to pass along the files with the code for your reference or use. Thank you in advance!

GROUP KEY:

DataTree<object> groupedTree = new DataTree<object>();

// Early exit if lengths mismatch
if (L.Count != K.Count)
{
  Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Inputs L and K must be of the same length.");
  return;
}

// Grouping logic
Dictionary<object, GH_Path> keyToPath = new Dictionary<object, GH_Path>();
int branchCounter = 0;

// Import Elefront namespace if you haven't already
// using Elefront; // Uncomment this if needed

for (int i = 0; i < L.Count; i++)
{
  object item = L[i];
  object key = K[i];

  if (!keyToPath.ContainsKey(key))
  {
    keyToPath[key] = new GH_Path(branchCounter);
    branchCounter++;
  }

  GH_Path path = keyToPath[key];

  // Try to extract and preserve the Rhino geometry reference
  if (item is IGH_GeometricGoo geoGoo)
  {
    // Get the actual Rhino geometry
    Rhino.Geometry.GeometryBase geometry = null;

    try
    {
      geometry = geoGoo.ScriptVariable() as Rhino.Geometry.GeometryBase;
    }
    catch
    {
      // If extraction fails, just add the original item
      groupedTree.Add(item, path);
      continue;
    }

    if (geometry != null)
    {
      // If this is referenced geometry (not a new object), try to preserve it
      if (geometry.IsDocumentControlled)
      {
        // This is a direct reference to a Rhino object, add it directly
        groupedTree.Add(item, path);
      }
      else
      {
        // This is likely a new geometry object
        // We need to duplicate it and try to copy attributes if possible

        // Option 1: Try to use Elefront's APIs if available
        // This is placeholder code - you'll need to replace with actual Elefront API calls
        // ElefrontAttributeCopier.CopyAttributes(sourceGeometry, newGeometry);

        // For now, just add the item and note that attributes might be lost
        groupedTree.Add(item, path);
      }
    }
    else
    {
      // Fallback for when we can't extract geometry
      groupedTree.Add(item, path);
    }
  }
  else
  {
    // Non-geometry objects
    groupedTree.Add(item, path);
  }
}

A = groupedTree;

// Compact 2-line status message
int itemCount = L.Count;
int branchCount = keyToPath.Count;
Component.Message = $"GrpKey v1.0\n{itemCount} items → {branchCount} branches";

BRANCH SIZE SPLIT

// Clear output trees
DataTree<object> treeA = new DataTree<object>();
DataTree<object> treeB = new DataTree<object>();
DataTree<object> treeC = new DataTree<object>();

// Get threshold values for branch sizes
int minThreshold = (int)minItems; // Branches with fewer items go to tree A
int maxThreshold = (int)maxItems; // Branches with more items go to tree C

// Validate thresholds
if (minThreshold < 0 || maxThreshold < minThreshold)
{
  Component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "minItems must be ≥ 0 and maxItems must be ≥ minItems");
  return;
}

// Initialize counters
int countA = 0;
int countB = 0;
int countC = 0;
int totalBranches = T.BranchCount;

for (int i = 0; i < totalBranches; i++)
{
  GH_Path originalPath = T.Paths[i];
  List<object> branchItems = T.Branch(originalPath);
  int itemCount = branchItems.Count;

  // Determine which output tree this branch should go to
  DataTree<object> targetTree;
  if (itemCount < minThreshold)
  {
    targetTree = treeA;
    countA++;
  }
  else if (itemCount <= maxThreshold)
  {
    targetTree = treeB;
    countB++;
  }
  else // itemCount > maxThreshold
  {
    targetTree = treeC;
    countC++;
  }

  // Process each item, preserving Rhino references when possible
  foreach (object item in branchItems)
  {
    if (item is IGH_GeometricGoo geoGoo)
    {
      try
      {
        // Get the actual Rhino geometry
        Rhino.Geometry.GeometryBase geometry = geoGoo.ScriptVariable() as Rhino.Geometry.GeometryBase;

        if (geometry != null && geometry.IsDocumentControlled)
        {
          // This is a direct reference to a Rhino object with attributes
          targetTree.Add(item, originalPath);
        }
        else
        {
          // Possibly new geometry, add as is (attributes may be lost)
          targetTree.Add(item, originalPath);
        }
      }
      catch
      {
        // Fallback if we can't extract geometry
        targetTree.Add(item, originalPath);
      }
    }
    else
    {
      // Non-geometry object
      targetTree.Add(item, originalPath);
    }
  }
}

// Assign output trees
A = treeA;
B = treeB;
C = treeC;

// Display component message
Component.Message = "BranchSizeSplit\n" +
                    countA + " branches in A (<" + minThreshold + " items)\n" +
                    countB + " branches in B (" + minThreshold + "-" + maxThreshold + " items)\n" +
                    countC + " branches in C (>" + maxThreshold + " items)";