Forum: Poser Python Scripting


Subject: Is there a way to support Undo with LoadLibraryPose

yarp opened this issue on Apr 23, 2021 ยท 28 posts


adp001 posted Sat, 24 April 2021 at 11:58 AM

I tried to make a "discrete" Undo before. Turns out it is slower than saving the whole scene. But maybe you can use something of it to play around:

from __future__ import print_function
import sys

try:
    import poser
except ImportError:
    # Not required while inside Poser Python, but very helpfull for external editors.
    # See https://adp.spdns.org
    from PoserLibs import POSER_FAKE as poser

if sys.version_info.major > 2:
    # Python 3 (Poser 12 and above)
    map = lambda a, b: [a(_b) for _b in b]
    basestring = str


def actor_hasgeom(ac):
    assert isinstance(ac, poser.ActorType)
    return hasattr(ac, "Geometry") 
           and ac.Geometry() is not None 
           and ac.Geometry().NumVertices() > 0


def collect_parms(actor, include_morphs=True):
    assert isinstance(actor, poser.ActorType)
    geom = actor.Geometry() if actor_hasgeom(actor) else None
    parm_dict = dict()
    for parm in actor.Parameters():  # type: poser.ParmType
        d = parm_dict.setdefault(parm.InternalName(), dict())
        for entry in dir(poser.ParmType):
            if "MorphTargetDelta" in entry:
                # This is a special case
                if entry.startswith("Set"):
                    # This one isn't needed here.
                    continue
                morph = list()
                if geom is not None and include_morphs and parm.IsMorphTarget():
                    for i in range(geom.NumVertices()):
                        x, y, z = parm.MorphTargetDelta(i)
                        if x != 0 or y != 0 or z != 0:
                            morph.append((i, x, y, z))
                d["MorphTargetDelta"] = morph or None
            elif entry.startswith("Set"):
                # For an undo we need to save anything we can "Set"
                attr_name = entry[3:]
                if attr_name in ("ValueFrame", "RangeConstant", "RangeLinear", "RangeSpline",
                                 "SplineBreak", "UpdateCallback"):
                    # ignore this in this version...
                    continue
                value = getattr(parm, attr_name)()
                d[attr_name] = value

        parm_dict[parm.InternalName()] = d
    return parm_dict


def collectFigure(figure):
    actor_dict = dict()
    for actor in figure.Actors():
        actor_dict[actor.InternalName()] = collect_parms(actor, include_morphs=True)
    return actor_dict


def collectLights():
    light_dict = dict()
    for light in poser.Scene().Lights():
        light_dict[light.InternalName()] = collect_parms(light)
    return light_dict


def collectCams():
    cam_dict = dict()
    for cam in poser.Scene().Cameras():
        cam_dict[cam.InternalName()] = collect_parms(cam)
    return cam_dict


def collectProps():
    prop_dict = dict()
    for prop in [ac for ac in poser.Scene().Actors() if ac.IsProp()]:
        prop_dict[prop.InternalName()] = collect_parms(prop)
    return prop_dict


def set_parms(actor, parm_dicts):
    assert isinstance(actor, poser.ActorType)
    assert isinstance(parm_dicts, dict)

    for parm_name, parm_dict in parm_dicts.items():
        parm = actor.Parameter(parm_name)
        if not parm:
            continue
        for parm_key, parm_value in parm_dict.items():
            if parm_key == "MorphTargetDelta":
                if parm_value is not None:
                    for entry in parm_value:
                        parm.SetMorphTargetDelta(*entry)
            else:
                attr_name = "Set%s" % parm_key
                getattr(parm, attr_name)(parm_value)


def undo_figure(figure, actor_dict):
    print("Undo figure", end=" ")
    assert isinstance(figure, poser.FigureType)
    inames = set(ac.InternalName() for ac in figure.Actors())
    for key, value in actor_dict.items():
        print(key, end=" ")
        poser.Scene().ProcessSomeEvents()
        if key in inames:
            set_parms(figure.ActorByInternalName(key), value)
    print("Done")


def undo_lights(light_dict):
    print("Undo lights", end=" ")
    inames = set(ac.InternalName() for ac in poser.Scene().Lights())
    for key, value in light_dict.items():
        print(key, end=" ")
        if key in inames:
            set_parms(poser.Scene().ActorByInternalName(key), value)
    print("Done")


def undo_cams(cam_dict):
    print("Undo cams", end=" ")
    inames = set(ac.InternalName() for ac in poser.Scene().Cameras())
    for key, value in cam_dict.items():
        print(key, end=" ")
        if key in inames:
            set_parms(poser.Scene().ActorByInternalName(key), value)
    print("Done")


def undo_props(prop_dict):
    print("Undo props", end=" ")
    inames = set(ac.InternalName() for ac in poser.Scene().Actors() if ac.IsProp())
    for key, value in prop_dict.items():
        print(key, end=" ")
        if key in inames:
            set_parms(poser.Scene().ActorByInternalName(key), value)
    print("Done")


lights = collectLights()
cams = collectCams()
props = collectProps()
actors = collectFigure(poser.Scene().CurrentFigure())
print("Scene data collected for undo.")
poser.Scene().ProcessSomeEvents()
print("Undo figure now.")
poser.Scene().ProcessSomeEvents()
undo_figure(poser.Scene().CurrentFigure(), actors)
#undo_cams(cams)
#undo_lights(lights)
#undo_props(props)

Saving is quit fast. But when undoing something it feels like Poser has died. So better try undoing lights or cams first :)