1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2005 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup spnode
22 */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "DNA_anim_types.h"
27 #include "DNA_node_types.h"
28
29 #include "BLI_blenlib.h"
30 #include "BLI_easing.h"
31 #include "BLI_math.h"
32
33 #include "BKE_anim_data.h"
34 #include "BKE_context.h"
35 #include "BKE_lib_id.h"
36 #include "BKE_main.h"
37 #include "BKE_node.h"
38
39 #include "ED_node.h" /* own include */
40 #include "ED_render.h"
41 #include "ED_screen.h"
42 #include "ED_util.h"
43
44 #include "RNA_access.h"
45 #include "RNA_define.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "UI_resources.h"
51 #include "UI_view2d.h"
52
53 #include "BLT_translation.h"
54
55 #include "node_intern.h" /* own include */
56
57 /* ****************** Relations helpers *********************** */
58
ntree_has_drivers(bNodeTree * ntree)59 static bool ntree_has_drivers(bNodeTree *ntree)
60 {
61 AnimData *adt = BKE_animdata_from_id(&ntree->id);
62 if (adt == NULL) {
63 return false;
64 }
65 return !BLI_listbase_is_empty(&adt->drivers);
66 }
67
ntree_check_nodes_connected_dfs(bNodeTree * ntree,bNode * from,bNode * to)68 static bool ntree_check_nodes_connected_dfs(bNodeTree *ntree, bNode *from, bNode *to)
69 {
70 if (from->flag & NODE_TEST) {
71 return false;
72 }
73 from->flag |= NODE_TEST;
74 LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
75 if (link->fromnode == from) {
76 if (link->tonode == to) {
77 return true;
78 }
79
80 if (ntree_check_nodes_connected_dfs(ntree, link->tonode, to)) {
81 return true;
82 }
83 }
84 }
85 return false;
86 }
87
ntree_check_nodes_connected(bNodeTree * ntree,bNode * from,bNode * to)88 static bool ntree_check_nodes_connected(bNodeTree *ntree, bNode *from, bNode *to)
89 {
90 if (from == to) {
91 return true;
92 }
93 ntreeNodeFlagSet(ntree, NODE_TEST, false);
94 return ntree_check_nodes_connected_dfs(ntree, from, to);
95 }
96
node_group_has_output_dfs(bNode * node)97 static bool node_group_has_output_dfs(bNode *node)
98 {
99 bNodeTree *ntree = (bNodeTree *)node->id;
100 if (ntree->id.tag & LIB_TAG_DOIT) {
101 return false;
102 }
103 ntree->id.tag |= LIB_TAG_DOIT;
104 for (bNode *current_node = ntree->nodes.first; current_node != NULL;
105 current_node = current_node->next) {
106 if (current_node->type == NODE_GROUP) {
107 if (current_node->id && node_group_has_output_dfs(current_node)) {
108 return true;
109 }
110 }
111 if (current_node->flag & NODE_DO_OUTPUT && current_node->type != NODE_GROUP_OUTPUT) {
112 return true;
113 }
114 }
115 return false;
116 }
117
node_group_has_output(Main * bmain,bNode * node)118 static bool node_group_has_output(Main *bmain, bNode *node)
119 {
120 BLI_assert(ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP));
121 bNodeTree *ntree = (bNodeTree *)node->id;
122 if (ntree == NULL) {
123 return false;
124 }
125 BKE_main_id_tag_listbase(&bmain->nodetrees, LIB_TAG_DOIT, false);
126 return node_group_has_output_dfs(node);
127 }
128
node_connected_to_output(Main * bmain,bNodeTree * ntree,bNode * node)129 bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node)
130 {
131 /* Special case for drivers: if node tree has any drivers we assume it is
132 * always to be tagged for update when node changes. Otherwise we will be
133 * doomed to do some deep and nasty deep search of indirect dependencies,
134 * which will be too complicated without real benefit.
135 */
136 if (ntree_has_drivers(ntree)) {
137 return true;
138 }
139 LISTBASE_FOREACH (bNode *, current_node, &ntree->nodes) {
140 /* Special case for group nodes -- if modified node connected to a group
141 * with active output inside we consider refresh is needed.
142 *
143 * We could make check more grained here by taking which socket the node
144 * is connected to and so eventually.
145 */
146 if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
147 if (current_node->id != NULL && ntree_has_drivers((bNodeTree *)current_node->id)) {
148 return true;
149 }
150 if (ntree_check_nodes_connected(ntree, node, current_node) &&
151 node_group_has_output(bmain, current_node)) {
152 return true;
153 }
154 }
155 if (current_node->flag & NODE_DO_OUTPUT) {
156 if (ntree_check_nodes_connected(ntree, node, current_node)) {
157 return true;
158 }
159 }
160 }
161 return false;
162 }
163
164 /* ****************** Add *********************** */
165
166 typedef struct bNodeListItem {
167 struct bNodeListItem *next, *prev;
168 struct bNode *node;
169 } bNodeListItem;
170
171 typedef struct NodeInsertOfsData {
172 bNodeTree *ntree;
173 bNode *insert; /* inserted node */
174 bNode *prev, *next; /* prev/next node in the chain */
175 bNode *insert_parent;
176
177 wmTimer *anim_timer;
178
179 float offset_x; /* offset to apply to node chain */
180 } NodeInsertOfsData;
181
sort_nodes_locx(const void * a,const void * b)182 static int sort_nodes_locx(const void *a, const void *b)
183 {
184 const bNodeListItem *nli1 = a;
185 const bNodeListItem *nli2 = b;
186 const bNode *node1 = nli1->node;
187 const bNode *node2 = nli2->node;
188
189 if (node1->locx > node2->locx) {
190 return 1;
191 }
192 return 0;
193 }
194
socket_is_available(bNodeTree * UNUSED (ntree),bNodeSocket * sock,const bool allow_used)195 static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used)
196 {
197 if (nodeSocketIsHidden(sock)) {
198 return 0;
199 }
200
201 if (!allow_used && (sock->flag & SOCK_IN_USE)) {
202 return 0;
203 }
204
205 return 1;
206 }
207
best_socket_output(bNodeTree * ntree,bNode * node,bNodeSocket * sock_target,const bool allow_multiple)208 static bNodeSocket *best_socket_output(bNodeTree *ntree,
209 bNode *node,
210 bNodeSocket *sock_target,
211 const bool allow_multiple)
212 {
213 /* first look for selected output */
214 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
215 if (!socket_is_available(ntree, sock, allow_multiple)) {
216 continue;
217 }
218
219 if (sock->flag & SELECT) {
220 return sock;
221 }
222 }
223
224 /* try to find a socket with a matching name */
225 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
226 if (!socket_is_available(ntree, sock, allow_multiple)) {
227 continue;
228 }
229
230 /* check for same types */
231 if (sock->type == sock_target->type) {
232 if (STREQ(sock->name, sock_target->name)) {
233 return sock;
234 }
235 }
236 }
237
238 /* otherwise settle for the first available socket of the right type */
239 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
240 if (!socket_is_available(ntree, sock, allow_multiple)) {
241 continue;
242 }
243
244 /* check for same types */
245 if (sock->type == sock_target->type) {
246 return sock;
247 }
248 }
249
250 /* Always allow linking to an reroute node. The socket type of the reroute sockets might change
251 * after the link has been created. */
252 if (node->type == NODE_REROUTE) {
253 return node->outputs.first;
254 }
255
256 return NULL;
257 }
258
259 /* this is a bit complicated, but designed to prioritize finding
260 * sockets of higher types, such as image, first */
best_socket_input(bNodeTree * ntree,bNode * node,int num,int replace)261 static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
262 {
263 int maxtype = 0;
264 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
265 maxtype = max_ii(sock->type, maxtype);
266 }
267
268 /* find sockets of higher 'types' first (i.e. image) */
269 int a = 0;
270 for (int socktype = maxtype; socktype >= 0; socktype--) {
271 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
272 if (!socket_is_available(ntree, sock, replace)) {
273 a++;
274 continue;
275 }
276
277 if (sock->type == socktype) {
278 /* increment to make sure we don't keep finding
279 * the same socket on every attempt running this function */
280 a++;
281 if (a > num) {
282 return sock;
283 }
284 }
285 }
286 }
287
288 return NULL;
289 }
290
snode_autoconnect_input(SpaceNode * snode,bNode * node_fr,bNodeSocket * sock_fr,bNode * node_to,bNodeSocket * sock_to,int replace)291 static bool snode_autoconnect_input(SpaceNode *snode,
292 bNode *node_fr,
293 bNodeSocket *sock_fr,
294 bNode *node_to,
295 bNodeSocket *sock_to,
296 int replace)
297 {
298 bNodeTree *ntree = snode->edittree;
299
300 /* then we can connect */
301 if (replace) {
302 nodeRemSocketLinks(ntree, sock_to);
303 }
304
305 nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to);
306 return true;
307 }
308
snode_autoconnect(Main * bmain,SpaceNode * snode,const bool allow_multiple,const bool replace)309 static void snode_autoconnect(Main *bmain,
310 SpaceNode *snode,
311 const bool allow_multiple,
312 const bool replace)
313 {
314 bNodeTree *ntree = snode->edittree;
315 ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
316
317 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
318 if (node->flag & NODE_SELECT) {
319 bNodeListItem *nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
320 nli->node = node;
321 BLI_addtail(nodelist, nli);
322 }
323 }
324
325 /* sort nodes left to right */
326 BLI_listbase_sort(nodelist, sort_nodes_locx);
327
328 int numlinks = 0;
329 LISTBASE_FOREACH (bNodeListItem *, nli, nodelist) {
330 bool has_selected_inputs = false;
331
332 if (nli->next == NULL) {
333 break;
334 }
335
336 bNode *node_fr = nli->node;
337 bNode *node_to = nli->next->node;
338 /* corner case: input/output node aligned the wrong way around (T47729) */
339 if (BLI_listbase_is_empty(&node_to->inputs) || BLI_listbase_is_empty(&node_fr->outputs)) {
340 SWAP(bNode *, node_fr, node_to);
341 }
342
343 /* if there are selected sockets, connect those */
344 LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) {
345 if (sock_to->flag & SELECT) {
346 has_selected_inputs = 1;
347
348 if (!socket_is_available(ntree, sock_to, replace)) {
349 continue;
350 }
351
352 /* check for an appropriate output socket to connect from */
353 bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
354 if (!sock_fr) {
355 continue;
356 }
357
358 if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
359 numlinks++;
360 }
361 }
362 }
363
364 if (!has_selected_inputs) {
365 /* no selected inputs, connect by finding suitable match */
366 int num_inputs = BLI_listbase_count(&node_to->inputs);
367
368 for (int i = 0; i < num_inputs; i++) {
369
370 /* find the best guess input socket */
371 bNodeSocket *sock_to = best_socket_input(ntree, node_to, i, replace);
372 if (!sock_to) {
373 continue;
374 }
375
376 /* check for an appropriate output socket to connect from */
377 bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
378 if (!sock_fr) {
379 continue;
380 }
381
382 if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
383 numlinks++;
384 break;
385 }
386 }
387 }
388 }
389
390 if (numlinks > 0) {
391 ntreeUpdateTree(bmain, ntree);
392 }
393
394 BLI_freelistN(nodelist);
395 MEM_freeN(nodelist);
396 }
397
398 /* *************************** link viewer op ******************** */
399
node_link_viewer(const bContext * C,bNode * tonode)400 static int node_link_viewer(const bContext *C, bNode *tonode)
401 {
402 SpaceNode *snode = CTX_wm_space_node(C);
403
404 /* context check */
405 if (tonode == NULL || BLI_listbase_is_empty(&tonode->outputs)) {
406 return OPERATOR_CANCELLED;
407 }
408 if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
409 return OPERATOR_CANCELLED;
410 }
411
412 /* get viewer */
413 bNode *viewer_node = NULL;
414 LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
415 if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
416 if (node->flag & NODE_DO_OUTPUT) {
417 viewer_node = node;
418 break;
419 }
420 }
421 }
422 /* no viewer, we make one active */
423 if (viewer_node == NULL) {
424 LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
425 if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
426 node->flag |= NODE_DO_OUTPUT;
427 viewer_node = node;
428 break;
429 }
430 }
431 }
432
433 bNodeSocket *sock = NULL;
434 bNodeLink *link = NULL;
435
436 /* try to find an already connected socket to cycle to the next */
437 if (viewer_node) {
438 link = NULL;
439
440 for (link = snode->edittree->links.first; link; link = link->next) {
441 if (link->tonode == viewer_node && link->fromnode == tonode) {
442 if (link->tosock == viewer_node->inputs.first) {
443 break;
444 }
445 }
446 }
447 if (link) {
448 /* unlink existing connection */
449 sock = link->fromsock;
450 nodeRemLink(snode->edittree, link);
451
452 /* find a socket after the previously connected socket */
453 for (sock = sock->next; sock; sock = sock->next) {
454 if (!nodeSocketIsHidden(sock)) {
455 break;
456 }
457 }
458 }
459 }
460
461 if (tonode) {
462 /* Find a selected socket that overrides the socket to connect to */
463 LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
464 if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
465 sock = sock2;
466 break;
467 }
468 }
469 }
470
471 /* find a socket starting from the first socket */
472 if (!sock) {
473 for (sock = tonode->outputs.first; sock; sock = sock->next) {
474 if (!nodeSocketIsHidden(sock)) {
475 break;
476 }
477 }
478 }
479
480 if (sock) {
481 /* add a new viewer if none exists yet */
482 if (!viewer_node) {
483 /* XXX location is a quick hack, just place it next to the linked socket */
484 viewer_node = node_add_node(C, NULL, CMP_NODE_VIEWER, sock->locx + 100, sock->locy);
485 if (!viewer_node) {
486 return OPERATOR_CANCELLED;
487 }
488
489 link = NULL;
490 }
491 else {
492 /* get link to viewer */
493 for (link = snode->edittree->links.first; link; link = link->next) {
494 if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) {
495 break;
496 }
497 }
498 }
499
500 if (link == NULL) {
501 nodeAddLink(snode->edittree, tonode, sock, viewer_node, viewer_node->inputs.first);
502 }
503 else {
504 link->fromnode = tonode;
505 link->fromsock = sock;
506 /* make sure the dependency sorting is updated */
507 snode->edittree->update |= NTREE_UPDATE_LINKS;
508 }
509 ntreeUpdateTree(CTX_data_main(C), snode->edittree);
510 snode_update(snode, viewer_node);
511 }
512
513 return OPERATOR_FINISHED;
514 }
515
node_active_link_viewer_exec(bContext * C,wmOperator * UNUSED (op))516 static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
517 {
518 SpaceNode *snode = CTX_wm_space_node(C);
519 bNode *node = nodeGetActive(snode->edittree);
520
521 if (!node) {
522 return OPERATOR_CANCELLED;
523 }
524
525 ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
526
527 if (node_link_viewer(C, node) == OPERATOR_CANCELLED) {
528 return OPERATOR_CANCELLED;
529 }
530
531 snode_notify(C, snode);
532
533 return OPERATOR_FINISHED;
534 }
535
NODE_OT_link_viewer(wmOperatorType * ot)536 void NODE_OT_link_viewer(wmOperatorType *ot)
537 {
538 /* identifiers */
539 ot->name = "Link to Viewer Node";
540 ot->description = "Link to viewer node";
541 ot->idname = "NODE_OT_link_viewer";
542
543 /* api callbacks */
544 ot->exec = node_active_link_viewer_exec;
545 ot->poll = composite_node_editable;
546
547 /* flags */
548 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
549 }
550
551 /* *************************** add link op ******************** */
552
node_link_update_header(bContext * C,bNodeLinkDrag * UNUSED (nldrag))553 static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag))
554 {
555 char header[UI_MAX_DRAW_STR];
556
557 BLI_strncpy(header, TIP_("LMB: drag node link, RMB: cancel"), sizeof(header));
558 ED_workspace_status_text(C, header);
559 }
560
node_count_links(bNodeTree * ntree,bNodeSocket * sock)561 static int node_count_links(bNodeTree *ntree, bNodeSocket *sock)
562 {
563 int count = 0;
564 LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
565 if (link->fromsock == sock) {
566 count++;
567 }
568 if (link->tosock == sock) {
569 count++;
570 }
571 }
572 return count;
573 }
574
node_remove_extra_links(SpaceNode * snode,bNodeLink * link)575 static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link)
576 {
577 bNodeTree *ntree = snode->edittree;
578 bNodeSocket *from = link->fromsock, *to = link->tosock;
579 int to_count = node_count_links(ntree, to);
580 int from_count = node_count_links(ntree, from);
581 int to_link_limit = nodeSocketLinkLimit(to);
582 int from_link_limit = nodeSocketLinkLimit(from);
583
584 LISTBASE_FOREACH_MUTABLE (bNodeLink *, tlink, &ntree->links) {
585 if (tlink == link) {
586 continue;
587 }
588
589 if (tlink && tlink->fromsock == from) {
590 if (from_count > from_link_limit) {
591 nodeRemLink(ntree, tlink);
592 tlink = NULL;
593 from_count--;
594 }
595 }
596
597 if (tlink && tlink->tosock == to) {
598 if (to_count > to_link_limit) {
599 nodeRemLink(ntree, tlink);
600 tlink = NULL;
601 to_count--;
602 }
603 }
604 }
605 }
606
node_link_exit(bContext * C,wmOperator * op,bool apply_links)607 static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
608 {
609 Main *bmain = CTX_data_main(C);
610 SpaceNode *snode = CTX_wm_space_node(C);
611 bNodeTree *ntree = snode->edittree;
612 bNodeLinkDrag *nldrag = op->customdata;
613 bool do_tag_update = false;
614
615 /* avoid updates while applying links */
616 ntree->is_updating = true;
617 LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
618 bNodeLink *link = linkdata->data;
619
620 /* See note below, but basically TEST flag means that the link
621 * was connected to output (or to a node which affects the
622 * output).
623 */
624 do_tag_update |= (link->flag & NODE_LINK_TEST) != 0;
625
626 if (apply_links && link->tosock && link->fromsock) {
627 /* before actually adding the link,
628 * let nodes perform special link insertion handling
629 */
630 if (link->fromnode->typeinfo->insert_link) {
631 link->fromnode->typeinfo->insert_link(ntree, link->fromnode, link);
632 }
633 if (link->tonode->typeinfo->insert_link) {
634 link->tonode->typeinfo->insert_link(ntree, link->tonode, link);
635 }
636
637 /* add link to the node tree */
638 BLI_addtail(&ntree->links, link);
639
640 ntree->update |= NTREE_UPDATE_LINKS;
641
642 /* tag tonode for update */
643 link->tonode->update |= NODE_UPDATE;
644
645 /* we might need to remove a link */
646 node_remove_extra_links(snode, link);
647
648 if (link->tonode) {
649 do_tag_update |= (do_tag_update || node_connected_to_output(bmain, ntree, link->tonode));
650 }
651 }
652 else {
653 nodeRemLink(ntree, link);
654 }
655 }
656 ntree->is_updating = false;
657
658 do_tag_update |= ED_node_is_simulation(snode);
659
660 ntreeUpdateTree(bmain, ntree);
661 snode_notify(C, snode);
662 if (do_tag_update) {
663 snode_dag_update(C, snode);
664 }
665
666 BLI_remlink(&snode->linkdrag, nldrag);
667 /* links->data pointers are either held by the tree or freed already */
668 BLI_freelistN(&nldrag->links);
669 MEM_freeN(nldrag);
670 }
671
node_link_find_socket(bContext * C,wmOperator * op,float cursor[2])672 static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
673 {
674 SpaceNode *snode = CTX_wm_space_node(C);
675 bNodeLinkDrag *nldrag = op->customdata;
676
677 if (nldrag->in_out == SOCK_OUT) {
678 bNode *tnode;
679 bNodeSocket *tsock = NULL;
680 if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) {
681 LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
682 bNodeLink *link = linkdata->data;
683
684 /* skip if this is already the target socket */
685 if (link->tosock == tsock) {
686 continue;
687 }
688 /* skip if socket is on the same node as the fromsock */
689 if (tnode && link->fromnode == tnode) {
690 continue;
691 }
692
693 /* attach links to the socket */
694 link->tonode = tnode;
695 link->tosock = tsock;
696 }
697 }
698 else {
699 LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
700 bNodeLink *link = linkdata->data;
701
702 link->tonode = NULL;
703 link->tosock = NULL;
704 }
705 }
706 }
707 else {
708 bNode *tnode;
709 bNodeSocket *tsock = NULL;
710 if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) {
711 LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
712 bNodeLink *link = linkdata->data;
713
714 /* skip if this is already the target socket */
715 if (link->fromsock == tsock) {
716 continue;
717 }
718 /* skip if socket is on the same node as the fromsock */
719 if (tnode && link->tonode == tnode) {
720 continue;
721 }
722
723 /* attach links to the socket */
724 link->fromnode = tnode;
725 link->fromsock = tsock;
726 }
727 }
728 else {
729 LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
730 bNodeLink *link = linkdata->data;
731
732 link->fromnode = NULL;
733 link->fromsock = NULL;
734 }
735 }
736 }
737 }
738
739 /* loop that adds a nodelink, called by function below */
740 /* in_out = starting socket */
node_link_modal(bContext * C,wmOperator * op,const wmEvent * event)741 static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
742 {
743 bNodeLinkDrag *nldrag = op->customdata;
744 ARegion *region = CTX_wm_region(C);
745 float cursor[2];
746
747 UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]);
748
749 switch (event->type) {
750 case MOUSEMOVE:
751 node_link_find_socket(C, op, cursor);
752
753 node_link_update_header(C, nldrag);
754 ED_region_tag_redraw(region);
755 break;
756
757 case LEFTMOUSE:
758 case RIGHTMOUSE:
759 case MIDDLEMOUSE: {
760 if (event->val == KM_RELEASE) {
761 node_link_exit(C, op, true);
762
763 ED_workspace_status_text(C, NULL);
764 ED_region_tag_redraw(region);
765 return OPERATOR_FINISHED;
766 }
767 break;
768 }
769 }
770
771 return OPERATOR_RUNNING_MODAL;
772 }
773
774 /* return 1 when socket clicked */
node_link_init(Main * bmain,SpaceNode * snode,float cursor[2],bool detach)775 static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor[2], bool detach)
776 {
777 bNodeLinkDrag *nldrag = NULL;
778
779 /* output indicated? */
780 bNode *node;
781 bNodeSocket *sock;
782 if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
783 nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
784
785 const int num_links = nodeCountSocketLinks(snode->edittree, sock);
786 int link_limit = nodeSocketLinkLimit(sock);
787 if (num_links > 0 && (num_links >= link_limit || detach)) {
788 /* dragged links are fixed on input side */
789 nldrag->in_out = SOCK_IN;
790 /* detach current links and store them in the operator data */
791 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) {
792 if (link->fromsock == sock) {
793 LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
794 bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
795 linkdata->data = oplink;
796 *oplink = *link;
797 oplink->next = oplink->prev = NULL;
798 oplink->flag |= NODE_LINK_VALID;
799
800 /* The link could be disconnected and in that case we
801 * wouldn't be able to check whether tag update is
802 * needed or not when releasing mouse button. So we
803 * cache whether the link affects output or not here
804 * using TEST flag.
805 */
806 oplink->flag &= ~NODE_LINK_TEST;
807 if (node_connected_to_output(bmain, snode->edittree, link->tonode)) {
808 oplink->flag |= NODE_LINK_TEST;
809 }
810
811 BLI_addtail(&nldrag->links, linkdata);
812 nodeRemLink(snode->edittree, link);
813 }
814 }
815 }
816 else {
817 /* dragged links are fixed on output side */
818 nldrag->in_out = SOCK_OUT;
819 /* create a new link */
820 LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
821 bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
822 linkdata->data = oplink;
823 oplink->fromnode = node;
824 oplink->fromsock = sock;
825 oplink->flag |= NODE_LINK_VALID;
826 oplink->flag &= ~NODE_LINK_TEST;
827 if (node_connected_to_output(bmain, snode->edittree, node)) {
828 oplink->flag |= NODE_LINK_TEST;
829 }
830
831 BLI_addtail(&nldrag->links, linkdata);
832 }
833 }
834 /* or an input? */
835 else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
836 nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
837
838 const int num_links = nodeCountSocketLinks(snode->edittree, sock);
839 int link_limit = nodeSocketLinkLimit(sock);
840 if (num_links > 0 && (num_links >= link_limit || detach)) {
841 /* dragged links are fixed on output side */
842 nldrag->in_out = SOCK_OUT;
843 /* detach current links and store them in the operator data */
844 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) {
845 if (link->tosock == sock) {
846 LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
847 bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
848 linkdata->data = oplink;
849 *oplink = *link;
850 oplink->next = oplink->prev = NULL;
851 oplink->flag |= NODE_LINK_VALID;
852 oplink->flag &= ~NODE_LINK_TEST;
853 if (node_connected_to_output(bmain, snode->edittree, link->tonode)) {
854 oplink->flag |= NODE_LINK_TEST;
855 }
856
857 BLI_addtail(&nldrag->links, linkdata);
858 nodeRemLink(snode->edittree, link);
859
860 /* send changed event to original link->tonode */
861 if (node) {
862 snode_update(snode, node);
863 }
864 }
865 }
866 }
867 else {
868 /* dragged links are fixed on input side */
869 nldrag->in_out = SOCK_IN;
870 /* create a new link */
871 LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
872 bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
873 linkdata->data = oplink;
874 oplink->tonode = node;
875 oplink->tosock = sock;
876 oplink->flag |= NODE_LINK_VALID;
877 oplink->flag &= ~NODE_LINK_TEST;
878 if (node_connected_to_output(bmain, snode->edittree, node)) {
879 oplink->flag |= NODE_LINK_TEST;
880 }
881
882 BLI_addtail(&nldrag->links, linkdata);
883 }
884 }
885
886 return nldrag;
887 }
888
node_link_invoke(bContext * C,wmOperator * op,const wmEvent * event)889 static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
890 {
891 Main *bmain = CTX_data_main(C);
892 SpaceNode *snode = CTX_wm_space_node(C);
893 ARegion *region = CTX_wm_region(C);
894
895 bool detach = RNA_boolean_get(op->ptr, "detach");
896
897 float cursor[2];
898 UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]);
899
900 ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
901
902 bNodeLinkDrag *nldrag = node_link_init(bmain, snode, cursor, detach);
903
904 if (nldrag) {
905 op->customdata = nldrag;
906 BLI_addtail(&snode->linkdrag, nldrag);
907
908 /* add modal handler */
909 WM_event_add_modal_handler(C, op);
910
911 return OPERATOR_RUNNING_MODAL;
912 }
913 return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
914 }
915
node_link_cancel(bContext * C,wmOperator * op)916 static void node_link_cancel(bContext *C, wmOperator *op)
917 {
918 SpaceNode *snode = CTX_wm_space_node(C);
919 bNodeLinkDrag *nldrag = op->customdata;
920
921 BLI_remlink(&snode->linkdrag, nldrag);
922
923 BLI_freelistN(&nldrag->links);
924 MEM_freeN(nldrag);
925 }
926
NODE_OT_link(wmOperatorType * ot)927 void NODE_OT_link(wmOperatorType *ot)
928 {
929 /* identifiers */
930 ot->name = "Link Nodes";
931 ot->idname = "NODE_OT_link";
932 ot->description = "Use the mouse to create a link between two nodes";
933
934 /* api callbacks */
935 ot->invoke = node_link_invoke;
936 ot->modal = node_link_modal;
937 // ot->exec = node_link_exec;
938 ot->poll = ED_operator_node_editable;
939 ot->cancel = node_link_cancel;
940
941 /* flags */
942 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
943
944 RNA_def_boolean(ot->srna, "detach", false, "Detach", "Detach and redirect existing links");
945 }
946
947 /* ********************** Make Link operator ***************** */
948
949 /* makes a link between selected output and input sockets */
node_make_link_exec(bContext * C,wmOperator * op)950 static int node_make_link_exec(bContext *C, wmOperator *op)
951 {
952 Main *bmain = CTX_data_main(C);
953 SpaceNode *snode = CTX_wm_space_node(C);
954 const bool replace = RNA_boolean_get(op->ptr, "replace");
955
956 ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
957
958 snode_autoconnect(bmain, snode, 1, replace);
959
960 /* deselect sockets after linking */
961 node_deselect_all_input_sockets(snode, 0);
962 node_deselect_all_output_sockets(snode, 0);
963
964 ntreeUpdateTree(CTX_data_main(C), snode->edittree);
965 snode_notify(C, snode);
966 snode_dag_update(C, snode);
967
968 return OPERATOR_FINISHED;
969 }
970
NODE_OT_link_make(wmOperatorType * ot)971 void NODE_OT_link_make(wmOperatorType *ot)
972 {
973 /* identifiers */
974 ot->name = "Make Links";
975 ot->description = "Makes a link between selected output in input sockets";
976 ot->idname = "NODE_OT_link_make";
977
978 /* callbacks */
979 ot->exec = node_make_link_exec;
980 /* XXX we need a special poll which checks that there are selected input/output sockets. */
981 ot->poll = ED_operator_node_editable;
982
983 /* flags */
984 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
985
986 RNA_def_boolean(
987 ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
988 }
989
990 /* ********************** Cut Link operator ***************** */
cut_links_intersect(bNodeLink * link,const float mcoords[][2],int tot)991 static bool cut_links_intersect(bNodeLink *link, const float mcoords[][2], int tot)
992 {
993 float coord_array[NODE_LINK_RESOL + 1][2];
994
995 if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
996 for (int i = 0; i < tot - 1; i++) {
997 for (int b = 0; b < NODE_LINK_RESOL; b++) {
998 if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) {
999 return 1;
1000 }
1001 }
1002 }
1003 }
1004 return 0;
1005 }
1006
cut_links_exec(bContext * C,wmOperator * op)1007 static int cut_links_exec(bContext *C, wmOperator *op)
1008 {
1009 Main *bmain = CTX_data_main(C);
1010 SpaceNode *snode = CTX_wm_space_node(C);
1011 ARegion *region = CTX_wm_region(C);
1012 bool do_tag_update = false;
1013
1014 int i = 0;
1015 float mcoords[256][2];
1016 RNA_BEGIN (op->ptr, itemptr, "path") {
1017 float loc[2];
1018
1019 RNA_float_get_array(&itemptr, "loc", loc);
1020 UI_view2d_region_to_view(
1021 ®ion->v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]);
1022 i++;
1023 if (i >= 256) {
1024 break;
1025 }
1026 }
1027 RNA_END;
1028
1029 if (i > 1) {
1030 bool found = false;
1031
1032 ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
1033
1034 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) {
1035 if (nodeLinkIsHidden(link)) {
1036 continue;
1037 }
1038
1039 if (cut_links_intersect(link, mcoords, i)) {
1040
1041 if (found == false) {
1042 /* TODO(sergey): Why did we kill jobs twice? */
1043 ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
1044 found = true;
1045 }
1046
1047 do_tag_update |= (do_tag_update ||
1048 node_connected_to_output(bmain, snode->edittree, link->tonode));
1049
1050 snode_update(snode, link->tonode);
1051 nodeRemLink(snode->edittree, link);
1052 }
1053 }
1054
1055 do_tag_update |= ED_node_is_simulation(snode);
1056
1057 if (found) {
1058 ntreeUpdateTree(CTX_data_main(C), snode->edittree);
1059 snode_notify(C, snode);
1060 if (do_tag_update) {
1061 snode_dag_update(C, snode);
1062 }
1063
1064 return OPERATOR_FINISHED;
1065 }
1066
1067 return OPERATOR_CANCELLED;
1068 }
1069
1070 return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
1071 }
1072
NODE_OT_links_cut(wmOperatorType * ot)1073 void NODE_OT_links_cut(wmOperatorType *ot)
1074 {
1075 ot->name = "Cut Links";
1076 ot->idname = "NODE_OT_links_cut";
1077 ot->description = "Use the mouse to cut (remove) some links";
1078
1079 ot->invoke = WM_gesture_lines_invoke;
1080 ot->modal = WM_gesture_lines_modal;
1081 ot->exec = cut_links_exec;
1082 ot->cancel = WM_gesture_lines_cancel;
1083
1084 ot->poll = ED_operator_node_editable;
1085
1086 /* flags */
1087 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1088
1089 /* properties */
1090 PropertyRNA *prop;
1091 prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
1092 RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
1093
1094 /* internal */
1095 RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1096 }
1097
1098 /* ********************** Detach links operator ***************** */
1099
detach_links_exec(bContext * C,wmOperator * UNUSED (op))1100 static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
1101 {
1102 SpaceNode *snode = CTX_wm_space_node(C);
1103 bNodeTree *ntree = snode->edittree;
1104
1105 ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
1106
1107 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1108 if (node->flag & SELECT) {
1109 nodeInternalRelink(ntree, node);
1110 }
1111 }
1112
1113 ntreeUpdateTree(CTX_data_main(C), ntree);
1114
1115 snode_notify(C, snode);
1116 snode_dag_update(C, snode);
1117
1118 return OPERATOR_FINISHED;
1119 }
1120
NODE_OT_links_detach(wmOperatorType * ot)1121 void NODE_OT_links_detach(wmOperatorType *ot)
1122 {
1123 ot->name = "Detach Links";
1124 ot->idname = "NODE_OT_links_detach";
1125 ot->description =
1126 "Remove all links to selected nodes, and try to connect neighbor nodes together";
1127
1128 ot->exec = detach_links_exec;
1129 ot->poll = ED_operator_node_editable;
1130
1131 /* flags */
1132 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1133 }
1134
1135 /* ****************** Set Parent ******************* */
1136
node_parent_set_exec(bContext * C,wmOperator * UNUSED (op))1137 static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
1138 {
1139 SpaceNode *snode = CTX_wm_space_node(C);
1140 bNodeTree *ntree = snode->edittree;
1141 bNode *frame = nodeGetActive(ntree);
1142 if (!frame || frame->type != NODE_FRAME) {
1143 return OPERATOR_CANCELLED;
1144 }
1145
1146 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1147 if (node == frame) {
1148 continue;
1149 }
1150 if (node->flag & NODE_SELECT) {
1151 nodeDetachNode(node);
1152 nodeAttachNode(node, frame);
1153 }
1154 }
1155
1156 ED_node_sort(ntree);
1157 WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1158
1159 return OPERATOR_FINISHED;
1160 }
1161
NODE_OT_parent_set(wmOperatorType * ot)1162 void NODE_OT_parent_set(wmOperatorType *ot)
1163 {
1164 /* identifiers */
1165 ot->name = "Make Parent";
1166 ot->description = "Attach selected nodes";
1167 ot->idname = "NODE_OT_parent_set";
1168
1169 /* api callbacks */
1170 ot->exec = node_parent_set_exec;
1171 ot->poll = ED_operator_node_editable;
1172
1173 /* flags */
1174 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1175 }
1176
1177 /* ****************** Join Nodes ******************* */
1178
1179 /* tags for depth-first search */
1180 #define NODE_JOIN_DONE 1
1181 #define NODE_JOIN_IS_DESCENDANT 2
1182
node_join_attach_recursive(bNode * node,bNode * frame)1183 static void node_join_attach_recursive(bNode *node, bNode *frame)
1184 {
1185 node->done |= NODE_JOIN_DONE;
1186
1187 if (node == frame) {
1188 node->done |= NODE_JOIN_IS_DESCENDANT;
1189 }
1190 else if (node->parent) {
1191 /* call recursively */
1192 if (!(node->parent->done & NODE_JOIN_DONE)) {
1193 node_join_attach_recursive(node->parent, frame);
1194 }
1195
1196 /* in any case: if the parent is a descendant, so is the child */
1197 if (node->parent->done & NODE_JOIN_IS_DESCENDANT) {
1198 node->done |= NODE_JOIN_IS_DESCENDANT;
1199 }
1200 else if (node->flag & NODE_TEST) {
1201 /* if parent is not an descendant of the frame, reattach the node */
1202 nodeDetachNode(node);
1203 nodeAttachNode(node, frame);
1204 node->done |= NODE_JOIN_IS_DESCENDANT;
1205 }
1206 }
1207 else if (node->flag & NODE_TEST) {
1208 nodeAttachNode(node, frame);
1209 node->done |= NODE_JOIN_IS_DESCENDANT;
1210 }
1211 }
1212
node_join_exec(bContext * C,wmOperator * UNUSED (op))1213 static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
1214 {
1215 SpaceNode *snode = CTX_wm_space_node(C);
1216 bNodeTree *ntree = snode->edittree;
1217
1218 /* XXX save selection: node_add_node call below sets the new frame as single
1219 * active+selected node */
1220 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1221 if (node->flag & NODE_SELECT) {
1222 node->flag |= NODE_TEST;
1223 }
1224 else {
1225 node->flag &= ~NODE_TEST;
1226 }
1227 }
1228
1229 bNode *frame = node_add_node(C, NULL, NODE_FRAME, 0.0f, 0.0f);
1230
1231 /* reset tags */
1232 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1233 node->done = 0;
1234 }
1235
1236 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1237 if (!(node->done & NODE_JOIN_DONE)) {
1238 node_join_attach_recursive(node, frame);
1239 }
1240 }
1241
1242 /* restore selection */
1243 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1244 if (node->flag & NODE_TEST) {
1245 node->flag |= NODE_SELECT;
1246 }
1247 }
1248
1249 ED_node_sort(ntree);
1250 WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1251
1252 return OPERATOR_FINISHED;
1253 }
1254
NODE_OT_join(wmOperatorType * ot)1255 void NODE_OT_join(wmOperatorType *ot)
1256 {
1257 /* identifiers */
1258 ot->name = "Join Nodes";
1259 ot->description = "Attach selected nodes to a new common frame";
1260 ot->idname = "NODE_OT_join";
1261
1262 /* api callbacks */
1263 ot->exec = node_join_exec;
1264 ot->poll = ED_operator_node_editable;
1265
1266 /* flags */
1267 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1268 }
1269
1270 /* ****************** Attach ******************* */
1271
node_find_frame_to_attach(ARegion * region,const bNodeTree * ntree,const int mouse_xy[2])1272 static bNode *node_find_frame_to_attach(ARegion *region,
1273 const bNodeTree *ntree,
1274 const int mouse_xy[2])
1275 {
1276 /* convert mouse coordinates to v2d space */
1277 float cursor[2];
1278 UI_view2d_region_to_view(®ion->v2d, UNPACK2(mouse_xy), &cursor[0], &cursor[1]);
1279
1280 LISTBASE_FOREACH_BACKWARD (bNode *, frame, &ntree->nodes) {
1281 /* skip selected, those are the nodes we want to attach */
1282 if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
1283 continue;
1284 }
1285 if (BLI_rctf_isect_pt_v(&frame->totr, cursor)) {
1286 return frame;
1287 }
1288 }
1289
1290 return NULL;
1291 }
1292
node_attach_invoke(bContext * C,wmOperator * UNUSED (op),const wmEvent * event)1293 static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1294 {
1295 ARegion *region = CTX_wm_region(C);
1296 SpaceNode *snode = CTX_wm_space_node(C);
1297 bNodeTree *ntree = snode->edittree;
1298 bNode *frame = node_find_frame_to_attach(region, ntree, event->mval);
1299
1300 if (frame) {
1301 LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree->nodes) {
1302 if (node->flag & NODE_SELECT) {
1303 if (node->parent == NULL) {
1304 /* disallow moving a parent into its child */
1305 if (nodeAttachNodeCheck(frame, node) == false) {
1306 /* attach all unparented nodes */
1307 nodeAttachNode(node, frame);
1308 }
1309 }
1310 else {
1311 /* attach nodes which share parent with the frame */
1312 bNode *parent;
1313 for (parent = frame->parent; parent; parent = parent->parent) {
1314 if (parent == node->parent) {
1315 break;
1316 }
1317 }
1318
1319 if (parent) {
1320 /* disallow moving a parent into its child */
1321 if (nodeAttachNodeCheck(frame, node) == false) {
1322 nodeDetachNode(node);
1323 nodeAttachNode(node, frame);
1324 }
1325 }
1326 }
1327 }
1328 }
1329 }
1330
1331 ED_node_sort(ntree);
1332 WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1333
1334 return OPERATOR_FINISHED;
1335 }
1336
NODE_OT_attach(wmOperatorType * ot)1337 void NODE_OT_attach(wmOperatorType *ot)
1338 {
1339 /* identifiers */
1340 ot->name = "Attach Nodes";
1341 ot->description = "Attach active node to a frame";
1342 ot->idname = "NODE_OT_attach";
1343
1344 /* api callbacks */
1345
1346 ot->invoke = node_attach_invoke;
1347 ot->poll = ED_operator_node_editable;
1348
1349 /* flags */
1350 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1351 }
1352
1353 /* ****************** Detach ******************* */
1354
1355 /* tags for depth-first search */
1356 #define NODE_DETACH_DONE 1
1357 #define NODE_DETACH_IS_DESCENDANT 2
1358
node_detach_recursive(bNode * node)1359 static void node_detach_recursive(bNode *node)
1360 {
1361 node->done |= NODE_DETACH_DONE;
1362
1363 if (node->parent) {
1364 /* call recursively */
1365 if (!(node->parent->done & NODE_DETACH_DONE)) {
1366 node_detach_recursive(node->parent);
1367 }
1368
1369 /* in any case: if the parent is a descendant, so is the child */
1370 if (node->parent->done & NODE_DETACH_IS_DESCENDANT) {
1371 node->done |= NODE_DETACH_IS_DESCENDANT;
1372 }
1373 else if (node->flag & NODE_SELECT) {
1374 /* if parent is not a descendant of a selected node, detach */
1375 nodeDetachNode(node);
1376 node->done |= NODE_DETACH_IS_DESCENDANT;
1377 }
1378 }
1379 else if (node->flag & NODE_SELECT) {
1380 node->done |= NODE_DETACH_IS_DESCENDANT;
1381 }
1382 }
1383
1384 /* detach the root nodes in the current selection */
node_detach_exec(bContext * C,wmOperator * UNUSED (op))1385 static int node_detach_exec(bContext *C, wmOperator *UNUSED(op))
1386 {
1387 SpaceNode *snode = CTX_wm_space_node(C);
1388 bNodeTree *ntree = snode->edittree;
1389
1390 /* reset tags */
1391 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1392 node->done = 0;
1393 }
1394 /* detach nodes recursively
1395 * relative order is preserved here!
1396 */
1397 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1398 if (!(node->done & NODE_DETACH_DONE)) {
1399 node_detach_recursive(node);
1400 }
1401 }
1402
1403 ED_node_sort(ntree);
1404 WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL);
1405
1406 return OPERATOR_FINISHED;
1407 }
1408
NODE_OT_detach(wmOperatorType * ot)1409 void NODE_OT_detach(wmOperatorType *ot)
1410 {
1411 /* identifiers */
1412 ot->name = "Detach Nodes";
1413 ot->description = "Detach selected nodes from parents";
1414 ot->idname = "NODE_OT_detach";
1415
1416 /* api callbacks */
1417 ot->exec = node_detach_exec;
1418 ot->poll = ED_operator_node_editable;
1419
1420 /* flags */
1421 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1422 }
1423
1424 /* ********************* automatic node insert on dragging ******************* */
1425
1426 /* prevent duplicate testing code below */
ed_node_link_conditions(ScrArea * area,bool test,SpaceNode ** r_snode,bNode ** r_select)1427 static bool ed_node_link_conditions(ScrArea *area,
1428 bool test,
1429 SpaceNode **r_snode,
1430 bNode **r_select)
1431 {
1432 SpaceNode *snode = area ? area->spacedata.first : NULL;
1433
1434 *r_snode = snode;
1435 *r_select = NULL;
1436
1437 /* no unlucky accidents */
1438 if (area == NULL || area->spacetype != SPACE_NODE) {
1439 return false;
1440 }
1441
1442 if (!test) {
1443 /* no need to look for a node */
1444 return true;
1445 }
1446
1447 bNode *node;
1448 bNode *select = NULL;
1449 for (node = snode->edittree->nodes.first; node; node = node->next) {
1450 if (node->flag & SELECT) {
1451 if (select) {
1452 break;
1453 }
1454 select = node;
1455 }
1456 }
1457 /* only one selected */
1458 if (node || select == NULL) {
1459 return false;
1460 }
1461
1462 /* correct node */
1463 if (BLI_listbase_is_empty(&select->inputs) || BLI_listbase_is_empty(&select->outputs)) {
1464 return false;
1465 }
1466
1467 /* test node for links */
1468 LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1469 if (nodeLinkIsHidden(link)) {
1470 continue;
1471 }
1472
1473 if (link->tonode == select || link->fromnode == select) {
1474 return false;
1475 }
1476 }
1477
1478 *r_select = select;
1479 return true;
1480 }
1481
1482 /* test == 0, clear all intersect flags */
ED_node_link_intersect_test(ScrArea * area,int test)1483 void ED_node_link_intersect_test(ScrArea *area, int test)
1484 {
1485 bNode *select;
1486 SpaceNode *snode;
1487 if (!ed_node_link_conditions(area, test, &snode, &select)) {
1488 return;
1489 }
1490
1491 /* clear flags */
1492 LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1493 link->flag &= ~NODE_LINKFLAG_HILITE;
1494 }
1495
1496 if (test == 0) {
1497 return;
1498 }
1499
1500 /* find link to select/highlight */
1501 bNodeLink *selink = NULL;
1502 float dist_best = FLT_MAX;
1503 LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1504 float coord_array[NODE_LINK_RESOL + 1][2];
1505
1506 if (nodeLinkIsHidden(link)) {
1507 continue;
1508 }
1509
1510 if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
1511 float dist = FLT_MAX;
1512
1513 /* loop over link coords to find shortest dist to
1514 * upper left node edge of a intersected line segment */
1515 for (int i = 0; i < NODE_LINK_RESOL; i++) {
1516 /* check if the node rect intersetcts the line from this point to next one */
1517 if (BLI_rctf_isect_segment(&select->totr, coord_array[i], coord_array[i + 1])) {
1518 /* store the shortest distance to the upper left edge
1519 * of all intersections found so far */
1520 const float node_xy[] = {select->totr.xmin, select->totr.ymax};
1521
1522 /* to be precise coord_array should be clipped by select->totr,
1523 * but not done since there's no real noticeable difference */
1524 dist = min_ff(
1525 dist_squared_to_line_segment_v2(node_xy, coord_array[i], coord_array[i + 1]), dist);
1526 }
1527 }
1528
1529 /* we want the link with the shortest distance to node center */
1530 if (dist < dist_best) {
1531 dist_best = dist;
1532 selink = link;
1533 }
1534 }
1535 }
1536
1537 if (selink) {
1538 selink->flag |= NODE_LINKFLAG_HILITE;
1539 }
1540 }
1541
1542 /* assumes sockets in list */
socket_best_match(ListBase * sockets)1543 static bNodeSocket *socket_best_match(ListBase *sockets)
1544 {
1545 /* find type range */
1546 int maxtype = 0;
1547 LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
1548 maxtype = max_ii(sock->type, maxtype);
1549 }
1550
1551 /* try all types, starting from 'highest' (i.e. colors, vectors, values) */
1552 for (int type = maxtype; type >= 0; type--) {
1553 LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
1554 if (!nodeSocketIsHidden(sock) && type == sock->type) {
1555 return sock;
1556 }
1557 }
1558 }
1559
1560 /* no visible sockets, unhide first of highest type */
1561 for (int type = maxtype; type >= 0; type--) {
1562 LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
1563 if (type == sock->type) {
1564 sock->flag &= ~SOCK_HIDDEN;
1565 return sock;
1566 }
1567 }
1568 }
1569
1570 return NULL;
1571 }
1572
node_parents_offset_flag_enable_cb(bNode * parent,void * UNUSED (userdata))1573 static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
1574 {
1575 /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
1576 parent->flag |= NODE_TEST;
1577
1578 return true;
1579 }
1580
node_offset_apply(bNode * node,const float offset_x)1581 static void node_offset_apply(bNode *node, const float offset_x)
1582 {
1583 /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
1584 if ((node->flag & NODE_TEST) == 0) {
1585 node->anim_init_locx = node->locx;
1586 node->anim_ofsx = (offset_x / UI_DPI_FAC);
1587 node->flag |= NODE_TEST;
1588 }
1589 }
1590
node_parent_offset_apply(NodeInsertOfsData * data,bNode * parent,const float offset_x)1591 static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, const float offset_x)
1592 {
1593 node_offset_apply(parent, offset_x);
1594
1595 /* Flag all children as offset to prevent them from being offset
1596 * separately (they've already moved with the parent). */
1597 LISTBASE_FOREACH (bNode *, node, &data->ntree->nodes) {
1598 if (nodeIsChildOf(parent, node)) {
1599 /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
1600 node->flag |= NODE_TEST;
1601 }
1602 }
1603 }
1604
1605 #define NODE_INSOFS_ANIM_DURATION 0.25f
1606
1607 /**
1608 * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, similar
1609 * to node_link_insert_offset_output_chain_cb below, but with slightly different logic
1610 */
node_link_insert_offset_frame_chain_cb(bNode * fromnode,bNode * tonode,void * userdata,const bool reversed)1611 static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode,
1612 bNode *tonode,
1613 void *userdata,
1614 const bool reversed)
1615 {
1616 NodeInsertOfsData *data = userdata;
1617 bNode *ofs_node = reversed ? fromnode : tonode;
1618
1619 if (ofs_node->parent && ofs_node->parent != data->insert_parent) {
1620 node_offset_apply(ofs_node->parent, data->offset_x);
1621 }
1622 else {
1623 node_offset_apply(ofs_node, data->offset_x);
1624 }
1625
1626 return true;
1627 }
1628
1629 /**
1630 * Applies #NodeInsertOfsData.offset_x to all children of \a parent.
1631 */
node_link_insert_offset_frame_chains(const bNodeTree * ntree,const bNode * parent,NodeInsertOfsData * data,const bool reversed)1632 static void node_link_insert_offset_frame_chains(const bNodeTree *ntree,
1633 const bNode *parent,
1634 NodeInsertOfsData *data,
1635 const bool reversed)
1636 {
1637 LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
1638 if (nodeIsChildOf(parent, node)) {
1639 nodeChainIter(ntree, node, node_link_insert_offset_frame_chain_cb, data, reversed);
1640 }
1641 }
1642 }
1643
1644 /**
1645 * Callback that applies NodeInsertOfsData.offset_x to a node or its parent,
1646 * considering the logic needed for offsetting nodes after link insert
1647 */
node_link_insert_offset_chain_cb(bNode * fromnode,bNode * tonode,void * userdata,const bool reversed)1648 static bool node_link_insert_offset_chain_cb(bNode *fromnode,
1649 bNode *tonode,
1650 void *userdata,
1651 const bool reversed)
1652 {
1653 NodeInsertOfsData *data = userdata;
1654 bNode *ofs_node = reversed ? fromnode : tonode;
1655
1656 if (data->insert_parent) {
1657 if (ofs_node->parent && (ofs_node->parent->flag & NODE_TEST) == 0) {
1658 node_parent_offset_apply(data, ofs_node->parent, data->offset_x);
1659 node_link_insert_offset_frame_chains(data->ntree, ofs_node->parent, data, reversed);
1660 }
1661 else {
1662 node_offset_apply(ofs_node, data->offset_x);
1663 }
1664
1665 if (nodeIsChildOf(data->insert_parent, ofs_node) == false) {
1666 data->insert_parent = NULL;
1667 }
1668 }
1669 else if (ofs_node->parent) {
1670 bNode *node = nodeFindRootParent(ofs_node);
1671 node_offset_apply(node, data->offset_x);
1672 }
1673 else {
1674 node_offset_apply(ofs_node, data->offset_x);
1675 }
1676
1677 return true;
1678 }
1679
node_link_insert_offset_ntree(NodeInsertOfsData * iofsd,ARegion * region,const int mouse_xy[2],const bool right_alignment)1680 static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd,
1681 ARegion *region,
1682 const int mouse_xy[2],
1683 const bool right_alignment)
1684 {
1685 bNodeTree *ntree = iofsd->ntree;
1686 bNode *insert = iofsd->insert;
1687 bNode *prev = iofsd->prev, *next = iofsd->next;
1688 bNode *init_parent = insert->parent; /* store old insert->parent for restoring later */
1689
1690 const float min_margin = U.node_margin * UI_DPI_FAC;
1691 const float width = NODE_WIDTH(insert);
1692 const bool needs_alignment = (next->totr.xmin - prev->totr.xmax) < (width + (min_margin * 2.0f));
1693
1694 float margin = width;
1695
1696 /* NODE_TEST will be used later, so disable for all nodes */
1697 ntreeNodeFlagSet(ntree, NODE_TEST, false);
1698
1699 /* insert->totr isn't updated yet,
1700 * so totr_insert is used to get the correct worldspace coords */
1701 rctf totr_insert;
1702 node_to_updated_rect(insert, &totr_insert);
1703
1704 /* frame attachment wasn't handled yet
1705 * so we search the frame that the node will be attached to later */
1706 insert->parent = node_find_frame_to_attach(region, ntree, mouse_xy);
1707
1708 /* this makes sure nodes are also correctly offset when inserting a node on top of a frame
1709 * without actually making it a part of the frame (because mouse isn't intersecting it)
1710 * - logic here is similar to node_find_frame_to_attach */
1711 if (!insert->parent ||
1712 (prev->parent && (prev->parent == next->parent) && (prev->parent != insert->parent))) {
1713 bNode *frame;
1714 rctf totr_frame;
1715
1716 /* check nodes front to back */
1717 for (frame = ntree->nodes.last; frame; frame = frame->prev) {
1718 /* skip selected, those are the nodes we want to attach */
1719 if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
1720 continue;
1721 }
1722
1723 /* for some reason frame y coords aren't correct yet */
1724 node_to_updated_rect(frame, &totr_frame);
1725
1726 if (BLI_rctf_isect_x(&totr_frame, totr_insert.xmin) &&
1727 BLI_rctf_isect_x(&totr_frame, totr_insert.xmax)) {
1728 if (BLI_rctf_isect_y(&totr_frame, totr_insert.ymin) ||
1729 BLI_rctf_isect_y(&totr_frame, totr_insert.ymax)) {
1730 /* frame isn't insert->parent actually, but this is needed to make offsetting
1731 * nodes work correctly for above checked cases (it is restored later) */
1732 insert->parent = frame;
1733 break;
1734 }
1735 }
1736 }
1737 }
1738
1739 /* *** ensure offset at the left (or right for right_alignment case) of insert_node *** */
1740
1741 float dist = right_alignment ? totr_insert.xmin - prev->totr.xmax :
1742 next->totr.xmin - totr_insert.xmax;
1743 /* distance between insert_node and prev is smaller than min margin */
1744 if (dist < min_margin) {
1745 const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
1746
1747 node_offset_apply(insert, addval);
1748
1749 totr_insert.xmin += addval;
1750 totr_insert.xmax += addval;
1751 margin += min_margin;
1752 }
1753
1754 /* *** ensure offset at the right (or left for right_alignment case) of insert_node *** */
1755
1756 dist = right_alignment ? next->totr.xmin - totr_insert.xmax : totr_insert.xmin - prev->totr.xmax;
1757 /* distance between insert_node and next is smaller than min margin */
1758 if (dist < min_margin) {
1759 const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
1760 if (needs_alignment) {
1761 bNode *offs_node = right_alignment ? next : prev;
1762 if (!offs_node->parent || offs_node->parent == insert->parent ||
1763 nodeIsChildOf(offs_node->parent, insert)) {
1764 node_offset_apply(offs_node, addval);
1765 }
1766 else if (!insert->parent && offs_node->parent) {
1767 node_offset_apply(nodeFindRootParent(offs_node), addval);
1768 }
1769 margin = addval;
1770 }
1771 /* enough room is available, but we want to ensure the min margin at the right */
1772 else {
1773 /* offset inserted node so that min margin is kept at the right */
1774 node_offset_apply(insert, -addval);
1775 }
1776 }
1777
1778 if (needs_alignment) {
1779 iofsd->insert_parent = insert->parent;
1780 iofsd->offset_x = margin;
1781
1782 /* flag all parents of insert as offset to prevent them from being offset */
1783 nodeParentsIter(insert, node_parents_offset_flag_enable_cb, NULL);
1784 /* iterate over entire chain and apply offsets */
1785 nodeChainIter(ntree,
1786 right_alignment ? next : prev,
1787 node_link_insert_offset_chain_cb,
1788 iofsd,
1789 !right_alignment);
1790 }
1791
1792 insert->parent = init_parent;
1793 }
1794
1795 /**
1796 * Modal handler for insert offset animation
1797 */
node_insert_offset_modal(bContext * C,wmOperator * UNUSED (op),const wmEvent * event)1798 static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1799 {
1800 SpaceNode *snode = CTX_wm_space_node(C);
1801 NodeInsertOfsData *iofsd = snode->iofsd;
1802 bool redraw = false;
1803
1804 if (!snode || event->type != TIMER || iofsd == NULL || iofsd->anim_timer != event->customdata) {
1805 return OPERATOR_PASS_THROUGH;
1806 }
1807
1808 const float duration = (float)iofsd->anim_timer->duration;
1809
1810 /* handle animation - do this before possibly aborting due to duration, since
1811 * main thread might be so busy that node hasn't reached final position yet */
1812 LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
1813 if (UNLIKELY(node->anim_ofsx)) {
1814 const float endval = node->anim_init_locx + node->anim_ofsx;
1815 if (IS_EQF(node->locx, endval) == false) {
1816 node->locx = BLI_easing_cubic_ease_in_out(
1817 duration, node->anim_init_locx, node->anim_ofsx, NODE_INSOFS_ANIM_DURATION);
1818 if (node->anim_ofsx < 0) {
1819 CLAMP_MIN(node->locx, endval);
1820 }
1821 else {
1822 CLAMP_MAX(node->locx, endval);
1823 }
1824 redraw = true;
1825 }
1826 }
1827 }
1828 if (redraw) {
1829 ED_region_tag_redraw(CTX_wm_region(C));
1830 }
1831
1832 /* end timer + free insert offset data */
1833 if (duration > NODE_INSOFS_ANIM_DURATION) {
1834 WM_event_remove_timer(CTX_wm_manager(C), NULL, iofsd->anim_timer);
1835
1836 LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
1837 node->anim_init_locx = node->anim_ofsx = 0.0f;
1838 }
1839
1840 snode->iofsd = NULL;
1841 MEM_freeN(iofsd);
1842
1843 return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
1844 }
1845
1846 return OPERATOR_RUNNING_MODAL;
1847 }
1848
1849 #undef NODE_INSOFS_ANIM_DURATION
1850
node_insert_offset_invoke(bContext * C,wmOperator * op,const wmEvent * event)1851 static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1852 {
1853 const SpaceNode *snode = CTX_wm_space_node(C);
1854 NodeInsertOfsData *iofsd = snode->iofsd;
1855
1856 if (!iofsd || !iofsd->insert) {
1857 return OPERATOR_CANCELLED;
1858 }
1859
1860 BLI_assert((snode->flag & SNODE_SKIP_INSOFFSET) == 0);
1861
1862 iofsd->ntree = snode->edittree;
1863 iofsd->anim_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.02);
1864
1865 node_link_insert_offset_ntree(
1866 iofsd, CTX_wm_region(C), event->mval, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT));
1867
1868 /* add temp handler */
1869 WM_event_add_modal_handler(C, op);
1870
1871 return OPERATOR_RUNNING_MODAL;
1872 }
1873
NODE_OT_insert_offset(wmOperatorType * ot)1874 void NODE_OT_insert_offset(wmOperatorType *ot)
1875 {
1876 /* identifiers */
1877 ot->name = "Insert Offset";
1878 ot->description = "Automatically offset nodes on insertion";
1879 ot->idname = "NODE_OT_insert_offset";
1880
1881 /* callbacks */
1882 ot->invoke = node_insert_offset_invoke;
1883 ot->modal = node_insert_offset_modal;
1884 ot->poll = ED_operator_node_editable;
1885
1886 /* flags */
1887 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
1888 }
1889
1890 /* assumes link with NODE_LINKFLAG_HILITE set */
ED_node_link_insert(Main * bmain,ScrArea * area)1891 void ED_node_link_insert(Main *bmain, ScrArea *area)
1892 {
1893 bNode *select;
1894 SpaceNode *snode;
1895 if (!ed_node_link_conditions(area, true, &snode, &select)) {
1896 return;
1897 }
1898
1899 /* get the link */
1900 bNodeLink *link;
1901 for (link = snode->edittree->links.first; link; link = link->next) {
1902 if (link->flag & NODE_LINKFLAG_HILITE) {
1903 break;
1904 }
1905 }
1906
1907 if (link) {
1908 bNodeSocket *best_input = socket_best_match(&select->inputs);
1909 bNodeSocket *best_output = socket_best_match(&select->outputs);
1910
1911 if (best_input && best_output) {
1912 bNode *node = link->tonode;
1913 bNodeSocket *sockto = link->tosock;
1914
1915 link->tonode = select;
1916 link->tosock = best_input;
1917 node_remove_extra_links(snode, link);
1918 link->flag &= ~NODE_LINKFLAG_HILITE;
1919
1920 nodeAddLink(snode->edittree, select, best_output, node, sockto);
1921
1922 /* set up insert offset data, it needs stuff from here */
1923 if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
1924 NodeInsertOfsData *iofsd = MEM_callocN(sizeof(NodeInsertOfsData), __func__);
1925
1926 iofsd->insert = select;
1927 iofsd->prev = link->fromnode;
1928 iofsd->next = node;
1929
1930 snode->iofsd = iofsd;
1931 }
1932
1933 ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */
1934 snode_update(snode, select);
1935 ED_node_tag_update_id(snode->id);
1936 }
1937 }
1938 }
1939