Blender V4.5
node_relationships.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "DNA_node_types.h"
12
13#include "BLI_easing.h"
14#include "BLI_listbase.h"
15#include "BLI_math_geom.h"
16#include "BLI_stack.hh"
17
18#include "BKE_context.hh"
20#include "BKE_node.hh"
22#include "BKE_node_runtime.hh"
24#include "BKE_screen.hh"
25
26#include "ED_node.hh" /* own include */
27#include "ED_render.hh"
28#include "ED_screen.hh"
29#include "ED_space_api.hh"
30#include "ED_viewer_path.hh"
31
32#include "RNA_access.hh"
33#include "RNA_define.hh"
34#include "RNA_prototypes.hh"
35
36#include "WM_api.hh"
37#include "WM_types.hh"
38
39#include "UI_interface_icons.hh"
40#include "UI_resources.hh"
41#include "UI_view2d.hh"
42
44#include "NOD_socket.hh"
45
46#include "node_intern.hh" /* own include */
47
50 bNode *insert; /* Inserted node. */
51 bNode *prev, *next; /* Previous/next node in the chain. */
52
54
55 float offset_x; /* Offset to apply to node chain. */
56};
57
59
61{
62 LISTBASE_FOREACH (bNodeLink *, link, links) {
63 link->flag &= ~NODE_LINK_TEMP_HIGHLIGHT;
64 }
65}
66
67/* -------------------------------------------------------------------- */
70
72{
73 bNodeLink oplink{};
74 if (socket.in_out == SOCK_OUT) {
75 oplink.fromnode = &node;
76 oplink.fromsock = &socket;
77 }
78 else {
79 oplink.tonode = &node;
80 oplink.tosock = &socket;
81 }
82 oplink.flag |= NODE_LINK_VALID;
83 return oplink;
84}
85
86static void pick_link(bNodeLinkDrag &nldrag,
87 SpaceNode &snode,
88 bNode *node,
89 bNodeLink &link_to_pick)
90{
92
93 bNodeLink link = create_drag_link(*link_to_pick.fromnode, *link_to_pick.fromsock);
94
95 nldrag.links.append(link);
96 bke::node_remove_link(snode.edittree, link_to_pick);
97 snode.edittree->ensure_topology_cache();
100
101 /* Send changed event to original link->tonode. */
102 if (node) {
104 }
105}
106
108 wmOperator &op,
109 bNodeLinkDrag &nldrag,
110 const float2 &cursor)
111{
112 SpaceNode *snode = CTX_wm_space_node(&C);
113 ARegion *region = CTX_wm_region(&C);
114 bNodeTree &node_tree = *snode->edittree;
115
116 float2 drag_start;
117 RNA_float_get_array(op.ptr, "drag_start", drag_start);
118 bNodeSocket *socket = node_find_indicated_socket(*snode, *region, drag_start, SOCK_IN);
119 bNode &node = socket->owner_node();
120
121 /* Distance to test overlapping of cursor on link. */
122 const float cursor_link_touch_distance = 12.5f * UI_SCALE_FAC;
123
124 bNodeLink *link_to_pick = nullptr;
125 clear_picking_highlight(&node_tree.links);
126 for (bNodeLink *link : socket->directly_linked_links()) {
127 /* Test if the cursor is near a link. */
128 std::array<float2, NODE_LINK_RESOL + 1> coords;
130
131 for (const int i : IndexRange(coords.size() - 1)) {
132 const float distance = dist_squared_to_line_segment_v2(cursor, coords[i], coords[i + 1]);
133 if (distance < cursor_link_touch_distance) {
134 link_to_pick = link;
135 nldrag.last_picked_multi_input_socket_link = link_to_pick;
136 }
137 }
138 }
139
140 /* If no linked was picked in this call, try using the one picked in the previous call.
141 * Not essential for the basic behavior, but can make interaction feel a bit better if
142 * the mouse moves to the right and loses the "selection." */
143 if (!link_to_pick) {
144 bNodeLink *last_picked_link = nldrag.last_picked_multi_input_socket_link;
145 if (last_picked_link) {
146 link_to_pick = last_picked_link;
147 }
148 }
149
150 if (link_to_pick) {
151 /* Highlight is set here and cleared in the next iteration or if the operation finishes. */
152 link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
154
155 if (!node_find_indicated_socket(*snode, *region, cursor, SOCK_IN)) {
156 pick_link(nldrag, *snode, &node, *link_to_pick);
157 }
158 }
159}
160
161static bool socket_is_available(bNodeTree * /*ntree*/, bNodeSocket *sock, const bool allow_used)
162{
163 if (!sock->is_visible()) {
164 return false;
165 }
166
167 if (!allow_used && (sock->flag & SOCK_IS_LINKED)) {
168 /* Multi input sockets are available (even if used). */
169 if (!(sock->flag & SOCK_MULTI_INPUT)) {
170 return false;
171 }
172 }
173
174 return true;
175}
176
178 bNode *node,
179 bNodeSocket *sock_target,
180 const bool allow_multiple)
181{
182 /* First look for selected output. */
183 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
184 if (!socket_is_available(ntree, sock, allow_multiple)) {
185 continue;
186 }
187
188 if (sock->flag & SELECT) {
189 return sock;
190 }
191 }
192
193 /* Try to find a socket with a matching name. */
194 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
195 if (!socket_is_available(ntree, sock, allow_multiple)) {
196 continue;
197 }
198
199 /* Check for same types. */
200 if (sock->type == sock_target->type) {
201 if (STREQ(sock->name, sock_target->name)) {
202 return sock;
203 }
204 }
205 }
206
207 /* Otherwise settle for the first available socket of the right type. */
208 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
209 if (!socket_is_available(ntree, sock, allow_multiple)) {
210 continue;
211 }
212
213 /* Check for same types. */
214 if (sock->type == sock_target->type) {
215 return sock;
216 }
217 }
218
219 /* Always allow linking to an reroute node. The socket type of the reroute sockets might change
220 * after the link has been created. */
221 if (node->is_reroute()) {
222 return (bNodeSocket *)node->outputs.first;
223 }
224
225 return nullptr;
226}
227
228/* This is a bit complicated, but designed to prioritize finding
229 * sockets of higher types, such as image, first. */
230static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
231{
232 int maxtype = 0;
233 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
234 maxtype = max_ii(sock->type, maxtype);
235 }
236
237 /* Find sockets of higher 'types' first (i.e. image). */
238 int a = 0;
239 for (int socktype = maxtype; socktype >= 0; socktype--) {
240 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
241 if (!socket_is_available(ntree, sock, replace)) {
242 a++;
243 continue;
244 }
245
246 if (sock->type == socktype) {
247 /* Increment to make sure we don't keep finding the same socket on every attempt running
248 * this function. */
249 a++;
250 if (a > num) {
251 return sock;
252 }
253 }
254 }
255 }
256
257 return nullptr;
258}
259
261 bNode *node_fr,
262 bNodeSocket *sock_fr,
263 bNode *node_to,
264 bNodeSocket *sock_to,
265 int replace)
266{
267 bNodeTree *ntree = snode.edittree;
268
269 if (replace) {
270 bke::node_remove_socket_links(*ntree, *sock_to);
271 }
272
273 bke::node_add_link(*ntree, *node_fr, *sock_fr, *node_to, *sock_to);
274 return true;
275}
276
281
283 bNodeLink &drag_link,
284 const float2 &cursor)
285{
286 const float2 &socket_location = socket.runtime->location;
287
289 for (bNodeLink *link : socket.directly_linked_links()) {
291 socket_location, link->multi_input_sort_id, link->tosock->runtime->total_inputs);
292 links.append({link, location});
293 };
294
295 links.append({&drag_link, cursor});
296
297 std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) {
298 return a.multi_socket_position.y < b.multi_socket_position.y;
299 });
300
301 for (const int i : links.index_range()) {
302 links[i].link->multi_input_sort_id = i;
303 }
304}
305
307{
308 for (bNodeSocket *socket : node.input_sockets()) {
309 if (!socket->is_multi_input()) {
310 continue;
311 }
312 Vector<bNodeLink *, 8> links = socket->directly_linked_links();
313 std::sort(links.begin(), links.end(), [](const bNodeLink *a, const bNodeLink *b) {
314 return a->multi_input_sort_id < b->multi_input_sort_id;
315 });
316
317 for (const int i : links.index_range()) {
318 links[i]->multi_input_sort_id = i;
319 }
320 }
321}
322
323static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace)
324{
325 bNodeTree *ntree = snode.edittree;
326 Vector<bNode *> sorted_nodes = get_selected_nodes(*ntree).extract_vector();
327
328 /* Sort nodes left to right. */
329 std::sort(sorted_nodes.begin(), sorted_nodes.end(), [](const bNode *a, const bNode *b) {
330 return a->location[0] < b->location[0];
331 });
332
333 // int numlinks = 0; /* UNUSED */
334 for (const int i : sorted_nodes.as_mutable_span().drop_back(1).index_range()) {
335 bool has_selected_inputs = false;
336
337 bNode *node_fr = sorted_nodes[i];
338 bNode *node_to = sorted_nodes[i + 1];
339 /* Corner case: input/output node aligned the wrong way around (#47729). */
340 if (BLI_listbase_is_empty(&node_to->inputs) || BLI_listbase_is_empty(&node_fr->outputs)) {
341 std::swap(node_fr, node_to);
342 }
343
344 /* If there are selected sockets, connect those. */
345 LISTBASE_FOREACH (bNodeSocket *, sock_to, &node_to->inputs) {
346 if (sock_to->flag & SELECT) {
347 has_selected_inputs = true;
348
349 if (!socket_is_available(ntree, sock_to, replace)) {
350 continue;
351 }
352
353 /* Check for an appropriate output socket to connect from. */
354 bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
355 if (!sock_fr) {
356 continue;
357 }
358
359 if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
360 // numlinks++;
361 }
362 }
363 }
364
365 if (!has_selected_inputs) {
366 /* No selected inputs, connect by finding suitable match. */
367 int num_inputs = BLI_listbase_count(&node_to->inputs);
368
369 for (int i = 0; i < num_inputs; i++) {
370
371 /* Find the best guess input socket. */
372 bNodeSocket *sock_to = best_socket_input(ntree, node_to, i, replace);
373 if (!sock_to) {
374 continue;
375 }
376
377 /* Check for an appropriate output socket to connect from. */
378 bNodeSocket *sock_fr = best_socket_output(ntree, node_fr, sock_to, allow_multiple);
379 if (!sock_fr) {
380 continue;
381 }
382
383 if (snode_autoconnect_input(snode, node_fr, sock_fr, node_to, sock_to, replace)) {
384 // numlinks++;
385 break;
386 }
387 }
388 }
389 }
390}
391
393
394namespace viewer_linking {
395
396/* -------------------------------------------------------------------- */
399
400/* Depending on the node tree type, different socket types are supported by viewer nodes. */
401static bool socket_can_be_viewed(const bNodeSocket &socket)
402{
403 if (!socket.is_icon_visible()) {
404 return false;
405 }
406 if (STREQ(socket.idname, "NodeSocketVirtual")) {
407 return false;
408 }
409 if (socket.owner_tree().type != NTREE_GEOMETRY) {
410 return true;
411 }
412 return ELEM(socket.typeinfo->type,
416 SOCK_INT,
420 SOCK_RGBA,
421 SOCK_MENU);
422}
423
428 bNode &viewer_node,
429 bNodeSocket &src_socket)
430{
431 if (viewer_node.type_legacy != GEO_NODE_VIEWER) {
432 /* In viewer nodes in the compositor, only the first input should be linked to. */
433 return (bNodeSocket *)viewer_node.inputs.first;
434 }
435 /* For the geometry nodes viewer, find the socket with the correct type. */
436
437 if (src_socket.type == SOCK_GEOMETRY) {
438 return static_cast<bNodeSocket *>(viewer_node.inputs.first);
439 }
440
441 ntree.ensure_topology_cache();
442 if (!socket_can_be_viewed(src_socket)) {
443 return nullptr;
444 }
445
446 NodeGeometryViewer &storage = *static_cast<NodeGeometryViewer *>(viewer_node.storage);
448 eNodeSocketDatatype(src_socket.type));
449 BLI_assert(data_type != CD_AUTO_FROM_NAME);
450 storage.data_type = data_type;
452
453 return static_cast<bNodeSocket *>(viewer_node.inputs.last);
454}
455
456static bool is_viewer_node(const bNode &node)
457{
459}
460
461static bool is_viewer_socket_in_viewer(const bNodeSocket &socket)
462{
463 const bNode &node = socket.owner_node();
465 if (node.typeinfo->type_legacy == GEO_NODE_VIEWER) {
466 return true;
467 }
468 return socket.index() == 0;
469}
470
471static bool is_viewer_socket(const bNodeSocket &socket)
472{
473 if (is_viewer_node(socket.owner_node())) {
474 return is_viewer_socket_in_viewer(socket);
475 }
476 return false;
477}
478
480{
481 SpaceNode *snode = CTX_wm_space_node(C);
483}
484
486{
487 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
488 if (link->tonode == &viewer_node) {
489 if (link->tosock->flag & SOCK_UNAVAIL) {
490 bke::node_remove_link(&btree, *link);
491 }
492 }
493 }
494}
495
497{
498 int last_linked_data_socket_index = -1;
499 bool has_linked_geometry_socket = false;
500 for (bNodeSocket *socket : node_to_view.output_sockets()) {
501 if (!socket_can_be_viewed(*socket)) {
502 continue;
503 }
504 for (bNodeLink *link : socket->directly_linked_links()) {
505 bNodeSocket &target_socket = *link->tosock;
506 bNode &target_node = *link->tonode;
507 if (is_viewer_socket(target_socket)) {
508 if (link->is_muted() || !(target_node.flag & NODE_DO_OUTPUT)) {
509 /* This socket is linked to a deactivated viewer, the viewer should be activated. */
510 return socket;
511 }
512 if (socket->type == SOCK_GEOMETRY) {
513 has_linked_geometry_socket = true;
514 }
515 else {
516 last_linked_data_socket_index = socket->index();
517 }
518 }
519 }
520 }
521
522 if (last_linked_data_socket_index == -1 && !has_linked_geometry_socket) {
523 /* Return the first socket that can be viewed. */
524 for (bNodeSocket *socket : node_to_view.output_sockets()) {
525 if (socket_can_be_viewed(*socket)) {
526 return socket;
527 }
528 }
529 return nullptr;
530 }
531
532 bNodeSocket *already_viewed_socket = nullptr;
533
534 /* Pick the next socket to be linked to the viewer. */
535 const int tot_outputs = node_to_view.output_sockets().size();
536 for (const int offset : IndexRange(1, tot_outputs)) {
537 const int index = (last_linked_data_socket_index + offset) % tot_outputs;
538 bNodeSocket &output_socket = node_to_view.output_socket(index);
539 if (!socket_can_be_viewed(output_socket)) {
540 continue;
541 }
542 if (has_linked_geometry_socket && output_socket.type == SOCK_GEOMETRY) {
543 /* Skip geometry sockets when cycling if one is already viewed. */
544 already_viewed_socket = &output_socket;
545 continue;
546 }
547
548 bool is_currently_viewed = false;
549 for (const bNodeLink *link : output_socket.directly_linked_links()) {
550 bNodeSocket &target_socket = *link->tosock;
551 bNode &target_node = *link->tonode;
552 if (!is_viewer_socket(target_socket)) {
553 continue;
554 }
555 if (link->is_muted()) {
556 continue;
557 }
558 if (!(target_node.flag & NODE_DO_OUTPUT)) {
559 continue;
560 }
561 is_currently_viewed = true;
562 break;
563 }
564 if (is_currently_viewed) {
565 already_viewed_socket = &output_socket;
566 continue;
567 }
568 return &output_socket;
569 }
570 return already_viewed_socket;
571}
572
573static void finalize_viewer_link(const bContext &C,
574 SpaceNode &snode,
575 bNode &viewer_node,
576 bNodeLink &viewer_link)
577{
578 Main *bmain = CTX_data_main(&C);
580 viewer_link.flag &= ~NODE_LINK_MUTED;
581 viewer_node.flag &= ~NODE_MUTED;
582 viewer_node.flag |= NODE_DO_OUTPUT;
583
584 if (snode.edittree->type == NTREE_GEOMETRY) {
585 viewer_path::activate_geometry_node(*bmain, snode, viewer_node);
586 }
587 else if (snode.edittree->type == NTREE_COMPOSIT) {
588 for (bNode *node : snode.nodetree->all_nodes()) {
589 if (node->is_type("CompositorNodeViewer") && node != &viewer_node) {
590 node->flag &= ~NODE_DO_OUTPUT;
591 }
592 }
593 }
595 BKE_main_ensure_invariants(*bmain, snode.edittree->id);
596}
597
599 const rctf &rect,
600 const Span<const bNode *> ignored_nodes)
601{
602 for (const bNode *node : tree.all_nodes()) {
603 if (node->is_frame()) {
604 continue;
605 }
606 if (ignored_nodes.contains(node)) {
607 continue;
608 }
609 if (BLI_rctf_isect(&rect, &node->runtime->draw_bounds, nullptr)) {
610 return node;
611 }
612 }
613 return nullptr;
614}
615
621 const float step_distance,
622 const float max_distance)
623{
624 /* Prefer moving viewer a bit further horizontally than vertically. */
625 const float y_scale = 0.5f;
626
627 Vector<float2> candidates;
628 candidates.append(initial);
629 for (float distance = step_distance; distance <= max_distance; distance += step_distance) {
630 const float arc_length = distance * M_PI;
631 const int checks = std::max<int>(2, ceilf(arc_length / step_distance));
632 for (const int i : IndexRange(checks)) {
633 const float angle = i / float(checks - 1) * M_PI;
634 const float candidate_x = initial.x + distance * std::sin(angle);
635 const float candidate_y = initial.y + distance * std::cos(angle) * y_scale;
636 candidates.append({candidate_x, candidate_y});
637 }
638 }
639 return candidates;
640}
641
647static void position_viewer_node(const bContext &C,
649 bNode &viewer_node,
650 const bNode &node_to_view)
651{
652 ScrArea &area = *CTX_wm_area(&C);
653 ARegion &region = *CTX_wm_region(&C);
655
656 tree.ensure_topology_cache();
657
658 const View2D &v2d = region.v2d;
659 rctf region_rect;
660 region_rect.xmin = 0;
661 region_rect.xmax = region.winx;
662 region_rect.ymin = 0;
663 region_rect.ymax = region.winy;
664 if (U.uiflag2 & USER_REGION_OVERLAP) {
665 region_rect.xmax -= sidebar.winx;
666 }
667
668 rctf region_bounds;
669 UI_view2d_region_to_view_rctf(&v2d, &region_rect, &region_bounds);
670
671 viewer_node.ui_order = tree.all_nodes().size();
673
674 const bool is_new_viewer_node = BLI_rctf_size_x(&viewer_node.runtime->draw_bounds) == 0;
675 if (!is_new_viewer_node &&
676 BLI_rctf_inside_rctf(&region_bounds, &viewer_node.runtime->draw_bounds) &&
677 viewer_node.runtime->draw_bounds.xmin > node_to_view.runtime->draw_bounds.xmax)
678 {
679 /* Stay at the old viewer position when the viewer node is still in view and on the right side
680 * of the node-to-view. */
681 return;
682 }
683
684 const float default_padding_x = U.node_margin;
685 const float default_padding_y = 10;
686 const float viewer_width = is_new_viewer_node ?
687 viewer_node.width * UI_SCALE_FAC :
688 BLI_rctf_size_x(&viewer_node.runtime->draw_bounds);
689 const float viewer_height = is_new_viewer_node ?
690 100 * UI_SCALE_FAC :
691 BLI_rctf_size_y(&viewer_node.runtime->draw_bounds);
692
693 const float2 main_candidate{node_to_view.runtime->draw_bounds.xmax + default_padding_x,
694 node_to_view.runtime->draw_bounds.ymax + viewer_height +
695 default_padding_y};
696
697 std::optional<float2> new_viewer_position;
698
699 const Vector<float2> position_candidates = get_viewer_node_position_candidates(
700 main_candidate, 50 * UI_SCALE_FAC, 800 * UI_SCALE_FAC);
701 for (const float2 &candidate_pos : position_candidates) {
702 rctf candidate;
703 candidate.xmin = candidate_pos.x;
704 candidate.xmax = candidate_pos.x + viewer_width;
705 candidate.ymin = candidate_pos.y - viewer_height;
706 candidate.ymax = candidate_pos.y;
707
708 if (!BLI_rctf_inside_rctf(&region_bounds, &candidate)) {
709 /* Avoid moving viewer outside of visible region. */
710 continue;
711 }
712
713 rctf padded_candidate = candidate;
714 BLI_rctf_pad(&padded_candidate, default_padding_x - 1, default_padding_y - 1);
715
716 const bNode *overlapping_node = find_overlapping_node(
717 tree, padded_candidate, {&viewer_node, &node_to_view});
718 if (!overlapping_node) {
719 new_viewer_position = candidate_pos;
720 break;
721 }
722 }
723
724 if (!new_viewer_position) {
725 new_viewer_position = main_candidate;
726 }
727
728 viewer_node.location[0] = new_viewer_position->x / UI_SCALE_FAC;
729 viewer_node.location[1] = new_viewer_position->y / UI_SCALE_FAC;
730 viewer_node.parent = nullptr;
731}
732
733static int view_socket(const bContext &C,
734 SpaceNode &snode,
735 bNodeTree &btree,
736 bNode &bnode_to_view,
737 bNodeSocket &bsocket_to_view)
738{
739 bNode *viewer_node = nullptr;
740 /* Try to find a viewer that is already active. */
741 for (bNode *node : btree.all_nodes()) {
742 if (is_viewer_node(*node)) {
743 if (node->flag & NODE_DO_OUTPUT && node->custom1 == NODE_VIEWER_SHORTCUT_NONE) {
744 viewer_node = node;
745 break;
746 }
747 }
748 }
749
750 /* Try to reactivate existing viewer connection. */
751 for (bNodeLink *link : bsocket_to_view.directly_linked_links()) {
752 bNodeSocket &target_socket = *link->tosock;
753 bNode &target_node = *link->tonode;
754 if (is_viewer_socket(target_socket) && ELEM(viewer_node, nullptr, &target_node)) {
755 finalize_viewer_link(C, snode, target_node, *link);
756 position_viewer_node(C, btree, target_node, bnode_to_view);
757 return OPERATOR_FINISHED;
758 }
759 }
760
761 if (viewer_node == nullptr) {
762 for (bNode *node : btree.all_nodes()) {
763 if (is_viewer_node(*node) && node->custom1 == NODE_VIEWER_SHORTCUT_NONE) {
764 viewer_node = node;
765 break;
766 }
767 }
768 }
769 if (viewer_node == nullptr) {
770 const float2 socket_location = bsocket_to_view.runtime->location;
771 const int viewer_type = get_default_viewer_type(&C);
772 const float2 location{socket_location.x / UI_SCALE_FAC + 100,
773 socket_location.y / UI_SCALE_FAC};
774 viewer_node = add_static_node(C, viewer_type, location);
775 }
776
777 bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_node, bsocket_to_view);
778 if (viewer_bsocket == nullptr) {
779 return OPERATOR_CANCELLED;
780 }
781 viewer_bsocket->flag &= ~SOCK_HIDDEN;
782
783 bNodeLink *viewer_link = nullptr;
784 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) {
785 if (link->tosock == viewer_bsocket) {
786 viewer_link = link;
787 break;
788 }
789 }
790 if (viewer_link == nullptr) {
791 viewer_link = &bke::node_add_link(
792 btree, bnode_to_view, bsocket_to_view, *viewer_node, *viewer_bsocket);
793 }
794 else {
795 viewer_link->fromnode = &bnode_to_view;
796 viewer_link->fromsock = &bsocket_to_view;
798 }
799 finalize_viewer_link(C, snode, *viewer_node, *viewer_link);
800 position_viewer_node(C, btree, *viewer_node, bnode_to_view);
801 return OPERATOR_CANCELLED;
802}
803
804static int node_link_viewer(const bContext &C, bNode &bnode_to_view, bNodeSocket *bsocket_to_view)
805{
806 SpaceNode &snode = *CTX_wm_space_node(&C);
807 bNodeTree *btree = snode.edittree;
808 btree->ensure_topology_cache();
809
810 if (bsocket_to_view == nullptr) {
811 bsocket_to_view = determine_socket_to_view(bnode_to_view);
812 }
813
814 if (bsocket_to_view == nullptr) {
815 return OPERATOR_CANCELLED;
816 }
817
818 return view_socket(C, snode, *btree, bnode_to_view, *bsocket_to_view);
819}
820
822
823} // namespace viewer_linking
824
825/* -------------------------------------------------------------------- */
828
830{
831 SpaceNode &snode = *CTX_wm_space_node(C);
832 bNode *node = bke::node_get_active(*snode.edittree);
833
834 if (!node) {
835 return OPERATOR_CANCELLED;
836 }
837
839
840 bNodeSocket *socket_to_view = nullptr;
841 LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
842 if (socket->flag & SELECT) {
843 socket_to_view = socket;
844 break;
845 }
846 }
847
848 if (viewer_linking::node_link_viewer(*C, *node, socket_to_view) == OPERATOR_CANCELLED) {
849 return OPERATOR_CANCELLED;
850 }
851
853
854 return OPERATOR_FINISHED;
855}
856
858{
860 return false;
861 }
862 SpaceNode *snode = CTX_wm_space_node(C);
863 if (ED_node_is_compositor(snode)) {
864 return true;
865 }
866 if (ED_node_is_geometry(snode)) {
868 /* The viewer node is not supported in the "Tool" context. */
869 return false;
870 }
871 return true;
872 }
873 return false;
874}
875
877{
878 /* identifiers */
879 ot->name = "Link to Viewer Node";
880 ot->description = "Link to viewer node";
881 ot->idname = "NODE_OT_link_viewer";
882
883 /* API callbacks. */
886
887 /* flags */
889}
890
892
893/* -------------------------------------------------------------------- */
896
902{
903 if (nldrag.in_out == SOCK_OUT) {
904 for (const bNodeLink &link : nldrag.links) {
905 if (link.tonode && link.tosock) {
906 return false;
907 }
908 }
909 }
910 else {
911 for (const bNodeLink &link : nldrag.links) {
912 if (link.fromnode && link.fromsock) {
913 return false;
914 }
915 }
916 }
917 return true;
918}
919
921 const bNodeLinkDrag &nldrag)
922{
923 /* Custom node trees aren't supported yet. */
924 if (node_tree.type == NTREE_CUSTOM) {
925 return false;
926 }
927 /* Only create the search menu when the drag has not already connected the links to a socket. */
928 if (!dragged_links_are_detached(nldrag)) {
929 return false;
930 }
931 if (nldrag.swap_links) {
932 return false;
933 }
934 /* Don't create the search menu if the drag is disconnecting a link from an input node. */
935 if (nldrag.start_socket->in_out == SOCK_IN && nldrag.start_link_count > 0) {
936 return false;
937 }
938 /* Don't allow a drag from the "new socket" (group input node or simulation nodes currently).
939 * Handling these properly in node callbacks increases the complexity too much for now. */
940 if (nldrag.start_socket->type == SOCK_CUSTOM) {
941 return false;
942 }
943 if (nldrag.start_socket->type == SOCK_TEXTURE) {
944 /* This socket types is not used anymore, but can currently still exists in files. */
945 return false;
946 }
947 return true;
948}
949
950static bool need_drag_link_tooltip(const bNodeTree &node_tree, const bNodeLinkDrag &nldrag)
951{
952 return nldrag.swap_links || should_create_drag_link_search_menu(node_tree, nldrag);
953}
954
955static void draw_draglink_tooltip(const bContext * /*C*/, ARegion * /*region*/, void *arg)
956{
957 bNodeLinkDrag *nldrag = static_cast<bNodeLinkDrag *>(arg);
958
959 uchar text_col[4];
960 UI_GetThemeColor4ubv(TH_TEXT, text_col);
961
962 const int padding = 4 * UI_SCALE_FAC;
963 const float x = nldrag->in_out == SOCK_IN ? nldrag->cursor[0] - 3.3f * padding :
964 nldrag->cursor[0];
965 const float y = nldrag->cursor[1] - 2.0f * UI_SCALE_FAC;
966
967 const bool new_link = nldrag->in_out == nldrag->start_socket->in_out;
968 const bool swap_links = nldrag->swap_links;
969
970 const int icon = !swap_links ? ICON_ADD : (new_link ? ICON_ANIM : ICON_UV_SYNC_SELECT);
971
973 x, y, icon, UI_INV_SCALE_FAC, 1.0f, 0.0f, text_col, false, UI_NO_ICON_OVERLAY_TEXT);
974}
975
976static void draw_draglink_tooltip_activate(const ARegion &region, bNodeLinkDrag &nldrag)
977{
978 if (nldrag.draw_handle == nullptr) {
981 }
982}
983
984static void draw_draglink_tooltip_deactivate(const ARegion &region, bNodeLinkDrag &nldrag)
985{
986 if (nldrag.draw_handle) {
987 ED_region_draw_cb_exit(region.runtime->type, nldrag.draw_handle);
988 nldrag.draw_handle = nullptr;
989 }
990}
991
992static int node_socket_count_links(const bNodeTree &ntree, const bNodeSocket &socket)
993{
994 int count = 0;
995 LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
996 if (ELEM(&socket, link->fromsock, link->tosock)) {
997 count++;
998 }
999 }
1000 return count;
1001}
1002
1004 const bNode *node,
1005 bNodeSocket *socket_to_match)
1006{
1007 bNodeSocket *first_socket = socket_to_match->in_out == SOCK_IN ?
1008 static_cast<bNodeSocket *>(node->inputs.first) :
1009 static_cast<bNodeSocket *>(node->outputs.first);
1010
1011 bNodeSocket *socket = socket_to_match->next ? socket_to_match->next : first_socket;
1012 while (socket != socket_to_match) {
1013 if (socket->is_visible()) {
1014 const bool sockets_are_compatible = socket->typeinfo == socket_to_match->typeinfo;
1015 if (sockets_are_compatible) {
1016 const int link_count = node_socket_count_links(ntree, *socket);
1017 const bool socket_has_capacity = link_count < bke::node_socket_link_limit(*socket);
1018 if (socket_has_capacity) {
1019 /* Found a valid free socket we can swap to. */
1020 return socket;
1021 }
1022 }
1023 }
1024 /* Wrap around the list end. */
1025 socket = socket->next ? socket->next : first_socket;
1026 }
1027
1028 return nullptr;
1029}
1030
1031static void displace_links(bNodeTree *ntree, const bNode *node, bNodeLink *inserted_link)
1032{
1033 bNodeSocket *linked_socket = node == inserted_link->tonode ? inserted_link->tosock :
1034 inserted_link->fromsock;
1035 bNodeSocket *replacement_socket = node_find_linkable_socket(*ntree, node, linked_socket);
1036
1037 if (linked_socket->is_input()) {
1038 BLI_assert(!linked_socket->is_multi_input());
1039 ntree->ensure_topology_cache();
1040
1041 if (linked_socket->directly_linked_links().is_empty()) {
1042 return;
1043 }
1044 bNodeLink *displaced_link = linked_socket->directly_linked_links().first();
1045
1046 if (!replacement_socket) {
1047 bke::node_remove_link(ntree, *displaced_link);
1048 return;
1049 }
1050
1051 displaced_link->tosock = replacement_socket;
1052
1053 if (replacement_socket->is_multi_input()) {
1054 /* Check for duplicate links when linking to multi input sockets. */
1055 for (bNodeLink *existing_link : replacement_socket->runtime->directly_linked_links) {
1056 if (existing_link->fromsock == displaced_link->fromsock) {
1057 bke::node_remove_link(ntree, *displaced_link);
1058 return;
1059 }
1060 }
1061 const int multi_input_sort_id = node_socket_count_links(*ntree, *replacement_socket) - 1;
1062 displaced_link->multi_input_sort_id = multi_input_sort_id;
1063 }
1064
1066 return;
1067 }
1068
1069 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
1070 if (link->fromsock == linked_socket) {
1071 if (replacement_socket) {
1072 link->fromsock = replacement_socket;
1074 }
1075 else {
1076 bke::node_remove_link(ntree, *link);
1078 }
1079 }
1080 }
1081}
1082
1084{
1085 bNodeLink &link = nldrag.links.first();
1086 if (!link.fromsock || !link.tosock) {
1087 return;
1088 }
1089 if (nldrag.start_socket->is_input()) {
1090 displace_links(&ntree, link.fromnode, &link);
1091 }
1092 else {
1093 displace_links(&ntree, link.tonode, &link);
1094 }
1095}
1096
1097static void node_swap_links(bNodeLinkDrag &nldrag, bNodeTree &ntree)
1098{
1099 bNodeSocket &linked_socket = *nldrag.hovered_socket;
1100 bNodeSocket *start_socket = nldrag.start_socket;
1101 bNode *start_node = nldrag.start_node;
1102
1103 if (linked_socket.is_input()) {
1104 LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
1105 if (link->tosock != &linked_socket) {
1106 continue;
1107 }
1108 if (link->fromnode == start_node) {
1109 /* Don't link a node to itself. */
1110 bke::node_remove_link(&ntree, *link);
1111 continue;
1112 }
1113
1114 link->tosock = start_socket;
1115 link->tonode = start_node;
1116 }
1117 }
1118 else {
1119 LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
1120 if (link->fromsock != &linked_socket) {
1121 continue;
1122 }
1123 if (link->tonode == start_node) {
1124 /* Don't link a node to itself. */
1125 bke::node_remove_link(&ntree, *link);
1126 continue;
1127 }
1128 link->fromsock = start_socket;
1129 link->fromnode = start_node;
1130 }
1131 }
1132
1134}
1135
1137{
1138 bNodeSocket &linked_socket = *nldrag.hovered_socket;
1139
1140 int link_count = node_socket_count_links(ntree, linked_socket);
1141 const int link_limit = bke::node_socket_link_limit(linked_socket);
1142 Set<bNodeLink *> links_to_remove;
1143
1144 ntree.ensure_topology_cache();
1145
1146 /* Remove duplicate links first. */
1147 for (const bNodeLink dragged_link : nldrag.links) {
1148 if (linked_socket.is_input()) {
1149 for (bNodeLink *link : linked_socket.runtime->directly_linked_links) {
1150 const bool duplicate_link = link->fromsock == dragged_link.fromsock;
1151 if (duplicate_link) {
1152 links_to_remove.add(link);
1153 link_count--;
1154 }
1155 }
1156 }
1157 else {
1158 for (bNodeLink *link : linked_socket.runtime->directly_linked_links) {
1159 const bool duplicate_link = link->tosock == dragged_link.tosock;
1160 if (duplicate_link) {
1161 links_to_remove.add(link);
1162 link_count--;
1163 }
1164 }
1165 }
1166 }
1167
1168 for (bNodeLink *link : linked_socket.runtime->directly_linked_links) {
1169 const bool link_limit_exceeded = !(link_count < link_limit);
1170 if (link_limit_exceeded) {
1171 if (links_to_remove.add(link)) {
1172 link_count--;
1173 }
1174 }
1175 }
1176
1177 for (bNodeLink *link : links_to_remove) {
1178 bke::node_remove_link(&ntree, *link);
1179 }
1180}
1181
1183{
1184 Main *bmain = CTX_data_main(&C);
1185 ARegion &region = *CTX_wm_region(&C);
1186 SpaceNode &snode = *CTX_wm_space_node(&C);
1187 bNodeTree &ntree = *snode.edittree;
1188
1189 /* Handle node links already occupying the socket. */
1190 if (const bNodeSocket *linked_socket = nldrag.hovered_socket) {
1191 /* Swapping existing links out of multi input sockets is not supported. */
1192 const bool connecting_to_multi_input = linked_socket->is_multi_input() ||
1193 nldrag.start_socket->is_multi_input();
1194 if (nldrag.swap_links && !connecting_to_multi_input) {
1195 const bool is_new_link = nldrag.in_out == nldrag.start_socket->in_out;
1196 if (is_new_link) {
1197 node_displace_existing_links(nldrag, ntree);
1198 }
1199 else {
1200 node_swap_links(nldrag, ntree);
1201 }
1202 }
1203 else {
1205 }
1206 }
1207
1208 for (const bNodeLink &link : nldrag.links) {
1209 if (!link.tosock || !link.fromsock) {
1210 continue;
1211 }
1212
1213 /* Before actually adding the link let nodes perform special link insertion handling. */
1214
1215 bNodeLink *new_link = MEM_mallocN<bNodeLink>(__func__);
1216 *new_link = link;
1217 if (link.fromnode->typeinfo->insert_link) {
1218 if (!link.fromnode->typeinfo->insert_link(&ntree, link.fromnode, new_link)) {
1219 MEM_freeN(new_link);
1220 continue;
1221 }
1222 }
1223 if (link.tonode->typeinfo->insert_link) {
1224 if (!link.tonode->typeinfo->insert_link(&ntree, link.tonode, new_link)) {
1225 MEM_freeN(new_link);
1226 continue;
1227 }
1228 }
1229
1230 BLI_addtail(&ntree.links, new_link);
1231 BKE_ntree_update_tag_link_added(&ntree, new_link);
1232 }
1233
1234 BKE_main_ensure_invariants(*bmain, ntree.id);
1235
1236 /* Ensure drag-link tool-tip is disabled. */
1237 draw_draglink_tooltip_deactivate(region, nldrag);
1238
1239 ED_workspace_status_text(&C, nullptr);
1240 ED_region_tag_redraw(&region);
1242
1243 snode.runtime->linkdrag.reset();
1244}
1245
1257
1258static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cursor)
1259{
1260 SpaceNode &snode = *CTX_wm_space_node(&C);
1261 ARegion &region = *CTX_wm_region(&C);
1262 bNodeLinkDrag &nldrag = *static_cast<bNodeLinkDrag *>(op.customdata);
1263
1264 if (nldrag.in_out == SOCK_OUT) {
1265 if (bNodeSocket *tsock = node_find_indicated_socket(snode, region, cursor, SOCK_IN)) {
1266 nldrag.hovered_socket = tsock;
1267 bNode &tnode = tsock->owner_node();
1268 for (bNodeLink &link : nldrag.links) {
1269 /* Skip if socket is on the same node as the fromsock. */
1270 if (link.fromnode == &tnode) {
1271 continue;
1272 }
1273
1274 /* Skip if tsock is already linked with this output. */
1275 bNodeLink *existing_link_connected_to_fromsock = nullptr;
1276 LISTBASE_FOREACH (bNodeLink *, existing_link, &snode.edittree->links) {
1277 if (existing_link->fromsock == link.fromsock && existing_link->tosock == tsock) {
1278 existing_link_connected_to_fromsock = existing_link;
1279 break;
1280 }
1281 }
1282
1283 /* Attach links to the socket. */
1284 link.tonode = &tnode;
1285 link.tosock = tsock;
1287 if (existing_link_connected_to_fromsock) {
1288 link.multi_input_sort_id = existing_link_connected_to_fromsock->multi_input_sort_id;
1289 continue;
1290 }
1291 if (tsock && tsock->is_multi_input()) {
1292 sort_multi_input_socket_links_with_drag(*tsock, link, cursor);
1293 }
1294 }
1295 }
1296 else {
1297 nldrag.hovered_socket = nullptr;
1298 for (bNodeLink &link : nldrag.links) {
1299 link.tonode = nullptr;
1300 link.tosock = nullptr;
1301 }
1305 }
1306 }
1307 }
1308 else {
1309 if (bNodeSocket *tsock = node_find_indicated_socket(snode, region, cursor, SOCK_OUT)) {
1310 nldrag.hovered_socket = tsock;
1311 bNode &node = tsock->owner_node();
1312 for (bNodeLink &link : nldrag.links) {
1313 /* Skip if this is already the target socket. */
1314 if (link.fromsock == tsock) {
1315 continue;
1316 }
1317 /* Skip if socket is on the same node as the `fromsock`. */
1318 if (link.tonode == &node) {
1319 continue;
1320 }
1321
1322 /* Attach links to the socket. */
1323 link.fromnode = &node;
1324 link.fromsock = tsock;
1325 }
1326 }
1327 else {
1328 nldrag.hovered_socket = nullptr;
1329 for (bNodeLink &link : nldrag.links) {
1330 link.fromnode = nullptr;
1331 link.fromsock = nullptr;
1332 }
1333 }
1334 }
1335}
1336
1337enum class NodeLinkAction : int {
1340 Swap = 2,
1342};
1343
1345{
1346 static const EnumPropertyItem modal_items[] = {
1347 {int(NodeLinkAction::Begin), "BEGIN", 0, "Drag Node-link", ""},
1348 {int(NodeLinkAction::Confirm), "CONFIRM", 0, "Confirm Link", ""},
1349 {int(NodeLinkAction::Cancel), "CANCEL", 0, "Cancel", ""},
1350 {int(NodeLinkAction::Swap), "SWAP", 0, "Swap Links", ""},
1351 {0, nullptr, 0, nullptr, nullptr},
1352 };
1353
1354 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Node Link Modal Map");
1355
1356 /* This function is called for each space-type, only needs to add map once. */
1357 if (keymap && keymap->modal_items) {
1358 return nullptr;
1359 }
1360
1361 keymap = WM_modalkeymap_ensure(keyconf, "Node Link Modal Map", modal_items);
1362
1363 WM_modalkeymap_assign(keymap, "NODE_OT_link");
1364
1365 return keymap;
1366}
1367
1369{
1370 bNodeLinkDrag &nldrag = *static_cast<bNodeLinkDrag *>(op->customdata);
1371 SpaceNode &snode = *CTX_wm_space_node(C);
1372 ARegion *region = CTX_wm_region(C);
1373
1374 UI_view2d_edge_pan_apply_event(C, &nldrag.pan_data, event);
1375
1376 float2 cursor;
1377 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &cursor.x, &cursor.y);
1378 nldrag.cursor[0] = event->mval[0];
1379 nldrag.cursor[1] = event->mval[1];
1380
1381 if (event->type == EVT_MODAL_MAP) {
1382 switch (event->val) {
1383 case int(NodeLinkAction::Begin): {
1385 }
1386 case int(NodeLinkAction::Confirm): {
1387 /* Add a search menu for compatible sockets if the drag released on empty space. */
1388 if (should_create_drag_link_search_menu(*snode.edittree, nldrag)) {
1389 bNodeLink &link = nldrag.links.first();
1390 if (nldrag.in_out == SOCK_OUT) {
1391 invoke_node_link_drag_add_menu(*C, *link.fromnode, *link.fromsock, cursor);
1392 }
1393 else {
1394 invoke_node_link_drag_add_menu(*C, *link.tonode, *link.tosock, cursor);
1395 }
1396 }
1397 add_dragged_links_to_tree(*C, nldrag);
1398 return OPERATOR_FINISHED;
1399 }
1400 case int(NodeLinkAction::Cancel): {
1401 node_link_cancel(C, op);
1402 return OPERATOR_CANCELLED;
1403 }
1404 case int(NodeLinkAction::Swap):
1405 if (event->prev_val == KM_PRESS) {
1406 nldrag.swap_links = true;
1407 }
1408 else if (event->prev_val == KM_RELEASE) {
1409 nldrag.swap_links = false;
1410 }
1412 }
1413 }
1414 else if (event->type == MOUSEMOVE) {
1415 if (nldrag.start_socket->is_multi_input() && nldrag.links.is_empty()) {
1416 pick_input_link_by_link_intersect(*C, *op, nldrag, cursor);
1417 }
1418 else {
1419 node_link_find_socket(*C, *op, cursor);
1420 ED_region_tag_redraw(region);
1421 }
1422
1423 if (need_drag_link_tooltip(*snode.edittree, nldrag)) {
1424 draw_draglink_tooltip_activate(*region, nldrag);
1425 }
1426 else {
1427 draw_draglink_tooltip_deactivate(*region, nldrag);
1428 }
1429 }
1430
1432}
1433
1435{
1436 tree.ensure_topology_cache();
1437 Vector<bNodeLink *> links = socket.directly_linked_links();
1438 for (bNodeLink *link : links) {
1439 if (!link->is_available()) {
1440 bke::node_remove_link(&tree, *link);
1441 }
1442 }
1443}
1444
1445static std::unique_ptr<bNodeLinkDrag> node_link_init(ARegion &region,
1446 SpaceNode &snode,
1447 const float2 cursor,
1448 const bool detach)
1449{
1450 if (bNodeSocket *sock = node_find_indicated_socket(snode, region, cursor, SOCK_OUT)) {
1451 remove_unavailable_links(*snode.edittree, *sock);
1452 snode.edittree->ensure_topology_cache();
1453 bNode &node = sock->owner_node();
1454
1455 std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
1456 nldrag->start_node = &node;
1457 nldrag->start_socket = sock;
1458 nldrag->start_link_count = bke::node_count_socket_links(*snode.edittree, *sock);
1459 int link_limit = bke::node_socket_link_limit(*sock);
1460 if (nldrag->start_link_count > 0 && (nldrag->start_link_count >= link_limit || detach)) {
1461 /* Dragged links are fixed on input side. */
1462 nldrag->in_out = SOCK_IN;
1463 /* Detach current links and store them in the operator data. */
1465 if (link->fromsock == sock) {
1466 bNodeLink oplink = *link;
1467 oplink.next = oplink.prev = nullptr;
1468 oplink.flag |= NODE_LINK_VALID;
1469
1470 nldrag->links.append(oplink);
1471 bke::node_remove_link(snode.edittree, *link);
1472 }
1473 }
1474 }
1475 else {
1476 /* Dragged links are fixed on output side. */
1477 nldrag->in_out = SOCK_OUT;
1478 nldrag->links.append(create_drag_link(node, *sock));
1479 }
1480 return nldrag;
1481 }
1482
1483 if (bNodeSocket *sock = node_find_indicated_socket(snode, region, cursor, SOCK_IN)) {
1484 remove_unavailable_links(*snode.edittree, *sock);
1485 snode.edittree->ensure_topology_cache();
1486 bNode &node = sock->owner_node();
1487 std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
1488 nldrag->last_node_hovered_while_dragging_a_link = &node;
1489 nldrag->start_node = &node;
1490 nldrag->start_socket = sock;
1491
1492 nldrag->start_link_count = bke::node_count_socket_links(*snode.edittree, *sock);
1493 if (nldrag->start_link_count > 0) {
1494 /* Dragged links are fixed on output side. */
1495 nldrag->in_out = SOCK_OUT;
1496 /* Detach current links and store them in the operator data. */
1497 bNodeLink *link_to_pick;
1499 if (link->tosock == sock) {
1500 link_to_pick = link;
1501 }
1502 }
1503
1504 if (link_to_pick != nullptr && !nldrag->start_socket->is_multi_input()) {
1505 bNodeLink oplink = *link_to_pick;
1506 oplink.next = oplink.prev = nullptr;
1507 oplink.flag |= NODE_LINK_VALID;
1508
1509 nldrag->links.append(oplink);
1510 bke::node_remove_link(snode.edittree, *link_to_pick);
1511
1512 /* Send changed event to original link->tonode. */
1514 }
1515 }
1516 else {
1517 /* Dragged links are fixed on input side. */
1518 nldrag->in_out = SOCK_IN;
1519 nldrag->links.append(create_drag_link(node, *sock));
1520 }
1521 return nldrag;
1522 }
1523
1524 return {};
1525}
1526
1528{
1529 Main &bmain = *CTX_data_main(C);
1530 SpaceNode &snode = *CTX_wm_space_node(C);
1531 ARegion &region = *CTX_wm_region(C);
1532
1533 bool detach = RNA_boolean_get(op->ptr, "detach");
1534
1535 int2 mval;
1536 WM_event_drag_start_mval(event, &region, mval);
1537
1538 float2 cursor;
1539 UI_view2d_region_to_view(&region.v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
1540 RNA_float_set_array(op->ptr, "drag_start", cursor);
1541
1543
1544 std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(region, snode, cursor, detach);
1545 if (!nldrag) {
1547 }
1548
1549 UI_view2d_edge_pan_operator_init(C, &nldrag->pan_data, op);
1550
1551 /* Add icons at the cursor when the link is dragged in empty space. */
1552 if (need_drag_link_tooltip(*snode.edittree, *nldrag)) {
1554 }
1555 snode.runtime->linkdrag = std::move(nldrag);
1556 op->customdata = snode.runtime->linkdrag.get();
1557
1559
1561}
1562
1564{
1565 /* identifiers */
1566 ot->name = "Link Nodes";
1567 ot->idname = "NODE_OT_link";
1568 ot->description = "Use the mouse to create a link between two nodes";
1569
1570 /* API callbacks. */
1571 ot->invoke = node_link_invoke;
1572 ot->modal = node_link_modal;
1574 ot->cancel = node_link_cancel;
1575
1576 /* flags */
1578
1579 RNA_def_boolean(ot->srna, "detach", false, "Detach", "Detach and redirect existing links");
1580 RNA_def_float_array(ot->srna,
1581 "drag_start",
1582 2,
1583 nullptr,
1586 "Drag Start",
1587 "The position of the mouse cursor at the start of the operation",
1590
1598}
1599
1601
1602/* -------------------------------------------------------------------- */
1605
1606/* Makes a link between selected output and input sockets. */
1608{
1609 Main &bmain = *CTX_data_main(C);
1610 SpaceNode &snode = *CTX_wm_space_node(C);
1611 bNodeTree &node_tree = *snode.edittree;
1612 const bool replace = RNA_boolean_get(op->ptr, "replace");
1613
1615
1616 snode_autoconnect(snode, true, replace);
1617
1618 /* Deselect sockets after linking. */
1619 node_deselect_all_input_sockets(node_tree, false);
1620 node_deselect_all_output_sockets(node_tree, false);
1621
1622 BKE_main_ensure_invariants(bmain, node_tree.id);
1623
1624 return OPERATOR_FINISHED;
1625}
1626
1628{
1629 /* identifiers */
1630 ot->name = "Make Links";
1631 ot->description = "Make a link between selected output and input sockets";
1632 ot->idname = "NODE_OT_link_make";
1633
1634 /* callbacks */
1635 ot->exec = node_make_link_exec;
1636 /* XXX we need a special poll which checks that there are selected input/output sockets. */
1638
1639 /* flags */
1640 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1641
1643 ot->srna, "replace", false, "Replace", "Replace socket connections with the new links");
1644}
1645
1647
1648/* -------------------------------------------------------------------- */
1651
1653{
1654 Main &bmain = *CTX_data_main(C);
1655 SpaceNode &snode = *CTX_wm_space_node(C);
1656 const ARegion &region = *CTX_wm_region(C);
1657
1658 Vector<float2> path;
1659 RNA_BEGIN (op->ptr, itemptr, "path") {
1660 float2 loc_region;
1661 RNA_float_get_array(&itemptr, "loc", loc_region);
1662 float2 loc_view;
1663 UI_view2d_region_to_view(&region.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y);
1664 path.append(loc_view);
1665 if (path.size() >= 256) {
1666 break;
1667 }
1668 }
1669 RNA_END;
1670
1671 if (path.is_empty()) {
1673 }
1674
1675 bool found = false;
1676
1678
1679 bNodeTree &node_tree = *snode.edittree;
1680 node_tree.ensure_topology_cache();
1681
1682 Set<bNodeLink *> links_to_remove;
1683 LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
1684 if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
1685 continue;
1686 }
1687
1688 if (link_path_intersection(*link, path)) {
1689
1690 if (!found) {
1691 /* TODO(sergey): Why did we kill jobs twice? */
1693 found = true;
1694 }
1695 links_to_remove.add(link);
1696 }
1697 }
1698
1699 Set<bNode *> affected_nodes;
1700 for (bNodeLink *link : links_to_remove) {
1701 bNode *to_node = link->tonode;
1702 bke::node_remove_link(snode.edittree, *link);
1703 affected_nodes.add(to_node);
1704 }
1705
1706 node_tree.ensure_topology_cache();
1707 for (bNode *node : affected_nodes) {
1709 }
1710
1712 if (found) {
1713 return OPERATOR_FINISHED;
1714 }
1715
1716 return OPERATOR_CANCELLED;
1717}
1718
1720{
1721 ot->name = "Cut Links";
1722 ot->idname = "NODE_OT_links_cut";
1723 ot->description = "Use the mouse to cut (remove) some links";
1724
1725 ot->invoke = WM_gesture_lines_invoke;
1726 ot->modal = WM_gesture_lines_modal;
1727 ot->exec = cut_links_exec;
1728 ot->cancel = WM_gesture_lines_cancel;
1729
1731
1732 /* flags */
1734
1735 /* properties */
1736 PropertyRNA *prop;
1737 prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
1739
1740 /* internal */
1741 RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1742}
1743
1745
1746/* -------------------------------------------------------------------- */
1749
1750bool all_links_muted(const bNodeSocket &socket)
1751{
1752 for (const bNodeLink *link : socket.directly_linked_links()) {
1753 if (!(link->flag & NODE_LINK_MUTED)) {
1754 return false;
1755 }
1756 }
1757 return true;
1758}
1759
1761{
1762 Main &bmain = *CTX_data_main(C);
1763 SpaceNode &snode = *CTX_wm_space_node(C);
1764 const ARegion &region = *CTX_wm_region(C);
1765 bNodeTree &ntree = *snode.edittree;
1766
1767 Vector<float2> path;
1768 RNA_BEGIN (op->ptr, itemptr, "path") {
1769 float2 loc_region;
1770 RNA_float_get_array(&itemptr, "loc", loc_region);
1771 float2 loc_view;
1772 UI_view2d_region_to_view(&region.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y);
1773 path.append(loc_view);
1774 if (path.size() >= 256) {
1775 break;
1776 }
1777 }
1778 RNA_END;
1779
1780 if (path.is_empty()) {
1782 }
1783
1785
1786 ntree.ensure_topology_cache();
1787
1788 Set<bNodeLink *> affected_links;
1789 LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
1790 if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
1791 continue;
1792 }
1793 if (!link_path_intersection(*link, path)) {
1794 continue;
1795 }
1796 affected_links.add(link);
1797 }
1798
1799 if (affected_links.is_empty()) {
1800 return OPERATOR_CANCELLED;
1801 }
1802
1803 bke::node_tree_runtime::AllowUsingOutdatedInfo allow_outdated_info{ntree};
1804
1805 for (bNodeLink *link : affected_links) {
1806 bke::node_link_set_mute(ntree, *link, !(link->flag & NODE_LINK_MUTED));
1807 const bool muted = link->flag & NODE_LINK_MUTED;
1808
1809 /* Propagate mute status downstream past reroute nodes. */
1810 if (link->tonode->is_reroute()) {
1811 Stack<bNodeLink *> links;
1812 links.push_multiple(link->tonode->output_socket(0).directly_linked_links());
1813 while (!links.is_empty()) {
1814 bNodeLink *link = links.pop();
1815 bke::node_link_set_mute(ntree, *link, muted);
1816 if (!link->tonode->is_reroute()) {
1817 continue;
1818 }
1819 links.push_multiple(link->tonode->output_socket(0).directly_linked_links());
1820 }
1821 }
1822 /* Propagate mute status upstream past reroutes, but only if all outputs are muted. */
1823 if (link->fromnode->is_reroute()) {
1824 if (!muted || all_links_muted(*link->fromsock)) {
1825 Stack<bNodeLink *> links;
1826 links.push_multiple(link->fromnode->input_socket(0).directly_linked_links());
1827 while (!links.is_empty()) {
1828 bNodeLink *link = links.pop();
1829 bke::node_link_set_mute(ntree, *link, muted);
1830 if (!link->fromnode->is_reroute()) {
1831 continue;
1832 }
1833 if (!muted || all_links_muted(*link->fromsock)) {
1834 links.push_multiple(link->fromnode->input_socket(0).directly_linked_links());
1835 }
1836 }
1837 }
1838 }
1839 }
1840
1842 return OPERATOR_FINISHED;
1843}
1844
1846{
1847 ot->name = "Mute Links";
1848 ot->idname = "NODE_OT_links_mute";
1849 ot->description = "Use the mouse to mute links";
1850
1851 ot->invoke = WM_gesture_lines_invoke;
1852 ot->modal = WM_gesture_lines_modal;
1853 ot->exec = mute_links_exec;
1854 ot->cancel = WM_gesture_lines_cancel;
1855
1857
1858 /* flags */
1860
1861 /* properties */
1862 PropertyRNA *prop;
1863 prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
1865
1866 /* internal */
1867 RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
1868}
1869
1871
1872/* -------------------------------------------------------------------- */
1875
1877{
1878 SpaceNode &snode = *CTX_wm_space_node(C);
1879 bNodeTree &ntree = *snode.edittree;
1880
1882
1883 for (bNode *node : ntree.all_nodes()) {
1884 if (node->flag & SELECT) {
1885 bke::node_internal_relink(ntree, *node);
1886 }
1887 }
1888
1890 return OPERATOR_FINISHED;
1891}
1892
1894{
1895 ot->name = "Detach Links";
1896 ot->idname = "NODE_OT_links_detach";
1897 ot->description =
1898 "Remove all links to selected nodes, and try to connect neighbor nodes together";
1899
1900 ot->exec = detach_links_exec;
1902
1903 /* flags */
1904 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1905}
1906
1908
1909/* -------------------------------------------------------------------- */
1912
1914{
1915 SpaceNode &snode = *CTX_wm_space_node(C);
1916 bNodeTree &ntree = *snode.edittree;
1917 bNode *frame = bke::node_get_active(ntree);
1918 if (!frame || !frame->is_frame()) {
1919 return OPERATOR_CANCELLED;
1920 }
1921
1922 for (bNode *node : ntree.all_nodes()) {
1923 if (node == frame) {
1924 continue;
1925 }
1926 if (node->flag & NODE_SELECT) {
1927 bke::node_detach_node(ntree, *node);
1928 bke::node_attach_node(ntree, *node, *frame);
1929 }
1930 }
1931
1934
1935 return OPERATOR_FINISHED;
1936}
1937
1939{
1940 /* identifiers */
1941 ot->name = "Make Parent";
1942 ot->description = "Attach selected nodes";
1943 ot->idname = "NODE_OT_parent_set";
1944
1945 /* API callbacks. */
1946 ot->exec = node_parent_set_exec;
1948
1949 /* flags */
1950 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1951}
1952
1954
1955/* -------------------------------------------------------------------- */
1958
1960 bool done;
1962};
1963
1965 MutableSpan<NodeJoinState> join_states,
1966 bNode *node,
1967 bNode *frame,
1968 const VectorSet<bNode *> &selected_nodes)
1969{
1970 join_states[node->index()].done = true;
1971
1972 if (node == frame) {
1973 join_states[node->index()].descendent = true;
1974 }
1975 else if (node->parent) {
1976 /* call recursively */
1977 if (!join_states[node->parent->index()].done) {
1978 node_join_attach_recursive(ntree, join_states, node->parent, frame, selected_nodes);
1979 }
1980
1981 /* in any case: if the parent is a descendant, so is the child */
1982 if (join_states[node->parent->index()].descendent) {
1983 join_states[node->index()].descendent = true;
1984 }
1985 else if (selected_nodes.contains(node)) {
1986 /* if parent is not an descendant of the frame, reattach the node */
1987 bke::node_detach_node(ntree, *node);
1988 bke::node_attach_node(ntree, *node, *frame);
1989 join_states[node->index()].descendent = true;
1990 }
1991 }
1992 else if (selected_nodes.contains(node)) {
1993 bke::node_attach_node(ntree, *node, *frame);
1994 join_states[node->index()].descendent = true;
1995 }
1996}
1997
1999{
2000 Vector<const bNode *> parents;
2001 for (const bNode *parent = node.parent; parent; parent = parent->parent) {
2002 parents.append(parent);
2003 }
2004 /* Reverse so that the root frame is the first element (if there is any). */
2005 std::reverse(parents.begin(), parents.end());
2006 return parents;
2007}
2008
2010{
2011 if (nodes.is_empty()) {
2012 return nullptr;
2013 }
2014 /* The common parent node also has to be a parent of the first node. */
2016 for (const bNode *node : nodes.drop_front(1)) {
2017 const Vector<const bNode *> parents = get_sorted_node_parents(*node);
2018 /* Possibly shrink set of candidates so that it only contains the parents common with the
2019 * current node. */
2020 candidates.resize(std::min(candidates.size(), parents.size()));
2021 for (const int i : candidates.index_range()) {
2022 if (candidates[i] != parents[i]) {
2023 candidates.resize(i);
2024 break;
2025 }
2026 }
2027 if (candidates.is_empty()) {
2028 break;
2029 }
2030 }
2031 if (candidates.is_empty()) {
2032 return nullptr;
2033 }
2034 return candidates.last();
2035}
2036
2038{
2039 Main &bmain = *CTX_data_main(C);
2040 SpaceNode &snode = *CTX_wm_space_node(C);
2041 bNodeTree &ntree = *snode.edittree;
2042
2043 const VectorSet<bNode *> selected_nodes = get_selected_nodes(ntree);
2044
2045 bNode *frame_node = add_static_node(*C, NODE_FRAME, snode.runtime->cursor);
2046 bke::node_set_active(ntree, *frame_node);
2047 frame_node->parent = const_cast<bNode *>(find_common_parent_node(selected_nodes.as_span()));
2048
2049 ntree.ensure_topology_cache();
2050
2051 Array<NodeJoinState> join_states(ntree.all_nodes().size(), NodeJoinState{false, false});
2052
2053 for (bNode *node : ntree.all_nodes()) {
2054 if (!join_states[node->index()].done) {
2055 node_join_attach_recursive(ntree, join_states, node, frame_node, selected_nodes);
2056 }
2057 }
2058
2060 BKE_main_ensure_invariants(bmain, snode.edittree->id);
2062
2063 return OPERATOR_FINISHED;
2064}
2065
2067{
2068 ARegion *region = CTX_wm_region(C);
2069 SpaceNode *snode = CTX_wm_space_node(C);
2070
2071 /* Convert mouse coordinates to v2d space. */
2073 event->mval[0],
2074 event->mval[1],
2075 &snode->runtime->cursor[0],
2076 &snode->runtime->cursor[1]);
2077
2078 snode->runtime->cursor[0] /= UI_SCALE_FAC;
2079 snode->runtime->cursor[1] /= UI_SCALE_FAC;
2080
2081 return node_join_exec(C, op);
2082}
2083
2085{
2086 /* identifiers */
2087 ot->name = "Join Nodes";
2088 ot->description = "Attach selected nodes to a new common frame";
2089 ot->idname = "NODE_OT_join";
2090
2091 /* API callbacks. */
2092 ot->exec = node_join_exec;
2093 ot->invoke = node_join_invoke;
2095
2096 /* flags */
2097 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2098}
2099
2101
2102/* -------------------------------------------------------------------- */
2105
2106static bNode *node_find_frame_to_attach(ARegion &region, bNodeTree &ntree, const int2 mouse_xy)
2107{
2108 /* convert mouse coordinates to v2d space */
2109 float2 cursor;
2110 UI_view2d_region_to_view(&region.v2d, mouse_xy.x, mouse_xy.y, &cursor.x, &cursor.y);
2111
2112 for (bNode *frame : tree_draw_order_calc_nodes_reversed(ntree)) {
2113 /* skip selected, those are the nodes we want to attach */
2114 if (!frame->is_frame() || (frame->flag & NODE_SELECT)) {
2115 continue;
2116 }
2117 if (BLI_rctf_isect_pt_v(&frame->runtime->draw_bounds, cursor)) {
2118 return frame;
2119 }
2120 }
2121
2122 return nullptr;
2123}
2124
2125static bool can_attach_node_to_frame(const bNode &node, const bNode &frame)
2126{
2127 /* Disallow moving a parent into its child. */
2128 if (node.is_frame() && bke::node_is_parent_and_child(node, frame)) {
2129 return false;
2130 }
2131 if (node.parent == nullptr) {
2132 return true;
2133 }
2134 if (node.parent == &frame) {
2135 return false;
2136 }
2137 /* Attach nodes which share parent with the frame. */
2138 const bool share_parent = bke::node_is_parent_and_child(*node.parent, frame);
2139 if (!share_parent) {
2140 return false;
2141 }
2142 return true;
2143}
2144
2146{
2147 ARegion &region = *CTX_wm_region(C);
2148 SpaceNode &snode = *CTX_wm_space_node(C);
2149 bNodeTree &ntree = *snode.edittree;
2150 bNode *frame = node_find_frame_to_attach(region, ntree, event->mval);
2151 if (frame == nullptr) {
2152 /* Return "finished" so that auto offset operator macros can work. */
2153 return OPERATOR_FINISHED;
2154 }
2155
2156 bool changed = false;
2157
2158 for (bNode *node : tree_draw_order_calc_nodes_reversed(*snode.edittree)) {
2159 if (!(node->flag & NODE_SELECT)) {
2160 continue;
2161 }
2162 if (!can_attach_node_to_frame(*node, *frame)) {
2163 continue;
2164 }
2165 bke::node_detach_node(ntree, *node);
2166 bke::node_attach_node(ntree, *node, *frame);
2167 changed = true;
2168 }
2169
2170 if (changed) {
2173 }
2174
2175 return OPERATOR_FINISHED;
2176}
2177
2179{
2180 ot->name = "Attach Nodes";
2181 ot->description = "Attach active node to a frame";
2182 ot->idname = "NODE_OT_attach";
2183
2184 ot->invoke = node_attach_invoke;
2186
2187 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2188}
2189
2191
2192/* -------------------------------------------------------------------- */
2195
2197 bool done;
2199};
2200
2202 MutableSpan<NodeDetachstate> detach_states,
2203 bNode *node)
2204{
2205 detach_states[node->index()].done = true;
2206
2207 if (node->parent) {
2208 /* Call recursively. */
2209 if (!detach_states[node->parent->index()].done) {
2210 node_detach_recursive(ntree, detach_states, node->parent);
2211 }
2212
2213 /* In any case: if the parent is a descendant, so is the child. */
2214 if (detach_states[node->parent->index()].descendent) {
2215 detach_states[node->index()].descendent = true;
2216 }
2217 else if (node->flag & NODE_SELECT) {
2218 /* If parent is not a descendant of a selected node, detach. */
2219 bke::node_detach_node(ntree, *node);
2220 detach_states[node->index()].descendent = true;
2221 }
2222 }
2223 else if (node->flag & NODE_SELECT) {
2224 detach_states[node->index()].descendent = true;
2225 }
2226}
2227
2228/* Detach the root nodes in the current selection. */
2230{
2231 SpaceNode &snode = *CTX_wm_space_node(C);
2232 bNodeTree &ntree = *snode.edittree;
2233
2234 Array<NodeDetachstate> detach_states(ntree.all_nodes().size(), NodeDetachstate{false, false});
2235
2236 /* Detach nodes recursively. Relative order is preserved here. */
2237 for (bNode *node : ntree.all_nodes()) {
2238 if (!detach_states[node->index()].done) {
2239 node_detach_recursive(ntree, detach_states, node);
2240 }
2241 }
2242
2245
2246 return OPERATOR_FINISHED;
2247}
2248
2250{
2251 /* identifiers */
2252 ot->name = "Detach Nodes";
2253 ot->description = "Detach selected nodes from parents";
2254 ot->idname = "NODE_OT_detach";
2255
2256 /* API callbacks. */
2257 ot->exec = node_detach_exec;
2259
2260 /* flags */
2261 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2262}
2263
2265
2266/* -------------------------------------------------------------------- */
2269
2271{
2272 bNode *selected_node = nullptr;
2273 int selected_node_count = 0;
2274 for (bNode *node : node_tree.all_nodes()) {
2275 if (node->flag & SELECT) {
2276 selected_node = node;
2277 selected_node_count++;
2278 }
2279 if (selected_node_count > 1) {
2280 return nullptr;
2281 }
2282 }
2283 if (!selected_node) {
2284 return nullptr;
2285 }
2286 if (selected_node->input_sockets().is_empty() || selected_node->output_sockets().is_empty()) {
2287 return nullptr;
2288 }
2289 return selected_node;
2290}
2291
2293{
2294 const bNodeSocket *main_input = get_main_socket(tree, node, SOCK_IN);
2295 const bNodeSocket *main_output = get_main_socket(tree, node, SOCK_IN);
2296 if (ELEM(nullptr, main_input, main_output)) {
2297 return false;
2298 }
2299 if (node.is_reroute()) {
2300 return true;
2301 }
2302 if (!tree.typeinfo->validate_link) {
2303 return true;
2304 }
2305 if (!tree.typeinfo->validate_link(eNodeSocketDatatype(link.fromsock->type),
2306 eNodeSocketDatatype(main_input->type)))
2307 {
2308 return false;
2309 }
2310 if (!tree.typeinfo->validate_link(eNodeSocketDatatype(main_output->type),
2312 {
2313 return false;
2314 }
2315 return true;
2316}
2317
2319 const ARegion &region,
2320 const bool attach_enabled,
2321 const bool is_new_node)
2322{
2323 bNodeTree &node_tree = *snode.edittree;
2324 node_tree.ensure_topology_cache();
2325
2327
2328 bNode *node_to_insert = get_selected_node_for_insertion(node_tree);
2329 if (!node_to_insert) {
2330 return;
2331 }
2332 Vector<bNodeSocket *> already_linked_sockets;
2333 for (bNodeSocket *socket : node_to_insert->input_sockets()) {
2334 already_linked_sockets.extend(socket->directly_linked_sockets());
2335 }
2336 for (bNodeSocket *socket : node_to_insert->output_sockets()) {
2337 already_linked_sockets.extend(socket->directly_linked_sockets());
2338 }
2339 if (!is_new_node && !already_linked_sockets.is_empty()) {
2340 return;
2341 }
2342
2343 /* Find link to select/highlight. */
2344 bNodeLink *selink = nullptr;
2345 float dist_best = FLT_MAX;
2346 LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
2347 if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
2348 continue;
2349 }
2350 if (ELEM(node_to_insert, link->fromnode, link->tonode)) {
2351 /* Don't insert on a link that is connected to the node already. */
2352 continue;
2353 }
2354 if (is_new_node && !already_linked_sockets.is_empty()) {
2355 /* Only allow links coming from or going to the already linked socket after
2356 * link-drag-search. */
2357 bool is_linked_to_linked = false;
2358 for (const bNodeSocket *socket : already_linked_sockets) {
2359 if (ELEM(socket, link->fromsock, link->tosock)) {
2360 is_linked_to_linked = true;
2361 break;
2362 }
2363 }
2364 if (!is_linked_to_linked) {
2365 continue;
2366 }
2367 }
2368
2369 std::array<float2, NODE_LINK_RESOL + 1> coords;
2370 node_link_bezier_points_evaluated(*link, coords);
2371 float dist = FLT_MAX;
2372
2373 /* Loop over link coords to find shortest dist to upper left node edge of a intersected line
2374 * segment. */
2375 for (int i = 0; i < NODE_LINK_RESOL; i++) {
2376 /* Check if the node rectangle intersects the line from this point to next one. */
2377 if (BLI_rctf_isect_segment(&node_to_insert->runtime->draw_bounds, coords[i], coords[i + 1]))
2378 {
2379 /* Store the shortest distance to the upper left edge of all intersections found so far. */
2380 const float node_xy[] = {node_to_insert->runtime->draw_bounds.xmin,
2381 node_to_insert->runtime->draw_bounds.ymax};
2382
2383 /* To be precise coords should be clipped by `select->draw_bounds`, but not done since
2384 * there's no real noticeable difference. */
2385 dist = min_ff(dist_squared_to_line_segment_v2(node_xy, coords[i], coords[i + 1]), dist);
2386 }
2387 }
2388
2389 /* We want the link with the shortest distance to node center. */
2390 if (dist < dist_best) {
2391 dist_best = dist;
2392 selink = link;
2393 }
2394 }
2395
2396 if (selink) {
2397 selink->flag |= NODE_LINK_INSERT_TARGET;
2398 if (!attach_enabled || !node_can_be_inserted_on_link(node_tree, *node_to_insert, *selink)) {
2400 }
2401 }
2402}
2403
2405{
2407
2408 ARegion &region = *CTX_wm_region(&C);
2409
2410 snode.edittree->ensure_topology_cache();
2411 const bNode *frame = node_find_frame_to_attach(region, *snode.edittree, cursor);
2412 if (!frame) {
2413 return;
2414 }
2415 for (const bNode *node : snode.edittree->all_nodes()) {
2416 if (!(node->flag & NODE_SELECT)) {
2417 continue;
2418 }
2419 if (!can_attach_node_to_frame(*node, *frame)) {
2420 continue;
2421 }
2422 /* We detected that a node can be attached to the frame, so highlight it. */
2424 return;
2425 }
2426}
2427
2432
2434{
2435 LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
2437 }
2438}
2439
2440void node_insert_on_link_flags(Main &bmain, SpaceNode &snode, bool is_new_node)
2441{
2442 bNodeTree &node_tree = *snode.edittree;
2443 node_tree.ensure_topology_cache();
2444 bNode *node_to_insert = get_selected_node_for_insertion(node_tree);
2445 if (!node_to_insert) {
2446 return;
2447 }
2448
2449 /* Find link to insert on. */
2450 bNodeTree &ntree = *snode.edittree;
2451 bNodeLink *old_link = nullptr;
2452 LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
2453 if (link->flag & NODE_LINK_INSERT_TARGET) {
2454 if (!(link->flag & NODE_LINK_INSERT_TARGET_INVALID)) {
2455 old_link = link;
2456 }
2457 break;
2458 }
2459 }
2461 if (old_link == nullptr) {
2462 return;
2463 }
2464
2465 bNodeSocket *best_input = nullptr;
2466 if (is_new_node) {
2467 for (bNodeSocket *socket : node_to_insert->input_sockets()) {
2468 if (!socket->directly_linked_sockets().is_empty()) {
2469 best_input = socket;
2470 break;
2471 }
2472 }
2473 }
2474 if (!best_input) {
2475 best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN);
2476 }
2477 bNodeSocket *best_output = nullptr;
2478 if (is_new_node) {
2479 for (bNodeSocket *socket : node_to_insert->output_sockets()) {
2480 if (!socket->directly_linked_sockets().is_empty()) {
2481 best_output = socket;
2482 break;
2483 }
2484 }
2485 }
2486 if (!best_output) {
2487 best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT);
2488 }
2489
2490 if (!node_to_insert->is_reroute()) {
2491 /* Ignore main sockets when the types don't match. */
2492 if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr &&
2493 !ntree.typeinfo->validate_link(eNodeSocketDatatype(old_link->fromsock->type),
2494 eNodeSocketDatatype(best_input->type)))
2495 {
2496 best_input = nullptr;
2497 }
2498 if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr &&
2499 !ntree.typeinfo->validate_link(eNodeSocketDatatype(best_output->type),
2500 eNodeSocketDatatype(old_link->tosock->type)))
2501 {
2502 best_output = nullptr;
2503 }
2504 }
2505
2506 bNode *from_node = old_link->fromnode;
2507 bNodeSocket *from_socket = old_link->fromsock;
2508 bNode *to_node = old_link->tonode;
2509
2510 const bool best_input_is_linked = best_input && best_input->is_directly_linked();
2511
2512 if (best_output != nullptr) {
2513 /* Relink the "start" of the existing link to the newly inserted node. */
2514 old_link->fromnode = node_to_insert;
2515 old_link->fromsock = best_output;
2517 }
2518 else {
2519 bke::node_remove_link(&ntree, *old_link);
2520 }
2521
2522 if (best_input != nullptr) {
2523 /* Don't change an existing link. */
2524 if (!best_input_is_linked) {
2525 /* Add a new link that connects the node on the left to the newly inserted node. */
2526 bke::node_add_link(ntree, *from_node, *from_socket, *node_to_insert, *best_input);
2527 }
2528 }
2529
2530 /* Set up insert offset data, it needs stuff from here. */
2531 if (U.uiflag & USER_NODE_AUTO_OFFSET) {
2532 BLI_assert(snode.runtime->iofsd == nullptr);
2534
2535 iofsd->insert = node_to_insert;
2536 iofsd->prev = from_node;
2537 iofsd->next = to_node;
2538
2539 snode.runtime->iofsd = iofsd;
2540 }
2541
2542 BKE_main_ensure_invariants(bmain, ntree.id);
2543}
2544
2546
2547/* -------------------------------------------------------------------- */
2550
2551static int get_main_socket_priority(const bNodeSocket *socket)
2552{
2553 switch (eNodeSocketDatatype(socket->type)) {
2554 case SOCK_CUSTOM:
2555 return 0;
2556 case SOCK_BOOLEAN:
2557 return 1;
2558 case SOCK_INT:
2559 return 2;
2560 case SOCK_FLOAT:
2561 return 3;
2562 case SOCK_VECTOR:
2563 return 4;
2564 case SOCK_RGBA:
2565 return 5;
2566 case SOCK_STRING:
2567 case SOCK_SHADER:
2568 case SOCK_OBJECT:
2569 case SOCK_IMAGE:
2570 case SOCK_ROTATION:
2571 case SOCK_MATRIX:
2572 case SOCK_GEOMETRY:
2573 case SOCK_COLLECTION:
2574 case SOCK_TEXTURE:
2575 case SOCK_MATERIAL:
2576 case SOCK_MENU:
2577 case SOCK_BUNDLE:
2578 case SOCK_CLOSURE:
2579 return 6;
2580 }
2581 return -1;
2582}
2583
2585{
2586 ListBase *sockets = (in_out == SOCK_IN) ? &node.inputs : &node.outputs;
2587
2588 /* Try to get the main socket based on the socket declaration. */
2589 bke::node_declaration_ensure(ntree, node);
2590 const nodes::NodeDeclaration *node_decl = node.declaration();
2591 if (node_decl != nullptr) {
2592 Span<nodes::SocketDeclaration *> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs :
2593 node_decl->outputs;
2594 for (const nodes::SocketDeclaration *socket_decl : socket_decls) {
2595 if (!socket_decl->is_default_link_socket) {
2596 continue;
2597 }
2598 bNodeSocket *socket = static_cast<bNodeSocket *>(BLI_findlink(sockets, socket_decl->index));
2599 if (socket && socket->is_visible()) {
2600 return socket;
2601 }
2602 }
2603 }
2604
2605 /* Find priority range. */
2606 int maxpriority = -1;
2607 LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
2608 if (sock->flag & SOCK_UNAVAIL) {
2609 continue;
2610 }
2611 maxpriority = max_ii(get_main_socket_priority(sock), maxpriority);
2612 }
2613
2614 /* Try all priorities, starting from 'highest'. */
2615 for (int priority = maxpriority; priority >= 0; priority--) {
2616 LISTBASE_FOREACH (bNodeSocket *, sock, sockets) {
2617 if (!!sock->is_visible() && priority == get_main_socket_priority(sock)) {
2618 return sock;
2619 }
2620 }
2621 }
2622
2623 return nullptr;
2624}
2625
2626static bool node_parents_offset_flag_enable_cb(bNode *parent, void * /*userdata*/)
2627{
2628 /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
2629 parent->flag |= NODE_TEST;
2630
2631 return true;
2632}
2633
2634static void node_offset_apply(bNode &node, const float offset_x)
2635{
2636 /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
2637 if ((node.flag & NODE_TEST) == 0) {
2638 node.runtime->anim_ofsx = (offset_x / UI_SCALE_FAC);
2639 node.flag |= NODE_TEST;
2640 }
2641}
2642
2643#define NODE_INSOFS_ANIM_DURATION 0.25f
2644
2650 bNode *tonode,
2651 void *userdata,
2652 const bool reversed)
2653{
2655 bNode *ofs_node = reversed ? fromnode : tonode;
2656
2657 node_offset_apply(*ofs_node, data->offset_x);
2658
2659 return true;
2660}
2661
2663 ARegion *region,
2664 const int mouse_xy[2],
2665 const bool right_alignment)
2666{
2667 bNodeTree *ntree = iofsd->ntree;
2668 bNode &insert = *iofsd->insert;
2669 bNode *prev = iofsd->prev, *next = iofsd->next;
2670 bNode *init_parent = insert.parent; /* store old insert.parent for restoring later */
2671
2672 const float min_margin = U.node_margin * UI_SCALE_FAC;
2673 const float width = NODE_WIDTH(insert);
2674 const bool needs_alignment = (next->runtime->draw_bounds.xmin -
2675 prev->runtime->draw_bounds.xmax) < (width + (min_margin * 2.0f));
2676
2677 float margin = width;
2678
2679 /* NODE_TEST will be used later, so disable for all nodes */
2681
2682 /* `insert.draw_bounds` isn't updated yet,
2683 * so `totr_insert` is used to get the correct world-space coords. */
2684 rctf totr_insert;
2685 node_to_updated_rect(insert, totr_insert);
2686
2687 /* Frame attachment wasn't handled yet so we search the frame that the node will be attached to
2688 * later. */
2689 insert.parent = node_find_frame_to_attach(*region, *ntree, mouse_xy);
2690
2691 /* This makes sure nodes are also correctly offset when inserting a node on top of a frame
2692 * without actually making it a part of the frame (because mouse isn't intersecting it)
2693 * - logic here is similar to node_find_frame_to_attach. */
2694 if (!insert.parent ||
2695 (prev->parent && (prev->parent == next->parent) && (prev->parent != insert.parent)))
2696 {
2697 rctf totr_frame;
2698
2699 /* check nodes front to back */
2700 for (bNode *frame : tree_draw_order_calc_nodes_reversed(*ntree)) {
2701 /* skip selected, those are the nodes we want to attach */
2702 if (!frame->is_frame() || (frame->flag & NODE_SELECT)) {
2703 continue;
2704 }
2705
2706 /* for some reason frame y coords aren't correct yet */
2707 node_to_updated_rect(*frame, totr_frame);
2708
2709 if (BLI_rctf_isect_x(&totr_frame, totr_insert.xmin) &&
2710 BLI_rctf_isect_x(&totr_frame, totr_insert.xmax))
2711 {
2712 if (BLI_rctf_isect_y(&totr_frame, totr_insert.ymin) ||
2713 BLI_rctf_isect_y(&totr_frame, totr_insert.ymax))
2714 {
2715 /* frame isn't insert.parent actually, but this is needed to make offsetting
2716 * nodes work correctly for above checked cases (it is restored later) */
2717 insert.parent = frame;
2718 break;
2719 }
2720 }
2721 }
2722 }
2723
2724 /* *** ensure offset at the left (or right for right_alignment case) of insert_node *** */
2725
2726 float dist = right_alignment ? totr_insert.xmin - prev->runtime->draw_bounds.xmax :
2727 next->runtime->draw_bounds.xmin - totr_insert.xmax;
2728 /* distance between insert_node and prev is smaller than min margin */
2729 if (dist < min_margin) {
2730 const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
2731
2732 node_offset_apply(insert, addval);
2733
2734 totr_insert.xmin += addval;
2735 totr_insert.xmax += addval;
2736 margin += min_margin;
2737 }
2738
2739 /* *** ensure offset at the right (or left for right_alignment case) of insert_node *** */
2740
2741 dist = right_alignment ? next->runtime->draw_bounds.xmin - totr_insert.xmax :
2742 totr_insert.xmin - prev->runtime->draw_bounds.xmax;
2743 /* distance between insert_node and next is smaller than min margin */
2744 if (dist < min_margin) {
2745 const float addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
2746 if (needs_alignment) {
2747 bNode *offs_node = right_alignment ? next : prev;
2748 node_offset_apply(*offs_node, addval);
2749 margin = addval;
2750 }
2751 /* enough room is available, but we want to ensure the min margin at the right */
2752 else {
2753 /* offset inserted node so that min margin is kept at the right */
2754 node_offset_apply(insert, -addval);
2755 }
2756 }
2757
2758 if (needs_alignment) {
2759 iofsd->offset_x = margin;
2760
2761 /* flag all parents of insert as offset to prevent them from being offset */
2763 /* iterate over entire chain and apply offsets */
2765 right_alignment ? next : prev,
2767 iofsd,
2768 !right_alignment);
2769 }
2770
2771 insert.parent = init_parent;
2772}
2773
2778{
2779 SpaceNode *snode = CTX_wm_space_node(C);
2780 NodeInsertOfsData *iofsd = static_cast<NodeInsertOfsData *>(op->customdata);
2781 bool redraw = false;
2782
2783 if (!snode || event->type != TIMER || iofsd == nullptr || iofsd->anim_timer != event->customdata)
2784 {
2785 return OPERATOR_PASS_THROUGH;
2786 }
2787
2788 const float duration = float(iofsd->anim_timer->time_duration);
2789
2790 /* handle animation - do this before possibly aborting due to duration, since
2791 * main thread might be so busy that node hasn't reached final position yet */
2792 for (bNode *node : snode->edittree->all_nodes()) {
2793 if (UNLIKELY(node->runtime->anim_ofsx)) {
2794 const float prev_duration = duration - float(iofsd->anim_timer->time_delta);
2795 /* Clamp duration to not overshoot. */
2796 const float clamped_duration = math::min(duration, NODE_INSOFS_ANIM_DURATION);
2797 if (prev_duration < clamped_duration) {
2798 const float offset_step = node->runtime->anim_ofsx *
2800 clamped_duration, 0.0f, 1.0f, NODE_INSOFS_ANIM_DURATION) -
2802 prev_duration, 0.0f, 1.0f, NODE_INSOFS_ANIM_DURATION));
2803 node->location[0] += offset_step;
2804 redraw = true;
2805 }
2806 }
2807 }
2808 if (redraw) {
2810 }
2811
2812 /* end timer + free insert offset data */
2813 if (duration > NODE_INSOFS_ANIM_DURATION) {
2815
2816 for (bNode *node : snode->edittree->all_nodes()) {
2817 node->runtime->anim_ofsx = 0.0f;
2818 }
2819
2820 MEM_freeN(iofsd);
2821
2823 }
2824
2826}
2827
2828#undef NODE_INSOFS_ANIM_DURATION
2829
2831 wmOperator *op,
2832 const wmEvent *event)
2833{
2834 const SpaceNode *snode = CTX_wm_space_node(C);
2835 NodeInsertOfsData *iofsd = snode->runtime->iofsd;
2836 snode->runtime->iofsd = nullptr;
2837 op->customdata = iofsd;
2838
2839 if (!iofsd || !iofsd->insert) {
2840 return OPERATOR_CANCELLED;
2841 }
2842
2844
2845 iofsd->ntree = snode->edittree;
2847
2849 iofsd, CTX_wm_region(C), event->mval, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT));
2850
2851 /* add temp handler */
2853
2855}
2856
2858{
2859 /* identifiers */
2860 ot->name = "Insert Offset";
2861 ot->description = "Automatically offset nodes on insertion";
2862 ot->idname = "NODE_OT_insert_offset";
2863
2864 /* callbacks */
2865 ot->invoke = node_insert_offset_invoke;
2868
2869 /* flags */
2871}
2872
2874
2875} // namespace blender::ed::space_node
SpaceNode * CTX_wm_space_node(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
#define NODE_FRAME
Definition BKE_node.hh:797
#define CMP_NODE_VIEWER
#define GEO_NODE_VIEWER
void BKE_ntree_update_tag_active_output_changed(bNodeTree *ntree)
void BKE_ntree_update_tag_link_changed(bNodeTree *ntree)
void BKE_ntree_update_tag_link_removed(bNodeTree *ntree)
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
void BKE_ntree_update_tag_link_added(bNodeTree *ntree, bNodeLink *link)
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:840
#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.cc:108
void BLI_kdtree_nd_ insert(KDTree *tree, int index, const float co[KD_DIMS]) ATTR_NONNULL(1
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
#define M_PI
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:291
ATTR_WARN_UNUSED_RESULT const size_t num
bool BLI_rctf_isect_x(const rctf *rect, float x)
Definition rct.cc:93
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
bool BLI_rctf_isect(const struct rctf *src1, const struct rctf *src2, struct rctf *dest)
bool BLI_rctf_isect_y(const rctf *rect, float y)
Definition rct.cc:104
bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2])
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition rct.cc:637
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
bool BLI_rctf_inside_rctf(const rctf *rct_a, const rctf *rct_b)
Definition rct.cc:193
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
unsigned char uchar
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
@ CD_AUTO_FROM_NAME
@ NODE_TEST
@ NODE_DO_OUTPUT
@ NODE_MUTED
@ NODE_SELECT
@ NTREE_CUSTOM
@ NTREE_GEOMETRY
@ NTREE_COMPOSIT
@ NODE_LINK_TEMP_HIGHLIGHT
@ NODE_LINK_MUTED
@ NODE_LINK_INSERT_TARGET
@ NODE_LINK_INSERT_TARGET_INVALID
@ NODE_LINK_VALID
eNodeSocketInOut
@ SOCK_OUT
@ SOCK_IN
@ NODE_VIEWER_SHORTCUT_NONE
@ SOCK_IS_LINKED
@ SOCK_MULTI_INPUT
@ SOCK_HIDDEN
@ SOCK_UNAVAIL
eNodeSocketDatatype
@ SOCK_INT
@ SOCK_TEXTURE
@ SOCK_VECTOR
@ SOCK_CLOSURE
@ SOCK_BOOLEAN
@ SOCK_MATERIAL
@ SOCK_SHADER
@ SOCK_MATRIX
@ SOCK_FLOAT
@ SOCK_IMAGE
@ SOCK_COLLECTION
@ SOCK_CUSTOM
@ SOCK_BUNDLE
@ SOCK_GEOMETRY
@ SOCK_ROTATION
@ SOCK_OBJECT
@ SOCK_STRING
@ SOCK_RGBA
@ SOCK_MENU
@ RGN_TYPE_UI
@ SNODE_INSERTOFS_DIR_RIGHT
@ SNODE_GEOMETRY_TOOL
#define UI_SCALE_FAC
#define UI_INV_SCALE_FAC
@ USER_NODE_AUTO_OFFSET
@ USER_REGION_OVERLAP
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
bool ED_node_is_compositor(const SpaceNode *snode)
Definition node_edit.cc:548
#define NODE_EDGE_PAN_OUTSIDE_PAD
Definition ED_node_c.hh:29
#define NODE_EDGE_PAN_INSIDE_PAD
Definition ED_node_c.hh:28
#define NODE_EDGE_PAN_MAX_SPEED
Definition ED_node_c.hh:31
#define NODE_EDGE_PAN_DELAY
Definition ED_node_c.hh:32
bool ED_node_is_geometry(SpaceNode *snode)
Definition node_edit.cc:563
#define NODE_EDGE_PAN_ZOOM_INFLUENCE
Definition ED_node_c.hh:33
#define NODE_EDGE_PAN_SPEED_RAMP
Definition ED_node_c.hh:30
void ED_preview_kill_jobs(wmWindowManager *wm, Main *bmain)
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:714
bool ED_operator_node_editable(bContext *C)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
#define REGION_DRAW_POST_PIXEL
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
#define UI_PRECISION_FLOAT_MAX
#define UI_NO_ICON_OVERLAY_TEXT
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, const IconTextOverlay *text_overlay, const bool inverted=false)
@ TH_TEXT
void UI_GetThemeColor4ubv(int colorid, unsigned char col[4])
void UI_view2d_edge_pan_operator_init(bContext *C, View2DEdgePanData *vpd, wmOperator *op)
void UI_view2d_edge_pan_cancel(bContext *C, View2DEdgePanData *vpd)
void UI_view2d_edge_pan_operator_properties_ex(wmOperatorType *ot, float inside_pad, float outside_pad, float speed_ramp, float max_speed, float delay, float zoom_influence)
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1667
void void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const wmEvent *event)
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1674
#define NC_NODE
Definition WM_types.hh:391
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_DISPLAY
Definition WM_types.hh:488
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
#define U
BMesh const char void * data
ccl_device_inline float arc_length(const float e2, const float gamma)
void append(const T &value)
bool is_empty() const
const T & first() const
bool add(const Key &key)
Definition BLI_set.hh:248
bool is_empty() const
Definition BLI_set.hh:595
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
bool is_empty() const
Definition BLI_stack.hh:308
void push_multiple(Span< T > values)
Definition BLI_stack.hh:281
bool contains(const Key &key) const
Span< Key > as_span() const
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
void extend(Span< T > array)
Vector< SocketDeclaration * > inputs
Vector< SocketDeclaration * > outputs
#define SELECT
#define ceilf(x)
KDTree_3d * tree
float distance(VecOp< float, D >, VecOp< float, D >) RET
uint padding(uint offset, uint alignment)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
bool node_is_parent_and_child(const bNode &parent, const bNode &child)
Definition node.cc:3662
void node_attach_node(bNodeTree &ntree, bNode &node, bNode &parent)
Definition node.cc:4255
bNode * node_get_active(bNodeTree &ntree)
Definition node.cc:4957
void node_remove_link(bNodeTree *ntree, bNodeLink &link)
Definition node.cc:4124
void node_chain_iterator(const bNodeTree *ntree, const bNode *node_start, bool(*callback)(bNode *, bNode *, void *, const bool), void *userdata, bool reversed)
Definition node.cc:3672
void node_parents_iterator(bNode *node, bool(*callback)(bNode *, void *), void *userdata)
Definition node.cc:3754
int node_count_socket_links(const bNodeTree &ntree, const bNodeSocket &sock)
Definition node.cc:4946
void node_internal_relink(bNodeTree &ntree, bNode &node)
Definition node.cc:4185
void node_detach_node(bNodeTree &ntree, bNode &node)
Definition node.cc:4263
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:4087
void node_link_set_mute(bNodeTree &ntree, bNodeLink &link, const bool muted)
Definition node.cc:4141
void node_remove_socket_links(bNodeTree &ntree, bNodeSocket &sock)
Definition node.cc:4150
bool node_declaration_ensure(bNodeTree &ntree, bNode &node)
Definition node.cc:5090
void node_set_active(bNodeTree &ntree, bNode &node)
Definition node.cc:4996
int node_socket_link_limit(const bNodeSocket &sock)
Definition node.cc:5025
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:5355
void node_tree_node_flag_set(bNodeTree &ntree, int flag, bool enable)
Definition node.cc:4846
static int view_socket(const bContext &C, SpaceNode &snode, bNodeTree &btree, bNode &bnode_to_view, bNodeSocket &bsocket_to_view)
static const bNode * find_overlapping_node(const bNodeTree &tree, const rctf &rect, const Span< const bNode * > ignored_nodes)
static int node_link_viewer(const bContext &C, bNode &bnode_to_view, bNodeSocket *bsocket_to_view)
static bNodeSocket * determine_socket_to_view(bNode &node_to_view)
static void finalize_viewer_link(const bContext &C, SpaceNode &snode, bNode &viewer_node, bNodeLink &viewer_link)
static bNodeSocket * node_link_viewer_get_socket(bNodeTree &ntree, bNode &viewer_node, bNodeSocket &src_socket)
static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node)
static bool is_viewer_socket(const bNodeSocket &socket)
static bool is_viewer_node(const bNode &node)
static bool socket_can_be_viewed(const bNodeSocket &socket)
static bool is_viewer_socket_in_viewer(const bNodeSocket &socket)
static Vector< float2 > get_viewer_node_position_candidates(const float2 initial, const float step_distance, const float max_distance)
static int get_default_viewer_type(const bContext *C)
static void position_viewer_node(const bContext &C, bNodeTree &tree, bNode &viewer_node, const bNode &node_to_view)
void NODE_OT_parent_set(wmOperatorType *ot)
static wmOperatorStatus node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void draw_draglink_tooltip_activate(const ARegion &region, bNodeLinkDrag &nldrag)
void node_deselect_all_input_sockets(bNodeTree &node_tree, bool deselect_nodes)
static bNodeSocket * best_socket_input(bNodeTree *ntree, bNode *node, int num, int replace)
void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion &region, bool attach_enabled, bool is_new_node)
static wmOperatorStatus mute_links_exec(bContext *C, wmOperator *op)
static void node_swap_links(bNodeLinkDrag &nldrag, bNodeTree &ntree)
void tree_draw_order_update(bNodeTree &ntree)
Definition node_draw.cc:321
static bool need_drag_link_tooltip(const bNodeTree &node_tree, const bNodeLinkDrag &nldrag)
static bNodeLink create_drag_link(bNode &node, bNodeSocket &socket)
static void clear_picking_highlight(ListBase *links)
static void sort_multi_input_socket_links_with_drag(bNodeSocket &socket, bNodeLink &drag_link, const float2 &cursor)
bNode * add_static_node(const bContext &C, int type, const float2 &location)
Definition node_add.cc:98
static wmOperatorStatus node_join_exec(bContext *C, wmOperator *)
static bool snode_autoconnect_input(SpaceNode &snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace)
static std::unique_ptr< bNodeLinkDrag > node_link_init(ARegion &region, SpaceNode &snode, const float2 cursor, const bool detach)
void invoke_node_link_drag_add_menu(bContext &C, bNode &node, bNodeSocket &socket, const float2 &cursor)
static wmOperatorStatus node_active_link_viewer_exec(bContext *C, wmOperator *)
void NODE_OT_detach(wmOperatorType *ot)
static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace)
static Vector< const bNode * > get_sorted_node_parents(const bNode &node)
bool all_links_muted(const bNodeSocket &socket)
static bool can_attach_node_to_frame(const bNode &node, const bNode &frame)
static bool socket_is_available(bNodeTree *, bNodeSocket *sock, const bool allow_used)
bNodeSocket * node_find_indicated_socket(SpaceNode &snode, ARegion &region, const float2 &cursor, const eNodeSocketInOut in_out)
static bNodeSocket * node_find_linkable_socket(const bNodeTree &ntree, const bNode *node, bNodeSocket *socket_to_match)
static wmOperatorStatus node_insert_offset_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
void NODE_OT_insert_offset(wmOperatorType *ot)
Array< bNode * > tree_draw_order_calc_nodes_reversed(bNodeTree &ntree)
Definition node_draw.cc:345
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:128
void NODE_OT_links_detach(wmOperatorType *ot)
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 wmOperatorStatus node_attach_invoke(bContext *C, wmOperator *, const wmEvent *event)
static void node_detach_recursive(bNodeTree &ntree, MutableSpan< NodeDetachstate > detach_states, bNode *node)
static void node_displace_existing_links(bNodeLinkDrag &nldrag, bNodeTree &ntree)
static void node_link_cancel(bContext *C, wmOperator *op)
static void pick_link(bNodeLinkDrag &nldrag, SpaceNode &snode, bNode *node, bNodeLink &link_to_pick)
static void displace_links(bNodeTree *ntree, const bNode *node, bNodeLink *inserted_link)
void NODE_OT_join(wmOperatorType *ot)
static bNode * node_find_frame_to_attach(ARegion &region, bNodeTree &ntree, const int2 mouse_xy)
static bool node_active_link_viewer_poll(bContext *C)
static wmOperatorStatus node_detach_exec(bContext *C, wmOperator *)
static wmOperatorStatus detach_links_exec(bContext *C, wmOperator *)
static bool node_can_be_inserted_on_link(bNodeTree &tree, bNode &node, const bNodeLink &link)
void NODE_OT_attach(wmOperatorType *ot)
void node_insert_on_frame_flag_set(bContext &C, SpaceNode &snode, const int2 &cursor)
bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link)
static void draw_draglink_tooltip_deactivate(const ARegion &region, bNodeLinkDrag &nldrag)
std::optional< float2 > link_path_intersection(const bNodeLink &link, const Span< float2 > path)
Definition node_add.cc:124
void NODE_OT_link_viewer(wmOperatorType *ot)
VectorSet< bNode * > get_selected_nodes(bNodeTree &node_tree)
void node_deselect_all_output_sockets(bNodeTree &node_tree, bool deselect_nodes)
static wmOperatorStatus node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
float2 node_to_view(const float2 &co)
Definition node_draw.cc:378
void node_insert_on_link_flags_clear(bNodeTree &node_tree)
static void add_dragged_links_to_tree(bContext &C, bNodeLinkDrag &nldrag)
static void remove_unavailable_links(bNodeTree &tree, bNodeSocket &socket)
static wmOperatorStatus node_parent_set_exec(bContext *C, wmOperator *)
void NODE_OT_link_make(wmOperatorType *ot)
void node_link_bezier_points_evaluated(const bNodeLink &link, std::array< float2, NODE_LINK_RESOL+1 > &coords)
Definition drawnode.cc:1704
wmKeyMap * node_link_modal_keymap(wmKeyConfig *keyconf)
static wmOperatorStatus node_make_link_exec(bContext *C, wmOperator *op)
static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, ARegion *region, const int mouse_xy[2], const bool right_alignment)
static bNodeSocket * best_socket_output(bNodeTree *ntree, bNode *node, bNodeSocket *sock_target, const bool allow_multiple)
static int node_socket_count_links(const bNodeTree &ntree, const bNodeSocket &socket)
static bool node_link_insert_offset_chain_cb(bNode *fromnode, bNode *tonode, void *userdata, const bool reversed)
void node_to_updated_rect(const bNode &node, rctf &r_rect)
Definition node_draw.cc:393
static bool should_create_drag_link_search_menu(const bNodeTree &node_tree, const bNodeLinkDrag &nldrag)
static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cursor)
void NODE_OT_link(wmOperatorType *ot)
void NODE_OT_links_mute(wmOperatorType *ot)
void node_insert_on_frame_flag_clear(SpaceNode &snode)
static const bNode * find_common_parent_node(const Span< const bNode * > nodes)
bNodeSocket * get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out)
static void node_join_attach_recursive(bNodeTree &ntree, MutableSpan< NodeJoinState > join_states, bNode *node, bNode *frame, const VectorSet< bNode * > &selected_nodes)
void update_multi_input_indices_for_removed_links(bNode &node)
void node_insert_on_link_flags(Main &bmain, SpaceNode &snode, bool is_new_node)
static bool node_parents_offset_flag_enable_cb(bNode *parent, void *)
static void node_remove_existing_links_if_needed(bNodeLinkDrag &nldrag, bNodeTree &ntree)
static wmOperatorStatus cut_links_exec(bContext *C, wmOperator *op)
static void node_offset_apply(bNode &node, const float offset_x)
static wmOperatorStatus node_join_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bNode * get_selected_node_for_insertion(bNodeTree &node_tree)
static void draw_draglink_tooltip(const bContext *, ARegion *, void *arg)
void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node)
T min(const T &a, const T &b)
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
#define NODE_LINK_RESOL
#define NODE_WIDTH(node)
#define NODE_INSOFS_ANIM_DURATION
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
PropertyRNA * RNA_def_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_float_array(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#define FLT_MAX
Definition stdcycles.h:14
ARegionRuntimeHandle * runtime
void * last
void * first
SpaceNode_Runtime * runtime
struct bNodeTree * edittree
struct bNodeTree * nodetree
char geometry_nodes_type
bNodeSocketRuntimeHandle * runtime
bNodeSocketTypeHandle * typeinfo
struct bNodeSocket * next
char idname[64]
bNodeTreeTypeHandle * typeinfo
ListBase links
float location[2]
bNodeTypeHandle * typeinfo
float width
ListBase inputs
struct bNode * parent
int16_t type_legacy
bNodeRuntimeHandle * runtime
void * storage
int16_t ui_order
ListBase outputs
int32_t identifier
std::optional< int > frame_identifier_to_highlight
std::unique_ptr< bNodeLinkDrag > linkdrag
float xmax
float xmin
float ymax
float ymin
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int mval[2]
Definition WM_types.hh:760
short prev_val
Definition WM_types.hh:811
void * customdata
Definition WM_types.hh:804
const void * modal_items
struct PointerRNA * ptr
double time_delta
Definition WM_types.hh:967
double time_duration
Definition WM_types.hh:965
i
Definition text_draw.cc:230
@ WM_CURSOR_KNIFE
Definition wm_cursors.hh:31
@ WM_CURSOR_MUTE
Definition wm_cursors.hh:60
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)
@ TIMER
@ EVT_MODAL_MAP
@ MOUSEMOVE
wmOperatorType * ot
Definition wm_files.cc:4225
void WM_gesture_lines_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_lines_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:929
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:956
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const wmEventType event_type, const double time_step)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)