Solved A SetBit problem aka cann't select node.

Hi !

I am try to set my scatter rig , the basic rule is :

  • node selected : set the node to the Scatter Object , try find selection tag and set to the "Scater Aera"
  • tag selected : set the node which tag on to the Scatter Object , tag to the "Scater Aera"

All they worked , but in the context " only tag selected " , the the scatter object doesn't active in object manager , I cann't find how this heppend .

the scatter shoud be :
aea081a5-458d-404b-af1e-a142f48905d4-1.gif
the " tag " context , it returns like this , the scatter is set right and active is object manager , but it doesn't active in attribute manager .
09efad69-5c37-429b-bb46-8ba1fe70ca6b-2.gif

How can I fix this ?
( It's a bare-bone rig that i can show this problem and easy to read , If a non delete setting is needed please let me kow)

@ Windows 11 21H2 Cinema 4D 2023.0.0

from typing import Optional
import c4d

doc: c4d.documents.BaseDocument  # The active document
op: Optional[c4d.BaseObject]  # The active object, None if unselected

def redshift_scatter() -> c4d.BaseObject :

    # Modifier ID
    ModifierID = 1018545 
    ModifierName = 'Scatter : '
    selection = False
    
    doc.StartUndo()
        
    selectedtag = doc.GetActiveTag()
    node = doc.GetActiveObject()

    # pass if nothing selected
    if not node and (not selectedtag or not selectedtag.CheckType(5673)) :
        return
    
    # node selected
    if node :
        
        
        doc.AddUndo(c4d.UNDOTYPE_BITS,node)
        node.DelBit(c4d.BIT_ACTIVE)
        
        tags = [tag for tag in node.GetTags() if tag.CheckType(5673)]
        if len(tags)==[]:
            pass
        if len(tags) == 1 :
            scatter_selection = tags[0].GetName()
            selection = True
        else:
            if selectedtag in tags :
                scatter_selection = selectedtag.GetName()
                selection = True
                doc.AddUndo(c4d.UNDOTYPE_BITS,selectedtag)
                selectedtag.DelBit(c4d.BIT_ACTIVE)
                
    
    # selection tag
    elif (selectedtag and selectedtag.CheckType(5673)) and not node:
        
        scatter_selection = selectedtag.GetName()
        selection = True
        node = selectedtag.GetObject()
        # deselect tag
        doc.AddUndo(c4d.UNDOTYPE_BITS,selectedtag)
        selectedtag.DelBit(c4d.BIT_ACTIVE)


    # world matrix.
    pos = node.GetMg()
    # Modifier 
    Modifier = c4d.BaseObject(ModifierID)
    Modifier[c4d.ID_MG_MOTIONGENERATOR_MODE] = 0
    Modifier[c4d.MG_OBJECT_LINK] = node
    #Modifier[c4d.MG_POLYSURFACE_SEED] = seed
    Modifier[c4d.MG_POLY_MODE_] = 3 # surface
    
    if selection :
        Modifier[c4d.MG_POLY_SELECTION] = scatter_selection
        
    Modifier.SetName(ModifierName + node.GetName())
    Modifier.SetMg(pos) # Set the world (global) matrix.
    Modifier.InsertAfter(node)
    doc.AddUndo(c4d.UNDOTYPE_NEWOBJ,Modifier)
    Modifier.SetBit(c4d.BIT_ACTIVE)

    doc.EndUndo()
    c4d.EventAdd()

    return Modifier

if __name__ == '__main__':
    redshift_scatter()

thanks :blush:

Hello @dunhou,

Thank you for reaching out to us. We are quite busy now now, I must unfortunately push your question to the end of next week.

Please excuse the delay and cheers,
Ferdinand

PS: I glanced at your post, and this might be a priority issue in the internal event loop, and you seem to have a problem with the Attribute Manger not updating as you want it to. You can try your luck with c4d. SendCoreMessage(c4d.COREMSG_CINEMA_FORCE_AM_UPDATE) after your last EndUndo and EventAdd, but that is more a guess than a solution, as I do not have time to read and run your script right now.

MAXON SDK Specialist
developers.maxon.net

@ferdinand

That's all right I will move on another work for now.
Thanks for your time :blush:

Hey @dunhou,

sorry for the delay, 2023.1 was consuming my time. The problem with your code is that it produces what is called a TriState selection in the C++ API, i.e., multiple things are selected and Cinema 4D tries then to display the intersection of attributes of the selected entities in the Attribute Manager. Which happened to be the empty set in your case, hence the empty Attribute Manager. The most straight forward way to solve this, is to simply use BaseDocument.SetActiveObject and start a new selection.

Find your modified code below; I have marked the minor change I made with a change fence. Also find a more technical explanation there.

Cheers,
Ferdinand

The result:
56d93d83-2dbd-4322-9d97-6e4aa75803ab-image.png
The code:

from typing import Optional
import c4d

doc: c4d.documents.BaseDocument  # The active document
op: Optional[c4d.BaseObject]  # The active object, None if unselected

def redshift_scatter() -> c4d.BaseObject :

    # Modifier ID
    ModifierID = 1018545 
    ModifierName = 'Scatter : '
    selection = False
    
    doc.StartUndo()
        
    selectedtag = doc.GetActiveTag()
    node = doc.GetActiveObject()

    # pass if nothing selected
    if not node and (not selectedtag or not selectedtag.CheckType(5673)) :
        return
    
    # node selected
    if node :
        doc.AddUndo(c4d.UNDOTYPE_BITS,node)
        node.DelBit(c4d.BIT_ACTIVE)
        
        tags = [tag for tag in node.GetTags() if tag.CheckType(5673)]
        if len(tags)==[]:
            pass
        if len(tags) == 1 :
            scatter_selection = tags[0].GetName()
            selection = True
        else:
            if selectedtag in tags :
                scatter_selection = selectedtag.GetName()
                selection = True
                doc.AddUndo(c4d.UNDOTYPE_BITS,selectedtag)
                selectedtag.DelBit(c4d.BIT_ACTIVE)
                
    # selection tag
    elif (selectedtag and selectedtag.CheckType(5673)) and not node:
        
        scatter_selection = selectedtag.GetName()
        selection = True
        node = selectedtag.GetObject()
        # deselect tag
        doc.AddUndo(c4d.UNDOTYPE_BITS,selectedtag)
        selectedtag.DelBit(c4d.BIT_ACTIVE)

    # world matrix.
    pos = node.GetMg()
    # Modifier 
    Modifier = c4d.BaseObject(ModifierID)
    Modifier[c4d.ID_MG_MOTIONGENERATOR_MODE] = 0
    Modifier[c4d.MG_OBJECT_LINK] = node
    #Modifier[c4d.MG_POLYSURFACE_SEED] = seed
    Modifier[c4d.MG_POLY_MODE_] = 3 # surface
    
    if selection :
        Modifier[c4d.MG_POLY_SELECTION] = scatter_selection
        
    Modifier.SetName(ModifierName + node.GetName())
    Modifier.SetMg(pos) # Set the world (global) matrix.
    Modifier.InsertAfter(node)
    doc.AddUndo(c4d.UNDOTYPE_NEWOBJ,Modifier)

    # --- Change Start -----------------------------------------------------------------------------

    # This line was the problem in your code. You sort of did everything correctly, you
    # deleted the selection bits in line #26, #39, and #49. But your code has sort of a fault, as
    # in that you do not make sure that all tags are deselected in all cases. #tag in line #28
    # contains only a subset of tags. Cinema 4D considers the scene state therefore to still be a 
    # multi-selection after your script ran.

    # Cinema 4D allows you to select multiple objects and will then display the intersection of
    # attributes of all selected nodes in the Attribute Manager. In the C++ API this is called a 
    # TriState, the Python API does not really understand the concept. So what happened here, is 
    # that Cinema 4D tried to build that TriState for the old selected tag and the 
    # new object, which happens to be the empty set, hence the empty AM in your screen cast.

    # You could either make sure that really everything is deselected by managing the bits more 
    # carefully, or do what I have done here, use the convenience function BaseDocument::
    # SetActiveObject() to "nuke" the scene selection state. The second argument defines if you
    # want to add to an existing selection or start a new one. c4d.SELECTION_NEW is the default
    # value, I have added it here only for verbosity.

    # Modifier.SetBit(c4d.BIT_ACTIVE)
    doc.SetActiveObject(Modifier, c4d.SELECTION_NEW)
    # --- Change End -------------------------------------------------------------------------------

    doc.EndUndo()
    c4d.EventAdd()

    return Modifier

if __name__ == '__main__':
    redshift_scatter()

MAXON SDK Specialist
developers.maxon.net

@ferdinand Thanks for the new solution for SetActiveObject . It works as expected.

Much appreciated !