Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_node')
-rw-r--r--source/blender/editors/space_node/node_intern.h2
-rw-r--r--source/blender/editors/space_node/node_ops.c13
-rw-r--r--source/blender/editors/space_node/node_relationships.c355
3 files changed, 362 insertions, 8 deletions
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index 6b1d947bfca..b08c9b10eeb 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -167,6 +167,8 @@ void NODE_OT_detach(struct wmOperatorType *ot);
void NODE_OT_link_viewer(struct wmOperatorType *ot);
+void NODE_OT_insert_offset(wmOperatorType *ot);
+
/* node_edit.c */
void snode_notify(struct bContext *C, struct SpaceNode *snode);
void snode_dag_update(struct bContext *C, struct SpaceNode *snode);
diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c
index 474ad4db4af..7464fb77d90 100644
--- a/source/blender/editors/space_node/node_ops.c
+++ b/source/blender/editors/space_node/node_ops.c
@@ -92,6 +92,8 @@ void node_operatortypes(void)
WM_operatortype_append(NODE_OT_link_viewer);
+ WM_operatortype_append(NODE_OT_insert_offset);
+
WM_operatortype_append(NODE_OT_read_renderlayers);
WM_operatortype_append(NODE_OT_read_fullsamplelayers);
WM_operatortype_append(NODE_OT_render_changed);
@@ -147,6 +149,17 @@ void ED_operatormacros_node(void)
mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
RNA_boolean_set(mot->ptr, "release_confirm", true);
WM_operatortype_macro_define(ot, "NODE_OT_attach");
+ WM_operatortype_macro_define(ot, "NODE_OT_insert_offset");
+
+ /* NODE_OT_translate_attach with remove_on_canel set to true */
+ ot = WM_operatortype_append_macro("NODE_OT_translate_attach_remove_on_cancel", "Move and Attach",
+ "Move nodes and attach to frame",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_boolean_set(mot->ptr, "release_confirm", true);
+ RNA_boolean_set(mot->ptr, "remove_on_cancel", true);
+ WM_operatortype_macro_define(ot, "NODE_OT_attach");
+ WM_operatortype_macro_define(ot, "NODE_OT_insert_offset");
/* Note: Currently not in a default keymap or menu due to messy keymaps
* and tricky invoke functionality.
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c
index c8951a1172e..8af4b8c4b8d 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -37,6 +37,7 @@
#include "BLI_math.h"
#include "BLI_blenlib.h"
+#include "BLI_easing.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -45,6 +46,7 @@
#include "ED_node.h" /* own include */
#include "ED_screen.h"
#include "ED_render.h"
+#include "ED_util.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -53,6 +55,7 @@
#include "WM_types.h"
#include "UI_view2d.h"
+#include "UI_resources.h"
#include "BLF_translation.h"
@@ -66,6 +69,17 @@ typedef struct bNodeListItem {
struct bNode *node;
} bNodeListItem;
+typedef struct NodeInsertOfsData {
+ bNodeTree *ntree;
+ bNode *insert; /* inserted node */
+ bNode *prev, *next; /* prev/next node in the chain */
+ bNode *insert_parent;
+
+ wmTimer *anim_timer;
+
+ float offset_x; /* offset to apply to node chain */
+} NodeInsertOfsData;
+
static int sort_nodes_locx(const void *a, const void *b)
{
const bNodeListItem *nli1 = a;
@@ -1099,25 +1113,33 @@ void NODE_OT_join(wmOperatorType *ot)
/* ****************** Attach ******************* */
-static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+static bNode *node_find_frame_to_attach(ARegion *ar, const bNodeTree *ntree, const int mouse_xy[2])
{
- ARegion *ar = CTX_wm_region(C);
- SpaceNode *snode = CTX_wm_space_node(C);
- bNodeTree *ntree = snode->edittree;
bNode *frame;
float cursor[2];
-
+
/* convert mouse coordinates to v2d space */
- UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]);
+ UI_view2d_region_to_view(&ar->v2d, UNPACK2(mouse_xy), &cursor[0], &cursor[1]);
/* check nodes front to back */
for (frame = ntree->nodes.last; frame; frame = frame->prev) {
/* skip selected, those are the nodes we want to attach */
if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT))
continue;
- if (BLI_rctf_isect_pt(&frame->totr, cursor[0], cursor[1]))
- break;
+ if (BLI_rctf_isect_pt_v(&frame->totr, cursor))
+ return frame;
}
+
+ return NULL;
+}
+
+static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ ARegion *ar = CTX_wm_region(C);
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNodeTree *ntree = snode->edittree;
+ bNode *frame = node_find_frame_to_attach(ar, ntree, event->mval);
+
if (frame) {
bNode *node, *parent;
for (node = ntree->nodes.last; node; node = node->prev) {
@@ -1371,6 +1393,312 @@ static bNodeSocket *socket_best_match(ListBase *sockets)
return NULL;
}
+static bool node_parents_offset_flag_enable_cb(bNode *parent, void *UNUSED(userdata))
+{
+ /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
+ parent->flag |= NODE_TEST;
+
+ return true;
+}
+
+static void node_offset_apply(bNode *node, const float offset_x)
+{
+ /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
+ if ((node->flag & NODE_TEST) == 0) {
+ node->anim_init_locx = node->locx;
+ node->anim_ofsx = (offset_x / UI_DPI_FAC);
+ node->flag |= NODE_TEST;
+ }
+}
+
+static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, const float offset_x)
+{
+ bNode *node;
+
+ node_offset_apply(parent, offset_x);
+
+ /* flag all childs as offset to prevent them from being offset
+ * separately (they've already moved with the parent) */
+ for (node = data->ntree->nodes.first; node; node = node->next) {
+ if (nodeIsChildOf(parent, node)) {
+ /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */
+ node->flag |= NODE_TEST;
+ }
+ }
+}
+
+#define NODE_INSOFS_ANIM_DURATION 0.25f
+
+/**
+ * Callback that applies NodeInsertOfsData.offset_x to a node or its parent, similiar
+ * to node_link_insert_offset_output_chain_cb below, but with slightly different logic
+ */
+static bool node_link_insert_offset_frame_chain_cb(
+ bNode *fromnode, bNode *tonode,
+ void *userdata,
+ const bool reversed)
+{
+ NodeInsertOfsData *data = userdata;
+ bNode *ofs_node = reversed ? fromnode : tonode;
+
+ if (ofs_node->parent && ofs_node->parent != data->insert_parent) {
+ node_offset_apply(ofs_node->parent, data->offset_x);
+ }
+ else {
+ node_offset_apply(ofs_node, data->offset_x);
+ }
+
+ return true;
+}
+
+/**
+ * Applies NodeInsertOfsData.offset_x to all childs of \a parent
+ */
+static void node_link_insert_offset_frame_chains(
+ const bNodeTree *ntree, const bNode *parent,
+ NodeInsertOfsData *data,
+ const bool reversed)
+{
+ bNode *node;
+
+ for (node = ntree->nodes.first; node; node = node->next) {
+ if (nodeIsChildOf(parent, node)) {
+ nodeChainIter(ntree, node, node_link_insert_offset_frame_chain_cb, data, reversed);
+ }
+ }
+}
+
+/**
+ * Callback that applies NodeInsertOfsData.offset_x to a node or its parent,
+ * considering the logic needed for offseting nodes after link insert
+ */
+static bool node_link_insert_offset_chain_cb(
+ bNode *fromnode, bNode *tonode,
+ void *userdata,
+ const bool reversed)
+{
+ NodeInsertOfsData *data = userdata;
+ bNode *ofs_node = reversed ? fromnode : tonode;
+
+ if (data->insert_parent) {
+ if (ofs_node->parent && (ofs_node->parent->flag & NODE_TEST) == 0) {
+ node_parent_offset_apply(data, ofs_node->parent, data->offset_x);
+ node_link_insert_offset_frame_chains(data->ntree, ofs_node->parent, data, reversed);
+ }
+ else {
+ node_offset_apply(ofs_node, data->offset_x);
+ }
+
+ if (nodeIsChildOf(data->insert_parent, ofs_node) == false) {
+ data->insert_parent = NULL;
+ }
+ }
+ else if (ofs_node->parent) {
+ bNode *node = nodeFindRootParent(ofs_node);
+ node_offset_apply(node, data->offset_x);
+ }
+ else {
+ node_offset_apply(ofs_node, data->offset_x);
+ }
+
+ return true;
+}
+
+static void node_link_insert_offset_ntree(
+ NodeInsertOfsData *iofsd, ARegion *ar,
+ const int mouse_xy[2], const bool right_alignment)
+{
+ bNodeTree *ntree = iofsd->ntree;
+ bNode *insert = iofsd->insert;
+ bNode *prev = iofsd->prev, *next = iofsd->next;
+ bNode *init_parent = insert->parent; /* store old insert->parent for restoring later */
+ rctf totr_insert;
+
+ const float min_margin = U.node_margin * UI_DPI_FAC;
+ const float width = NODE_WIDTH(insert);
+ const bool needs_alignment = (next->totr.xmin - prev->totr.xmax) < (width + (min_margin * 2.0f));
+
+ float margin = width;
+ float dist, addval;
+
+
+ /* NODE_TEST will be used later, so disable for all nodes */
+ ntreeNodeFlagSet(ntree, NODE_TEST, false);
+
+ /* insert->totr isn't updated yet, so totr_insert is used to get the correct worldspace coords */
+ node_to_updated_rect(insert, &totr_insert);
+
+ /* frame attachement was't handled yet so we search the frame that the node will be attached to later */
+ insert->parent = node_find_frame_to_attach(ar, ntree, mouse_xy);
+
+ /* this makes sure nodes are also correctly offset when inserting a node on top of a frame
+ * without actually making it a part of the frame (because mouse isn't intersecting it)
+ * - logic here is similar to node_find_frame_to_attach */
+ if (!insert->parent ||
+ (prev->parent && (prev->parent == next->parent) && (prev->parent != insert->parent)))
+ {
+ bNode *frame;
+ rctf totr_frame;
+
+ /* check nodes front to back */
+ for (frame = ntree->nodes.last; frame; frame = frame->prev) {
+ /* skip selected, those are the nodes we want to attach */
+ if ((frame->type != NODE_FRAME) || (frame->flag & NODE_SELECT))
+ continue;
+
+ /* for some reason frame y coords aren't correct yet */
+ node_to_updated_rect(frame, &totr_frame);
+
+ if (BLI_rctf_isect_x(&totr_frame, totr_insert.xmin) &&
+ BLI_rctf_isect_x(&totr_frame, totr_insert.xmax))
+ {
+ if (BLI_rctf_isect_y(&totr_frame, totr_insert.ymin) ||
+ BLI_rctf_isect_y(&totr_frame, totr_insert.ymax))
+ {
+ /* frame isn't insert->parent actually, but this is needed to make offsetting
+ * nodes work correctly for above checked cases (it is restored later) */
+ insert->parent = frame;
+ break;
+ }
+ }
+ }
+ }
+
+
+ /* *** ensure offset at the left (or right for right_alignment case) of insert_node *** */
+
+ dist = right_alignment ? totr_insert.xmin - prev->totr.xmax : next->totr.xmin - totr_insert.xmax;
+ /* distance between insert_node and prev is smaller than min margin */
+ if (dist < min_margin) {
+ addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
+
+ node_offset_apply(insert, addval);
+
+ totr_insert.xmin += addval;
+ totr_insert.xmax += addval;
+ margin += min_margin;
+ }
+
+ /* *** ensure offset at the right (or left for right_alignment case) of insert_node *** */
+
+ dist = right_alignment ? next->totr.xmin - totr_insert.xmax : totr_insert.xmin - prev->totr.xmax;
+ /* distance between insert_node and next is smaller than min margin */
+ if (dist < min_margin) {
+ addval = (min_margin - dist) * (right_alignment ? 1.0f : -1.0f);
+ if (needs_alignment) {
+ bNode *offs_node = right_alignment ? next : prev;
+ if (!offs_node->parent ||
+ offs_node->parent == insert->parent ||
+ nodeIsChildOf(offs_node->parent, insert))
+ {
+ node_offset_apply(offs_node, addval);
+ }
+ else if (!insert->parent && offs_node->parent) {
+ node_offset_apply(offs_node->parent, addval);
+ }
+ margin = addval;
+ }
+ /* enough room is available, but we want to ensure the min margin at the right */
+ else {
+ /* offset inserted node so that min margin is kept at the right */
+ node_offset_apply(insert, -addval);
+ }
+ }
+
+
+ if (needs_alignment) {
+ iofsd->insert_parent = insert->parent;
+ iofsd->offset_x = margin;
+
+ /* flag all parents of insert as offset to prevent them from being offset */
+ nodeParentsIter(insert, node_parents_offset_flag_enable_cb, NULL);
+ /* iterate over entire chain and apply offsets */
+ nodeChainIter(ntree, right_alignment ? next : prev, node_link_insert_offset_chain_cb, iofsd, !right_alignment);
+ }
+
+ insert->parent = init_parent;
+}
+
+/**
+ * Modal handler for insert offset animation
+ */
+static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ NodeInsertOfsData *iofsd = snode->iofsd;
+ bNode *node;
+ const float duration = (float)iofsd->anim_timer->duration;
+
+ if (!snode || event->type != TIMER || iofsd->anim_timer != event->customdata)
+ return OPERATOR_PASS_THROUGH;
+
+ /* end timer + free insert offset data */
+ if (duration > NODE_INSOFS_ANIM_DURATION) {
+ WM_event_remove_timer(CTX_wm_manager(C), NULL, iofsd->anim_timer);
+
+ for (node = snode->edittree->nodes.first; node; node = node->next) {
+ node->anim_init_locx = node->anim_ofsx = 0.0f;
+ }
+
+ snode->iofsd = NULL;
+ MEM_freeN(iofsd);
+
+ return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
+ }
+
+ /* handle animation */
+ for (node = snode->edittree->nodes.first; node; node = node->next) {
+ if (node->anim_ofsx) {
+ node->locx = BLI_easing_cubic_ease_in_out(duration, node->anim_init_locx, node->anim_ofsx,
+ NODE_INSOFS_ANIM_DURATION);
+ }
+ }
+ ED_region_tag_redraw(CTX_wm_region(C));
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+#undef NODE_INSOFS_ANIM_DURATION
+
+static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const SpaceNode *snode = CTX_wm_space_node(C);
+ NodeInsertOfsData *iofsd = snode->iofsd;
+
+ if (!iofsd || !iofsd->insert)
+ return OPERATOR_CANCELLED;
+
+ BLI_assert((snode->flag & SNODE_SKIP_INSOFFSET) == 0);
+
+ iofsd->ntree = snode->edittree;
+ iofsd->anim_timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.02);
+
+ node_link_insert_offset_ntree(
+ iofsd, CTX_wm_region(C),
+ event->mval, (snode->insert_ofs_dir == SNODE_INSERTOFS_DIR_RIGHT));
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void NODE_OT_insert_offset(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Insert Offset";
+ ot->description = "Automatically offset nodes on insertion";
+ ot->idname = "NODE_OT_insert_offset";
+
+ /* callbacks */
+ ot->invoke = node_insert_offset_invoke;
+ ot->modal = node_insert_offset_modal;
+ ot->poll = ED_operator_node_editable;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+}
+
/* assumes link with NODE_LINKFLAG_HILITE set */
void ED_node_link_insert(ScrArea *sa)
{
@@ -1401,6 +1729,17 @@ void ED_node_link_insert(ScrArea *sa)
nodeAddLink(snode->edittree, select, best_output, node, sockto);
+ /* set up insert offset data, it needs stuff from here */
+ if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
+ NodeInsertOfsData *iofsd = MEM_callocN(sizeof(NodeInsertOfsData), __func__);
+
+ iofsd->insert = select;
+ iofsd->prev = link->fromnode;
+ iofsd->next = node;
+
+ snode->iofsd = iofsd;
+ }
+
ntreeUpdateTree(G.main, snode->edittree); /* needed for pointers */
snode_update(snode, select);
ED_node_tag_update_id(snode->id);