Node callback for handling link insertion and swapping of occupied inputs.
authorLukas Tönne <lukas.toenne@gmail.com>
Thu, 3 Dec 2015 11:51:29 +0000 (12:51 +0100)
committerLukas Tönne <lukas.toenne@gmail.com>
Thu, 3 Dec 2015 12:04:04 +0000 (13:04 +0100)
Nodes have a feature for moving existing links to unoccupied sockets when connecting
to an already used input. This is based on the standard legacy socket types (value/float,
vector, color/rgba) and works reasonably well for shader, compositor and texture nodes.

For new pynode systems, however, the hardcoded nature of that feature has major drawbacks:
* It does not take different type systems into account, leading to meaningless connections
  when sockets are swapped and making the feature useless or outright debilitating.
* Advanced socket behaviors would be possible with a registerable callback, e.g. creating
  extensible input lists that move existing connections down to make room for a new link.

Now any handling of new links is done via the 'insert_links' callback, which can also be
registered through the RNA API. For the legacy shader/compo/tex nodes the behavior is the
same, using a C callback.

Note on the 'use_swap' flag: this has been removed because it was meaningless anyway:
It was disabled only for the insert-node-on-link feature, which works only for
completely unconnected nodes anyway, so there would be nothing to swap in the first place.

source/blender/blenkernel/BKE_node.h
source/blender/editors/space_node/node_relationships.c
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/composite/node_composite_util.c
source/blender/nodes/composite/nodes/node_composite_common.c
source/blender/nodes/intern/node_util.c
source/blender/nodes/intern/node_util.h
source/blender/nodes/shader/node_shader_util.c
source/blender/nodes/shader/nodes/node_shader_common.c
source/blender/nodes/texture/node_texture_util.c
source/blender/nodes/texture/nodes/node_texture_common.c

index 08a55036a039e4c9302b80057e5879109f6f55eb..5d03a42d118f96f83550230044ece676b5419ce4 100644 (file)
@@ -206,6 +206,8 @@ typedef struct bNodeType {
        /* can this node be added to a node tree */
        int (*poll_instance)(struct bNode *node, struct bNodeTree *nodetree);
        
+       /* optional handling of link insertion */
+       void (*insert_link)(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link);
        /* Update the internal links list, for muting and disconnect operators. */
        void (*update_internal_links)(struct bNodeTree *, struct bNode *node);
        
index 8a808b2ae9d61739a8d885aa3b334ce90f1938d9..f58cfe55f34a6a6f2bfefca04314c94f07003523 100644 (file)
@@ -29,8 +29,6 @@
  *  \ingroup spnode
  */
 
-#include <ctype.h>
-
 #include "MEM_guardedalloc.h"
 
 #include "DNA_node_types.h"
@@ -420,7 +418,6 @@ static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag))
 #undef HEADER_LENGTH
 }
 
-/* update link_count fields to avoid repeated link counting */
 static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
 {
        bNodeLink *link;
@@ -434,64 +431,13 @@ static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
        return count;
 }
 
-/* test if two sockets are interchangeable
- * XXX this could be made into a tree-type callback for flexibility
- */
-static bool node_link_socket_match(bNodeSocket *a, bNodeSocket *b)
-{
-       /* tests if alphabetic prefix matches
-        * this allows for imperfect matches, such as numeric suffixes,
-        * like Color1/Color2
-        */
-       int prefix_len = 0;
-       char *ca = a->name, *cb = b->name;
-       for (; *ca != '\0' && *cb != '\0'; ++ca, ++cb) {
-               /* end of common prefix? */
-               if (*ca != *cb) {
-                       /* prefix delimited by non-alphabetic char */
-                       if (isalpha(*ca) || isalpha(*cb))
-                               return false;
-                       break;
-               }
-               ++prefix_len;
-       }
-       return prefix_len > 0;
-}
-
-/* find an eligible socket for linking */
-static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNodeSocket *cur, bool use_swap)
-{
-       int cur_link_count = node_count_links(ntree, cur);
-       if (cur_link_count <= cur->limit) {
-               /* current socket is fine, use it */
-               return cur;
-       }
-       else if (use_swap) {
-               /* link swapping: try to find a free slot with a matching name */
-               
-               bNodeSocket *first = cur->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
-               bNodeSocket *sock;
-               
-               sock = cur->next ? cur->next : first; /* wrap around the list end */
-               while (sock != cur) {
-                       if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
-                               int link_count = node_count_links(ntree, sock);
-                               /* take +1 into account since we would add a new link */
-                               if (link_count + 1 <= sock->limit)
-                                       return sock; /* found a valid free socket we can swap to */
-                       }
-                       
-                       sock = sock->next ? sock->next : first; /* wrap around the list end */
-               }
-       }
-       return NULL;
-}
-
-static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link, bool use_swap)
+static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link)
 {
        bNodeTree *ntree = snode->edittree;
        bNodeSocket *from = link->fromsock, *to = link->tosock;
        bNodeLink *tlink, *tlink_next;
+       int to_count = node_count_links(ntree, to);
+       int from_count = node_count_links(ntree, from);
        
        for (tlink = ntree->links.first; tlink; tlink = tlink_next) {
                tlink_next = tlink->next;
@@ -499,28 +445,18 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link, bool use_
                        continue;
                
                if (tlink && tlink->fromsock == from) {
-                       bNodeSocket *new_from = node_find_linkable_socket(ntree, tlink->fromnode, from, use_swap);
-                       if (new_from && new_from != from) {
-                               /* redirect existing link */
-                               tlink->fromsock = new_from;
-                       }
-                       else if (!new_from) {
-                               /* no possible replacement, remove tlink */
+                       if (from_count > from->limit) {
                                nodeRemLink(ntree, tlink);
                                tlink = NULL;
+                               --from_count;
                        }
                }
                
                if (tlink && tlink->tosock == to) {
-                       bNodeSocket *new_to = node_find_linkable_socket(ntree, tlink->tonode, to, use_swap);
-                       if (new_to && new_to != to) {
-                               /* redirect existing link */
-                               tlink->tosock = new_to;
-                       }
-                       else if (!new_to) {
-                               /* no possible replacement, remove tlink */
+                       if (to_count > to->limit) {
                                nodeRemLink(ntree, tlink);
                                tlink = NULL;
+                               --to_count;
                        }
                }
        }
@@ -537,6 +473,14 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
                bNodeLink *link = linkdata->data;
                
                if (apply_links && link->tosock && link->fromsock) {
+                       /* before actually adding the link,
+                        * let nodes perform special link insertion handling
+                        */
+                       if (link->fromnode->typeinfo->insert_link)
+                               link->fromnode->typeinfo->insert_link(ntree, link->fromnode, link);
+                       if (link->tonode->typeinfo->insert_link)
+                               link->tonode->typeinfo->insert_link(ntree, link->tonode, link);
+                       
                        /* add link to the node tree */
                        BLI_addtail(&ntree->links, link);
                        
@@ -546,7 +490,7 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
                        link->tonode->update |= NODE_UPDATE;
                        
                        /* we might need to remove a link */
-                       node_remove_extra_links(snode, link, true);
+                       node_remove_extra_links(snode, link);
                }
                else
                        nodeRemLink(ntree, link);
@@ -1746,7 +1690,7 @@ void ED_node_link_insert(ScrArea *sa)
                        
                        link->tonode = select;
                        link->tosock = best_input;
-                       node_remove_extra_links(snode, link, false);
+                       node_remove_extra_links(snode, link);
                        link->flag &= ~NODE_LINKFLAG_HILITE;
                        
                        nodeAddLink(snode->edittree, select, best_output, node, sockto);
index e7e8f6d6121d39773e134f4209b9c43179ffefb6..51592ec5696f4ea891229d16f1a5be021e5463eb 100644 (file)
@@ -1182,6 +1182,24 @@ static void rna_Node_update_reg(bNodeTree *ntree, bNode *node)
        RNA_parameter_list_free(&list);
 }
 
+static void rna_Node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
+{
+       extern FunctionRNA rna_Node_insert_link_func;
+
+       PointerRNA ptr;
+       ParameterList list;
+       FunctionRNA *func;
+
+       RNA_pointer_create((ID *)ntree, node->typeinfo->ext.srna, node, &ptr);
+       func = &rna_Node_insert_link_func;
+
+       RNA_parameter_list_create(&list, &ptr, func);
+       RNA_parameter_set_lookup(&list, "link", &link);
+       node->typeinfo->ext.call(NULL, &ptr, func, &list);
+
+       RNA_parameter_list_free(&list);
+}
+
 static void rna_Node_init(const bContext *C, PointerRNA *ptr)
 {
        extern FunctionRNA rna_Node_init_func;
@@ -1384,12 +1402,13 @@ static bNodeType *rna_Node_register_base(Main *bmain, ReportList *reports, Struc
        nt->poll = (have_function[0]) ? rna_Node_poll : NULL;
        nt->poll_instance = (have_function[1]) ? rna_Node_poll_instance : rna_Node_poll_instance_default;
        nt->updatefunc = (have_function[2]) ? rna_Node_update_reg : NULL;
-       nt->initfunc_api = (have_function[3]) ? rna_Node_init : NULL;
-       nt->copyfunc_api = (have_function[4]) ? rna_Node_copy : NULL;
-       nt->freefunc_api = (have_function[5]) ? rna_Node_free : NULL;
-       nt->draw_buttons = (have_function[6]) ? rna_Node_draw_buttons : NULL;
-       nt->draw_buttons_ex = (have_function[7]) ? rna_Node_draw_buttons_ext : NULL;
-       nt->labelfunc = (have_function[8]) ? rna_Node_draw_label : NULL;
+       nt->insert_link = (have_function[3]) ? rna_Node_insert_link : NULL;
+       nt->initfunc_api = (have_function[4]) ? rna_Node_init : NULL;
+       nt->copyfunc_api = (have_function[5]) ? rna_Node_copy : NULL;
+       nt->freefunc_api = (have_function[6]) ? rna_Node_free : NULL;
+       nt->draw_buttons = (have_function[7]) ? rna_Node_draw_buttons : NULL;
+       nt->draw_buttons_ex = (have_function[8]) ? rna_Node_draw_buttons_ext : NULL;
+       nt->labelfunc = (have_function[9]) ? rna_Node_draw_label : NULL;
        
        /* sanitize size values in case not all have been registered */
        if (nt->maxwidth < nt->minwidth)
@@ -7706,6 +7725,13 @@ static void rna_def_node(BlenderRNA *brna)
        RNA_def_function_ui_description(func, "Update on editor changes");
        RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE);
        
+       /* insert_link */
+       func = RNA_def_function(srna, "insert_link", NULL);
+       RNA_def_function_ui_description(func, "Handle creation of a link to or from the node");
+       RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_REGISTER_OPTIONAL);
+       parm = RNA_def_pointer(func, "link", "NodeLink", "Link", "Node link that will be inserted");
+       RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+       
        /* init */
        func = RNA_def_function(srna, "init", NULL);
        RNA_def_function_ui_description(func, "Initialize a new instance of this node");
index bbfb07a316d8f3fff2fa4fede5ae3ce47c34f0d0..c6b1d37f8b4c233822c6d2b7fcb15815f55f61e0 100644 (file)
@@ -55,5 +55,6 @@ void cmp_node_type_base(bNodeType *ntype, int type, const char *name, short ncla
        
        ntype->poll = cmp_node_poll_default;
        ntype->updatefunc = cmp_node_update_default;
+       ntype->insert_link = node_insert_link_default;
        ntype->update_internal_links = node_update_internal_links_default;
 }
index 75e7fa8fbaceade78abf3850c966970e55084872..0fbbb54ee7ac9ac7e3c175826467f522223abf34 100644 (file)
@@ -51,6 +51,7 @@ void register_node_type_cmp_group(void)
        ntype.type = NODE_GROUP;
        ntype.poll = cmp_node_poll_default;
        ntype.poll_instance = node_group_poll_instance;
+       ntype.insert_link = node_insert_link_default;
        ntype.update_internal_links = node_update_internal_links_default;
        ntype.ext.srna = RNA_struct_find("CompositorNodeGroup");
        BLI_assert(ntype.ext.srna != NULL);
index 3a301f55487f1bbb93049de16ee781d55122f65c..dd5715891d5a06c74391e75cfd06e0ee3b803370 100644 (file)
@@ -29,6 +29,7 @@
  *  \ingroup nodes
  */
 
+#include <ctype.h>
 #include <limits.h>
 #include <string.h>
 
@@ -112,6 +113,95 @@ void node_filter_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int m
 }
 
 
+/*** Link Insertion ***/
+
+/* test if two sockets are interchangeable */
+static bool node_link_socket_match(bNodeSocket *a, bNodeSocket *b)
+{
+       /* tests if alphabetic prefix matches
+        * this allows for imperfect matches, such as numeric suffixes,
+        * like Color1/Color2
+        */
+       int prefix_len = 0;
+       char *ca = a->name, *cb = b->name;
+       for (; *ca != '\0' && *cb != '\0'; ++ca, ++cb) {
+               /* end of common prefix? */
+               if (*ca != *cb) {
+                       /* prefix delimited by non-alphabetic char */
+                       if (isalpha(*ca) || isalpha(*cb))
+                               return false;
+                       break;
+               }
+               ++prefix_len;
+       }
+       return prefix_len > 0;
+}
+
+static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
+{
+       bNodeLink *link;
+       int count = 0;
+       for (link = ntree->links.first; link; link = link->next) {
+               if (link->fromsock == sock)
+                       ++count;
+               if (link->tosock == sock)
+                       ++count;
+       }
+       return count;
+}
+
+/* find an eligible socket for linking */
+static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNodeSocket *cur)
+{
+       /* link swapping: try to find a free slot with a matching name */
+       
+       bNodeSocket *first = cur->in_out == SOCK_IN ? node->inputs.first : node->outputs.first;
+       bNodeSocket *sock;
+       
+       sock = cur->next ? cur->next : first; /* wrap around the list end */
+       while (sock != cur) {
+               if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) {
+                       int link_count = node_count_links(ntree, sock);
+                       /* take +1 into account since we would add a new link */
+                       if (link_count + 1 <= sock->limit)
+                               return sock; /* found a valid free socket we can swap to */
+               }
+               
+               sock = sock->next ? sock->next : first; /* wrap around the list end */
+       }
+       return NULL;
+}
+
+void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link)
+{
+       bNodeSocket *sock = link->tosock;
+       bNodeLink *tlink, *tlink_next;
+       
+       /* inputs can have one link only, outputs can have unlimited links */
+       if (node != link->tonode)
+               return;
+       
+       for (tlink = ntree->links.first; tlink; tlink = tlink_next) {
+               bNodeSocket *new_sock;
+               tlink_next = tlink->next;
+               
+               if (sock != tlink->tosock)
+                       continue;
+               
+               new_sock = node_find_linkable_socket(ntree, node, sock);
+               if (new_sock && new_sock != sock) {
+                       /* redirect existing link */
+                       tlink->tosock = new_sock;
+               }
+               else if (!new_sock) {
+                       /* no possible replacement, remove tlink */
+                       nodeRemLink(ntree, tlink);
+                       tlink = NULL;
+               }
+       }
+}
+
+
 /**** Internal Links (mute and disconnect) ****/
 
 /* common datatype priorities, works for compositor, shader and texture nodes alike
index 64b2028874b6f7032d90d8cb4a098c2da96f5d52..2e20a8e79d41967de16f7763456cfa3919739664 100644 (file)
@@ -76,6 +76,9 @@ void node_math_label(struct bNodeTree *ntree, struct bNode *node, char *label, i
 void node_vect_math_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen);
 void node_filter_label(struct bNodeTree *ntree, struct bNode *node, char *label, int maxlen);
 
+
+/*** Link Handling */
+void node_insert_link_default(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link);
 void node_update_internal_links_default(struct bNodeTree *ntree, struct bNode *node);
 
 float node_socket_get_float(struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *sock);
index 92b244bae55c5e0d5e9bf89312cb3925c0cb0caa..9bd43f331fbf4a4f40de1a89774d75e82eced1e3 100644 (file)
@@ -47,6 +47,7 @@ void sh_node_type_base(struct bNodeType *ntype, int type, const char *name, shor
        node_type_base(ntype, type, name, nclass, flag);
        
        ntype->poll = sh_node_poll_default;
+       ntype->insert_link = node_insert_link_default;
        ntype->update_internal_links = node_update_internal_links_default;
 }
 
index 7ff60ac716a7ac044e96cfff1f3075695e8d4662..796193a564e0ad630a3ef58f2af3eb1186bb03e9 100644 (file)
@@ -236,6 +236,7 @@ void register_node_type_sh_group(void)
        ntype.type = NODE_GROUP;
        ntype.poll = sh_node_poll_default;
        ntype.poll_instance = node_group_poll_instance;
+       ntype.insert_link = node_insert_link_default;
        ntype.update_internal_links = node_update_internal_links_default;
        ntype.ext.srna = RNA_struct_find("ShaderNodeGroup");
        BLI_assert(ntype.ext.srna != NULL);
index 42c684b8247c459d03c57d2abd1db2356ce5d0b5..32720364f73da4e9ed6f179cf742f0a47d23c8b5 100644 (file)
@@ -60,6 +60,7 @@ void tex_node_type_base(struct bNodeType *ntype, int type, const char *name, sho
        node_type_base(ntype, type, name, nclass, flag);
        
        ntype->poll = tex_node_poll_default;
+       ntype->insert_link = node_insert_link_default;
        ntype->update_internal_links = node_update_internal_links_default;
 }
 
index 914f1ef5110513e27f983917983223e19a09c703..79a4c4e3a6b228826797d9b91e6d55ed79f4daf2 100644 (file)
@@ -166,6 +166,7 @@ void register_node_type_tex_group(void)
        ntype.type = NODE_GROUP;
        ntype.poll = tex_node_poll_default;
        ntype.poll_instance = node_group_poll_instance;
+       ntype.insert_link = node_insert_link_default;
        ntype.update_internal_links = node_update_internal_links_default;
        ntype.ext.srna = RNA_struct_find("TextureNodeGroup");
        BLI_assert(ntype.ext.srna != NULL);