How can I retrieve the count of InstanceObject for each InstanceDefinition with C#?

Speaking of the block manager, it gives the good values indeed but you just can’t get a table with all the values unless you select all the blocks by hand and press “count”.
Middle-ages !

Hi @Gijs, @osuire,

The block count differences between the dialog version and the scripted version of BlockManager is a bug. I’ve logged the issue.

https://mcneel.myjetbrains.com/youtrack/issue/RH-62657

Both the dialog and scripted versions of the command count both top-level and nested blocks. The difference between the two is that the dialog version check to see of a block definition has multiple references of a nested block. The scripted version does not. This is illustrated in this example:

image

Above, there are two blocks: Circle and Gizmo. Gimzo contains 3 instances of Circle.

The dialog version “Count” display this:

image

The scripted version of “Count” displays this:

image

The fix is to make the scripted version match that of the dialog.

Note, I’ve added two new InstanceDefinition.UseCount methods to RhinoCommon to count instance references just like the dialog version of BlockManager. This new method will be available in Rhino 7 SR4.

https://mcneel.myjetbrains.com/youtrack/issue/RH-62640

Hope this helps.

– Dale

4 Likes

Thank you. Try this one.
blockInstancCount2.gh (10.7 KB)

1 Like

nice one, if I understand correctly this goes max 3 levels deep?

Yes. It can be a recursive function as well, but then it may affect the performance.

There’s no need to go further.
Two levels is already asking for trouble.
Three levels…

I does affect the performance already : on a larger test model (748 block definitions), the python script takes over a minute to execute, while the elefront “Reference Block By Name” takes 42 seconds, which is already much too long…

Looking forward to V7 SR.4 @dale

Hi @osuire,

When there is a Rhino 7 SR4 release candidate available, try the attached solution with it and let me know if works any faster than the above solutions.

test_idef_usecount.gh (6.7 KB)

– Dale

1 Like

Hi @dale ,

Thanks for your script and for reporting the bug ; now it’s fixed and the count is correct.
I tried tobunch-up your script with another one that inputs names instead.
I managed to output the GUIDs of the corresponding InstanceDefinitions, and the GUIDS of the objects that are contained inside.
But the count part doesn’t work, and I can’t make sense of the error message which goes something like :

  1. Warning (CS0618): ‘Rhino.DocObjects.Tables.InstanceDefinitionTable.Find(string, bool)’ is obsolete : ‘ignoreDeletedInstanceDefinitions is now redundant. Remove the second argument. Definitions are now always deleted permanently.’ (line 66)

I’m still struggling with the matryoshka nature of OO programming…

using System;
using System.Collections;
using System.Collections.Generic;

using Rhino;
using Rhino.Geometry;

using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Types;



/// <summary>
/// This class will be instantiated on demand by the Script component.
/// </summary>
public class Script_Instance : GH_ScriptInstance
{
#region Utility functions
  /// <summary>Print a String to the [Out] Parameter of the Script component.</summary>
  /// <param name="text">String to print.</param>
  private void Print(string text) { /* Implementation hidden. */ }
  /// <summary>Print a formatted String to the [Out] Parameter of the Script component.</summary>
  /// <param name="format">String format.</param>
  /// <param name="args">Formatting parameters.</param>
  private void Print(string format, params object[] args) { /* Implementation hidden. */ }
  /// <summary>Print useful information about an object instance to the [Out] Parameter of the Script component. </summary>
  /// <param name="obj">Object instance to parse.</param>
  private void Reflect(object obj) { /* Implementation hidden. */ }
  /// <summary>Print the signatures of all the overloads of a specific method to the [Out] Parameter of the Script component. </summary>
  /// <param name="obj">Object instance to parse.</param>
  private void Reflect(object obj, string method_name) { /* Implementation hidden. */ }
#endregion

#region Members
  /// <summary>Gets the current Rhino document.</summary>
  private readonly RhinoDoc RhinoDocument;
  /// <summary>Gets the Grasshopper document that owns this script.</summary>
  private readonly GH_Document GrasshopperDocument;
  /// <summary>Gets the Grasshopper script component that owns this script.</summary>
  private readonly IGH_Component Component;
  /// <summary>
  /// Gets the current iteration count. The first call to RunScript() is associated with Iteration==0.
  /// Any subsequent call within the same solution will increment the Iteration count.
  /// </summary>
  private readonly int Iteration;
#endregion

  /// <summary>
  /// This procedure contains the user code. Input parameters are provided as regular arguments,
  /// Output parameters as ref arguments. You don't have to assign output parameters,
  /// they will have a default value.
  /// </summary>
  private void RunScript(string name, ref object guidBlockDefs, ref object guidInBlockDefs, ref object blockInstanceCount)
  {
    var def = RhinoDocument.InstanceDefinitions.Find(name);
    if(def == null)
      return;

    guidBlockDefs = def.Id;
    guidInBlockDefs = def.GetObjectIds();

    var guids = Convert.ToString(guidBlockDefs);
    var rc = -1;
    var idef = RhinoDocument.InstanceDefinitions.Find(guids, true);
    if (null != idef)
      // Returns the number of references of this instance definition in the model.
      // Both top-level references and nested instances are included in the count.
      rc = idef.UseCount();
    blockInstanceCount = rc;


  }

  // <Custom additional code> 

  // </Custom additional code> 
}

this message is saying that you don’t need the second argument anymore (the boolean that indicates if you want to include deleted definitions) as this option is removed

OK, I deleted the boolean.
But why doesn’t it work ?

Basically, all I did was merge the two components Dale made into one, passing the guids inside the script.

Instead of getting the block counts, I get all “-1” values :frowning:

Merge fail.gh (4.8 KB)
Test file V6.3dm (367.8 KB)

edit: seems passing guid doesn’t work

Well, if I don’t convert “guidBlockDefs” to a string, it complains about “RhinoDocument.InstanceDefinitions.Find” getting an object instead of a string.
So I just converted it to a string
Smart, huh ?
AintISmart

Regarding the list, well, I expect to get a count for each InstanceDefinition, so it makes sense, no ?

I’m really pretending to know what I’m doing here, sorry for being such a newb with C# and still having the guts to post here in the developers section…

sorry about the list comment, this was my c# newbie mistake

1 Like

I think I was a bit confused by the names you used, but after looking closer at the GetObjectIds() method, I think this gives the ids of the objects inside the instance. Is this what you want? In the meantime discourse says:

It could be even better if you gave other people space to share their points of view, too. Can you invite them over?

edit: so I’m a bit lost at what you need to get out of your script, as the blockinstance count is already working in the other component

Yes !
-The “guidBlockDefs” output is the list of guids of the InstanceDefinitions corresponding to the names that are input.
-The “guidInBlockDefs” output is a tree giving the guids of objects inside the InstanceDefinitions
-The “blockInstanceCount” output is meant to give the count for each InstanceDefinitions… but it doesn’t work !

I know it’s already working. I just want a single component instead of 2.
Also, Dale’s first components retrieves all the guids of InstanceDefinitions in the model.

This is absolutely not what I want because first, I need to filter out instanceDefinitions that are references (in a worksession for example).
Moreover, I much prefer use the block name as input because it is more “human-friendly” than a guid.
Since block names are unique, it just works.

is this close to what you need?
test_idef_usecount-adjusted.gh (9.5 KB)

No. I want names as input, not as outputs.

This is what I want :

test_idef_usecount-adjusted2gh.gh (12.0 KB)

1 Like

Spot-on !

Great case of learning by trial, error, and then someone actually giving you the answer :slight_smile:
Thanks Gijs !

1 Like