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:
Hans Goudey 2022-09-03 14:12:55 -05:00
parent 4b818b1513
commit 56193eccf6
Notes: blender-bot 2023-02-14 03:52:45 +01:00
Referenced by issue #101628, new reroutes become children of existing far off frames
3 changed files with 94 additions and 174 deletions

View File

@ -5,6 +5,8 @@
* \ingroup spnode
*/
#include <numeric>
#include "MEM_guardedalloc.h"
#include "DNA_collection_types.h"
@ -20,6 +22,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_report.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
* \{ */
static bool add_reroute_intersect_check(const bNodeLink &link,
float mcoords[][2],
int tot,
float2 &result)
static std::optional<float2> path_link_intersection(const bNodeLink &link, const Span<float2> path)
{
std::array<float2, NODE_LINK_RESOL + 1> coords;
node_link_bezier_points_evaluated(link, coords);
for (int i = 0; i < tot - 1; i++) {
for (int b = 0; b < NODE_LINK_RESOL; b++) {
if (isect_seg_seg_v2_point(mcoords[i], mcoords[i + 1], coords[b], coords[b + 1], result) >
0) {
return true;
for (const int i : path.index_range().drop_back(1)) {
for (const int j : IndexRange(NODE_LINK_RESOL)) {
float2 result;
if (isect_seg_seg_v2_point(path[i], path[i + 1], coords[j], coords[j + 1], result) > 0) {
return result;
}
}
}
return false;
return std::nullopt;
}
struct bNodeSocketLink {
struct bNodeSocketLink *next, *prev;
bNodeSocket *sock;
bNodeLink *link;
float2 point;
struct RerouteCutsForSocket {
/* The output socket's owner node. */
bNode *from_node;
/* Intersected links connected to the socket and their path intersection locations. */
Map<bNodeLink *, float2> links;
};
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)
{
const ARegion &region = *CTX_wm_region(C);
SpaceNode &snode = *CTX_wm_space_node(C);
ARegion &region = *CTX_wm_region(C);
bNodeTree &ntree = *snode.edittree;
float mcoords[256][2];
int i = 0;
/* Get the cut path */
Vector<float2> path;
RNA_BEGIN (op->ptr, itemptr, "path") {
float2 loc;
RNA_float_get_array(&itemptr, "loc", loc);
UI_view2d_region_to_view(&region.v2d, loc.x, loc.y, &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(&region.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) {
ListBase output_links, input_links;
bNodeSocketLink *socklink;
/* always first */
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
node_deselect_all(snode);
/* Find cut links and sort them by sockets */
BLI_listbase_clear(&output_links);
BLI_listbase_clear(&input_links);
LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
continue;
}
float2 insert_point;
if (add_reroute_intersect_check(*link, mcoords, i, insert_point)) {
add_reroute_insert_socket_link(&output_links, link->fromsock, link, insert_point);
add_reroute_insert_socket_link(&input_links, link->tosock, link, insert_point);
/* Clear flag */
link->flag &= ~NODE_LINK_TEST;
}
}
/* 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);
return OPERATOR_FINISHED;
if (path.is_empty()) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
ntree.ensure_topology_cache();
const Vector<bNode *> frame_nodes = ntree.nodes_by_type("NodeFrame");
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
node_deselect_all(snode);
/* All link "cuts" that start at a particular output socket. Deduplicating new reroutes per
* output socket is useful because it allows reusing reroutes for connected intersections.
* 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) {
if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
continue;
}
const std::optional<float2> intersection = path_link_intersection(*link, path);
if (!intersection) {
continue;
}
RerouteCutsForSocket &from_cuts = cuts_per_socket.lookup_or_add_default(link->fromsock);
from_cuts.from_node = link->fromnode;
from_cuts.links.add(link, *intersection);
}
for (const auto item : cuts_per_socket.items()) {
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;
}
}
}
ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
return OPERATOR_FINISHED;
}
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;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
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);
node_map.add_new(node, new_node);
changed = true;
@ -2234,12 +2234,12 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
if (node->flag & SELECT) {
/* No ID refcounting, this node is virtual,
* detached from any actual Blender data currently. */
bNode *new_node = blender::bke::node_copy_with_mapping(nullptr,
*node,
LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_MAIN,
false,
socket_map);
bNode *new_node = bke::node_copy_with_mapping(nullptr,
*node,
LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_MAIN,
false,
socket_map);
node_map.add_new(node, new_node);
}
}
@ -2372,7 +2372,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
/* copy nodes from clipboard */
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);
node_map.add_new(node, new_node);
}

View File

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