diff options
Diffstat (limited to 'source/blender/editors/space_node/node_relationships.cc')
-rw-r--r-- | source/blender/editors/space_node/node_relationships.cc | 1016 |
1 files changed, 490 insertions, 526 deletions
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index e10bedb18f4..637c795d4d7 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -11,6 +11,7 @@ #include "DNA_node_types.h" #include "BLI_easing.h" +#include "BLI_stack.hh" #include "BKE_anim_data.h" #include "BKE_context.h" @@ -18,6 +19,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" #include "BKE_node_tree_update.h" #include "BKE_screen.h" @@ -25,8 +27,8 @@ #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" -#include "ED_spreadsheet.h" #include "ED_util.h" +#include "ED_viewer_path.hh" #include "RNA_access.h" #include "RNA_define.h" @@ -46,19 +48,11 @@ #include "BLT_translation.h" #include "NOD_node_declaration.hh" -#include "NOD_node_tree_ref.hh" #include "NOD_socket_declarations.hh" #include "NOD_socket_declarations_geometry.hh" #include "node_intern.hh" /* own include */ -using namespace blender::nodes::node_tree_ref_types; - -struct bNodeListItem { - struct bNodeListItem *next, *prev; - struct bNode *node; -}; - struct NodeInsertOfsData { bNodeTree *ntree; bNode *insert; /* inserted node */ @@ -70,6 +64,10 @@ struct NodeInsertOfsData { float offset_x; /* offset to apply to node chain */ }; +namespace blender::ed::space_node { + +bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out); + static void clear_picking_highlight(ListBase *links) { LISTBASE_FOREACH (bNodeLink *, link, links) { @@ -77,8 +75,6 @@ static void clear_picking_highlight(ListBase *links) } } -namespace blender::ed::space_node { - /* -------------------------------------------------------------------- */ /** \name Add Node * \{ */ @@ -109,11 +105,9 @@ static void pick_link( nldrag.links.append(link); nodeRemLink(snode.edittree, &link_to_pick); - + snode.edittree->ensure_topology_cache(); BLI_assert(nldrag.last_node_hovered_while_dragging_a_link != nullptr); - - sort_multi_input_socket_links( - snode, *nldrag.last_node_hovered_while_dragging_a_link, nullptr, nullptr); + update_multi_input_indices_for_removed_links(*nldrag.last_node_hovered_while_dragging_a_link); /* Send changed event to original link->tonode. */ if (node) { @@ -127,10 +121,8 @@ static void pick_input_link_by_link_intersect(const bContext &C, const float2 &cursor) { SpaceNode *snode = CTX_wm_space_node(&C); - const ARegion *region = CTX_wm_region(&C); - const View2D *v2d = ®ion->v2d; - float drag_start[2]; + float2 drag_start; RNA_float_get_array(op.ptr, "drag_start", drag_start); bNode *node; bNodeSocket *socket; @@ -139,26 +131,16 @@ static void pick_input_link_by_link_intersect(const bContext &C, /* Distance to test overlapping of cursor on link. */ const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC; - const int resolution = NODE_LINK_RESOL; - bNodeLink *link_to_pick = nullptr; clear_picking_highlight(&snode->edittree->links); LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { if (link->tosock == socket) { /* Test if the cursor is near a link. */ - float vec[4][2]; - node_link_bezier_handles(v2d, snode, *link, vec); - - float data[NODE_LINK_RESOL * 2 + 2]; - BKE_curve_forward_diff_bezier( - vec[0][0], vec[1][0], vec[2][0], vec[3][0], data, resolution, sizeof(float[2])); - BKE_curve_forward_diff_bezier( - vec[0][1], vec[1][1], vec[2][1], vec[3][1], data + 1, resolution, sizeof(float[2])); - - for (int i = 0; i < resolution * 2; i += 2) { - float *l1 = &data[i]; - float *l2 = &data[i + 2]; - float distance = dist_squared_to_line_segment_v2(cursor, l1, l2); + std::array<float2, NODE_LINK_RESOL + 1> coords; + node_link_bezier_points_evaluated(*link, coords); + + for (const int i : IndexRange(coords.size() - 1)) { + const float distance = dist_squared_to_line_segment_v2(cursor, coords[i], coords[i + 1]); if (distance < cursor_link_touch_distance) { link_to_pick = link; nldrag.last_picked_multi_input_socket_link = link_to_pick; @@ -188,7 +170,7 @@ static void pick_input_link_by_link_intersect(const bContext &C, } } -static bool socket_is_available(bNodeTree *UNUSED(ntree), bNodeSocket *sock, const bool allow_used) +static bool socket_is_available(bNodeTree * /*ntree*/, bNodeSocket *sock, const bool allow_used) { if (nodeSocketIsHidden(sock)) { return false; @@ -310,35 +292,24 @@ struct LinkAndPosition { float2 multi_socket_position; }; -void sort_multi_input_socket_links(SpaceNode &snode, - bNode &node, - bNodeLink *drag_link, - const float2 *cursor) +static void sort_multi_input_socket_links_with_drag(bNode &node, + bNodeLink &drag_link, + const float2 &cursor) { - LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { - if (!(socket->flag & SOCK_MULTI_INPUT)) { + for (bNodeSocket *socket : node.input_sockets()) { + if (!socket->is_multi_input()) { continue; } - Vector<LinkAndPosition, 8> links; + const float2 &socket_location = {socket->locx, socket->locy}; - LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) { - if (link->tosock == socket) { - links.append( - {link, - node_link_calculate_multi_input_position({link->tosock->locx, link->tosock->locy}, - link->multi_input_socket_index, - link->tosock->total_inputs)}); - } - } + Vector<LinkAndPosition, 8> links; + for (bNodeLink *link : socket->directly_linked_links()) { + const float2 location = node_link_calculate_multi_input_position( + socket_location, link->multi_input_socket_index, link->tosock->total_inputs); + links.append({link, location}); + }; - if (drag_link) { - LinkAndPosition link_and_position{}; - link_and_position.link = drag_link; - if (cursor) { - link_and_position.multi_socket_position = *cursor; - } - links.append(link_and_position); - } + links.append({&drag_link, cursor}); std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) { return a.multi_socket_position.y < b.multi_socket_position.y; @@ -350,6 +321,23 @@ void sort_multi_input_socket_links(SpaceNode &snode, } } +void update_multi_input_indices_for_removed_links(bNode &node) +{ + for (bNodeSocket *socket : node.input_sockets()) { + if (!socket->is_multi_input()) { + continue; + } + Vector<bNodeLink *, 8> links = socket->directly_linked_links(); + std::sort(links.begin(), links.end(), [](const bNodeLink *a, const bNodeLink *b) { + return a->multi_input_socket_index < b->multi_input_socket_index; + }); + + for (const int i : links.index_range()) { + links[i]->multi_input_socket_index = i; + } + } +} + static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace) { bNodeTree *ntree = snode.edittree; @@ -434,18 +422,18 @@ namespace viewer_linking { * \{ */ /* Depending on the node tree type, different socket types are supported by viewer nodes. */ -static bool socket_can_be_viewed(const OutputSocketRef &socket) +static bool socket_can_be_viewed(const bNodeSocket &socket) { - if (nodeSocketIsHidden(socket.bsocket())) { + if (nodeSocketIsHidden(&socket)) { return false; } - if (socket.idname() == "NodeSocketVirtual") { + if (STREQ(socket.idname, "NodeSocketVirtual")) { return false; } - if (socket.tree().btree()->type != NTREE_GEOMETRY) { + if (socket.owner_tree().type != NTREE_GEOMETRY) { return true; } - return ELEM(socket.typeinfo()->type, + return ELEM(socket.typeinfo->type, SOCK_GEOMETRY, SOCK_FLOAT, SOCK_VECTOR, @@ -502,44 +490,25 @@ static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree, return nullptr; } -static bool is_viewer_node(const NodeRef &node) -{ - return ELEM(node.bnode()->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER); -} - -static Vector<const NodeRef *> find_viewer_nodes(const NodeTreeRef &tree) +static bool is_viewer_node(const bNode &node) { - Vector<const NodeRef *> viewer_nodes; - for (const NodeRef *node : tree.nodes()) { - if (is_viewer_node(*node)) { - viewer_nodes.append(node); - } - } - return viewer_nodes; + return ELEM(node.type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER); } -static bool is_viewer_socket_in_viewer(const InputSocketRef &socket) +static bool is_viewer_socket_in_viewer(const bNodeSocket &socket) { - const NodeRef &node = socket.node(); + const bNode &node = socket.owner_node(); BLI_assert(is_viewer_node(node)); - if (node.typeinfo()->type == GEO_NODE_VIEWER) { + if (node.typeinfo->type == GEO_NODE_VIEWER) { return true; } return socket.index() == 0; } -static bool is_linked_to_viewer(const OutputSocketRef &socket, const NodeRef &viewer_node) +static bool is_viewer_socket(const bNodeSocket &socket) { - for (const InputSocketRef *target_socket : socket.directly_linked_sockets()) { - if (&target_socket->node() != &viewer_node) { - continue; - } - if (!target_socket->is_available()) { - continue; - } - if (is_viewer_socket_in_viewer(*target_socket)) { - return true; - } + if (is_viewer_node(socket.owner_node())) { + return is_viewer_socket_in_viewer(socket); } return false; } @@ -561,141 +530,165 @@ static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode & } } -static const NodeRef *get_existing_viewer(const NodeTreeRef &tree) +static bNodeSocket *determine_socket_to_view(bNode &node_to_view) { - Vector<const NodeRef *> viewer_nodes = find_viewer_nodes(tree); - - /* Check if there is already an active viewer node that should be used. */ - for (const NodeRef *viewer_node : viewer_nodes) { - if (viewer_node->bnode()->flag & NODE_DO_OUTPUT) { - return viewer_node; + int last_linked_socket_index = -1; + for (bNodeSocket *socket : node_to_view.output_sockets()) { + if (!socket_can_be_viewed(*socket)) { + continue; } - } - - /* If no active but non-active viewers exist, make one active. */ - if (!viewer_nodes.is_empty()) { - viewer_nodes[0]->bnode()->flag |= NODE_DO_OUTPUT; - return viewer_nodes[0]; - } - return nullptr; -} - -static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *active_viewer_node, - const NodeRef &node_to_view) -{ - /* Check if any of the output sockets is selected, which is the case when the user just clicked - * on the socket. */ - for (const OutputSocketRef *output_socket : node_to_view.outputs()) { - if (output_socket->bsocket()->flag & SELECT) { - return output_socket; + for (bNodeLink *link : socket->directly_linked_links()) { + bNodeSocket &target_socket = *link->tosock; + bNode &target_node = *link->tonode; + if (is_viewer_socket(target_socket)) { + if (link->is_muted() || !(target_node.flag & NODE_DO_OUTPUT)) { + /* This socket is linked to a deactivated viewer, the viewer should be activated. */ + return socket; + } + last_linked_socket_index = socket->index(); + } } } - const OutputSocketRef *last_socket_linked_to_viewer = nullptr; - if (active_viewer_node != nullptr) { - for (const OutputSocketRef *output_socket : node_to_view.outputs()) { - if (!socket_can_be_viewed(*output_socket)) { - continue; - } - if (is_linked_to_viewer(*output_socket, *active_viewer_node)) { - last_socket_linked_to_viewer = output_socket; + if (last_linked_socket_index == -1) { + /* Return the first socket that can be viewed. */ + for (bNodeSocket *socket : node_to_view.output_sockets()) { + if (socket_can_be_viewed(*socket)) { + return socket; } } + return nullptr; } - if (last_socket_linked_to_viewer == nullptr) { - /* If no output is connected to a viewer, use the first output that can be viewed. */ - for (const OutputSocketRef *output_socket : node_to_view.outputs()) { - if (socket_can_be_viewed(*output_socket)) { - return output_socket; - } + + /* Pick the next socket to be linked to the viewer. */ + const int tot_outputs = node_to_view.output_sockets().size(); + for (const int offset : IndexRange(1, tot_outputs)) { + const int index = (last_linked_socket_index + offset) % tot_outputs; + bNodeSocket &output_socket = node_to_view.output_socket(index); + if (!socket_can_be_viewed(output_socket)) { + continue; } - } - else { - /* Pick the next socket to be linked to the viewer. */ - const int tot_outputs = node_to_view.outputs().size(); - for (const int offset : IndexRange(1, tot_outputs - 1)) { - const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs; - const OutputSocketRef &output_socket = node_to_view.output(index); - if (!socket_can_be_viewed(output_socket)) { + bool is_currently_viewed = false; + for (const bNodeLink *link : output_socket.directly_linked_links()) { + bNodeSocket &target_socket = *link->tosock; + bNode &target_node = *link->tonode; + if (!is_viewer_socket(target_socket)) { + continue; + } + if (link->is_muted()) { continue; } - if (is_linked_to_viewer(output_socket, *active_viewer_node)) { + if (!(target_node.flag & NODE_DO_OUTPUT)) { continue; } - return &output_socket; + is_currently_viewed = true; + break; + } + if (is_currently_viewed) { + continue; } + return &output_socket; } return nullptr; } -static int link_socket_to_viewer(const bContext &C, - bNode *viewer_bnode, - bNode &bnode_to_view, - bNodeSocket &bsocket_to_view) +static void finalize_viewer_link(const bContext &C, + SpaceNode &snode, + bNode &viewer_node, + bNodeLink &viewer_link) { - SpaceNode &snode = *CTX_wm_space_node(&C); - bNodeTree &btree = *snode.edittree; + Main *bmain = CTX_data_main(&C); + remove_links_to_unavailable_viewer_sockets(*snode.edittree, viewer_node); + viewer_link.flag &= ~NODE_LINK_MUTED; + viewer_node.flag &= ~NODE_MUTED; + viewer_node.flag |= NODE_DO_OUTPUT; + if (snode.edittree->type == NTREE_GEOMETRY) { + viewer_path::activate_geometry_node(*bmain, snode, viewer_node); + } + ED_node_tree_propagate_change(&C, bmain, snode.edittree); +} - if (viewer_bnode == nullptr) { - /* Create a new viewer node if none exists. */ - const int viewer_type = get_default_viewer_type(&C); - viewer_bnode = node_add_node( - C, nullptr, viewer_type, bsocket_to_view.locx + 100, bsocket_to_view.locy); - if (viewer_bnode == nullptr) { - return OPERATOR_CANCELLED; +static int view_socket(const bContext &C, + SpaceNode &snode, + bNodeTree &btree, + bNode &bnode_to_view, + bNodeSocket &bsocket_to_view) +{ + bNode *viewer_node = nullptr; + /* Try to find a viewer that is already active. */ + LISTBASE_FOREACH (bNode *, node, &btree.nodes) { + if (is_viewer_node(*node)) { + if (node->flag & NODE_DO_OUTPUT) { + viewer_node = node; + break; + } } } - bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_bnode, bsocket_to_view); + /* Try to reactivate existing viewer connection. */ + for (bNodeLink *link : bsocket_to_view.directly_linked_links()) { + bNodeSocket &target_socket = *link->tosock; + bNode &target_node = *link->tonode; + if (is_viewer_socket(target_socket) && ELEM(viewer_node, nullptr, &target_node)) { + finalize_viewer_link(C, snode, target_node, *link); + return OPERATOR_FINISHED; + } + } + + if (viewer_node == nullptr) { + LISTBASE_FOREACH (bNode *, node, &btree.nodes) { + if (is_viewer_node(*node)) { + viewer_node = node; + break; + } + } + } + if (viewer_node == nullptr) { + const int viewer_type = get_default_viewer_type(&C); + const float2 location{bsocket_to_view.locx / UI_DPI_FAC + 100, + bsocket_to_view.locy / UI_DPI_FAC}; + viewer_node = add_static_node(C, viewer_type, location); + } + + bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, *viewer_node, bsocket_to_view); if (viewer_bsocket == nullptr) { return OPERATOR_CANCELLED; } - - bNodeLink *link_to_change = nullptr; - LISTBASE_FOREACH (bNodeLink *, link, &btree.links) { + bNodeLink *viewer_link = nullptr; + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) { if (link->tosock == viewer_bsocket) { - link_to_change = link; + viewer_link = link; break; } } - - if (link_to_change == nullptr) { - nodeAddLink(&btree, &bnode_to_view, &bsocket_to_view, viewer_bnode, viewer_bsocket); + if (viewer_link == nullptr) { + viewer_link = nodeAddLink( + &btree, &bnode_to_view, &bsocket_to_view, viewer_node, viewer_bsocket); } else { - link_to_change->fromnode = &bnode_to_view; - link_to_change->fromsock = &bsocket_to_view; + viewer_link->fromnode = &bnode_to_view; + viewer_link->fromsock = &bsocket_to_view; BKE_ntree_update_tag_link_changed(&btree); } - - remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode); - - if (btree.type == NTREE_GEOMETRY) { - ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(&C), &snode, viewer_bnode); - } - - ED_node_tree_propagate_change(&C, CTX_data_main(&C), &btree); - return OPERATOR_FINISHED; + finalize_viewer_link(C, snode, *viewer_node, *viewer_link); + return OPERATOR_CANCELLED; } -static int node_link_viewer(const bContext &C, bNode &bnode_to_view) +static int node_link_viewer(const bContext &C, bNode &bnode_to_view, bNodeSocket *bsocket_to_view) { SpaceNode &snode = *CTX_wm_space_node(&C); bNodeTree *btree = snode.edittree; + btree->ensure_topology_cache(); - const NodeTreeRef tree{btree}; - const NodeRef &node_to_view = *tree.find_node(bnode_to_view); - const NodeRef *active_viewer_node = get_existing_viewer(tree); + if (bsocket_to_view == nullptr) { + bsocket_to_view = determine_socket_to_view(bnode_to_view); + } - const OutputSocketRef *socket_to_view = find_output_socket_to_be_viewed(active_viewer_node, - node_to_view); - if (socket_to_view == nullptr) { - return OPERATOR_FINISHED; + if (bsocket_to_view == nullptr) { + return OPERATOR_CANCELLED; } - bNodeSocket &bsocket_to_view = *socket_to_view->bsocket(); - bNode *viewer_bnode = active_viewer_node ? active_viewer_node->bnode() : nullptr; - return link_socket_to_viewer(C, viewer_bnode, bnode_to_view, bsocket_to_view); + return view_socket(C, snode, *btree, bnode_to_view, *bsocket_to_view); } /** \} */ @@ -706,7 +699,7 @@ static int node_link_viewer(const bContext &C, bNode &bnode_to_view) /** \name Link to Viewer Node Operator * \{ */ -static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) +static int node_active_link_viewer_exec(bContext *C, wmOperator * /*op*/) { SpaceNode &snode = *CTX_wm_space_node(C); bNode *node = nodeGetActive(snode.edittree); @@ -717,7 +710,15 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - if (viewer_linking::node_link_viewer(*C, *node) == OPERATOR_CANCELLED) { + bNodeSocket *socket_to_view = nullptr; + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { + if (socket->flag & SELECT) { + socket_to_view = socket; + break; + } + } + + if (viewer_linking::node_link_viewer(*C, *node, socket_to_view) == OPERATOR_CANCELLED) { return OPERATOR_CANCELLED; } @@ -804,7 +805,7 @@ static bool should_create_drag_link_search_menu(const bNodeTree &node_tree, return true; } -static void draw_draglink_tooltip(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg) +static void draw_draglink_tooltip(const bContext * /*C*/, ARegion * /*region*/, void *arg) { bNodeLinkDrag *nldrag = static_cast<bNodeLinkDrag *>(arg); @@ -814,7 +815,8 @@ static void draw_draglink_tooltip(const bContext *UNUSED(C), ARegion *UNUSED(reg nldrag->cursor[0]; const float y = nldrag->cursor[1] - 2.0f * UI_DPI_FAC; - UI_icon_draw_ex(x, y, ICON_ADD, U.inv_dpi_fac, 1.0f, 0.0f, text_col, false); + UI_icon_draw_ex( + x, y, ICON_ADD, U.inv_dpi_fac, 1.0f, 0.0f, text_col, false, UI_NO_ICON_OVERLAY_TEXT); } static void draw_draglink_tooltip_activate(const ARegion ®ion, bNodeLinkDrag &nldrag) @@ -833,7 +835,7 @@ static void draw_draglink_tooltip_deactivate(const ARegion ®ion, bNodeLinkDra } } -static void node_link_update_header(bContext *C, bNodeLinkDrag *UNUSED(nldrag)) +static void node_link_update_header(bContext *C, bNodeLinkDrag * /*nldrag*/) { char header[UI_MAX_DRAW_STR]; @@ -931,7 +933,7 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links) ED_node_tree_propagate_change(&C, bmain, &ntree); - /* Ensure draglink tooltip is disabled. */ + /* Ensure drag-link tool-tip is disabled. */ draw_draglink_tooltip_deactivate(*CTX_wm_region(&C), *nldrag); ED_workspace_status_text(&C, nullptr); @@ -949,6 +951,7 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur if (nldrag->in_out == SOCK_OUT) { bNode *tnode; bNodeSocket *tsock = nullptr; + snode.edittree->ensure_topology_cache(); if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) { for (bNodeLink *link : nldrag->links) { /* skip if socket is on the same node as the fromsock */ @@ -975,19 +978,19 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur continue; } if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) { - sort_multi_input_socket_links(snode, *tnode, link, &cursor); + sort_multi_input_socket_links_with_drag(*tnode, *link, cursor); } } } else { for (bNodeLink *link : nldrag->links) { - if (nldrag->last_node_hovered_while_dragging_a_link) { - sort_multi_input_socket_links( - snode, *nldrag->last_node_hovered_while_dragging_a_link, nullptr, &cursor); - } link->tonode = nullptr; link->tosock = nullptr; } + if (nldrag->last_node_hovered_while_dragging_a_link) { + update_multi_input_indices_for_removed_links( + *nldrag->last_node_hovered_while_dragging_a_link); + } } } else { @@ -1184,7 +1187,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) bool detach = RNA_boolean_get(op->ptr, "detach"); - int mval[2]; + int2 mval; WM_event_drag_start_mval(event, ®ion, mval); float2 cursor; @@ -1324,28 +1327,6 @@ void NODE_OT_link_make(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Node Link Intersect - * \{ */ - -static bool node_links_intersect(bNodeLink &link, const float mcoords[][2], int tot) -{ - float coord_array[NODE_LINK_RESOL + 1][2]; - - if (node_link_bezier_points(nullptr, nullptr, link, coord_array, NODE_LINK_RESOL)) { - for (int i = 0; i < tot - 1; i++) { - for (int b = 0; b < NODE_LINK_RESOL; b++) { - if (isect_seg_seg_v2(mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1]) > 0) { - return true; - } - } - } - } - return false; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Cut Link Operator * \{ */ @@ -1353,56 +1334,63 @@ static int cut_links_exec(bContext *C, wmOperator *op) { Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); - ARegion ®ion = *CTX_wm_region(C); + const ARegion ®ion = *CTX_wm_region(C); - int i = 0; - float mcoords[256][2]; + Vector<float2> path; RNA_BEGIN (op->ptr, itemptr, "path") { - float loc[2]; - - RNA_float_get_array(&itemptr, "loc", loc); - UI_view2d_region_to_view( - ®ion.v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]); - i++; - if (i >= 256) { + float2 loc_region; + RNA_float_get_array(&itemptr, "loc", loc_region); + float2 loc_view; + UI_view2d_region_to_view(®ion.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y); + path.append(loc_view); + if (path.size() >= 256) { break; } } RNA_END; - if (i > 1) { - bool found = false; + if (path.is_empty()) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } - ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + bool found = false; - LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode.edittree->links) { - if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { - continue; - } + ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); - if (node_links_intersect(*link, mcoords, i)) { + bNodeTree &node_tree = *snode.edittree; - if (found == false) { - /* TODO(sergey): Why did we kill jobs twice? */ - ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); - found = true; - } + Set<bNode *> affected_nodes; - bNode *to_node = link->tonode; - nodeRemLink(snode.edittree, link); - sort_multi_input_socket_links(snode, *to_node, nullptr, nullptr); - } + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &node_tree.links) { + if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { + continue; } - ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); - if (found) { - return OPERATOR_FINISHED; + if (link_path_intersection(*link, path)) { + + if (!found) { + /* TODO(sergey): Why did we kill jobs twice? */ + ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + found = true; + } + + bNode *to_node = link->tonode; + nodeRemLink(snode.edittree, link); + affected_nodes.add(to_node); } + } - return OPERATOR_CANCELLED; + node_tree.ensure_topology_cache(); + for (bNode *node : affected_nodes) { + update_multi_input_indices_for_removed_links(*node); } - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); + if (found) { + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; } void NODE_OT_links_cut(wmOperatorType *ot) @@ -1436,69 +1424,99 @@ void NODE_OT_links_cut(wmOperatorType *ot) /** \name Mute Links Operator * \{ */ +static bool all_links_muted(const bNodeSocket &socket) +{ + for (const bNodeLink *link : socket.directly_linked_links()) { + if (!(link->flag & NODE_LINK_MUTED)) { + return false; + } + } + return true; +} + static int mute_links_exec(bContext *C, wmOperator *op) { Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); - ARegion ®ion = *CTX_wm_region(C); + const ARegion ®ion = *CTX_wm_region(C); + bNodeTree &ntree = *snode.edittree; - int i = 0; - float mcoords[256][2]; + Vector<float2> path; RNA_BEGIN (op->ptr, itemptr, "path") { - float loc[2]; - - RNA_float_get_array(&itemptr, "loc", loc); - UI_view2d_region_to_view( - ®ion.v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]); - i++; - if (i >= 256) { + float2 loc_region; + RNA_float_get_array(&itemptr, "loc", loc_region); + float2 loc_view; + UI_view2d_region_to_view(®ion.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y); + path.append(loc_view); + if (path.size() >= 256) { break; } } RNA_END; - if (i > 1) { - ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + if (path.is_empty()) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } - /* Count intersected links and clear test flag. */ - int tot = 0; - LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) { - if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { - continue; - } - link->flag &= ~NODE_LINK_TEST; - if (node_links_intersect(*link, mcoords, i)) { - tot++; - } + ED_preview_kill_jobs(CTX_wm_manager(C), &bmain); + + ntree.ensure_topology_cache(); + + Set<bNodeLink *> affected_links; + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { + continue; } - if (tot == 0) { - return OPERATOR_CANCELLED; + if (!link_path_intersection(*link, path)) { + continue; } + affected_links.add(link); + } - /* Mute links. */ - LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) { - if (node_link_is_hidden_or_dimmed(region.v2d, *link) || (link->flag & NODE_LINK_TEST)) { - continue; - } + if (affected_links.is_empty()) { + return OPERATOR_CANCELLED; + } - if (node_links_intersect(*link, mcoords, i)) { - nodeMuteLinkToggle(snode.edittree, link); + bke::node_tree_runtime::AllowUsingOutdatedInfo allow_outdated_info{ntree}; + + for (bNodeLink *link : affected_links) { + nodeLinkSetMute(&ntree, link, !(link->flag & NODE_LINK_MUTED)); + const bool muted = link->flag & NODE_LINK_MUTED; + + /* Propagate mute status downstream past reroute nodes. */ + if (link->tonode->is_reroute()) { + Stack<bNodeLink *> links; + links.push_multiple(link->tonode->output_sockets().first()->directly_linked_links()); + while (!links.is_empty()) { + bNodeLink *link = links.pop(); + nodeLinkSetMute(&ntree, link, muted); + if (!link->tonode->is_reroute()) { + continue; + } + links.push_multiple(link->tonode->output_sockets().first()->directly_linked_links()); } } - - /* Clear remaining test flags. */ - LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) { - if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { - continue; + /* Propagate mute status upstream past reroutes, but only if all outputs are muted. */ + if (link->fromnode->is_reroute()) { + if (!muted || all_links_muted(*link->fromsock)) { + Stack<bNodeLink *> links; + links.push_multiple(link->fromnode->input_sockets().first()->directly_linked_links()); + while (!links.is_empty()) { + bNodeLink *link = links.pop(); + nodeLinkSetMute(&ntree, link, muted); + if (!link->fromnode->is_reroute()) { + continue; + } + if (!muted || all_links_muted(*link->fromsock)) { + links.push_multiple(link->fromnode->input_sockets().first()->directly_linked_links()); + } + } } - link->flag &= ~NODE_LINK_TEST; } - - ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree); - return OPERATOR_FINISHED; } - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); + return OPERATOR_FINISHED; } void NODE_OT_links_mute(wmOperatorType *ot) @@ -1532,7 +1550,7 @@ void NODE_OT_links_mute(wmOperatorType *ot) /** \name Detach Links Operator * \{ */ -static int detach_links_exec(bContext *C, wmOperator *UNUSED(op)) +static int detach_links_exec(bContext *C, wmOperator * /*op*/) { SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; @@ -1569,7 +1587,7 @@ void NODE_OT_links_detach(wmOperatorType *ot) /** \name Set Parent Operator * \{ */ -static int node_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) +static int node_parent_set_exec(bContext *C, wmOperator * /*op*/) { SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; @@ -1619,7 +1637,9 @@ void NODE_OT_parent_set(wmOperatorType *ot) #define NODE_JOIN_DONE 1 #define NODE_JOIN_IS_DESCENDANT 2 -static void node_join_attach_recursive(bNode *node, bNode *frame) +static void node_join_attach_recursive(bNode *node, + bNode *frame, + const Set<bNode *> &selected_nodes) { node->done |= NODE_JOIN_DONE; @@ -1629,43 +1649,35 @@ static void node_join_attach_recursive(bNode *node, bNode *frame) else if (node->parent) { /* call recursively */ if (!(node->parent->done & NODE_JOIN_DONE)) { - node_join_attach_recursive(node->parent, frame); + node_join_attach_recursive(node->parent, frame, selected_nodes); } /* in any case: if the parent is a descendant, so is the child */ if (node->parent->done & NODE_JOIN_IS_DESCENDANT) { node->done |= NODE_JOIN_IS_DESCENDANT; } - else if (node->flag & NODE_TEST) { + else if (selected_nodes.contains(node)) { /* if parent is not an descendant of the frame, reattach the node */ nodeDetachNode(node); nodeAttachNode(node, frame); node->done |= NODE_JOIN_IS_DESCENDANT; } } - else if (node->flag & NODE_TEST) { + else if (selected_nodes.contains(node)) { nodeAttachNode(node, frame); node->done |= NODE_JOIN_IS_DESCENDANT; } } -static int node_join_exec(bContext *C, wmOperator *UNUSED(op)) +static int node_join_exec(bContext *C, wmOperator * /*op*/) { + Main &bmain = *CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; - /* XXX save selection: node_add_node call below sets the new frame as single - * active+selected node */ - LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { - if (node->flag & NODE_SELECT) { - node->flag |= NODE_TEST; - } - else { - node->flag &= ~NODE_TEST; - } - } + const Set<bNode *> selected_nodes = get_selected_nodes(ntree); - bNode *frame = node_add_node(*C, nullptr, NODE_FRAME, 0.0f, 0.0f); + bNode *frame_node = nodeAddStaticNode(C, &ntree, NODE_FRAME); /* reset tags */ LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { @@ -1674,18 +1686,12 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op)) LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { if (!(node->done & NODE_JOIN_DONE)) { - node_join_attach_recursive(node, frame); - } - } - - /* restore selection */ - LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { - if (node->flag & NODE_TEST) { - node->flag |= NODE_SELECT; + node_join_attach_recursive(node, frame_node, selected_nodes); } } node_sort(ntree); + ED_node_tree_propagate_change(C, &bmain, snode.edittree); WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); return OPERATOR_FINISHED; @@ -1714,11 +1720,11 @@ void NODE_OT_join(wmOperatorType *ot) static bNode *node_find_frame_to_attach(ARegion ®ion, const bNodeTree &ntree, - const int mouse_xy[2]) + const int2 mouse_xy) { /* convert mouse coordinates to v2d space */ - float cursor[2]; - UI_view2d_region_to_view(®ion.v2d, UNPACK2(mouse_xy), &cursor[0], &cursor[1]); + float2 cursor; + UI_view2d_region_to_view(®ion.v2d, mouse_xy.x, mouse_xy.y, &cursor.x, &cursor.y); LISTBASE_FOREACH_BACKWARD (bNode *, frame, &ntree.nodes) { /* skip selected, those are the nodes we want to attach */ @@ -1733,39 +1739,43 @@ static bNode *node_find_frame_to_attach(ARegion ®ion, return nullptr; } -static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +static int node_attach_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event) { ARegion ®ion = *CTX_wm_region(C); SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; bNode *frame = node_find_frame_to_attach(region, ntree, event->mval); + if (frame == nullptr) { + /* Return "finished" so that auto offset operator macros can work. */ + return OPERATOR_FINISHED; + } - if (frame) { - LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) { - if (node->flag & NODE_SELECT) { - if (node->parent == nullptr) { - /* disallow moving a parent into its child */ - if (nodeAttachNodeCheck(frame, node) == false) { - /* attach all unparented nodes */ - nodeAttachNode(node, frame); - } + LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) { + if (!(node->flag & NODE_SELECT)) { + continue; + } + + if (node->parent == nullptr) { + /* disallow moving a parent into its child */ + if (nodeAttachNodeCheck(frame, node) == false) { + /* attach all unparented nodes */ + nodeAttachNode(node, frame); + } + } + else { + /* attach nodes which share parent with the frame */ + bNode *parent; + for (parent = frame->parent; parent; parent = parent->parent) { + if (parent == node->parent) { + break; } - else { - /* attach nodes which share parent with the frame */ - bNode *parent; - for (parent = frame->parent; parent; parent = parent->parent) { - if (parent == node->parent) { - break; - } - } + } - if (parent) { - /* disallow moving a parent into its child */ - if (nodeAttachNodeCheck(frame, node) == false) { - nodeDetachNode(node); - nodeAttachNode(node, frame); - } - } + if (parent) { + /* disallow moving a parent into its child */ + if (nodeAttachNodeCheck(frame, node) == false) { + nodeDetachNode(node); + nodeAttachNode(node, frame); } } } @@ -1829,7 +1839,7 @@ static void node_detach_recursive(bNode *node) } /* detach the root nodes in the current selection */ -static int node_detach_exec(bContext *C, wmOperator *UNUSED(op)) +static int node_detach_exec(bContext *C, wmOperator * /*op*/) { SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; @@ -1874,139 +1884,173 @@ void NODE_OT_detach(wmOperatorType *ot) /** \name Automatic Node Insert on Dragging * \{ */ -/* prevent duplicate testing code below */ -static bool ed_node_link_conditions(ScrArea *area, - bool test, - SpaceNode **r_snode, - bNode **r_select) +static bNode *get_selected_node_for_insertion(bNodeTree &node_tree) { - SpaceNode *snode = area ? (SpaceNode *)area->spacedata.first : nullptr; + bNode *selected_node = nullptr; + int selected_node_count = 0; + for (bNode *node : node_tree.all_nodes()) { + if (node->flag & SELECT) { + selected_node = node; + selected_node_count++; + } + if (selected_node_count > 1) { + return nullptr; + } + } + if (!selected_node) { + return nullptr; + } + if (selected_node->input_sockets().is_empty() || selected_node->output_sockets().is_empty()) { + return nullptr; + } + if (std::any_of(selected_node->input_sockets().begin(), + selected_node->input_sockets().end(), + [&](const bNodeSocket *socket) { return socket->is_directly_linked(); })) { + return nullptr; + } + if (std::any_of(selected_node->output_sockets().begin(), + selected_node->output_sockets().end(), + [&](const bNodeSocket *socket) { return socket->is_directly_linked(); })) { + return nullptr; + }; + return selected_node; +} - *r_snode = snode; - *r_select = nullptr; +void node_insert_on_link_flags_set(SpaceNode &snode, const ARegion ®ion) +{ + bNodeTree &node_tree = *snode.edittree; + node_tree.ensure_topology_cache(); - /* no unlucky accidents */ - if (area == nullptr || area->spacetype != SPACE_NODE) { - return false; - } + node_insert_on_link_flags_clear(node_tree); - if (!test) { - /* no need to look for a node */ - return true; + bNode *node_to_insert = get_selected_node_for_insertion(node_tree); + if (!node_to_insert) { + return; } - bNode *node; - bNode *select = nullptr; - for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) { - if (node->flag & SELECT) { - if (select) { - break; - } - select = node; + /* find link to select/highlight */ + bNodeLink *selink = nullptr; + float dist_best = FLT_MAX; + LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) { + if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { + continue; } - } - /* only one selected */ - if (node || select == nullptr) { - return false; - } - /* correct node */ - if (BLI_listbase_is_empty(&select->inputs) || BLI_listbase_is_empty(&select->outputs)) { - return false; - } + std::array<float2, NODE_LINK_RESOL + 1> coords; + node_link_bezier_points_evaluated(*link, coords); + float dist = FLT_MAX; - ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + /* loop over link coords to find shortest dist to + * upper left node edge of a intersected line segment */ + for (int i = 0; i < NODE_LINK_RESOL; i++) { + /* Check if the node rectangle intersects the line from this point to next one. */ + if (BLI_rctf_isect_segment(&node_to_insert->totr, coords[i], coords[i + 1])) { + /* store the shortest distance to the upper left edge + * of all intersections found so far */ + const float node_xy[] = {node_to_insert->totr.xmin, node_to_insert->totr.ymax}; - /* test node for links */ - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (node_link_is_hidden_or_dimmed(region->v2d, *link)) { - continue; + /* to be precise coords should be clipped by select->totr, + * but not done since there's no real noticeable difference */ + dist = min_ff(dist_squared_to_line_segment_v2(node_xy, coords[i], coords[i + 1]), dist); + } } - if (link->tonode == select || link->fromnode == select) { - return false; + /* we want the link with the shortest distance to node center */ + if (dist < dist_best) { + dist_best = dist; + selink = link; } } - *r_select = select; - return true; + if (selink) { + selink->flag |= NODE_LINKFLAG_HILITE; + } } -/** \} */ - -} // namespace blender::ed::space_node - -/* -------------------------------------------------------------------- */ -/** \name Node Line Intersection Test - * \{ */ - -void ED_node_link_intersect_test(ScrArea *area, int test) +void node_insert_on_link_flags_clear(bNodeTree &node_tree) { - using namespace blender::ed::space_node; + LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) { + link->flag &= ~NODE_LINKFLAG_HILITE; + } +} - bNode *select; - SpaceNode *snode; - if (!ed_node_link_conditions(area, test, &snode, &select)) { +void node_insert_on_link_flags(Main &bmain, SpaceNode &snode) +{ + bNodeTree &node_tree = *snode.edittree; + node_tree.ensure_topology_cache(); + bNode *node_to_insert = get_selected_node_for_insertion(node_tree); + if (!node_to_insert) { return; } - /* clear flags */ - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - link->flag &= ~NODE_LINKFLAG_HILITE; + /* Find link to insert on. */ + bNodeTree &ntree = *snode.edittree; + bNodeLink *old_link = nullptr; + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (link->flag & NODE_LINKFLAG_HILITE) { + old_link = link; + break; + } } - - if (test == 0) { + if (old_link == nullptr) { return; } - ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + old_link->flag &= ~NODE_LINKFLAG_HILITE; - /* find link to select/highlight */ - bNodeLink *selink = nullptr; - float dist_best = FLT_MAX; - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - float coord_array[NODE_LINK_RESOL + 1][2]; + bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN); + bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT); - if (node_link_is_hidden_or_dimmed(region->v2d, *link)) { - continue; + if (node_to_insert->type != NODE_REROUTE) { + /* Ignore main sockets when the types don't match. */ + if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr && + !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type), + static_cast<eNodeSocketDatatype>(best_input->type))) { + best_input = nullptr; } + if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr && + !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type), + static_cast<eNodeSocketDatatype>(old_link->tosock->type))) { + best_output = nullptr; + } + } - if (node_link_bezier_points(nullptr, nullptr, *link, coord_array, NODE_LINK_RESOL)) { - float dist = FLT_MAX; - - /* loop over link coords to find shortest dist to - * upper left node edge of a intersected line segment */ - for (int i = 0; i < NODE_LINK_RESOL; i++) { - /* Check if the node rectangle intersects the line from this point to next one. */ - if (BLI_rctf_isect_segment(&select->totr, coord_array[i], coord_array[i + 1])) { - /* store the shortest distance to the upper left edge - * of all intersections found so far */ - const float node_xy[] = {select->totr.xmin, select->totr.ymax}; + bNode *from_node = old_link->fromnode; + bNodeSocket *from_socket = old_link->fromsock; + bNode *to_node = old_link->tonode; - /* to be precise coord_array should be clipped by select->totr, - * but not done since there's no real noticeable difference */ - dist = min_ff( - dist_squared_to_line_segment_v2(node_xy, coord_array[i], coord_array[i + 1]), dist); - } - } + if (best_output != nullptr) { + /* Relink the "start" of the existing link to the newly inserted node. */ + old_link->fromnode = node_to_insert; + old_link->fromsock = best_output; + BKE_ntree_update_tag_link_changed(&ntree); + } + else { + nodeRemLink(&ntree, old_link); + } - /* we want the link with the shortest distance to node center */ - if (dist < dist_best) { - dist_best = dist; - selink = link; - } - } + if (best_input != nullptr) { + /* Add a new link that connects the node on the left to the newly inserted node. */ + nodeAddLink(&ntree, from_node, from_socket, node_to_insert, best_input); } - if (selink) { - selink->flag |= NODE_LINKFLAG_HILITE; + /* Set up insert offset data, it needs stuff from here. */ + if ((snode.flag & SNODE_SKIP_INSOFFSET) == 0) { + BLI_assert(snode.runtime->iofsd == nullptr); + NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__); + + iofsd->insert = node_to_insert; + iofsd->prev = from_node; + iofsd->next = to_node; + + snode.runtime->iofsd = iofsd; } + + ED_node_tree_propagate_change(nullptr, &bmain, &ntree); } /** \} */ -namespace blender::ed::space_node { - /* -------------------------------------------------------------------- */ /** \name Node Insert Offset Operator * \{ */ @@ -2042,13 +2086,13 @@ static int get_main_socket_priority(const bNodeSocket *socket) } /** Get the "main" socket based on the node declaration or an heuristic. */ -static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) +bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) { ListBase *sockets = (in_out == SOCK_IN) ? &node.inputs : &node.outputs; /* Try to get the main socket based on the socket declaration. */ nodeDeclarationEnsure(&ntree, &node); - const nodes::NodeDeclaration *node_decl = node.runtime->declaration; + const nodes::NodeDeclaration *node_decl = node.declaration(); if (node_decl != nullptr) { Span<nodes::SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs() : node_decl->outputs(); @@ -2098,7 +2142,7 @@ static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketIn return nullptr; } -static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata)) +static bool node_parents_offset_flag_enable_cb(bNode *parent, void * /*userdata*/) { /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */ parent->flag |= NODE_TEST; @@ -2323,10 +2367,10 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd, /** * Modal handler for insert offset animation */ -static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +static int node_insert_offset_modal(bContext *C, wmOperator *op, const wmEvent *event) { SpaceNode *snode = CTX_wm_space_node(C); - NodeInsertOfsData *iofsd = snode->runtime->iofsd; + NodeInsertOfsData *iofsd = static_cast<NodeInsertOfsData *>(op->customdata); bool redraw = false; if (!snode || event->type != TIMER || iofsd == nullptr || @@ -2334,7 +2378,7 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w return OPERATOR_PASS_THROUGH; } - const float duration = (float)iofsd->anim_timer->duration; + const float duration = float(iofsd->anim_timer->duration); /* handle animation - do this before possibly aborting due to duration, since * main thread might be so busy that node hasn't reached final position yet */ @@ -2366,7 +2410,6 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w node->anim_init_locx = node->anim_ofsx = 0.0f; } - snode->runtime->iofsd = nullptr; MEM_freeN(iofsd); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); @@ -2381,6 +2424,8 @@ static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent { const SpaceNode *snode = CTX_wm_space_node(C); NodeInsertOfsData *iofsd = snode->runtime->iofsd; + snode->runtime->iofsd = nullptr; + op->customdata = iofsd; if (!iofsd || !iofsd->insert) { return OPERATOR_CANCELLED; @@ -2419,84 +2464,3 @@ void NODE_OT_insert_offset(wmOperatorType *ot) /** \} */ } // namespace blender::ed::space_node - -/* -------------------------------------------------------------------- */ -/** \name Note Link Insert - * \{ */ - -void ED_node_link_insert(Main *bmain, ScrArea *area) -{ - using namespace blender::ed::space_node; - - bNode *node_to_insert; - SpaceNode *snode; - if (!ed_node_link_conditions(area, true, &snode, &node_to_insert)) { - return; - } - - /* Find link to insert on. */ - bNodeTree &ntree = *snode->edittree; - bNodeLink *old_link = nullptr; - LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { - if (link->flag & NODE_LINKFLAG_HILITE) { - old_link = link; - break; - } - } - if (old_link == nullptr) { - return; - } - - old_link->flag &= ~NODE_LINKFLAG_HILITE; - - bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN); - bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT); - - if (node_to_insert->type != NODE_REROUTE) { - /* Ignore main sockets when the types don't match. */ - if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr && - !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type), - static_cast<eNodeSocketDatatype>(best_input->type))) { - best_input = nullptr; - } - if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr && - !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type), - static_cast<eNodeSocketDatatype>(old_link->tosock->type))) { - best_output = nullptr; - } - } - - bNode *from_node = old_link->fromnode; - bNodeSocket *from_socket = old_link->fromsock; - bNode *to_node = old_link->tonode; - - if (best_output != nullptr) { - /* Relink the "start" of the existing link to the newly inserted node. */ - old_link->fromnode = node_to_insert; - old_link->fromsock = best_output; - BKE_ntree_update_tag_link_changed(&ntree); - } - else { - nodeRemLink(&ntree, old_link); - } - - if (best_input != nullptr) { - /* Add a new link that connects the node on the left to the newly inserted node. */ - nodeAddLink(&ntree, from_node, from_socket, node_to_insert, best_input); - } - - /* Set up insert offset data, it needs stuff from here. */ - if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) { - NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__); - - iofsd->insert = node_to_insert; - iofsd->prev = from_node; - iofsd->next = to_node; - - snode->runtime->iofsd = iofsd; - } - - ED_node_tree_propagate_change(nullptr, bmain, snode->edittree); -} - -/** \} */ |