Toggle links tool for Node Editor

This commit adds a new tool, Toggle Links, to the node editor. This tool
allows the user to toggle the status (linked/not linked) between desired
sockets. The tool can be used either by using the f key or the menus.

This functionality is analogue to one found in object editing modes except
for its additional toggle functionality.

To use this tool, the user has to first select an input and an output socket.
Selecting is done by clicking with right mouse button on a socket. After the 
tool has been invoked, the link between those two sockets is toggled. The 
result may vary based on existing linkage.

There can be only one input and one output selected at maximum in a node 
tree. This means that if the user selects a socket while one of the same 
type is already selected, the old one will be deselected.

The tool complements the current way of connecting nodes. One possible use
for it is to use it to review output of nodes by using a viewer node. Just
select wanted input socket of a viewer node, set it visible and use selection 
of an output socket in conjuction with f key to show the output in the viewer
node. Select another output and hit f to see its output and so on.
This commit is contained in:
Juho Vepsalainen 2007-12-27 10:17:33 +00:00
parent 0dc38a5bd9
commit d06dc00af9
8 changed files with 222 additions and 64 deletions

@ -143,6 +143,7 @@ void nodeVerifyType(struct bNodeTree *ntree, struct bNode *node);
void nodeAddToPreview(struct bNode *, float *, int, int);
void nodeUnlinkNode(struct bNodeTree *ntree, struct bNode *node);
struct bNode *nodeAddNodeType(struct bNodeTree *ntree, int type, struct bNodeTree *ngroup);
void nodeFreeNode(struct bNodeTree *ntree, struct bNode *node);
struct bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node);
@ -150,6 +151,8 @@ struct bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node);
struct bNodeLink *nodeAddLink(struct bNodeTree *ntree, struct bNode *fromnode, struct bNodeSocket *fromsock, struct bNode *tonode, struct bNodeSocket *tosock);
void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link);
int nodeFindNode(struct bNodeTree *ntree, struct bNodeSocket *sock, struct bNode **nodep, int *sockindex);
struct bNodeLink *nodeFindLink(struct bNodeTree *ntree, struct bNodeSocket *from, struct bNodeSocket *to);
int nodeCountSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);

@ -483,6 +483,20 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree)
BLI_addtail(&ngroup->nodes, node);
node->locx-= 0.5f*(min[0]+max[0]);
node->locy-= 0.5f*(min[1]+max[1]);
/* set selin and selout of the nodetree */
for(sock= node->inputs.first; sock; sock= sock->next) {
if(sock->flag & SOCK_SEL) {
ngroup->selin= sock;
break;
}
}
for(sock= node->outputs.first; sock; sock= sock->next) {
if(sock->flag & SOCK_SEL) {
ngroup->selout= sock;
break;
}
}
}
}
@ -653,7 +667,8 @@ void nodeGroupSocketUseFlags(bNodeTree *ngroup)
}
}
static void find_node_with_socket(bNodeTree *ntree, bNodeSocket *sock, bNode **nodep, int *sockindex)
/* finds a node based on given socket */
int nodeFindNode(bNodeTree *ntree, bNodeSocket *sock, bNode **nodep, int *sockindex)
{
bNode *node;
bNodeSocket *tsock;
@ -671,13 +686,15 @@ static void find_node_with_socket(bNodeTree *ntree, bNodeSocket *sock, bNode **n
if(tsock)
break;
}
if(node) {
*nodep= node;
*sockindex= index;
}
else {
*nodep= NULL;
if(sockindex) *sockindex= index;
return 1;
}
*nodep= NULL;
return 0;
}
/* returns 1 if its OK */
@ -717,7 +734,7 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode)
for(link= ntree->links.first; link; link= link->next) {
if(link->tonode==gnode) {
/* link->tosock->tosock is on the node we look for */
find_node_with_socket(ngroup, link->tosock->tosock, &nextn, &index);
nodeFindNode(ngroup, link->tosock->tosock, &nextn, &index);
if(nextn==NULL) printf("wrong stuff!\n");
else if(nextn->new_node==NULL) printf("wrong stuff too!\n");
else {
@ -727,7 +744,7 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode)
}
else if(link->fromnode==gnode) {
/* link->fromsock->tosock is on the node we look for */
find_node_with_socket(ngroup, link->fromsock->tosock, &nextn, &index);
nodeFindNode(ngroup, link->fromsock->tosock, &nextn, &index);
if(nextn==NULL) printf("1 wrong stuff!\n");
else if(nextn->new_node==NULL) printf("1 wrong stuff too!\n");
else {
@ -898,6 +915,28 @@ bNodeTree *ntreeCopyTree(bNodeTree *ntree, int internal_select)
nnode->flag |= NODE_SELECT;
}
node->flag &= ~NODE_ACTIVE;
/* deselect original sockets */
for(sock= node->inputs.first; sock; sock= sock->next) {
if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
}
for(sock= node->outputs.first; sock; sock= sock->next) {
if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
}
/* set tree selin and selout to new sockets */
for(sock= nnode->inputs.first; sock; sock= sock->next) {
if(sock->flag & SOCK_SEL) {
ntree->selin= sock;
break;
}
}
for(sock= nnode->outputs.first; sock; sock= sock->next) {
if(sock->flag & SOCK_SEL) {
ntree->selout= sock;
break;
}
}
}
if(node==last) break;
}
@ -941,7 +980,7 @@ bNodeTree *ntreeCopyTree(bNodeTree *ntree, int internal_select)
/* ************** Free stuff ********** */
/* goes over entire tree */
static void node_unlink_node(bNodeTree *ntree, bNode *node)
void nodeUnlinkNode(bNodeTree *ntree, bNode *node)
{
bNodeLink *link, *next;
bNodeSocket *sock;
@ -985,7 +1024,7 @@ static void composit_free_node_cache(bNode *node)
void nodeFreeNode(bNodeTree *ntree, bNode *node)
{
node_unlink_node(ntree, node);
nodeUnlinkNode(ntree, node);
BLI_remlink(&ntree->nodes, node);
/* since it is called while free database, node->id is undefined */

@ -72,6 +72,7 @@ void snode_make_group_editable(struct SpaceNode *snode, struct bNode *gnode);
void node_hide(struct SpaceNode *snode);
void node_read_renderlayers(struct SpaceNode *snode);
void clear_scene_in_nodes(struct Scene *sce);
void node_toggle_link(struct SpaceNode *snode);
void node_transform_ext(int mode, int unused);
void node_shader_default(struct Material *ma);

@ -92,7 +92,8 @@ typedef struct bNodeSocket {
#define SOCK_IN_USE 4
/* unavailable is for dynamic sockets */
#define SOCK_UNAVAIL 8
/* flag for selection status */
#define SOCK_SEL 16
#
#
typedef struct bNodePreview {
@ -167,6 +168,10 @@ typedef struct bNodeTree {
ListBase alltypes; /* type definitions */
struct bNodeType *owntype; /* for groups or dynamic trees, no read/write */
/* selected input/output socket */
bNodeSocket *selin;
bNodeSocket *selout;
/* callbacks */
void (*timecursor)(int nr);
void (*stats_draw)(char *str);

@ -1988,7 +1988,8 @@ static void draw_nodespace_back(ScrArea *sa, SpaceNode *snode)
}
/* nice AA filled circle */
static void socket_circle_draw(float x, float y, float size, int type, int select)
/* this might have some more generic use */
static void circle_draw(float x, float y, float size, int type, int col[3])
{
/* 16 values of sin function */
static float si[16] = {
@ -2006,28 +2007,7 @@ static void socket_circle_draw(float x, float y, float size, int type, int selec
};
int a;
if(select==0) {
if(type==-1)
glColor3ub(0, 0, 0);
else if(type==SOCK_VALUE)
glColor3ub(160, 160, 160);
else if(type==SOCK_VECTOR)
glColor3ub(100, 100, 200);
else if(type==SOCK_RGBA)
glColor3ub(200, 200, 40);
else
glColor3ub(100, 200, 100);
}
else {
if(type==SOCK_VALUE)
glColor3ub(200, 200, 200);
else if(type==SOCK_VECTOR)
glColor3ub(140, 140, 240);
else if(type==SOCK_RGBA)
glColor3ub(240, 240, 100);
else
glColor3ub(140, 240, 140);
}
glColor3ub(col[0], col[1], col[2]);
glBegin(GL_POLYGON);
for(a=0; a<16; a++)
@ -2045,6 +2025,41 @@ static void socket_circle_draw(float x, float y, float size, int type, int selec
glDisable(GL_BLEND);
}
static void socket_circle_draw(bNodeSocket *sock, float size)
{
int col[3];
/* choose color based on sock flags */
if(sock->flag & SELECT) {
if(sock->flag & SOCK_SEL) {
col[0]= 240; col[1]= 200; col[2]= 40;}
else if(sock->type==SOCK_VALUE) {
col[0]= 200; col[1]= 200; col[2]= 200;}
else if(sock->type==SOCK_VECTOR) {
col[0]= 140; col[1]= 140; col[2]= 240;}
else if(sock->type==SOCK_RGBA) {
col[0]= 240; col[1]= 240; col[2]= 100;}
else {
col[0]= 140; col[1]= 240; col[2]= 140;}
}
else if(sock->flag & SOCK_SEL) {
col[0]= 200; col[1]= 160; col[2]= 0;}
else {
if(sock->type==-1) {
col[0]= 0; col[1]= 0; col[2]= 0;}
else if(sock->type==SOCK_VALUE) {
col[0]= 160; col[1]= 160; col[2]= 160;}
else if(sock->type==SOCK_VECTOR) {
col[0]= 100; col[1]= 100; col[2]= 200;}
else if(sock->type==SOCK_RGBA) {
col[0]= 200; col[1]= 200; col[2]= 40;}
else {
col[0]= 100; col[1]= 200; col[2]= 100;}
}
circle_draw(sock->locx, sock->locy, size, sock->type, col);
}
/* not a callback */
static void node_draw_preview(bNodePreview *preview, rctf *prv)
{
@ -2459,7 +2474,7 @@ static void node_draw_basis(ScrArea *sa, SpaceNode *snode, bNode *node)
/* socket inputs, buttons */
for(sock= node->inputs.first; sock; sock= sock->next) {
if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
socket_circle_draw(sock, NODE_SOCKSIZE);
if(block && sock->link==NULL) {
float *butpoin= sock->ns.vec;
@ -2501,7 +2516,7 @@ static void node_draw_basis(ScrArea *sa, SpaceNode *snode, bNode *node)
/* socket outputs */
for(sock= node->outputs.first; sock; sock= sock->next) {
if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
socket_circle_draw(sock, NODE_SOCKSIZE);
BIF_ThemeColor(TH_TEXT);
ofs= 0;
@ -2589,12 +2604,12 @@ void node_draw_hidden(SpaceNode *snode, bNode *node)
/* sockets */
for(sock= node->inputs.first; sock; sock= sock->next) {
if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
socket_circle_draw(sock, NODE_SOCKSIZE);
}
for(sock= node->outputs.first; sock; sock= sock->next) {
if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
socket_circle_draw(sock, NODE_SOCKSIZE);
}
}
@ -2805,10 +2820,10 @@ static void node_draw_group(ScrArea *sa, SpaceNode *snode, bNode *gnode)
/* group sockets */
for(sock= gnode->inputs.first; sock; sock= sock->next)
if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
socket_circle_draw(sock, NODE_SOCKSIZE);
for(sock= gnode->outputs.first; sock; sock= sock->next)
if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
socket_circle_draw(sock->locx, sock->locy, NODE_SOCKSIZE, sock->type, sock->flag & SELECT);
socket_circle_draw(sock, NODE_SOCKSIZE);
/* and finally the whole tree */
node_draw_nodetree(sa, snode, ngroup);

@ -830,6 +830,23 @@ static void snode_bg_viewmove(SpaceNode *snode)
window_set_cursor(win, oldcursor);
}
static void reset_sel_socket(SpaceNode *snode, int in_out)
{
bNode *node;
bNodeSocket *sock;
for(node= snode->edittree->nodes.first; node; node= node->next) {
if(in_out & SOCK_IN) {
for(sock= node->inputs.first; sock; sock= sock->next)
if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
}
if(in_out & SOCK_OUT) {
for(sock= node->outputs.first; sock; sock= sock->next)
if(sock->flag & SOCK_SEL) sock->flag&= ~SOCK_SEL;
}
}
}
/* checks mouse position, and returns found node/socket */
/* type is SOCK_IN and/or SOCK_OUT */
static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int in_out)
@ -1568,6 +1585,33 @@ static void node_insert_convertor(SpaceNode *snode, bNodeLink *link)
#endif
static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeLink *link)
{
bNodeLink *tlink;
bNodeSocket *sock;
if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
if(link!=tlink && tlink->tosock==link->tosock)
break;
}
if(tlink) {
/* is there a free input socket with same type? */
for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) {
if(sock->type==tlink->fromsock->type)
if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit)
break;
}
if(sock)
tlink->tosock= sock;
else {
nodeRemLink(snode->edittree, tlink);
}
}
}
}
/* loop that adds a nodelink, called by function below */
/* in_out = starting socket */
static int node_add_link_drag(SpaceNode *snode, bNode *node, bNodeSocket *sock, int in_out)
@ -1638,35 +1682,12 @@ static int node_add_link_drag(SpaceNode *snode, bNode *node, bNodeSocket *sock,
nodeRemLink(snode->edittree, link);
}
else {
bNodeLink *tlink;
/* send changed events for original tonode and new */
if(link->tonode)
NodeTagChanged(snode->edittree, link->tonode);
/* we might need to remove a link */
if(in_out==SOCK_OUT) {
if(tsock && nodeCountSocketLinks(snode->edittree, link->tosock) > tsock->limit) {
for(tlink= snode->edittree->links.first; tlink; tlink= tlink->next) {
if(link!=tlink && tlink->tosock==link->tosock)
break;
}
if(tlink) {
/* is there a free input socket with same type? */
for(tsock= tlink->tonode->inputs.first; tsock; tsock= tsock->next) {
if(tsock->type==tlink->fromsock->type)
if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit)
break;
}
if(tsock)
tlink->tosock= tsock;
else {
nodeRemLink(snode->edittree, tlink);
}
}
}
}
if(in_out==SOCK_OUT) node_remove_extra_links(snode, tsock, link);
}
ntreeSolveOrder(snode->edittree);
@ -1733,10 +1754,18 @@ static int node_add_link(SpaceNode *snode)
void node_delete(SpaceNode *snode)
{
bNode *node, *next;
bNodeSocket *sock;
for(node= snode->edittree->nodes.first; node; node= next) {
next= node->next;
if(node->flag & SELECT) {
/* set selin and selout NULL if the sockets belong to a node to be deleted */
for(sock= node->inputs.first; sock; sock= sock->next)
if(snode->edittree->selin == sock) snode->edittree->selin= NULL;
for(sock= node->outputs.first; sock; sock= sock->next)
if(snode->edittree->selout == sock) snode->edittree->selout= NULL;
/* check id user here, nodeFreeNode is called for free dbase too */
if(node->id)
node->id->us--;
@ -1826,6 +1855,36 @@ void node_select_linked(SpaceNode *snode, int out)
allqueue(REDRAWNODE, 1);
}
void node_toggle_link(SpaceNode *snode)
{
bNode *fromnode, *tonode;
bNodeLink *remlink, *link;
bNodeSocket *outsock= snode->edittree->selout;
bNodeSocket *insock= snode->edittree->selin;
if(!insock || !outsock) return;
remlink= nodeFindLink(snode->edittree, outsock, insock);
if(remlink) nodeRemLink(snode->edittree, remlink);
else {
if(nodeFindNode(snode->edittree, outsock, &fromnode, NULL) &&
nodeFindNode(snode->edittree, insock, &tonode, NULL)) {
link= nodeAddLink(snode->edittree, fromnode, outsock, tonode, insock);
NodeTagChanged(snode->edittree, tonode);
node_remove_extra_links(snode, insock, link);
}
else return;
}
ntreeSolveOrder(snode->edittree);
snode_verify_groups(snode);
snode_handle_recalc(snode);
allqueue(REDRAWNODE, 0);
BIF_undo_push("Toggle Link");
}
static void node_border_link_delete(SpaceNode *snode)
{
rcti rect;
@ -2091,6 +2150,8 @@ static int node_uiDoBlocks(ScrArea *sa, short event)
void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
{
SpaceNode *snode= spacedata;
bNode *actnode;
bNodeSocket *actsock;
unsigned short event= evt->event;
short val= evt->val, doredraw=0, fromlib= 0;
@ -2118,7 +2179,29 @@ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
break;
case RIGHTMOUSE:
if(!node_mouse_select(snode, event))
if(find_indicated_socket(snode, &actnode, &actsock, SOCK_IN)) {
if(actsock->flag & SOCK_SEL) {
snode->edittree->selin= NULL;
actsock->flag&= ~SOCK_SEL;
}
else {
snode->edittree->selin= actsock;
reset_sel_socket(snode, SOCK_IN);
actsock->flag|= SOCK_SEL;
}
}
else if(find_indicated_socket(snode, &actnode, &actsock, SOCK_OUT)) {
if(actsock->flag & SOCK_SEL) {
snode->edittree->selout= NULL;
actsock->flag&= ~SOCK_SEL;
}
else {
snode->edittree->selout= actsock;
reset_sel_socket(snode, SOCK_OUT);
actsock->flag|= SOCK_SEL;
}
}
else if(!node_mouse_select(snode, event))
toolbox_n();
break;
@ -2194,6 +2277,9 @@ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt)
case EKEY:
snode_handle_recalc(snode);
break;
case FKEY:
node_toggle_link(snode);
break;
case GKEY:
if(fromlib) fromlib= -1;
else {

@ -513,6 +513,9 @@ static void do_node_nodemenu(void *arg, int event)
case 10: /* execute */
addqueue(curarea->win, UI_BUT_EVENT, B_NODE_TREE_EXEC);
break;
case 11: /* toggle link */
node_toggle_link(snode);
break;
}
if(fromlib==-1) error_libdata();
@ -536,6 +539,10 @@ static uiBlock *node_nodemenu(void *arg_unused)
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Duplicate|Shift D", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Delete|X", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 3, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Toggle Link|F", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 11, "");
uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Make Group|Ctrl G", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 4, "");

@ -1648,6 +1648,8 @@ static TBitem tb_node_node[]= {
{ 0, "Duplicate|Shift D", TB_SHIFT|'d', NULL},
{ 0, "Delete|X", 'x', NULL},
{ 0, "SEPR", 0, NULL},
{ 0, "Toggle Link|F", 'f', NULL},
{ 0, "SEPR", 0, NULL},
{ 0, "Make Group|Ctrl G", TB_CTRL|'g', NULL},
{ 0, "Ungroup|Alt G", TB_ALT|'g', NULL},
{ 0, "Edit Group|Tab", TB_TAB, NULL},