Cleanup: Refactor node add reroute operator

Use C++ Map that supports the duplication natively. Use vectors instead
of linked lists, and adjust naming. Also remove combination of reroutes
for input sockets, which doesn't make sense since a reroute isn't
allowed to combine multiple input links into one output.
This commit is contained in:
2022-09-03 14:12:55 -05:00
parent 4b818b1513
commit 56193eccf6
3 changed files with 94 additions and 174 deletions

View File

@@ -5,6 +5,8 @@
* \ingroup spnode * \ingroup spnode
*/ */
#include <numeric>
#include "MEM_guardedalloc.h" #include "MEM_guardedalloc.h"
#include "DNA_collection_types.h" #include "DNA_collection_types.h"
@@ -20,6 +22,7 @@
#include "BKE_lib_id.h" #include "BKE_lib_id.h"
#include "BKE_main.h" #include "BKE_main.h"
#include "BKE_node.h" #include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h" #include "BKE_node_tree_update.h"
#include "BKE_report.h" #include "BKE_report.h"
#include "BKE_scene.h" #include "BKE_scene.h"
@@ -101,195 +104,113 @@ bNode *add_static_node(const bContext &C, int type, const float2 &location)
/** \name Add Reroute Operator /** \name Add Reroute Operator
* \{ */ * \{ */
static bool add_reroute_intersect_check(const bNodeLink &link, static std::optional<float2> path_link_intersection(const bNodeLink &link, const Span<float2> path)
float mcoords[][2],
int tot,
float2 &result)
{ {
std::array<float2, NODE_LINK_RESOL + 1> coords; std::array<float2, NODE_LINK_RESOL + 1> coords;
node_link_bezier_points_evaluated(link, coords); node_link_bezier_points_evaluated(link, coords);
for (int i = 0; i < tot - 1; i++) { for (const int i : path.index_range().drop_back(1)) {
for (int b = 0; b < NODE_LINK_RESOL; b++) { for (const int j : IndexRange(NODE_LINK_RESOL)) {
if (isect_seg_seg_v2_point(mcoords[i], mcoords[i + 1], coords[b], coords[b + 1], result) > float2 result;
0) { if (isect_seg_seg_v2_point(path[i], path[i + 1], coords[j], coords[j + 1], result) > 0) {
return true; return result;
} }
} }
} }
return false; return std::nullopt;
} }
struct bNodeSocketLink { struct RerouteCutsForSocket {
struct bNodeSocketLink *next, *prev; /* The output socket's owner node. */
bNode *from_node;
bNodeSocket *sock; /* Intersected links connected to the socket and their path intersection locations. */
bNodeLink *link; Map<bNodeLink *, float2> links;
float2 point;
}; };
static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb,
bNodeSocket *sock,
bNodeLink *link,
const float2 &point)
{
bNodeSocketLink *socklink, *prev;
socklink = MEM_cnew<bNodeSocketLink>("socket link");
socklink->sock = sock;
socklink->link = link;
copy_v2_v2(socklink->point, point);
for (prev = (bNodeSocketLink *)lb->last; prev; prev = prev->prev) {
if (prev->sock == sock) {
break;
}
}
BLI_insertlinkafter(lb, prev, socklink);
return socklink;
}
static bNodeSocketLink *add_reroute_do_socket_section(bContext *C,
bNodeSocketLink *socklink,
int in_out)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
bNode *reroute_node = nullptr;
bNodeSocket *cursock = socklink->sock;
float2 insert_point{0.0f, 0.0f};
int num_links;
num_links = 0;
while (socklink && socklink->sock == cursock) {
if (!(socklink->link->flag & NODE_LINK_TEST)) {
socklink->link->flag |= NODE_LINK_TEST;
/* create the reroute node for this cursock */
if (!reroute_node) {
reroute_node = nodeAddStaticNode(C, ntree, NODE_REROUTE);
/* add a single link to/from the reroute node to replace multiple links */
if (in_out == SOCK_OUT) {
nodeAddLink(ntree,
socklink->link->fromnode,
socklink->link->fromsock,
reroute_node,
(bNodeSocket *)reroute_node->inputs.first);
}
else {
nodeAddLink(ntree,
reroute_node,
(bNodeSocket *)reroute_node->outputs.first,
socklink->link->tonode,
socklink->link->tosock);
}
}
/* insert the reroute node into the link */
if (in_out == SOCK_OUT) {
socklink->link->fromnode = reroute_node;
socklink->link->fromsock = (bNodeSocket *)reroute_node->outputs.first;
}
else {
socklink->link->tonode = reroute_node;
socklink->link->tosock = (bNodeSocket *)reroute_node->inputs.first;
}
insert_point += socklink->point;
num_links++;
}
socklink = socklink->next;
}
if (num_links > 0) {
/* average cut point from shared links */
mul_v2_fl(insert_point, 1.0f / num_links);
reroute_node->locx = insert_point[0] / UI_DPI_FAC;
reroute_node->locy = insert_point[1] / UI_DPI_FAC;
LISTBASE_FOREACH_BACKWARD (bNode *, frame_node, &ntree->nodes) {
if (frame_node->type == NODE_FRAME && BLI_rctf_isect_pt_v(&frame_node->totr, insert_point)) {
nodeAttachNode(reroute_node, frame_node);
break;
}
}
}
return socklink;
}
static int add_reroute_exec(bContext *C, wmOperator *op) static int add_reroute_exec(bContext *C, wmOperator *op)
{ {
const ARegion &region = *CTX_wm_region(C);
SpaceNode &snode = *CTX_wm_space_node(C); SpaceNode &snode = *CTX_wm_space_node(C);
ARegion &region = *CTX_wm_region(C);
bNodeTree &ntree = *snode.edittree; bNodeTree &ntree = *snode.edittree;
float mcoords[256][2];
int i = 0;
/* Get the cut path */ Vector<float2> path;
RNA_BEGIN (op->ptr, itemptr, "path") { RNA_BEGIN (op->ptr, itemptr, "path") {
float2 loc; float2 loc_region;
RNA_float_get_array(&itemptr, "loc", loc); RNA_float_get_array(&itemptr, "loc", loc_region);
UI_view2d_region_to_view(&region.v2d, loc.x, loc.y, &mcoords[i][0], &mcoords[i][1]); float2 loc_view;
i++; UI_view2d_region_to_view(&region.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y);
if (i >= 256) { path.append(loc_view);
if (path.size() >= 256) {
break; break;
} }
} }
RNA_END; RNA_END;
if (i > 1) { if (path.is_empty()) {
ListBase output_links, input_links; return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
bNodeSocketLink *socklink; }
ntree.ensure_topology_cache();
const Vector<bNode *> frame_nodes = ntree.nodes_by_type("NodeFrame");
/* always first */
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
node_deselect_all(snode); node_deselect_all(snode);
/* Find cut links and sort them by sockets */ /* All link "cuts" that start at a particular output socket. Deduplicating new reroutes per
BLI_listbase_clear(&output_links); * output socket is useful because it allows reusing reroutes for connected intersections.
BLI_listbase_clear(&input_links); * Further deduplication using the second map means we only have one cut per link.*/
Map<bNodeSocket *, RerouteCutsForSocket> cuts_per_socket;
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) { if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
continue; continue;
} }
float2 insert_point; const std::optional<float2> intersection = path_link_intersection(*link, path);
if (add_reroute_intersect_check(*link, mcoords, i, insert_point)) { if (!intersection) {
add_reroute_insert_socket_link(&output_links, link->fromsock, link, insert_point); continue;
add_reroute_insert_socket_link(&input_links, link->tosock, link, insert_point); }
RerouteCutsForSocket &from_cuts = cuts_per_socket.lookup_or_add_default(link->fromsock);
from_cuts.from_node = link->fromnode;
from_cuts.links.add(link, *intersection);
}
/* Clear flag */ for (const auto item : cuts_per_socket.items()) {
link->flag &= ~NODE_LINK_TEST; const Map<bNodeLink *, float2> &cuts = item.value.links;
bNode *reroute = nodeAddStaticNode(C, &ntree, NODE_REROUTE);
nodeAddLink(&ntree,
item.value.from_node,
item.key,
reroute,
static_cast<bNodeSocket *>(reroute->inputs.first));
/* Reconnect links from the original output socket to the new reroute. */
for (bNodeLink *link : cuts.keys()) {
link->fromnode = reroute;
link->fromsock = static_cast<bNodeSocket *>(reroute->outputs.first);
BKE_ntree_update_tag_link_changed(&ntree);
}
/* Place the new reroute at the average location of all connected cuts. */
const float2 loc = std::accumulate(cuts.values().begin(), cuts.values().end(), float2(0)) /
cuts.size() / UI_DPI_FAC;
reroute->locx = loc.x;
reroute->locy = loc.y;
/* Attach the reroute node to frame nodes behind it. */
for (const int i : frame_nodes.index_range()) {
bNode *frame_node = frame_nodes.last(i);
if (BLI_rctf_isect_pt_v(&frame_node->totr, loc)) {
nodeAttachNode(reroute, frame_node);
break;
}
} }
} }
/* Create reroute nodes for intersected links.
* Only one reroute if links share the same input/output socket.
*/
socklink = (bNodeSocketLink *)output_links.first;
while (socklink) {
socklink = add_reroute_do_socket_section(C, socklink, SOCK_OUT);
}
socklink = (bNodeSocketLink *)input_links.first;
while (socklink) {
socklink = add_reroute_do_socket_section(C, socklink, SOCK_IN);
}
BLI_freelistN(&output_links);
BLI_freelistN(&input_links);
/* always last */
ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree); ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
return OPERATOR_FINISHED; return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
} }
void NODE_OT_add_reroute(wmOperatorType *ot) void NODE_OT_add_reroute(wmOperatorType *ot)

View File

@@ -1367,7 +1367,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
bNode *lastnode = (bNode *)ntree->nodes.last; bNode *lastnode = (bNode *)ntree->nodes.last;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) { if (node->flag & SELECT) {
bNode *new_node = blender::bke::node_copy_with_mapping( bNode *new_node = bke::node_copy_with_mapping(
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map); ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, new_node); node_map.add_new(node, new_node);
changed = true; changed = true;
@@ -2234,7 +2234,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
if (node->flag & SELECT) { if (node->flag & SELECT) {
/* No ID refcounting, this node is virtual, /* No ID refcounting, this node is virtual,
* detached from any actual Blender data currently. */ * detached from any actual Blender data currently. */
bNode *new_node = blender::bke::node_copy_with_mapping(nullptr, bNode *new_node = bke::node_copy_with_mapping(nullptr,
*node, *node,
LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_MAIN, LIB_ID_CREATE_NO_MAIN,
@@ -2372,7 +2372,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
/* copy nodes from clipboard */ /* copy nodes from clipboard */
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) { LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
bNode *new_node = blender::bke::node_copy_with_mapping( bNode *new_node = bke::node_copy_with_mapping(
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map); ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, new_node); node_map.add_new(node, new_node);
} }

View File

@@ -462,8 +462,7 @@ static bool node_group_separate_selected(
bNode *newnode; bNode *newnode;
if (make_copy) { if (make_copy) {
/* make a copy */ /* make a copy */
newnode = blender::bke::node_copy_with_mapping( newnode = bke::node_copy_with_mapping(&ngroup, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
&ngroup, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, newnode); node_map.add_new(node, newnode);
} }
else { else {