undos

Demonstrates adding undo steps for document modifications.

undos.py

"""Demonstrates adding undo steps for document modifications.

Topics:
    - Undo steps for insertions
    - Undo steps for modifications
    - Undo steps for removals
"""
__author__ = "Ferdinand Hoppe"
__copyright__ = "Copyright (C) 2026 MAXON Computer GmbH"
__license__ = "Apache-2.0 License"
__version__ = "2027.0.0"
__date__ = "07/09/2026"

import c4d
import mxutils


doc: c4d.documents.BaseDocument # The active Cinema 4D document.
op: c4d.BaseObject | None # The currently selected object in the Cinema 4D document, can be None.

def main() -> None:
    """Runs the example.
    """
    # Create a new document, so that we start with a clean slate.
    doc: c4d.documents.BaseDocument = mxutils.CheckType(c4d.documents.BaseDocument())
    doc.SetDocumentName("Undo Example")
    c4d.documents.InsertBaseDocument(doc)

    # You can write code without undo steps, but your scene modifications will then not be undoable.

    # Open a new undo step for the active document. All undo actions return a bool, but it is
    # uncommon to check for that, as most undo actions cannot fail. But when you want 'perfect'
    # code you can check. Although this 'perfection' can be cumbersome to read and write.
    if not doc.StartUndo():
        raise RuntimeError("Failed to start undo step.")

    # Or a bit shorter form of check.
    # mxutils.CheckType(doc.StartUndo())

    # Add a cube object to the document and add a an undo item. Adding elements requires the AddUndo 
    # call AFTER the action.
    cube: c4d.BaseObject = mxutils.CheckType(c4d.BaseObject(c4d.Ocube))
    doc.InsertObject(cube)
    doc.AddUndo(c4d.UNDOTYPE_NEW, cube)

    # Add a material to the document and add an undo item.
    material: c4d.BaseMaterial = mxutils.CheckType(c4d.BaseMaterial(c4d.Mmaterial))
    doc.InsertMaterial(material)
    doc.AddUndo(c4d.UNDOTYPE_NEW, material)

    # Add render data to the document and add an undo item.
    renderData: c4d.documents.RenderData = mxutils.CheckType(c4d.documents.RenderData())
    doc.InsertRenderData(renderData)
    doc.AddUndo(c4d.UNDOTYPE_NEW, renderData)

    # Close the undo step.
    doc.EndUndo()

    # Now we are going to modify a few parameters and properties, which all require the undo item
    # BEFORE making any modifications, we need to start a new undo step.
    doc.StartUndo()

    # Change the transform of the cube, the AddUndo call must come before the modification.
    doc.AddUndo(c4d.UNDOTYPE_CHANGE, cube)
    cube.SetMg(c4d.utils.MatrixMove(c4d.Vector(100, 0, 0)))

    # Change the color of the material and its brightness.
    doc.AddUndo(c4d.UNDOTYPE_CHANGE, material)
    material[c4d.MATERIAL_COLOR_COLOR] = c4d.Vector(1, 0, 0)
    material[c4d.MATERIAL_COLOR_BRIGHTNESS] = 0.5

    doc.EndUndo()
    c4d.EventAdd()

    # All other undo types follow the BEFORE principle, meaning you must call AddUndo before making 
    # any changes. Here we add an undo step for removing the cube from the scene.
    doc.StartUndo()
    doc.AddUndo(c4d.UNDOTYPE_DELETE, cube)
    cube.Remove()
    doc.EndUndo()

    print("Press undo once, to see the cube being undeleted, once more to undo the translation of "
          "the cube and the red color of the material, and once more to see the cube and material "
          "addition being undone.")

if __name__ == "__main__":
    c4d.ClearPythonConsole()
    main()