SOLVED Dirty State with Python Generator

Hi all,
I'm trying to get a python generator to only refresh when a source object is dirty, but can't seem to get it to work.
I move the object, change its geometry, change its parameters, it is never recognized as dirty.
I tried many different Dirty Flags and I can't get it to work.
Any idea what I'm doing wrong?

def main():
    obj = op[c4d.ID_USERDATA,2]
    if obj.IsDirty(c4d.DIRTYFLAGS_ALL):
        print "object is dirty"
        return c4d.BaseObject(c4d.Ocube)
    else:
        return None

python_dirtyState.c4d

Hi,

you probably did not uncheck "optimize cache" in your Python generator object. You also should touch the object after checking its dirty status and handle your cache as you probably do not want your object to turn into a null object. Here is an example:

import c4d

def main():
    """ Example on how to optimize your cache manually. For this to work you
     obviously have to uncheck 'optimize cache' in your Python generator object
     in the attribute editor.

    This script expects you to put an object as a child into the generator
    object. The generator will then return that object.
    """
    # op is predefined as the python generator object.
    first_child = op.GetDown()
    # No child object
    if first_child is None:
        return c4d.BaseList2D(c4d.Onull)
    # Return the child if it is dirty or if op has not build any caches yet.
    if first_child.IsDirty(c4d.DIRTYFLAGS_ALL) or op.GetCache() is None:
        # Consume the dirty flag of the child.
        first_child.Touch()
        # Get a clone of the child, as we cannot insert the same node twice
        # into the document (Cinema has a mono-hierarchical graph).
        clone = first_child.GetClone(c4d.COPYFLAGS_NO_HIERARCHY)
        # Center / align the clone to the Python generator object.
        clone.SetMg(c4d.Matrix())
        print "Build cache"
        return clone
    # Otherwise return the cache.
    else:
        print "Returned cache"
        return op.GetCache()

Cheers
zipit

Hi Zipit, thanks for the reply.

The Optimized Cache is off.
I isolated the dirty check code since I couldn't get that part to work.

Your example works for a child, if I replace it with a link as in my case, then it doesn't work.

def main():
    linked_object = op[c4d.ID_USERDATA,2]
    # No linked object
    if linked_object is None:
        return c4d.BaseList2D(c4d.Onull)
    # Return the linked object if it is dirty or if op has not build any caches yet.
    if linked_object.IsDirty(c4d.DIRTYFLAGS_ALL) or op.GetCache() is None:
        # Consume the dirty flag of the linked object.
        linked_object.Touch()
        # Get a clone of the linked object, as we cannot insert the same node twice
        # into the document (Cinema has a mono-hierarchical graph).
        clone = linked_object.GetClone(c4d.COPYFLAGS_NO_HIERARCHY)
        # Center / align the clone to the Python generator object.
        clone.SetMg(c4d.Matrix())
        print "Build cache"
        return clone
    # Otherwise return the cache.
    else:
        #print "Returned cache"
        return op.GetCache()

Maybe the python generator can only dirty check its children? Or perhaps the links need special handling?

Hi,

you can dirty check everything, but you have to use a bit more low level approach for your scenario.

import c4d

""" For this scenario, we need to store the dirty count of the linked object
 somewhere. The problem is, we cannot define an object (variable) with a
 sufficient life time (at least not without some ugly hacks) as we are
 bound to the scope of main().

One solution to store data persistently over multiple executions of a
 scripting object is to store the data in the node attached to that script,
 in our case the Python generator object.

To do that we need a plugin ID (which you can register here in the forum),
 so that we can store that data collision free. REPLACE THIS FOLLOWING ID
 WITH SUCH AN ID.
"""
ID_PYGEN_DIRTY_CACHE = 10000000

def main():
    linked_object = op[c4d.ID_USERDATA, 2]
    # Get the last cached dirty count and the current dirty count.
    lst_dcount = op[ID_PYGEN_DIRTY_CACHE]
    cur_dcount = linked_object.GetDirty(c4d.DIRTYFLAGS_DATA)

    if linked_object is None:
        return c4d.BaseList2D(c4d.Onull)
    # Return the linked object if its dirty count exceeds our cached dirty
    # count or there is no cached dirty count.
    if lst_dcount is None or lst_dcount < cur_dcount:
        # Cache the current dirty count.
        op[ID_PYGEN_DIRTY_CACHE] = cur_dcount
        clone = linked_object.GetClone(c4d.COPYFLAGS_NO_HIERARCHY)
        clone.SetMg(c4d.Matrix())
        print "Built cache"
        return clone
    # Otherwise return the cache.
    else:
        print "Returned cache"
        return op.GetCache()

Cheers
zipit

Thanks a lot for the explanation!
I replaced the plugin id with a user data and it seems to work ok. Still trying to figure out the matrix on the other thread though.

Here is the adapted code:

def main():
    ID_PYGEN_DIRTY_CACHE = op[c4d.ID_USERDATA,3]
    linked_object = op[c4d.ID_USERDATA, 2]
    # Get the last cached dirty count and the current dirty count.
    lst_dcount = ID_PYGEN_DIRTY_CACHE
    cur_dcount = linked_object.GetDirty(c4d.DIRTYFLAGS_ALL)

    if linked_object is None:
        return c4d.BaseList2D(c4d.Onull)
    # Return the linked object if its dirty count exceeds our cached dirty
    # count or there is no cached dirty count.
    if lst_dcount is None or lst_dcount < cur_dcount or op.GetCache() is None:
        # Cache the current dirty count.
        op[c4d.ID_USERDATA,3] = cur_dcount
        clone = linked_object.GetClone(c4d.COPYFLAGS_NO_HIERARCHY)
        clone.SetMg(c4d.Matrix())
        print "Built cache"
        return clone
    # Otherwise return the cache.
    else:
        print "Returned cache"
        return op.GetCache()

And the file:
python_dirtyState_0001.c4d