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:
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
|
@ -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 ®ion = *CTX_wm_region(C);
|
||||
SpaceNode &snode = *CTX_wm_space_node(C);
|
||||
ARegion ®ion = *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(®ion.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(®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) {
|
||||
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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue