Hello - please post a file that has some of these, or send to tech@mcneel.com, with a link back here in your comments. I see, on the linked topic’s private comments, that the developer tried to reproduce this and could not.
I’ll try in safe mode also in a sec.
Safe is ok - actually thats caused by my plugin But this never happens in v5 only in v6 and v7
@pascal i spotted the issue this starts happening after calling this method Rhino.RhinoDoc.ActiveDoc.InstanceDefinitions.Compact(true);
I guess this now should be moved to the developer category as this is programming related issue. @dale do you see any particular thing why Compact() triggers such issue? This one is called in Rhino.Commands.Command.EndCommand with if (e.CommandEnglishName == "BlockManager") some code and then idefTable.Compact(true) - and as i said v5 doesn’t suffer cause of this only v6 and v7
Ok. So far i checked that it doesn’t matter when i call Compact(true) it always create the same problem it can be anywhere it causes alwyas the same highly undesired side effect.
Below is enough to start this happening.
from scriptcontext import doc
if __name__ == "__main__":
doc.InstanceDefinitions.Compact(True)
Sure. Here you are. Open this file(v7) bm_bug_recreate.3dm (272.5 KB) then run this code bm_recreate_code.py (115 Bytes) and open BlockManager and you should see this:
Exactly same procedure open 3dm run this code bm_recreate_code_2.py (111 Bytes) and open BlockManager and you should see the same as above.
As a sidenote its weird that when i delete idef from idefTable i need to purge it - otherwise next time when i will want to create idef which have the same one as deleted one i will get notice that such block alread exists.
I was able to track this down and fix it. The problem is with the call to ‘doc.InstanceDefinitions.Compact’ which was deleting instance definition geometry but accidentally clearing the definitions’ IsDeleted flag. The BlockManager was reporting exactly what was happening, there was an instance definition in the table with no geometry.
The following code works around the issue.
from scriptcontext import doc
doc.InstanceDefinitions.Delete(0,True,False)
doc.InstanceDefinitions.Compact(True)
doc.InstanceDefinitions.Delete(0,True,False)
doc.Views.Redraw()
This issue was reported as RH-64200 and there is a fix pending for Rhino 7.6.
I expect sc.doc.InstanceDefinitions.Purge() function inside unembedBlocks() to completely remove the block definition, but it seems some data is not deleted
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
from collections import OrderedDict
import re
def getBlockParameters(blockInstanceName):
"""
function to get parameters stored in blockDescription. This is file specific information and equal for
all block instances from the same definition
Args:
blockInstanceName: name of a blockdefinition in the file
Returns: dict
"""
blockDescription = rs.BlockDescription(blockInstanceName)
params = OrderedDict()
if blockDescription:
m = re.findall('#TNM_(.+?)=(.*);', blockDescription)
if m:
for parameter in m:
try:
value = json.loads(parameter[1])
except:
value = parameter[1]
params[parameter[0]] = value
return params
def updateBlockParameters(blockInstanceName, params):
"""
function to store/update parameters in blockDescription. This is file specific information and equal for
all block instances from the same definition
Args:
blockInstanceName: name of a blockdefinition in the file
params: dict containing key, value pairs
Returns: None
"""
oldParams = getBlockParameters(blockInstanceName)
newParams = oldParams
for k, v in params.items():
newParams[k] = v
blockDescription = ""
for key, value in newParams.items():
if isinstance(value, dict):
value = json.dumps(dict, ensure_ascii=False)
if value == None:
value = ''
new = '#TNM_%s=%s;\n' % (key, value)
blockDescription += new
rs.BlockDescription(blockInstanceName, blockDescription)
def relinkBlockFromFile(idef):
"""
function to reload block from file, regardless wether file was changed in the meantime
Args:
idef: InstanceDefinition
Returns: None
"""
params = getBlockParameters(idef.Name)
blockPath = params.get('blockPath')
# change the source archive to read contents from the file
updateType = Rhino.DocObjects.InstanceDefinitionUpdateType.Linked
sc.doc.ActiveDoc.InstanceDefinitions.ModifySourceArchive(idef.Index, blockPath, updateType, True)
idef.LayerStyle = Rhino.DocObjects.InstanceDefinitionLayerStyle.Active
def embedBlockInModel(blockName):
"""
function to embed block in model
Args:
blockName: string, block definition name
Returns: None
"""
# store filename in TNM_blockparameters (or BlockAttributes)
filename = rs.BlockPath(blockName)
updateBlockParameters(blockName, {'blockPath': filename})
#embed the block
result = rs.Command('_NoEcho !_-BlockManager Properties "' + blockName + '" UpdateType Embedded _enter _enter',
False)
def embedBlocks():
"""
function to embed all block definitions (direct or referenced) into model
Args:
blockNames: list of blockdefinition names to embed
Returns: None
"""
for idef in sc.doc.ActiveDoc.InstanceDefinitions.GetList(True):
if not idef.Name:
continue
updateBlockParameters(idef.Name, {'isReference': rs.IsBlockReference(idef.Name)})
embedBlockInModel(idef.Name)
def unembedBlocks():
"""
function to unembed all block definitions embedded by embedBlocks()
and reload block objects from file
Args:
blockNames: list of blockdefinition names to embed
Returns: None
"""
#first purge all blocks that were embedded by embedBlocks()
for idef in sc.doc.ActiveDoc.InstanceDefinitions.GetList(True):
if not idef.Name:
continue
params = getBlockParameters(idef.Name)
isReference = params.get('isReference')
# print 'beforepurge', idef.Name, isReference, idef.IsReference, idef.IsTenuous
if isReference != 'False':
sc.doc.InstanceDefinitions.Purge(idef.Index)
sc.doc.InstanceDefinitions.Compact(True)
sc.doc.InstanceDefinitions.Purge(idef.Index)
sc.doc.Views.Redraw()
# for the remaining blockDefinitions, discard the embedded geometry and reload the file geometry
for idef in sc.doc.ActiveDoc.InstanceDefinitions.GetList(True):
if not idef.Name:
continue
params = getBlockParameters(idef.Name)
isReference = params.get('isReference')
if isReference == 'False':
relinkBlockFromFile(idef)
embedBlocks()
unembedBlocks()
# print all current blockNames
for item in rs.BlockNames():
print item
If you check the return value, you’ll see that the call to Purge fails because there are still instance references in the document. Delete all these first before purging. InstanceDefinition.GetReferences will get you all the references. Also, you shouldn’t need to call Compact.