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:
@@ -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 ®ion = *CTX_wm_region(C);
|
||||||
SpaceNode &snode = *CTX_wm_space_node(C);
|
SpaceNode &snode = *CTX_wm_space_node(C);
|
||||||
ARegion ®ion = *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(®ion.v2d, loc.x, loc.y, &mcoords[i][0], &mcoords[i][1]);
|
float2 loc_view;
|
||||||
i++;
|
UI_view2d_region_to_view(®ion.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)
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user