Cleanup: Simplify node editor socket picking

The main change is returning a socket pointer instead of using two
return arguments. Also use the topology cache instead of linked lists,
references over pointers, and slightly adjust whitespace.
This commit is contained in:
Hans Goudey 2023-01-03 13:34:51 -05:00
parent a6355a4542
commit e091291b5b
Notes: blender-bot 2023-02-14 06:46:23 +01:00
Referenced by commit 2ab72f6db8, Fix T103964: Assert on mouse hover of empty node editor
Referenced by commit 20b2d6fc71, Fix T103624: Last node is skipped for cursor and link picking
Referenced by issue #103637, Nodes can no longer be connected in node editors
Referenced by issue #103624, Regression: Nodes created by the "Use Nodes" button initially can't accept inputs
6 changed files with 118 additions and 136 deletions

View File

@ -2610,6 +2610,18 @@ int node_get_resize_cursor(NodeResizeDirection directions)
return WM_CURSOR_EDIT;
}
static const bNode *find_node_under_cursor(SpaceNode &snode, const float2 &cursor)
{
/* Check nodes front to back. */
const Span<bNode *> nodes = snode.edittree->all_nodes();
for (int i = nodes.index_range().last(); i > 0; i--) {
if (BLI_rctf_isect_pt(&nodes[i]->runtime->totr, cursor[0], cursor[1])) {
return nodes[i];
}
}
return nullptr;
}
void node_set_cursor(wmWindow &win, SpaceNode &snode, const float2 &cursor)
{
const bNodeTree *ntree = snode.edittree;
@ -2617,36 +2629,28 @@ void node_set_cursor(wmWindow &win, SpaceNode &snode, const float2 &cursor)
WM_cursor_set(&win, WM_CURSOR_DEFAULT);
return;
}
bNode *node;
bNodeSocket *sock;
int wmcursor = WM_CURSOR_DEFAULT;
if (node_find_indicated_socket(
snode, &node, &sock, cursor, (eNodeSocketInOut)(SOCK_IN | SOCK_OUT))) {
if (node_find_indicated_socket(snode, cursor, SOCK_IN | SOCK_OUT)) {
WM_cursor_set(&win, WM_CURSOR_DEFAULT);
return;
}
const bNode *node = find_node_under_cursor(snode, cursor);
if (!node) {
WM_cursor_set(&win, WM_CURSOR_DEFAULT);
return;
}
const NodeResizeDirection dir = node_get_resize_direction(node, cursor[0], cursor[1]);
if (node->is_frame() && dir == NODE_RESIZE_NONE) {
/* Indicate that frame nodes can be moved/selected on their borders. */
const rctf frame_inside = node_frame_rect_inside(*node);
if (!BLI_rctf_isect_pt(&frame_inside, cursor[0], cursor[1])) {
WM_cursor_set(&win, WM_CURSOR_NSEW_SCROLL);
return;
}
WM_cursor_set(&win, WM_CURSOR_DEFAULT);
return;
}
/* Check nodes front to back. */
for (node = (bNode *)ntree->nodes.last; node; node = node->prev) {
if (BLI_rctf_isect_pt(&node->runtime->totr, cursor[0], cursor[1])) {
break; /* First hit on node stops. */
}
}
if (node) {
NodeResizeDirection dir = node_get_resize_direction(node, cursor[0], cursor[1]);
wmcursor = node_get_resize_cursor(dir);
/* We want to indicate that Frame nodes can be moved/selected on their borders. */
if (node->type == NODE_FRAME && dir == NODE_RESIZE_NONE) {
const rctf frame_inside = node_frame_rect_inside(*node);
if (!BLI_rctf_isect_pt(&frame_inside, cursor[0], cursor[1])) {
wmcursor = WM_CURSOR_NSEW_SCROLL;
}
}
}
WM_cursor_set(&win, wmcursor);
WM_cursor_set(&win, node_get_resize_cursor(dir));
}
static void count_multi_input_socket_links(bNodeTree &ntree, SpaceNode &snode)

View File

@ -1205,24 +1205,21 @@ static bool cursor_isect_multi_input_socket(const float2 &cursor, const bNodeSoc
return false;
}
bool node_find_indicated_socket(SpaceNode &snode,
bNode **nodep,
bNodeSocket **sockp,
const float2 &cursor,
const eNodeSocketInOut in_out)
bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
const float2 &cursor,
const eNodeSocketInOut in_out)
{
rctf rect;
const float size_sock_padded = NODE_SOCKSIZE + 4;
*nodep = nullptr;
*sockp = nullptr;
snode.edittree->ensure_topology_cache();
const Span<bNode *> nodes = snode.edittree->all_nodes();
for (int i = nodes.index_range().last(); i > 0; i--) {
bNode &node = *nodes[i];
/* check if we click in a socket */
LISTBASE_FOREACH_BACKWARD (bNode *, node, &snode.edittree->nodes) {
BLI_rctf_init_pt_radius(&rect, cursor, size_sock_padded);
if (!(node->flag & NODE_HIDDEN)) {
if (!(node.flag & NODE_HIDDEN)) {
/* extra padding inside and out - allow dragging on the text areas too */
if (in_out == SOCK_IN) {
rect.xmax += NODE_SOCKSIZE;
@ -1235,37 +1232,31 @@ bool node_find_indicated_socket(SpaceNode &snode,
}
if (in_out & SOCK_IN) {
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
for (bNodeSocket *sock : node.input_sockets()) {
if (sock->is_visible()) {
const float2 location(sock->runtime->locx, sock->runtime->locy);
if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) {
if (sock->flag & SOCK_MULTI_INPUT && !(node.flag & NODE_HIDDEN)) {
if (cursor_isect_multi_input_socket(cursor, *sock)) {
if (!socket_is_occluded(location, *node, snode)) {
*nodep = node;
*sockp = sock;
return true;
if (!socket_is_occluded(location, node, snode)) {
return sock;
}
}
}
else if (BLI_rctf_isect_pt(&rect, location.x, location.y)) {
if (!socket_is_occluded(location, *node, snode)) {
*nodep = node;
*sockp = sock;
return true;
if (!socket_is_occluded(location, node, snode)) {
return sock;
}
}
}
}
}
if (in_out & SOCK_OUT) {
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
for (bNodeSocket *sock : node.output_sockets()) {
if (sock->is_visible()) {
const float2 location(sock->runtime->locx, sock->runtime->locy);
if (BLI_rctf_isect_pt(&rect, location.x, location.y)) {
if (!socket_is_occluded(location, *node, snode)) {
*nodep = node;
*sockp = sock;
return true;
if (!socket_is_occluded(location, node, snode)) {
return sock;
}
}
}
@ -1273,7 +1264,7 @@ bool node_find_indicated_socket(SpaceNode &snode,
}
}
return false;
return nullptr;
}
/** \} */

View File

@ -311,12 +311,9 @@ bool composite_node_editable(bContext *C);
bool node_has_hidden_sockets(bNode *node);
void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set);
int node_render_changed_exec(bContext *, wmOperator *);
/** Type is #SOCK_IN and/or #SOCK_OUT. */
bool node_find_indicated_socket(SpaceNode &snode,
bNode **nodep,
bNodeSocket **sockp,
const float2 &cursor,
eNodeSocketInOut in_out);
bNodeSocket *node_find_indicated_socket(SpaceNode &snode,
const float2 &cursor,
eNodeSocketInOut in_out);
float node_link_dim_factor(const View2D &v2d, const bNodeLink &link);
bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link);

View File

@ -124,27 +124,24 @@ static void pick_input_link_by_link_intersect(const bContext &C,
float2 drag_start;
RNA_float_get_array(op.ptr, "drag_start", drag_start);
bNode *node;
bNodeSocket *socket;
node_find_indicated_socket(*snode, &node, &socket, drag_start, SOCK_IN);
bNodeSocket *socket = node_find_indicated_socket(*snode, drag_start, SOCK_IN);
bNode &node = socket->owner_node();
/* Distance to test overlapping of cursor on link. */
const float cursor_link_touch_distance = 12.5f * UI_DPI_FAC;
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. */
std::array<float2, NODE_LINK_RESOL + 1> coords;
node_link_bezier_points_evaluated(*link, coords);
for (bNodeLink *link : socket->directly_linked_links()) {
/* Test if the cursor is near a link. */
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;
}
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;
}
}
}
@ -164,8 +161,8 @@ static void pick_input_link_by_link_intersect(const bContext &C,
link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
ED_area_tag_redraw(CTX_wm_area(&C));
if (!node_find_indicated_socket(*snode, &node, &socket, cursor, SOCK_IN)) {
pick_link(nldrag, *snode, node, *link_to_pick);
if (!node_find_indicated_socket(*snode, cursor, SOCK_IN)) {
pick_link(nldrag, *snode, &node, *link_to_pick);
}
}
}
@ -952,13 +949,11 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur
bNodeLinkDrag &nldrag = *static_cast<bNodeLinkDrag *>(op.customdata);
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)) {
if (bNodeSocket *tsock = node_find_indicated_socket(snode, cursor, SOCK_IN)) {
bNode &tnode = tsock->owner_node();
for (bNodeLink &link : nldrag.links) {
/* skip if socket is on the same node as the fromsock */
if (tnode && link.fromnode == tnode) {
if (link.fromnode == &tnode) {
continue;
}
@ -972,16 +967,16 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur
}
/* attach links to the socket */
link.tonode = tnode;
link.tonode = &tnode;
link.tosock = tsock;
nldrag.last_node_hovered_while_dragging_a_link = tnode;
nldrag.last_node_hovered_while_dragging_a_link = &tnode;
if (existing_link_connected_to_fromsock) {
link.multi_input_socket_index =
existing_link_connected_to_fromsock->multi_input_socket_index;
continue;
}
if (link.tosock && link.tosock->flag & SOCK_MULTI_INPUT) {
sort_multi_input_socket_links_with_drag(*tnode, link, cursor);
sort_multi_input_socket_links_with_drag(tnode, link, cursor);
}
}
}
@ -997,21 +992,20 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur
}
}
else {
bNode *tnode;
bNodeSocket *tsock = nullptr;
if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_OUT)) {
if (bNodeSocket *tsock = node_find_indicated_socket(snode, cursor, SOCK_OUT)) {
bNode &node = tsock->owner_node();
for (bNodeLink &link : nldrag.links) {
/* skip if this is already the target socket */
if (link.fromsock == tsock) {
continue;
}
/* skip if socket is on the same node as the fromsock */
if (tnode && link.tonode == tnode) {
if (link.tonode == &node) {
continue;
}
/* attach links to the socket */
link.fromnode = tnode;
link.fromnode = &node;
link.fromsock = tsock;
}
}
@ -1094,12 +1088,11 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(SpaceNode &snode,
const float2 cursor,
const bool detach)
{
/* output indicated? */
bNode *node;
bNodeSocket *sock;
if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
if (bNodeSocket *sock = node_find_indicated_socket(snode, cursor, SOCK_OUT)) {
bNode &node = sock->owner_node();
std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
nldrag->start_node = node;
nldrag->start_node = &node;
nldrag->start_socket = sock;
nldrag->start_link_count = nodeCountSocketLinks(snode.edittree, sock);
int link_limit = nodeSocketLinkLimit(sock);
@ -1121,16 +1114,16 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(SpaceNode &snode,
else {
/* dragged links are fixed on output side */
nldrag->in_out = SOCK_OUT;
nldrag->links.append(create_drag_link(*node, *sock));
nldrag->links.append(create_drag_link(node, *sock));
}
return nldrag;
}
/* or an input? */
if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
if (bNodeSocket *sock = node_find_indicated_socket(snode, cursor, SOCK_IN)) {
bNode &node = sock->owner_node();
std::unique_ptr<bNodeLinkDrag> nldrag = std::make_unique<bNodeLinkDrag>();
nldrag->last_node_hovered_while_dragging_a_link = node;
nldrag->start_node = node;
nldrag->last_node_hovered_while_dragging_a_link = &node;
nldrag->start_node = &node;
nldrag->start_socket = sock;
nldrag->start_link_count = nodeCountSocketLinks(snode.edittree, sock);
@ -1154,15 +1147,13 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(SpaceNode &snode,
nodeRemLink(snode.edittree, link_to_pick);
/* send changed event to original link->tonode */
if (node) {
BKE_ntree_update_tag_node_property(snode.edittree, node);
}
BKE_ntree_update_tag_node_property(snode.edittree, &node);
}
}
else {
/* dragged links are fixed on input side */
nldrag->in_out = SOCK_IN;
nldrag->links.append(create_drag_link(*node, *sock));
nldrag->links.append(create_drag_link(node, *sock));
}
return nldrag;
}

View File

@ -175,14 +175,9 @@ static bool is_position_over_node_or_socket(SpaceNode &snode, const float2 &mous
if (node_under_mouse_tweak(*snode.edittree, mouse)) {
return true;
}
bNode *node;
bNodeSocket *sock;
if (node_find_indicated_socket(
snode, &node, &sock, mouse, (eNodeSocketInOut)(SOCK_IN | SOCK_OUT))) {
if (node_find_indicated_socket(snode, mouse, SOCK_IN | SOCK_OUT)) {
return true;
}
return false;
}
@ -532,9 +527,8 @@ static bool node_mouse_select(bContext *C,
const Object *ob = CTX_data_active_object(C);
const Scene *scene = CTX_data_scene(C);
const wmWindowManager *wm = CTX_wm_manager(C);
bNode *node;
bNode *node = nullptr;
bNodeSocket *sock = nullptr;
bNodeSocket *tsock;
/* always do socket_select when extending selection. */
const bool socket_select = (params->sel_op == SEL_OP_XOR) ||
@ -551,7 +545,9 @@ static bool node_mouse_select(bContext *C,
if (socket_select) {
/* NOTE: unlike nodes #SelectPick_Params isn't fully supported. */
const bool extend = (params->sel_op == SEL_OP_XOR);
if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN)) {
sock = node_find_indicated_socket(snode, cursor, SOCK_IN);
if (sock) {
node = &sock->owner_node();
found = true;
node_was_selected = node->flag & SELECT;
@ -560,41 +556,43 @@ static bool node_mouse_select(bContext *C,
node_socket_toggle(node, *sock, true);
changed = true;
}
else if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_OUT)) {
found = true;
node_was_selected = node->flag & SELECT;
if (!changed) {
sock = node_find_indicated_socket(snode, cursor, SOCK_OUT);
if (sock) {
node = &sock->owner_node();
found = true;
node_was_selected = node->flag & SELECT;
if (sock->flag & SELECT) {
if (extend) {
node_socket_deselect(node, *sock, true);
changed = true;
if (sock->flag & SELECT) {
if (extend) {
node_socket_deselect(node, *sock, true);
changed = true;
}
}
}
else {
/* Only allow one selected output per node, for sensible linking.
* Allow selecting outputs from different nodes though, if extend is true. */
if (node) {
for (tsock = (bNodeSocket *)node->outputs.first; tsock; tsock = tsock->next) {
else {
/* Only allow one selected output per node, for sensible linking.
* Allow selecting outputs from different nodes though, if extend is true. */
for (bNodeSocket *tsock : node->output_sockets()) {
if (tsock == sock) {
continue;
}
node_socket_deselect(node, *tsock, true);
changed = true;
}
}
if (!extend) {
for (bNode *tnode : node_tree.all_nodes()) {
if (tnode == node) {
continue;
}
for (tsock = (bNodeSocket *)tnode->outputs.first; tsock; tsock = tsock->next) {
node_socket_deselect(tnode, *tsock, true);
changed = true;
if (!extend) {
for (bNode *tnode : node_tree.all_nodes()) {
if (tnode == node) {
continue;
}
for (bNodeSocket *tsock : tnode->output_sockets()) {
node_socket_deselect(tnode, *tsock, true);
changed = true;
}
}
}
node_socket_select(node, *sock);
changed = true;
}
node_socket_select(node, *sock);
changed = true;
}
}
}

View File

@ -254,6 +254,7 @@ typedef enum eNodeSocketInOut {
SOCK_IN = 1 << 0,
SOCK_OUT = 1 << 1,
} eNodeSocketInOut;
ENUM_OPERATORS(eNodeSocketInOut, SOCK_OUT);
/** #bNodeSocket.flag, first bit is selection. */
typedef enum eNodeSocketFlag {