From 7f759ec547a563284f29f7a80b96316854b37ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Thu, 3 Dec 2015 12:51:29 +0100 Subject: [PATCH] Node callback for handling link insertion and swapping of occupied inputs. 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 | 2 + .../editors/space_node/node_relationships.c | 90 ++++--------------- source/blender/makesrna/intern/rna_nodetree.c | 38 ++++++-- .../nodes/composite/node_composite_util.c | 1 + .../composite/nodes/node_composite_common.c | 1 + source/blender/nodes/intern/node_util.c | 90 +++++++++++++++++++ source/blender/nodes/intern/node_util.h | 3 + .../blender/nodes/shader/node_shader_util.c | 1 + .../nodes/shader/nodes/node_shader_common.c | 1 + .../blender/nodes/texture/node_texture_util.c | 1 + .../nodes/texture/nodes/node_texture_common.c | 1 + 11 files changed, 150 insertions(+), 79 deletions(-) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 08a55036a03..5d03a42d118 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -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); diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 8a808b2ae9d..f58cfe55f34 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -29,8 +29,6 @@ * \ingroup spnode */ -#include - #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); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index e7e8f6d6121..51592ec5696 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -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"); diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.c index bbfb07a316d..c6b1d37f8b4 100644 --- a/source/blender/nodes/composite/node_composite_util.c +++ b/source/blender/nodes/composite/node_composite_util.c @@ -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; } diff --git a/source/blender/nodes/composite/nodes/node_composite_common.c b/source/blender/nodes/composite/nodes/node_composite_common.c index 75e7fa8fbac..0fbbb54ee7a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_common.c +++ b/source/blender/nodes/composite/nodes/node_composite_common.c @@ -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); diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 3a301f55487..dd5715891d5 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -29,6 +29,7 @@ * \ingroup nodes */ +#include #include #include @@ -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 diff --git a/source/blender/nodes/intern/node_util.h b/source/blender/nodes/intern/node_util.h index 64b2028874b..2e20a8e79d4 100644 --- a/source/blender/nodes/intern/node_util.h +++ b/source/blender/nodes/intern/node_util.h @@ -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); diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c index 92b244bae55..9bd43f331fb 100644 --- a/source/blender/nodes/shader/node_shader_util.c +++ b/source/blender/nodes/shader/node_shader_util.c @@ -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; } diff --git a/source/blender/nodes/shader/nodes/node_shader_common.c b/source/blender/nodes/shader/nodes/node_shader_common.c index 7ff60ac716a..796193a564e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_common.c +++ b/source/blender/nodes/shader/nodes/node_shader_common.c @@ -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); diff --git a/source/blender/nodes/texture/node_texture_util.c b/source/blender/nodes/texture/node_texture_util.c index 42c684b8247..32720364f73 100644 --- a/source/blender/nodes/texture/node_texture_util.c +++ b/source/blender/nodes/texture/node_texture_util.c @@ -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; } diff --git a/source/blender/nodes/texture/nodes/node_texture_common.c b/source/blender/nodes/texture/nodes/node_texture_common.c index 914f1ef5110..79a4c4e3a6b 100644 --- a/source/blender/nodes/texture/nodes/node_texture_common.c +++ b/source/blender/nodes/texture/nodes/node_texture_common.c @@ -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);