| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739 |
- from .utilities import (prRed, prGreen, prPurple, prWhite,
- prOrange,
- wrapRed, wrapGreen, wrapPurple, wrapWhite,
- wrapOrange,)
- from .base_definitions import GraphError
- # BE VERY CAREFUL
- # the x_containers files import * from this file
- # so all the top-level imports are carried over
- def get_socket_value(node_socket):
- # if node_socket.bl_idname in ['RelationshipSocket', 'xFormSocket']:
- 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: unify the fill_paramaters for auto-gen nodes
- def fill_parameters(nc, np = None):
- from .utilities import get_node_prototype
- if not np:
- if ( (nc.signature[0] in ["MANTIS_AUTOGENERATED", "SCHEMA_AUTOGENERATED" ]) or
- (nc.signature[-1] in ["NodeGroupOutput", "NodeGroupInput"]) ): # I think this is harmless
- return None
- else:
- np = get_node_prototype(nc.signature, nc.base_tree)
- if not np:
- raise RuntimeError(wrapRed("No node prototype found for... %s" % ( [nc.base_tree] + list(nc.signature[1:]) ) ) )
- for key in nc.parameters.keys():
- node_socket = np.inputs.get(key)
- # if (nc.signature[0] is "MANTIS_AUTOGENERATED"):
- # node_socket = None
- # for node_socket in np.inputs:
- # if node_socket.identifier == nc.signature[-1]:
- # break
- if nc.parameters[key] is not None:
- continue # will be filled by the node itself
- if not node_socket:
- #maybe the node socket has no name
- if ( ( len(np.inputs) == 0) and ( len(np.outputs) == 1) ):
- # this is a simple input node.
- node_socket = np.outputs[0]
- elif key == 'Name': # for Links we just use the Node Label, or if there is no label, the name.
- nc.parameters[key] = np.label if np.label else np.name
- continue
- else:
- pass
- if node_socket:
- if node_socket.bl_idname in ['RelationshipSocket', 'xFormSocket']:
- continue
- elif hasattr(node_socket, "default_value"):
- if (value := get_socket_value(node_socket)) is not None:
- nc.parameters[key] = value
- else:
- raise RuntimeError(wrapRed("No value found for " + nc.__repr__() + " when filling out node parameters for " + np.name + "::"+node_socket.name))
- else:
- pass
- # if key in ['Use Target Z']:
- # prRed (nc, key, nc.parameters[key])
- def evaluate_input(node_container, input_name, index=0):
- if not (node_container.inputs.get(input_name)):
- # just return the parameter, there is no socket associated
- # prOrange("No input: %s, %s" %(node_container, input_name))
- return node_container.parameters.get(input_name)
- trace = trace_single_line(node_container, input_name, index)
- # this should give a key error if there is a problem
- # it is NOT handled here because it should NOT happen
- try:
- prop = trace[0][-1].parameters[trace[1].name] #[0] = nodes [-1] = last node, read its parameters
- except Exception as e:
- prRed (trace[1].name, trace[0][-1], "prepared" if trace[0][-1].prepared else "NO","executed" if trace[0][-1].executed else "NO")
- raise e
- return prop # this should not be necessary...but dicts will be dicts
- def check_for_driver(node_container, input_name, index = None):
- prop = evaluate_input(node_container, input_name)
- if (index is not None):
- prop = prop[index]
- return (prop.__class__.__name__ == 'MantisDriver')
- def trace_node_lines(node_container):
- """ Tells the depth of a node within the node tree. """
- node_lines = []
- if hasattr(node_container, "inputs"):
- for key, socket in node_container.inputs.items():
- # Recrusive search through the tree.
- # * checc each relevant input socket in the node
- # * for EACH input, find the node it's connected to
- # * repeat from here until you get all the lines
- if ( ( key in ["Relationship", "Parent", "Input Relationship", "Target"])
- and (socket.is_connected) ):
- # it is necesary to check the key because of Link nodes,
- # which don't really traverse like normal.
- # TODO: see if I can refactor this to make it traverse
- other = socket.from_node
- if (other):
- other_lines = trace_node_lines(other)
- if not other_lines:
- node_lines.append([other])
- for line in other_lines:
- node_lines.append( [other] + line )
- return node_lines
- # TODO: modify this to work with multi-input nodes
- def 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/out
- def 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, socket
- def trace_all_lines_up(nc, output_name):
- copy_items = {}
- for item in dir(nc):
- if "__" not in item:
- copy_items[item]=getattr(nc, item)
- # we want to copy it, BUT:
- copy_items["outputs"]:{output_name:nc.outputs[output_name]}
- # override outputs with just the one we care about.
-
- check_me = type('', (object,), copy_items)
- return get_depth_lines(check_me)[1]
- def num_hierarchy_connections(nc):
- num=0
- for out in nc.outputs:
- for link in out.links:
- if link.is_hierarchy: num+=1
- return num
- def list_hierarchy_connections(nc):
- return len(nc.hierarchy_connections)-1
- hc=[]
- for out in nc.outputs:
- for link in out.links:
- if link.is_hierarchy: hc.append(link.to_node)
- return num
- # what this is doing is giving a list of Output-Index that is the path to the given node, from a given root.
- # HOW TO REWRITE...
- # we simply do the same thing, but we look at the outputs, not the old hierarchy-connections
- # we can do the same tree-search but we simply ignore an output if it is not hierarchy.
- # the existing code isn't complicated, it's just hard to read. So this new code should be easier to read, too.
- def get_depth_lines(root):
- from .base_definitions import GraphError
- path, nc_path = [0,], [root,]
- lines, nc_paths = {}, {}
- nc_len = len(root.hierarchy_connections)-1
- curheight=0
- while (path[0] <= nc_len):
- # this doesn't seem to make this any slower. It is good to check it.
- if nc_path[-1] in nc_path[:-1]:
- raise GraphError(wrapRed(f"Infinite loop detected while depth sorting for root {root}."))
- #
- nc_path.append(nc_path[-1].hierarchy_connections[path[-1]])
- if (not (node_lines := lines.get(nc_path[-1].signature, None))):
- node_lines = lines[nc_path[-1].signature] = set()
- if (not (node_paths := nc_paths.get(nc_path[-1].signature, None))):
- node_paths = nc_paths[nc_path[-1].signature] = set()
- node_lines.add(tuple(path)); node_paths.add(tuple(nc_path))
- if nc_path[-1].hierarchy_connections: # if there is at least one element
- path.append(0); curheight+=1
- else: # at this point, nc_path is one longer than path because path is a segment between two nodes
- # or more siimply, because nc_path has the root in it and path starts with the first node
- path[curheight] = path[curheight] + 1
- nc_path.pop() # so we go back and horizontal
- if ( path[-1] <= len(nc_path[-1].hierarchy_connections)-1 ):
- pass # and continue if we can
- elif curheight > 0: # otherwise we keep going back
- while(len(path) > 1):
- path.pop(); curheight -= 1; path[curheight]+=1; nc_path.pop()
- if ( (len(nc_path)>1) and path[-1] < len(nc_path[-1].hierarchy_connections) ):
- break
- return lines, nc_paths
- # same but because the checks end up costing a fair amount of time, I don't want to use this one unless I need to.
- def get_prepared_depth_lines(root,):
- # import pstats, io, cProfile
- # from pstats import SortKey
- # with cProfile.Profile() as pr:
- path, nc_path = [0,], [root,]
- lines, nc_paths = {}, {}
- nc_len = len(prepared_connections(root, ))-1
- curheight=0
- while (path[0] <= nc_len):
- if nc_path[-1] in nc_path[:-1]:
- raise GraphError(wrapRed(f"Infinite loop detected while depth sorting for root {root}."))
- nc_path.append(prepared_connections(nc_path[-1], )[path[-1]])
- if (not (node_lines := lines.get(nc_path[-1].signature, None))):
- node_lines = lines[nc_path[-1].signature] = set()
- if (not (node_paths := nc_paths.get(nc_path[-1].signature, None))):
- node_paths = nc_paths[nc_path[-1].signature] = set()
- node_lines.add(tuple(path)); node_paths.add(tuple(nc_path))
- if prepared_connections(nc_path[-1], ): # if there is at least one element
- path.append(0); curheight+=1
- else: # at this point, nc_path is one longer than path because path is a segment between two nodes
- # or more siimply, because nc_path has the root in it and path starts with the first node
- path[curheight] = path[curheight] + 1
- nc_path.pop() # so we go back and horizontal
- if path[-1] <= len(prepared_connections(nc_path[-1], ))-1:
- pass # and continue if we can
- elif curheight > 0: # otherwise we keep going back
- while(len(path) > 1):
- path.pop(); curheight -= 1; path[curheight]+=1; nc_path.pop()
- if (len(nc_path)>1) and path[-1] < len(prepared_connections(nc_path[-1], ) ):
- break
- # from the Python docs at https://docs.python.org/3/library/profile.html#module-cProfile
- # s = io.StringIO()
- # sortby = SortKey.TIME
- # ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
- # ps.print_stats()
- # print(s.getvalue())
- return lines, nc_paths
- def prepared_connections(nc):
- if nc.prepared:
- return nc.hierarchy_connections
- else:
- ret = []
- for hc in nc.hierarchy_connections:
- if hc.prepared:
- ret.append(hc)
- return ret
- # return [hc for hc in nc.hierarchy_connections if hc.prepared]
- def node_depth(lines):
- maxlen = 0
- for line in lines:
- if ( (l := len(line) ) > maxlen):
- maxlen = l
- return maxlen
-
- #TODO rewrite this so it'll work with new nc_path thing
- # not a high priority bc this was debugging code for something that
- # works and has since ben refactored to work better
- def printable_path(nc, path, no_wrap = False):
- string = ""; cur_nc = nc
- #DO: find out if the copy is necessary
- path = path.copy(); path.reverse()
- dummy = lambda a : a
- while path:
- wrap = dummy
- if not no_wrap:
- wrap=wrapWhite
- if (cur_nc.node_type == 'DRIVER'):
- wrap = wrapPurple
- elif (cur_nc.node_type == 'XFORM'):
- wrap = wrapOrange
- elif (cur_nc.node_type == 'LINK'):
- wrap = wrapGreen
- string += wrap(cur_nc.__repr__()) + " -> "
- try:
- cur_nc = get_from_path(cur_nc, [path.pop()] )
- except IndexError:
- string = string[:-4]
- return string
- string = string[:-4]
- return string
- # why is this not printing groups in brackets?
- def get_parent(node_container, type = 'XFORM'):
- # type variable for selecting whether to get either
- # the parent xForm or the inheritance node
- node_line, socket = trace_single_line(node_container, "Relationship")
- parent_nc = None
- for i in range(len(node_line)):
- # check each of the possible parent types.
- if ( (node_line[ i ].__class__.__name__ == 'LinkInherit') ):
- try: # it's the next one
- if (type == 'XFORM'):
- return node_line[ i + 1 ]
- else: # type = 'LINK'
- return node_line[ i ]
- except IndexError: # if there is no next one...
- return None # then there's no parent!
- return None
- # TODO!
- #
- # make this do shorthand parenting - if no parent, then use World
- # if the parent node is skipped, use the previous node (an xForm)
- # with default settings.
- # it is OK to generate a new, "fake" node container for this!
- def get_target_and_subtarget(node_container, linkOb, input_name = "Target"):
- from bpy.types import PoseBone, Object, SplineIKConstraint, ArmatureModifier, HookModifier
- subtarget = ''; target = node_container.evaluate_input(input_name)
- if target:
- if not hasattr(target, "bGetObject"):
- prRed(f"No {input_name} target found for {linkOb.name} in {node_container} because there is no connected node, or node is wrong type")
- return
- if (isinstance(target.bGetObject(), PoseBone)):
- subtarget = target.bGetObject().name
- target = target.bGetParentArmature()
- elif (isinstance(target.bGetObject(), Object) ):
- target = target.bGetObject()
- else:
- raise RuntimeError("Cannot interpret linkOb target!")
-
- if (isinstance(linkOb, SplineIKConstraint)):
- if target and target.type not in ["CURVE"]:
- raise GraphError(wrapRed("Error: %s requires a Curve input, not %s" %
- (node_container, type(target))))
- linkOb.target = target# don't get a subtarget
- if (input_name == 'Pole Target'):
- linkOb.pole_target, linkOb.pole_subtarget = target, subtarget
- else:
- if hasattr(linkOb, "target"):
- linkOb.target = target
- if hasattr(linkOb, "object"):
- linkOb.object = target
- if hasattr(linkOb, "subtarget"):
- linkOb.subtarget = subtarget
- def 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.
- # TODO: this should handle sub-properties better
- def evaluate_sockets(nc, c, 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 c.__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(c, prop)[index], prop1, val)
- else:
- try:
- setattr(ob, att_name, val)
- except Exception as e:
- prRed(ob, att_name, val); raise e
- # HACK I think I should do this in __init__
- if not hasattr(nc, "drivers"):
- nc.drivers = {}
- # end HACK
- for prop, (sock, default) in props_sockets.items():
- # c = nc.bObject
- # 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 c.__class__.__name__ in ["Key"]: # this is a property of a property...
- sub_props = [c]
- 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(c, prop, default)
- if nc.node_type in ['LINK',]:
- printname = wrapOrange(nc.GetxForm().bGetObject().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 c.__class__.__name__ in ["NodesModifier"]:
- nc.drivers[sock] = "[\""+original_prop+"\"]" # lol. It is a dict element not a "true" property
- elif c.__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(c, 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(c, prop, not nc.evaluate_input(sock))
- else:
- try:
- # prRed(c.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(c, prop) and (not isinstance(getattr(c, prop), str)) and hasattr(getattr(c, 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(c,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(c,prop)[val_index] = value
- else:
- # prOrange("Skipping the Thing", getattr(c, prop))
- safe_setattr(c, prop, nc.evaluate_input(sock))
- except Exception as e:
- prRed(c, nc, prop, sock, nc.evaluate_input(sock))
- raise e
- def finish_driver(nc, 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:
- # todo: deduplicate this terrible hack
- c = None # no idea what this c and sub_prop thing is, HACK?
- if hasattr(nc, "bObject"):
- c = nc.bObject # STUPID # stupid and bad HACK here too
- if ("." in prop) and nc.__class__.__name__ != "DeformerMorphTargetDeform": # this is a property of a property...
- sub_props = [c]
- 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[nc.bObject] # we use "unsafe" brackets instead of get() because we want to see any errors that occur
- else:
- driver["owner"] = nc.bObject
- # prPurple("Successfully created driver for %s" % prop)
- driver["prop"] = prop
- return driver
- # prWhite(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 None
- def finish_drivers(nc):
- # gonna make this into a common function...
- drivers = []
- if not hasattr(nc, "drivers"):
- # prGreen(f"No Drivers to construct for {nc}")
- return # HACK
- for driver_item, prop in nc.drivers.items():
- if isinstance(prop, list):
- for sub_item in prop:
- drivers.append(finish_driver(nc, (driver_item, sub_item[1]), sub_item[0]))
- else:
- drivers.append(finish_driver(nc, driver_item, prop))
- from .drivers import CreateDrivers
- CreateDrivers(drivers)
- from .base_definitions import from_name_filter, to_name_filter
- def detect_hierarchy_link(from_node, from_socket, to_node, to_socket,):
- if to_node.node_type in ['DUMMY_SCHEMA', 'SCHEMA']:
- return False
- if (from_socket in from_name_filter) or (to_socket in to_name_filter):
- return False
- # if from_node.__class__.__name__ in ["UtilityCombineVector", "UtilityCombineThreeBool"]:
- # return False
- return True
- #Dummy classes for logic with node containers, they are not meant to do
- # each and every little thing the "real" Blender classes do.
- class NodeLink:
- from_node = None
- from_socket = None
- to_node = None
- to_socket = None
-
- def __init__(self, from_node, from_socket, to_node, to_socket, multi_input_sort_id=0):
- if from_node.signature == to_node.signature:
- raise RuntimeError("Cannot connect a node to itself.")
- self.from_node = from_node
- self.from_socket = from_socket
- self.to_node = to_node
- self.to_socket = to_socket
- self.from_node.outputs[self.from_socket].links.append(self)
- # it is the responsibility of the node that uses these links to sort them correctly based on the sort_id
- self.multi_input_sort_id = multi_input_sort_id
- self.to_node.inputs[self.to_socket].links.append(self)
- self.is_hierarchy = detect_hierarchy_link(from_node, from_socket, to_node, to_socket,)
- self.is_alive = True
-
- def __repr__(self):
- return self.from_node.outputs[self.from_socket].__repr__() + " --> " + self.to_node.inputs[self.to_socket].__repr__()
- # link_string = # if I need to colorize output for debugging.
- # if self.is_hierarchy:
- # return wrapOrange(link_string)
- # else:
- # return wrapWhite(link_string)
-
- def die(self):
- self.is_alive = False
- self.to_node.inputs[self.to_socket].flush_links()
- self.from_node.outputs[self.from_socket].flush_links()
-
- def insert_node(self, middle_node, middle_node_in, middle_node_out, re_init_hierarchy = True):
- to_node = self.to_node
- to_socket = self.to_socket
- self.to_node = middle_node
- self.to_socket = middle_node_in
- middle_node.outputs[middle_node_out].connect(to_node, to_socket)
- if re_init_hierarchy:
- from .utilities import init_connections, init_dependencies
- init_connections(self.from_node)
- init_connections(middle_node)
- init_dependencies(middle_node)
- init_dependencies(to_node)
- class NodeSocket:
- @property # this is a read-only property.
- def is_linked(self):
- return bool(self.links)
-
- def __init__(self, is_input = False,
- node = None, name = None,
- traverse_target = None):
- self.can_traverse = False # to/from the other side of the parent node
- self.traverse_target = None
- self.node = node
- self.name = name
- self.is_input = is_input
- self.links = []
- if (traverse_target):
- self.can_traverse = True
-
- def connect(self, node, socket, sort_id=0):
- if (self.is_input):
- to_node = self.node; from_node = node
- to_socket = self.name; from_socket = socket
- else:
- from_node = self.node; to_node = node
- from_socket = self.name; to_socket = socket
- for l in from_node.outputs[from_socket].links:
- if l.to_node==to_node and l.to_socket==to_socket:
- return None
- new_link = NodeLink(
- from_node,
- from_socket,
- to_node,
- to_socket,
- sort_id)
- return new_link
-
- def set_traverse_target(self, traverse_target):
- self.traverse_target = traverse_target
- self.can_traverse = True
-
- def flush_links(self):
- self.links = [l for l in self.links if l.is_alive]
-
- @property
- def is_connected(self):
- return len(self.links)>0
-
-
- def __repr__(self):
- return self.node.__repr__() + "::" + self.name
- # do I need this and the link class above?
- class DummyLink:
- #gonna use this for faking links to keep the interface consistent
- def __init__(self, from_socket, to_socket, nc_from=None, nc_to=None, original_from=None, multi_input_sort_id=0):
- self.from_socket = from_socket
- self.to_socket = to_socket
- self.nc_from = nc_from
- self.nc_to = nc_to
- self.multi_input_sort_id = multi_input_sort_id
- # self.from_node = from_socket.node
- # self.to_node = to_socket.node
- if (original_from):
- self.original_from = original_from
- else:
- self.original_from = self.from_socket
- def __repr__(self):
- return(self.nc_from.__repr__()+":"+self.from_socket.name + " -> " + self.nc_to.__repr__()+":"+self.to_socket.name)
- # Here we setup some properties that we want every class to have
- # I am not using inheritance because I don't like it, I think it adds a bunch of weird complexity
- # This, on the otherhand, is basically just extending the class definitions I have with some boilerplate
- # and that means I can avoid writing the boilerplate, get the benefits of class inheritance, and none of the bad
- #
- # I don't want these classes to share a superclass, because I want them to be nothing more than a way to
- # package some methods and data together, so my node tree can be like a linked list using basic types like dict
- # essentially, I don't have any use for a superclass, but I do need these properties and attributes.
- # and now, instead of declaring it, inheriting it, and shadowing it elsewhere, I simply do not declare it unless I need it
- #
- # I've looked in to using an interface or abstract metaclass but all of those seem more complicated than doing this.
- def setup_container(some_class):
- # NOTE: DO NOT use default properties except for None, we don't want to share data between classes.
- def flush_links(self):
- for inp in self.inputs.values():
- inp.flush_links()
- for out in self.outputs.values():
- out.flush_links()
- functions = {
- # importantly: these below are not properties.
- "evaluate_input" : lambda self, input_name, index=0 : evaluate_input(self, input_name, index),
- "fill_parameters" : lambda self : fill_parameters(self),
- 'flush_links': flush_links,
- "bPrepare" : lambda self, bContext=None : None,
- "bExecute" : lambda self, bContext=None : None,
- "bFinalize" : lambda self, bContext=None : None,
- }
- for n, f in functions.items():
- if not hasattr(some_class, n):
- setattr(some_class, n, f)
- some_class.__repr__ = lambda self : self.signature.__repr__()
|