Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Undo for InsertUnder() is not right. Why?

    Cinema 4D SDK
    python
    2
    4
    699
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • L
      LingZA
      last edited by LingZA

      Undo for InsertUnder() is not right. Why?
      The code below:
      484e1741-a95f-4822-b48a-fba442a0041c-image.png
      The operations below:
      dfc966ba-afb0-4042-9f8b-7ed824d28062-image.png
      After executing the script:
      90c8752e-afd3-47fe-9efe-35676aeb084c-image.png
      After several undo operations:
      f14cf3ca-9d2a-4310-bb91-2762400c6149-image.png

      The first question is that the undo result is not right.
      The second question is that the undo times are not right.
      There should be one time for undo to get the correct undo result if there is no problem.

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @LingZA
        last edited by ferdinand

        Hello @lingza,

        Thank you for reaching out to us. The likely reason why your script is not working is the position of your AddUndo call. As the Python documentation states,

        always has to be called before a change is made. In the case of the creation of a new object the call is done afterwards,

        There is also a slight ambiguity with how to treat hierarchy modifications: as a UNDOTYPE_CHANGE event or as an UNDOTYPE_DELETEOBJ and UNDOTYPE_NEWOBJ event. You can do both, but as hinted at in the UNDOTYPE_CHANGE description, hierarchy modifications also count as changes. Find a script example below which works fine for me, i.e., undo does revert to the state before running the script.

        Cheers,
        Ferdinand

        Scene state before the script ran (and also the state after an undo):
        before.png
        Scene state after the script ran:
        after.png

        The script:

        """Provides an example for creating undo items for hierarchy modifications.
        """
        
        import c4d
        
        doc: c4d.documents.BaseDocument  # The active document
        
        def main() -> None:
            """
            """
            # Get the active selection as you did.
            selection: list[c4d.BaseObject] = doc.GetActiveObjects(
                c4d.GETACTIVEOBJECTFLAGS_CHILDREN | c4d.GETACTIVEOBJECTFLAGS_SELECTIONORDER)
            if len(selection) < 2:
                return
            
            # We declare the first item in the list our root to which we are going to parent things.
            root: c4d.BaseObject = selection.pop(0)
        
            # Start an undo stack.
            doc.StartUndo()
            for item in selection:
                # Here you got things mixed up a bit. An undo item must be added before the action is 
                # carried out, e.g., UNDO_CHANGE, and only after when a new item is being added, i.e., 
                # UNDOTYPE_NEWOBJ. While the situation might be ambiguous here - is moving an item adding
                # an item or not, your symbol UNDO_CHANGE did not line up with place where you called 
                # AddUndo.
        
                # I decided to go the easy route and treat this just as a change event and not as a removal
                # and new object event.
                doc.AddUndo(c4d.UNDO_CHANGE, item)
                # A node can only be attached to one thing. In the Python layer, GeListNode.Remove() is
                # called automatically when one calls one of the insertion methods, in C++ not. So, this line 
                # is only for verbosity.
                item.Remove()
                # Insert the item under the root.
                item.InsertUnderLast(root)
        
            # Close the undo stack and push an update event to Cinema 4D.
            doc.EndUndo()
            c4d.EventAdd()
        
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        1 Reply Last reply Reply Quote 0
        • L
          LingZA
          last edited by

          Thanks! Sorry, I am so careless that I ignore the message "Needs to be called before the change.".

          UNDOTYPE_CHANGE:

          Any change to an object, including hierarchy modifications; modification in positioning (object has been moved from A to B), substructures etc. (Needs to be called before the change.)

          1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand
            last edited by

            Thanks! Sorry, I am so careless that I ignore the message "Needs to be called before the change.".

            No worries, you are not the first one who trips over Undo call order, including myself at some point 😉

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • First post
              Last post