Blender  V2.93
node_relationships.c
Go to the documentation of this file.
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 
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_curve.h"
36 #include "BKE_lib_id.h"
37 #include "BKE_main.h"
38 #include "BKE_node.h"
39 
40 #include "ED_node.h" /* own include */
41 #include "ED_render.h"
42 #include "ED_screen.h"
43 #include "ED_util.h"
44 
45 #include "RNA_access.h"
46 #include "RNA_define.h"
47 
48 #include "WM_api.h"
49 #include "WM_types.h"
50 
51 #include "UI_resources.h"
52 #include "UI_view2d.h"
53 
54 #include "BLT_translation.h"
55 
56 #include "node_intern.h" /* own include */
57 
58 /* ****************** Relations helpers *********************** */
59 
61 {
62  const AnimData *adt = BKE_animdata_from_id(&ntree->id);
63  if (adt == NULL) {
64  return false;
65  }
66  return !BLI_listbase_is_empty(&adt->drivers);
67 }
68 
70 {
71  if (from->flag & NODE_TEST) {
72  return false;
73  }
74  from->flag |= NODE_TEST;
75  LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
76  if (link->fromnode == from) {
77  if (link->tonode == to) {
78  return true;
79  }
80 
81  if (ntree_check_nodes_connected_dfs(ntree, link->tonode, to)) {
82  return true;
83  }
84  }
85  }
86  return false;
87 }
88 
90 {
91  if (from == to) {
92  return true;
93  }
96 }
97 
99 {
100  bNodeTree *ntree = (bNodeTree *)node->id;
101  if (ntree->id.tag & LIB_TAG_DOIT) {
102  return false;
103  }
104  ntree->id.tag |= LIB_TAG_DOIT;
105  for (bNode *current_node = ntree->nodes.first; current_node != NULL;
106  current_node = current_node->next) {
107  if (current_node->type == NODE_GROUP) {
108  if (current_node->id && node_group_has_output_dfs(current_node)) {
109  return true;
110  }
111  }
112  if (current_node->flag & NODE_DO_OUTPUT && current_node->type != NODE_GROUP_OUTPUT) {
113  return true;
114  }
115  }
116  return false;
117 }
118 
119 static bool node_group_has_output(Main *bmain, bNode *node)
120 {
122  bNodeTree *ntree = (bNodeTree *)node->id;
123  if (ntree == NULL) {
124  return false;
125  }
128 }
129 
131 {
132  /* Special case for drivers: if node tree has any drivers we assume it is
133  * always to be tagged for update when node changes. Otherwise we will be
134  * doomed to do some deep and nasty deep search of indirect dependencies,
135  * which will be too complicated without real benefit.
136  */
137  if (ntree_has_drivers(ntree)) {
138  return true;
139  }
140  LISTBASE_FOREACH (bNode *, current_node, &ntree->nodes) {
141  /* Special case for group nodes -- if modified node connected to a group
142  * with active output inside we consider refresh is needed.
143  *
144  * We could make check more grained here by taking which socket the node
145  * is connected to and so eventually.
146  */
147  if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
148  if (current_node->id != NULL && ntree_has_drivers((bNodeTree *)current_node->id)) {
149  return true;
150  }
151  if (ntree_check_nodes_connected(ntree, node, current_node) &&
152  node_group_has_output(bmain, current_node)) {
153  return true;
154  }
155  }
156  if (current_node->flag & NODE_DO_OUTPUT) {
157  if (ntree_check_nodes_connected(ntree, node, current_node)) {
158  return true;
159  }
160  }
161  }
162  return false;
163 }
164 
165 /* ****************** Add *********************** */
166 
167 typedef struct bNodeListItem {
169  struct bNode *node;
171 
172 typedef struct NodeInsertOfsData {
174  bNode *insert; /* inserted node */
175  bNode *prev, *next; /* prev/next node in the chain */
177 
179 
180  float offset_x; /* offset to apply to node chain */
182 
184 {
185  LISTBASE_FOREACH (bNodeLink *, link, links) {
186  link->flag &= ~NODE_LINK_TEMP_HIGHLIGHT;
187  }
188 }
189 
190 static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bNodeSocket *sock)
191 {
192  LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
193  bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
194  linkdata->data = oplink;
195  if (sock->in_out == SOCK_OUT) {
196  oplink->fromnode = node;
197  oplink->fromsock = sock;
198  }
199  else {
200  oplink->tonode = node;
201  oplink->tosock = sock;
202  }
203  oplink->flag |= NODE_LINK_VALID;
204  oplink->flag &= ~NODE_LINK_TEST;
205  if (node_connected_to_output(bmain, snode->edittree, node)) {
206  oplink->flag |= NODE_LINK_TEST;
207  }
208  return linkdata;
209 }
210 
211 static void pick_link(const bContext *C,
212  wmOperator *op,
213  bNodeLinkDrag *nldrag,
214  SpaceNode *snode,
215  bNode *node,
216  bNodeLink *link_to_pick)
217 {
219  RNA_boolean_set(op->ptr, "has_link_picked", true);
220 
221  Main *bmain = CTX_data_main(C);
222  LinkData *linkdata = create_drag_link(
223  bmain, snode, link_to_pick->fromnode, link_to_pick->fromsock);
224 
225  BLI_addtail(&nldrag->links, linkdata);
226  nodeRemLink(snode->edittree, link_to_pick);
227 
229 
232 
233  /* Send changed event to original link->tonode. */
234  if (node) {
235  snode_update(snode, node);
236  }
237 }
238 
240  wmOperator *op,
241  bNodeLinkDrag *nldrag,
242  const float *cursor)
243 {
244  SpaceNode *snode = CTX_wm_space_node(C);
245  const ARegion *region = CTX_wm_region(C);
246  const View2D *v2d = &region->v2d;
247 
248  float drag_start[2];
249  RNA_float_get_array(op->ptr, "drag_start", drag_start);
250  bNode *node;
251  bNodeSocket *socket;
252  node_find_indicated_socket(snode, &node, &socket, drag_start, SOCK_IN);
253 
254  /* Distance to test overlapping of cursor on link. */
255  const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC;
256 
257  const int resolution = NODE_LINK_RESOL;
258 
259  bNodeLink *link_to_pick = NULL;
261  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
262  if (link->tosock == socket) {
263  /* Test if the cursor is near a link. */
264  float vec[4][2];
265  node_link_bezier_handles(v2d, snode, link, vec);
266 
267  float data[NODE_LINK_RESOL * 2 + 2];
269  vec[0][0], vec[1][0], vec[2][0], vec[3][0], data, resolution, sizeof(float[2]));
271  vec[0][1], vec[1][1], vec[2][1], vec[3][1], data + 1, resolution, sizeof(float[2]));
272 
273  for (int i = 0; i < resolution * 2; i += 2) {
274  float *l1 = &data[i];
275  float *l2 = &data[i + 2];
276  float distance = dist_squared_to_line_segment_v2(cursor, l1, l2);
277  if (distance < cursor_link_touch_distance) {
278  link_to_pick = link;
279  nldrag->last_picked_multi_input_socket_link = link_to_pick;
280  }
281  }
282  }
283  }
284 
285  /* If no linked was picked in this call, try using the one picked in the previous call.
286  * Not essential for the basic behavior, but can make interaction feel a bit better if
287  * the mouse moves to the right and loses the "selection." */
288  if (!link_to_pick) {
289  bNodeLink *last_picked_link = nldrag->last_picked_multi_input_socket_link;
290  if (last_picked_link) {
291  link_to_pick = last_picked_link;
292  }
293  }
294 
295  if (link_to_pick) {
296  /* Highlight is set here and cleared in the next iteration or if the operation finishes. */
297  link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
299 
300  if (!node_find_indicated_socket(snode, &node, &socket, cursor, SOCK_IN)) {
301  pick_link(C, op, nldrag, snode, node, link_to_pick);
302  }
303  }
304 }
305 
306 static int sort_nodes_locx(const void *a, const void *b)
307 {
308  const bNodeListItem *nli1 = a;
309  const bNodeListItem *nli2 = b;
310  const bNode *node1 = nli1->node;
311  const bNode *node2 = nli2->node;
312 
313  if (node1->locx > node2->locx) {
314  return 1;
315  }
316  return 0;
317 }
318 
319 static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used)
320 {
321  if (nodeSocketIsHidden(sock)) {
322  return 0;
323  }
324 
325  if (!allow_used && (sock->flag & SOCK_IN_USE)) {
326  return 0;
327  }
328 
329  return 1;
330 }
331 
333  bNode *node,
334  bNodeSocket *sock_target,
335  const bool allow_multiple)
336 {
337  /* first look for selected output */
338  LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
339  if (!socket_is_available(ntree, sock, allow_multiple)) {
340  continue;
341  }
342 
343  if (sock->flag & SELECT) {
344  return sock;
345  }
346  }
347 
348  /* try to find a socket with a matching name */
349  LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
350  if (!socket_is_available(ntree, sock, allow_multiple)) {
351  continue;
352  }
353 
354  /* check for same types */
355  if (sock->type == sock_target->type) {
356  if (STREQ(sock->name, sock_target->name)) {
357  return sock;
358  }
359  }
360  }
361 
362  /* otherwise settle for the first available socket of the right type */
363  LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
364  if (!socket_is_available(ntree, sock, allow_multiple)) {
365  continue;
366  }
367 
368  /* check for same types */
369  if (sock->type == sock_target->type) {
370  return sock;
371  }
372  }
373 
374  /* Always allow linking to an reroute node. The socket type of the reroute sockets might change
375  * after the link has been created. */
376  if (node->type == NODE_REROUTE) {
377  return node->outputs.first;
378  }
379 
380  return NULL;
381 }
382 
383 /* this is a bit complicated, but designed to prioritize finding
384  * sockets of higher types, such as image, first */
385 static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
386 {
387  int maxtype = 0;
388  LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
389  maxtype = max_ii(sock->type, maxtype);
390  }
391 
392  /* find sockets of higher 'types' first (i.e. image) */
393  int a = 0;
394  for (int socktype = maxtype; socktype >= 0; socktype--) {
395  LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
396  if (!socket_is_available(ntree, sock, replace)) {
397  a++;
398  continue;
399  }
400 
401  if (sock->type == socktype) {
402  /* increment to make sure we don't keep finding
403  * the same socket on every attempt running this function */
404  a++;
405  if (a > num) {
406  return sock;
407  }
408  }
409  }
410  }
411 
412  return NULL;
413 }
414 
416  bNode *node_fr,
417  bNodeSocket *sock_fr,
418  bNode *node_to,
419  bNodeSocket *sock_to,
420  int replace)
421 {
422  bNodeTree *ntree = snode->edittree;
423 
424  /* then we can connect */
425  if (replace) {
426  nodeRemSocketLinks(ntree, sock_to);
427  }
428 
429  nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to);
430  return true;
431 }
432 
433 typedef struct LinkAndPosition {
434  struct bNodeLink *link;
437 
438 static int compare_link_by_y_position(const void *a, const void *b)
439 {
440  const LinkAndPosition *link_and_position_a = *(const LinkAndPosition **)a;
441  const LinkAndPosition *link_and_position_b = *(const LinkAndPosition **)b;
442 
443  BLI_assert(link_and_position_a->link->tosock == link_and_position_b->link->tosock);
444  const float link_a_y = link_and_position_a->multi_socket_position[1];
445  const float link_b_y = link_and_position_b->multi_socket_position[1];
446  return link_a_y > link_b_y ? 1 : -1;
447 }
448 
450  bNode *node,
451  bNodeLink *drag_link,
452  float cursor[2])
453 {
454  LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
455  if (!(socket->flag & SOCK_MULTI_INPUT)) {
456  continue;
457  }
458  /* The total is calculated in #node_update_nodetree, which runs before this draw step. */
459  int total_inputs = socket->total_inputs + 1;
460  struct LinkAndPosition **input_links = MEM_malloc_arrayN(
461  total_inputs, sizeof(LinkAndPosition *), __func__);
462 
463  int index = 0;
464  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
465  if (link->tosock == socket) {
466  struct LinkAndPosition *link_and_position = MEM_callocN(sizeof(struct LinkAndPosition),
467  __func__);
468  link_and_position->link = link;
470  link->tosock->locy,
473  link_and_position->multi_socket_position);
474  input_links[index] = link_and_position;
475  index++;
476  }
477  }
478 
479  if (drag_link) {
480  LinkAndPosition *link_and_position = MEM_callocN(sizeof(LinkAndPosition), __func__);
481  link_and_position->link = drag_link;
482  copy_v2_v2(link_and_position->multi_socket_position, cursor);
483  input_links[index] = link_and_position;
484  index++;
485  }
486 
487  qsort(input_links, index, sizeof(bNodeLink *), compare_link_by_y_position);
488 
489  for (int i = 0; i < index; i++) {
490  input_links[i]->link->multi_input_socket_index = i;
491  }
492 
493  for (int i = 0; i < index; i++) {
494  if (input_links[i]) {
495  MEM_freeN(input_links[i]);
496  }
497  }
498  MEM_freeN(input_links);
499  }
500 }
501 
502 static void snode_autoconnect(Main *bmain,
503  SpaceNode *snode,
504  const bool allow_multiple,
505  const bool replace)
506 {
507  bNodeTree *ntree = snode->edittree;
508  ListBase *nodelist = MEM_callocN(sizeof(ListBase), "items_list");
509 
511  if (node->flag & NODE_SELECT) {
512  bNodeListItem *nli = MEM_mallocN(sizeof(bNodeListItem), "temporary node list item");
513  nli->node = node;
514  BLI_addtail(nodelist, nli);
515  }
516  }
517 
518  /* sort nodes left to right */
520 
521  int numlinks = 0;
522  LISTBASE_FOREACH (bNodeListItem *, nli, nodelist) {
523  bool has_selected_inputs = false;
524 
525  if (nli->next == NULL) {
526  break;
527  }
528 
529  bNode *node_fr = nli->node;
530  bNode *node_to = nli->next->node;
531  /* corner case: input/output node aligned the wrong way around (T47729) */
532  if (BLI_listbase_is_empty(&node_to->inputs) || BLI_listbase_is_empty(&node_fr->outputs)) {
533  SWAP(bNode *, node_fr, node_to);
534  }
535 
536  /* if there are selected sockets, connect those */
537  LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) {
538  if (sock_to->flag & SELECT) {
539  has_selected_inputs = 1;
540 
541  if (!socket_is_available(ntree, sock_to, replace)) {
542  continue;
543  }
544 
545  /* check for an appropriate output socket to connect from */
546  bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
547  if (!sock_fr) {
548  continue;
549  }
550 
551  if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
552  numlinks++;
553  }
554  }
555  }
556 
557  if (!has_selected_inputs) {
558  /* no selected inputs, connect by finding suitable match */
559  int num_inputs = BLI_listbase_count(&node_to->inputs);
560 
561  for (int i = 0; i < num_inputs; i++) {
562 
563  /* find the best guess input socket */
564  bNodeSocket *sock_to = best_socket_input(ntree, node_to, i, replace);
565  if (!sock_to) {
566  continue;
567  }
568 
569  /* check for an appropriate output socket to connect from */
570  bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
571  if (!sock_fr) {
572  continue;
573  }
574 
575  if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
576  numlinks++;
577  break;
578  }
579  }
580  }
581  }
582 
583  if (numlinks > 0) {
584  ntreeUpdateTree(bmain, ntree);
585  }
586 
587  BLI_freelistN(nodelist);
588  MEM_freeN(nodelist);
589 }
590 
591 /* *************************** link viewer op ******************** */
592 
593 static int node_link_viewer(const bContext *C, bNode *tonode)
594 {
595  SpaceNode *snode = CTX_wm_space_node(C);
596 
597  /* context check */
598  if (tonode == NULL || BLI_listbase_is_empty(&tonode->outputs)) {
599  return OPERATOR_CANCELLED;
600  }
602  return OPERATOR_CANCELLED;
603  }
604 
605  /* get viewer */
606  bNode *viewer_node = NULL;
607  LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
609  if (node->flag & NODE_DO_OUTPUT) {
610  viewer_node = node;
611  break;
612  }
613  }
614  }
615  /* no viewer, we make one active */
616  if (viewer_node == NULL) {
617  LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
619  node->flag |= NODE_DO_OUTPUT;
620  viewer_node = node;
621  break;
622  }
623  }
624  }
625 
626  bNodeSocket *sock = NULL;
627  bNodeLink *link = NULL;
628 
629  /* try to find an already connected socket to cycle to the next */
630  if (viewer_node) {
631  link = NULL;
632 
633  for (link = snode->edittree->links.first; link; link = link->next) {
634  if (link->tonode == viewer_node && link->fromnode == tonode) {
635  if (link->tosock == viewer_node->inputs.first) {
636  break;
637  }
638  }
639  }
640  if (link) {
641  /* unlink existing connection */
642  sock = link->fromsock;
643  nodeRemLink(snode->edittree, link);
644 
645  /* find a socket after the previously connected socket */
646  for (sock = sock->next; sock; sock = sock->next) {
647  if (!nodeSocketIsHidden(sock)) {
648  break;
649  }
650  }
651  }
652  }
653 
654  if (tonode) {
655  /* Find a selected socket that overrides the socket to connect to */
656  LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) {
657  if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) {
658  sock = sock2;
659  break;
660  }
661  }
662  }
663 
664  /* find a socket starting from the first socket */
665  if (!sock) {
666  for (sock = tonode->outputs.first; sock; sock = sock->next) {
667  if (!nodeSocketIsHidden(sock)) {
668  break;
669  }
670  }
671  }
672 
673  if (sock) {
674  /* add a new viewer if none exists yet */
675  if (!viewer_node) {
676  /* XXX location is a quick hack, just place it next to the linked socket */
677  viewer_node = node_add_node(C, NULL, CMP_NODE_VIEWER, sock->locx + 100, sock->locy);
678  if (!viewer_node) {
679  return OPERATOR_CANCELLED;
680  }
681 
682  link = NULL;
683  }
684  else {
685  /* get link to viewer */
686  for (link = snode->edittree->links.first; link; link = link->next) {
687  if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) {
688  break;
689  }
690  }
691  }
692 
693  if (link == NULL) {
694  nodeAddLink(snode->edittree, tonode, sock, viewer_node, viewer_node->inputs.first);
695  }
696  else {
697  link->fromnode = tonode;
698  link->fromsock = sock;
699  /* make sure the dependency sorting is updated */
701  }
703  snode_update(snode, viewer_node);
704  }
705 
706  return OPERATOR_FINISHED;
707 }
708 
710 {
711  SpaceNode *snode = CTX_wm_space_node(C);
712  bNode *node = nodeGetActive(snode->edittree);
713 
714  if (!node) {
715  return OPERATOR_CANCELLED;
716  }
717 
719 
721  return OPERATOR_CANCELLED;
722  }
723 
724  snode_notify(C, snode);
725 
726  return OPERATOR_FINISHED;
727 }
728 
730 {
731  /* identifiers */
732  ot->name = "Link to Viewer Node";
733  ot->description = "Link to viewer node";
734  ot->idname = "NODE_OT_link_viewer";
735 
736  /* api callbacks */
739 
740  /* flags */
742 }
743 
744 /* *************************** add link op ******************** */
745 
747 {
748  char header[UI_MAX_DRAW_STR];
749 
750  BLI_strncpy(header, TIP_("LMB: drag node link, RMB: cancel"), sizeof(header));
751  ED_workspace_status_text(C, header);
752 }
753 
754 static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
755 {
756  int count = 0;
758  if (ELEM(socket, link->fromsock, link->tosock)) {
759  count++;
760  }
761  }
762  return count;
763 }
764 
766 {
767  bNodeTree *ntree = snode->edittree;
768  bNodeSocket *from = link->fromsock, *to = link->tosock;
769  int to_count = node_count_links(ntree, to);
770  int from_count = node_count_links(ntree, from);
771  int to_link_limit = nodeSocketLinkLimit(to);
772  int from_link_limit = nodeSocketLinkLimit(from);
773 
775  if (tlink == link) {
776  continue;
777  }
778 
779  if (tlink && tlink->fromsock == from) {
780  if (from_count > from_link_limit) {
781  nodeRemLink(ntree, tlink);
782  tlink = NULL;
783  from_count--;
784  }
785  }
786 
787  if (tlink && tlink->tosock == to) {
788  if (to_count > to_link_limit) {
789  nodeRemLink(ntree, tlink);
790  tlink = NULL;
791  to_count--;
792  }
793  else if (tlink->fromsock == from) {
794  /* Also remove link if it comes from the same output. */
795  nodeRemLink(ntree, tlink);
796  tlink = NULL;
797  to_count--;
798  from_count--;
799  }
800  }
801  }
802 }
803 
804 static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
805 {
806  Main *bmain = CTX_data_main(C);
807  SpaceNode *snode = CTX_wm_space_node(C);
808  bNodeTree *ntree = snode->edittree;
809  bNodeLinkDrag *nldrag = op->customdata;
810  bool do_tag_update = false;
811 
812  /* avoid updates while applying links */
813  ntree->is_updating = true;
814  LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
815  bNodeLink *link = linkdata->data;
816 
817  /* See note below, but basically TEST flag means that the link
818  * was connected to output (or to a node which affects the
819  * output).
820  */
821  do_tag_update |= (link->flag & NODE_LINK_TEST) != 0;
822 
823  if (apply_links && link->tosock && link->fromsock) {
824  /* before actually adding the link,
825  * let nodes perform special link insertion handling
826  */
829  }
830  if (link->tonode->typeinfo->insert_link) {
832  }
833 
834  /* add link to the node tree */
836 
838 
839  /* tag tonode for update */
841 
842  /* we might need to remove a link */
844 
845  if (link->tonode) {
846  do_tag_update |= (do_tag_update || node_connected_to_output(bmain, ntree, link->tonode));
847  }
848  }
849  else {
851  }
852  }
853  ntree->is_updating = false;
854 
855  do_tag_update |= ED_node_is_geometry(snode);
856 
857  ntreeUpdateTree(bmain, ntree);
858  snode_notify(C, snode);
859  if (do_tag_update) {
860  snode_dag_update(C, snode);
861  }
862 
863  BLI_remlink(&snode->runtime->linkdrag, nldrag);
864  /* links->data pointers are either held by the tree or freed already */
865  BLI_freelistN(&nldrag->links);
866  MEM_freeN(nldrag);
867 }
868 
869 static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
870 {
871  SpaceNode *snode = CTX_wm_space_node(C);
872  bNodeLinkDrag *nldrag = op->customdata;
873 
874  if (nldrag->in_out == SOCK_OUT) {
875  bNode *tnode;
876  bNodeSocket *tsock = NULL;
877  if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) {
878  LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
879  bNodeLink *link = linkdata->data;
880 
881  /* skip if socket is on the same node as the fromsock */
882  if (tnode && link->fromnode == tnode) {
883  continue;
884  }
885 
886  /* Skip if tsock is already linked with this output. */
887  bNodeLink *existing_link_connected_to_fromsock = NULL;
888  LISTBASE_FOREACH (bNodeLink *, existing_link, &snode->edittree->links) {
889  if (existing_link->fromsock == link->fromsock && existing_link->tosock == tsock) {
890  existing_link_connected_to_fromsock = existing_link;
891  break;
892  }
893  }
894 
895  /* attach links to the socket */
896  link->tonode = tnode;
897  link->tosock = tsock;
899  if (existing_link_connected_to_fromsock) {
901  existing_link_connected_to_fromsock->multi_input_socket_index;
902  continue;
903  }
904  if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) {
905  sort_multi_input_socket_links(snode, tnode, link, cursor);
906  }
907  }
908  }
909  else {
910  LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
911  bNodeLink *link = linkdata->data;
914  snode, nldrag->last_node_hovered_while_dragging_a_link, NULL, cursor);
915  }
916  link->tonode = NULL;
917  link->tosock = NULL;
918  }
919  }
920  }
921  else {
922  bNode *tnode;
923  bNodeSocket *tsock = NULL;
924  if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) {
925  LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
926  bNodeLink *link = linkdata->data;
927 
928  /* skip if this is already the target socket */
929  if (link->fromsock == tsock) {
930  continue;
931  }
932  /* skip if socket is on the same node as the fromsock */
933  if (tnode && link->tonode == tnode) {
934  continue;
935  }
936 
937  /* attach links to the socket */
938  link->fromnode = tnode;
939  link->fromsock = tsock;
940  }
941  }
942  else {
943  LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
944  bNodeLink *link = linkdata->data;
945 
946  link->fromnode = NULL;
947  link->fromsock = NULL;
948  }
949  }
950  }
951 }
952 
953 /* Loop that adds a node-link, called by function below. */
954 /* in_out = starting socket */
955 static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
956 {
957  bNodeLinkDrag *nldrag = op->customdata;
958  ARegion *region = CTX_wm_region(C);
959  float cursor[2];
960 
961  UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]);
962 
963  switch (event->type) {
964  case MOUSEMOVE:
965  if (nldrag->from_multi_input_socket && !RNA_boolean_get(op->ptr, "has_link_picked")) {
966  pick_input_link_by_link_intersect(C, op, nldrag, cursor);
967  }
968  else {
969  node_link_find_socket(C, op, cursor);
970 
971  node_link_update_header(C, nldrag);
972  ED_region_tag_redraw(region);
973  }
974  break;
975 
976  case LEFTMOUSE:
977  case RIGHTMOUSE:
978  case MIDDLEMOUSE: {
979  if (event->val == KM_RELEASE) {
980  node_link_exit(C, op, true);
981 
983  ED_region_tag_redraw(region);
984  SpaceNode *snode = CTX_wm_space_node(C);
986  return OPERATOR_FINISHED;
987  }
988  break;
989  }
990  }
991 
992  return OPERATOR_RUNNING_MODAL;
993 }
994 
995 /* return 1 when socket clicked */
996 static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor[2], bool detach)
997 {
998  bNodeLinkDrag *nldrag = NULL;
999 
1000  /* output indicated? */
1001  bNode *node;
1002  bNodeSocket *sock;
1003  if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
1004  nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
1005 
1006  const int num_links = nodeCountSocketLinks(snode->edittree, sock);
1007  int link_limit = nodeSocketLinkLimit(sock);
1008  if (num_links > 0 && (num_links >= link_limit || detach)) {
1009  /* dragged links are fixed on input side */
1010  nldrag->in_out = SOCK_IN;
1011  /* detach current links and store them in the operator data */
1013  if (link->fromsock == sock) {
1014  LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
1015  bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
1016  linkdata->data = oplink;
1017  *oplink = *link;
1018  oplink->next = oplink->prev = NULL;
1019  oplink->flag |= NODE_LINK_VALID;
1020 
1021  /* The link could be disconnected and in that case we
1022  * wouldn't be able to check whether tag update is
1023  * needed or not when releasing mouse button. So we
1024  * cache whether the link affects output or not here
1025  * using TEST flag.
1026  */
1027  oplink->flag &= ~NODE_LINK_TEST;
1028  if (node_connected_to_output(bmain, snode->edittree, link->tonode)) {
1029  oplink->flag |= NODE_LINK_TEST;
1030  }
1031 
1032  BLI_addtail(&nldrag->links, linkdata);
1033  nodeRemLink(snode->edittree, link);
1034  }
1035  }
1036  }
1037  else {
1038  /* dragged links are fixed on output side */
1039  nldrag->in_out = SOCK_OUT;
1040  /* create a new link */
1041  LinkData *linkdata = create_drag_link(bmain, snode, node, sock);
1042 
1043  BLI_addtail(&nldrag->links, linkdata);
1044  }
1045  }
1046  /* or an input? */
1047  else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
1048  nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata");
1050 
1051  const int num_links = nodeCountSocketLinks(snode->edittree, sock);
1052  if (num_links > 0) {
1053  /* dragged links are fixed on output side */
1054  nldrag->in_out = SOCK_OUT;
1055  /* detach current links and store them in the operator data */
1056  bNodeLink *link_to_pick;
1058  if (link->tosock == sock) {
1059  if (sock->flag & SOCK_MULTI_INPUT) {
1060  nldrag->from_multi_input_socket = true;
1061  }
1062  link_to_pick = link;
1063  }
1064  }
1065 
1066  if (link_to_pick != NULL && !nldrag->from_multi_input_socket) {
1067  LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
1068  bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
1069  linkdata->data = oplink;
1070  *oplink = *link_to_pick;
1071  oplink->next = oplink->prev = NULL;
1072  oplink->flag |= NODE_LINK_VALID;
1073  oplink->flag &= ~NODE_LINK_TEST;
1074  if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) {
1075  oplink->flag |= NODE_LINK_TEST;
1076  }
1077 
1078  BLI_addtail(&nldrag->links, linkdata);
1079  nodeRemLink(snode->edittree, link_to_pick);
1080 
1081  /* send changed event to original link->tonode */
1082  if (node) {
1083  snode_update(snode, node);
1084  }
1085  }
1086  }
1087  else {
1088  /* dragged links are fixed on input side */
1089  nldrag->in_out = SOCK_IN;
1090  /* create a new link */
1091  LinkData *linkdata = create_drag_link(bmain, snode, node, sock);
1092 
1093  BLI_addtail(&nldrag->links, linkdata);
1094  }
1095  }
1096 
1097  return nldrag;
1098 }
1099 
1100 static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1101 {
1102  Main *bmain = CTX_data_main(C);
1103  SpaceNode *snode = CTX_wm_space_node(C);
1104  ARegion *region = CTX_wm_region(C);
1105 
1106  bool detach = RNA_boolean_get(op->ptr, "detach");
1107 
1108  float cursor[2];
1109  UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]);
1110  RNA_float_set_array(op->ptr, "drag_start", cursor);
1111  RNA_boolean_set(op->ptr, "has_link_picked", false);
1112 
1114 
1115  bNodeLinkDrag *nldrag = node_link_init(bmain, snode, cursor, detach);
1116 
1117  if (nldrag) {
1118  op->customdata = nldrag;
1119  BLI_addtail(&snode->runtime->linkdrag, nldrag);
1120 
1121  /* add modal handler */
1123 
1124  return OPERATOR_RUNNING_MODAL;
1125  }
1127 }
1128 
1130 {
1131  SpaceNode *snode = CTX_wm_space_node(C);
1132  bNodeLinkDrag *nldrag = op->customdata;
1133 
1134  BLI_remlink(&snode->runtime->linkdrag, nldrag);
1135 
1136  BLI_freelistN(&nldrag->links);
1137  MEM_freeN(nldrag);
1139 }
1140 
1142 {
1143  /* identifiers */
1144  ot->name = "Link Nodes";
1145  ot->idname = "NODE_OT_link";
1146  ot->description = "Use the mouse to create a link between two nodes";
1147 
1148  /* api callbacks */
1151  // ot->exec = node_link_exec;
1154 
1155  /* flags */
1157 
1158  PropertyRNA *prop;
1159 
1160  RNA_def_boolean(ot->srna, "detach", false, "Detach", "Detach and redirect existing links");
1161  prop = RNA_def_boolean(
1162  ot->srna,
1163  "has_link_picked",
1164  false,
1165  "Has Link Picked",
1166  "The operation has placed a link. Only used for multi-input sockets, where the "
1167  "link is picked later");
1170  "drag_start",
1171  2,
1172  0,
1175  "Drag Start",
1176  "The position of the mouse cursor at the start of the operation",
1181 }
1182 
1183 /* ********************** Make Link operator ***************** */
1184 
1185 /* makes a link between selected output and input sockets */
1187 {
1188  Main *bmain = CTX_data_main(C);
1189  SpaceNode *snode = CTX_wm_space_node(C);
1190  const bool replace = RNA_boolean_get(op->ptr, "replace");
1191 
1193 
1194  snode_autoconnect(bmain, snode, 1, replace);
1195 
1196  /* deselect sockets after linking */
1199 
1201  snode_notify(C, snode);
1202  snode_dag_update(C, snode);
1203 
1204  return OPERATOR_FINISHED;
1205 }
1206 
1208 {
1209  /* identifiers */
1210  ot->name = "Make Links";
1211  ot->description = "Makes a link between selected output in input sockets";
1212  ot->idname = "NODE_OT_link_make";
1213 
1214  /* callbacks */
1216  /* XXX we need a special poll which checks that there are selected input/output sockets. */
1218 
1219  /* flags */
1221 
1223  ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
1224 }
1225 
1226 /* ********************** Node Link Intersect ***************** */
1227 static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int tot)
1228 {
1229  float coord_array[NODE_LINK_RESOL + 1][2];
1230 
1231  if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
1232  for (int i = 0; i < tot - 1; i++) {
1233  for (int b = 0; b < NODE_LINK_RESOL; b++) {
1234  if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) {
1235  return 1;
1236  }
1237  }
1238  }
1239  }
1240  return 0;
1241 }
1242 
1243 /* ********************** Cut Link operator ***************** */
1245 {
1246  Main *bmain = CTX_data_main(C);
1247  SpaceNode *snode = CTX_wm_space_node(C);
1248  ARegion *region = CTX_wm_region(C);
1249  bool do_tag_update = false;
1250 
1251  int i = 0;
1252  float mcoords[256][2];
1253  RNA_BEGIN (op->ptr, itemptr, "path") {
1254  float loc[2];
1255 
1256  RNA_float_get_array(&itemptr, "loc", loc);
1258  &region->v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]);
1259  i++;
1260  if (i >= 256) {
1261  break;
1262  }
1263  }
1264  RNA_END;
1265 
1266  if (i > 1) {
1267  bool found = false;
1268 
1270 
1272  if (nodeLinkIsHidden(link)) {
1273  continue;
1274  }
1275 
1276  if (node_links_intersect(link, mcoords, i)) {
1277 
1278  if (found == false) {
1279  /* TODO(sergey): Why did we kill jobs twice? */
1281  found = true;
1282  }
1283 
1284  do_tag_update |= (do_tag_update ||
1285  node_connected_to_output(bmain, snode->edittree, link->tonode));
1286 
1287  snode_update(snode, link->tonode);
1288  bNode *to_node = link->tonode;
1289  nodeRemLink(snode->edittree, link);
1290  sort_multi_input_socket_links(snode, to_node, NULL, NULL);
1291  }
1292  }
1293 
1294  do_tag_update |= ED_node_is_geometry(snode);
1295 
1296  if (found) {
1298  snode_notify(C, snode);
1299  if (do_tag_update) {
1300  snode_dag_update(C, snode);
1301  }
1302 
1303  return OPERATOR_FINISHED;
1304  }
1305 
1306  return OPERATOR_CANCELLED;
1307  }
1308 
1310 }
1311 
1313 {
1314  ot->name = "Cut Links";
1315  ot->idname = "NODE_OT_links_cut";
1316  ot->description = "Use the mouse to cut (remove) some links";
1317 
1320  ot->exec = cut_links_exec;
1322 
1324 
1325  /* flags */
1327 
1328  /* properties */
1329  PropertyRNA *prop;
1330  prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
1332 
1333  /* internal */
1334  RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1335 }
1336 
1337 /* ********************** Mute links operator ***************** */
1338 
1340 {
1341  Main *bmain = CTX_data_main(C);
1342  SpaceNode *snode = CTX_wm_space_node(C);
1343  ARegion *region = CTX_wm_region(C);
1344  bool do_tag_update = false;
1345 
1346  int i = 0;
1347  float mcoords[256][2];
1348  RNA_BEGIN (op->ptr, itemptr, "path") {
1349  float loc[2];
1350 
1351  RNA_float_get_array(&itemptr, "loc", loc);
1353  &region->v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]);
1354  i++;
1355  if (i >= 256) {
1356  break;
1357  }
1358  }
1359  RNA_END;
1360 
1361  if (i > 1) {
1363 
1364  /* Count intersected links and clear test flag. */
1365  int tot = 0;
1366  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1367  if (nodeLinkIsHidden(link)) {
1368  continue;
1369  }
1370  link->flag &= ~NODE_LINK_TEST;
1371  if (node_links_intersect(link, mcoords, i)) {
1372  tot++;
1373  }
1374  }
1375  if (tot == 0) {
1376  return OPERATOR_CANCELLED;
1377  }
1378 
1379  /* Mute links. */
1380  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1381  if (nodeLinkIsHidden(link) || (link->flag & NODE_LINK_TEST)) {
1382  continue;
1383  }
1384 
1385  if (node_links_intersect(link, mcoords, i)) {
1386  do_tag_update |= (do_tag_update ||
1387  node_connected_to_output(bmain, snode->edittree, link->tonode));
1388 
1389  snode_update(snode, link->tonode);
1390  nodeMuteLinkToggle(snode->edittree, link);
1391  }
1392  }
1393 
1394  /* Clear remaining test flags. */
1395  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1396  if (nodeLinkIsHidden(link)) {
1397  continue;
1398  }
1399  link->flag &= ~NODE_LINK_TEST;
1400  }
1401 
1402  do_tag_update |= ED_node_is_geometry(snode);
1403 
1405  snode_notify(C, snode);
1406  if (do_tag_update) {
1407  snode_dag_update(C, snode);
1408  }
1409 
1410  return OPERATOR_FINISHED;
1411  }
1412 
1414 }
1415 
1417 {
1418  ot->name = "Mute Links";
1419  ot->idname = "NODE_OT_links_mute";
1420  ot->description = "Use the mouse to mute links";
1421 
1424  ot->exec = mute_links_exec;
1426 
1428 
1429  /* flags */
1431 
1432  /* properties */
1433  PropertyRNA *prop;
1434  prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
1436 
1437  /* internal */
1438  RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1439 }
1440 
1441 /* ********************** Detach links operator ***************** */
1442 
1444 {
1445  SpaceNode *snode = CTX_wm_space_node(C);
1446  bNodeTree *ntree = snode->edittree;
1447 
1449 
1451  if (node->flag & SELECT) {
1453  }
1454  }
1455 
1457 
1458  snode_notify(C, snode);
1459  snode_dag_update(C, snode);
1460 
1461  return OPERATOR_FINISHED;
1462 }
1463 
1465 {
1466  ot->name = "Detach Links";
1467  ot->idname = "NODE_OT_links_detach";
1468  ot->description =
1469  "Remove all links to selected nodes, and try to connect neighbor nodes together";
1470 
1473 
1474  /* flags */
1476 }
1477 
1478 /* ****************** Set Parent ******************* */
1479 
1481 {
1482  SpaceNode *snode = CTX_wm_space_node(C);
1483  bNodeTree *ntree = snode->edittree;
1484  bNode *frame = nodeGetActive(ntree);
1485  if (!frame || frame->type != NODE_FRAME) {
1486  return OPERATOR_CANCELLED;
1487  }
1488 
1490  if (node == frame) {
1491  continue;
1492  }
1493  if (node->flag & NODE_SELECT) {
1495  nodeAttachNode(node, frame);
1496  }
1497  }
1498 
1501 
1502  return OPERATOR_FINISHED;
1503 }
1504 
1506 {
1507  /* identifiers */
1508  ot->name = "Make Parent";
1509  ot->description = "Attach selected nodes";
1510  ot->idname = "NODE_OT_parent_set";
1511 
1512  /* api callbacks */
1515 
1516  /* flags */
1518 }
1519 
1520 /* ****************** Join Nodes ******************* */
1521 
1522 /* tags for depth-first search */
1523 #define NODE_JOIN_DONE 1
1524 #define NODE_JOIN_IS_DESCENDANT 2
1525 
1527 {
1528  node->done |= NODE_JOIN_DONE;
1529 
1530  if (node == frame) {
1531  node->done |= NODE_JOIN_IS_DESCENDANT;
1532  }
1533  else if (node->parent) {
1534  /* call recursively */
1535  if (!(node->parent->done & NODE_JOIN_DONE)) {
1536  node_join_attach_recursive(node->parent, frame);
1537  }
1538 
1539  /* in any case: if the parent is a descendant, so is the child */
1540  if (node->parent->done & NODE_JOIN_IS_DESCENDANT) {
1541  node->done |= NODE_JOIN_IS_DESCENDANT;
1542  }
1543  else if (node->flag & NODE_TEST) {
1544  /* if parent is not an descendant of the frame, reattach the node */
1546  nodeAttachNode(node, frame);
1547  node->done |= NODE_JOIN_IS_DESCENDANT;
1548  }
1549  }
1550  else if (node->flag & NODE_TEST) {
1551  nodeAttachNode(node, frame);
1552  node->done |= NODE_JOIN_IS_DESCENDANT;
1553  }
1554 }
1555 
1557 {
1558  SpaceNode *snode = CTX_wm_space_node(C);
1559  bNodeTree *ntree = snode->edittree;
1560 
1561  /* XXX save selection: node_add_node call below sets the new frame as single
1562  * active+selected node */
1564  if (node->flag & NODE_SELECT) {
1565  node->flag |= NODE_TEST;
1566  }
1567  else {
1568  node->flag &= ~NODE_TEST;
1569  }
1570  }
1571 
1572  bNode *frame = node_add_node(C, NULL, NODE_FRAME, 0.0f, 0.0f);
1573 
1574  /* reset tags */
1576  node->done = 0;
1577  }
1578 
1580  if (!(node->done & NODE_JOIN_DONE)) {
1582  }
1583  }
1584 
1585  /* restore selection */
1587  if (node->flag & NODE_TEST) {
1588  node->flag |= NODE_SELECT;
1589  }
1590  }
1591 
1594 
1595  return OPERATOR_FINISHED;
1596 }
1597 
1599 {
1600  /* identifiers */
1601  ot->name = "Join Nodes";
1602  ot->description = "Attach selected nodes to a new common frame";
1603  ot->idname = "NODE_OT_join";
1604 
1605  /* api callbacks */
1606  ot->exec = node_join_exec;
1608 
1609  /* flags */
1611 }
1612 
1613 /* ****************** Attach ******************* */
1614 
1616  const bNodeTree *ntree,
1617  const int mouse_xy[2])
1618 {
1619  /* convert mouse coordinates to v2d space */
1620  float cursor[2];
1621  UI_view2d_region_to_view(&region->v2d, UNPACK2(mouse_xy), &cursor[0], &cursor[1]);
1622 
1623  LISTBASE_FOREACH_BACKWARD (bNode *, frame, &ntree->nodes) {
1624  /* skip selected, those are the nodes we want to attach */
1625  if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
1626  continue;
1627  }
1628  if (BLI_rctf_isect_pt_v(&frame->totr, cursor)) {
1629  return frame;
1630  }
1631  }
1632 
1633  return NULL;
1634 }
1635 
1636 static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1637 {
1638  ARegion *region = CTX_wm_region(C);
1639  SpaceNode *snode = CTX_wm_space_node(C);
1640  bNodeTree *ntree = snode->edittree;
1641  bNode *frame = node_find_frame_to_attach(region, ntree, event->mval);
1642 
1643  if (frame) {
1645  if (node->flag & NODE_SELECT) {
1646  if (node->parent == NULL) {
1647  /* disallow moving a parent into its child */
1648  if (nodeAttachNodeCheck(frame, node) == false) {
1649  /* attach all unparented nodes */
1650  nodeAttachNode(node, frame);
1651  }
1652  }
1653  else {
1654  /* attach nodes which share parent with the frame */
1655  bNode *parent;
1656  for (parent = frame->parent; parent; parent = parent->parent) {
1657  if (parent == node->parent) {
1658  break;
1659  }
1660  }
1661 
1662  if (parent) {
1663  /* disallow moving a parent into its child */
1664  if (nodeAttachNodeCheck(frame, node) == false) {
1666  nodeAttachNode(node, frame);
1667  }
1668  }
1669  }
1670  }
1671  }
1672  }
1673 
1676 
1677  return OPERATOR_FINISHED;
1678 }
1679 
1681 {
1682  /* identifiers */
1683  ot->name = "Attach Nodes";
1684  ot->description = "Attach active node to a frame";
1685  ot->idname = "NODE_OT_attach";
1686 
1687  /* api callbacks */
1688 
1691 
1692  /* flags */
1694 }
1695 
1696 /* ****************** Detach ******************* */
1697 
1698 /* tags for depth-first search */
1699 #define NODE_DETACH_DONE 1
1700 #define NODE_DETACH_IS_DESCENDANT 2
1701 
1703 {
1704  node->done |= NODE_DETACH_DONE;
1705 
1706  if (node->parent) {
1707  /* call recursively */
1708  if (!(node->parent->done & NODE_DETACH_DONE)) {
1709  node_detach_recursive(node->parent);
1710  }
1711 
1712  /* in any case: if the parent is a descendant, so is the child */
1713  if (node->parent->done & NODE_DETACH_IS_DESCENDANT) {
1715  }
1716  else if (node->flag & NODE_SELECT) {
1717  /* if parent is not a descendant of a selected node, detach */
1720  }
1721  }
1722  else if (node->flag & NODE_SELECT) {
1724  }
1725 }
1726 
1727 /* detach the root nodes in the current selection */
1729 {
1730  SpaceNode *snode = CTX_wm_space_node(C);
1731  bNodeTree *ntree = snode->edittree;
1732 
1733  /* reset tags */
1735  node->done = 0;
1736  }
1737  /* detach nodes recursively
1738  * relative order is preserved here!
1739  */
1741  if (!(node->done & NODE_DETACH_DONE)) {
1743  }
1744  }
1745 
1748 
1749  return OPERATOR_FINISHED;
1750 }
1751 
1753 {
1754  /* identifiers */
1755  ot->name = "Detach Nodes";
1756  ot->description = "Detach selected nodes from parents";
1757  ot->idname = "NODE_OT_detach";
1758 
1759  /* api callbacks */
1762 
1763  /* flags */
1765 }
1766 
1767 /* ********************* automatic node insert on dragging ******************* */
1768 
1769 /* prevent duplicate testing code below */
1771  bool test,
1772  SpaceNode **r_snode,
1773  bNode **r_select)
1774 {
1775  SpaceNode *snode = area ? area->spacedata.first : NULL;
1776 
1777  *r_snode = snode;
1778  *r_select = NULL;
1779 
1780  /* no unlucky accidents */
1781  if (area == NULL || area->spacetype != SPACE_NODE) {
1782  return false;
1783  }
1784 
1785  if (!test) {
1786  /* no need to look for a node */
1787  return true;
1788  }
1789 
1790  bNode *node;
1791  bNode *select = NULL;
1792  for (node = snode->edittree->nodes.first; node; node = node->next) {
1793  if (node->flag & SELECT) {
1794  if (select) {
1795  break;
1796  }
1797  select = node;
1798  }
1799  }
1800  /* only one selected */
1801  if (node || select == NULL) {
1802  return false;
1803  }
1804 
1805  /* correct node */
1806  if (BLI_listbase_is_empty(&select->inputs) || BLI_listbase_is_empty(&select->outputs)) {
1807  return false;
1808  }
1809 
1810  /* test node for links */
1811  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1812  if (nodeLinkIsHidden(link)) {
1813  continue;
1814  }
1815 
1816  if (link->tonode == select || link->fromnode == select) {
1817  return false;
1818  }
1819  }
1820 
1821  *r_select = select;
1822  return true;
1823 }
1824 
1825 /* test == 0, clear all intersect flags */
1827 {
1828  bNode *select;
1829  SpaceNode *snode;
1830  if (!ed_node_link_conditions(area, test, &snode, &select)) {
1831  return;
1832  }
1833 
1834  /* clear flags */
1835  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1837  }
1838 
1839  if (test == 0) {
1840  return;
1841  }
1842 
1843  /* find link to select/highlight */
1844  bNodeLink *selink = NULL;
1845  float dist_best = FLT_MAX;
1846  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1847  float coord_array[NODE_LINK_RESOL + 1][2];
1848 
1849  if (nodeLinkIsHidden(link)) {
1850  continue;
1851  }
1852 
1853  if (node_link_bezier_points(NULL, NULL, link, coord_array, NODE_LINK_RESOL)) {
1854  float dist = FLT_MAX;
1855 
1856  /* loop over link coords to find shortest dist to
1857  * upper left node edge of a intersected line segment */
1858  for (int i = 0; i < NODE_LINK_RESOL; i++) {
1859  /* Check if the node rectangle intersects the line from this point to next one. */
1860  if (BLI_rctf_isect_segment(&select->totr, coord_array[i], coord_array[i + 1])) {
1861  /* store the shortest distance to the upper left edge
1862  * of all intersections found so far */
1863  const float node_xy[] = {select->totr.xmin, select->totr.ymax};
1864 
1865  /* to be precise coord_array should be clipped by select->totr,
1866  * but not done since there's no real noticeable difference */
1867  dist = min_ff(
1868  dist_squared_to_line_segment_v2(node_xy, coord_array[i], coord_array[i + 1]), dist);
1869  }
1870  }
1871 
1872  /* we want the link with the shortest distance to node center */
1873  if (dist < dist_best) {
1874  dist_best = dist;
1875  selink = link;
1876  }
1877  }
1878  }
1879 
1880  if (selink) {
1881  selink->flag |= NODE_LINKFLAG_HILITE;
1882  }
1883 }
1884 
1885 /* assumes sockets in list */
1887 {
1888  /* find type range */
1889  int maxtype = 0;
1890  LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
1891  maxtype = max_ii(sock->type, maxtype);
1892  }
1893 
1894  /* try all types, starting from 'highest' (i.e. colors, vectors, values) */
1895  for (int type = maxtype; type >= 0; type--) {
1896  LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
1897  if (!nodeSocketIsHidden(sock) && type == sock->type) {
1898  return sock;
1899  }
1900  }
1901  }
1902 
1903  /* no visible sockets, unhide first of highest type */
1904  for (int type = maxtype; type >= 0; type--) {
1905  LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
1906  if (type == sock->type) {
1907  sock->flag &= ~SOCK_HIDDEN;
1908  return sock;
1909  }
1910  }
1911  }
1912 
1913  return NULL;
1914 }
1915 
1916 static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
1917 {
1918  /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
1919  parent->flag |= NODE_TEST;
1920 
1921  return true;
1922 }
1923 
1924 static void node_offset_apply(bNode *node, const float offset_x)
1925 {
1926  /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
1927  if ((node->flag & NODE_TEST) == 0) {
1928  node->anim_init_locx = node->locx;
1929  node->anim_ofsx = (offset_x / UI_DPI_FAC);
1930  node->flag |= NODE_TEST;
1931  }
1932 }
1933 
1934 static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, const float offset_x)
1935 {
1936  node_offset_apply(parent, offset_x);
1937 
1938  /* Flag all children as offset to prevent them from being offset
1939  * separately (they've already moved with the parent). */
1940  LISTBASE_FOREACH (bNode *, node, &data->ntree->nodes) {
1941  if (nodeIsChildOf(parent, node)) {
1942  /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
1943  node->flag |= NODE_TEST;
1944  }
1945  }
1946 }
1947 
1948 #define NODE_INSOFS_ANIM_DURATION 0.25f
1949 
1955  bNode *tonode,
1956  void *userdata,
1957  const bool reversed)
1958 {
1959  NodeInsertOfsData *data = userdata;
1960  bNode *ofs_node = reversed ? fromnode : tonode;
1961 
1962  if (ofs_node->parent && ofs_node->parent != data->insert_parent) {
1963  node_offset_apply(ofs_node->parent, data->offset_x);
1964  }
1965  else {
1966  node_offset_apply(ofs_node, data->offset_x);
1967  }
1968 
1969  return true;
1970 }
1971 
1976  const bNode *parent,
1978  const bool reversed)
1979 {
1981  if (nodeIsChildOf(parent, node)) {
1983  }
1984  }
1985 }
1986 
1992  bNode *tonode,
1993  void *userdata,
1994  const bool reversed)
1995 {
1996  NodeInsertOfsData *data = userdata;
1997  bNode *ofs_node = reversed ? fromnode : tonode;
1998 
1999  if (data->insert_parent) {
2000  if (ofs_node->parent && (ofs_node->parent->flag & NODE_TEST) == 0) {
2001  node_parent_offset_apply(data, ofs_node->parent, data->offset_x);
2002  node_link_insert_offset_frame_chains(data->ntree, ofs_node->parent, data, reversed);
2003  }
2004  else {
2005  node_offset_apply(ofs_node, data->offset_x);
2006  }
2007 
2008  if (nodeIsChildOf(data->insert_parent, ofs_node) == false) {
2009  data->insert_parent = NULL;
2010  }
2011  }
2012  else if (ofs_node->parent) {
2013  bNode *node = nodeFindRootParent(ofs_node);
2014  node_offset_apply(node, data->offset_x);
2015  }
2016  else {
2017  node_offset_apply(ofs_node, data->offset_x);
2018  }
2019 
2020  return true;
2021 }
2022 
2024  ARegion *region,
2025  const int mouse_xy[2],
2026  const bool right_alignment)
2027 {
2028  bNodeTree *ntree = iofsd->ntree;
2029  bNode *insert = iofsd->insert;
2030  bNode *prev = iofsd->prev, *next = iofsd->next;
2031  bNode *init_parent = insert->parent; /* store old insert->parent for restoring later */
2032 
2033  const float min_margin = U.node_margin * UI_DPI_FAC;
2034  const float width = NODE_WIDTH(insert);
2035  const bool needs_alignment = (next->totr.xmin - prev->totr.xmax) < (width + (min_margin * 2.0f));
2036 
2037  float margin = width;
2038 
2039  /* NODE_TEST will be used later, so disable for all nodes */
2040  ntreeNodeFlagSet(ntree, NODE_TEST, false);
2041 
2042  /* insert->totr isn't updated yet,
2043  * so totr_insert is used to get the correct worldspace coords */
2044  rctf totr_insert;
2045  node_to_updated_rect(insert, &totr_insert);
2046 
2047  /* frame attachment wasn't handled yet
2048  * so we search the frame that the node will be attached to later */
2049  insert->parent = node_find_frame_to_attach(region, ntree, mouse_xy);
2050 
2051  /* this makes sure nodes are also correctly offset when inserting a node on top of a frame
2052  * without actually making it a part of the frame (because mouse isn't intersecting it)
2053  * - logic here is similar to node_find_frame_to_attach */
2054  if (!insert->parent ||
2055  (prev->parent && (prev->parent == next->parent) && (prev->parent != insert->parent))) {
2056  bNode *frame;
2057  rctf totr_frame;
2058 
2059  /* check nodes front to back */
2060  for (frame = ntree->nodes.last; frame; frame = frame->prev) {
2061  /* skip selected, those are the nodes we want to attach */
2062  if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
2063  continue;
2064  }
2065 
2066  /* for some reason frame y coords aren't correct yet */
2067  node_to_updated_rect(frame, &totr_frame);
2068 
2069  if (BLI_rctf_isect_x(&totr_frame, totr_insert.xmin) &&
2070  BLI_rctf_isect_x(&totr_frame, totr_insert.xmax)) {
2071  if (BLI_rctf_isect_y(&totr_frame, totr_insert.ymin) ||
2072  BLI_rctf_isect_y(&totr_frame, totr_insert.ymax)) {
2073  /* frame isn't insert->parent actually, but this is needed to make offsetting
2074  * nodes work correctly for above checked cases (it is restored later) */
2075  insert->parent = frame;
2076  break;
2077  }
2078  }
2079  }
2080  }
2081 
2082  /* *** ensure offset at the left (or right for right_alignment case) of insert_node *** */
2083 
2084  float dist = right_alignment ? totr_insert.xmin - prev->totr.xmax :
2085  next->totr.xmin - totr_insert.xmax;
2086  /* distance between insert_node and prev is smaller than min margin */
2087  if (dist < min_margin) {
2088  const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
2089 
2090  node_offset_apply(insert, addval);
2091 
2092  totr_insert.xmin += addval;
2093  totr_insert.xmax += addval;
2094  margin += min_margin;
2095  }
2096 
2097  /* *** ensure offset at the right (or left for right_alignment case) of insert_node *** */
2098 
2099  dist = right_alignment ? next->totr.xmin - totr_insert.xmax : totr_insert.xmin - prev->totr.xmax;
2100  /* distance between insert_node and next is smaller than min margin */
2101  if (dist < min_margin) {
2102  const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
2103  if (needs_alignment) {
2104  bNode *offs_node = right_alignment ? next : prev;
2105  if (!offs_node->parent || offs_node->parent == insert->parent ||
2106  nodeIsChildOf(offs_node->parent, insert)) {
2107  node_offset_apply(offs_node, addval);
2108  }
2109  else if (!insert->parent && offs_node->parent) {
2110  node_offset_apply(nodeFindRootParent(offs_node), addval);
2111  }
2112  margin = addval;
2113  }
2114  /* enough room is available, but we want to ensure the min margin at the right */
2115  else {
2116  /* offset inserted node so that min margin is kept at the right */
2117  node_offset_apply(insert, -addval);
2118  }
2119  }
2120 
2121  if (needs_alignment) {
2122  iofsd->insert_parent = insert->parent;
2123  iofsd->offset_x = margin;
2124 
2125  /* flag all parents of insert as offset to prevent them from being offset */
2127  /* iterate over entire chain and apply offsets */
2129  right_alignment ? next : prev,
2131  iofsd,
2132  !right_alignment);
2133  }
2134 
2135  insert->parent = init_parent;
2136 }
2137 
2142 {
2143  SpaceNode *snode = CTX_wm_space_node(C);
2144  NodeInsertOfsData *iofsd = snode->runtime->iofsd;
2145  bool redraw = false;
2146 
2147  if (!snode || event->type != TIMER || iofsd == NULL || iofsd->anim_timer != event->customdata) {
2148  return OPERATOR_PASS_THROUGH;
2149  }
2150 
2151  const float duration = (float)iofsd->anim_timer->duration;
2152 
2153  /* handle animation - do this before possibly aborting due to duration, since
2154  * main thread might be so busy that node hasn't reached final position yet */
2155  LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
2156  if (UNLIKELY(node->anim_ofsx)) {
2157  const float endval = node->anim_init_locx + node->anim_ofsx;
2158  if (IS_EQF(node->locx, endval) == false) {
2160  duration, node->anim_init_locx, node->anim_ofsx, NODE_INSOFS_ANIM_DURATION);
2161  if (node->anim_ofsx < 0) {
2162  CLAMP_MIN(node->locx, endval);
2163  }
2164  else {
2165  CLAMP_MAX(node->locx, endval);
2166  }
2167  redraw = true;
2168  }
2169  }
2170  }
2171  if (redraw) {
2173  }
2174 
2175  /* end timer + free insert offset data */
2176  if (duration > NODE_INSOFS_ANIM_DURATION) {
2178 
2179  LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
2180  node->anim_init_locx = node->anim_ofsx = 0.0f;
2181  }
2182 
2183  snode->runtime->iofsd = NULL;
2184  MEM_freeN(iofsd);
2185 
2187  }
2188 
2189  return OPERATOR_RUNNING_MODAL;
2190 }
2191 
2192 #undef NODE_INSOFS_ANIM_DURATION
2193 
2194 static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2195 {
2196  const SpaceNode *snode = CTX_wm_space_node(C);
2197  NodeInsertOfsData *iofsd = snode->runtime->iofsd;
2198 
2199  if (!iofsd || !iofsd->insert) {
2200  return OPERATOR_CANCELLED;
2201  }
2202 
2203  BLI_assert((snode->flag & SNODE_SKIP_INSOFFSET) == 0);
2204 
2205  iofsd->ntree = snode->edittree;
2207 
2209  iofsd, CTX_wm_region(C), event->mval, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT));
2210 
2211  /* add temp handler */
2213 
2214  return OPERATOR_RUNNING_MODAL;
2215 }
2216 
2218 {
2219  /* identifiers */
2220  ot->name = "Insert Offset";
2221  ot->description = "Automatically offset nodes on insertion";
2222  ot->idname = "NODE_OT_insert_offset";
2223 
2224  /* callbacks */
2228 
2229  /* flags */
2231 }
2232 
2233 /* assumes link with NODE_LINKFLAG_HILITE set */
2235 {
2236  bNode *select;
2237  SpaceNode *snode;
2238  if (!ed_node_link_conditions(area, true, &snode, &select)) {
2239  return;
2240  }
2241 
2242  /* get the link */
2243  bNodeLink *link;
2244  for (link = snode->edittree->links.first; link; link = link->next) {
2245  if (link->flag & NODE_LINKFLAG_HILITE) {
2246  break;
2247  }
2248  }
2249 
2250  if (link) {
2251  bNodeSocket *best_input = socket_best_match(&select->inputs);
2252  bNodeSocket *best_output = socket_best_match(&select->outputs);
2253 
2254  if (best_input && best_output) {
2255  bNode *node = link->tonode;
2256  bNodeSocket *sockto = link->tosock;
2257 
2258  link->tonode = select;
2259  link->tosock = best_input;
2260  node_remove_extra_links(snode, link);
2262 
2263  bNodeLink *new_link = nodeAddLink(snode->edittree, select, best_output, node, sockto);
2264 
2265  /* Copy the socket index for the new link, and reset it for the old link. This way the
2266  * relative order of links is preserved, and the links get drawn in the right place. */
2269 
2270  /* set up insert offset data, it needs stuff from here */
2271  if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
2272  NodeInsertOfsData *iofsd = MEM_callocN(sizeof(NodeInsertOfsData), __func__);
2273 
2274  iofsd->insert = select;
2275  iofsd->prev = link->fromnode;
2276  iofsd->next = node;
2277 
2278  snode->runtime->iofsd = iofsd;
2279  }
2280 
2281  ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */
2282  snode_update(snode, select);
2283  ED_node_tag_update_id((ID *)snode->edittree);
2284  ED_node_tag_update_id(snode->id);
2285  }
2286  }
2287 }
typedef float(TangentPoint)[2]
struct AnimData * BKE_animdata_from_id(struct ID *id)
Definition: anim_data.c:96
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:714
struct SpaceNode * CTX_wm_space_node(const bContext *C)
Definition: context.c:854
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:689
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:725
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1018
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:699
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition: curve.c:1804
void BKE_main_id_tag_listbase(struct ListBase *lb, const int tag, const bool value)
Definition: lib_id.c:891
void ntreeNodeFlagSet(const bNodeTree *ntree, const int flag, const bool enable)
Definition: node.cc:3173
#define NODE_REROUTE
Definition: BKE_node.h:873
bool nodeLinkIsHidden(const struct bNodeLink *link)
bool nodeAttachNodeCheck(const struct bNode *node, const struct bNode *parent)
void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node)
Definition: node.cc:2364
void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock)
Definition: node.cc:2348
#define NODE_CUSTOM_GROUP
Definition: BKE_node.h:876
bool nodeIsChildOf(const bNode *parent, const bNode *child)
Definition: node.cc:1861
void nodeChainIter(const bNodeTree *ntree, const bNode *node_start, bool(*callback)(bNode *, bNode *, void *, const bool), void *userdata, const bool reversed)
Definition: node.cc:1879
void nodeAttachNode(struct bNode *node, struct bNode *parent)
Definition: node.cc:2449
struct bNode * nodeGetActive(struct bNodeTree *ntree)
Definition: node.cc:3561
void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link)
Definition: node.cc:2231
int nodeSocketIsHidden(const struct bNodeSocket *sock)
void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link)
Definition: node.cc:2325
void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree)
Definition: node.cc:4262
int nodeSocketLinkLimit(const struct bNodeSocket *sock)
struct bNodeLink * nodeAddLink(struct bNodeTree *ntree, struct bNode *fromnode, struct bNodeSocket *fromsock, struct bNode *tonode, struct bNodeSocket *tosock)
Definition: node.cc:2189
void nodeParentsIter(bNode *node, bool(*callback)(bNode *, void *), void *userdata)
Definition: node.cc:1972
struct bNode * nodeFindRootParent(bNode *node)
Definition: node.cc:1849
#define NODE_FRAME
Definition: BKE_node.h:872
int nodeCountSocketLinks(const struct bNodeTree *ntree, const struct bNodeSocket *sock)
void nodeDetachNode(struct bNode *node)
Definition: node.cc:2462
#define BLI_assert(a)
Definition: BLI_assert.h:58
float BLI_easing_cubic_ease_in_out(float time, float begin, float change, float duration)
Definition: easing.c:134
void BLI_kdtree_nd_() insert(KDTree *tree, int index, const float co[KD_DIMS]) ATTR_NONNULL(1
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:124
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:188
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
Definition: BLI_listbase.h:184
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:547
void void BLI_listbase_sort(struct ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:133
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
int isect_seg_seg_v2(const float v1[2], const float v2[2], const float v3[2], const float v4[2])
Definition: math_geom.c:1151
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition: math_geom.c:338
MINLINE void copy_v2_v2(float r[2], const float a[2])
bool BLI_rctf_isect_x(const rctf *rect, const float x)
Definition: rct.c:114
bool BLI_rctf_isect_y(const rctf *rect, const float y)
Definition: rct.c:125
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2])
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
#define UNPACK2(a)
#define CLAMP_MAX(a, c)
#define SWAP(type, a, b)
#define UNUSED(x)
#define UNLIKELY(x)
#define ELEM(...)
#define IS_EQF(a, b)
#define STREQ(a, b)
#define CLAMP_MIN(a, b)
#define TIP_(msgid)
@ LIB_TAG_DOIT
Definition: DNA_ID.h:554
#define NODE_LINK_VALID
#define NODE_TEST
#define NODE_DO_OUTPUT
#define NODE_LINK_TEST
#define NODE_LINK_TEMP_HIGHLIGHT
@ SOCK_OUT
@ SOCK_IN
@ SOCK_MULTI_INPUT
@ SOCK_IN_USE
@ SOCK_HIDDEN
#define NODE_SELECT
#define NODE_LINKFLAG_HILITE
@ NTREE_UPDATE_LINKS
#define NODE_UPDATE
@ SNODE_SKIP_INSOFFSET
@ SPACE_NODE
@ SNODE_INSERTOFS_DIR_RIGHT
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_node_tag_update_id(struct ID *id)
Definition: node_draw.cc:124
bool ED_node_is_geometry(struct SpaceNode *snode)
Definition: node_edit.c:464
void ED_node_sort(struct bNodeTree *ntree)
Definition: node_draw.cc:253
void ED_preview_kill_jobs(struct wmWindowManager *wm, struct Main *bmain)
void ED_area_tag_redraw(ScrArea *area)
Definition: area.c:745
bool ED_operator_node_editable(struct bContext *C)
Definition: screen_ops.c:303
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:667
void ED_workspace_status_text(struct bContext *C, const char *str)
Definition: area.c:840
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
Group RGB to Bright Vector Camera Vector Combine Material Light Line Style Layer Add Ambient Diffuse Glossy Refraction Transparent Toon Principled Hair Volume Principled Light Particle Volume Image Sky Noise Wave Voronoi Brick Texture Vector Combine Vertex Separate Vector White RGB Map Separate Set Z Dilate Combine Combine Color Channel CMP_NODE_SPLITVIEWER
NODE_GROUP_OUTPUT
NODE_GROUP
Group RGB to Bright Vector Camera Vector Combine Material Light Line Style Layer Add Ambient Diffuse Glossy Refraction Transparent Toon Principled Hair Volume Principled Light Particle Volume Image Sky Noise Wave Voronoi Brick Texture Vector Combine Vertex Separate Vector White CMP_NODE_VIEWER
#define RNA_BEGIN(sptr, itemptr, propname)
Definition: RNA_access.h:1248
#define RNA_END
Definition: RNA_access.h:1255
StructRNA RNA_OperatorMousePath
@ PROP_SKIP_SAVE
Definition: RNA_types.h:204
@ PROP_HIDDEN
Definition: RNA_types.h:202
#define C
Definition: RandGen.cpp:39
#define UI_PRECISION_FLOAT_MAX
#define UI_MAX_DRAW_STR
Definition: UI_interface.h:90
#define UI_DPI_FAC
Definition: UI_interface.h:309
void UI_view2d_region_to_view(const struct View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
#define NC_NODE
Definition: WM_types.h:295
@ OPTYPE_BLOCKING
Definition: WM_types.h:157
@ OPTYPE_UNDO
Definition: WM_types.h:155
@ OPTYPE_REGISTER
Definition: WM_types.h:153
#define ND_DISPLAY
Definition: WM_types.h:391
#define KM_RELEASE
Definition: WM_types.h:243
unsigned int U
Definition: btGjkEpa3.h:78
#define SELECT
OperationNode * node
StackEntry * from
bool node_link_bezier_points(const View2D *v2d, const SpaceNode *snode, const bNodeLink *link, float coord_array[][2], const int resol)
Definition: drawnode.c:3746
bool node_link_bezier_handles(const View2D *v2d, const SpaceNode *snode, const bNodeLink *link, float vec[4][2])
Definition: drawnode.c:3637
bNodeTree * ntree
int count
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:48
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static ulong * next
static unsigned a[3]
Definition: RandGen.cpp:92
static void area(int d1, int d2, int e1, int e2, float weights[2])
bNode * node_add_node(const bContext *C, const char *idname, int type, float locx, float locy)
Definition: node_add.c:70
void node_to_updated_rect(const bNode *node, rctf *r_rect)
Definition: node_draw.cc:330
void snode_update(SpaceNode *snode, bNode *node)
Definition: node_edit.c:643
void snode_dag_update(bContext *C, SpaceNode *snode)
Definition: node_edit.c:393
void node_link_calculate_multi_input_position(const float socket_x, const float socket_y, const int index, const int total_inputs, float r[2])
Definition: node_edit.c:111
void snode_notify(bContext *C, SpaceNode *snode)
Definition: node_edit.c:411
int node_find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, const float cursor[2], int in_out)
Definition: node_edit.c:1149
bool composite_node_editable(bContext *C)
Definition: node_edit.c:382
#define NODE_LINK_RESOL
Definition: node_intern.h:335
#define NODE_WIDTH(node)
Definition: node_intern.h:329
void node_deselect_all_input_sockets(struct SpaceNode *snode, const bool deselect_nodes)
Definition: node_select.c:213
void node_deselect_all_output_sockets(struct SpaceNode *snode, const bool deselect_nodes)
Definition: node_select.c:246
static int node_make_link_exec(bContext *C, wmOperator *op)
#define NODE_DETACH_IS_DESCENDANT
static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
struct LinkAndPosition LinkAndPosition
static int cut_links_exec(bContext *C, wmOperator *op)
static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
static void node_link_cancel(bContext *C, wmOperator *op)
static void node_join_attach_recursive(bNode *node, bNode *frame)
static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag))
void NODE_OT_attach(wmOperatorType *ot)
static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode, bNode *tonode, void *userdata, const bool reversed)
void NODE_OT_parent_set(wmOperatorType *ot)
void sort_multi_input_socket_links(SpaceNode *snode, bNode *node, bNodeLink *drag_link, float cursor[2])
void NODE_OT_link_make(wmOperatorType *ot)
static bNode * node_find_frame_to_attach(ARegion *region, const bNodeTree *ntree, const int mouse_xy[2])
static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
static void node_link_exit(bContext *C, wmOperator *op, bool apply_links)
static void pick_link(const bContext *C, wmOperator *op, bNodeLinkDrag *nldrag, SpaceNode *snode, bNode *node, bNodeLink *link_to_pick)
void NODE_OT_detach(wmOperatorType *ot)
struct NodeInsertOfsData NodeInsertOfsData
static void clear_picking_highlight(ListBase *links)
static int node_detach_exec(bContext *C, wmOperator *UNUSED(op))
static int compare_link_by_y_position(const void *a, const void *b)
static void snode_autoconnect(Main *bmain, SpaceNode *snode, const bool allow_multiple, const bool replace)
static bool ntree_check_nodes_connected(bNodeTree *ntree, bNode *from, bNode *to)
static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
#define NODE_JOIN_DONE
static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link)
static bool node_link_insert_offset_chain_cb(bNode *fromnode, bNode *tonode, void *userdata, const bool reversed)
void NODE_OT_links_cut(wmOperatorType *ot)
static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
static bool ed_node_link_conditions(ScrArea *area, bool test, SpaceNode **r_snode, bNode **r_select)
static void pick_input_link_by_link_intersect(const bContext *C, wmOperator *op, bNodeLinkDrag *nldrag, const float *cursor)
static void node_detach_recursive(bNode *node)
void NODE_OT_insert_offset(wmOperatorType *ot)
void NODE_OT_link(wmOperatorType *ot)
void NODE_OT_links_detach(wmOperatorType *ot)
#define NODE_JOIN_IS_DESCENDANT
static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
#define NODE_INSOFS_ANIM_DURATION
static int mute_links_exec(bContext *C, wmOperator *op)
#define NODE_DETACH_DONE
static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
static int sort_nodes_locx(const void *a, const void *b)
static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, const float offset_x)
struct bNodeListItem bNodeListItem
static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, ARegion *region, const int mouse_xy[2], const bool right_alignment)
void NODE_OT_join(wmOperatorType *ot)
static bool node_group_has_output_dfs(bNode *node)
void NODE_OT_link_viewer(wmOperatorType *ot)
static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2])
static bNodeSocket * best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
void ED_node_link_insert(Main *bmain, ScrArea *area)
static bool node_group_has_output(Main *bmain, bNode *node)
static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used)
static LinkData * create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bNodeSocket *sock)
static bool snode_autoconnect_input(SpaceNode *snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace)
static bNodeLinkDrag * node_link_init(Main *bmain, SpaceNode *snode, float cursor[2], bool detach)
static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int tot)
static void node_link_insert_offset_frame_chains(const bNodeTree *ntree, const bNode *parent, NodeInsertOfsData *data, const bool reversed)
static bNodeSocket * socket_best_match(ListBase *sockets)
static void node_offset_apply(bNode *node, const float offset_x)
static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
void ED_node_link_intersect_test(ScrArea *area, int test)
static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool ntree_has_drivers(bNodeTree *ntree)
static bool ntree_check_nodes_connected_dfs(bNodeTree *ntree, bNode *from, bNode *to)
static int node_link_viewer(const bContext *C, bNode *tonode)
bool node_connected_to_output(Main *bmain, bNodeTree *ntree, bNode *node)
void NODE_OT_links_mute(wmOperatorType *ot)
static bNodeSocket * best_socket_output(bNodeTree *ntree, bNode *node, bNodeSocket *sock_target, const bool allow_multiple)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:6272
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
Definition: rna_access.c:6378
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6261
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
Definition: rna_access.c:6390
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3481
PropertyRNA * RNA_def_float_array(StructOrFunctionRNA *cont_, const char *identifier, int len, const float *default_value, float hardmin, float hardmax, const char *ui_name, const char *ui_description, float softmin, float softmax)
Definition: rna_define.c:4065
PropertyRNA * RNA_def_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
Definition: rna_define.c:4210
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1512
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3585
ListBase drivers
Definition: DNA_ID.h:273
int tag
Definition: DNA_ID.h:292
float multi_socket_position[2]
struct bNodeLink * link
void * data
Definition: DNA_listBase.h:42
void * last
Definition: DNA_listBase.h:47
void * first
Definition: DNA_listBase.h:47
Definition: BKE_main.h:116
ListBase nodetrees
Definition: BKE_main.h:170
struct ListBase linkdrag
Definition: node_intern.h:79
struct NodeInsertOfsData * iofsd
Definition: node_intern.h:83
SpaceNode_Runtime * runtime
struct bNodeTree * edittree
struct ID * id
struct bNodeLink * last_picked_multi_input_socket_link
Definition: node_intern.h:62
struct bNode * last_node_hovered_while_dragging_a_link
Definition: node_intern.h:66
ListBase links
Definition: node_intern.h:57
bool from_multi_input_socket
Definition: node_intern.h:58
struct bNode * node
struct bNodeListItem * next
struct bNodeListItem * prev
char name[64]
struct bNodeSocket * next
short total_inputs
short is_updating
ListBase nodes
ListBase links
void(* insert_link)(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link)
Definition: BKE_node.h:310
int update
ListBase inputs
struct bNodeType * typeinfo
struct bNode * parent
float locx
rctf totr
struct bNode * prev
short type
struct bNode * next
ListBase outputs
float xmax
Definition: DNA_vec_types.h:85
float xmin
Definition: DNA_vec_types.h:85
float ymax
Definition: DNA_vec_types.h:86
float ymin
Definition: DNA_vec_types.h:86
short val
Definition: WM_types.h:579
int mval[2]
Definition: WM_types.h:583
short type
Definition: WM_types.h:577
void * customdata
Definition: WM_types.h:631
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:752
const char * name
Definition: WM_types.h:721
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:768
const char * idname
Definition: WM_types.h:723
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:776
void(* cancel)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:760
struct StructRNA * srna
Definition: WM_types.h:802
const char * description
Definition: WM_types.h:726
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:736
struct PointerRNA * ptr
double duration
Definition: WM_types.h:705
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: util_avxb.h:167
ccl_device_inline float distance(const float2 &a, const float2 &b)
@ WM_CURSOR_KNIFE
Definition: wm_cursors.h:47
@ WM_CURSOR_MUTE
Definition: wm_cursors.h:75
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ TIMER
@ MOUSEMOVE
@ LEFTMOUSE
@ MIDDLEMOUSE
wmOperatorType * ot
Definition: wm_files.c:3156
int WM_gesture_lines_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lines_cancel(bContext *C, wmOperator *op)
int WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
Definition: wm_window.c:1669
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1632