Bug: Number of (Block) InstanceDefinitions Increases when replaced

When I Insert a Block instance (linked) into my Rhino document everything seems OK. But if I remove the block instance from the document (using the Block Manager or by simply deleting the block), some info about the (completely removed linkd block) remains in the document. I wouldn’t have noticed this if i hadn’t made a C# script trying to pick up the block.

Fig.1. Only one linked Block exists in the file, but after inserting and removing three times…:
bild

Fig 2. After deleting the (only) existing block:
bild

Only after reloading the Rhino document the “block remains” are removed.

After a number of insertions/deletions (I still have only one instance of the linked block definition in my file) my script reports the following to the Rhino command line:

There shouldn’t be any problem with the following “debug script” (I prevent it from crashing, because there’s actually only one (1) instancedefinition in the file):

  private void RunScript(string BlockName, ref object Block)
  {

    RhinoApp.ClearCommandHistoryWindow();

    var block_defs = doc.InstanceDefinitions;
    var block_defs_count = block_defs.Count;
    if (block_defs_count == 0)
    {
      RhinoApp.WriteLine("Document contains no instance definitions.");
      return;
    }

    for (var i = 0; i < block_defs_count;i++)
    {
      RhinoApp.WriteLine("-------------------------------");
      RhinoApp.WriteLine(String.Format("Reading block definition #{0} from file.", i));

      var block = doc.InstanceDefinitions[i];
      if (block == null)
        continue;

      RhinoApp.WriteLine(String.Format("Found {0} block definitions.", block_defs_count));
      RhinoApp.WriteLine(String.Format("Block Name: {0}", block.Name));
      RhinoApp.WriteLine(String.Format("Block Type: {0}", block.GetType()));
      RhinoApp.WriteLine(String.Format("Block IsReference: {0}", block.IsReference));


      // Block ID's
      var id = block.GetObjectIds();
      RhinoApp.WriteLine(String.Format("Id.Length = {0}", id.Length));
      if (id == null || id.Length < i+1)
      {
        RhinoApp.WriteLine(String.Format("Ops, Id #{0} doesn't actually exist!", i));
        continue;
      }
      RhinoApp.WriteLine(String.Format("Block Id: {0}", id[i]));

      // Block Objects
      var objs = block.GetObjects();
      if (objs == null || objs.Length == 0)
      {
        RhinoApp.WriteLine(String.Format("Ops, Block #{0} doesn't actually exist!", i));
        continue;
      }
      RhinoApp.WriteLine(String.Format("Block Object: {0}", objs[i]));
    }

I tried Purge, but no definitions were purged:
bild

This means that the excess number of block definitions remains in the file an “Count” returns the wrong numbers, and so any script trying to access or iterate over the non-existing ghost InstanceDefinitions will crash with a groan.

Is there any workaround I could apply to avoid, or to clean up this messy remnant info so that scripts can iterate over the InstanceDefinitions without crashing?

// Rolf

Does it still exist if you save small? Could that be a remainder due to saved undoes or something?

“Save Small” didn’t help. I also modified the code (“Count”) to “ActiveCount” to ensure that I don’t iterate over deleted instances

var block_defs_count = block_defs.ActiveCount;

… but it still reports excess number of InstanceDefinitions (Edit2: I also added the test IsValid and (is) Disposed and IsDeleted, but warnings seems screwed up for IsDeleted):

bild

// Rolf

Hi Rolf - I don’t know what you mean by ‘simply deleting the block’ but if you delete all instances and then Purge (in Rhino) then the definitions will show as Deleted=True. Deleting instances does not affect the definition.

-Pascal

Does that apply to deleting linked InstanceDefinitions as well?

// Rolf

Yes. That is, deleting linked instances does nothing to the instance definition The way to delete instance definitions is via the Purge command, if no instances are in the file.

-Pascal

But my problem isn’t the definition (it resides in another file). My problem is that the Rhino document which has links to the block definition reports that it has linked block instances even after deleting all linked instances, and even after Purging the file after deleting the instances.

In short: There’s remnant info that shouldn’t be there. So problem is not solved.

// Rolf

Hi Rolf - an instance definition that has been purged should show ‘IsDeleted’ = True . It needs to be there for Undo, but you should be able to check its deleted status in RhinoCommon.

-Pascal

Look at this info. Right now i do have one (instance) in the document. Which one of the four reported? (yes, even regarding the “Deleted” status:

//
Reading block def #0 from doc.
Block Name:
Block IsValid True
Block is Disposed False
Block IsDeleted True // Really?
Block IsDocumentControlled True
Block IsTenuous False
Block ArchiveFileStatus LinkedFileIsUpToDate
Id.Length = 1
Block Id: 9650fbe4-e75f-49b2-8e04-3fad2fad1e49
Block Object: InstanceObject: (unnamed) (0)

Huh? OK, if you say so, but as far as I can see, this is the only instance that actually exist (has block id).

But it is of course preferred to use only objects that are not deleted, so let’s try this one:

//
Reading block def #1 from doc.
Block Name: Scapula Complete Isotropic Name 8bfb6493-8586-4f18-a271-a696aba30e27
Block IsValid True
Block is Disposed False
Block IsDeleted False
Block IsDocumentControlled True
Block IsTenuous True
Block ArchiveFileStatus NotALinkedInstanceDefinition
Id.Length = 1
Ops, Id #1 doesn’t actually exist!

Um… Not deleted, but still doesn’t exist? And has no Id. Aha. (pretending to understand)

//
Reading block def #2 from doc.
Block Name:
Block IsValid True
Block is Disposed False
Block IsDeleted True
Block IsDocumentControlled True
Block IsTenuous False
Block ArchiveFileStatus LinkedFileIsUpToDate
Id.Length = 1
Ops, Id #2 doesn’t actually exist!

OK, this one got both delete status and actual existence in sync.

//
Reading block def #3 from doc.
Block Name: Scapula Complete Isotropic Name 01
Block IsValid True
Block is Disposed False
Block IsDeleted False
Block IsDocumentControlled True
Block IsTenuous True
Block ArchiveFileStatus NotALinkedInstanceDefinition
Id.Length = 1
Ops, Id #3 doesn’t actually exist!

Again, which one “not deleted” object is preferred?

I didn’t make this up. This is the log:

// Rolf

Hi Rolf - if you are looking for an object id, for an object in the file, a block instance, then the ID from the definition is of no use.

Instance = an instance of the block actually in existence in the document- a thing you can pick on, select delete etc. Each instance will have its own unique guid as an object in the file.

Instance definition = geometry held in the file that defines the block. This is not an object in the document, it is held behind the scenes and is independent of there actually being any instances of it in the document.

I don’t know if that clarifies or muddies the waters, but the ‘IsDeleted’ applies to the definition.

-Pascal

But there are no definitions in the file, but still two “IsDeleted = False” and two two “IsDeleted = True”

At first I didn’t look at this attribute at all (it only looks even more strange knowing that there’s no definitions at all in the file, but anyway), I just want to access that one either linked or embedded Block instance in the document, if any one such exist.

But all I see is a bunch of InstanceDefinitions although there has never been any Block definition in the file, only linked instances (repeatedly inserted and deleted, and from that the counter increases).

How does all that come together? :slight_smile:

// Rolf

Hi Rolf - run the Insert command - does it offer (drop down list) the two blocks that are IsDeleted=False?

(There has to be a block definition even if the block is linked)

-Pascal

No. I deleted the instance I had inserted (and the counter reverted from 4 to 3). No block definition in the file, and no block instance either (as said, I just deleted the one I had there). And at this point I drop down the definitions to pick from (there are none, an never has been)

But still you see all the block-info (remnant crap) in the log to the left. Causing my scripts to go bananas.

// Rolf

Hi Rolf - hmmm… between

I deleted the instance I had inserted (and the counter reverted from 4 to 3).

and

" No block definition in the file,"

what happened? Was any command run?

-Pascal

Now I tried to insert the linked block again, but then Rhino (not me) see a conflict there, which should not be possible because a you saw in the previous post, there are no block definitions nor block intsances in the file!

Ops, forgot the “conflict” dialog popping up:
bild

// Rolf

I have to run but I’ll try to reproduce this… there’s obviously a missing step, here.

-Pascal

It may not be of any use for you, but I attach the scirpt producing the log to the Rhino command line.

BlockReference.gh (6.3 KB)

// Rolf

You seem to confuse InstanceDefinitions with InstanceObjects, some of the ‘logic’ in your script doesn’t make sense.

GetObjectIds() and GetObjects() get the (top-level) objects IDs and objects respectively, contained in the InstanceDefinition currently under inspection. As such it doesn’t make sense to have a check like id.Length < i + 1, since the amount of (top-level) objects in an instance definition has nothing to do with how many instance definitions you have in your file.

GetObjectIds() gives an array that is equal in length to the one given by GetObjects().

Also note that a file can contain any amount of InstanceDefinition, without there being any InstanceObject. The latter being an actual object created from an InstanceDefinition.

InstanceDefinitions you should meddle with only when you want to change such definitions. For your use you will want to stick to block instances.

InstanceObjects are of type InstanceReference (a bit of a cock-up with the naming of types there). IOW block instances in a file are code-wise represented with instances of the class InstanceObject, which are of type ObjectType.InstanceReference. Block instances are created from InstanceDefinition. InstanceDefinition is the template from which all InstanceObject are created. Block instances you look up in doc.Objects, not in doc.InstanceDefinitions.

Here a short script that shows better how to use block instances.

IteratingOverBlocks.gh (2.9 KB)

1 Like

Well, in my utter confusion I actually got hold of my Mesh instance by going “backwards” from definition to instances referencing the definition. Result:

Mentioned here (which describes what I’m actually trying to achieve):

With the exploratory logging code I was trying to figure out how to access a Block Definition - via a Linked Block Instance (reference) - to get to the original file, and from there reference the block into a grasshopper parameter (remnants from exploratory logging code doesn’t have to look overtly “logical” BTW). Regarding the “strange” Id index, it was a remnant which was fixed in the GetObjects() call but overlooked for the GetObjectIds() call ending up as a bug (which BTW didn’t do any difference to the code after fixing it).

Anyway, this “backwards” lookup code accessed a named Block Instance above (see code below):

  private void RunScript(string BlockDefName, ref object Block)
  {
    if (doc.InstanceDefinitions.ActiveCount == 0)
      return;
    for (var i = 0; i < doc.InstanceDefinitions.ActiveCount;i++)
    {
      var blockdef = doc.InstanceDefinitions[i];
      if (blockdef == null)
        continue;
      if (blockdef.IsValid && !blockdef.Disposed && !blockdef.IsDeleted && blockdef.Name == BlockDefName)
      {
        var refs = blockdef.GetObjects();
        if (refs == null || refs.Length == 0)
          continue;
        Block = refs[0].Geometry;
        return;
      }
    }
    Block = null;
  }

It works. Even if navigating “backwards”. Perhaps it was sheer luck, who knows, but initially not knowing the “correct way” I ended up accessing it backwards. But, neither my “backwards” access nor your more correct access path solves my problem.

I need to reference a block in GH via a Linked Block Instance which was defined in another file (not in the open RhinoDocument).

Hence the if (refs == null || refs.Length == 0) in my original code (refs in this context means "references the Block Definition I was looking for, so my “backwards” approach wasn’t without a reason, but never mind.

I don’t think I have a problem with that bit. These concepts are crystal clear. Not exaclty so with the paths for accessing them though.

OK, preferred. My logging didn’t disclose that. But just now learning the correct access path for instances I agree. Thanks.

But how do I access from GH the block definition & block instance which are indirectly linked to (with no embedding) from my RhinoDocument? (better described here)

// Rolf

Except that doesn’t make sense. GetObjects() gives you the objects in the InstanceDefinition, not whether they might be InstanceDefinitions or so, they will be objects that can be InstanceObjects. See the recursivity of the dump functions in the script components.

Find out through SourceArchive. If it is a non-null or empty string it’ll be a path to file it is from. Adapted DumpInstanceDefinition:

private void DumpInstanceDefinitionInfo(int level, InstanceDefinition idef)
  {
    string t = "";
    for(int i = 0; i < level; i++) {
      t += "   ";
    }
    var obs = idef.GetObjects();
    RhinoApp.WriteLine(String.Format("{0} idef {1} with {2} obs. InstanceDefinition at idx {3} in doc.InstanceDefinitions. Originally from {4}", t, idef.Id, obs.Length, RhinoDocument.InstanceDefinitions.InstanceDefinitionIndex(idef.Id, true), idef.SourceArchive));
    foreach(var ob in obs) {
      RhinoApp.WriteLine(String.Format("{0}* ob {1} is a {2}", t, ob.Name ?? "(noname)", ob.ObjectType));
      if(ob.ObjectType == ObjectType.InstanceReference) DumpInstanceDefinitionInfo(level + 1, (ob as InstanceObject).InstanceDefinition);
    }
  }

sample output:

InstanceObject (unnamed) d176d169-6859-4118-aef3-4aa13488c0c8
    idef 22e4fd8a-c4f2-43bc-b49c-3e8cfbf91458 with 1 obs. InstanceDefinition at idx 0 in doc.InstanceDefinitions. Originally from N:\MiscFiles\FileWithBlockInstance.3dm
   * ob (noname) is a InstanceReference
       idef 552a67a0-4386-45ec-ab7a-4ca8231ca838 with 2 obs. InstanceDefinition at idx 1 in doc.InstanceDefinitions. Originally from 
      * ob (noname) is a Extrusion
      * ob (noname) is a Extrusion

Note that for a nested InstanceDefinition the SourceArchive may be null/empty string, if it is from the file its parent definitions resides in (like the sample output).

I used Ctrl+I to insert as linked block instance the attached 3dm file. The file contains one block instance.

FileWithBlockInstance.3dm (40.0 KB)

With that inserted I then opened the modified GH definition:

IteratingOverBlocks_with_SourceArchive.gh (3.7 KB)

The entire output for a file with attached 3dm as inserted block instance is:

InstanceObject (unnamed) d176d169-6859-4118-aef3-4aa13488c0c8
    idef 22e4fd8a-c4f2-43bc-b49c-3e8cfbf91458 with 1 obs. InstanceDefinition at idx 0 in doc.InstanceDefinitions. Originally from N:\MiscFiles\FileWithBlockInstance.3dm
   * ob (noname) is a InstanceReference
       idef 552a67a0-4386-45ec-ab7a-4ca8231ca838 with 2 obs. InstanceDefinition at idx 1 in doc.InstanceDefinitions. Originally from 
      * ob (noname) is a Extrusion
      * ob (noname) is a Extrusion
$$  idef 22e4fd8a-c4f2-43bc-b49c-3e8cfbf91458 with 1 obs. InstanceDefinition at idx 0 in doc.InstanceDefinitions. Originally from N:\MiscFiles\FileWithBlockInstance.3dm
$$ * ob (noname) is a InstanceReference
$$     idef 552a67a0-4386-45ec-ab7a-4ca8231ca838 with 2 obs. InstanceDefinition at idx 1 in doc.InstanceDefinitions. Originally from 
$$    * ob (noname) is a Extrusion
$$    * ob (noname) is a Extrusion
$$  idef 552a67a0-4386-45ec-ab7a-4ca8231ca838 with 2 obs. InstanceDefinition at idx 1 in doc.InstanceDefinitions. Originally from 
$$ * ob (noname) is a Extrusion
$$ * ob (noname) is a Extrusion

The double-dollar prefixed lines are from a second script command that iterates over the InstanceDefinitions of the document. You’ll see that there are two InstanceDefinitions in the table, each with their own index. That is because the file as a whole was inserted as a block instance, i.e. an InstanceDefinition for it was automatically created. And there is an InstanceDefinition for the one that already was contained in the file - it is now accessible through the document InstanceDefinitions table.

For your case you probably want to iterate over the InstanceDefinitions table and look for those InstanceDefinitions that have their SourceArchive set to a file.