| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 | from .utilities import (prRed, prGreen, prPurple, prWhite,                              prOrange,                              wrapRed, wrapGreen, wrapPurple, wrapWhite,                              wrapOrange,)from .base_definitions import GraphError, NodeSocket, MantisNodefrom collections.abc import Callable# BE VERY CAREFUL# the x_containers files import * from this file# so all the top-level imports are carried over#TODO: refactor the socket definitions so this becomes unnecessary.def get_socket_value(node_socket):    value = None    if hasattr(node_socket, "default_value"):         value = node_socket.default_value    if node_socket.bl_idname == 'MatrixSocket':        value =  node_socket.TellValue()    return value# TODO: modify this to work with multi-input nodesdef trace_single_line(node_container, input_name, link_index=0):    # DO: refactor this for new link class    """Traces a line to its input."""    nodes = [node_container]    # Trace a single line    if (socket := node_container.inputs.get(input_name) ):        while (socket.is_linked):            link = socket.links[link_index]; link_index = 0            if (socket := link.from_node.outputs.get(link.from_socket)):                nodes.append(socket.node)                if socket.can_traverse:                    socket = socket.traverse_target                else: # this is an output.                    break            else:                break    return nodes, socket# this is same as the other, just flip from/to and in/outdef trace_single_line_up(node_container, output_name,):    """I use this to get the xForm from a link node."""    nodes = [node_container]    if hasattr(node_container, "outputs"):        # Trace a single line        if (socket := node_container.outputs.get(output_name) ):            while (socket.is_linked):                # This is bad, but it's efficient for nodes that only expect                #  one path along the given line                link = socket.links[0] # TODO: find out if this is wise.                other = link.to_node.inputs.get(link.to_socket)                if (other):                    socket = other                    if socket.can_traverse:                        socket = socket.traverse_target                        nodes.append(socket.node)                    else: # this is an input.                        nodes.append(socket.node)                        break                else:                    break    return nodes, socketdef trace_line_up_branching(node : MantisNode, output_name : str,        break_condition : Callable = lambda node : False):    """ Returns all leaf nodes at the ends of branching lines from an output."""    leaf_nodes = []    if hasattr(node, "outputs"):        # Trace a single line        if (socket := node.outputs.get(output_name) ):            check_sockets={socket}            while (check_sockets):                # This is bad, but it's efficient for nodes that only expect                #  one path along the given line                socket = check_sockets.pop()                for link in socket.links:                    other = link.to_node.inputs.get(link.to_socket)                    if (other):                        socket = other                        if break_condition(socket.node):                            leaf_nodes.append(socket.node)                        elif socket.is_input and socket.can_traverse:                            check_sockets.add(socket.traverse_target)                        else: # this is an input.                            leaf_nodes.append(socket.node)    return leaf_nodesdef setup_custom_props(nc):    from .utilities import get_node_prototype    if nc.signature[0] == 'SCHEMA_AUTOGENERATED':        from .base_definitions import custom_props_types        if nc.__class__.__name__ not in custom_props_types:            # prRed(f"Reminder: figure out how to deal with custom property setting for Schema Node {nc}")            raise RuntimeError(wrapRed(f"Custom Properties not set up for node {nc}"))        return    else:        np = get_node_prototype(nc.signature, nc.base_tree)    if np:        setup_custom_props_from_np(nc, np)    else:        prRed("Failed to setup custom properties for: nc")def setup_custom_props_from_np(nc, np):    for inp in np.inputs:        if inp.identifier == "__extend__": continue        if not (inp.name in nc.inputs.keys()) :            socket = NodeSocket(is_input = True, name = inp.name, node = nc,)            nc.inputs[inp.name] = socket            nc.parameters[inp.name] = None            for attr_name in ["min", "max", "soft_min", "soft_max", "description"]:                try:                    setattr(socket, attr_name, getattr(inp, attr_name))                except AttributeError:                    pass    for out in np.outputs:        if out.identifier == "__extend__": continue        if not (out.name in nc.outputs.keys()) :            nc.outputs[out.name] = NodeSocket(is_input = False, name = out.name, node = nc,)            def prepare_parameters(nc):    # some nodes add new parameters at runtime, e.g. Drivers    # so we need to take that stuff from the node_containers that have    #  been executed prior to this node.    for s_name, sock in nc.inputs.items():        if not (sock.is_linked):            continue        if (sock.name  in sock.links[0].from_node.parameters.keys()):            nc.parameters[s_name] = sock.links[0].from_node.parameters[sock.name]    # should work, this is ugly.def check_for_driver(node_container, input_name, index = None):    prop = node_container.evaluate_input(input_name)    if (index is not None):        prop = prop[index]    return (prop.__class__.__name__ == 'MantisDriver')# TODO: this should handle sub-properties betterdef evaluate_sockets(nc, b_object, props_sockets,):    # this is neccesary because some things use dict properties for dynamic properties and setattr doesn't work    def safe_setattr(ob, att_name, val):        if ob.__class__.__name__ in ["NodesModifier"]:            ob[att_name]=val        elif b_object.__class__.__name__ in ["Key"]:            if not val: val=0            ob.key_blocks[att_name].value=val        elif "]." in att_name:            # it is of the form prop[int].prop2            prop=att_name.split('[')[0]            prop1=att_name.split('.')[1]            index = int(att_name.split('[')[1][0])            setattr(getattr(b_object, prop)[index], prop1, val)        else:            try:                setattr(ob, att_name, val)            except Exception as e:                prRed(ob, att_name, val); raise e    for prop, (sock, default) in props_sockets.items():        # annoyingly, sometimes the socket is an array        index = None        if isinstance(sock, tuple):            index = sock[1]; sock = sock[0]        if (check_for_driver(nc, sock, index)):            sock = (sock, index)            original_prop = prop            # TODO: deduplicate this terrible hack            if ("." in prop) and not b_object.__class__.__name__ in ["Key"]: # this is a property of a property...                sub_props = [b_object]                while ("." in prop):                    split_prop = prop.split(".")                    prop = split_prop[1]                    sub_prop = (split_prop[0])                    if ("[" in sub_prop):                        sub_prop, index = sub_prop.split("[")                        index = int(index[0])                        sub_props.append(getattr(sub_props[-1], sub_prop)[index])                    else:                        sub_props.append(getattr(sub_props[-1], sub_prop))                safe_setattr(sub_props[-1], prop, default)            # this is really stupid            else:                safe_setattr(b_object, prop, default)            if nc.node_type in ['LINK',]:                printname  = wrapOrange(b_object.id_data.name)            elif nc.node_type in ['XFORM',]:                printname  = wrapOrange(nc.bGetObject().name)            else:                printname = wrapOrange(nc)            print("Adding driver %s to %s in %s" % (wrapPurple(original_prop), wrapWhite(nc.signature[-1]), printname))            if b_object.__class__.__name__ in ["NodesModifier"]:                nc.drivers[sock] = "[\""+original_prop+"\"]" # lol. It is a dict element not a "true" property            elif b_object.__class__.__name__ in ["Key"]:                nc.drivers[sock] = "key_blocks[\""+original_prop+"\"].value"            else:                nc.drivers[sock] = original_prop        else: # here we can do error checking for the socket if needed            if (index is not None):                safe_setattr(b_object, prop, nc.evaluate_input(sock)[index])            else:                    # 'mute' is better than 'enabled'                # UGLY HACK          # because it is available in older                if (prop == 'mute'): # Blenders.                    safe_setattr(b_object, prop, not bool(nc.evaluate_input(sock)))                elif (prop == 'hide'): # this will not cast it for me, annoying.                    safe_setattr(b_object, prop, bool(nc.evaluate_input(sock)))                else:                    try:                        # prRed(b_object.name, nc, prop, nc.evaluate_input(sock) )                        # print( nc.evaluate_input(sock))                    # value_eval = nc.evaluate_input(sock)                    # just wanna see if we are dealing with some collection                    # check hasattr in case it is one of those ["such-and-such"] props, and ignore those                        if hasattr(b_object, prop) and (not isinstance(getattr(b_object, prop), str)) and hasattr(getattr(b_object, prop), "__getitem__"):                            # prGreen("Doing the thing")                            for val_index, value in enumerate(nc.evaluate_input(sock)):                                # assume this will work, both because val should have the right number of elements, and because this should be the right data type.                                from .drivers import MantisDriver                                if isinstance(value, MantisDriver):                                    getattr(b_object,prop)[val_index] =  default[val_index]                                    print("Adding driver %s to %s in %s" % (wrapPurple(prop), wrapWhite(nc.signature[-1]), nc))                                    try:                                        nc.drivers[sock].append((prop, val_index))                                    except:                                        nc.drivers[sock] = [(prop, val_index)]                                else:                                    getattr(b_object,prop)[val_index] =  value                        else:                            # prOrange("Skipping the Thing", getattr(b_object, prop))                            safe_setattr(b_object, prop, nc.evaluate_input(sock))                    except Exception as e:                        prRed(b_object, nc, prop, sock, nc.evaluate_input(sock))                        raise edef finish_driver(nc, b_object, driver_item, prop):    # prWhite(nc, prop)    index = driver_item[1]; driver_sock = driver_item[0]    driver_trace = trace_single_line(nc, driver_sock)    driver_provider, driver_socket = driver_trace[0][-1], driver_trace[1]    if index is not None:        driver = driver_provider.parameters[driver_socket.name][index].copy()        # this is harmless and necessary for the weird ones where the property is a vector too        driver["ind"] = index    else:        driver = driver_provider.parameters[driver_socket.name].copy()    if driver:        if ("." in prop) and nc.__class__.__name__ != "DeformerMorphTargetDeform": # this is a property of a property...            sub_props = [b_object]            while ("." in prop):                split_prop = prop.split(".")                prop = split_prop[1]                sub_prop = (split_prop[0])                if ("[" in sub_prop):                    sub_prop, index = sub_prop.split("[")                    index = int(index[0])                    sub_props.append(getattr(sub_props[-1], sub_prop)[index])                else:                    sub_props.append(getattr(sub_props[-1], sub_prop))            driver["owner"] = sub_props[-1]        elif nc.node_type in ['XFORM',] and nc.__class__.__name__ in ['xFormBone']:            # TODO: I really shouldn't have to hardcode this. Look into better solutions.            if prop in ['hide', 'show_wire']: # we need to get the bone, not the pose bone.                bone_col = nc.bGetParentArmature().data.bones            else:                bone_col = nc.bGetParentArmature().pose.bones            driver["owner"] = bone_col[b_object] # we use "unsafe" brackets instead of get() because we want to see any errors that occur        # HACK having special cases here is indicitave of a deeper problem that should be refactored        elif nc.__class__.__name__ in ['xFormCurvePin'] and \                      prop in ['offset_factor', 'forward_axis', 'up_axis']:                driver["owner"] = b_object.constraints['Curve Pin']        else:            driver["owner"] = b_object        driver["prop"] = prop        return driver    else:        prOrange("Provider", driver_provider)        prGreen("socket", driver_socket)        print (index)        prPurple(driver_provider.parameters[driver_socket.name])        prRed("Failed to create driver for %s" % prop)        return Nonedef finish_drivers(nc):    drivers = []    if not hasattr(nc, "drivers"):        return # HACK    for driver_item, prop in nc.drivers.items():        b_objects = [nc.bObject]        if nc.node_type == 'LINK':            b_objects = nc.bObject # this is already a list        for b_object in b_objects:            if isinstance(prop, list):                for sub_item in prop:                        drivers.append(finish_driver(nc, b_object, (driver_item, sub_item[1]), sub_item[0]))                else:                    drivers.append(finish_driver(nc, b_object, (driver_item, sub_item[1]), sub_item[0]))            else:                drivers.append(finish_driver(nc, b_object, driver_item, prop))    from .drivers import CreateDrivers    CreateDrivers(drivers)
 |