Prechádzať zdrojové kódy

Cleanup: remove old comments and dead code paths

also fix bugs and warnings in Add/Edit/Remove custom property operators
added better warnings to execution when graph fails to execute
Added a break in the readtree while loop to prevent eternal loops
Joseph Brandenburg 9 mesiacov pred
rodič
commit
5b5dd52f2f

+ 52 - 22
__init__.py

@@ -72,26 +72,16 @@ class MantisNodeCategory(NodeCategory):
 class SchemaNodeCategory(NodeCategory):
     @classmethod
     def poll(cls, context):
-        # doesn't seem to work tho
-        try:
-            return (context.space_data.path[len(path)-1].node_tree.bl_idname == 'SchemaTree')
-        except:
-            return True
+        return (context.space_data.path[len(context.space_data.path)-1].node_tree.bl_idname == 'SchemaTree')
 
 
 input_category=[
             NodeItem("InputFloatNode"),
             NodeItem("InputVectorNode"),
             NodeItem("InputBooleanNode"),
-            # NodeItem("InputBooleanThreeTupleNode"),
-            # NodeItem("InputRotationOrderNode"),
-            # NodeItem("InputTransformSpaceNode"),
             NodeItem("InputStringNode"),
             NodeItem("InputIntNode"),
-            # NodeItem("InputQuaternionNode"),
-            # NodeItem("InputQuaternionNodeAA"),
             NodeItem("InputMatrixNode"),
-            # NodeItem("InputLayerMaskNode"), # DEPRECATED since we have Bone Collections now
             NodeItem("InputExistingGeometryObject"),
             NodeItem("InputExistingGeometryData"),
     ]
@@ -122,9 +112,7 @@ link_relationship_category = [
 deformer_category=[NodeItem(cls.bl_idname) for cls in deformer_definitions.TellClasses()]
 xForm_category = [
          NodeItem("xFormGeometryObject"),
-        # NodeItem("xFormNullNode"), # REMOVED since GeometryObject makes an empty if it has no Geometry
         NodeItem("xFormBoneNode"),
-        # NodeItem("xFormRootNode"), # REMOVED since it is a no-op
         NodeItem("xFormArmatureNode"),
     ]
 driver_category = [
@@ -173,12 +161,10 @@ groups_category = [
         NodeItem("MantisSchemaGroup"),
     ]
 
-# THIS is stupid, should be filled out automatically
+
 node_categories = [
     # identifier, label, items list
     MantisNodeCategory('INPUT', "Input", items=input_category),
-    # MantisNodeCategory('LINK', "Link", items=[]),
-    # MantisNodeCategory('LINK_TRACKING', "Link", items=[]),
     MantisNodeCategory('LINK_TRANSFORM', "Link (Transform)", items=link_transform_category),
     MantisNodeCategory('LINK_TRACKING', "Link (Tracking)", items=link_tracking_category),
     MantisNodeCategory('LINK_RELATIONSHIP', "Link (Inheritance)", items=link_relationship_category),
@@ -192,7 +178,6 @@ node_categories = [
 ]
 
 schema_category=[NodeItem(cls.bl_idname) for cls in schema_definitions.TellClasses()]
-
 schema_categories = [
     SchemaNodeCategory('SCHEMA_SCHEMA', "Schema", items=schema_category),
 ]
@@ -222,9 +207,51 @@ def init_keymaps():
 
 addon_keymaps = []
 
+# handlers!
+#annoyingly these have to be persistent
+from bpy.app.handlers import persistent
+@persistent
+def update_handler(scene):
+    context=bpy.context
+    if context.space_data:
+        if not hasattr(context.space_data, "path"):
+            return
+        trees = [p.node_tree for p in context.space_data.path]
+        if not trees: return
+        if (node_tree := trees[0]).bl_idname in ['MantisTree']:
+            if node_tree.do_live_update and not (node_tree.is_executing or node_tree.is_exporting):
+                prev_links = node_tree.num_links
+                node_tree.num_links = len(node_tree.links)
+                if (prev_links == -1):
+                    return
+                if prev_links != node_tree.num_links:
+                    node_tree.tree_valid = False
+                if node_tree.tree_valid == False:
+                        scene.render.use_lock_interface = True
+                        node_tree.update_tree(context)
+                        scene.render.use_lock_interface = False
+
+@persistent
+def execute_handler(scene):
+    context = bpy.context
+    if context.space_data:
+        if not hasattr(context.space_data, "path"):
+            return
+        trees = [p.node_tree for p in context.space_data.path]
+        if not trees: return
+        if (node_tree := trees[0]).bl_idname in ['MantisTree']:
+            if node_tree.tree_valid and node_tree.do_live_update and not (node_tree.is_executing or node_tree.is_exporting):
+                scene.render.use_lock_interface = True
+                node_tree.execute_tree(context)
+                scene.render.use_lock_interface = False
+                node_tree.tree_valid = False
+
 
 
 def register():
+    if bpy.app.version >= (4, 4):
+        raise NotImplementedError("Blender 4.4 is not supported at this time.")
+
     from bpy.utils import register_class
     
     for cls in classes:
@@ -238,11 +265,14 @@ def register():
     nodeitems_utils.register_node_categories('SchemaNodeCategories', schema_categories)
 
 
-    if (not bpy.app.background):
-        km, kmi = init_keymaps()
-        for k in kmi:
-            k.active = True
-            addon_keymaps.append((km, k))
+    km, kmi = init_keymaps()
+    for k in kmi:
+        k.active = True
+        addon_keymaps.append((km, k))
+    # add the handlers
+    bpy.app.handlers.depsgraph_update_pre.append(update_handler)
+    bpy.app.handlers.depsgraph_update_post.append(execute_handler)
+
 
     
 

+ 21 - 405
base_definitions.py

@@ -11,19 +11,16 @@ from .utilities import (prRed, prGreen, prPurple, prWhite,
 
 from .utilities import get_socket_maps, relink_socket_map, do_relink
 
-from bpy.app.handlers import persistent
 
 def TellClasses():
     #Why use a function to do this? Because I don't need every class to register.
     return [ MantisTree,
              SchemaTree,
-            #  MantisNode,
-            #  SchemaNode,
              MantisNodeGroup,
              SchemaGroup,
-             MantisVisualizeTree,
-             MantisVisualizeNode,
            ]
+def error_popup_draw(self, context):
+    self.layout.label(text="Error executing tree. See Console.")
 
 class MantisTree(NodeTree):
     '''A custom node tree type that will show up in the editor type list'''
@@ -38,14 +35,8 @@ class MantisTree(NodeTree):
     is_executing:BoolProperty(default=False)
     is_exporting:BoolProperty(default=False)
     execution_id:StringProperty(default='')
-
     
     parsed_tree={}
-    
-    def interface_update(self, context):
-        # no idea what this does
-        print ("Update Interface function in MantisTree class") 
-
 
     if bpy.app.version >= (3, 2):  # in 3.1 this can lead to a crash
         @classmethod
@@ -86,32 +77,10 @@ class MantisTree(NodeTree):
                 except Exception as e:
                     print("Node \"%s\" failed to update display with error: %s" %(wrapGreen(node.name), wrapRed(e)))
                     # raise e
-        # from .utilities import all_trees_in_tree
-        # all_my_trees = all_trees_in_tree(self,)
-        # for tree in all_my_trees:
-        # I think using the current visible tree is actually fine
-        if True:
-            # HACK 
-            for l in current_tree.links:
-                l.is_valid = True # I don't want Blender marking these for me. No warning is better than a wrong warning.
-            # end HACK
-
-        # in the future, fix it properly. Here is a start.
-        else: # this is harder to fix than I thought!
-            tree_path_names=[None,]
-            for i, path_item in enumerate(bpy.context.space_data.path[:-1]):
-                tree_path_names.append(path_item.node_tree.nodes.active.name)
-            for l in current_tree.links: # We need to loop through actual links so we can filter them
-                if l.is_valid == False:
-                    if nc_from := self.parsed_tree.get( (*tree_path_names, l.from_node.name)) is None:
-                        continue
-                    for o in nc_from.outputs.values():
-                        for mlink in o.links:
-                            if (mlink.to_socket == l.to_socket.name) and (mlink.to_node.signature[-1] == l.to_node.name):
-                                l.is_valid = not mlink.is_hierarchy
-                                # in reality, we need to trace the link UP and find a cycle - then find a link in the cycle that is non-hierarchy
-                                # the actual link that BLENDER marks as invalid may be a hierarchy link.
-        # let's see if this works
+        
+        # TODO: deal with invalid links properly.
+        #    - Non-hierarchy links should be ignored in the circle-check and so the links should be marked valid in such a circle
+        #    - hierarchy-links should be marked invalid and prevent the tree from executing.
 
         
     
@@ -126,72 +95,18 @@ class MantisTree(NodeTree):
             readtree.execute_tree(self.parsed_tree, self, context)
         except RecursionError as e:
             prRed("Recursion error while parsing tree.")
-            # prRed(e); node_tree.do_live_update = False
-        # except Exception:
-        #     pass
-        finally: # first time I have ever used a finally block in my life.
+        finally:
             self.is_executing = False
 
-    
-
-# class SchemaPropertyGroup(bpy.types.PropertyGroup):
 
 class SchemaTree(NodeTree):
     '''A node tree representing a schema to generate a Mantis tree'''
     bl_idname = 'SchemaTree'
     bl_label = "Rigging Nodes Schema"
     bl_icon = 'RIGID_BODY_CONSTRAINT'
-
-    tree_valid:BoolProperty(default=False)
-    do_live_update:BoolProperty(default=True) # use this to disable updates for e.g. scripts
-    is_executing:BoolProperty(default=False)
-    num_links:IntProperty(default=-1)
-    # filepath:StringProperty(default="", subtype='FILE_PATH')
     
-    parsed_tree={}
+    do_live_update:BoolProperty(default=False) # this is only needed for consistency, it is never used.
 
-    # def update(self):
-    #     for n in self.nodes:
-    #         if hasattr(n, "update"): n.update()
-
-    # def update_tree(self, context):
-    #     prRed("update tree for Schema Tree!")
-    #     # self.tree_valid = True
-    #     # return
-    #     from . import readtree
-    #     prGreen("Validating Tree: %s" % self.name)
-    #     parsed_tree = readtree.parse_tree(self)
-    #     self.parsed_tree=parsed_tree
-    #     current_tree = bpy.context.space_data.path[-1].node_tree
-    #     self.tree_valid = True
-    #     prWhite("Number of Nodes: %s" % (len(self.parsed_tree)))
-    #     self.display_update(context)
-    
-    # def display_update(self, context):
-    #     prRed("display update for Schema Tree!")
-    #     return
-    #     current_tree = bpy.context.space_data.path[-1].node_tree
-    #     for node in current_tree.nodes:
-    #         if hasattr(node, "display_update"):
-    #             try:
-    #                 node.display_update(self.parsed_tree, context)
-    #             except Exception as e:
-    #                 print("Node \"%s\" failed to update display with error: %s" %(wrapGreen(node.name), wrapRed(e)))
-    #                 # raise e
-
-    # def execute_tree(self,context):
-    #     self.is_executing = True
-    #     prRed("executing Schema Tree!")
-    #     self.tree_valid = False
-    #     self.is_executing = False
-    #     return
-    #     prGreen("Executing Tree: %s" % self.name)
-    #     from . import readtree
-    #     try:
-    #         readtree.execute_tree(self.parsed_tree, self, context)
-    #     except RecursionError as e:
-    #         prRed("Recursion error while parsing tree.")
-    #         prRed(e); node_tree.do_live_update = False
 
     if bpy.app.version >= (3, 2):  # in 3.1 this can lead to a crash
         @classmethod
@@ -205,11 +120,6 @@ class SchemaTree(NodeTree):
 
 
 class MantisNode:
-    # num_links:IntProperty(default=-1) # is this used anywhere?
-
-    # is_triggering_execute:BoolProperty(default=False)
-
-    # do_display_update:BoolProperty(default=False)
     @classmethod
     def poll(cls, ntree):
         return (ntree.bl_idname in ['MantisTree', 'SchemaTree'])
@@ -220,25 +130,17 @@ class MantisNode:
             node_tree = context.space_data.path[0].node_tree
             from . import readtree
             if node_tree.do_live_update:
-                # prOrange("Updating from insert_link callback")
                 node_tree.update_tree(context)
                 if (link.to_socket.is_linked == False):
                     node_tree.num_links+=1
-                elif (link.to_socket.is_multi_input):# and 
-                    #len(link.to_socket.links) < link.to_socket.link_limit ):
-                    # the above doesn't work and I can't be bothered to fix it right now TODO
+                elif (link.to_socket.is_multi_input):
                     node_tree.num_links+=1
             
 class SchemaNode:
-    # is_triggering_execute:BoolProperty(default=False)
-    # do_display_update:BoolProperty(default=False)
     @classmethod
     def poll(cls, ntree):
         return (ntree.bl_idname in ['SchemaTree'])
 
-
-                
-
 class LinkNode(MantisNode):
     useTarget : BoolProperty(default=False)
     @classmethod
@@ -256,21 +158,15 @@ class DeformerNode(MantisNode):
         return (ntree.bl_idname in ['MantisTree', 'SchemaTree'])
 
 
-
-
-# from bpy.types import NodeCustomGroup
-
 def poll_node_tree(self, object):
     if isinstance(object, MantisTree):
         return True
     return False
 
-
-# TODO this should check ID's instead of name
+# TODO: try to check identifiers instead of name.
 def node_group_update(node):
     toggle_update = node.id_data.do_live_update
     node.id_data.do_live_update = False
-    # prWhite (node.name, len(node.inputs), len(node.outputs))
 
     identifiers_in={socket.identifier:socket for socket in node.inputs}
     identifiers_out={socket.identifier:socket for socket in node.outputs}
@@ -286,17 +182,16 @@ def node_group_update(node):
         if item.in_out == 'OUTPUT':
             if s:= identifiers_out.get(item.identifier): # if the requested output doesn't exist, update
                 found_out.append(item.identifier)
-                if update_output: continue # done here
+                if update_output: continue
                 if s.bl_idname != item.socket_type: update_output = True; continue
-            else: update_output = True; continue # prRed(f"Not found: {item.name}"); 
+            else: update_output = True; continue
         else:
             if s:= identifiers_in.get(item.identifier): # if the requested input doesn't exist, update
                 found_in.append(item.identifier)
                 if update_input: continue # done here
                 if s.bl_idname != item.socket_type: update_input = True; continue
-            else: update_input = True; continue # prGreen(f"Not found: {item.name}");
+            else: update_input = True; continue
     
-
     # Schema has an extra input for Length and for Extend.
     if node.bl_idname == 'MantisSchemaGroup':
         found_in.extend(['Schema Length', ''])
@@ -312,10 +207,8 @@ def node_group_update(node):
             node.outputs.remove(out)
     #
     if len(node.inputs) > 0 and (inp := node.inputs[-1]).bl_idname == 'WildcardSocket' and inp.is_linked:
-        # prPurple("oink! I am a piggy!")
         update_input = True
     if len(node.outputs) > 0 and  (out := node.outputs[-1]).bl_idname == 'WildcardSocket' and out.is_linked:
-        # prPurple("woof! I am a doggy!")
         update_output = True
     #
     if not (update_input or update_output):
@@ -359,9 +252,8 @@ def node_group_update(node):
 
 
 def node_tree_prop_update(self, context):
-    if self.is_updating: # this looks dumb... but update() can be called from update() in a sort of accidental way.
-        return
-    # prGreen("updating me...")
+    if self.is_updating: # update() can be called from update() and that leads to an infinite loop.
+        return           # so we check if an update is currently running.
     self.is_updating = True
     node_group_update(self)
     self.is_updating = False
@@ -381,8 +273,8 @@ class MantisNodeGroup(Node, MantisNode):
     is_updating:BoolProperty(default=False)
     
     def update(self):
-        if self.is_updating: # this looks dumb... but update() can be called from update() in a sort of accidental way.
-            return
+        if self.is_updating: # update() can be called from update() and that leads to an infinite loop.
+            return           # so we check if an update is currently running.
         self.is_updating = True
         node_group_update(self)
         self.is_updating = False
@@ -393,8 +285,6 @@ class MantisNodeGroup(Node, MantisNode):
         row.operator("mantis.edit_group", text="", icon='NODETREE', emboss=True)
         
 
-class CircularDependencyError(Exception):
-    pass
 class GraphError(Exception):
     pass
 
@@ -409,50 +299,27 @@ def poll_node_tree_schema(self, object):
         return True
     return False
 
-# def update_schema_length(self, context):
-#     pass # for now
-
 
 # TODO tiny UI problem - inserting new links into the tree will not place them in the right place.
 
-#this is a schema node in a mantis tree... kinda confusing
 class SchemaGroup(Node, MantisNode):
     bl_idname = "MantisSchemaGroup"
     bl_label = "Node Schema"
     
     node_tree:PointerProperty(type=NodeTree, poll=poll_node_tree_schema, update=node_tree_prop_update,)
-    # schema_length:IntProperty(default=5, update=update_schema_length)
     is_updating:BoolProperty(default=False)
 
-
-    # incoming link
-    # from-node = name
-    # from socket = index or identifier or something
-    # property is unset as soon as it is used
-    # so the update function checks this property and handles the incoming link in an allowed place
-    # and actually the handle-link function can work as a switch - when the property is unset, it allows new links
-    # otherwise it unsets the property and returns.
-    
-    # def init(self, context):
-    #     self.inputs.new("IntSocket", "Schema Length")
-    #     self.inputs.new("WildcardSocket", "")
-
     def draw_buttons(self, context, layout):
         row = layout.row(align=True)
         row.prop(self, "node_tree", text="")
         row.operator("mantis.edit_group", text="", icon='NODETREE', emboss=True)
-        # layout.prop(self, "schema_length", text="n=")
-    
-    # WHAT IF:
-    #   - the function that creates the input/output map returns to a property in the node
-    #   - then the node handles the update in its update function.
 
     def update(self):
-        if self.is_updating: # this looks dumb... but update() can be called from update() in a sort of accidental way.
-            return
+        if self.is_updating: # update() can be called from update() and that leads to an infinite loop.
+            return           # so we check if an update is currently running.
         self.is_updating = True
         node_group_update(self)
-        # kinda dumb but necessary since update doesn't always fix this
+        # reset things if necessary:
         if self.node_tree:
             if len(self.inputs) == 0:
                 self.inputs.new("IntSocket", "Schema Length", identifier='Schema Length')
@@ -460,260 +327,8 @@ class SchemaGroup(Node, MantisNode):
                 self.inputs.new("WildcardSocket", "", identifier="__extend__")
         self.is_updating = False
 
-    # def insert_link(self, link):
-    #     if self.node_tree is None:
-    #         link.is_valid = False
-    #         return
-    #     sock_type = link.from_socket.bl_idname
-    #     for i, sock in enumerate(self.inputs):
-    #         if sock == link.to_socket: # dumb but whatever
-    #             identifier = link.to_socket.identifier
-    #             if sock.bl_idname not in ["WildcardSocket"]:
-    #                 if sock.is_linked == True:
-    #                     links = [ getattr(l, "from_socket") for l in sock.links ]
-    #                     name = sock.name
-    #                     # self.inputs.remove(sock)
-    #                     # new_input = self.inputs.new(sock_type, name, identifier=identifier, use_multi_input=True); self.inputs.move(-1, i)
-    #                     sock.display_shape = 'SQUARE_DOT'
-    #                     interface_item = self.node_tree.interface.items_tree[name]
-    #                     if not (interface_parent := self.node_tree.interface.items_tree.get('Array')):
-    #                         interface_parent = self.node_tree.interface.new_panel(name='Array')
-    #                     self.node_tree.interface.move_to_parent(interface_item, interface_parent, len(interface_parent.interface_items))
-    #                     # sock.link_limit = self.schema_length TODO this will be very hard to get at this point
-    #                     # self.id_data.links.new()
-    #             else: #if link.to_socket  == self.inputs[-1]:
-    #                     self.inputs.remove(sock)#self.inputs[-1])
-    #                     #
-    #                     name_stem = link.from_socket.bl_idname.replace('Socket',''); num=0
-    #                     if hasattr(link.from_socket, "default_value"):
-    #                         name_stem = type(link.from_socket.default_value).__name__
-    #                     for n in self.inputs:
-    #                         if name_stem in n.name: num+=1
-    #                     name = name_stem + '.' + str(num).zfill(3)
-    #                     #
-    #                     new_input = self.inputs.new(sock_type, name, identifier=identifier, use_multi_input=False); self.inputs.move(-1, i+1)
-    #                     new_input.link_limit = 1
-    #                     # link.to_socket = new_input
-    #                     # this seems to work
-
-    #                     self.inputs.new("WildcardSocket", "")
-    #                     if not (interface_parent := self.node_tree.interface.items_tree.get('Constant')):
-    #                         interface_parent = self.node_tree.interface.new_panel(name='Constant')
-    #                     self.node_tree.interface.new_socket(name=name,in_out='INPUT', socket_type=sock_type, parent=interface_parent)
-    #                     return
-            
-    # TODO: investigate whether this is necessary
-    # @classmethod
-    # def poll(cls, ntree):
-    #     return (ntree.bl_idname in ['MantisTree'])
-
-
 
 
-# handlers!
-
-#annoyingly these have to be persistent
-@persistent
-def update_handler(scene):
-    context=bpy.context
-    if context.space_data:
-        if not hasattr(context.space_data, "path"):
-            return
-        trees = [p.node_tree for p in context.space_data.path]
-        if not trees: return
-        if (node_tree := trees[0]).bl_idname in ['MantisTree']:
-            if node_tree.do_live_update and not (node_tree.is_executing or node_tree.is_exporting):
-                prev_links = node_tree.num_links
-                node_tree.num_links = len(node_tree.links)
-                if (prev_links == -1):
-                    return
-                if prev_links != node_tree.num_links:
-                    node_tree.tree_valid = False
-                if node_tree.tree_valid == False:
-                        node_tree.update_tree(context)
-
-@persistent
-def execute_handler(scene):
-    context = bpy.context
-    if context.space_data:
-        if not hasattr(context.space_data, "path"):
-            return
-        trees = [p.node_tree for p in context.space_data.path]
-        if not trees: return
-        if (node_tree := trees[0]).bl_idname in ['MantisTree']:
-            if node_tree.tree_valid and node_tree.do_live_update and not (node_tree.is_executing or node_tree.is_exporting):
-                node_tree.execute_tree(context)
-                node_tree.tree_valid = False
-
-# @persistent
-# def load_post_handler(scene):
-#     print ("cuss and darn")
-
-#     # import bpy
-#     import sys
-
-#     def wrapRed(skk):    return "\033[91m{}\033[00m".format(skk)
-#     def intercept(fn, *args):
-#         print(wrapRed("Intercepting:"), fn)
-#         sys.stdout.flush()
-
-#         fn(*args)
-
-#         print(wrapRed("... done"))
-#         sys.stdout.flush()
-
-#     for attr in dir(bpy.app.handlers):
-#         if attr.startswith("_"):
-#             continue
-
-#         handler_list = getattr(bpy.app.handlers, attr)
-#         if attr =='load_post_handler':
-#             continue
-#         if not isinstance(handler_list, list):
-#             continue
-#         if not handler_list:
-#             continue
-
-#         print("Intercept Setup:", attr)
-
-#         handler_list[:] = [lambda *args: intercept(fn, *args) for fn in handler_list]
-
-#         # import cProfile
-#         from os import environ
-#         do_profile=False
-#         print (environ.get("DOPROFILE"))
-#         if environ.get("DOPROFILE"):
-#             do_profile=True
-#         if do_profile:
-#             # cProfile.runctx("tree.update_tree(context)", None, locals())
-#             # cProfile.runctx("tree.execute_tree(context)", None, locals())
-#             import hunter
-#             hunter.trace(stdlib=False, action=hunter.CallPrinter(force_colors=False))
-# #     def msgbus_callback(*args):
-# #         # print("something changed!")
-# #         print("Something changed!", args)
-# #     owner = object()
-
-#     subscribe_to = (bpy.types.Node, "location")
-#     subscribe_to = (bpy.types.Node, "color")
-#     subscribe_to = (bpy.types.Node, "dimensions")
-#     subscribe_to = (bpy.types.Node, "height")
-#     subscribe_to = (bpy.types.Node, "width")
-#     subscribe_to = (bpy.types.Node, "inputs")
-#     subscribe_to = (bpy.types.Node, "outputs")
-#     subscribe_to = (bpy.types.Node, "select")
-#     subscribe_to = (bpy.types.Node, "name")
-#     subscribe_to = (bpy.types.NodeSocket, "name")
-#     subscribe_to = (bpy.types.NodeSocket, "display_shape")
-
-
-#     bpy.msgbus.subscribe_rna(
-#         key=subscribe_to,
-#         owner=owner,
-#         args=(1, 2, 3),
-#         notify=msgbus_callback,
-#     )
-
-
-# print ("cuss and darn")
-
-# bpy.app.handlers.load_post.append(set_tree_invalid)
-bpy.app.handlers.depsgraph_update_pre.append(update_handler)
-bpy.app.handlers.depsgraph_update_post.append(execute_handler)
-# bpy.app.handlers.load_post.append(load_post_handler)
-
-# # import bpy
-# import sys
-
-# def wrapRed(skk):    return "\033[91m{}\033[00m".format(skk)
-# def intercept(fn, *args):
-#     print(wrapRed("Intercepting:"), fn)
-#     sys.stdout.flush()
-
-#     fn(*args)
-
-#     print(wrapRed("... done"))
-#     sys.stdout.flush()
-
-# for attr in dir(bpy.app.handlers):
-#     if attr.startswith("_"):
-#         continue
-
-#     handler_list = getattr(bpy.app.handlers, attr)
-#     if attr =='load_post_handler':
-#         continue
-#     if not isinstance(handler_list, list):
-#         continue
-#     if not handler_list:
-#         continue
-
-#     print("Intercept Setup:", attr)
-
-#     handler_list[:] = [lambda *args: intercept(fn, *args) for fn in handler_list]
-
-class MantisVisualizeTree(NodeTree):
-    '''A custom node tree type that will show up in the editor type list'''
-    bl_idname = 'MantisVisualizeTree'
-    bl_label = "mantis output"
-    bl_icon = 'HIDE_OFF'
-
-
-class MantisVisualizeNode(Node):
-    bl_idname = "MantisVisualizeNode"
-    bl_label = "Node"
-    @classmethod
-    def poll(cls, ntree):
-        return (ntree.bl_idname in ['MantisVisualizeTree'])
-    
-    def init(self, context):
-        pass
-    
-    def gen_data(self, nc, np = None):
-        self.use_custom_color = True
-        if nc.node_type == 'XFORM':
-            self.color = (1.0 ,0.5, 0.0)
-        if nc.node_type == 'LINK':
-            self.color = (0.4 ,0.2, 1.0)
-        if nc.node_type == 'UTILITY':
-            self.color = (0.2 ,0.2, 0.2)
-        if nc.node_type == 'SCHEMA':
-            self.color = (0.85 ,0.95, 0.9)
-        if nc.node_type == 'DUMMY':
-            self.color = (0.05 ,0.05, 0.15)
-        self.name = ''.join(nc.signature[1:])
-
-        if np:
-            if np.label:
-                self.label=np.label
-            else:
-                self.label=np.name
-            for inp in nc.inputs:
-                s = self.inputs.new('WildcardSocket', inp)
-                try:
-                    if sock := np.inputs.get(inp):
-                        s.color = inp.color
-                except AttributeError: #default bl_idname types like Float and Vector, no biggie
-                    pass
-                except KeyError:
-                    pass
-            for out in nc.outputs:
-                s = self.outputs.new('WildcardSocket', out)
-                try:
-                    if sock := np.outputs.get(out):
-                        s.color = out.color
-                except AttributeError: #default bl_idname types like Float and Vector, no biggie
-                    pass
-                except KeyError:
-                    pass
-            self.location = np.location # will get overwritten by Grandalf later.
-        else:
-            self.label = nc.signature[-1] # which is be the unique name.
-            for inp in nc.inputs:
-                self.inputs.new('WildcardSocket', inp)
-            for out in nc.outputs:
-                self.outputs.new('WildcardSocket', out)
-
-                
 # replace names with bl_idnames for reading the tree and solving schemas.
 replace_types = ["NodeGroupInput", "NodeGroupOutput", "SchemaIncomingConnection",
                  "SchemaArrayInput", "SchemaConstInput", "SchemaConstOutput", "SchemaIndex",
@@ -724,6 +339,7 @@ replace_types = ["NodeGroupInput", "NodeGroupOutput", "SchemaIncomingConnection"
 #   in schema generation and this is the easiest way to do it for now.
 custom_props_types = ["LinkArmature", "UtilityKeyframe", "UtilityFCurve", "UtilityDriver", "xFormBone"]
 
+# filters for determining if a link is a hierarchy link or a non-hierarchy (cyclic) link.
 from_name_filter = ["Driver",]
 to_name_filter = [
                    "Custom Object xForm Override",

+ 5 - 29
deformer_containers.py

@@ -187,7 +187,7 @@ class DeformerArmature:
         evaluate_sockets(self, d, props_sockets)
         #
         if (skin_method := self.evaluate_input("Skinning Method")) == "AUTOMATIC_HEAT":
-            # This is reatarded and leads to somewhat unpredictable
+            # This is bad and leads to somewhat unpredictable
             #  behaviour, e.g. what object will be selected? What mode?
             # also bpy.ops is ugly and prone to error when used in
             #  scripts. I don't intend to use bpy.ops when I can avoid it.
@@ -323,13 +323,12 @@ class DeformerMorphTargetDeform:
 
     def gen_morph_target_modifier(self):
         mod_name = self.evaluate_input("Name")
-        # self.GetxForm().bGetObject().add_rest_position_attribute = True # this ended up being unnecessary
         try:
             m = self.GetxForm().bGetObject().modifiers[mod_name]
         except KeyError:
             m = self.GetxForm().bGetObject().modifiers.new(mod_name, type='NODES')
         self.bObject = m
-        # at this point we make the node tre
+        # at this point we make the node tree
         from bpy import data
         ng = data.node_groups.new(mod_name, "GeometryNodeTree")
         m.node_group = ng
@@ -340,8 +339,6 @@ class DeformerMorphTargetDeform:
         # TODO CLEANUP here
         if (position := ng.nodes.get("Position")) is None: position = ng.nodes.new("GeometryNodeInputPosition")
         if (index := ng.nodes.get("Index")) is None: index = ng.nodes.new("GeometryNodeInputIndex")
-        # if (rest_position := ng.nodes.get("Rest Position")) is None: rest_position = ng.nodes.new("GeometryNodeInputNamedAttribute"); rest_position.name = "Rest Position"
-        # rest_position.data_type = "FLOAT_VECTOR"; rest_position.inputs["Name"].default_value = "rest_position"
         rest_position = position
         add_these = []
 
@@ -354,7 +351,6 @@ class DeformerMorphTargetDeform:
                 targets.append(v)
         for i, t in enumerate(targets):
             mt_node = t.links[0].from_node
-            # mt_name = "Morph Target."+str(i).zfill(3)
             mt_name = mt_node.GetxForm().bGetObject().name
             vg = mt_node.parameters["Morph Target"]["vertex_group"]
             if vg: mt_name = mt_name+"."+vg
@@ -367,8 +363,6 @@ class DeformerMorphTargetDeform:
             ng.interface.new_socket(mt_name+" Value", in_out = "INPUT", socket_type="NodeSocketFloat")
             ob_node = ng.nodes.new("GeometryNodeObjectInfo")
             sample_index = ng.nodes.new("GeometryNodeSampleIndex"); sample_index.data_type = 'FLOAT_VECTOR'
-            # if (rest_position := ng.nodes.get("Rest Position")) is None: rest_position = ng.nodes.new("GeometryNodeInputNamedAttribute"); rest_position.name = "Rest Position"
-            # rest_position.data_type = "FLOAT_VECTOR"; rest_position.inputs["Name"].default_value = "rest_position"
             subtract = ng.nodes.new("ShaderNodeVectorMath"); subtract.operation="SUBTRACT"
             scale1 = ng.nodes.new("ShaderNodeVectorMath"); scale1.operation="SCALE"
             
@@ -390,10 +384,6 @@ class DeformerMorphTargetDeform:
                 # ng.links.new(input=rest_position.outputs["Attribute"], output=subtract.inputs[1])
                 ng.links.new(input=rest_position.outputs["Position"], output=subtract.inputs[1])
 
-            # IMPORTANT TODO (?):
-               # relative objects can be recursive! Need to go back and back and back as long as we have relative objects!
-               # in reality I am not sure haha
-
             ng.links.new(input=subtract.outputs["Vector"], output=scale1.inputs[0])
 
             # TODO: this should be exposed as a node tree input
@@ -426,16 +416,12 @@ class DeformerMorphTargetDeform:
         SugiyamaGraph(ng, 12)
 
 
-        # for k,v in props_sockets.items():
-        #     print(wrapWhite(k), wrapOrange(v))
         evaluate_sockets(self, m, props_sockets)
         for socket, ob in object_map.items():
             m[socket]=ob
         finish_drivers(self)
 
     def gen_shape_key(self): # TODO: make this a feature of the node definition that appears only when there are no prior deformers - and shows a warning!
-        self.gen_morph_target_modifier()
-        return
         # TODO: the below works well, but it is quite slow. It does not seem to have better performence. Its only advantage is export to FBX.
         # there are a number of things I need to fix here
         #   - reuse shape keys if possible
@@ -465,7 +451,6 @@ class DeformerMorphTargetDeform:
             # hafta make new geometry for the object and add shape keys and all that
             # the benefit to all this being maybe better performence and exporting to game engines via .fbx
 
-        #
         # first make a basis shape key
         ob.shape_key_add(name='Basis', from_mix=False)
         keys={}
@@ -503,16 +488,7 @@ class DeformerMorphTargetDeform:
         self.bObject = sk.id_data
         evaluate_sockets(self, sk.id_data, props_sockets)
         finish_drivers(self)
-            
-        
         prWhite(f"Initializing morph target took {time() -start_time} seconds")
-        
-        
-        
-            
-
-            
-
         # then we need to get all the data from the morph targets, pull all the relative shapes first and add them, vertex groups and properties
         # next we add all the shape keys that are left, and their vertex groups
         # set the slider ranges to -10 and 10
@@ -523,10 +499,10 @@ class DeformerMorphTargetDeform:
         # let's find out if there is a prior deformer.
         # if not, then there should be an option to use plain 'ol shape keys
         # GN is always desirable as an option though because it can be baked.
-        if self.inputs["Deformer"].is_linked:
+        if self.inputs["Deformer"].is_linked and True:
+            # for now we won't do Blender Shape Keys
             self.gen_morph_target_modifier()
-        else:
-            # for now we'll just do it this way.
+        else: # TODO: give the user the option to do this via a node property.
             self.gen_shape_key()
 
 

+ 0 - 246
deformer_definitions.py

@@ -13,252 +13,6 @@ def TellClasses():
              DeformerMorphTargetDeform,
              DeformerMorphTarget,
            ]
-icons = (
-          'NONE', 'QUESTION', 'ERROR', 'CANCEL', 'TRIA_RIGHT',
-          'TRIA_DOWN', 'TRIA_LEFT', 'TRIA_UP', 'ARROW_LEFTRIGHT',
-          'PLUS', 'DISCLOSURE_TRI_RIGHT', 'DISCLOSURE_TRI_DOWN',
-          'RADIOBUT_OFF', 'RADIOBUT_ON', 'MENU_PANEL', 'BLENDER',
-          'GRIP', 'DOT', 'COLLAPSEMENU', 'X', 'DUPLICATE', 'TRASH',
-          'COLLECTION_NEW', 'OPTIONS', 'NODE', 'NODE_SEL', 'WINDOW', 
-          'WORKSPACE', 'RIGHTARROW_THIN', 'BORDERMOVE', 'VIEWZOOM',
-          'ADD', 'REMOVE', 'PANEL_CLOSE', 'COPY_ID', 'EYEDROPPER',
-          'CHECKMARK', 'AUTO', 'CHECKBOX_DEHLT', 'CHECKBOX_HLT',
-          'UNLOCKED', 'LOCKED', 'UNPINNED', 'PINNED', 'SCREEN_BACK',
-          'RIGHTARROW', 'DOWNARROW_HLT', 'FCURVE_SNAPSHOT',
-          'OBJECT_HIDDEN', 'TOPBAR', 'STATUSBAR', 'PLUGIN', 'HELP',
-          'GHOST_ENABLED', 'COLOR', 'UNLINKED', 'LINKED', 'HAND',
-          'ZOOM_ALL', 'ZOOM_SELECTED', 'ZOOM_PREVIOUS', 'ZOOM_IN',
-          'ZOOM_OUT', 'DRIVER_DISTANCE', 'DRIVER_ROTATIONAL_DIFFERENCE',
-          'DRIVER_TRANSFORM', 'FREEZE', 'STYLUS_PRESSURE',
-          'GHOST_DISABLED', 'FILE_NEW', 'FILE_TICK', 'QUIT', 'URL',
-          'RECOVER_LAST', 'THREE_DOTS', 'FULLSCREEN_ENTER',
-          'FULLSCREEN_EXIT', 'BRUSHES_ALL', 'LIGHT', 'MATERIAL',
-          'TEXTURE', 'ANIM', 'WORLD', 'SCENE', 'OUTPUT', 'SCRIPT',
-          'PARTICLES', 'PHYSICS', 'SPEAKER', 'TOOL_SETTINGS',
-          'SHADERFX', 'MODIFIER', 'BLANK1', 'FAKE_USER_OFF',
-          'FAKE_USER_ON', 'VIEW3D', 'GRAPH', 'OUTLINER', 'PROPERTIES',
-          'FILEBROWSER', 'IMAGE', 'INFO', 'SEQUENCE', 'TEXT',
-          'SPREADSHEET', 'SOUND', 'ACTION', 'NLA', 'PREFERENCES',
-          'TIME', 'NODETREE', 'GEOMETRY_NODES', 'CONSOLE', 'TRACKER',
-          'ASSET_MANAGER', 'NODE_COMPOSITING', 'NODE_TEXTURE',
-          'NODE_MATERIAL', 'UV', 'OBJECT_DATAMODE', 'EDITMODE_HLT',
-          'UV_DATA', 'VPAINT_HLT', 'TPAINT_HLT', 'WPAINT_HLT',
-          'SCULPTMODE_HLT', 'POSE_HLT', 'PARTICLEMODE', 'TRACKING',
-          'TRACKING_BACKWARDS', 'TRACKING_FORWARDS',
-          'TRACKING_BACKWARDS_SINGLE', 'TRACKING_FORWARDS_SINGLE',
-          'TRACKING_CLEAR_BACKWARDS', 'TRACKING_CLEAR_FORWARDS',
-          'TRACKING_REFINE_BACKWARDS', 'TRACKING_REFINE_FORWARDS',
-          'SCENE_DATA', 'RENDERLAYERS', 'WORLD_DATA', 'OBJECT_DATA',
-          'MESH_DATA', 'CURVE_DATA', 'META_DATA', 'LATTICE_DATA',
-          'LIGHT_DATA', 'MATERIAL_DATA', 'TEXTURE_DATA', 'ANIM_DATA',
-          'CAMERA_DATA', 'PARTICLE_DATA', 'LIBRARY_DATA_DIRECT',
-          'GROUP', 'ARMATURE_DATA', 'COMMUNITY', 'BONE_DATA',
-          'CONSTRAINT', 'SHAPEKEY_DATA', 'CONSTRAINT_BONE',
-          'CAMERA_STEREO', 'PACKAGE', 'UGLYPACKAGE', 'EXPERIMENTAL',
-          'BRUSH_DATA', 'IMAGE_DATA', 'FILE', 'FCURVE', 'FONT_DATA',
-          'RENDER_RESULT', 'SURFACE_DATA', 'EMPTY_DATA', 'PRESET',
-          'RENDER_ANIMATION', 'RENDER_STILL', 'LIBRARY_DATA_BROKEN',
-          'BOIDS', 'STRANDS', 'GREASEPENCIL', 'LINE_DATA',
-          'LIBRARY_DATA_OVERRIDE', 'GROUP_BONE', 'GROUP_VERTEX',
-          'GROUP_VCOL', 'GROUP_UVS', 'FACE_MAPS', 'RNA', 'RNA_ADD',
-          'MOUSE_LMB', 'MOUSE_MMB', 'MOUSE_RMB', 'MOUSE_MOVE',
-          'MOUSE_LMB_DRAG', 'MOUSE_MMB_DRAG', 'MOUSE_RMB_DRAG',
-          'MEMORY', 'PRESET_NEW', 'DECORATE', 'DECORATE_KEYFRAME',
-          'DECORATE_ANIMATE', 'DECORATE_DRIVER', 'DECORATE_LINKED',
-          'DECORATE_LIBRARY_OVERRIDE', 'DECORATE_UNLOCKED',
-          'DECORATE_LOCKED', 'DECORATE_OVERRIDE', 'FUND',
-          'TRACKER_DATA', 'HEART', 'ORPHAN_DATA', 'USER', 'SYSTEM',
-          'SETTINGS', 'OUTLINER_OB_EMPTY', 'OUTLINER_OB_MESH',
-          'OUTLINER_OB_CURVE', 'OUTLINER_OB_LATTICE',
-          'OUTLINER_OB_META', 'OUTLINER_OB_LIGHT', 'OUTLINER_OB_CAMERA',
-          'OUTLINER_OB_ARMATURE', 'OUTLINER_OB_FONT',
-          'OUTLINER_OB_SURFACE', 'OUTLINER_OB_SPEAKER',
-          'OUTLINER_OB_FORCE_FIELD', 'OUTLINER_OB_GROUP_INSTANCE',
-          'OUTLINER_OB_GREASEPENCIL', 'OUTLINER_OB_LIGHTPROBE',
-          'OUTLINER_OB_IMAGE', 'OUTLINER_COLLECTION',
-          'RESTRICT_COLOR_OFF', 'RESTRICT_COLOR_ON', 'HIDE_ON',
-          'HIDE_OFF', 'RESTRICT_SELECT_ON', 'RESTRICT_SELECT_OFF',
-          'RESTRICT_RENDER_ON', 'RESTRICT_RENDER_OFF',
-          'RESTRICT_INSTANCED_OFF', 'OUTLINER_DATA_EMPTY',
-          'OUTLINER_DATA_MESH', 'OUTLINER_DATA_CURVE',
-          'OUTLINER_DATA_LATTICE', 'OUTLINER_DATA_META',
-          'OUTLINER_DATA_LIGHT', 'OUTLINER_DATA_CAMERA',
-          'OUTLINER_DATA_ARMATURE', 'OUTLINER_DATA_FONT',
-          'OUTLINER_DATA_SURFACE', 'OUTLINER_DATA_SPEAKER',
-          'OUTLINER_DATA_LIGHTPROBE', 'OUTLINER_DATA_GP_LAYER',
-          'OUTLINER_DATA_GREASEPENCIL', 'GP_SELECT_POINTS',
-          'GP_SELECT_STROKES', 'GP_MULTIFRAME_EDITING',
-          'GP_ONLY_SELECTED', 'GP_SELECT_BETWEEN_STROKES',
-          'MODIFIER_OFF', 'MODIFIER_ON', 'ONIONSKIN_OFF',
-          'ONIONSKIN_ON', 'RESTRICT_VIEW_ON', 'RESTRICT_VIEW_OFF',
-          'RESTRICT_INSTANCED_ON', 'MESH_PLANE', 'MESH_CUBE',
-          'MESH_CIRCLE', 'MESH_UVSPHERE', 'MESH_ICOSPHERE', 'MESH_GRID',
-          'MESH_MONKEY', 'MESH_CYLINDER', 'MESH_TORUS', 'MESH_CONE',
-          'MESH_CAPSULE', 'EMPTY_SINGLE_ARROW', 'LIGHT_POINT',
-          'LIGHT_SUN', 'LIGHT_SPOT', 'LIGHT_HEMI', 'LIGHT_AREA', 'CUBE',
-          'SPHERE', 'CONE', 'META_PLANE', 'META_CUBE', 'META_BALL',
-          'META_ELLIPSOID', 'META_CAPSULE', 'SURFACE_NCURVE',
-          'SURFACE_NCIRCLE', 'SURFACE_NSURFACE', 'SURFACE_NCYLINDER',
-          'SURFACE_NSPHERE', 'SURFACE_NTORUS', 'EMPTY_AXIS', 'STROKE',
-          'EMPTY_ARROWS', 'CURVE_BEZCURVE', 'CURVE_BEZCIRCLE',
-          'CURVE_NCURVE', 'CURVE_NCIRCLE', 'CURVE_PATH',
-          'LIGHTPROBE_CUBEMAP', 'LIGHTPROBE_PLANAR', 'LIGHTPROBE_GRID',
-          'COLOR_RED', 'COLOR_GREEN', 'COLOR_BLUE', 'TRIA_RIGHT_BAR',
-          'TRIA_DOWN_BAR', 'TRIA_LEFT_BAR', 'TRIA_UP_BAR',
-          'FORCE_FORCE', 'FORCE_WIND', 'FORCE_VORTEX', 'FORCE_MAGNETIC',
-          'FORCE_HARMONIC', 'FORCE_CHARGE', 'FORCE_LENNARDJONES',
-          'FORCE_TEXTURE', 'FORCE_CURVE', 'FORCE_BOID',
-          'FORCE_TURBULENCE', 'FORCE_DRAG', 'FORCE_FLUIDFLOW',
-          'RIGID_BODY', 'RIGID_BODY_CONSTRAINT', 'IMAGE_PLANE',
-          'IMAGE_BACKGROUND', 'IMAGE_REFERENCE', 'NODE_INSERT_ON',
-          'NODE_INSERT_OFF', 'NODE_TOP', 'NODE_SIDE', 'NODE_CORNER',
-          'ANCHOR_TOP', 'ANCHOR_BOTTOM', 'ANCHOR_LEFT', 'ANCHOR_RIGHT',
-          'ANCHOR_CENTER', 'SELECT_SET', 'SELECT_EXTEND',
-          'SELECT_SUBTRACT', 'SELECT_INTERSECT', 'SELECT_DIFFERENCE',
-          'ALIGN_LEFT', 'ALIGN_CENTER', 'ALIGN_RIGHT', 'ALIGN_JUSTIFY',
-          'ALIGN_FLUSH', 'ALIGN_TOP', 'ALIGN_MIDDLE', 'ALIGN_BOTTOM',
-          'BOLD', 'ITALIC', 'UNDERLINE', 'SMALL_CAPS', 'CON_ACTION',
-          'MOD_LENGTH', 'MOD_DASH', 'MOD_LINEART', 'HOLDOUT_OFF',
-          'HOLDOUT_ON', 'INDIRECT_ONLY_OFF', 'INDIRECT_ONLY_ON',
-          'CON_CAMERASOLVER', 'CON_FOLLOWTRACK', 'CON_OBJECTSOLVER',
-          'CON_LOCLIKE', 'CON_ROTLIKE', 'CON_SIZELIKE', 'CON_TRANSLIKE',
-          'CON_DISTLIMIT', 'CON_LOCLIMIT', 'CON_ROTLIMIT',
-          'CON_SIZELIMIT', 'CON_SAMEVOL', 'CON_TRANSFORM',
-          'CON_TRANSFORM_CACHE', 'CON_CLAMPTO', 'CON_KINEMATIC',
-          'CON_LOCKTRACK', 'CON_SPLINEIK', 'CON_STRETCHTO',
-          'CON_TRACKTO', 'CON_ARMATURE', 'CON_CHILDOF', 'CON_FLOOR',
-          'CON_FOLLOWPATH', 'CON_PIVOT', 'CON_SHRINKWRAP',
-          'MODIFIER_DATA', 'MOD_WAVE', 'MOD_BUILD', 'MOD_DECIM',
-          'MOD_MIRROR', 'MOD_SOFT', 'MOD_SUBSURF', 'HOOK',
-          'MOD_PHYSICS', 'MOD_PARTICLES', 'MOD_BOOLEAN',
-          'MOD_EDGESPLIT', 'MOD_ARRAY', 'MOD_UVPROJECT', 'MOD_DISPLACE',
-          'MOD_CURVE', 'MOD_LATTICE', 'MOD_TINT', 'MOD_ARMATURE',
-          'MOD_SHRINKWRAP', 'MOD_CAST', 'MOD_MESHDEFORM', 'MOD_BEVEL',
-          'MOD_SMOOTH', 'MOD_SIMPLEDEFORM', 'MOD_MASK', 'MOD_CLOTH',
-          'MOD_EXPLODE', 'MOD_FLUIDSIM', 'MOD_MULTIRES', 'MOD_FLUID',
-          'MOD_SOLIDIFY', 'MOD_SCREW', 'MOD_VERTEX_WEIGHT',
-          'MOD_DYNAMICPAINT', 'MOD_REMESH', 'MOD_OCEAN', 'MOD_WARP',
-          'MOD_SKIN', 'MOD_TRIANGULATE', 'MOD_WIREFRAME',
-          'MOD_DATA_TRANSFER', 'MOD_NORMALEDIT',
-          'MOD_PARTICLE_INSTANCE', 'MOD_HUE_SATURATION', 'MOD_NOISE',
-          'MOD_OFFSET', 'MOD_SIMPLIFY', 'MOD_THICKNESS', 'MOD_INSTANCE',
-          'MOD_TIME', 'MOD_OPACITY', 'REC', 'PLAY', 'FF', 'REW',
-          'PAUSE', 'PREV_KEYFRAME', 'NEXT_KEYFRAME', 'PLAY_SOUND',
-          'PLAY_REVERSE', 'PREVIEW_RANGE', 'ACTION_TWEAK', 'PMARKER_ACT',
-          'PMARKER_SEL', 'PMARKER', 'MARKER_HLT', 'MARKER',
-          'KEYFRAME_HLT', 'KEYFRAME', 'KEYINGSET', 'KEY_DEHLT',
-          'KEY_HLT', 'MUTE_IPO_OFF', 'MUTE_IPO_ON', 'DRIVER',
-          'SOLO_OFF', 'SOLO_ON', 'FRAME_PREV', 'FRAME_NEXT',
-          'NLA_PUSHDOWN', 'IPO_CONSTANT', 'IPO_LINEAR', 'IPO_BEZIER',
-          'IPO_SINE', 'IPO_QUAD', 'IPO_CUBIC', 'IPO_QUART', 'IPO_QUINT',
-          'IPO_EXPO', 'IPO_CIRC', 'IPO_BOUNCE', 'IPO_ELASTIC',
-          'IPO_BACK', 'IPO_EASE_IN', 'IPO_EASE_OUT', 'IPO_EASE_IN_OUT',
-          'NORMALIZE_FCURVES', 'VERTEXSEL', 'EDGESEL', 'FACESEL',
-          'CURSOR', 'PIVOT_BOUNDBOX', 'PIVOT_CURSOR',
-          'PIVOT_INDIVIDUAL', 'PIVOT_MEDIAN', 'PIVOT_ACTIVE',
-          'CENTER_ONLY', 'ROOTCURVE', 'SMOOTHCURVE', 'SPHERECURVE',
-          'INVERSESQUARECURVE', 'SHARPCURVE', 'LINCURVE', 'NOCURVE',
-          'RNDCURVE', 'PROP_OFF', 'PROP_ON', 'PROP_CON',
-          'PROP_PROJECTED', 'PARTICLE_POINT', 'PARTICLE_TIP',
-          'PARTICLE_PATH', 'SNAP_FACE_NEAREST', 'SNAP_FACE_CENTER',
-          'SNAP_PERPENDICULAR', 'SNAP_MIDPOINT', 'SNAP_OFF', 'SNAP_ON',
-          'SNAP_NORMAL', 'SNAP_GRID', 'SNAP_VERTEX', 'SNAP_EDGE',
-          'SNAP_FACE', 'SNAP_VOLUME', 'SNAP_INCREMENT',
-          'STICKY_UVS_LOC', 'STICKY_UVS_DISABLE', 'STICKY_UVS_VERT',
-          'CLIPUV_DEHLT', 'CLIPUV_HLT', 'SNAP_PEEL_OBJECT', 'GRID',
-          'OBJECT_ORIGIN', 'ORIENTATION_GLOBAL', 'ORIENTATION_GIMBAL',
-          'ORIENTATION_LOCAL', 'ORIENTATION_NORMAL', 'ORIENTATION_VIEW',
-          'COPYDOWN', 'PASTEDOWN', 'PASTEFLIPUP', 'PASTEFLIPDOWN',
-          'VIS_SEL_11', 'VIS_SEL_10', 'VIS_SEL_01', 'VIS_SEL_00',
-          'AUTOMERGE_OFF', 'AUTOMERGE_ON', 'UV_VERTEXSEL', 'UV_EDGESEL',
-          'UV_FACESEL', 'UV_ISLANDSEL', 'UV_SYNC_SELECT',
-          'GP_CAPS_FLAT', 'GP_CAPS_ROUND', 'FIXED_SIZE',
-          'TRANSFORM_ORIGINS', 'GIZMO', 'ORIENTATION_CURSOR',
-          'NORMALS_VERTEX', 'NORMALS_FACE', 'NORMALS_VERTEX_FACE',
-          'SHADING_BBOX', 'SHADING_WIRE', 'SHADING_SOLID',
-          'SHADING_RENDERED', 'SHADING_TEXTURE', 'OVERLAY', 'XRAY',
-          'LOCKVIEW_OFF', 'LOCKVIEW_ON', 'AXIS_SIDE', 'AXIS_FRONT',
-          'AXIS_TOP', 'LAYER_USED', 'LAYER_ACTIVE',
-          'OUTLINER_OB_CURVES', 'OUTLINER_DATA_CURVES', 'CURVES_DATA',
-          'OUTLINER_OB_POINTCLOUD', 'OUTLINER_DATA_POINTCLOUD',
-          'POINTCLOUD_DATA', 'OUTLINER_OB_VOLUME',
-          'OUTLINER_DATA_VOLUME', 'VOLUME_DATA', 'CURRENT_FILE', 'HOME',
-          'DOCUMENTS', 'TEMP', 'SORTALPHA', 'SORTBYEXT', 'SORTTIME',
-          'SORTSIZE', 'SHORTDISPLAY', 'LONGDISPLAY', 'IMGDISPLAY',
-          'BOOKMARKS', 'FONTPREVIEW', 'FILTER', 'NEWFOLDER',
-          'FOLDER_REDIRECT', 'FILE_PARENT', 'FILE_REFRESH',
-          'FILE_FOLDER', 'FILE_BLANK', 'FILE_BLEND', 'FILE_IMAGE',
-          'FILE_MOVIE', 'FILE_SCRIPT', 'FILE_SOUND', 'FILE_FONT',
-          'FILE_TEXT', 'SORT_DESC', 'SORT_ASC', 'LINK_BLEND',
-          'APPEND_BLEND', 'IMPORT', 'EXPORT', 'LOOP_BACK',
-          'LOOP_FORWARDS', 'BACK', 'FORWARD', 'FILE_ARCHIVE',
-          'FILE_CACHE', 'FILE_VOLUME', 'FILE_3D', 'FILE_HIDDEN',
-          'FILE_BACKUP', 'DISK_DRIVE', 'MATPLANE', 'MATSPHERE',
-          'MATCUBE', 'MONKEY', 'CURVES', 'ALIASED', 'ANTIALIASED',
-          'MAT_SPHERE_SKY', 'MATSHADERBALL', 'MATCLOTH', 'MATFLUID',
-          'WORDWRAP_OFF', 'WORDWRAP_ON', 'SYNTAX_OFF', 'SYNTAX_ON',
-          'LINENUMBERS_OFF', 'LINENUMBERS_ON', 'SCRIPTPLUGINS', 'DISC',
-          'DESKTOP', 'EXTERNAL_DRIVE', 'NETWORK_DRIVE', 'SEQ_SEQUENCER',
-          'SEQ_PREVIEW', 'SEQ_LUMA_WAVEFORM', 'SEQ_CHROMA_SCOPE',
-          'SEQ_HISTOGRAM', 'SEQ_SPLITVIEW', 'SEQ_STRIP_META',
-          'SEQ_STRIP_DUPLICATE', 'IMAGE_RGB', 'IMAGE_RGB_ALPHA',
-          'IMAGE_ALPHA', 'IMAGE_ZDEPTH', 'HANDLE_AUTOCLAMPED',
-          'HANDLE_AUTO', 'HANDLE_ALIGNED', 'HANDLE_VECTOR',
-          'HANDLE_FREE', 'VIEW_PERSPECTIVE', 'VIEW_ORTHO',
-          'VIEW_CAMERA', 'VIEW_PAN', 'VIEW_ZOOM', 'BRUSH_BLOB',
-          'BRUSH_BLUR', 'BRUSH_CLAY', 'BRUSH_CLAY_STRIPS',
-          'BRUSH_CLONE', 'BRUSH_CREASE', 'BRUSH_FILL', 'BRUSH_FLATTEN',
-          'BRUSH_GRAB', 'BRUSH_INFLATE', 'BRUSH_LAYER', 'BRUSH_MASK',
-          'BRUSH_MIX', 'BRUSH_NUDGE', 'BRUSH_PAINT_SELECT',
-          'BRUSH_PINCH', 'BRUSH_SCRAPE', 'BRUSH_SCULPT_DRAW',
-          'BRUSH_SMEAR', 'BRUSH_SMOOTH', 'BRUSH_SNAKE_HOOK',
-          'BRUSH_SOFTEN', 'BRUSH_TEXDRAW', 'BRUSH_TEXFILL',
-          'BRUSH_TEXMASK', 'BRUSH_THUMB', 'BRUSH_ROTATE',
-          'GPBRUSH_SMOOTH', 'GPBRUSH_THICKNESS', 'GPBRUSH_STRENGTH',
-          'GPBRUSH_GRAB', 'GPBRUSH_PUSH', 'GPBRUSH_TWIST',
-          'GPBRUSH_PINCH', 'GPBRUSH_RANDOMIZE', 'GPBRUSH_CLONE',
-          'GPBRUSH_WEIGHT', 'GPBRUSH_PENCIL', 'GPBRUSH_PEN',
-          'GPBRUSH_INK', 'GPBRUSH_INKNOISE', 'GPBRUSH_BLOCK',
-          'GPBRUSH_MARKER', 'GPBRUSH_FILL', 'GPBRUSH_AIRBRUSH',
-          'GPBRUSH_CHISEL', 'GPBRUSH_ERASE_SOFT', 'GPBRUSH_ERASE_HARD',
-          'GPBRUSH_ERASE_STROKE', 'BRUSH_CURVES_ADD',
-          'BRUSH_CURVES_COMB', 'BRUSH_CURVES_CUT',
-          'BRUSH_CURVES_DELETE', 'BRUSH_CURVES_DENSITY',
-          'BRUSH_CURVES_GROW_SHRINK', 'BRUSH_CURVES_PINCH',
-          'BRUSH_CURVES_PUFF', 'BRUSH_CURVES_SLIDE',
-          'BRUSH_CURVES_SMOOTH', 'BRUSH_CURVES_SNAKE_HOOK',
-          'KEYTYPE_KEYFRAME_VEC', 'KEYTYPE_BREAKDOWN_VEC',
-          'KEYTYPE_EXTREME_VEC', 'KEYTYPE_JITTER_VEC',
-          'KEYTYPE_MOVING_HOLD_VEC', 'HANDLETYPE_FREE_VEC',
-          'HANDLETYPE_ALIGNED_VEC', 'HANDLETYPE_VECTOR_VEC',
-          'HANDLETYPE_AUTO_VEC', 'HANDLETYPE_AUTO_CLAMP_VEC',
-          'COLORSET_01_VEC', 'COLORSET_02_VEC', 'COLORSET_03_VEC',
-          'COLORSET_04_VEC', 'COLORSET_05_VEC', 'COLORSET_06_VEC',
-          'COLORSET_07_VEC', 'COLORSET_08_VEC', 'COLORSET_09_VEC',
-          'COLORSET_10_VEC', 'COLORSET_11_VEC', 'COLORSET_12_VEC',
-          'COLORSET_13_VEC', 'COLORSET_14_VEC', 'COLORSET_15_VEC',
-          'COLORSET_16_VEC', 'COLORSET_17_VEC', 'COLORSET_18_VEC',
-          'COLORSET_19_VEC', 'COLORSET_20_VEC', 'COLLECTION_COLOR_01',
-          'COLLECTION_COLOR_02', 'COLLECTION_COLOR_03',
-          'COLLECTION_COLOR_04', 'COLLECTION_COLOR_05',
-          'COLLECTION_COLOR_06', 'COLLECTION_COLOR_07',
-          'COLLECTION_COLOR_08', 'SEQUENCE_COLOR_01',
-          'SEQUENCE_COLOR_02', 'SEQUENCE_COLOR_03',
-          'SEQUENCE_COLOR_04', 'SEQUENCE_COLOR_05',
-          'SEQUENCE_COLOR_06', 'SEQUENCE_COLOR_07',
-          'SEQUENCE_COLOR_08', 'SEQUENCE_COLOR_09',
-          'LIBRARY_DATA_INDIRECT', 'LIBRARY_DATA_OVERRIDE_NONEDITABLE',
-          'EVENT_A', 'EVENT_B', 'EVENT_C', 'EVENT_D', 'EVENT_E',
-          'EVENT_F', 'EVENT_G', 'EVENT_H', 'EVENT_I', 'EVENT_J',
-          'EVENT_K', 'EVENT_L', 'EVENT_M', 'EVENT_N', 'EVENT_O',
-          'EVENT_P', 'EVENT_Q', 'EVENT_R', 'EVENT_S', 'EVENT_T',
-          'EVENT_U', 'EVENT_V', 'EVENT_W', 'EVENT_X', 'EVENT_Y',
-          'EVENT_Z', 'EVENT_SHIFT', 'EVENT_CTRL', 'EVENT_ALT',
-          'EVENT_OS', 'EVENT_F1', 'EVENT_F2', 'EVENT_F3', 'EVENT_F4',
-          'EVENT_F5', 'EVENT_F6', 'EVENT_F7', 'EVENT_F8', 'EVENT_F9',
-          'EVENT_F10', 'EVENT_F11', 'EVENT_F12', 'EVENT_ESC',
-          'EVENT_TAB', 'EVENT_PAGEUP', 'EVENT_PAGEDOWN', 'EVENT_RETURN',
-          'EVENT_SPACEKEY')
 
 
 def default_traverse(self, socket):

+ 0 - 5
drivers.py

@@ -12,12 +12,9 @@ from .utilities import (prRed, prGreen, prPurple, prWhite,
 
 # SO: the idea is that the driver's input is a Python dictionary
 #     with all of the requisite information to build the driver
-
 # I need a generic function to create the driver
 
 # EXAMPLE INPUT:
-
-
 # example   =     {"owner":None,
                     # "prop":None,
                     # "ind":-1,
@@ -46,8 +43,6 @@ from .utilities import (prRed, prGreen, prPurple, prWhite,
                             # "type":"KEYFRAME",},],
                     # }
 
-#Explain this ^ ?
-#
 
 class MantisDriver(dict):
     pass

+ 0 - 111
f_nodegraph.py

@@ -1,111 +0,0 @@
-# TODO FIXME UNBREAK
-# why in the hell does this file even exist
-
-
-
-#Node Graph Functions
-
-        
-        
-def SeekNodePathUntil(node, input_name, nodeType, direction = 'BACK'):
-    from .node_container_common import trace_single_line, trace_single_line_up
-    if direction == 'BACK':
-        return trace_single_line(node, input_name)
-    else: # 'FORWARD'
-        return trace_single_line_up(node, input_name)
-    
-# may not work anymore but that should be OK
-def get_node_container(node, context):
-    # from .utilities import parse_node_tree, print_lines
-    base_tree = context.space_data.path[0].node_tree
-    from .readtree import get_tree_data
-    nodes = base_tree.parsed_tree
-    node_container = None
-    if (node.id_data != context.space_data.path[-1].node_tree):
-        return None, None
-    if (node.id_data == base_tree):
-        try:
-            #other_node = node.inputs['Parent'].links[0].from_node
-            node_container = nodes.get( ('NONE', node.name) )
-        except IndexError: # node just isn't connected'
-            nodes = None
-        return node_container, nodes
-    else: # find it in Node-Groups
-          # I am checking the active node, which should always
-          #  be the path of Group Nodes.
-          # if not, then the user is doing something sp0oky
-        for node_container in nodes.values():
-            if len(node_container.signature) != len(context.space_data.path)+1:
-                continue
-            tree = base_tree; found = False
-            for name in node_container.signature[0:]:
-                g_node = tree.nodes.get(name)
-                if not (g_node == tree.nodes.active): continue 
-                if (hasattr(g_node, 'node_tree')):
-                    tree = g_node.node_tree
-                elif name == node.name: found = True; break
-            else:
-                found = False
-                continue
-            if found == True:
-                return node_container, nodes
-        else:
-            return None, None
-    return None, None
-                
-def GetUpstreamXFormNodes(node_container, context):
-    if (node_container):
-        input_name=None
-        if node_container.node_type == 'LINK':
-            input_name = 'Input Relationship'
-            if node_container.__class__.__name__ == 'LinkInherit':
-                input_name = 'Parent'
-        elif node_container.node_type == 'XFORM':
-            input_name = 'Relationship'
-        xF = SeekNodePathUntil(node_container, input_name, ['xFormArmature', 'xFormBone', 'xFormRoot'])
-        return xF
-        
-    else:
-        return None
-        
-def GetDownstreamXFormNodes(node_container, context):
-    if (node_container):
-        output_name=None
-        if node_container.node_type == 'LINK':
-            output_name = 'Output Relationship'
-            if node_container.__class__.__name__ == 'LinkInherit':
-                output_name = 'Inheritance'
-        elif node_container.node_type == 'XFORM':
-            output_name = 'xForm Out'
-        xF = SeekNodePathUntil(node_container, output_name, ['xFormArmature', 'xFormBone', 'xFormRoot'], direction = 'FORWARD')
-        return xF
-    else:
-        return None
-    
-        
-# def get_parent(node_container):
-    # node_line, socket = trace_single_line(node_container, "Relationship")
-    # parent_nc = None
-    # for i in range(len(node_line)):
-        # print (node_line[i])
-        # # check each of the possible parent types.
-        # if ( (node_line[ i ].__class__.__name__ == 'LinkInherit') ):
-            # try: # it's the next one
-                # return node_line[ i + 1 ]
-            # except IndexError: # if there is no next one...
-                # return None # then there's no parent!
-    # return None
-    # # TO DO!
-    # #
-    # # 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!
-    
-    # #my_sig = get_node_signature(node, tree)
-    
-    
-    
-def FindIKNode():
-    pass
- 

+ 5 - 51
i_o.py

@@ -40,7 +40,7 @@ def remove_special_characters(name):
 
 
 def fix_custom_parameter(n, property_definition, ):
-    if n.bl_idname in ['xFormNullNode', 'xFormBoneNode', 'xFormRootNode', 'xFormArmatureNode', 'xFormGeometryObjectNode',]:
+    if n.bl_idname in ['xFormNullNode', 'xFormBoneNode', 'xFormArmatureNode', 'xFormGeometryObjectNode',]:
         prop_name = property_definition["name"]
         prop_type = property_definition["bl_idname"]
         
@@ -201,20 +201,6 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
                 else:
                     v = None
                 v_type = type(v)
-
-                # this identifies a weird bug...
-                # try:
-                #     problem = v != s.default_value
-                #     if problem:
-                #         prRed(s.node.name, s.name, s.bl_idname, s.default_value)
-                #         print(s.default_value, v)
-                # except AttributeError:
-                #     pass
-                # so it seems doing the .get thing bypassed some kind of getter that made the value "nice"
-                # so e.g. enums were using the Int identifier and bool vectors used int bitmasks
-
-                # if s.bl_idname == 'TransformSpaceSocket':
-                #     prWhite(v)
                 if v is None:
                     return socket # we don't need to store this.
                 if not is_jsonable(v):
@@ -235,8 +221,6 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
                         socket["soft_max"] = v
                     if v := getattr(s,'description', None):
                         socket["description"] = v
-                # if s.bl_idname == 'TransformSpaceSocket':
-                #     prRed(socket['default_value'])
                 return socket
                 #
 
@@ -248,10 +232,6 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
                 socket = socket_data(s)
                 socket["index"]=i
                 sockets[s.identifier] = socket
-            # for i, s in enumerate(n.inputs):
-            #     sockets[s.identifier]["index"] = i
-            # for i, s in enumerate(n.outputs):
-            #     sockets[s.identifier]["index"] = i
             
             node_props["sockets"] = sockets
             nodes[n.name] = node_props
@@ -261,30 +241,12 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
 
         in_sockets = {}
         out_sockets = {}
-        # new_tree_items = {}
 
         in_node = {"name":"MANTIS_AUTOGEN_GROUP_INPUT", "bl_idname":"NodeGroupInput", "sockets":in_sockets}
         out_node = {"name":"MANTIS_AUTOGEN_GROUP_OUTPUT", "bl_idname":"NodeGroupOutput", "sockets":out_sockets}
 
 
         add_input_node, add_output_node = False, False
-        
-#
-# dict_keys(['Driver Variable', 'second', 'main', 'drivers and such', 'Copy Location', 'Float', 'Reroute', 'Reroute.001', 'Reroute.002'])
-# bl_idname, name, label, location, 
-
-# Variable Type {'name': 'Variable Type', 'bl_idname': 'EnumDriverVariableType', 'is_output': False, 'is_multi_input': False, 'default_value': 'SINGLE_PROP', 'index': 0}
-# Property {'name': 'Property', 'bl_idname': 'ParameterStringSocket', 'is_output': False, 'is_multi_input': False, 'default_value': 'slide', 'index': 1}
-# Property Index {'name': 'Property Index', 'bl_idname': 'IntSocket', 'is_output': False, 'is_multi_input': False, 'default_value': 0, 'index': 2}
-# Evaluation Space {'name': 'Evaluation Space', 'bl_idname': 'EnumDriverVariableEvaluationSpace', 'is_output': False, 'is_multi_input': False, 'default_value': 'WORLD_SPACE', 'index': 3}
-# Rotation Mode {'name': 'Rotation Mode', 'bl_idname': 'EnumDriverRotationMode', 'is_output': False, 'is_multi_input': False, 'default_value': 'AUTO', 'index': 4}
-# xForm 1 {'name': 'xForm 1', 'bl_idname': 'xFormSocket', 'is_output': False, 'is_multi_input': False, 'index': 5}
-# xForm 2 {'name': 'xForm 2', 'bl_idname': 'xFormSocket', 'is_output': False, 'is_multi_input': False, 'index': 6}
-# Driver Variable {'name': 'Driver Variable', 'bl_idname': 'DriverVariableSocket', 'is_output': True, 'is_multi_input': False, 'index': 0}
-
-
-# Socket_254
-# index 16
 
         unique_sockets_from={}
         unique_sockets_to={}
@@ -295,7 +257,6 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
             c, d = l.to_node.name, l.to_socket.identifier
 
             # get the indices of the sockets to be absolutely sure
-
             for e, outp in enumerate(l.from_node.outputs):
                 # for some reason, 'is' does not return True no matter what...
                 # so we are gonn compare the memory address directly, this is stupid
@@ -316,8 +277,6 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
                 if (only_selected and l.from_node.select) and (not l.to_node.select):
                     # handle an output in the tree
                     add_output_node=True
-
-
                     if not (sock_name := unique_sockets_to.get(l.to_socket.node.name+l.to_socket.identifier)):
                         sock_name = l.to_socket.name; name_stub = sock_name
                         used_names = list(tree_in_out.keys()); i=0
@@ -352,7 +311,6 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
 
                 elif (only_selected and (not l.from_node.select)) and l.to_node.select:
                     add_input_node=True
-
                     # we need to get a unique name for this
                     # use the Tree IN/Out because we are dealing with Group in/out
                     if not (sock_name := unique_sockets_from.get(l.from_socket.node.name+l.from_socket.identifier)):
@@ -418,8 +376,6 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
             out_node["location"] = Vector((all_nodes_bounding_box[1].x+400, all_nodes_bounding_box[0].lerp(all_nodes_bounding_box[1], 0.5).y))
             nodes["MANTIS_AUTOGEN_GROUP_OUTPUT"]=out_node
 
-        # f_curves = {}
-
         export_data[tree.name] = (tree_info, tree_in_out, nodes, links,) # f_curves)
     import json
 
@@ -429,7 +385,6 @@ def export_to_json(trees, path="", write_file=True, only_selected=False):
     with open(path, "w") as file:
         print(wrapWhite("Writing mantis tree data to: "), wrapGreen(file.name))
         file.write( json.dumps(export_data, indent = 4) )
-    
     # I'm gonna do this in a totally naive way, because this should already be sorted properly
     #   for the sake of dependency satisfaction. So the current "tree" should be the "main" tree
     tree.filepath = path
@@ -449,7 +404,6 @@ def do_import_from_file(filepath, context):
         data = json.load(f)
         do_import(data,context)
 
-        # repeat this because we left the with, this is bad and ugly but I don't care
         for tree in all_trees:
             tree.do_live_update = True
 
@@ -459,6 +413,10 @@ def do_import_from_file(filepath, context):
         except AttributeError: # not hovering over the Node Editor
             pass
         return {'FINISHED'}
+    # otherwise:
+    # repeat this because we left the with, this is bad and ugly but I don't care
+    for tree in all_trees:
+        tree.do_live_update = True
     return {'CANCELLED'}
 
 def do_import(data, context):
@@ -913,10 +871,6 @@ class MantisReloadNodeTree(Operator):
         self.report({'INFO'}, "reloading tree")
         return do_import_from_file(base_tree.filepath, context)
 
-
-
-
-
 # todo:
 #  - export metarig and option to import it
 #  - same with controls

+ 0 - 7
link_containers.py

@@ -2,8 +2,6 @@ from .node_container_common import *
 from bpy.types import Node, Bone
 from .base_definitions import MantisNode, GraphError
 
-#TODO: get rid of this, it's unnecesary here, we always want to import
-#  all classes in this file
 def TellClasses():
     return [
              # special
@@ -47,11 +45,6 @@ def default_evaluate_input(nc, input_name):
     else:
         return evaluate_input(nc, input_name)
 
-# def set_constraint_name(nc):
-#     if name := nc.evaluate_input("Name"):
-#         return name
-#     return nc.__class__.__name__
-
 # set the name if it is available, otherwise just use the constraint's nice name
 set_constraint_name = lambda nc : nc.evaluate_input("Name") if nc.evaluate_input("Name") else nc.__class__.__name__
 

+ 2 - 31
link_definitions.py

@@ -38,7 +38,8 @@ def default_traverse(self, socket):
         return None
 
 
-from mathutils import Color
+from mathutils import Color # these colors were sampled from Blender's UI
+# TODO: maybe read the relevant colors from the Theme
 linkColor = Color((0.028034, 0.093164, 0.070379)).from_scene_linear_to_srgb()
 inheritColor = Color((0.083213, 0.131242, 0.116497)).from_scene_linear_to_srgb()
 trackingColor = Color((0.033114, 0.049013, 0.131248)).from_scene_linear_to_srgb()
@@ -644,36 +645,6 @@ class LinkArmatureNode(Node, LinkNode):
         else:
             layout.label(text="")
 
-# why did I define this twice?
-# class LinkSplineIK(Node, LinkNode):
-#     '''A node representing Spline IK'''
-#     bl_idname = 'LinkSplineIK'
-#     bl_label = "LinkSplineIK"
-#     bl_icon = 'CON_SPLINEIK'
-#     useTarget : bpy.props.BoolProperty(default=True)
-#     def init(self, context):
-#         self.inputs.new('xFormSocket', "Parent")
-#         self.inputs.new ('xFormSocket', "Target")
-#         self.inputs.new ('xFormSocket', "Pole Target")
-#         self.inputs.new ('BooleanSocket', "Use Tail")
-#         self.inputs.new ('BooleanSocket', "Stretch")
-#         self.inputs.new ('FloatFactorSocket', "Position")
-#         self.inputs.new ('FloatFactorSocket', "Rotation")
-#         self.inputs.new ('FloatFactorSocket', "Influence")
-#         self.inputs.new ('EnableSocket', "Enable")
-#         self.outputs.new('RelationshipSocket', "Inheritance")
-#         # color
-#         self.use_custom_color = True
-#         self.color = ikColor
-
-#     def traverse(self, socket):
-#         if (socket == self.outputs["Inheritance"]):
-#             return self.inputs["Parent"]
-#         if (socket == self.inputs["Parent"]):
-#             return self.outputs["Inheritance"]
-#         return None
-
-
 class LinkSplineIKNode(Node, LinkNode):
     """"A node representing Spline IK"""
     bl_idname = "LinkSplineIK"

+ 0 - 12
math_containers.py

@@ -129,18 +129,6 @@ class MathStaticFloat:
         self.prepared = True
         self.executed = True
 
-
-# enumVectorOperations = (('ADD', 'Add', 'Add (Component-wise)'),
-#                         ('SUBTRACT', "Subtract", "Subtract (Component-wise)"),
-#                         ('MULTIPLY', "Multiply", "Multiply (Component-wise)"),
-#                         ('SCALE', "Scale", "Scales vector by input float or average magnitude of input vector's components."),
-#                         ('DIVIDE', "Divide", "Divide (Component-wise)"),
-#                         ('POWER', "Power", "Power (Component-wise)"),
-#                         ('LENGTH', "Length", "Length"),
-#                         ('CROSS', "Cross Product", "Cross product of A X B"),
-#                         ('DOT', "Dot Product", "Dot product of A . B"),
-
-
 class MathStaticVector:
     '''A node representing an armature object'''
 

+ 1 - 14
math_definitions.py

@@ -44,11 +44,6 @@ class MathStaticInt(Node, MantisNode):
             else:
                 self.inputs["Int B"].hide = False
 
-    def traverse(self, socket):
-        return default_traverse(self,socket)
-                   
-
-
 # do... make the operations now
 class MathStaticFloatNode(Node, MantisNode):
     """A node that performs mathematical operations on float numbers as a preprocess step before generating the rig."""
@@ -76,10 +71,6 @@ class MathStaticFloatNode(Node, MantisNode):
             else:
                 self.inputs["Float B"].hide = False
 
-    def traverse(self, socket):
-        return default_traverse(self,socket)
-
-
 class MathStaticVectorNode(Node, MantisNode):
     """Performs a vector math operation as a preprocess before executing the tree."""
     bl_idname = "MathStaticVector"
@@ -121,8 +112,4 @@ class MathStaticVectorNode(Node, MantisNode):
                 self.inputs["Scalar A"].hide = False
             else:
                 self.inputs["Vector B"].hide = False
-                self.inputs["Scalar A"].hide = True
-
-    
-    def traverse(self, socket):
-        return default_traverse(self,socket)
+                self.inputs["Scalar A"].hide = True

+ 9 - 66
misc_containers.py

@@ -2,7 +2,7 @@ from .node_container_common import *
 
 # The fact that I need this means that some of these classes should
 #  probably be moved to link_containers.py
-from .xForm_containers import xFormRoot, xFormArmature, xFormBone
+from .xForm_containers import xFormArmature, xFormBone
 
 from math import pi, tau
 
@@ -17,11 +17,7 @@ def TellClasses():
              InputRotationOrder,
              InputTransformSpace,
              InputString,
-             InputQuaternion,
-             InputQuaternionAA,
              InputMatrix,
-            #  InputLayerMask,
-             # InputGeometry,
              InputExistingGeometryObject,
              InputExistingGeometryData,
              UtilityPointFromCurve,
@@ -50,10 +46,10 @@ def TellClasses():
              UtilityIntToString,
              UtilityArrayGet,
              UtilitySetBoneMatrixTail,
-             #
+             # Control flow switches
              UtilityCompare,
              UtilityChoose,
-             #
+             # useful NoOp:
              UtilityPrint,
             ]
 
@@ -66,17 +62,9 @@ def matrix_from_head_tail(head, tail):
     return m
 
 #*#-------------------------------#++#-------------------------------#*#
-# G E N E R I C   N O D E S
+# U T I L I T Y   N O D E S
 #*#-------------------------------#++#-------------------------------#*#
 
-
-# in reality, none of these inputs have names
-#  so I am using the socket name for now
-#  I suppose I could use any name :3
-
-# TODO: the inputs that do not have names should have an empty string
-#   TODO after that: make this work with identifiers instead, stupid.
-
 class InputFloat:
     '''A node representing float input'''
     
@@ -159,7 +147,7 @@ class InputBoolean:
         return self.parameters[""]
 
 class InputBooleanThreeTuple:
-    '''A node representing inheritance'''
+    '''A node representing a tuple of three booleans'''
         
     def __init__(self, signature, base_tree):
         self.base_tree=base_tree
@@ -239,47 +227,6 @@ class InputString:
     def evaluate_input(self, input_name):
         return self.parameters[""]
     
-class InputQuaternion:
-    '''A node representing quaternion input'''
-    def __init__(self, signature, base_tree):
-        self.base_tree=base_tree
-        self.signature = signature
-        self.inputs = {}
-        self.outputs = {"" : NodeSocket(name = 'QuaternionSocket', node=self) }
-        self.parameters = {'':None, "Mute":None}
-        self.node_type = 'UTILITY'
-        self.hierarchy_connections = []
-        self.connections = []
-        self.hierarchy_dependencies = []
-        self.dependencies = []
-        self.prepared = True
-        self.executed = True
-        
-        
-    def evaluate_input(self, input_name):
-        return self.parameters[""]
-    
-
-class InputQuaternionAA:
-    '''A node representing axis-angle quaternion input'''
-        
-    def __init__(self, signature, base_tree):
-        self.base_tree=base_tree
-        self.signature = signature
-        self.inputs = {}
-        self.outputs  = {"" : NodeSocket(name = 'QuaternionSocketAA', node=self) }
-        self.parameters = {'':None, "Mute":None}
-        self.node_type = 'UTILITY'
-        self.hierarchy_connections = []
-        self.connections = []
-        self.hierarchy_dependencies = []
-        self.dependencies = []
-        self.prepared = True
-        self.executed = True
-        
-    def evaluate_input(self, input_name):
-        return self.parameters[""]
-    
 class InputMatrix:
     '''A node representing axis-angle quaternion input'''
         
@@ -686,7 +633,7 @@ class UtilityDriverVariable:
     def GetxForm(self, index=1):
         trace = trace_single_line(self, "xForm 1" if index == 1 else "xForm 2")
         for node in trace[0]:
-            if (node.__class__ in [xFormRoot, xFormArmature, xFormBone]):
+            if (node.__class__ in [xFormArmature, xFormBone]):
                 return node #this will fetch the first one, that's good!
         return None
 
@@ -952,7 +899,7 @@ class UtilitySwitch:
     def GetxForm(self,):
         trace = trace_single_line(self, "Parameter" )
         for node in trace[0]:
-            if (node.__class__ in [xFormRoot, xFormArmature, xFormBone]):
+            if (node.__class__ in [xFormArmature, xFormBone]):
                 return node #this will fetch the first one, that's good!
         return None
 
@@ -1563,12 +1510,6 @@ class UtilityTransformationMatrix:
         self.prepared = False
         self.executed = False
 
-# enumMatrixTransform  = (('TRANSLATE', 'Translate', 'Translate'),
-#                         ('ROTATE_AXIS_ANGLE', "Rotate (Vector-angle)", "Rotates a number of radians around an axis"),
-#                         ('ROTATE_EULER', "Rotate (Euler)", "Euler Rotation"),
-#                         ('ROTATE_QUATERNION', "Rotate (Quaternion)", "Quaternion Rotation"),
-#                         ('SCALE', "Scale", "Scale"),)
-
     def bPrepare(self, bContext = None,):
         from mathutils import Matrix, Vector
         if (operation := self.evaluate_input("Operation")) == 'ROTATE_AXIS_ANGLE':
@@ -1579,6 +1520,8 @@ class UtilityTransformationMatrix:
             if axis := self.evaluate_input("Vector"):
                 m[0][3]=axis[0];m[1][3]=axis[1];m[2][3]=axis[2]
             self.parameters['Matrix'] = m
+        elif (operation := self.evaluate_input("Operation")) == 'SCALE':
+            self.parameters["Matrix"] = Matrix.Scale(self.evaluate_input("W"), 4, Vector(self.evaluate_input("Vector")).normalized())
         else:
             raise NotImplementedError(self.evaluate_input("Operation").__repr__()+ "  Operation not yet implemented.")
         self.prepared = True

+ 1 - 65
node_container_common.py

@@ -2,7 +2,7 @@ from .utilities import (prRed, prGreen, prPurple, prWhite,
                               prOrange,
                               wrapRed, wrapGreen, wrapPurple, wrapWhite,
                               wrapOrange,)
-from .base_definitions import GraphError, CircularDependencyError
+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
@@ -164,38 +164,6 @@ def trace_all_lines_up(nc, output_name):
     check_me = type('', (object,), copy_items)
     return get_depth_lines(check_me)[1]
 
-# KEEP THIS
-# don't modify this.... until the other one actually works.
-# def original_get_depth_lines(root):
-#     path, seek, nc_path = [0,], root, [root,]
-#     lines, nc_paths = {}, {}
-#     nc_len = len(root.hierarchy_connections)-1
-#     curheight=0
-#     while (path[0] <= nc_len):
-#         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 not hasattr(nc_path[-1], "hierarchy_connections"):
-#             # TODO even though I am going to completely rewrite this function, I need to fix this
-#             prRed(f"{nc_path[-1]} has some sort of stupid hierarchy problem. Ignoring...")
-#             nc_path[-1].hierarchy_connections = []; nc_path[-1].connected_to = 0
-#         if nc_path[-1].hierarchy_connections:
-#             path.append(0); curheight+=1
-#         else:
-#             path[curheight] = path[curheight] + 1
-#             nc_path.pop()
-#             connected_nodes = nc_path[-1].hierarchy_connections
-#             if ( path[-1] <= len(connected_nodes)-1 ):
-#                 seek = connected_nodes[path[-1]]
-#             elif curheight > 0:
-#                 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
 
 
 
@@ -733,11 +701,6 @@ class DummyLink:
         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
@@ -751,26 +714,6 @@ class DummyLink:
 # 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.
-    # @property
-    # def dependencies(self):
-    #     c = []
-    #     for i in nc.inputs.values():
-    #         for l in i.links:
-    #             if l.is_hierarchy:
-    #             c.append(l.from_node)
-
-    # def default_prepare(self, bContext=None):
-    #     for dep in self.dependencies:
-
-    # def del_me(self):
-    #     from itertools import chain
-    #     links = []
-    #     for socket in chain(self.inputs, self.outputs):
-    #         for i in len(socket.links):
-    #             links.append(l)
-    #     for l in links:
-    #         del l
-    #     # just make sure to clean up links.
 
     def flush_links(self):
         for inp in self.inputs.values():
@@ -779,20 +722,13 @@ def setup_container(some_class):
             out.flush_links()
 
     functions = {
-        # "dependencies": dependencies,
-        # "num_dependencies": property(lambda self: len(self.dependencies)),
-        # "connections": some-property, # this one is potentially better as a property tho
-        # "hierarchy_connections": some-property, # I tried making this a getter property but it was a bit slower.
         # 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,
-        # then I am not sure I want these because it is really easy to check
-        #   and it may be helpful to know if a node doesn't have one of these properties.
         "bPrepare" : lambda self, bContext=None : None,
         "bExecute" : lambda self, bContext=None : None,
         "bFinalize" : lambda self, bContext=None : None,
-        # "__del__" : del_me,
     }
     for n, f in functions.items():
         if not hasattr(some_class, n):

+ 23 - 89
nodes_generic.py

@@ -213,52 +213,6 @@ class InputMatrixNode(Node, MantisNode):
         mat_sock = self.outputs[0]
         mat_sock.default_value = self.set_matrix()
 
-# TODO: reimplement the nodes beneath here
-
-# from .utilities import QuerySocket, to_mathutils_value
-# class ComposeMatrixNode(Node, MantisNode):
-#     '''A utility node for composing a matrix'''
-#     bl_idname = 'ComposeMatrixNode'
-#     bl_label = "Compose Matrix"
-#     bl_icon = 'NODE'
-
-#     def init(self, context):
-#         self.inputs.new('VectorTranslationSocket', "Translation")
-#         self.inputs.new('GenericRotationSocket', "Rotation")
-#         self.inputs.new('VectorScaleSocket', "Scale")
-#         self.outputs.new('MatrixSocket', "Matrix")
-
-#     def update_node(self, context = None):
-#         from mathutils import Matrix, Euler, Quaternion, Vector
-#         mat_sock = self.outputs[0]
-#         rotation = Matrix.Identity(4)
-#         scale = Matrix.Identity(4)
-#         translation = Matrix.Identity(4)
-
-#         sock = QuerySocket(self.inputs["Rotation"])[0]
-#         val = to_mathutils_value(sock)
-#         if (val):
-#             if (isinstance(val, Vector)):
-#                 val = Euler((val[0], val[1], val[2]), 'XYZ')
-#             rotation = val.to_matrix().to_4x4()
-#         sock = QuerySocket(self.inputs["Scale"])[0]
-#         val = to_mathutils_value(sock)
-#         if (val):
-#             if (isinstance(val, Vector)):
-#                 scale = Matrix.Scale(val[0],4,(1.0,0.0,0.0)) @ Matrix.Scale(val[1],4,(0.0,1.0,0.0)) @ Matrix.Scale(val[2],4,(0.0,0.0,1.0))
-#         sock = QuerySocket(self.inputs["Translation"])[0]
-#         val = to_mathutils_value(sock)
-#         if (val):
-#             if (isinstance(val, Vector)):
-#                 translation = Matrix.Translation((val))
-        
-#         mat = translation @ rotation @ scale
-#         mat_sock.default_value = ( mat[0][0], mat[0][1], mat[0][2], mat[0][3],
-#                                    mat[1][0], mat[1][1], mat[1][2], mat[1][3],
-#                                    mat[2][0], mat[2][1], mat[2][2], mat[2][3],
-#                                    mat[3][0], mat[3][1], mat[3][2], mat[3][3], )
-
-
 class ScaleBoneLengthNode(Node, MantisNode):
     '''Scale Bone Length'''
     bl_idname = 'ScaleBoneLength'
@@ -487,6 +441,9 @@ class UtilityDriverVariableNode(Node, MantisNode):
         self.inputs[3].hide = True
     
 
+# TODO: make a way to edit the fCurve directly.
+# I had a working version of this in the past, but it required doing sinful things like
+# keeping track of the RAM address of the window.
 class UtilityFCurveNode(Node, MantisNode):
     """Creates an fCurve for use with a driver."""
     bl_idname = "UtilityFCurve"
@@ -494,55 +451,17 @@ class UtilityFCurveNode(Node, MantisNode):
     bl_icon = "NODE"
     
     use_kf_nodes   : bpy.props.BoolProperty(default=True)
-    # fake_fcurve_ob : bpy.props.PointerProperty(type=bpy.types.Object)
     initialized : bpy.props.BoolProperty(default = False)
     
     def init(self, context):
         self.outputs.new("FCurveSocket", "fCurve")
-        # if not self.fake_fcurve_ob:
-        #     ob = bpy.data.objects.new("fake_ob_"+self.name, None)
-        #     self.fake_fcurve_ob = ob
-        #     ob.animation_data_create()
-        #     ob.animation_data.action = bpy.data.actions.new('fake_action_'+self.name)
-        #     fc = ob.animation_data.action.fcurves.new('location', index=0, action_group='location')
-        #     fc.keyframe_points.add(2)
-        #     kf0 = fc.keyframe_points[0]; kf0.co_ui = (0, 0)
-        #     kf1 = fc.keyframe_points[1]; kf1.co_ui = (1, 1)
-        #     #
-        #     kf0.interpolation = 'BEZIER'
-        #     kf0.handle_left_type  = 'AUTO_CLAMPED'
-        #     kf0.handle_right_type = 'AUTO_CLAMPED'
-        #     kf1.interpolation = 'BEZIER'
-        #     kf1.handle_left_type  = 'AUTO_CLAMPED'
-        #     kf1.handle_right_type = 'AUTO_CLAMPED'
-            #
         self.initialized = True
             
     def draw_buttons(self, context, layout):
-        # return
-        # if self.use_kf_nodes:
-            # layout.prop(self, "use_kf_nodes",  text="[ Use fCurve data ]", toggle=True, invert_checkbox=True)
         layout.operator( 'mantis.fcurve_node_add_kf' )
         if (len(self.inputs) > 0):
             layout.operator( 'mantis.fcurve_node_remove_kf' )
-        # else:
-            # layout.prop(self, "use_kf_nodes",  text="[ Use Keyframe Nodes ]", toggle=True)
-            # layout.operator('mantis.edit_fcurve_node')
-        
-            
-    
-    # THE DIFFICULT part is getting it to show up in the graph editor
-    # TRY:
-    #       a modal operator that opens the Graph Editor
-    #       and then finishes when it is closed
-    #       it would reveal the object holding the fCurve before
-    #       showing the Graph Editor
-    #       And hide it after closing it.
-    #
-        
-        
-        
-        
+
 
 class UtilityDriverNode(Node, MantisNode):
     """Represents a Driver relationship"""
@@ -726,6 +645,8 @@ class UtilitySetBoneLength(Node, MantisNode):
         self.outputs.new("MatrixSocket", "Bone Matrix")
         self.initialized = True
 
+# TODO: more keyframe types should be supported in the future.
+# Some of the code that can do this is commented out here until I can implement it properly.
 class UtilityKeyframe(Node, MantisNode):
     """A keyframe for a FCurve"""
     bl_idname = "UtilityKeyframe"
@@ -751,8 +672,7 @@ class UtilityKeyframe(Node, MantisNode):
         # there will eventually be inputs for e.g. key type, key handles, etc.
         # right now I am gonna hardcode LINEAR keyframes so I don't have to deal with anything else
         # TODO TODO TODO
-    
-    
+
     # def display_update(self, parsed_tree, context):
     #     if context.space_data:
     #         nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
@@ -838,11 +758,25 @@ class UtilityTransformationMatrix(Node, MantisNode):
         self.outputs.new("MatrixSocket", "Matrix")
         self.initialized = True
 
+
+    def display_update(self, parsed_tree, context):
+        operation = self.inputs['Operation'].default_value
+        if self.inputs['Operation'].is_linked:
+            if context.space_data:
+                nc = parsed_tree.get(get_signature_from_edited_tree(self, context))
+                operation = nc.evaluate_input("Operation")
+        if operation in ["ROTATE_AXIS_ANGLE", "SCALE"]:
+            self.inputs["Vector"].hide = False
+            self.inputs["W"].hide = False
+        if operation in ["TRANSLATE"]:
+            self.inputs["Vector"].hide = False
+            self.inputs["W"].hide = True
+            
+
 # Blender calculates bone roll this way...
 # https://projects.blender.org/blender/blender/src/commit/dd209221675ac7b62ce47b7ea42f15cbe34a6035/source/blender/editors/armature/armature_edit.cc#L281
 # but this looks like it will be harder to re-implement than to re-use. Unfortunately, it doesn't apply directly to a matrix so I have to call a method
-# in the edit bone. Thus, this node currently does nothing and the xForm node has to handle it by reading back through the tree....
-# this will lead to bugs.
+# in the edit bone.
 # So instead, we need to avoid calculating the roll for now.
 # but I want to make that its own node and add roll-recalc to this node, too.
 

+ 0 - 8
ops_generate_tree.py

@@ -738,9 +738,7 @@ def do_generate_armature(context, node_tree):
         #This will generate it in the current node tree and OVERWRITE!
         node_tree.nodes.clear()
         
-        world_in = node_tree.nodes.new("xFormRootNode")
         armature = node_tree.nodes.new("xFormArmatureNode")
-        world_in.location = (-200, 0)
         armature.location = ( 0, 0)
         
         
@@ -755,9 +753,6 @@ def do_generate_armature(context, node_tree):
                 lines.append([]) # add the root itself HACK ugly
                 milestone=time()
                 prPurple("got the bone paths", time() - milestone); milestone=time()
-                # create links:
-                node_tree.links.new(world_in.outputs["World Out"], armature.inputs['Relationship'])
-                
                 # set up some properties:
                 armature.inputs["Name"].default_value = armOb.name
                 armature.name = armOb.name; armature.label = armOb.name
@@ -943,10 +938,7 @@ def do_generate_armature(context, node_tree):
 
         
         for node in node_tree.nodes:
-            if (node == world_in):
-                continue
             node.select = False
-        node_tree.nodes.active = world_in
         
         prGreen("Finished generating %d nodes in %f seconds." % (len(node_tree.nodes), time() - start))
         #bpy.ops.node.cleanup()

+ 50 - 383
ops_nodegroup.py

@@ -17,13 +17,10 @@ def TellClasses():
         ForceDisplayUpdate,
         CleanUpNodeGraph,
         MantisMuteNode,
-        MantisVisualizeOutput,
-        TestOperator,
         # xForm
         AddCustomProperty,
         EditCustomProperty,
         RemoveCustomProperty,
-        # Fcurve
         # EditFCurveNode,
         FcurveAddKeyframeInput,
         FcurveRemoveKeyframeInput,
@@ -36,45 +33,13 @@ def TellClasses():
         # ExportNodeTreeToJSON,]
 
 def mantis_tree_poll_op(context):
-    # return True
     space = context.space_data
     if hasattr(space, "node_tree"):
         if (space.node_tree):
-            return (space.tree_type == "MantisTree")
+            return (space.tree_type in ["MantisTree", "SchemaTree"])
     return False
 
 
-def get_override(active=None, edit=False, selected=[], type='VIEW_3D'):
-    #no clue what this does... taken from sorcar
-    override = bpy.context.copy()
-    if (type == 'VIEW_3D'):
-        override["active_object"] = active
-        if (edit):
-            override["edit_object"] = active
-        if (active not in selected):
-            selected.append(active)
-        override["selected_object"] = selected
-    flag = False
-    for window in bpy.data.window_managers[0].windows:
-        for area in window.screen.areas:
-            if area.type == type:
-                override["area"] = area
-                override["region"] = [i for i in area.regions if i.type == 'WINDOW'][0]
-                flag = True
-                break
-        if (flag):
-            break
-    return override
-
-def ChooseNodeGroupNode(treetype):
-    #I don't need this anymore... but I'm leaving it here
-    #  because this is a useful thing to separate
-    #  in case I add multiple tree types in the future
-    if (treetype == "MantisTree"):
-        return "MantisNodeGroup"
-    # if (treetype == "LinkTree"):
-    #     return "linkGroupNode"
-
 #########################################################################3
 
 class MantisGroupNodes(Operator):
@@ -96,11 +61,6 @@ class MantisGroupNodes(Operator):
         trees=[base_tree]
         selected_nodes=export_to_json(trees, write_file=False, only_selected=True)
         selected_nodes[base_tree.name][0]["name"]=grp_name
-        # this is for debugging the result of the export
-        # for k,v in selected_nodes[base_tree.name][2].items():
-        #     prPurple(k)
-        #     for k1, v1 in v["sockets"].items():
-        #         prRed("    ", k1, v1["name"])
         do_import(selected_nodes, context)
 
         affected_links_in = []
@@ -131,10 +91,6 @@ class MantisGroupNodes(Operator):
 
         grp_node.location = Vector((all_nodes_bounding_box[0].x+200, all_nodes_bounding_box[0].lerp(all_nodes_bounding_box[1], 0.5).y))
 
-        # for l in selected_nodes[base_tree.name][3]:
-        #     if source := l.get("source"):
-        #         n_from = base_tree.nodes.get(source[0])
-        #         # s_from = n_from.
 
         for n in selected_nodes[base_tree.name][2].values():
             for s in n["sockets"].values():
@@ -214,13 +170,6 @@ class ExecuteNodeTree(Operator):
         if environ.get("DOPROFILE"):
             do_profile=True
         if do_profile:
-            # cProfile.runctx("tree.update_tree(context)", None, locals())
-            # cProfile.runctx("tree.execute_tree(context)", None, locals())
-            # import hunter
-            # hunter.trace(stdlib=False, action=hunter.CallPrinter(force_colors=False))
-            # tree.update_tree(context)
-            # tree.execute_tree(context)
-            # return {"FINISHED"}
             import pstats, io
             from pstats import SortKey
             with cProfile.Profile() as pr:
@@ -240,55 +189,6 @@ class ExecuteNodeTree(Operator):
         prGreen("Finished executing tree in %f seconds" % (time() - start_time))
         return {"FINISHED"}
 
-# class CreateMetaGroup(Operator):
-    # """Create Meta Rig group node"""
-    # bl_idname = "mantis.create_meta_group"
-    # bl_label = "Create Meta Rig group node"
-
-    # @classmethod
-    # def poll(cls, context):
-        # return (mantis_tree_poll_op(context))
-
-    # def execute(self, context):
-        # space = context.space_data
-        # path = space.path
-        # node_tree = space.path[len(path)-1].node_tree
-        # # selected_nodes = [i for i in node_tree.nodes if i.select]
-        # ob = bpy.context.active_object
-        # matrices_build = []
-        # if (ob):
-            # if (ob.type == 'ARMATURE'):
-                # for pb in ob.pose.bones:
-                    # matrices_build.append((pb.name, pb.matrix, pb.length))
-        # xloc = -400
-        # yloc = 400
-        # loops = 0
-        # node_group = bpy.data.node_groups.new(ob.name, space.tree_type) 
-        # group_node = node_tree.nodes.new("MantisNodeGroup")
-        # group_output = node_group.nodes.new("NodeGroupOutput")
-        # path.append(node_group, node=group_node)
-        # group_node.node_tree = node_group
-        # gTree = group_node.node_tree
-        # for name, m, length in matrices_build:
-            # n = gTree.nodes.new("MetaRigMatrixNode")
-            # n.first_row = m[0]
-            # n.second_row = m[1]
-            # n.third_row = m[2]
-            # n.fourth_row = [m[3][0], m[3][1], m[3][2], length]
-            # print (n.fourth_row[3])
-            # n.name = name
-            # n.label = name
-            # n.location = (xloc + loops*250, yloc)
-            # if (yloc > -800):
-                # yloc-=55
-            # else:
-                # loops+=1
-                # yloc = 400
-            # node_group.links.new(n.outputs["Matrix"], group_output.inputs[''])
-            # node_group.outputs["Matrix"].name = name
-        # return {"FINISHED"}
-
-
 class QueryNodeSockets(Operator):
     """Utility Operator for querying the data in a socket"""
     bl_idname = "mantis.query_sockets"
@@ -364,58 +264,6 @@ class MantisMuteNode(Operator):
                 hide.default_value = node.mute
         return {"FINISHED"}
 
-
-class MantisVisualizeOutput(Operator):
-    """Mantis Visualize Output Operator"""
-    bl_idname = "mantis.visualize_output"
-    bl_label = "Visualize Output"
-
-    @classmethod
-    def poll(cls, context):
-        return (mantis_tree_poll_op(context))
-
-    def execute(self, context):
-        from time import time
-        from .utilities import wrapGreen, prGreen
-        
-        tree=context.space_data.path[0].node_tree
-        tree.update_tree(context)
-        # tree.execute_tree(context)
-        prGreen(f"Visualize Tree: {tree.name}")
-        nodes = tree.parsed_tree
-        from .readtree import visualize_tree
-        visualize_tree(nodes, tree, context)
-        return {"FINISHED"}
-
-
-class TestOperator(Operator):
-    """Mantis Test Operator"""
-    bl_idname = "mantis.test_operator"
-    bl_label = "Mantis Test Operator"
-
-    @classmethod
-    def poll(cls, context):
-        return (mantis_tree_poll_op(context))
-
-    def execute(self, context):
-        path = context.space_data.path
-        base_tree = path[0].node_tree
-        tree = path[len(path)-1].node_tree
-        node = tree.nodes.active
-        node.display_update(base_tree.parsed_tree, context)
-        # from .base_definitions import get_signature_from_edited_tree
-        # if nc := base_tree.parsed_tree.get(get_signature_from_edited_tree(node, context)):
-        #     from .utilities import get_all_dependencies
-        #     deps = get_all_dependencies(nc)
-        #     self.report({'INFO'}, f"Number of Node Dependencies: {len(deps)}")
-        #     # for n in deps:
-        #     #     prGreen(n)
-        # else:
-        #     # prRed("No NC found in parsed tree.")
-        #     self.report({'ERROR_INVALID_CONTEXT'}, "No data for node.")
-        return {"FINISHED"}
-
-
 ePropertyType =(
         ('BOOL'  , "Boolean", "Boolean", 0),
         ('INT'   , "Integer", "Integer", 1),
@@ -448,25 +296,25 @@ class AddCustomProperty(bpy.types.Operator):
     soft_max:bpy.props.FloatProperty(default = 1)
     description:bpy.props.StringProperty(default = "")
     
-    node_invoked : bpy.props.PointerProperty(type=bpy.types.Node, 
-                options ={'HIDDEN'}) # note this seems to affect all
-                                     # subsequent properties
+    tree_invoked : bpy.props.StringProperty(options ={'HIDDEN'})
+    node_invoked : bpy.props.StringProperty(options ={'HIDDEN'})
 
     @classmethod
     def poll(cls, context):
         return True #( hasattr(context, 'node') ) 
 
     def invoke(self, context, event):
-        self.node_invoked = context.node
+        self.tree_invoked = context.node.id_data.name
+        self.node_invoked = context.node.name
         wm = context.window_manager
         return wm.invoke_props_dialog(self)
         
     def execute(self, context):
-        n = self.node_invoked
+        n = bpy.data.node_groups[self.tree_invoked].nodes[self.node_invoked]
         # For whatever reason, context.node doesn't exist anymore
         #   (probably because I use a window to execute)
         # so as a sort of dumb workaround I am saving it to a hidden
-        #  property of the operator... it works.
+        #  property of the operator... it works but Blender complains.
         socktype = ''
         if not (self.prop_name):
             self.report({'ERROR_INVALID_INPUT'}, "Must name the property.")
@@ -504,51 +352,67 @@ class AddCustomProperty(bpy.types.Operator):
         
         return {'FINISHED'}
 
-#DOESN'T WORK YET
+def main_get_existing_custom_properties(operator, context):
+    ret = []; i = -1
+    n = bpy.data.node_groups[operator.tree_invoked].nodes[operator.node_invoked]
+    for inp in n.inputs:
+        if 'Parameter' in inp.bl_idname:
+            ret.append( (inp.identifier, inp.name, "Custom Property to Modify", i := i + 1), )
+    return ret
+            
+
 class EditCustomProperty(bpy.types.Operator):
     """Edit Custom Property"""
     bl_idname = "mantis.edit_custom_property"
     bl_label = "Edit Custom Property"
 
+    def get_existing_custom_properties(self, context):
+        return main_get_existing_custom_properties(self, context)
 
+    prop_edit : bpy.props.EnumProperty(
+        items=get_existing_custom_properties,
+        name="Property to Edit?",
+        description="Select which property to edit",)
     prop_type : bpy.props.EnumProperty(
         items=ePropertyType,
         name="New Property Type",
         description="Type of data for new Property",
         default = 'BOOL',)
     prop_name  : bpy.props.StringProperty(default='Prop')
-    
     min:bpy.props.FloatProperty(default = 0)
     max:bpy.props.FloatProperty(default = 1)
     soft_min:bpy.props.FloatProperty(default = 0)
     soft_max:bpy.props.FloatProperty(default = 1)
     description:bpy.props.StringProperty(default = "") # TODO: use getters to fill these automatically
     
-    node_invoked : bpy.props.PointerProperty(type=bpy.types.Node, 
-                options ={'HIDDEN'}) # note this seems to affect all
-                                     # subsequent properties
+    tree_invoked : bpy.props.StringProperty(options ={'HIDDEN'})
+    node_invoked : bpy.props.StringProperty(options ={'HIDDEN'})
 
     @classmethod
     def poll(cls, context):
         return True #( hasattr(context, 'node') ) 
 
     def invoke(self, context, event):
-        self.node_invoked = context.node
+        self.tree_invoked = context.node.id_data.name
+        self.node_invoked = context.node.name
         wm = context.window_manager
         return wm.invoke_props_dialog(self)
         
     def execute(self, context):
-        n = self.node_invoked
-        prop = n.inputs.get( self.prop_name )
-        if (s := n.inputs.get(self.prop_name)):
-            if self.prop_type in ['INT','FLOAT']:
-                new_prop.min = self.min
-                new_prop.max = self.max
-                new_prop.soft_min = self.soft_min
-                new_prop.soft_max = self.soft_max
-            new_prop.description = self.description
-        
-        return {'FINISHED'}
+        n = bpy.data.node_groups[self.tree_invoked].nodes[self.node_invoked]
+        prop = n.inputs.get( self.prop_edit )
+        if prop:
+            prop.name = self.prop_name
+            if (s := n.inputs.get(self.prop_edit)):
+                if self.prop_type in ['INT','FLOAT']:
+                    prop.min = self.min
+                    prop.max = self.max
+                    prop.soft_min = self.soft_min
+                    prop.soft_max = self.soft_max
+                prop.description = self.description
+            return {'FINISHED'}
+        else:
+            self.report({'ERROR_INVALID_INPUT'}, "Cannot edit a property that does not exist.")
 
 
 
@@ -558,31 +422,22 @@ class RemoveCustomProperty(bpy.types.Operator):
     bl_label = "Remove Custom Property"
 
     def get_existing_custom_properties(self, context):
-        ret = []; i = -1
-        n = context.active_node
-        for inp in n.inputs:
-            if 'Parameter' in inp.bl_idname:
-                ret.append( (inp.identifier, inp.name, "Parameter to remove", i := i + 1), )
-        if ret:
-            return ret
-        return None
-                
-
+        return main_get_existing_custom_properties(self, context)
+    
     prop_remove : bpy.props.EnumProperty(
         items=get_existing_custom_properties,
         name="Property to remove?",
         description="Select which property to remove",)
-    node_invoked : bpy.props.PointerProperty(type=bpy.types.Node, 
-                options ={'HIDDEN'}) # note this seems to affect all
-                                     # subsequent properties
+    tree_invoked : bpy.props.StringProperty(options ={'HIDDEN'})
+    node_invoked : bpy.props.StringProperty(options ={'HIDDEN'})
 
     @classmethod
     def poll(cls, context):
         return True #(hasattr(context, 'active_node') )
 
     def invoke(self, context, event):
-        print (context.node)
-        self.node_invoked = context.node
+        self.tree_invoked = context.node.id_data.name
+        self.node_invoked = context.node.name
         t = context.node.id_data
         # HACK the props dialog makes this necesary
         #  because context.node only exists during the event that
@@ -590,12 +445,12 @@ class RemoveCustomProperty(bpy.types.Operator):
         t.nodes.active = context.node # HACK
         context.node.select = True # HACK
         # I need this bc of the callback for the enum property.
-        #  for whatever reason I can't use node_invoked there
+        #  for whatever reason I can't use tree_invoked there
         wm = context.window_manager
         return wm.invoke_props_dialog(self)
         
     def execute(self, context):
-        n = self.node_invoked
+        n = bpy.data.node_groups[self.tree_invoked].nodes[self.node_invoked]
         # For whatever reason, context.node doesn't exist anymore
         #   (probably because I use a window to execute)
         # so as a sort of dumb workaround I am saving it to a hidden
@@ -605,7 +460,7 @@ class RemoveCustomProperty(bpy.types.Operator):
                 break
         else:
             self.report({'ERROR'}, "Input not found")
-            raise RuntimeError("This should not happen!")
+            return {'CANCELLED'}
         # it's possible that the output property's identifier isn't the
         #   exact same... but I don' care. Shouldn't ever happen. TODO
         for j, out in enumerate(n.outputs):
@@ -619,153 +474,6 @@ class RemoveCustomProperty(bpy.types.Operator):
         return {'FINISHED'}
 
 
-# TODO: not a priority
-#  This one will remove the old socket and add a new one
-#  and it'll put it back in place and reconnect the links
-#   It's OK to just ask the user to do this manually for now
-#
- # class EditCustomProperty(bpy.types.Operator):
-    # """Edit Custom Property in xForm Node"""
-    # bl_idname = "mantis.edit_custom_property"
-    # bl_label = "Edit Custom Property"
-
-
-    # prop_type : bpy.props.EnumProperty(
-        # items=ePropertyType,
-        # name="New Property Type",
-        # description="Type of data for new Property",
-        # default = 'BOOL',)
-    # prop_name  : bpy.props.StringProperty(default='Prop')
-    
-    # min:bpy.props.FloatProperty(default = 0)
-    # max:bpy.props.FloatProperty(default = 1)
-    # soft_min:bpy.props.FloatProperty(default = 0)
-    # soft_max:bpy.props.FloatProperty(default = 1)
-    # description:bpy.props.StringProperty(default = "")
-    
-    # node_invoked : bpy.props.PointerProperty(type=bpy.types.Node, 
-                # options ={'HIDDEN'}) # note this seems to affect all
-                                     # # subsequent properties
-
-    # @classmethod
-    # def poll(cls, context):
-        # return True #( hasattr(context, 'node') ) 
-
-    # def invoke(self, context, event):
-        # print (context.node)
-        # self.node_invoked = context.node
-        # print(dir(self))
-        # wm = context.window_manager
-        # return wm.invoke_props_dialog(self)
-        
-    # def execute(self, context):
-        # n = self.node_invoked
-        # # For whatever reason, context.node doesn't exist anymore
-        # #   (probably because I use a window to execute)
-        # # so as a sort of dumb workaround I am saving it to a hidden
-        # #  property of the operator... it works.
-        # socktype = ''
-        # if not (self.prop_name):
-            # self.report({'ERROR_INVALID_INPUT'}, "Must name the property.")
-            # return {'CANCELLED'}
-        # if self.prop_type == 'BOOL':
-            # socktype = 'ParameterBoolSocket'
-        # if self.prop_type == 'INT':
-            # socktype = 'ParameterIntSocket'
-        # if self.prop_type == 'FLOAT':
-            # socktype = 'ParameterFloatSocket'
-        # if self.prop_type == 'VECTOR':
-            # socktype = 'ParameterVectorSocket'
-        # if self.prop_type == 'STRING':
-            # socktype = 'ParameterStringSocket'
-        # #if self.prop_type == 'ENUM':
-        # #    sock_type = 'ParameterStringSocket'
-        # if (s := n.inputs.get(self.prop_name)):
-            # try:
-                # number = int(self.prop_name[-3:])
-                # # see if it has a number
-                # number+=1
-                # self.prop_name = self.prop_name[:-3] + str(number).zfill(3)
-            # except ValueError:
-                # self.prop_name+='.001'
-        # new_prop = n.inputs.new( socktype, self.prop_name)
-        # if self.prop_type in ['INT','FLOAT']:
-            # new_prop.min = self.min
-            # new_prop.max = self.max
-            # new_prop.soft_min = self.soft_min
-            # new_prop.soft_max = self.soft_max
-        # new_prop.description = self.description
-            
-            
-        # return {'FINISHED'}
-
-class EditFCurveNode(bpy.types.Operator):
-    """Edit the fCurve owned by fCurve node"""
-    bl_idname = "mantis.edit_fcurve_node"
-    bl_label = "Edit fCurve"
-    bl_options = {'INTERNAL'}
-    
-    my_window : bpy.props.StringProperty(default = "-1")
-    node_invoked : bpy.props.PointerProperty(type=bpy.types.Node, 
-                options ={'HIDDEN'}) # note this seems to affect all
-                                     # subsequent properties
-    fake_fcurve_ob: bpy.props.PointerProperty(
-                type=bpy.types.Object, 
-                options ={'HIDDEN'},)
-    prev_active: bpy.props.PointerProperty(
-                type=bpy.types.Object, 
-                options ={'HIDDEN'},)
-    
-
-    @classmethod
-    def poll(cls, context):
-        return True #(hasattr(context, 'active_node') )
-
-    def modal(self, context, event):
-        for w in context.window_manager.windows:
-            if str(w.as_pointer()) == self.my_window:
-                break
-        else:
-            context.scene.collection.objects.unlink( self.fake_fcurve_ob )
-            context.view_layer.objects.active = self.prev_active
-            self.prev_active.select_set(True)
-            # at this point I will push the fcurve to nodes
-            #  or some kind of internal data
-            return {'FINISHED'}
-        # I can't currently think of anything I need to do with w
-        return {'PASS_THROUGH'}
-        
-        
-    def invoke(self, context, event):
-        self.node_invoked = context.node
-        self.fake_fcurve_ob = self.node_invoked.fake_fcurve_ob
-        context.scene.collection.objects.link( self.fake_fcurve_ob )
-        self.prev_active = context.view_layer.objects.active
-        context.view_layer.objects.active = self.fake_fcurve_ob
-        self.fake_fcurve_ob.select_set(True)
-        context.window_manager.modal_handler_add(self)
-        # this is added to the active window.
-        if (self.my_window == "-1"):
-            prev_windows = set()
-            for w in context.window_manager.windows:
-                prev_windows.add(w.as_pointer())
-            bpy.ops.wm.window_new()
-            for w in context.window_manager.windows:
-                w_int = w.as_pointer()
-                if (w_int not in prev_windows):
-                    self.my_window = str(w_int)
-                    break
-            else:
-                print ("cancelled")
-                return {'CANCELLED'}
-            # set up properties for w
-            # w.height = 256 # READ
-            # w.width = 400  # ONLY
-            w.screen.areas[0].type = 'GRAPH_EDITOR'
-            w.screen.areas[0].spaces[0].auto_snap = 'NONE'
-            
-        return {'RUNNING_MODAL'}
-
 
 # SIMPLE node operators...
 # May rewrite these in a more generic way later
@@ -830,8 +538,6 @@ class DriverRemoveDriverVariableInput(bpy.types.Operator):
         n.inputs.remove(n.inputs[-1])
         return {'FINISHED'}
         
-        
-        
 class LinkArmatureAddTargetInput(bpy.types.Operator):
     """Add a Driver Variable input to the Driver node"""
     bl_idname = "mantis.link_armature_node_add_target"
@@ -862,43 +568,4 @@ class LinkArmatureRemoveTargetInput(bpy.types.Operator):
     def execute(self, context):
         n = context.node
         n.inputs.remove(n.inputs[-1]); n.inputs.remove(n.inputs[-1])
-        return {'FINISHED'}
-
-
-
-# class ExportNodeTreeToJSON(Operator):
-#     """Export this node tree as a JSON file"""
-#     bl_idname = "mantis.export_node_tree_json"
-#     bl_label = "Export Mantis Tree to JSON"
-
-#     @classmethod
-#     def poll(cls, context):
-#         return (mantis_tree_poll_op(context))
-
-#     def execute(self, context):
-#         from .i_o import export_to_json
-#         import bpy
-
-#         tree = context.space_data.path[0].node_tree
-#         # tree.update_tree(context)
-#         trees = {tree}
-#         check_trees=[tree]
-#         while check_trees:
-#             check = check_trees.pop()
-#             for n in check.nodes:
-#                 if hasattr(n, "node_tree"):
-#                     if n.node_tree not in trees:
-#                         check_trees.append(n.node_tree)
-#                         trees.add(n.node_tree)
-        
-
-
-#         def remove_special_characters(stritree):
-#             # https://stackoverflow.com/questions/295135/turn-a-stritree-into-a-valid-filename
-#             # thank you user "Sophie Gage"
-#             import re # regular expressions
-#             return re.sub('[^\w_.)( -]', '', stritree)
-
-#         path = bpy.path.abspath('//')+remove_special_characters(tree.name)+".json"
-#         export_to_json(trees, path)
-#         return {"FINISHED"}
+        return {'FINISHED'}

+ 16 - 181
readtree.py

@@ -57,15 +57,13 @@ def reroute_links_grpin(nc, all_nc):
 # FIXME I don't think these signatures are unique.
 def insert_lazy_parents(nc):
     from .link_containers import LinkInherit
-    from .xForm_containers import xFormRoot, xFormArmature
+    from .xForm_containers import xFormArmature
     from .node_container_common import NodeLink
     inherit_nc = None
     if nc.inputs["Relationship"].is_connected:
         link = nc.inputs["Relationship"].links[0]
         # print(nc)
         from_nc = link.from_node
-        if isinstance(from_nc, xFormRoot):
-            return
         if from_nc.node_type in ["XFORM"] and link.from_socket in ["xForm Out"]:
             inherit_nc = LinkInherit(("MANTIS_AUTOGENERATED", *nc.signature[1:], "LAZY_INHERIT"), nc.base_tree)
             for from_link in from_nc.outputs["xForm Out"].links:
@@ -95,11 +93,6 @@ def insert_lazy_parents(nc):
             init_dependencies(from_nc)
             init_connections(inherit_nc)
             init_dependencies(inherit_nc)
-            # and the inherit node never was
-    # this doesn't seem to be necessary or helpful.
-    # elif isinstance(nc, xFormArmature):
-    #         inherit_nc = xFormRoot(("MANTIS_AUTOGENERATED", *nc.signature[1:], "LAZY_ROOT"), nc.base_tree)
-    #         init_connections(inherit_nc) # we need to do this here because parse_tree expects a link not a xForm
     return inherit_nc
 
 
@@ -236,6 +229,8 @@ def is_signature_in_other_signature(sig_a, sig_b):
     # this is the easiest but not the best way to do this:
     # this function is hideous but it does not seem to have any significant effect on timing
     #    tested it with profiling on a full character rig.
+    # OK. Had another test in a more extreme situation and this one came out on top for time spent and calls
+    # gotta optimize this one.
     sig_a = list(sig_a)
     sig_a = ['MANTIS_NONE' if val is None else val for val in sig_a]
     sig_b = list(sig_b)
@@ -543,47 +538,8 @@ def sort_tree_into_layers(nodes, context):
             prWhite (p, p.dependencies)
     return layers
 
-
-
-def visualize_tree(nodes, base_tree, context):
-    # first create a MantisVisualizeTree
-    import bpy
-    tree = bpy.data.node_groups.new(base_tree.name+'_visualized', type='MantisVisualizeTree')
-    # HACK but OK since this isn't really "public" code
-    all_links = set()
-    for n in nodes.values():
-        vis = tree.nodes.new('MantisVisualizeNode')
-        vis.gen_data(n, get_node_prototype(n.signature, base_tree))
-
-        for inp in n.inputs.values():
-            for l in inp.links:
-                all_links.add(l)
-        for out in n.outputs.values():
-            for l in out.links:
-                all_links.add(l)
-
-    for l in all_links:
-        if l.to_node.node_type in ['SCHEMA_DUMMY', 'SCHEMA', 'DUMMY'] or \
-           l.from_node.node_type in ['SCHEMA_DUMMY', 'SCHEMA', 'DUMMY']:
-            pass
-        n_name_in = '.'.join(l.from_node.signature[1:])
-        s_name_in = l.from_socket
-        n_name_out = '.'.join(l.to_node.signature[1:])
-        s_name_out = l.to_socket
-        try:
-            tree.links.new(
-                input=tree.nodes.get(n_name_in).outputs.get(s_name_in),
-                output=tree.nodes.get(n_name_out).inputs.get(s_name_out),
-                )
-        except Exception as e:
-            print (type(e))
-            prRed(f"Could not make link {n_name_in}:{s_name_in}-->{n_name_out}:{s_name_out}")
-            print(e)
-                # raise e
-    # at this point not all links are in the tree yet!
-
-    from .utilities import SugiyamaGraph
-    SugiyamaGraph(tree, 16)
+def error_popup_draw(self, context):
+    self.layout.label(text="Error executing tree. There is an illegal cycle somewhere in the tree.")
 
 #execute tree is really slow overall, but still completes 1000s of nodes in only 
 def execute_tree(nodes, base_tree, context):
@@ -609,15 +565,24 @@ def execute_tree(nodes, base_tree, context):
     # check for cycles here by keeping track of the number of times a node has been visited.
     visited={}
     check_max_len=len(nodes)**2 # seems too high but safe. In a well-ordered graph, I guess this number should be less than the number of nodes.
-    
+    max_iterations = len(nodes)**2
+    i = 0
+
     while(xForm_pass):
+        if i >= max_iterations:
+            raise GraphError("There is a cycle somewhere in the graph.")
+            bpy.context.window_manager.popup_menu(error_popup_draw, title="Error", icon='ERROR')
+            break
+        i+=1    
         n = xForm_pass.pop()
         if visited.get(n.signature):
             visited[n.signature]+=1
         else:
             visited[n.signature]=0
         if visited[n.signature] > check_max_len:
-            prRed("There is a cycle in the graph somewhere. Fix it!")
+            raise GraphError("There is a cycle in the graph somewhere. Fix it!")
+            bpy.context.window_manager.popup_menu(error_popup_draw, title="Error", icon='ERROR')
+            break
             # we're trying to solve the halting problem at this point.. don't do that.
             # TODO find a better way! there are algo's for this but they will require using a different solving algo, too
         if n.prepared:
@@ -694,133 +659,3 @@ def execute_tree(nodes, base_tree, context):
         context.view_layer.objects.active = original_active
         original_active.select_set(True)
 
-
-
-
-
-def execute_tree_old(nodes, base_tree, context):
-    # execute_tree_nosort(nodes, base_tree, context); return
-    import bpy
-    from time import time
-    from .node_container_common import GraphError
-    from uuid import uuid4
-    start_time  = time()
-    original_active = context.view_layer.objects.active
-    
-    # if we solve Schema trees here, then we don't need to worry about parsed_tree getting more complex
-    # however, at this point, we are going to have to do some re-connections
-    #   and I am not sure this won't mess up the dependency solving.
-    # worst case scenario, I think I can run the init_connections()
-    #   bit again after swapping the dummy-node for the solved schema tree
-
-    layers = sort_tree_into_layers(nodes, context)
-    start_execution_time = time()
-
-    # raise NotImplementedError
-    
-    #             Execute the first pass (xForm, Utility)              #
-    for i in range(len(layers)):
-        for node in layers[i]:
-            if (node.node_type in ['XFORM', 'UTILITY']):
-                try:
-                    # ex_start_time = time()
-                    # if not node.prepared:
-                    # we have to do this over again oof
-                    node.bPrepare(context)
-                    if not node.executed:
-                        node.bExecute(context)
-                    # prPurple(f"Execution step took time... {time() - ex_start_time}")
-                except Exception as e:
-                    # prRed(node)
-                    # for dep in node.dependencies:
-                    #     prOrange(" ", dep, dep.prepared&dep.executed)
-                    #     for dep in dep.dependencies:
-                    #         prWhite("  ", dep, dep.prepared&dep.executed)
-                    #         for dep in dep.dependencies:
-                    #             prPurple("   ", dep, dep.prepared&dep.executed)
-                    #             for dep in dep.dependencies:
-                    #                 prRed("    ", dep, dep.prepared&dep.executed)
-                    prRed("Execution failed at %s" % node); raise e
-    #                     Switch to Pose Mode                          #
-    active = None
-    switch_me = []
-    for n in nodes.values():
-        # if it is a armature, switch modes
-        # total hack                   #kinda dumb
-        if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
-            try:
-                ob = n.bGetObject()
-            except KeyError: # for bones
-                ob = None
-            # TODO this will be a problem if and when I add mesh/curve stuff
-            if (hasattr(ob, 'mode') and ob.mode == 'EDIT'):
-                switch_me.append(ob)
-                active = ob
-                context.view_layer.objects.active = ob# need to have an active ob, not None, to switch modes.
-            # we override selected_objects to prevent anyone else from mode-switching
-    # TODO it's possible but unlikely that the user will try to run a 
-    #    graph with no armature nodes in it.
-    if (active):
-        for ob in switch_me:
-            ob.pose_position='REST'
-        with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
-            bpy.ops.object.mode_set(mode='POSE')
-    #               Execute second pass (Link, Driver)                 #
-    for i in range(len(layers)):
-        for n in layers[i]:
-            # Now do the Link & Driver nodes during the second pass.
-            if (n.node_type in ['LINK', 'DRIVER']):
-                try:
-                    if not n.prepared:
-                        n.bPrepare(context)
-                    if not n.executed:
-                        n.bExecute(context)
-                except Exception as e:
-                    prRed("Execution failed at %s" % n); raise e
-                # except GraphError:
-                #     pass                     
-                # except Exception as e:
-                #     prRed (n); raise e
-                    
-    #                          Finalize                                #
-    for i in range(len(layers)):
-        for node in layers[i]:
-            if (hasattr(node, "bFinalize")):
-                node.bFinalize(context)
-    
-    
-    for n in nodes.values():
-        # if it is a armature, switch modes
-        # total hack                   #kinda dumb
-        if ((hasattr(n, "bGetObject")) and (n.__class__.__name__ == "xFormArmature" )):
-            try:
-                ob = n.bGetObject()
-            except KeyError: # for bones
-                ob = None
-            # TODO this will be a problem if and when I add mesh/curve stuff
-            if (hasattr(ob, 'mode') and ob.mode == 'POSE'):
-                switch_me.append(ob)
-                active = ob
-    if (active):
-        for ob in switch_me:
-            ob.pose_position='POSE'
-        with context.temp_override(**{'active_object':active, 'selected_objects':switch_me}):
-            bpy.ops.object.mode_set(mode='OBJECT')
-    
-    prGreen("Executed Tree in %s seconds" % (time() - start_execution_time))
-    prGreen("Finished executing tree in %f seconds" % (time() - start_time))
-    if (original_active):
-        context.view_layer.objects.active = original_active
-        original_active.select_set(True)
-
-
-
-# TODO right now:
-# I am working on getting schema to ignore non-hierarchy dependencies
-# I need to ensure that the links are being made between nodes
-# so I need to check the OUTPUTS of the arrays on the DUMMY_SCHEMA of the node that is sending
-#   backwards connections out
-# then I need to make sure that they are going to the right place
-# also it is probably eventually necessary to break schema solve into schema solve_step
-#   which solves one step and decrements the length by one
-#   and holds on to the solver for next time

+ 1 - 27
schema_containers.py

@@ -38,11 +38,7 @@ class SchemaIndex:
     def __init__(self, signature, base_tree):
         self.base_tree=base_tree
         self.signature = signature
-        self.inputs = {}#{
-        #   "X"   : NodeSocket(is_input = True, name = "X", node = self),
-        #   "Y"   : NodeSocket(is_input = True, name = "Y", node = self),
-        #   "Z"   : NodeSocket(is_input = True, name = "Z", node = self),
-        # }
+        self.inputs = {}
         self.outputs = {
           "Index" : NodeSocket(name = "Index", node=self),
           "Schema Length" : NodeSocket(name = "Schema Length", node=self),
@@ -193,27 +189,5 @@ class SchemaIncomingConnection:
         self.prepared = True
         self.executed = True
 
-
-
-# class SchemaChoose:
-#     def __init__(self, signature, base_tree):
-#         self.base_tree=base_tree
-#         self.signature = signature
-#         init_parameters(self)
-#         self.node_type = 'UTILITY'
-        
-#     def evaluate_input(self, input_name):
-#         return evaluate_input(self, input_name)
-    
-#     def bExecute(self, bContext = None,):
-#         pass
-    
-#     def __repr__(self):
-#         return self.signature.__repr__()
-        
-#     def fill_parameters(self):
-#         fill_parameters(self)
-
-
 for c in TellClasses():
     setup_container(c)

+ 0 - 94
schema_definitions.py

@@ -21,9 +21,6 @@ def TellClasses():
         SchemaConstOutput,
         SchemaOutgoingConnection,
         SchemaIncomingConnection,
-        # # iterators
-        # SchemaIntMath,
-        # SchemaDeclarationValidWhen,
         ]
 
 
@@ -231,94 +228,3 @@ class SchemaIncomingConnection(Node, SchemaNode):
             self.outputs.new('WildcardSocket', '', identifier='__extend__')
         self.initialized = True
 
-
-
-# have a string for name
-# assign/get
-# and a fallback if none
-# get should take an integer: 0 = index, -1 = index -1, etc., no positive ints allowed
-# class SchemaLocalVariable(Node, SchemaNode):
-#     '''Constant Inputs'''
-#     bl_idname = 'SchemaIncomingConnection'
-#     bl_label = "Incoming Connection"
-#     bl_icon = 'GIZMO'
-    
-#     def init(self, context):
-#         # self.outputs.new("IntSocket", "Index")
-#         pass
-
-
-# class SchemaIntMath(Node, SchemaNode):
-#     '''Int Math'''
-#     bl_idname = 'SchemaIntMath'
-#     bl_label = "Int Math"
-#     bl_icon = 'GIZMO'
-    
-#     # def init(self, context):
-#     #     self.update()
-
-
-# class SchemaDeclarationValidWhen(Node, SchemaNode):
-#     '''Declaration Valid When'''
-#     bl_idname = 'SchemaDeclarationValidWhen'
-#     bl_label = "Declaration Valid When"
-#     bl_icon = 'GIZMO'
-    
-#     def init(self, context):
-#         self.inputs.new('IntSocket', 'Valid From')
-#         self.inputs.new('IntSocket', 'Valid Until')
-#         self.inputs.new('IntSocket', 'Add to N') # +
-#         self.inputs.new('IntSocket', 'Multiply N') # *
-#         self.inputs.new('IntSocket', 'Modulo of N') # %
-#         # self.inputs.new('IntSocket', 'n')
-
-
-# I need to figure out what to do with this right here...
-# There are a few options:
-#  - an actual control flow (if, then) -- but I don' like that because it's not declarative
-#  - "declaration valid when" statement that is basically a range with simple math rules
-#      - this is funcionally almost entirely the same
-#      - perhaps this sort of range plugs into the end of the schema?
-#      - but I want it to operate kind of like a frame or simulation zone
-#  - Begin / End declaration makes it more like a framed region
-#      - hypothetically I don't need to have any begin and I can just imply it
-#      - I don't wanna have to develop a bunch of code for dealing with new links that are only there for the sake of schema
-#  - then I ran into the problem that the in/out connections are relevant to a specific declaration
-#  - what I need is a way to modify the declaration in the loop, not a way to construct a bunch of different iterators....
-#  - so maybe I can get away with basic maths only
-
-# so I need a way to control a declaration by the index
-#   - a switch node, maybe one with an arbitrary socket type like wildcard that just adapts
-#   - it should be possible to do math with the index and len(schema)
-#       - example, for naming a bone 'Center' if index == len(schema)//2
-#            - the "if" is what annoys me about this
-#       - making numbers and chiral identifiers for names
-#       - array gets
-#   - 
-
-
-
-# class SchemaChoose(Node, SchemaNode):
-#     '''Choose'''
-#     bl_idname = 'SchemaChoose'
-#     bl_label = "Choose"
-#     bl_icon = 'GIZMO'
-#     initialized : bpy.props.BoolProperty(default = False)
-    
-#     def init(self, context):
-#         self.inputs.new('IntSocket', 'Number of Choices')
-#         self.inputs.new('IntSocket', 'Choose Index')
-#         self.outputs.new('WildcardSocket', 'Choice')
-#         self.update()
-
-#     def update(self):
-#         self.initialized = False
-#         input_map = get_socket_maps(self)[0]
-#         # print (input_map)
-#         self.inputs.clear()
-#         self.inputs.new('IntSocket', 'Number of Choices')
-#         self.inputs.new('IntSocket', 'Choose Index')
-#         #
-#         # update on this one requires being able to read the tree!
-#             # self.inputs.new("WildcardSocket", "")
-#         self.initialized = True

+ 9 - 161
utilities.py

@@ -314,18 +314,6 @@ def init_dependencies(nc):
     nc.hierarchy_dependencies = hc
     nc.dependencies = c
 
-# schema_input_types = [
-#         'SchemaIndex',
-#         'SchemaArrayInput',
-#         'SchemaArrayInputGet',
-#         'SchemaConstInput',
-#         'SchemaIncomingConnection',
-# ]
-# schema_output_types = [
-#         'SchemaArrayOutput',
-#         'SchemaConstOutput',
-#         'SchemaOutgoingConnection',
-# ]
 
 from .base_definitions import from_name_filter, to_name_filter
 
@@ -333,18 +321,6 @@ def init_schema_dependencies(schema, all_nc):
     schema_name = schema.signature[-1]
     all_input_nodes = []
     all_output_nodes = []
-    # all_inernal_nodes = []
-    # for nc in all_nc.values():
-    #     for t in schema_input_types:
-    #         if nc.signature == (*schema.signature, t):
-    #             all_input_nodes.append(nc)
-    #     for t in schema_output_types:
-    #         if nc.signature == (*schema.signature, t):
-    #             all_output_nodes.append(nc)
-    # prOrange (schema.connections)
-    # print (schema.hierarchy_connections)
-    # prOrange (schema.dependencies)
-    # prOrange (schema.hierarchy_dependencies)
 
     # so the challenge is to map these and check both ends
     from .base_definitions import from_name_filter, to_name_filter
@@ -405,71 +381,9 @@ def init_schema_dependencies(schema, all_nc):
                         if hierarchy:
                             hc.append(from_link.from_node)
                         c.append(from_link.from_node)
-        # prPurple(item.in_out)
-        # if hierarchy:
-        #     prOrange(item.name)
-        # else:
-        #     prWhite(item.name)
-        #     print(hierarchy_reason)
-
-        # else:
-        #     c = schema.connections
-        #     hc = schema.hierarchy_connections
-        #     if item.parent and item.parent.name == 'Array':
-        #         if nc := all_nc.get((*schema.signature, 'SchemaArrayOutput')):
-        #             for from_link in nc.inputs[item.name].links:
-        #                 if from_link.from_socket in from_name_filter:
-        #                     hierarchy = False
-        #             for to_link in schema.outputs[item.identifier].links:
-        #                 if to_link.to_socket in to_name_filter:
-        #                     hierarchy = False
-        #     if item.parent and item.parent.name == 'Constant':
-        #         if nc := all_nc.get((*schema.signature, 'SchemaConstOutput')):
-        #             for from_link in nc.inputs[item.name].links:
-        #                 if from_link.from_socket in from_name_filter:
-        #                     hierarchy = False
-        #             for to_link in schema.outputs[item.identifier].links:
-        #                 if to_link.to_socket in to_name_filter:
-        #                     hierarchy = False
-        #     if item.parent and item.parent.name == 'Connection':
-        #         if nc := all_nc.get((*schema.signature, 'SchemaOutgoingConnection')):
-        #             for from_link in nc.inputs[item.name].links:
-        #                 if from_link.from_socket in from_name_filter:
-        #                     hierarchy = False
-        #             for to_link in schema.outputs[item.identifier].links:
-        #                 if to_link.to_socket in to_name_filter:
-        #                     hierarchy = False
-    # for nc in all_input_nodes:
-    #     for output in nc.outputs.values():
-    #         for l in output.links:
-    #             if l.to_socket in to_name_filter:
-    #                 print("not hierarchy", l.to_socket)
-    #             else:
-    #                 print("hierarchy", l.to_socket)
-    # for inp in schema.inputs.values():
-    #     for l in inp.links:
-    #         if l.from_socket in from_name_filter:
-    #             print("not hierarchy", l.from_socket)
-    #         else:
-    #             print("hierarchy", l.from_socket)
-    
-    # we need to get dependencies and connections
-    # but we can use the same method to do each
-
-
-    # prPurple (schema.connections)
-    # # print (schema.hierarchy_connections)
-    # prPurple (schema.dependencies)
-    # prPurple (schema.hierarchy_dependencies)
-    # #
 
 
 def check_and_add_root(n, roots, include_non_hierarchy=False):
-    # if not (hasattr(n, 'inputs')) or ( len(n.inputs) == 0):
-    #     roots.append(n)
-    # elif (hasattr(n, 'inputs')):
-    #     for inp in n.inputs.values():
-    #         if inp.is_linked: return
     if include_non_hierarchy == True and len(n.dependencies) > 0:
         return 
     elif len(n.hierarchy_dependencies) > 0:
@@ -534,30 +448,8 @@ def to_mathutils_value(socket):
     if hasattr(socket, "default_value"):
         from mathutils import Matrix, Euler, Quaternion, Vector
         val = socket.default_value
-        # if socket.bl_idname in [
-        #     'NodeSocketVector',
-        #     'NodeSocketVectorAcceleration',
-        #     'NodeSocketVectorDirection',
-        #     'NodeSocketVectorTranslation',
-        #     'NodeSocketVectorXYZ',
-        #     'NodeSocketVectorVelocity',
-        #     'VectorSocket',
-        #     'VectorEulerSocket',
-        #     'VectorTranslationSocket',
-        #     'VectorScaleSocket',
-        #     'ParameterVectorSocket',]:
-        # # if "Vector" in socket.bl_idname:
-        #     return (Vector(( val[0], val[1], val[2], )))
-        # if socket.bl_idname in ['NodeSocketVectorEuler']:
-        #     return (Euler(( val[0], val[1], val[2])), 'XYZ',) #TODO make choice
         if socket.bl_idname in ['MatrixSocket']:
             return socket.TellValue()
-        # elif socket.bl_idname in ['QuaternionSocket']:
-        #     return (Quaternion( (val[0], val[1], val[2], val[3],)) )
-        # elif socket.bl_idname in ['QuaternionSocketAA']:
-        #     return (Quaternion( (val[1], val[2], val[3],), val[0], ) )
-        # elif socket.bl_idname in ['BooleanThreeTupleSocket']:
-        #     return (val[0], val[1], val[2]) 
         else:
             return val
     else:
@@ -711,11 +603,7 @@ def class_for_mantis_prototype_node(prototype_node):
     # But I actually think this is a bad idea since I might not
     #  want to use this name convention in the future
     #  this is easy enough for now, may refactor.
-    #
-    # kek, turns out it was completely friggin' inconsistent already
-    if prototype_node.bl_idname == 'xFormRootNode':
-        return classes["xFormRoot"]
-    elif prototype_node.bl_idname == 'xFormArmatureNode':
+    if prototype_node.bl_idname == 'xFormArmatureNode':
         return classes["xFormArmature"]
     elif prototype_node.bl_idname == 'xFormBoneNode':
         return classes["xFormBone"]
@@ -775,10 +663,11 @@ def class_for_mantis_prototype_node(prototype_node):
     return None
 
 
-# This is really, really stupid HACK
+# This is really, really stupid way to do this
 def gen_nc_input_for_data(socket):
     # Class List #TODO deduplicate
     from . import xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers, math_containers, schema_containers
+    from .internal_containers import NoOpNode
     classes = {}
     for module in [xForm_containers, link_containers, misc_containers, primitives_containers, deformer_containers, math_containers, schema_containers]:
         for cls in module.TellClasses():
@@ -787,8 +676,8 @@ def gen_nc_input_for_data(socket):
     socket_class_map = {
                         "MatrixSocket"                         : classes["InputMatrix"],
                         "xFormSocket"                          : None,
-                        "RelationshipSocket"                   : classes["xFormRoot"], # world in
-                        "DeformerSocket"                       : classes["xFormRoot"], # world in
+                        "RelationshipSocket"                   : NoOpNode,
+                        "DeformerSocket"                       : NoOpNode,
                         "GeometrySocket"                       : classes["InputExistingGeometryData"],
                         "EnableSocket"                         : classes["InputBoolean"],
                         "HideSocket"                           : classes["InputBoolean"],
@@ -797,8 +686,6 @@ def gen_nc_input_for_data(socket):
                         "DriverVariableSocket"                 : None, 
                         "FCurveSocket"                         : None, 
                         "KeyframeSocket"                       : None,
-                        # "LayerMaskInputSocket"               : classes["InputLayerMask"],
-                        # "LayerMaskSocket"                    : classes["InputLayerMask"],
                         "BoneCollectionSocket"                 : classes["InputString"],
                         #
                         "xFormParameterSocket"                 : None,
@@ -812,8 +699,8 @@ def gen_nc_input_for_data(socket):
                         "BooleanSocket"                        : classes["InputBoolean"],
                         "BooleanThreeTupleSocket"              : classes["InputBooleanThreeTuple"],
                         "RotationOrderSocket"                  : classes["InputRotationOrder"],
-                        "QuaternionSocket"                     : classes["InputQuaternion"],
-                        "QuaternionSocketAA"                   : classes["InputQuaternionAA"],
+                        "QuaternionSocket"                     : None,
+                        "QuaternionSocketAA"                   : None,
                         "IntSocket"                            : classes["InputFloat"],
                         "StringSocket"                         : classes["InputString"],
                         #
@@ -831,7 +718,7 @@ def gen_nc_input_for_data(socket):
                         "EnumYScaleMode"                       : classes["InputString"],
                         "EnumXZScaleMode"                      : classes["InputString"],
                         "EnumCurveSocket"                      : classes["InputString"],
-                        "EnumMetaRigSocket"             : classes["InputString"],
+                        "EnumMetaRigSocket"                    : classes["InputString"],
                         # Deformers
                         "EnumSkinning"                         : classes["InputString"],
                         #
@@ -848,7 +735,7 @@ def gen_nc_input_for_data(socket):
                         "EnumDriverVariableEvaluationSpace"    : classes["InputString"],
                         "EnumDriverRotationMode"               : classes["InputString"],
                         "EnumDriverType"                       : classes["InputString"],
-                        "EnumKeyframeInterpTypeSocket"  : classes["InputString"],
+                        "EnumKeyframeInterpTypeSocket"         : classes["InputString"],
                         "EnumKeyframeBezierHandleTypeSocket"   : classes["InputString"],
                         # Math
                         "MathFloatOperation"                   : classes["InputString"],
@@ -987,45 +874,6 @@ def mesh_from_curve(crv, context,):
         EnsureCurveIsRibbon(crv)
         return bpy.data.meshes.new_from_object(crv)
 
-
-# def DataFromRibbon(obCrv, factorsList, context, fReport=None,):
-#     # BUG
-#     # no reasonable results if input is not  a ribbon
-#     import time
-#     start = time.time()
-#     """Returns a point from a u-value along a curve"""
-#     rM = MeshFromCurve(obCrv, context)
-#     ribbons = f_mesh.DetectRibbons(rM, fReport= fReport)
-#     for ribbon in ribbons:
-#         # could be improved, this will do a rotation for every ribbon
-#         # if even one is a circle
-#         if (ribbon[2]) == True:
-#             # could be a better implementation
-#             dupeCrv = obCrv.copy()
-#             dupeCrv.data = obCrv.data.copy()
-#             dupeCrv.data.extrude = 0
-#             dupeCrv.data.bevel_depth = 0 
-#             wM = MeshFromCurve(dupeCrv, context)
-#             wires = f_mesh.DetectWireEdges(wM)
-#             bpy.data.curves.remove(dupeCrv.data) #removes the object, too
-#             ribbonsNew = []
-#             for ribbon, wire in zip(ribbons, wires):
-#                 if (ribbon[2] == True): #if it's a circle
-#                     rNew = f_mesh.RotateRibbonToMatchWire(ribbon, rM, wire, wM)
-#                 else:
-#                     rNew = ribbon
-#                 ribbonsNew.append( rNew )
-#             ribbons = ribbonsNew
-#             break
-#     data = f_mesh.DataFromRibbon(rM, factorsList, obCrv.matrix_world, ribbons=ribbons, fReport=fReport)
-#     bpy.data.meshes.remove(rM)
-#     print ("time elapsed: ", time.time() - start)
-#     #expects data...
-#     # if ()
-
-
-#     return data
-
 def DetectRibbon(f, bm, skipMe):
     fFirst = f.index
     cont = True

+ 1 - 119
xForm_containers.py

@@ -6,7 +6,6 @@ def TellClasses():
              
     return [ 
              # xForm
-             xFormRoot,
              xFormArmature,
              xFormBone,
              xFormGeometryObject,
@@ -16,50 +15,6 @@ def TellClasses():
 # X - F O R M   N O D E S
 #*#-------------------------------#++#-------------------------------#*#
 
-# class xFormNull:
-    # '''A node representing an Empty object'''
-    # inputs =
-    # {
-     # "Name":None,
-     # "Rotation Order":None,
-     # "Matrix":None,
-     # "Relationship":None,
-    # }
-    # outputs =
-    # {
-     # "xFormOut":None,
-    # }
-    # parameters =
-    # {
-     # "Name":None,
-     # "Rotation Order":None,
-     # "Matrix":None,
-     # "Relationship":None,
-    # }
-    
-    # def evaluate_input(self, input):
-        # pass
-    
-    # def instantiate_blender_object(self):
-        # pass
-
-class xFormRoot:
-    '''A node representing the root of the scene.'''
-    
-    def __init__(self, signature, base_tree):
-        self.base_tree=base_tree
-        self.signature = signature
-        self.inputs = {}
-        self.outputs = {"World Out":NodeSocket(name="World Out", node = self),}
-        self.parameters = {}
-        self.node_type = 'XFORM'
-        self.hierarchy_connections = []
-        self.connections = []
-        self.hierarchy_dependencies = []
-        self.dependencies = []
-        self.prepared = True
-        self.executed = True
-
 
 class xFormArmature:
     '''A node representing an armature object'''
@@ -147,12 +102,6 @@ class xFormArmature:
         ob.matrix_world = matrix.copy()
         ob.data.pose_position = 'REST'
         
-        # # first, get the parent object
-        # parent_node = get_parent(self)
-        # if hasattr(parent_node, "bObject"):
-            # # this won't work of course, TODO
-            # ob.parent = parent_node.bObject
-            # # print (self.bObject)
         if True:
             from bpy.types import EditBone
             parent_nc = get_parent(self, type='LINK')
@@ -195,16 +144,6 @@ class xFormArmature:
         
         self.executed = True
     
-    # # not used yet
-    # #
-    # def bFinalize(self, bContext = None):
-        # import bpy
-        # ob = self.bGetObject()
-        # prevAct = bContext.view_layer.objects.active
-        # bContext.view_layer.objects.active = ob
-        # bpy.ops.object.mode_set(mode='OBJECT')
-        # print ("Changing Armature Mode to OBJECT")
-        # bContext.view_layer.objects.active = prevAct
 
     def bGetObject(self, mode = ''):
         import bpy; return bpy.data.objects[self.bObject]
@@ -772,64 +711,7 @@ class xFormBone:
             except AttributeError:
                 ob=None
             if type(ob) in [bpy.types.Object]:
-                pb.custom_shape = ob
-        #
-        # pb.bone.hide = self.evaluate_input("Hide")
-        # pb.custom_shape_scale_xyz = self.evaluate_input("Custom Object Scale")
-        # pb.custom_shape_translation = self.evaluate_input("Custom Object Translation")
-        # pb.custom_shape_rotation_euler = self.evaluate_input("Custom Object Rotation")
-        # pb.use_custom_shape_bone_size = self.evaluate_input("Custom Object Scale to Bone Length")
-        # pb.bone.show_wire = self.evaluate_input("Custom Object Wireframe")
-
-
-        # #
-        # # D E P R E C A T E D
-        # #
-        # # Bone Groups
-        # if bg_name := self.evaluate_input("Bone Group"): # this is a string
-        #     obArm = self.bGetParentArmature()
-        #     # Temporary! Temporary! HACK
-        #     color_set_items= [
-        #                        "DEFAULT",
-        #                        "THEME01",
-        #                        "THEME02",
-        #                        "THEME03",
-        #                        "THEME04",
-        #                        "THEME05",
-        #                        "THEME06",
-        #                        "THEME07",
-        #                        "THEME08",
-        #                        "THEME09",
-        #                        "THEME10",
-        #                        "THEME11",
-        #                        "THEME12",
-        #                        "THEME13",
-        #                        "THEME14",
-        #                        "THEME15",
-        #                        "THEME16",
-        #                        "THEME17",
-        #                        "THEME18",
-        #                        "THEME19",
-        #                        "THEME20",
-        #                        # "CUSTOM",
-        #                      ]
-        #     try:
-        #         bg = obArm.pose.bone_groups.get(bg_name)
-        #     except SystemError:
-        #         bg = None
-        #         pass # no clue why this happens. uninitialzied?
-        #     if not bg:
-        #         bg = obArm.pose.bone_groups.new(name=bg_name)
-        #         #HACK lol
-        #         from random import randint
-        #         bg.color_set = color_set_items[randint(0,14)]
-        #         #15-20 are black by default, gross
-        #         # this is good enough for now!
-            
-        #     pb.bone_group = bg
-            
-        
-        
+                pb.custom_shape = ob 
 
     def bGetObject(self, mode = 'POSE'):
         if mode in ["POSE", "OBJECT"] and self.bGetParentArmature().mode == "EDIT":

+ 2 - 24
xForm_definitions.py

@@ -12,7 +12,6 @@ def TellClasses():
     return [
         # xFormNullNode,
         xFormBoneNode,
-        xFormRootNode,
         xFormArmatureNode,
         xFormGeometryObjectNode,
         ]
@@ -56,18 +55,6 @@ def check_if_connected(start, end, line):
         return False
     return True
 
-class xFormRootNode(Node, xFormNode):
-    '''A node representing the world node'''
-    bl_idname = 'xFormRootNode'
-    bl_label = "World Root"
-    bl_icon = 'WORLD'
-    initialized : bpy.props.BoolProperty(default = False)
-
-    def init(self, context):
-        self.outputs.new('RelationshipSocket', "World Out")
-        self.initialized=True
-
-
 
 # I had chat gpt flip these so they may be a little innacurate
 # always visible
@@ -225,7 +212,8 @@ class xFormBoneNode(Node, xFormNode):
         # return
         layout.operator("mantis.add_custom_property", text='+Add Custom Parameter')
         # layout.label(text="Edit Parameter ... not implemented")
-        if (len(self.inputs) >= self.socket_count):
+        if (len(self.inputs) > self.socket_count):
+            layout.operator("mantis.edit_custom_property", text=' Edit Custom Parameter')
             layout.operator("mantis.remove_custom_property", text='-Remove Custom Parameter')
         else:
             layout.label(text="")
@@ -308,16 +296,6 @@ class xFormBoneNode(Node, xFormNode):
             for name in bbone_names.keys():
                 if name in ['BBone Segments']: continue
                 self.inputs[name].hide = not self.display_bb_settings
-            
-
-    # def update(self):
-    #     self.display_update()
-
-        
-    
-    # def copy(ectype, archtype):
-        # # TODO: automatically avoid duplicating names
-        # ectype.inputs["Name"].default_value = ""
 
 
 class xFormArmatureNode(Node, xFormNode):