Get user text Attributes of Rhino object in order

Hei,

Iam baking geometries using Elefront where it sets to each object own tree path and branch id as user text attributes.

how to get in C# the guid of the geometries also in the same order by iteration user text attributes?
0-BM_III.3dm (2.1 MB)
unnamed.gh (6.2 KB)

Hi @anon76319893,

I am not exactly sure what you are trying to do. EleFront should be able to hand all user text sorting, filtering, etc. I opened the .3dm file you provided and I don’t see any user text on the geometry. Perhaps you can explain a bit more what you are trying to do.

Thanks,
Evan

Basically

Iam Baking geometries with certrain datatree structure:

Then in another Grasshopper file iam refering to these geometries:

So i need to get Guid of each object inside “Elefront::kansi” layer in the same order inside C# component
I know i can just input Guid component and get them in order. Like this:


But i need to make it inside C# component

So, inside C# iam searching for object that has name “Elefront::kansi” and then fetching their Guid values, but the order is not same. As far as i understood, Elefront gives order number of each geometry and saves it to usertext attribute when baked. and then we refering in other grasshopper, it sorts the geometries and creates Datatree according to the user attributes:

So in C# after searching for each object that has name “Elefront::kansi”. I want to access it is user attributes to fetching the values that in which path and branch number the geometry exits and then i want to create same datatree of their Guid.

Hopefully i explained the situation clearly.

I am not sure why you need to use a custom C# component to access the user text, eleFront should do that. However, why don’t you just feed the the GUIDs to C# as sorted by eleFront so that you are ensured that the data trees are synced up.

I see that you are using Rhino 7 from you screenshots. I would recommend using the latest eleFront 5.0.5 for Rhino 7. For example, we have removed the “maintain tree structure” in the R7 release as any sorting or filter can easily and should be done using key-value pairs.

In the image below the R7 version of eleFront is running along with the R6 version of eleFront. You can see in 1) that the GUID is passed through the C# component and the tree maintained when “item access” is enabled and 2) the user text is available through the attributes stored on the geometry, in this case you are seeing the attribute container since no actual user text is stored on the geometry in your example.

Hi Evan!
I found this thread, and I see you might have a lot of experience with this…

I am experimenting with ClaudeAI, creating some 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#:

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)";```