Blender  V3.3
node_relationships.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2005 Blender Foundation. All rights reserved. */
3 
8 #include "MEM_guardedalloc.h"
9 
10 #include "DNA_anim_types.h"
11 #include "DNA_node_types.h"
12 
13 #include "BLI_easing.h"
14 
15 #include "BKE_anim_data.h"
16 #include "BKE_context.h"
17 #include "BKE_curve.h"
18 #include "BKE_lib_id.h"
19 #include "BKE_main.h"
20 #include "BKE_node.h"
21 #include "BKE_node_tree_update.h"
22 #include "BKE_screen.h"
23 
24 #include "ED_node.h" /* own include */
25 #include "ED_render.h"
26 #include "ED_screen.h"
27 #include "ED_space_api.h"
28 #include "ED_spreadsheet.h"
29 #include "ED_util.h"
30 
31 #include "RNA_access.h"
32 #include "RNA_define.h"
33 #include "RNA_prototypes.h"
34 
35 #include "DEG_depsgraph.h"
36 
37 #include "WM_api.h"
38 #include "WM_types.h"
39 
40 #include "GPU_state.h"
41 
42 #include "UI_interface_icons.h"
43 #include "UI_resources.h"
44 #include "UI_view2d.h"
45 
46 #include "BLT_translation.h"
47 
48 #include "NOD_node_declaration.hh"
49 #include "NOD_node_tree_ref.hh"
52 
53 #include "node_intern.hh" /* own include */
54 
56 
57 struct bNodeListItem {
58  struct bNodeListItem *next, *prev;
59  struct bNode *node;
60 };
61 
64  bNode *insert; /* inserted node */
65  bNode *prev, *next; /* prev/next node in the chain */
67 
69 
70  float offset_x; /* offset to apply to node chain */
71 };
72 
73 static void clear_picking_highlight(ListBase *links)
74 {
75  LISTBASE_FOREACH (bNodeLink *, link, links) {
76  link->flag &= ~NODE_LINK_TEMP_HIGHLIGHT;
77  }
78 }
79 
80 namespace blender::ed::space_node {
81 
82 /* -------------------------------------------------------------------- */
87 {
88  bNodeLink *oplink = MEM_cnew<bNodeLink>(__func__);
89  if (sock.in_out == SOCK_OUT) {
90  oplink->fromnode = &node;
91  oplink->fromsock = &sock;
92  }
93  else {
94  oplink->tonode = &node;
95  oplink->tosock = &sock;
96  }
97  oplink->flag |= NODE_LINK_VALID;
98  oplink->flag |= NODE_LINK_DRAGGED;
99  return oplink;
100 }
101 
102 static void pick_link(
103  wmOperator &op, bNodeLinkDrag &nldrag, SpaceNode &snode, bNode *node, bNodeLink &link_to_pick)
104 {
106  RNA_boolean_set(op.ptr, "has_link_picked", true);
107 
108  bNodeLink *link = create_drag_link(*link_to_pick.fromnode, *link_to_pick.fromsock);
109 
110  nldrag.links.append(link);
111  nodeRemLink(snode.edittree, &link_to_pick);
112 
114 
116  snode, *nldrag.last_node_hovered_while_dragging_a_link, nullptr, nullptr);
117 
118  /* Send changed event to original link->tonode. */
119  if (node) {
121  }
122 }
123 
125  wmOperator &op,
126  bNodeLinkDrag &nldrag,
127  const float2 &cursor)
128 {
129  SpaceNode *snode = CTX_wm_space_node(&C);
130  const ARegion *region = CTX_wm_region(&C);
131  const View2D *v2d = &region->v2d;
132 
133  float drag_start[2];
134  RNA_float_get_array(op.ptr, "drag_start", drag_start);
135  bNode *node;
136  bNodeSocket *socket;
137  node_find_indicated_socket(*snode, &node, &socket, drag_start, SOCK_IN);
138 
139  /* Distance to test overlapping of cursor on link. */
140  const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC;
141 
142  const int resolution = NODE_LINK_RESOL;
143 
144  bNodeLink *link_to_pick = nullptr;
146  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
147  if (link->tosock == socket) {
148  /* Test if the cursor is near a link. */
149  float vec[4][2];
150  node_link_bezier_handles(v2d, snode, *link, vec);
151 
152  float data[NODE_LINK_RESOL * 2 + 2];
154  vec[0][0], vec[1][0], vec[2][0], vec[3][0], data, resolution, sizeof(float[2]));
156  vec[0][1], vec[1][1], vec[2][1], vec[3][1], data + 1, resolution, sizeof(float[2]));
157 
158  for (int i = 0; i < resolution * 2; i += 2) {
159  float *l1 = &data[i];
160  float *l2 = &data[i + 2];
161  float distance = dist_squared_to_line_segment_v2(cursor, l1, l2);
162  if (distance < cursor_link_touch_distance) {
163  link_to_pick = link;
164  nldrag.last_picked_multi_input_socket_link = link_to_pick;
165  }
166  }
167  }
168  }
169 
170  /* If no linked was picked in this call, try using the one picked in the previous call.
171  * Not essential for the basic behavior, but can make interaction feel a bit better if
172  * the mouse moves to the right and loses the "selection." */
173  if (!link_to_pick) {
174  bNodeLink *last_picked_link = nldrag.last_picked_multi_input_socket_link;
175  if (last_picked_link) {
176  link_to_pick = last_picked_link;
177  }
178  }
179 
180  if (link_to_pick) {
181  /* Highlight is set here and cleared in the next iteration or if the operation finishes. */
182  link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
184 
185  if (!node_find_indicated_socket(*snode, &node, &socket, cursor, SOCK_IN)) {
186  pick_link(op, nldrag, *snode, node, *link_to_pick);
187  }
188  }
189 }
190 
191 static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used)
192 {
193  if (nodeSocketIsHidden(sock)) {
194  return false;
195  }
196 
197  if (!allow_used && (sock->flag & SOCK_IN_USE)) {
198  /* Multi input sockets are available (even if used). */
199  if (!(sock->flag & SOCK_MULTI_INPUT)) {
200  return false;
201  }
202  }
203 
204  return true;
205 }
206 
208  bNode *node,
209  bNodeSocket *sock_target,
210  const bool allow_multiple)
211 {
212  /* first look for selected output */
213  LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
214  if (!socket_is_available(ntree, sock, allow_multiple)) {
215  continue;
216  }
217 
218  if (sock->flag & SELECT) {
219  return sock;
220  }
221  }
222 
223  /* try to find a socket with a matching name */
224  LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
225  if (!socket_is_available(ntree, sock, allow_multiple)) {
226  continue;
227  }
228 
229  /* check for same types */
230  if (sock->type == sock_target->type) {
231  if (STREQ(sock->name, sock_target->name)) {
232  return sock;
233  }
234  }
235  }
236 
237  /* otherwise settle for the first available socket of the right type */
238  LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
239  if (!socket_is_available(ntree, sock, allow_multiple)) {
240  continue;
241  }
242 
243  /* check for same types */
244  if (sock->type == sock_target->type) {
245  return sock;
246  }
247  }
248 
249  /* Always allow linking to an reroute node. The socket type of the reroute sockets might change
250  * after the link has been created. */
251  if (node->type == NODE_REROUTE) {
252  return (bNodeSocket *)node->outputs.first;
253  }
254 
255  return nullptr;
256 }
257 
258 /* this is a bit complicated, but designed to prioritize finding
259  * sockets of higher types, such as image, first */
260 static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
261 {
262  int maxtype = 0;
263  LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
264  maxtype = max_ii(sock->type, maxtype);
265  }
266 
267  /* find sockets of higher 'types' first (i.e. image) */
268  int a = 0;
269  for (int socktype = maxtype; socktype >= 0; socktype--) {
270  LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
271  if (!socket_is_available(ntree, sock, replace)) {
272  a++;
273  continue;
274  }
275 
276  if (sock->type == socktype) {
277  /* increment to make sure we don't keep finding
278  * the same socket on every attempt running this function */
279  a++;
280  if (a > num) {
281  return sock;
282  }
283  }
284  }
285  }
286 
287  return nullptr;
288 }
289 
291  bNode *node_fr,
292  bNodeSocket *sock_fr,
293  bNode *node_to,
294  bNodeSocket *sock_to,
295  int replace)
296 {
297  bNodeTree *ntree = snode.edittree;
298 
299  /* then we can connect */
300  if (replace) {
301  nodeRemSocketLinks(ntree, sock_to);
302  }
303 
304  nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to);
305  return true;
306 }
307 
311 };
312 
314  bNode &node,
315  bNodeLink *drag_link,
316  const float2 *cursor)
317 {
318  LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
319  if (!(socket->flag & SOCK_MULTI_INPUT)) {
320  continue;
321  }
323 
324  LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) {
325  if (link->tosock == socket) {
326  links.append(
327  {link,
328  node_link_calculate_multi_input_position({link->tosock->locx, link->tosock->locy},
329  link->multi_input_socket_index,
330  link->tosock->total_inputs)});
331  }
332  }
333 
334  if (drag_link) {
335  LinkAndPosition link_and_position{};
336  link_and_position.link = drag_link;
337  if (cursor) {
338  link_and_position.multi_socket_position = *cursor;
339  }
340  links.append(link_and_position);
341  }
342 
343  std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) {
344  return a.multi_socket_position.y < b.multi_socket_position.y;
345  });
346 
347  for (const int i : links.index_range()) {
348  links[i].link->multi_input_socket_index = i;
349  }
350  }
351 }
352 
353 static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace)
354 {
355  bNodeTree *ntree = snode.edittree;
356  Vector<bNode *> sorted_nodes;
357 
359  if (node->flag & NODE_SELECT) {
360  sorted_nodes.append(node);
361  }
362  }
363 
364  /* Sort nodes left to right. */
365  std::sort(sorted_nodes.begin(), sorted_nodes.end(), [](const bNode *a, const bNode *b) {
366  return a->locx < b->locx;
367  });
368 
369  int numlinks = 0;
370  for (const int i : sorted_nodes.as_mutable_span().drop_back(1).index_range()) {
371  bool has_selected_inputs = false;
372 
373  bNode *node_fr = sorted_nodes[i];
374  bNode *node_to = sorted_nodes[i + 1];
375  /* corner case: input/output node aligned the wrong way around (T47729) */
376  if (BLI_listbase_is_empty(&node_to->inputs) || BLI_listbase_is_empty(&node_fr->outputs)) {
377  SWAP(bNode *, node_fr, node_to);
378  }
379 
380  /* if there are selected sockets, connect those */
381  LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) {
382  if (sock_to->flag & SELECT) {
383  has_selected_inputs = true;
384 
385  if (!socket_is_available(ntree, sock_to, replace)) {
386  continue;
387  }
388 
389  /* check for an appropriate output socket to connect from */
390  bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
391  if (!sock_fr) {
392  continue;
393  }
394 
395  if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
396  numlinks++;
397  }
398  }
399  }
400 
401  if (!has_selected_inputs) {
402  /* no selected inputs, connect by finding suitable match */
403  int num_inputs = BLI_listbase_count(&node_to->inputs);
404 
405  for (int i = 0; i < num_inputs; i++) {
406 
407  /* find the best guess input socket */
408  bNodeSocket *sock_to = best_socket_input(ntree, node_to, i, replace);
409  if (!sock_to) {
410  continue;
411  }
412 
413  /* check for an appropriate output socket to connect from */
414  bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
415  if (!sock_fr) {
416  continue;
417  }
418 
419  if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
420  numlinks++;
421  break;
422  }
423  }
424  }
425  }
426 }
427 
430 namespace viewer_linking {
431 
432 /* -------------------------------------------------------------------- */
436 /* Depending on the node tree type, different socket types are supported by viewer nodes. */
437 static bool socket_can_be_viewed(const OutputSocketRef &socket)
438 {
439  if (nodeSocketIsHidden(socket.bsocket())) {
440  return false;
441  }
442  if (socket.idname() == "NodeSocketVirtual") {
443  return false;
444  }
445  if (socket.tree().btree()->type != NTREE_GEOMETRY) {
446  return true;
447  }
448  return ELEM(socket.typeinfo()->type,
450  SOCK_FLOAT,
451  SOCK_VECTOR,
452  SOCK_INT,
453  SOCK_BOOLEAN,
454  SOCK_RGBA);
455 }
456 
458 {
459  switch (socket_type) {
460  case SOCK_FLOAT:
461  return CD_PROP_FLOAT;
462  case SOCK_INT:
463  return CD_PROP_INT32;
464  case SOCK_VECTOR:
465  return CD_PROP_FLOAT3;
466  case SOCK_BOOLEAN:
467  return CD_PROP_BOOL;
468  case SOCK_RGBA:
469  return CD_PROP_COLOR;
470  default:
471  /* Fallback. */
472  return CD_AUTO_FROM_NAME;
473  }
474 }
475 
480  bNode &viewer_node,
481  bNodeSocket &src_socket)
482 {
483  if (viewer_node.type != GEO_NODE_VIEWER) {
484  /* In viewer nodes in the compositor, only the first input should be linked to. */
485  return (bNodeSocket *)viewer_node.inputs.first;
486  }
487  /* For the geometry nodes viewer, find the socket with the correct type. */
488  LISTBASE_FOREACH (bNodeSocket *, viewer_socket, &viewer_node.inputs) {
489  if (viewer_socket->type == src_socket.type) {
490  if (viewer_socket->type == SOCK_GEOMETRY) {
491  return viewer_socket;
492  }
493  NodeGeometryViewer *storage = (NodeGeometryViewer *)viewer_node.storage;
495  (eNodeSocketDatatype)src_socket.type);
496  BLI_assert(data_type != CD_AUTO_FROM_NAME);
497  storage->data_type = data_type;
498  viewer_node.typeinfo->updatefunc(&ntree, &viewer_node);
499  return viewer_socket;
500  }
501  }
502  return nullptr;
503 }
504 
505 static bool is_viewer_node(const NodeRef &node)
506 {
508 }
509 
511 {
512  Vector<const NodeRef *> viewer_nodes;
513  for (const NodeRef *node : tree.nodes()) {
514  if (is_viewer_node(*node)) {
515  viewer_nodes.append(node);
516  }
517  }
518  return viewer_nodes;
519 }
520 
521 static bool is_viewer_socket_in_viewer(const InputSocketRef &socket)
522 {
523  const NodeRef &node = socket.node();
525  if (node.typeinfo()->type == GEO_NODE_VIEWER) {
526  return true;
527  }
528  return socket.index() == 0;
529 }
530 
531 static bool is_linked_to_viewer(const OutputSocketRef &socket, const NodeRef &viewer_node)
532 {
533  for (const InputSocketRef *target_socket : socket.directly_linked_sockets()) {
534  if (&target_socket->node() != &viewer_node) {
535  continue;
536  }
537  if (!target_socket->is_available()) {
538  continue;
539  }
540  if (is_viewer_socket_in_viewer(*target_socket)) {
541  return true;
542  }
543  }
544  return false;
545 }
546 
547 static int get_default_viewer_type(const bContext *C)
548 {
549  SpaceNode *snode = CTX_wm_space_node(C);
551 }
552 
554 {
555  LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
556  if (link->tonode == &viewer_node) {
557  if (link->tosock->flag & SOCK_UNAVAIL) {
558  nodeRemLink(&btree, link);
559  }
560  }
561  }
562 }
563 
565 {
567 
568  /* Check if there is already an active viewer node that should be used. */
569  for (const NodeRef *viewer_node : viewer_nodes) {
570  if (viewer_node->bnode()->flag & NODE_DO_OUTPUT) {
571  return viewer_node;
572  }
573  }
574 
575  /* If no active but non-active viewers exist, make one active. */
576  if (!viewer_nodes.is_empty()) {
577  viewer_nodes[0]->bnode()->flag |= NODE_DO_OUTPUT;
578  return viewer_nodes[0];
579  }
580  return nullptr;
581 }
582 
583 static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *active_viewer_node,
584  const NodeRef &node_to_view)
585 {
586  /* Check if any of the output sockets is selected, which is the case when the user just clicked
587  * on the socket. */
588  for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
589  if (output_socket->bsocket()->flag & SELECT) {
590  return output_socket;
591  }
592  }
593 
594  const OutputSocketRef *last_socket_linked_to_viewer = nullptr;
595  if (active_viewer_node != nullptr) {
596  for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
597  if (!socket_can_be_viewed(*output_socket)) {
598  continue;
599  }
600  if (is_linked_to_viewer(*output_socket, *active_viewer_node)) {
601  last_socket_linked_to_viewer = output_socket;
602  }
603  }
604  }
605  if (last_socket_linked_to_viewer == nullptr) {
606  /* If no output is connected to a viewer, use the first output that can be viewed. */
607  for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
608  if (socket_can_be_viewed(*output_socket)) {
609  return output_socket;
610  }
611  }
612  }
613  else {
614  /* Pick the next socket to be linked to the viewer. */
615  const int tot_outputs = node_to_view.outputs().size();
616  for (const int offset : IndexRange(1, tot_outputs - 1)) {
617  const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs;
618  const OutputSocketRef &output_socket = node_to_view.output(index);
619  if (!socket_can_be_viewed(output_socket)) {
620  continue;
621  }
622  if (is_linked_to_viewer(output_socket, *active_viewer_node)) {
623  continue;
624  }
625  return &output_socket;
626  }
627  }
628  return nullptr;
629 }
630 
631 static int link_socket_to_viewer(const bContext &C,
632  bNode *viewer_bnode,
633  bNode &bnode_to_view,
634  bNodeSocket &bsocket_to_view)
635 {
636  SpaceNode &snode = *CTX_wm_space_node(&C);
637  bNodeTree &btree = *snode.edittree;
638 
639  if (viewer_bnode == nullptr) {
640  /* Create a new viewer node if none exists. */
641  const int viewer_type = get_default_viewer_type(&C);
642  /* The socket location is in view space, so dividing by #UI_DPI_FAC
643  * brings it into the coordinate space of the node editor. */
644  viewer_bnode = node_add_node(C,
645  nullptr,
646  viewer_type,
647  bsocket_to_view.locx / UI_DPI_FAC + 100,
648  bsocket_to_view.locy / UI_DPI_FAC);
649  if (viewer_bnode == nullptr) {
650  return OPERATOR_CANCELLED;
651  }
652  }
653 
654  bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_bnode, bsocket_to_view);
655  if (viewer_bsocket == nullptr) {
656  return OPERATOR_CANCELLED;
657  }
658 
659  bNodeLink *link_to_change = nullptr;
660  LISTBASE_FOREACH (bNodeLink *, link, &btree.links) {
661  if (link->tosock == viewer_bsocket) {
662  link_to_change = link;
663  break;
664  }
665  }
666 
667  if (link_to_change == nullptr) {
668  nodeAddLink(&btree, &bnode_to_view, &bsocket_to_view, viewer_bnode, viewer_bsocket);
669  }
670  else {
671  link_to_change->fromnode = &bnode_to_view;
672  link_to_change->fromsock = &bsocket_to_view;
674  }
675 
676  remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode);
677 
678  if (btree.type == NTREE_GEOMETRY) {
680  }
681 
683  return OPERATOR_FINISHED;
684 }
685 
686 static int node_link_viewer(const bContext &C, bNode &bnode_to_view)
687 {
688  SpaceNode &snode = *CTX_wm_space_node(&C);
689  bNodeTree *btree = snode.edittree;
690 
691  const NodeTreeRef tree{btree};
692  const NodeRef &node_to_view = *tree.find_node(bnode_to_view);
693  const NodeRef *active_viewer_node = get_existing_viewer(tree);
694 
695  const OutputSocketRef *socket_to_view = find_output_socket_to_be_viewed(active_viewer_node,
696  node_to_view);
697  if (socket_to_view == nullptr) {
698  return OPERATOR_FINISHED;
699  }
700 
701  bNodeSocket &bsocket_to_view = *socket_to_view->bsocket();
702  bNode *viewer_bnode = active_viewer_node ? active_viewer_node->bnode() : nullptr;
703  return link_socket_to_viewer(C, viewer_bnode, bnode_to_view, bsocket_to_view);
704 }
705 
708 } // namespace viewer_linking
709 
710 /* -------------------------------------------------------------------- */
715 {
716  SpaceNode &snode = *CTX_wm_space_node(C);
717  bNode *node = nodeGetActive(snode.edittree);
718 
719  if (!node) {
720  return OPERATOR_CANCELLED;
721  }
722 
724 
726  return OPERATOR_CANCELLED;
727  }
728 
730 
731  return OPERATOR_FINISHED;
732 }
733 
735 {
737  return false;
738  }
739  SpaceNode *snode = CTX_wm_space_node(C);
740  return ED_node_is_compositor(snode) || ED_node_is_geometry(snode);
741 }
742 
744 {
745  /* identifiers */
746  ot->name = "Link to Viewer Node";
747  ot->description = "Link to viewer node";
748  ot->idname = "NODE_OT_link_viewer";
749 
750  /* api callbacks */
753 
754  /* flags */
756 }
757 
760 /* -------------------------------------------------------------------- */
768 static bool dragged_links_are_detached(const bNodeLinkDrag &nldrag)
769 {
770  if (nldrag.in_out == SOCK_OUT) {
771  for (const bNodeLink *link : nldrag.links) {
772  if (link->tonode && link->tosock) {
773  return false;
774  }
775  }
776  }
777  else {
778  for (const bNodeLink *link : nldrag.links) {
779  if (link->fromnode && link->fromsock) {
780  return false;
781  }
782  }
783  }
784  return true;
785 }
786 
788  const bNodeLinkDrag &nldrag)
789 {
790  /* Custom node trees aren't supported yet. */
791  if (node_tree.type == NTREE_CUSTOM) {
792  return false;
793  }
794  /* Only create the search menu when the drag has not already connected the links to a socket. */
795  if (!dragged_links_are_detached(nldrag)) {
796  return false;
797  }
798  /* Don't create the search menu if the drag is disconnecting a link from an input node. */
799  if (nldrag.start_socket->in_out == SOCK_IN && nldrag.start_link_count > 0) {
800  return false;
801  }
802  /* Don't allow a drag from the "new socket" of a group input node. Handling these
803  * properly in node callbacks increases the complexity too much for now. */
805  if (nldrag.start_socket->type == SOCK_CUSTOM) {
806  return false;
807  }
808  }
809  return true;
810 }
811 
812 static void draw_draglink_tooltip(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg)
813 {
814  bNodeLinkDrag *nldrag = static_cast<bNodeLinkDrag *>(arg);
815 
816  const uchar text_col[4] = {255, 255, 255, 255};
817  const int padding = 4 * UI_DPI_FAC;
818  const float x = nldrag->in_out == SOCK_IN ? nldrag->cursor[0] - 3.3f * padding :
819  nldrag->cursor[0];
820  const float y = nldrag->cursor[1] - 2.0f * UI_DPI_FAC;
821 
822  UI_icon_draw_ex(x, y, ICON_ADD, U.inv_dpi_fac, 1.0f, 0.0f, text_col, false);
823 }
824 
825 static void draw_draglink_tooltip_activate(const ARegion &region, bNodeLinkDrag &nldrag)
826 {
827  if (nldrag.draw_handle == nullptr) {
830  }
831 }
832 
833 static void draw_draglink_tooltip_deactivate(const ARegion &region, bNodeLinkDrag &nldrag)
834 {
835  if (nldrag.draw_handle) {
836  ED_region_draw_cb_exit(region.type, nldrag.draw_handle);
837  nldrag.draw_handle = nullptr;
838  }
839 }
840 
842 {
843  char header[UI_MAX_DRAW_STR];
844 
845  BLI_strncpy(header, TIP_("LMB: drag node link, RMB: cancel"), sizeof(header));
846  ED_workspace_status_text(C, header);
847 }
848 
849 static int node_count_links(const bNodeTree &ntree, const bNodeSocket &socket)
850 {
851  int count = 0;
852  LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
853  if (ELEM(&socket, link->fromsock, link->tosock)) {
854  count++;
855  }
856  }
857  return count;
858 }
859 
860 static void node_remove_extra_links(SpaceNode &snode, bNodeLink &link)
861 {
862  bNodeTree &ntree = *snode.edittree;
863  bNodeSocket &from = *link.fromsock;
864  bNodeSocket &to = *link.tosock;
865  int to_count = node_count_links(ntree, to);
866  int from_count = node_count_links(ntree, from);
867  int to_link_limit = nodeSocketLinkLimit(&to);
868  int from_link_limit = nodeSocketLinkLimit(&from);
869 
871  if (tlink == &link) {
872  continue;
873  }
874 
875  if (tlink && tlink->fromsock == &from) {
876  if (from_count > from_link_limit) {
877  nodeRemLink(&ntree, tlink);
878  tlink = nullptr;
879  from_count--;
880  }
881  }
882 
883  if (tlink && tlink->tosock == &to) {
884  if (to_count > to_link_limit) {
885  nodeRemLink(&ntree, tlink);
886  tlink = nullptr;
887  to_count--;
888  }
889  else if (tlink->fromsock == &from) {
890  /* Also remove link if it comes from the same output. */
891  nodeRemLink(&ntree, tlink);
892  tlink = nullptr;
893  to_count--;
894  from_count--;
895  }
896  }
897  }
898 }
899 
900 static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links)
901 {
902  Main *bmain = CTX_data_main(&C);
903  ARegion &region = *CTX_wm_region(&C);
904  SpaceNode &snode = *CTX_wm_space_node(&C);
905  bNodeTree &ntree = *snode.edittree;
906  bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op.customdata;
907 
908  /* avoid updates while applying links */
909  ntree.is_updating = true;
910  for (bNodeLink *link : nldrag->links) {
911  link->flag &= ~NODE_LINK_DRAGGED;
912 
913  if (apply_links && link->tosock && link->fromsock) {
914  /* before actually adding the link,
915  * let nodes perform special link insertion handling
916  */
917  if (link->fromnode->typeinfo->insert_link) {
918  link->fromnode->typeinfo->insert_link(&ntree, link->fromnode, link);
919  }
920  if (link->tonode->typeinfo->insert_link) {
921  link->tonode->typeinfo->insert_link(&ntree, link->tonode, link);
922  }
923 
924  /* add link to the node tree */
925  BLI_addtail(&ntree.links, link);
927 
928  /* we might need to remove a link */
929  node_remove_extra_links(snode, *link);
930  }
931  else {
932  nodeRemLink(&ntree, link);
933  }
934  }
935  ntree.is_updating = false;
936 
938 
939  /* Ensure draglink tooltip is disabled. */
941 
942  ED_workspace_status_text(&C, nullptr);
943  ED_region_tag_redraw(&region);
945 
946  snode.runtime->linkdrag.reset();
947 }
948 
949 static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cursor)
950 {
951  SpaceNode &snode = *CTX_wm_space_node(&C);
952  bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op.customdata;
953 
954  if (nldrag->in_out == SOCK_OUT) {
955  bNode *tnode;
956  bNodeSocket *tsock = nullptr;
957  if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) {
958  for (bNodeLink *link : nldrag->links) {
959  /* skip if socket is on the same node as the fromsock */
960  if (tnode && link->fromnode == tnode) {
961  continue;
962  }
963 
964  /* Skip if tsock is already linked with this output. */
965  bNodeLink *existing_link_connected_to_fromsock = nullptr;
966  LISTBASE_FOREACH (bNodeLink *, existing_link, &snode.edittree->links) {
967  if (existing_link->fromsock == link->fromsock && existing_link->tosock == tsock) {
968  existing_link_connected_to_fromsock = existing_link;
969  break;
970  }
971  }
972 
973  /* attach links to the socket */
974  link->tonode = tnode;
975  link->tosock = tsock;
977  if (existing_link_connected_to_fromsock) {
979  existing_link_connected_to_fromsock->multi_input_socket_index;
980  continue;
981  }
982  if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) {
983  sort_multi_input_socket_links(snode, *tnode, link, &cursor);
984  }
985  }
986  }
987  else {
988  for (bNodeLink *link : nldrag->links) {
991  snode, *nldrag->last_node_hovered_while_dragging_a_link, nullptr, &cursor);
992  }
993  link->tonode = nullptr;
994  link->tosock = nullptr;
995  }
996  }
997  }
998  else {
999  bNode *tnode;
1000  bNodeSocket *tsock = nullptr;
1001  if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) {
1002  for (bNodeLink *link : nldrag->links) {
1003  /* skip if this is already the target socket */
1004  if (link->fromsock == tsock) {
1005  continue;
1006  }
1007  /* skip if socket is on the same node as the fromsock */
1008  if (tnode && link->tonode == tnode) {
1009  continue;
1010  }
1011 
1012  /* attach links to the socket */
1013  link->fromnode = tnode;
1014  link->fromsock = tsock;
1015  }
1016  }
1017  else {
1018  for (bNodeLink *link : nldrag->links) {
1019  link->fromnode = nullptr;
1020  link->fromsock = nullptr;
1021  }
1022  }
1023  }
1024 }
1025 
1026 /* Loop that adds a node-link, called by function below. */
1027 /* in_out = starting socket */
1028 static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
1029 {
1030  bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
1031  SpaceNode &snode = *CTX_wm_space_node(C);
1032  ARegion *region = CTX_wm_region(C);
1033 
1034  UI_view2d_edge_pan_apply_event(C, &nldrag->pan_data, event);
1035 
1036  float2 cursor;
1037  UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &cursor.x, &cursor.y);
1038  nldrag->cursor[0] = event->mval[0];
1039  nldrag->cursor[1] = event->mval[1];
1040 
1041  switch (event->type) {
1042  case MOUSEMOVE:
1043  if (nldrag->from_multi_input_socket && !RNA_boolean_get(op->ptr, "has_link_picked")) {
1044  pick_input_link_by_link_intersect(*C, *op, *nldrag, cursor);
1045  }
1046  else {
1047  node_link_find_socket(*C, *op, cursor);
1048 
1049  node_link_update_header(C, nldrag);
1050  ED_region_tag_redraw(region);
1051  }
1052 
1053  if (should_create_drag_link_search_menu(*snode.edittree, *nldrag)) {
1054  draw_draglink_tooltip_activate(*region, *nldrag);
1055  }
1056  else {
1057  draw_draglink_tooltip_deactivate(*region, *nldrag);
1058  }
1059  break;
1060  case LEFTMOUSE:
1061  if (event->val == KM_RELEASE) {
1062  /* Add a search menu for compatible sockets if the drag released on empty space. */
1063  if (should_create_drag_link_search_menu(*snode.edittree, *nldrag)) {
1064  bNodeLink &link = *nldrag->links.first();
1065  if (nldrag->in_out == SOCK_OUT) {
1066  invoke_node_link_drag_add_menu(*C, *link.fromnode, *link.fromsock, cursor);
1067  }
1068  else {
1069  invoke_node_link_drag_add_menu(*C, *link.tonode, *link.tosock, cursor);
1070  }
1071  }
1072 
1073  /* Finish link. */
1074  node_link_exit(*C, *op, true);
1075  return OPERATOR_FINISHED;
1076  }
1077  break;
1078  case RIGHTMOUSE:
1079  case MIDDLEMOUSE: {
1080  if (event->val == KM_RELEASE) {
1081  node_link_exit(*C, *op, true);
1082  return OPERATOR_FINISHED;
1083  }
1084  break;
1085  }
1086  case EVT_ESCKEY: {
1087  node_link_exit(*C, *op, true);
1088  return OPERATOR_FINISHED;
1089  }
1090  }
1091 
1092  return OPERATOR_RUNNING_MODAL;
1093 }
1094 
1095 static std::unique_ptr<bNodeLinkDrag> node_link_init(SpaceNode &snode,
1096  float2 cursor,
1097  const bool detach)
1098 {
1099  /* output indicated? */
1100  bNode *node;
1101  bNodeSocket *sock;
1102  if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
1103  std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
1104  nldrag->start_node = node;
1105  nldrag->start_socket = sock;
1106  nldrag->start_link_count = nodeCountSocketLinks(snode.edittree, sock);
1107  int link_limit = nodeSocketLinkLimit(sock);
1108  if (nldrag->start_link_count > 0 && (nldrag->start_link_count >= link_limit || detach)) {
1109  /* dragged links are fixed on input side */
1110  nldrag->in_out = SOCK_IN;
1111  /* detach current links and store them in the operator data */
1112  LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode.edittree->links) {
1113  if (link->fromsock == sock) {
1114  bNodeLink *oplink = MEM_cnew<bNodeLink>("drag link op link");
1115  *oplink = *link;
1116  oplink->next = oplink->prev = nullptr;
1117  oplink->flag |= NODE_LINK_VALID;
1118  oplink->flag |= NODE_LINK_DRAGGED;
1119 
1120  nldrag->links.append(oplink);
1121  nodeRemLink(snode.edittree, link);
1122  }
1123  }
1124  }
1125  else {
1126  /* dragged links are fixed on output side */
1127  nldrag->in_out = SOCK_OUT;
1128  /* create a new link */
1129  nldrag->links.append(create_drag_link(*node, *sock));
1130  }
1131  return nldrag;
1132  }
1133 
1134  /* or an input? */
1135  if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
1136  std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
1137  nldrag->last_node_hovered_while_dragging_a_link = node;
1138  nldrag->start_node = node;
1139  nldrag->start_socket = sock;
1140 
1141  nldrag->start_link_count = nodeCountSocketLinks(snode.edittree, sock);
1142  if (nldrag->start_link_count > 0) {
1143  /* dragged links are fixed on output side */
1144  nldrag->in_out = SOCK_OUT;
1145  /* detach current links and store them in the operator data */
1146  bNodeLink *link_to_pick;
1147  LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode.edittree->links) {
1148  if (link->tosock == sock) {
1149  if (sock->flag & SOCK_MULTI_INPUT) {
1150  nldrag->from_multi_input_socket = true;
1151  }
1152  link_to_pick = link;
1153  }
1154  }
1155 
1156  if (link_to_pick != nullptr && !nldrag->from_multi_input_socket) {
1157  bNodeLink *oplink = MEM_cnew<bNodeLink>("drag link op link");
1158  *oplink = *link_to_pick;
1159  oplink->next = oplink->prev = nullptr;
1160  oplink->flag |= NODE_LINK_VALID;
1161  oplink->flag |= NODE_LINK_DRAGGED;
1162 
1163  nldrag->links.append(oplink);
1164  nodeRemLink(snode.edittree, link_to_pick);
1165 
1166  /* send changed event to original link->tonode */
1167  if (node) {
1169  }
1170  }
1171  }
1172  else {
1173  /* dragged links are fixed on input side */
1174  nldrag->in_out = SOCK_IN;
1175  /* create a new link */
1176  nldrag->links.append(create_drag_link(*node, *sock));
1177  }
1178  return nldrag;
1179  }
1180 
1181  return {};
1182 }
1183 
1184 static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1185 {
1186  Main &bmain = *CTX_data_main(C);
1187  SpaceNode &snode = *CTX_wm_space_node(C);
1188  ARegion &region = *CTX_wm_region(C);
1189 
1190  bool detach = RNA_boolean_get(op->ptr, "detach");
1191 
1192  int mval[2];
1193  WM_event_drag_start_mval(event, &region, mval);
1194 
1195  float2 cursor;
1196  UI_view2d_region_to_view(&region.v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
1197  RNA_float_set_array(op->ptr, "drag_start", cursor);
1198  RNA_boolean_set(op->ptr, "has_link_picked", false);
1199 
1201 
1202  std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(snode, cursor, detach);
1203 
1204  if (nldrag) {
1205  UI_view2d_edge_pan_operator_init(C, &nldrag->pan_data, op);
1206 
1207  /* Add "+" icon when the link is dragged in empty space. */
1208  if (should_create_drag_link_search_menu(*snode.edittree, *nldrag)) {
1210  }
1211  snode.runtime->linkdrag = std::move(nldrag);
1212  op->customdata = snode.runtime->linkdrag.get();
1213 
1214  /* add modal handler */
1216 
1217  return OPERATOR_RUNNING_MODAL;
1218  }
1220 }
1221 
1223 {
1224  SpaceNode *snode = CTX_wm_space_node(C);
1225  bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op->customdata;
1226 
1228 
1229  snode->runtime->linkdrag.reset();
1230 
1232 }
1233 
1235 {
1236  /* identifiers */
1237  ot->name = "Link Nodes";
1238  ot->idname = "NODE_OT_link";
1239  ot->description = "Use the mouse to create a link between two nodes";
1240 
1241  /* api callbacks */
1244  // ot->exec = node_link_exec;
1247 
1248  /* flags */
1250 
1251  PropertyRNA *prop;
1252 
1253  RNA_def_boolean(ot->srna, "detach", false, "Detach", "Detach and redirect existing links");
1254  prop = RNA_def_boolean(
1255  ot->srna,
1256  "has_link_picked",
1257  false,
1258  "Has Link Picked",
1259  "The operation has placed a link. Only used for multi-input sockets, where the "
1260  "link is picked later");
1263  "drag_start",
1264  2,
1265  nullptr,
1268  "Drag Start",
1269  "The position of the mouse cursor at the start of the operation",
1274 
1282 }
1283 
1286 /* -------------------------------------------------------------------- */
1290 /* makes a link between selected output and input sockets */
1292 {
1293  Main &bmain = *CTX_data_main(C);
1294  SpaceNode &snode = *CTX_wm_space_node(C);
1295  const bool replace = RNA_boolean_get(op->ptr, "replace");
1296 
1298 
1299  snode_autoconnect(snode, true, replace);
1300 
1301  /* deselect sockets after linking */
1302  node_deselect_all_input_sockets(snode, false);
1303  node_deselect_all_output_sockets(snode, false);
1304 
1305  ED_node_tree_propagate_change(C, &bmain, snode.edittree);
1306 
1307  return OPERATOR_FINISHED;
1308 }
1309 
1311 {
1312  /* identifiers */
1313  ot->name = "Make Links";
1314  ot->description = "Makes a link between selected output in input sockets";
1315  ot->idname = "NODE_OT_link_make";
1316 
1317  /* callbacks */
1319  /* XXX we need a special poll which checks that there are selected input/output sockets. */
1321 
1322  /* flags */
1324 
1326  ot->srna, "replace", false, "Replace", "Replace socket connections with the new links");
1327 }
1328 
1331 /* -------------------------------------------------------------------- */
1335 static bool node_links_intersect(bNodeLink &link, const float mcoords[][2], int tot)
1336 {
1337  float coord_array[NODE_LINK_RESOL + 1][2];
1338 
1339  if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) {
1340  for (int i = 0; i < tot - 1; i++) {
1341  for (int b = 0; b < NODE_LINK_RESOL; b++) {
1342  if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) {
1343  return true;
1344  }
1345  }
1346  }
1347  }
1348  return false;
1349 }
1350 
1353 /* -------------------------------------------------------------------- */
1358 {
1359  Main &bmain = *CTX_data_main(C);
1360  SpaceNode &snode = *CTX_wm_space_node(C);
1361  ARegion &region = *CTX_wm_region(C);
1362 
1363  int i = 0;
1364  float mcoords[256][2];
1365  RNA_BEGIN (op->ptr, itemptr, "path") {
1366  float loc[2];
1367 
1368  RNA_float_get_array(&itemptr, "loc", loc);
1370  &region.v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]);
1371  i++;
1372  if (i >= 256) {
1373  break;
1374  }
1375  }
1376  RNA_END;
1377 
1378  if (i > 1) {
1379  bool found = false;
1380 
1382 
1383  LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode.edittree->links) {
1384  if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
1385  continue;
1386  }
1387 
1388  if (node_links_intersect(*link, mcoords, i)) {
1389 
1390  if (found == false) {
1391  /* TODO(sergey): Why did we kill jobs twice? */
1393  found = true;
1394  }
1395 
1396  bNode *to_node = link->tonode;
1397  nodeRemLink(snode.edittree, link);
1398  sort_multi_input_socket_links(snode, *to_node, nullptr, nullptr);
1399  }
1400  }
1401 
1403  if (found) {
1404  return OPERATOR_FINISHED;
1405  }
1406 
1407  return OPERATOR_CANCELLED;
1408  }
1409 
1411 }
1412 
1414 {
1415  ot->name = "Cut Links";
1416  ot->idname = "NODE_OT_links_cut";
1417  ot->description = "Use the mouse to cut (remove) some links";
1418 
1421  ot->exec = cut_links_exec;
1423 
1425 
1426  /* flags */
1428 
1429  /* properties */
1430  PropertyRNA *prop;
1431  prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
1433 
1434  /* internal */
1435  RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1436 }
1437 
1440 /* -------------------------------------------------------------------- */
1445 {
1446  Main &bmain = *CTX_data_main(C);
1447  SpaceNode &snode = *CTX_wm_space_node(C);
1448  ARegion &region = *CTX_wm_region(C);
1449 
1450  int i = 0;
1451  float mcoords[256][2];
1452  RNA_BEGIN (op->ptr, itemptr, "path") {
1453  float loc[2];
1454 
1455  RNA_float_get_array(&itemptr, "loc", loc);
1457  &region.v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]);
1458  i++;
1459  if (i >= 256) {
1460  break;
1461  }
1462  }
1463  RNA_END;
1464 
1465  if (i > 1) {
1467 
1468  /* Count intersected links and clear test flag. */
1469  int tot = 0;
1470  LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) {
1471  if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
1472  continue;
1473  }
1474  link->flag &= ~NODE_LINK_TEST;
1475  if (node_links_intersect(*link, mcoords, i)) {
1476  tot++;
1477  }
1478  }
1479  if (tot == 0) {
1480  return OPERATOR_CANCELLED;
1481  }
1482 
1483  /* Mute links. */
1484  LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) {
1485  if (node_link_is_hidden_or_dimmed(region.v2d, *link) || (link->flag & NODE_LINK_TEST)) {
1486  continue;
1487  }
1488 
1489  if (node_links_intersect(*link, mcoords, i)) {
1490  nodeMuteLinkToggle(snode.edittree, link);
1491  }
1492  }
1493 
1494  /* Clear remaining test flags. */
1495  LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) {
1496  if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
1497  continue;
1498  }
1499  link->flag &= ~NODE_LINK_TEST;
1500  }
1501 
1503  return OPERATOR_FINISHED;
1504  }
1505 
1507 }
1508 
1510 {
1511  ot->name = "Mute Links";
1512  ot->idname = "NODE_OT_links_mute";
1513  ot->description = "Use the mouse to mute links";
1514 
1517  ot->exec = mute_links_exec;
1519 
1521 
1522  /* flags */
1524 
1525  /* properties */
1526  PropertyRNA *prop;
1527  prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
1529 
1530  /* internal */
1531  RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1532 }
1533 
1536 /* -------------------------------------------------------------------- */
1541 {
1542  SpaceNode &snode = *CTX_wm_space_node(C);
1543  bNodeTree &ntree = *snode.edittree;
1544 
1546 
1548  if (node->flag & SELECT) {
1550  }
1551  }
1552 
1554  return OPERATOR_FINISHED;
1555 }
1556 
1558 {
1559  ot->name = "Detach Links";
1560  ot->idname = "NODE_OT_links_detach";
1561  ot->description =
1562  "Remove all links to selected nodes, and try to connect neighbor nodes together";
1563 
1566 
1567  /* flags */
1569 }
1570 
1573 /* -------------------------------------------------------------------- */
1578 {
1579  SpaceNode &snode = *CTX_wm_space_node(C);
1580  bNodeTree &ntree = *snode.edittree;
1581  bNode *frame = nodeGetActive(&ntree);
1582  if (!frame || frame->type != NODE_FRAME) {
1583  return OPERATOR_CANCELLED;
1584  }
1585 
1587  if (node == frame) {
1588  continue;
1589  }
1590  if (node->flag & NODE_SELECT) {
1592  nodeAttachNode(node, frame);
1593  }
1594  }
1595 
1596  node_sort(ntree);
1598 
1599  return OPERATOR_FINISHED;
1600 }
1601 
1603 {
1604  /* identifiers */
1605  ot->name = "Make Parent";
1606  ot->description = "Attach selected nodes";
1607  ot->idname = "NODE_OT_parent_set";
1608 
1609  /* api callbacks */
1612 
1613  /* flags */
1615 }
1616 
1619 /* -------------------------------------------------------------------- */
1623 /* tags for depth-first search */
1624 #define NODE_JOIN_DONE 1
1625 #define NODE_JOIN_IS_DESCENDANT 2
1626 
1628 {
1629  node->done |= NODE_JOIN_DONE;
1630 
1631  if (node == frame) {
1632  node->done |= NODE_JOIN_IS_DESCENDANT;
1633  }
1634  else if (node->parent) {
1635  /* call recursively */
1636  if (!(node->parent->done & NODE_JOIN_DONE)) {
1637  node_join_attach_recursive(node->parent, frame);
1638  }
1639 
1640  /* in any case: if the parent is a descendant, so is the child */
1641  if (node->parent->done & NODE_JOIN_IS_DESCENDANT) {
1642  node->done |= NODE_JOIN_IS_DESCENDANT;
1643  }
1644  else if (node->flag & NODE_TEST) {
1645  /* if parent is not an descendant of the frame, reattach the node */
1647  nodeAttachNode(node, frame);
1648  node->done |= NODE_JOIN_IS_DESCENDANT;
1649  }
1650  }
1651  else if (node->flag & NODE_TEST) {
1652  nodeAttachNode(node, frame);
1653  node->done |= NODE_JOIN_IS_DESCENDANT;
1654  }
1655 }
1656 
1658 {
1659  SpaceNode &snode = *CTX_wm_space_node(C);
1660  bNodeTree &ntree = *snode.edittree;
1661 
1662  /* XXX save selection: node_add_node call below sets the new frame as single
1663  * active+selected node */
1665  if (node->flag & NODE_SELECT) {
1666  node->flag |= NODE_TEST;
1667  }
1668  else {
1669  node->flag &= ~NODE_TEST;
1670  }
1671  }
1672 
1673  bNode *frame = node_add_node(*C, nullptr, NODE_FRAME, 0.0f, 0.0f);
1674 
1675  /* reset tags */
1677  node->done = 0;
1678  }
1679 
1681  if (!(node->done & NODE_JOIN_DONE)) {
1683  }
1684  }
1685 
1686  /* restore selection */
1688  if (node->flag & NODE_TEST) {
1689  node->flag |= NODE_SELECT;
1690  }
1691  }
1692 
1693  node_sort(ntree);
1695 
1696  return OPERATOR_FINISHED;
1697 }
1698 
1700 {
1701  /* identifiers */
1702  ot->name = "Join Nodes";
1703  ot->description = "Attach selected nodes to a new common frame";
1704  ot->idname = "NODE_OT_join";
1705 
1706  /* api callbacks */
1707  ot->exec = node_join_exec;
1709 
1710  /* flags */
1712 }
1713 
1716 /* -------------------------------------------------------------------- */
1721  const bNodeTree &ntree,
1722  const int mouse_xy[2])
1723 {
1724  /* convert mouse coordinates to v2d space */
1725  float cursor[2];
1726  UI_view2d_region_to_view(&region.v2d, UNPACK2(mouse_xy), &cursor[0], &cursor[1]);
1727 
1729  /* skip selected, those are the nodes we want to attach */
1730  if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
1731  continue;
1732  }
1733  if (BLI_rctf_isect_pt_v(&frame->totr, cursor)) {
1734  return frame;
1735  }
1736  }
1737 
1738  return nullptr;
1739 }
1740 
1741 static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1742 {
1743  ARegion &region = *CTX_wm_region(C);
1744  SpaceNode &snode = *CTX_wm_space_node(C);
1745  bNodeTree &ntree = *snode.edittree;
1746  bNode *frame = node_find_frame_to_attach(region, ntree, event->mval);
1747 
1748  if (frame) {
1750  if (node->flag & NODE_SELECT) {
1751  if (node->parent == nullptr) {
1752  /* disallow moving a parent into its child */
1753  if (nodeAttachNodeCheck(frame, node) == false) {
1754  /* attach all unparented nodes */
1755  nodeAttachNode(node, frame);
1756  }
1757  }
1758  else {
1759  /* attach nodes which share parent with the frame */
1760  bNode *parent;
1761  for (parent = frame->parent; parent; parent = parent->parent) {
1762  if (parent == node->parent) {
1763  break;
1764  }
1765  }
1766 
1767  if (parent) {
1768  /* disallow moving a parent into its child */
1769  if (nodeAttachNodeCheck(frame, node) == false) {
1771  nodeAttachNode(node, frame);
1772  }
1773  }
1774  }
1775  }
1776  }
1777  }
1778 
1779  node_sort(ntree);
1781 
1782  return OPERATOR_FINISHED;
1783 }
1784 
1786 {
1787  /* identifiers */
1788  ot->name = "Attach Nodes";
1789  ot->description = "Attach active node to a frame";
1790  ot->idname = "NODE_OT_attach";
1791 
1792  /* api callbacks */
1793 
1796 
1797  /* flags */
1799 }
1800 
1803 /* -------------------------------------------------------------------- */
1807 /* tags for depth-first search */
1808 #define NODE_DETACH_DONE 1
1809 #define NODE_DETACH_IS_DESCENDANT 2
1810 
1812 {
1813  node->done |= NODE_DETACH_DONE;
1814 
1815  if (node->parent) {
1816  /* call recursively */
1817  if (!(node->parent->done & NODE_DETACH_DONE)) {
1818  node_detach_recursive(node->parent);
1819  }
1820 
1821  /* in any case: if the parent is a descendant, so is the child */
1822  if (node->parent->done & NODE_DETACH_IS_DESCENDANT) {
1824  }
1825  else if (node->flag & NODE_SELECT) {
1826  /* if parent is not a descendant of a selected node, detach */
1829  }
1830  }
1831  else if (node->flag & NODE_SELECT) {
1833  }
1834 }
1835 
1836 /* detach the root nodes in the current selection */
1838 {
1839  SpaceNode &snode = *CTX_wm_space_node(C);
1840  bNodeTree &ntree = *snode.edittree;
1841 
1842  /* reset tags */
1844  node->done = 0;
1845  }
1846  /* detach nodes recursively
1847  * relative order is preserved here!
1848  */
1850  if (!(node->done & NODE_DETACH_DONE)) {
1852  }
1853  }
1854 
1855  node_sort(ntree);
1857 
1858  return OPERATOR_FINISHED;
1859 }
1860 
1862 {
1863  /* identifiers */
1864  ot->name = "Detach Nodes";
1865  ot->description = "Detach selected nodes from parents";
1866  ot->idname = "NODE_OT_detach";
1867 
1868  /* api callbacks */
1871 
1872  /* flags */
1874 }
1875 
1878 /* -------------------------------------------------------------------- */
1882 /* prevent duplicate testing code below */
1884  bool test,
1885  SpaceNode **r_snode,
1886  bNode **r_select)
1887 {
1888  SpaceNode *snode = area ? (SpaceNode *)area->spacedata.first : nullptr;
1889 
1890  *r_snode = snode;
1891  *r_select = nullptr;
1892 
1893  /* no unlucky accidents */
1894  if (area == nullptr || area->spacetype != SPACE_NODE) {
1895  return false;
1896  }
1897 
1898  if (!test) {
1899  /* no need to look for a node */
1900  return true;
1901  }
1902 
1903  bNode *node;
1904  bNode *select = nullptr;
1905  for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
1906  if (node->flag & SELECT) {
1907  if (select) {
1908  break;
1909  }
1910  select = node;
1911  }
1912  }
1913  /* only one selected */
1914  if (node || select == nullptr) {
1915  return false;
1916  }
1917 
1918  /* correct node */
1919  if (BLI_listbase_is_empty(&select->inputs) || BLI_listbase_is_empty(&select->outputs)) {
1920  return false;
1921  }
1922 
1924 
1925  /* test node for links */
1926  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1927  if (node_link_is_hidden_or_dimmed(region->v2d, *link)) {
1928  continue;
1929  }
1930 
1931  if (link->tonode == select || link->fromnode == select) {
1932  return false;
1933  }
1934  }
1935 
1936  *r_select = select;
1937  return true;
1938 }
1939 
1942 } // namespace blender::ed::space_node
1943 
1944 /* -------------------------------------------------------------------- */
1949 {
1950  using namespace blender::ed::space_node;
1951 
1952  bNode *select;
1953  SpaceNode *snode;
1954  if (!ed_node_link_conditions(area, test, &snode, &select)) {
1955  return;
1956  }
1957 
1958  /* clear flags */
1959  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1960  link->flag &= ~NODE_LINKFLAG_HILITE;
1961  }
1962 
1963  if (test == 0) {
1964  return;
1965  }
1966 
1968 
1969  /* find link to select/highlight */
1970  bNodeLink *selink = nullptr;
1971  float dist_best = FLT_MAX;
1972  LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
1973  float coord_array[NODE_LINK_RESOL + 1][2];
1974 
1975  if (node_link_is_hidden_or_dimmed(region->v2d, *link)) {
1976  continue;
1977  }
1978 
1979  if (node_link_bezier_points(nullptr, nullptr, *link, coord_array, NODE_LINK_RESOL)) {
1980  float dist = FLT_MAX;
1981 
1982  /* loop over link coords to find shortest dist to
1983  * upper left node edge of a intersected line segment */
1984  for (int i = 0; i < NODE_LINK_RESOL; i++) {
1985  /* Check if the node rectangle intersects the line from this point to next one. */
1986  if (BLI_rctf_isect_segment(&select->totr, coord_array[i], coord_array[i + 1])) {
1987  /* store the shortest distance to the upper left edge
1988  * of all intersections found so far */
1989  const float node_xy[] = {select->totr.xmin, select->totr.ymax};
1990 
1991  /* to be precise coord_array should be clipped by select->totr,
1992  * but not done since there's no real noticeable difference */
1993  dist = min_ff(
1994  dist_squared_to_line_segment_v2(node_xy, coord_array[i], coord_array[i + 1]), dist);
1995  }
1996  }
1997 
1998  /* we want the link with the shortest distance to node center */
1999  if (dist < dist_best) {
2000  dist_best = dist;
2001  selink = link;
2002  }
2003  }
2004  }
2005 
2006  if (selink) {
2007  selink->flag |= NODE_LINKFLAG_HILITE;
2008  }
2009 }
2010 
2013 namespace blender::ed::space_node {
2014 
2015 /* -------------------------------------------------------------------- */
2019 static int get_main_socket_priority(const bNodeSocket *socket)
2020 {
2021  switch ((eNodeSocketDatatype)socket->type) {
2022  case __SOCK_MESH:
2023  return -1;
2024  case SOCK_CUSTOM:
2025  return 0;
2026  case SOCK_BOOLEAN:
2027  return 1;
2028  case SOCK_INT:
2029  return 2;
2030  case SOCK_FLOAT:
2031  return 3;
2032  case SOCK_VECTOR:
2033  return 4;
2034  case SOCK_RGBA:
2035  return 5;
2036  case SOCK_STRING:
2037  case SOCK_SHADER:
2038  case SOCK_OBJECT:
2039  case SOCK_IMAGE:
2040  case SOCK_GEOMETRY:
2041  case SOCK_COLLECTION:
2042  case SOCK_TEXTURE:
2043  case SOCK_MATERIAL:
2044  return 6;
2045  }
2046  return -1;
2047 }
2048 
2051 {
2052  ListBase *sockets = (in_out == SOCK_IN) ? &node.inputs : &node.outputs;
2053 
2054  /* Try to get the main socket based on the socket declaration. */
2056  const nodes::NodeDeclaration *node_decl = node.runtime->declaration;
2057  if (node_decl != nullptr) {
2058  Span<nodes::SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs() :
2059  node_decl->outputs();
2060  int index;
2061  LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) {
2062  const nodes::SocketDeclaration &socket_decl = *socket_decls[index];
2063  if (nodeSocketIsHidden(socket)) {
2064  continue;
2065  }
2066  if (socket_decl.is_default_link_socket()) {
2067  return socket;
2068  }
2069  }
2070  }
2071 
2072  /* find priority range */
2073  int maxpriority = -1;
2074  LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
2075  if (sock->flag & SOCK_UNAVAIL) {
2076  continue;
2077  }
2078  maxpriority = max_ii(get_main_socket_priority(sock), maxpriority);
2079  }
2080 
2081  /* try all priorities, starting from 'highest' */
2082  for (int priority = maxpriority; priority >= 0; priority--) {
2083  LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
2084  if (!nodeSocketIsHidden(sock) && priority == get_main_socket_priority(sock)) {
2085  return sock;
2086  }
2087  }
2088  }
2089 
2090  /* no visible sockets, unhide first of highest priority */
2091  for (int priority = maxpriority; priority >= 0; priority--) {
2092  LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
2093  if (sock->flag & SOCK_UNAVAIL) {
2094  continue;
2095  }
2096  if (priority == get_main_socket_priority(sock)) {
2097  sock->flag &= ~SOCK_HIDDEN;
2098  return sock;
2099  }
2100  }
2101  }
2102 
2103  return nullptr;
2104 }
2105 
2106 static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
2107 {
2108  /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
2109  parent->flag |= NODE_TEST;
2110 
2111  return true;
2112 }
2113 
2114 static void node_offset_apply(bNode &node, const float offset_x)
2115 {
2116  /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
2117  if ((node.flag & NODE_TEST) == 0) {
2118  node.anim_init_locx = node.locx;
2119  node.anim_ofsx = (offset_x / UI_DPI_FAC);
2120  node.flag |= NODE_TEST;
2121  }
2122 }
2123 
2124 static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, const float offset_x)
2125 {
2126  node_offset_apply(*parent, offset_x);
2127 
2128  /* Flag all children as offset to prevent them from being offset
2129  * separately (they've already moved with the parent). */
2130  LISTBASE_FOREACH (bNode *, node, &data->ntree->nodes) {
2131  if (nodeIsChildOf(parent, node)) {
2132  /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
2133  node->flag |= NODE_TEST;
2134  }
2135  }
2136 }
2137 
2138 #define NODE_INSOFS_ANIM_DURATION 0.25f
2139 
2145  bNode *tonode,
2146  void *userdata,
2147  const bool reversed)
2148 {
2149  NodeInsertOfsData *data = (NodeInsertOfsData *)userdata;
2150  bNode *ofs_node = reversed ? fromnode : tonode;
2151 
2152  if (ofs_node->parent && ofs_node->parent != data->insert_parent) {
2153  node_offset_apply(*ofs_node->parent, data->offset_x);
2154  }
2155  else {
2156  node_offset_apply(*ofs_node, data->offset_x);
2157  }
2158 
2159  return true;
2160 }
2161 
2166  const bNode *parent,
2168  const bool reversed)
2169 {
2171  if (nodeIsChildOf(parent, node)) {
2173  }
2174  }
2175 }
2176 
2182  bNode *tonode,
2183  void *userdata,
2184  const bool reversed)
2185 {
2186  NodeInsertOfsData *data = (NodeInsertOfsData *)userdata;
2187  bNode *ofs_node = reversed ? fromnode : tonode;
2188 
2189  if (data->insert_parent) {
2190  if (ofs_node->parent && (ofs_node->parent->flag & NODE_TEST) == 0) {
2191  node_parent_offset_apply(data, ofs_node->parent, data->offset_x);
2192  node_link_insert_offset_frame_chains(data->ntree, ofs_node->parent, data, reversed);
2193  }
2194  else {
2195  node_offset_apply(*ofs_node, data->offset_x);
2196  }
2197 
2198  if (nodeIsChildOf(data->insert_parent, ofs_node) == false) {
2199  data->insert_parent = nullptr;
2200  }
2201  }
2202  else if (ofs_node->parent) {
2203  bNode *node = nodeFindRootParent(ofs_node);
2204  node_offset_apply(*node, data->offset_x);
2205  }
2206  else {
2207  node_offset_apply(*ofs_node, data->offset_x);
2208  }
2209 
2210  return true;
2211 }
2212 
2214  ARegion *region,
2215  const int mouse_xy[2],
2216  const bool right_alignment)
2217 {
2218  bNodeTree *ntree = iofsd->ntree;
2219  bNode &insert = *iofsd->insert;
2220  bNode *prev = iofsd->prev, *next = iofsd->next;
2221  bNode *init_parent = insert.parent; /* store old insert.parent for restoring later */
2222 
2223  const float min_margin = U.node_margin * UI_DPI_FAC;
2224  const float width = NODE_WIDTH(insert);
2225  const bool needs_alignment = (next->totr.xmin - prev->totr.xmax) < (width + (min_margin * 2.0f));
2226 
2227  float margin = width;
2228 
2229  /* NODE_TEST will be used later, so disable for all nodes */
2230  ntreeNodeFlagSet(ntree, NODE_TEST, false);
2231 
2232  /* `insert.totr` isn't updated yet,
2233  * so `totr_insert` is used to get the correct world-space coords. */
2234  rctf totr_insert;
2235  node_to_updated_rect(insert, totr_insert);
2236 
2237  /* frame attachment wasn't handled yet
2238  * so we search the frame that the node will be attached to later */
2239  insert.parent = node_find_frame_to_attach(*region, *ntree, mouse_xy);
2240 
2241  /* this makes sure nodes are also correctly offset when inserting a node on top of a frame
2242  * without actually making it a part of the frame (because mouse isn't intersecting it)
2243  * - logic here is similar to node_find_frame_to_attach */
2244  if (!insert.parent ||
2245  (prev->parent && (prev->parent == next->parent) && (prev->parent != insert.parent))) {
2246  bNode *frame;
2247  rctf totr_frame;
2248 
2249  /* check nodes front to back */
2250  for (frame = (bNode *)ntree->nodes.last; frame; frame = frame->prev) {
2251  /* skip selected, those are the nodes we want to attach */
2252  if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT)) {
2253  continue;
2254  }
2255 
2256  /* for some reason frame y coords aren't correct yet */
2257  node_to_updated_rect(*frame, totr_frame);
2258 
2259  if (BLI_rctf_isect_x(&totr_frame, totr_insert.xmin) &&
2260  BLI_rctf_isect_x(&totr_frame, totr_insert.xmax)) {
2261  if (BLI_rctf_isect_y(&totr_frame, totr_insert.ymin) ||
2262  BLI_rctf_isect_y(&totr_frame, totr_insert.ymax)) {
2263  /* frame isn't insert.parent actually, but this is needed to make offsetting
2264  * nodes work correctly for above checked cases (it is restored later) */
2265  insert.parent = frame;
2266  break;
2267  }
2268  }
2269  }
2270  }
2271 
2272  /* *** ensure offset at the left (or right for right_alignment case) of insert_node *** */
2273 
2274  float dist = right_alignment ? totr_insert.xmin - prev->totr.xmax :
2275  next->totr.xmin - totr_insert.xmax;
2276  /* distance between insert_node and prev is smaller than min margin */
2277  if (dist < min_margin) {
2278  const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
2279 
2280  node_offset_apply(insert, addval);
2281 
2282  totr_insert.xmin += addval;
2283  totr_insert.xmax += addval;
2284  margin += min_margin;
2285  }
2286 
2287  /* *** ensure offset at the right (or left for right_alignment case) of insert_node *** */
2288 
2289  dist = right_alignment ? next->totr.xmin - totr_insert.xmax : totr_insert.xmin - prev->totr.xmax;
2290  /* distance between insert_node and next is smaller than min margin */
2291  if (dist < min_margin) {
2292  const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
2293  if (needs_alignment) {
2294  bNode *offs_node = right_alignment ? next : prev;
2295  if (!offs_node->parent || offs_node->parent == insert.parent ||
2296  nodeIsChildOf(offs_node->parent, &insert)) {
2297  node_offset_apply(*offs_node, addval);
2298  }
2299  else if (!insert.parent && offs_node->parent) {
2300  node_offset_apply(*nodeFindRootParent(offs_node), addval);
2301  }
2302  margin = addval;
2303  }
2304  /* enough room is available, but we want to ensure the min margin at the right */
2305  else {
2306  /* offset inserted node so that min margin is kept at the right */
2307  node_offset_apply(insert, -addval);
2308  }
2309  }
2310 
2311  if (needs_alignment) {
2312  iofsd->insert_parent = insert.parent;
2313  iofsd->offset_x = margin;
2314 
2315  /* flag all parents of insert as offset to prevent them from being offset */
2317  /* iterate over entire chain and apply offsets */
2319  right_alignment ? next : prev,
2321  iofsd,
2322  !right_alignment);
2323  }
2324 
2325  insert.parent = init_parent;
2326 }
2327 
2331 static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
2332 {
2333  SpaceNode *snode = CTX_wm_space_node(C);
2334  NodeInsertOfsData *iofsd = snode->runtime->iofsd;
2335  bool redraw = false;
2336 
2337  if (!snode || event->type != TIMER || iofsd == nullptr ||
2338  iofsd->anim_timer != event->customdata) {
2339  return OPERATOR_PASS_THROUGH;
2340  }
2341 
2342  const float duration = (float)iofsd->anim_timer->duration;
2343 
2344  /* handle animation - do this before possibly aborting due to duration, since
2345  * main thread might be so busy that node hasn't reached final position yet */
2346  LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
2347  if (UNLIKELY(node->anim_ofsx)) {
2348  const float endval = node->anim_init_locx + node->anim_ofsx;
2349  if (IS_EQF(node->locx, endval) == false) {
2351  duration, node->anim_init_locx, node->anim_ofsx, NODE_INSOFS_ANIM_DURATION);
2352  if (node->anim_ofsx < 0) {
2353  CLAMP_MIN(node->locx, endval);
2354  }
2355  else {
2356  CLAMP_MAX(node->locx, endval);
2357  }
2358  redraw = true;
2359  }
2360  }
2361  }
2362  if (redraw) {
2364  }
2365 
2366  /* end timer + free insert offset data */
2367  if (duration > NODE_INSOFS_ANIM_DURATION) {
2368  WM_event_remove_timer(CTX_wm_manager(C), nullptr, iofsd->anim_timer);
2369 
2370  LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
2371  node->anim_init_locx = node->anim_ofsx = 0.0f;
2372  }
2373 
2374  snode->runtime->iofsd = nullptr;
2375  MEM_freeN(iofsd);
2376 
2378  }
2379 
2380  return OPERATOR_RUNNING_MODAL;
2381 }
2382 
2383 #undef NODE_INSOFS_ANIM_DURATION
2384 
2385 static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2386 {
2387  const SpaceNode *snode = CTX_wm_space_node(C);
2388  NodeInsertOfsData *iofsd = snode->runtime->iofsd;
2389 
2390  if (!iofsd || !iofsd->insert) {
2391  return OPERATOR_CANCELLED;
2392  }
2393 
2394  BLI_assert((snode->flag & SNODE_SKIP_INSOFFSET) == 0);
2395 
2396  iofsd->ntree = snode->edittree;
2398 
2400  iofsd, CTX_wm_region(C), event->mval, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT));
2401 
2402  /* add temp handler */
2404 
2405  return OPERATOR_RUNNING_MODAL;
2406 }
2407 
2409 {
2410  /* identifiers */
2411  ot->name = "Insert Offset";
2412  ot->description = "Automatically offset nodes on insertion";
2413  ot->idname = "NODE_OT_insert_offset";
2414 
2415  /* callbacks */
2419 
2420  /* flags */
2422 }
2423 
2426 } // namespace blender::ed::space_node
2427 
2428 /* -------------------------------------------------------------------- */
2433 {
2434  using namespace blender::ed::space_node;
2435 
2436  bNode *node_to_insert;
2437  SpaceNode *snode;
2438  if (!ed_node_link_conditions(area, true, &snode, &node_to_insert)) {
2439  return;
2440  }
2441 
2442  /* Find link to insert on. */
2443  bNodeTree &ntree = *snode->edittree;
2444  bNodeLink *old_link = nullptr;
2445  LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
2446  if (link->flag & NODE_LINKFLAG_HILITE) {
2447  old_link = link;
2448  break;
2449  }
2450  }
2451  if (old_link == nullptr) {
2452  return;
2453  }
2454 
2455  old_link->flag &= ~NODE_LINKFLAG_HILITE;
2456 
2457  bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN);
2458  bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT);
2459 
2460  if (node_to_insert->type != NODE_REROUTE) {
2461  /* Ignore main sockets when the types don't match. */
2462  if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr &&
2463  !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type),
2464  static_cast<eNodeSocketDatatype>(best_input->type))) {
2465  best_input = nullptr;
2466  }
2467  if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr &&
2468  !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type),
2469  static_cast<eNodeSocketDatatype>(old_link->tosock->type))) {
2470  best_output = nullptr;
2471  }
2472  }
2473 
2474  bNode *from_node = old_link->fromnode;
2475  bNodeSocket *from_socket = old_link->fromsock;
2476  bNode *to_node = old_link->tonode;
2477 
2478  if (best_output != nullptr) {
2479  /* Relink the "start" of the existing link to the newly inserted node. */
2480  old_link->fromnode = node_to_insert;
2481  old_link->fromsock = best_output;
2483  }
2484  else {
2485  nodeRemLink(&ntree, old_link);
2486  }
2487 
2488  if (best_input != nullptr) {
2489  /* Add a new link that connects the node on the left to the newly inserted node. */
2490  nodeAddLink(&ntree, from_node, from_socket, node_to_insert, best_input);
2491  }
2492 
2493  /* Set up insert offset data, it needs stuff from here. */
2494  if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
2495  NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__);
2496 
2497  iofsd->insert = node_to_insert;
2498  iofsd->prev = from_node;
2499  iofsd->next = to_node;
2500 
2501  snode->runtime->iofsd = iofsd;
2502  }
2503 
2504  ED_node_tree_propagate_change(nullptr, bmain, snode->edittree);
2505 }
2506 
typedef float(TangentPoint)[2]
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
struct SpaceNode * CTX_wm_space_node(const bContext *C)
Definition: context.c:878
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition: curve.cc:1717
#define NODE_REROUTE
Definition: BKE_node.h:986
#define CMP_NODE_SPLITVIEWER
Definition: BKE_node.h:1235
#define CMP_NODE_VIEWER
Definition: BKE_node.h:1196
bool nodeAttachNodeCheck(const struct bNode *node, const struct bNode *parent)
void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node)
Definition: node.cc:2490
void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock)
Definition: node.cc:2455
#define GEO_NODE_VIEWER
Definition: BKE_node.h:1413
bool nodeIsChildOf(const bNode *parent, const bNode *child)
Definition: node.cc:2028
void nodeAttachNode(struct bNode *node, struct bNode *parent)
Definition: node.cc:2594
struct bNode * nodeGetActive(struct bNodeTree *ntree)
Definition: node.cc:3601
void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link)
Definition: node.cc:2338
int nodeSocketIsHidden(const struct bNodeSocket *sock)
void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link)
Definition: node.cc:2432
int nodeSocketLinkLimit(const struct bNodeSocket *sock)
void ntreeNodeFlagSet(const bNodeTree *ntree, int flag, bool enable)
Definition: node.cc:3237
struct bNodeLink * nodeAddLink(struct bNodeTree *ntree, struct bNode *fromnode, struct bNodeSocket *fromsock, struct bNode *tonode, struct bNodeSocket *tosock)
Definition: node.cc:2296
void nodeParentsIter(bNode *node, bool(*callback)(bNode *, void *), void *userdata)
Definition: node.cc:2115
struct bNode * nodeFindRootParent(bNode *node)
Definition: node.cc:2020
void nodeChainIter(const bNodeTree *ntree, const bNode *node_start, bool(*callback)(bNode *, bNode *, void *, const bool), void *userdata, bool reversed)
Definition: node.cc:2039
bool nodeDeclarationEnsure(struct bNodeTree *ntree, struct bNode *node)
Definition: node.cc:3731
#define NODE_FRAME
Definition: BKE_node.h:985
int nodeCountSocketLinks(const struct bNodeTree *ntree, const struct bNodeSocket *sock)
#define NODE_GROUP_INPUT
Definition: BKE_node.h:987
void nodeDetachNode(struct bNode *node)
Definition: node.cc:2607
void BKE_ntree_update_tag_node_property(struct bNodeTree *ntree, struct bNode *node)
void BKE_ntree_update_tag_link_changed(struct bNodeTree *ntree)
void BKE_ntree_update_tag_link_added(struct bNodeTree *ntree, struct bNodeLink *link)
struct ARegion * BKE_area_find_region_type(const struct ScrArea *area, int type)
#define BLI_assert(a)
Definition: BLI_assert.h:46
float BLI_easing_cubic_ease_in_out(float time, float begin, float change, float duration)
Definition: easing.c:107
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
Definition: BLI_listbase.h:348
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
Definition: BLI_listbase.h:344
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
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:1108
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition: math_geom.c:283
bool BLI_rctf_isect_x(const rctf *rect, float x)
Definition: rct.c:92
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
bool BLI_rctf_isect_y(const rctf *rect, float y)
Definition: rct.c:103
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, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
unsigned char uchar
Definition: BLI_sys_types.h:70
#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)
eCustomDataType
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_COLOR
@ CD_PROP_INT32
@ CD_PROP_BOOL
@ CD_AUTO_FROM_NAME
#define NODE_LINK_VALID
#define NODE_TEST
#define NODE_LINK_DRAGGED
#define NODE_DO_OUTPUT
#define NTREE_GEOMETRY
#define NTREE_CUSTOM
#define NODE_LINK_TEST
#define NODE_LINK_TEMP_HIGHLIGHT
eNodeSocketInOut
@ SOCK_OUT
@ SOCK_IN
@ SOCK_MULTI_INPUT
@ SOCK_IN_USE
@ SOCK_HIDDEN
@ SOCK_UNAVAIL
eNodeSocketDatatype
@ SOCK_INT
@ SOCK_TEXTURE
@ __SOCK_MESH
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_MATERIAL
@ SOCK_SHADER
@ SOCK_FLOAT
@ SOCK_IMAGE
@ SOCK_COLLECTION
@ SOCK_CUSTOM
@ SOCK_GEOMETRY
@ SOCK_OBJECT
@ SOCK_STRING
@ SOCK_RGBA
#define NODE_SELECT
#define NODE_LINKFLAG_HILITE
@ RGN_TYPE_WINDOW
@ SNODE_SKIP_INSOFFSET
@ SNODE_INSERTOFS_DIR_RIGHT
@ SPACE_NODE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
bool ED_node_is_compositor(struct SpaceNode *snode)
Definition: node_edit.cc:466
bool ED_node_is_geometry(struct SpaceNode *snode)
Definition: node_edit.cc:481
#define NODE_EDGE_PAN_OUTSIDE_PAD
Definition: ED_node.h:38
#define NODE_EDGE_PAN_INSIDE_PAD
Definition: ED_node.h:37
#define NODE_EDGE_PAN_MAX_SPEED
Definition: ED_node.h:40
#define NODE_EDGE_PAN_DELAY
Definition: ED_node.h:41
#define NODE_EDGE_PAN_ZOOM_INFLUENCE
Definition: ED_node.h:42
void ED_node_tree_propagate_change(const struct bContext *C, struct Main *bmain, struct bNodeTree *ntree)
#define NODE_EDGE_PAN_SPEED_RAMP
Definition: ED_node.h:39
void ED_preview_kill_jobs(struct wmWindowManager *wm, struct Main *bmain)
void ED_area_tag_redraw(ScrArea *area)
Definition: area.c:729
bool ED_operator_node_editable(struct bContext *C)
Definition: screen_ops.c:318
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
void ED_workspace_status_text(struct bContext *C, const char *str)
Definition: area.c:816
void * ED_region_draw_cb_activate(struct ARegionType *art, void(*draw)(const struct bContext *, struct ARegion *, void *), void *customdata, int type)
Definition: spacetypes.c:226
#define REGION_DRAW_POST_PIXEL
Definition: ED_space_api.h:63
bool ED_region_draw_cb_exit(struct ARegionType *art, void *handle)
Definition: spacetypes.c:241
void ED_spreadsheet_context_paths_set_geometry_node(struct Main *bmain, struct SpaceNode *snode, struct bNode *node)
_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 y
_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
Read Guarded memory(de)allocation.
NODE_GROUP_OUTPUT
#define RNA_BEGIN(sptr, itemptr, propname)
Definition: RNA_access.h:543
#define RNA_END
Definition: RNA_access.h:550
PropertyFlag
Definition: RNA_types.h:183
@ PROP_SKIP_SAVE
Definition: RNA_types.h:218
@ PROP_HIDDEN
Definition: RNA_types.h:216
#define C
Definition: RandGen.cpp:25
#define UI_PRECISION_FLOAT_MAX
#define UI_MAX_DRAW_STR
Definition: UI_interface.h:91
#define UI_DPI_FAC
Definition: UI_interface.h:305
void UI_icon_draw_ex(float x, float y, int icon_id, float aspect, float alpha, float desaturate, const uchar mono_color[4], bool mono_border)
void UI_view2d_edge_pan_operator_init(struct bContext *C, struct View2DEdgePanData *vpd, struct wmOperator *op)
void UI_view2d_region_to_view(const struct View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
void UI_view2d_edge_pan_operator_properties_ex(struct wmOperatorType *ot, float inside_pad, float outside_pad, float speed_ramp, float max_speed, float delay, float zoom_influence)
void void UI_view2d_edge_pan_apply_event(struct bContext *C, struct View2DEdgePanData *vpd, const struct wmEvent *event)
void UI_view2d_edge_pan_cancel(struct bContext *C, struct View2DEdgePanData *vpd)
@ KM_RELEASE
Definition: WM_types.h:268
@ OPTYPE_BLOCKING
Definition: WM_types.h:150
@ OPTYPE_DEPENDS_ON_CURSOR
Definition: WM_types.h:184
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define NC_NODE
Definition: WM_types.h:344
#define ND_DISPLAY
Definition: WM_types.h:439
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: avxb.h:154
unsigned int U
Definition: btGjkEpa3.h:78
void sort(btMatrix3x3 &U, btVector3 &sigma, btMatrix3x3 &V, int t)
Helper function of 3X3 SVD for sorting singular values.
MutableSpan< T > as_mutable_span()
Definition: BLI_vector.hh:330
void append(const T &value)
Definition: BLI_vector.hh:433
bool is_empty() const
Definition: BLI_vector.hh:706
IndexRange index_range() const
Definition: BLI_vector.hh:920
const T & first() const
Definition: BLI_vector.hh:680
Span< SocketDeclarationPtr > outputs() const
Span< SocketDeclarationPtr > inputs() const
Span< const InputSocketRef * > directly_linked_sockets() const
const NodeRef & node() const
bNodeSocketType * typeinfo() const
StringRefNull idname() const
const NodeTreeRef & tree() const
bNodeSocket * bsocket() const
#define SELECT
OperationNode * node
StackEntry * from
void * tree
bNodeTree * ntree
uint padding(uint offset, uint alignment)
int count
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
static ulong * next
static unsigned a[3]
Definition: RandGen.cpp:78
Insertion insert(const float3 &point_prev, const float3 &handle_prev, const float3 &handle_next, const float3 &point_next, float parameter)
Definition: curve_bezier.cc:61
static void area(int d1, int d2, int e1, int e2, float weights[2])
static const NodeRef * get_existing_viewer(const NodeTreeRef &tree)
static bool is_viewer_socket_in_viewer(const InputSocketRef &socket)
static bool is_linked_to_viewer(const OutputSocketRef &socket, const NodeRef &viewer_node)
static Vector< const NodeRef * > find_viewer_nodes(const NodeTreeRef &tree)
static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node)
static bNodeSocket * node_link_viewer_get_socket(bNodeTree &ntree, bNode &viewer_node, bNodeSocket &src_socket)
static bool socket_can_be_viewed(const OutputSocketRef &socket)
static int link_socket_to_viewer(const bContext &C, bNode *viewer_bnode, bNode &bnode_to_view, bNodeSocket &bsocket_to_view)
static int node_link_viewer(const bContext &C, bNode &bnode_to_view)
static eCustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type)
static bool is_viewer_node(const NodeRef &node)
static const OutputSocketRef * find_output_socket_to_be_viewed(const NodeRef *active_viewer_node, const NodeRef &node_to_view)
static int get_default_viewer_type(const bContext *C)
void NODE_OT_parent_set(wmOperatorType *ot)
static int cut_links_exec(bContext *C, wmOperator *op)
static void draw_draglink_tooltip_activate(const ARegion &region, bNodeLinkDrag &nldrag)
void node_sort(bNodeTree &ntree)
Definition: node_draw.cc:215
static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
static bool snode_autoconnect_input(SpaceNode &snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace)
void invoke_node_link_drag_add_menu(bContext &C, bNode &node, bNodeSocket &socket, const float2 &cursor)
static void node_remove_extra_links(SpaceNode &snode, bNodeLink &link)
static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links)
void NODE_OT_detach(wmOperatorType *ot)
static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace)
static void node_link_insert_offset_frame_chains(const bNodeTree *ntree, const bNode *parent, NodeInsertOfsData *data, const bool reversed)
static bNode * node_find_frame_to_attach(ARegion &region, const bNodeTree &ntree, const int mouse_xy[2])
void NODE_OT_insert_offset(wmOperatorType *ot)
static int get_main_socket_priority(const bNodeSocket *socket)
float2 node_link_calculate_multi_input_position(const float2 &socket_position, const int index, const int total_inputs)
Definition: node_edit.cc:104
static void pick_link(wmOperator &op, bNodeLinkDrag &nldrag, SpaceNode &snode, bNode *node, bNodeLink &link_to_pick)
static bNodeSocket * get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out)
static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
void NODE_OT_links_detach(wmOperatorType *ot)
bool node_link_bezier_points(const View2D *v2d, const SpaceNode *snode, const bNodeLink &link, float coord_array[][2], const int resol)
Definition: drawnode.cc:1665
bNode * node_add_node(const bContext &C, const char *idname, int type, float locx, float locy)
Definition: node_add.cc:52
static bool dragged_links_are_detached(const bNodeLinkDrag &nldrag)
void NODE_OT_links_cut(wmOperatorType *ot)
static void pick_input_link_by_link_intersect(const bContext &C, wmOperator &op, bNodeLinkDrag &nldrag, const float2 &cursor)
static int mute_links_exec(bContext *C, wmOperator *op)
static void node_link_cancel(bContext *C, wmOperator *op)
static void draw_draglink_tooltip(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg)
void NODE_OT_join(wmOperatorType *ot)
void node_deselect_all_output_sockets(SpaceNode &snode, bool deselect_nodes)
Definition: node_select.cc:287
static bool node_active_link_viewer_poll(bContext *C)
void NODE_OT_attach(wmOperatorType *ot)
bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link)
Definition: node_edit.cc:1320
static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void draw_draglink_tooltip_deactivate(const ARegion &region, bNodeLinkDrag &nldrag)
static bool node_links_intersect(bNodeLink &link, const float mcoords[][2], int tot)
static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
void NODE_OT_link_viewer(wmOperatorType *ot)
bool node_find_indicated_socket(SpaceNode &snode, bNode **nodep, bNodeSocket **sockp, const float2 &cursor, const eNodeSocketInOut in_out)
Definition: node_edit.cc:1226
static int node_make_link_exec(bContext *C, wmOperator *op)
static int node_count_links(const bNodeTree &ntree, const bNodeSocket &socket)
static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag))
static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
static bNodeSocket * best_socket_output(bNodeTree *ntree, bNode *node, bNodeSocket *sock_target, const bool allow_multiple)
static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
void NODE_OT_link_make(wmOperatorType *ot)
static bNodeLink * create_drag_link(bNode &node, bNodeSocket &sock)
float2 node_to_view(const bNode &node, const float2 &co)
Definition: node_draw.cc:284
static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, ARegion *region, const int mouse_xy[2], const bool right_alignment)
void sort_multi_input_socket_links(SpaceNode &snode, bNode &node, bNodeLink *drag_link, const float2 *cursor)
static void node_detach_recursive(bNode *node)
static bool node_link_insert_offset_chain_cb(bNode *fromnode, bNode *tonode, void *userdata, const bool reversed)
static void node_join_attach_recursive(bNode *node, bNode *frame)
void node_to_updated_rect(const bNode &node, rctf &r_rect)
Definition: node_draw.cc:291
bool node_link_bezier_handles(const View2D *v2d, const SpaceNode *snode, const bNodeLink &link, float vec[4][2])
Definition: drawnode.cc:1587
static bool should_create_drag_link_search_menu(const bNodeTree &node_tree, const bNodeLinkDrag &nldrag)
static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cursor)
void NODE_OT_link(wmOperatorType *ot)
static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
void node_deselect_all_input_sockets(SpaceNode &snode, bool deselect_nodes)
Definition: node_select.cc:257
static bNodeSocket * best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
static std::unique_ptr< bNodeLinkDrag > node_link_init(SpaceNode &snode, float2 cursor, const bool detach)
void NODE_OT_links_mute(wmOperatorType *ot)
static int node_detach_exec(bContext *C, wmOperator *UNUSED(op))
static bool node_link_insert_offset_frame_chain_cb(bNode *fromnode, bNode *tonode, void *userdata, const bool reversed)
static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used)
static bool ed_node_link_conditions(ScrArea *area, bool test, SpaceNode **r_snode, bNode **r_select)
static void node_offset_apply(bNode &node, const float offset_x)
static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, const float offset_x)
static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
T distance(const T &a, const T &b)
SymEdge< T > * prev(const SymEdge< T > *se)
Definition: delaunay_2d.cc:105
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
#define NODE_LINK_RESOL
Definition: node_intern.hh:123
#define NODE_WIDTH(node)
Definition: node_intern.hh:115
#define NODE_DETACH_IS_DESCENDANT
static void clear_picking_highlight(ListBase *links)
#define NODE_JOIN_DONE
#define NODE_JOIN_IS_DESCENDANT
#define NODE_INSOFS_ANIM_DURATION
#define NODE_DETACH_DONE
void ED_node_link_insert(Main *bmain, ScrArea *area)
void ED_node_link_intersect_test(ScrArea *area, int test)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:4874
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
Definition: rna_access.c:4980
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
Definition: rna_access.c:4992
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
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:4076
PropertyRNA * RNA_def_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
Definition: rna_define.c:4221
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1490
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:3597
struct ARegionType * type
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
SpaceNode_Runtime * runtime
struct bNodeTree * edittree
struct bNode * node
struct bNodeListItem * next
char name[64]
bool(* validate_link)(eNodeSocketDatatype from, eNodeSocketDatatype to)
Definition: BKE_node.h:402
short is_updating
struct bNodeTreeType * typeinfo
ListBase nodes
ListBase links
void(* updatefunc)(struct bNodeTree *ntree, struct bNode *node)
Definition: BKE_node.h:265
void(* insert_link)(struct bNodeTree *ntree, struct bNode *node, struct bNodeLink *link)
Definition: BKE_node.h:301
ListBase inputs
struct bNodeType * typeinfo
struct bNode * parent
struct bNode * prev
short type
void * storage
ListBase outputs
float xmax
Definition: DNA_vec_types.h:69
float xmin
Definition: DNA_vec_types.h:69
float ymax
Definition: DNA_vec_types.h:70
float ymin
Definition: DNA_vec_types.h:70
short val
Definition: WM_types.h:680
int mval[2]
Definition: WM_types.h:684
short type
Definition: WM_types.h:678
void * customdata
Definition: WM_types.h:715
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:935
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
void(* cancel)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:927
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
struct PointerRNA * ptr
double duration
Definition: WM_types.h:872
@ WM_CURSOR_KNIFE
Definition: wm_cursors.h:31
@ WM_CURSOR_MUTE
Definition: wm_cursors.h:59
void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2])
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
@ EVT_ESCKEY
wmOperatorType * ot
Definition: wm_files.c:3479
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:1682
wmTimer * WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
Definition: wm_window.c:1630