Blender V4.3
node_group.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstdlib>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_anim_types.h"
14#include "DNA_node_types.h"
15
16#include "BLI_linklist.h"
17#include "BLI_listbase.h"
18#include "BLI_map.hh"
20#include "BLI_rand.hh"
21#include "BLI_set.hh"
22#include "BLI_string.h"
23#include "BLI_vector.hh"
24
25#include "BLT_translation.hh"
26
27#include "BKE_action.hh"
28#include "BKE_animsys.h"
29#include "BKE_context.hh"
30#include "BKE_lib_id.hh"
31#include "BKE_main.hh"
32#include "BKE_node_runtime.hh"
34#include "BKE_report.hh"
35
36#include "ANIM_action.hh"
37
39
40#include "ED_node.hh" /* own include */
41#include "ED_node.hh"
42#include "ED_node_preview.hh"
43#include "ED_render.hh"
44#include "ED_screen.hh"
45
46#include "RNA_access.hh"
47#include "RNA_define.hh"
48#include "RNA_path.hh"
49#include "RNA_prototypes.hh"
50
51#include "WM_api.hh"
52#include "WM_types.hh"
53
54#include "UI_resources.hh"
55
56#include "NOD_common.h"
57#include "NOD_composite.hh"
58#include "NOD_geometry.hh"
59#include "NOD_shader.h"
60#include "NOD_socket.hh"
61#include "NOD_texture.h"
62
63#include "node_intern.hh" /* own include */
64
66
67/* -------------------------------------------------------------------- */
72{
74 SpaceNode *snode = CTX_wm_space_node(C);
75
76 /* Group operators only defined for standard node tree types.
77 * Disabled otherwise to allow python-nodes define their own operators
78 * with same key-map. */
79 if (STR_ELEM(snode->tree_idname,
80 "ShaderNodeTree",
81 "CompositorNodeTree",
82 "TextureNodeTree",
83 "GeometryNodeTree"))
84 {
85 return true;
86 }
87 }
88 return false;
89}
90
92{
94 SpaceNode *snode = CTX_wm_space_node(C);
95
96 /* Group operators only defined for standard node tree types.
97 * Disabled otherwise to allow python-nodes define their own operators
98 * with same key-map. */
99 if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
100 ED_node_is_geometry(snode))
101 {
102 return true;
103 }
104 }
105 return false;
106}
107
108static const char *group_ntree_idname(bContext *C)
109{
110 SpaceNode *snode = CTX_wm_space_node(C);
111 return snode->tree_idname;
112}
113
115{
116 SpaceNode *snode = CTX_wm_space_node(C);
117
118 if (ED_node_is_shader(snode)) {
120 }
121 if (ED_node_is_compositor(snode)) {
123 }
124 if (ED_node_is_texture(snode)) {
126 }
127 if (ED_node_is_geometry(snode)) {
129 }
130
131 return "";
132}
133
134static bNode *node_group_get_active(bContext *C, const char *node_idname)
135{
136 SpaceNode *snode = CTX_wm_space_node(C);
137 bNode *node = bke::node_get_active(snode->edittree);
138
139 if (node && STREQ(node->idname, node_idname)) {
140 return node;
141 }
142 return nullptr;
143}
144
145/* Maps old to new identifiers for simulation input node pairing. */
146static void remap_pairing(bNodeTree &dst_tree,
147 Span<bNode *> nodes,
148 const Map<int32_t, int32_t> &identifier_map)
149{
150 for (bNode *dst_node : nodes) {
151 if (bke::all_zone_input_node_types().contains(dst_node->type)) {
152 const bke::bNodeZoneType &zone_type = *bke::zone_type_by_node_type(dst_node->type);
153 int &output_node_id = zone_type.get_corresponding_output_id(*dst_node);
154 if (output_node_id == 0) {
155 continue;
156 }
157 output_node_id = identifier_map.lookup_default(output_node_id, 0);
158 if (output_node_id == 0) {
160 }
161 }
162 }
163}
164
167/* -------------------------------------------------------------------- */
172{
173 SpaceNode *snode = CTX_wm_space_node(C);
174 const char *node_idname = node_group_idname(C);
175 const bool exit = RNA_boolean_get(op->ptr, "exit");
176
178
179 bNode *gnode = node_group_get_active(C, node_idname);
180
181 if (gnode && !exit) {
182 bNodeTree *ngroup = (bNodeTree *)gnode->id;
183
184 if (ngroup) {
185 ED_node_tree_push(snode, ngroup, gnode);
186 }
187 }
188 else {
189 ED_node_tree_pop(snode);
190 }
191
194
195 return OPERATOR_FINISHED;
196}
197
199{
200 /* identifiers */
201 ot->name = "Edit Group";
202 ot->description = "Edit node group";
203 ot->idname = "NODE_OT_group_edit";
204
205 /* api callbacks */
208
209 /* flags */
211
212 PropertyRNA *prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "");
214}
215
218/* -------------------------------------------------------------------- */
227 const StringRef dst_basepath)
228{
230 sizeof(*basepath_change), AT);
231 basepath_change->src_basepath = BLI_strdupn(src_basepath.data(), src_basepath.size());
232 basepath_change->dst_basepath = BLI_strdupn(dst_basepath.data(), dst_basepath.size());
233 return basepath_change;
234}
235
237{
238 if (basepath_change->src_basepath != basepath_change->dst_basepath) {
239 MEM_freeN((void *)basepath_change->src_basepath);
240 }
241 MEM_freeN((void *)basepath_change->dst_basepath);
242 MEM_freeN(basepath_change);
243}
244
246 const bNodeTree &ngroup,
247 const bNode &gnode,
248 const Map<int32_t, int32_t> &node_identifier_map)
249{
250 for (bNestedNodeRef &ref : ntree.nested_node_refs_span()) {
251 if (ref.path.node_id != gnode.identifier) {
252 continue;
253 }
254 const bNestedNodeRef *child_ref = ngroup.find_nested_node_ref(ref.path.id_in_node);
255 if (!child_ref) {
256 continue;
257 }
258 constexpr int32_t missing_id = -1;
259 const int32_t new_node_id = node_identifier_map.lookup_default(child_ref->path.node_id,
260 missing_id);
261 if (new_node_id == missing_id) {
262 continue;
263 }
264 ref.path.node_id = new_node_id;
265 ref.path.id_in_node = child_ref->path.id_in_node;
266 }
267}
268
272static void node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
273{
274 ListBase anim_basepaths = {nullptr, nullptr};
275 Vector<bNode *> nodes_delayed_free;
276 const bNodeTree *ngroup = reinterpret_cast<const bNodeTree *>(gnode->id);
277
278 /* `wgroup` is a temporary copy of the #NodeTree we're merging in
279 * - All of wgroup's nodes are copied across to their new home.
280 * - `ngroup` (i.e. the source NodeTree) is left unscathed.
281 * - Temp copy. do change ID user-count for the copies.
282 */
283 bNodeTree *wgroup = bke::node_tree_copy_tree(bmain, ngroup);
284
285 /* Add the nodes into the `ntree`. */
286 Vector<bNode *> new_nodes;
287 Map<int32_t, int32_t> node_identifier_map;
288 LISTBASE_FOREACH_MUTABLE (bNode *, node, &wgroup->nodes) {
289 new_nodes.append(node);
290 /* Remove interface nodes.
291 * This also removes remaining links to and from interface nodes.
292 */
293 if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {
294 /* We must delay removal since sockets will reference this node. see: #52092 */
295 nodes_delayed_free.append(node);
296 }
297
298 /* Keep track of this node's RNA "base" path (the part of the path identifying the node)
299 * if the old node-tree has animation data which potentially covers this node. */
300 std::optional<std::string> old_animation_basepath;
301 if (wgroup->adt) {
302 PointerRNA ptr = RNA_pointer_create(&wgroup->id, &RNA_Node, node);
303 old_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
304 }
305
306 /* migrate node */
307 BLI_remlink(&wgroup->nodes, node);
308 BLI_addtail(&ntree->nodes, node);
309 const int32_t old_identifier = node->identifier;
310 bke::node_unique_id(ntree, node);
311 bke::node_unique_name(ntree, node);
312 node_identifier_map.add(old_identifier, node->identifier);
313
315
316 if (wgroup->adt) {
317 PointerRNA ptr = RNA_pointer_create(&ntree->id, &RNA_Node, node);
318 const std::optional<std::string> new_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
319 BLI_addtail(&anim_basepaths,
320 animation_basepath_change_new(*old_animation_basepath, *new_animation_basepath));
321 }
322
323 if (!node->parent) {
324 node->locx += gnode->locx;
325 node->locy += gnode->locy;
326 }
327
328 node->flag |= NODE_SELECT;
329 }
330 wgroup->runtime->nodes_by_id.clear();
331
332 bNodeLink *glinks_first = (bNodeLink *)ntree->links.last;
333
334 /* Add internal links to the ntree */
335 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) {
336 BLI_remlink(&wgroup->links, link);
337 BLI_addtail(&ntree->links, link);
339 }
340
341 bNodeLink *glinks_last = (bNodeLink *)ntree->links.last;
342
343 /* and copy across the animation,
344 * note that the animation data's action can be nullptr here */
345 if (wgroup->adt) {
346 /* firstly, wgroup needs to temporary dummy action
347 * that can be destroyed, as it shares copies */
348 bAction *waction = reinterpret_cast<bAction *>(BKE_id_copy(bmain, &wgroup->adt->action->id));
349 const bool assign_ok = animrig::assign_action(waction, {wgroup->id, *wgroup->adt});
350 BLI_assert_msg(assign_ok, "assigning a copy of an already-assigned Action should work");
351 UNUSED_VARS_NDEBUG(assign_ok);
352
353 /* now perform the moving */
354 BKE_animdata_transfer_by_basepath(bmain, &wgroup->id, &ntree->id, &anim_basepaths);
355
356 /* paths + their wrappers need to be freed */
357 LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
358 animation_basepath_change_free(basepath_change);
359 }
360
361 /* free temp action too */
362 if (waction) {
363 const bool unassign_ok = animrig::unassign_action({wgroup->id, *wgroup->adt});
364 BLI_assert_msg(unassign_ok, "unassigning an Action that was just assigned should work");
365 UNUSED_VARS_NDEBUG(unassign_ok);
366 BKE_id_free(bmain, waction);
367 }
368 }
369
370 remap_pairing(*ntree, new_nodes, node_identifier_map);
371
372 /* free the group tree (takes care of user count) */
373 BKE_id_free(bmain, wgroup);
374
375 /* restore external links to and from the gnode */
376
377 /* input links */
378 if (glinks_first != nullptr) {
379 for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) {
380 if (link->fromnode->type == NODE_GROUP_INPUT) {
381 const char *identifier = link->fromsock->identifier;
382 int num_external_links = 0;
383
384 /* find external links to this input */
385 for (bNodeLink *tlink = (bNodeLink *)ntree->links.first; tlink != glinks_first->next;
386 tlink = tlink->next)
387 {
388 if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) {
390 ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock);
391 num_external_links++;
392 }
393 }
394
395 /* if group output is not externally linked,
396 * convert the constant input value to ensure somewhat consistent behavior */
397 if (num_external_links == 0) {
398 /* TODO */
399#if 0
400 bNodeSocket *sock = node_group_find_input_socket(gnode, identifier);
401 BLI_assert(sock);
402
403 nodeSocketCopy(
404 ntree, link->tosock->new_sock, link->tonode->new_node, ntree, sock, gnode);
405#endif
406 }
407 }
408 }
409
410 /* Also iterate over new links to cover passthrough links. */
411 glinks_last = (bNodeLink *)ntree->links.last;
412
413 /* output links */
414 for (bNodeLink *link = (bNodeLink *)ntree->links.first; link != glinks_first->next;
415 link = link->next)
416 {
417 if (link->fromnode == gnode) {
418 const char *identifier = link->fromsock->identifier;
419 int num_internal_links = 0;
420
421 /* find internal links to this output */
422 for (bNodeLink *tlink = glinks_first->next; tlink != glinks_last->next;
423 tlink = tlink->next)
424 {
425 /* only use active output node */
426 if (tlink->tonode->type == NODE_GROUP_OUTPUT && (tlink->tonode->flag & NODE_DO_OUTPUT)) {
427 if (STREQ(tlink->tosock->identifier, identifier)) {
429 ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock);
430 num_internal_links++;
431 }
432 }
433 }
434
435 /* if group output is not internally linked,
436 * convert the constant output value to ensure somewhat consistent behavior */
437 if (num_internal_links == 0) {
438 /* TODO */
439#if 0
440 bNodeSocket *sock = node_group_find_output_socket(gnode, identifier);
441 BLI_assert(sock);
442
443 nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode);
444#endif
445 }
446 }
447 }
448 }
449
450 for (bNode *node : nodes_delayed_free) {
451 bke::node_remove_node(bmain, ntree, node, false);
452 }
453
454 update_nested_node_refs_after_ungroup(*ntree, *ngroup, *gnode, node_identifier_map);
455
456 /* delete the group instance and dereference group tree */
457 bke::node_remove_node(bmain, ntree, gnode, true);
458}
459
461{
462 Main *bmain = CTX_data_main(C);
463 SpaceNode *snode = CTX_wm_space_node(C);
464 const char *node_idname = node_group_idname(C);
465
467
468 blender::Vector<bNode *> nodes_to_ungroup;
469 LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
470 if (node->flag & NODE_SELECT) {
471 if (STREQ(node->idname, node_idname)) {
472 if (node->id != nullptr) {
473 nodes_to_ungroup.append(node);
474 }
475 }
476 }
477 }
478 if (nodes_to_ungroup.is_empty()) {
479 return OPERATOR_CANCELLED;
480 }
481 for (bNode *node : nodes_to_ungroup) {
482 node_group_ungroup(bmain, snode->edittree, node);
483 }
485 return OPERATOR_FINISHED;
486}
487
489{
490 /* identifiers */
491 ot->name = "Ungroup";
492 ot->description = "Ungroup selected nodes";
493 ot->idname = "NODE_OT_group_ungroup";
494
495 /* api callbacks */
498
499 /* flags */
501}
502
505/* -------------------------------------------------------------------- */
513 Main &bmain, bNodeTree &ntree, bNodeTree &ngroup, const float2 &offset, const bool make_copy)
514{
515 node_deselect_all(ntree);
516
517 ListBase anim_basepaths = {nullptr, nullptr};
518
519 Map<bNode *, bNode *> node_map;
521 Map<int32_t, int32_t> node_identifier_map;
522
523 /* Add selected nodes into the ntree, ignoring interface nodes. */
524 VectorSet<bNode *> nodes_to_move = get_selected_nodes(ngroup);
525 nodes_to_move.remove_if(
526 [](const bNode *node) { return node->is_group_input() || node->is_group_output(); });
527
528 for (bNode *node : nodes_to_move) {
529 bNode *newnode;
530 if (make_copy) {
531 newnode = bke::node_copy_with_mapping(&ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
532 node_identifier_map.add(node->identifier, newnode->identifier);
533 }
534 else {
535 newnode = node;
536 BLI_remlink(&ngroup.nodes, newnode);
537 BLI_addtail(&ntree.nodes, newnode);
538 const int32_t old_identifier = node->identifier;
539 bke::node_unique_id(&ntree, newnode);
540 bke::node_unique_name(&ntree, newnode);
541 node_identifier_map.add(old_identifier, newnode->identifier);
542 }
543 node_map.add_new(node, newnode);
544
545 /* Keep track of this node's RNA "base" path (the part of the path identifying the node)
546 * if the old node-tree has animation data which potentially covers this node. */
547 if (ngroup.adt) {
548 PointerRNA ptr = RNA_pointer_create(&ngroup.id, &RNA_Node, newnode);
549 if (const std::optional<std::string> path = RNA_path_from_ID_to_struct(&ptr)) {
550 BLI_addtail(&anim_basepaths, animation_basepath_change_new(*path, *path));
551 }
552 }
553
554 /* ensure valid parent pointers, detach if parent stays inside the group */
555 if (newnode->parent && !(newnode->parent->flag & NODE_SELECT)) {
556 bke::node_detach_node(&ngroup, newnode);
557 }
558
559 if (!newnode->parent) {
560 newnode->locx += offset.x;
561 newnode->locy += offset.y;
562 }
563 }
564 if (!make_copy) {
566 }
567
568 /* add internal links to the ntree */
569 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup.links) {
570 const bool fromselect = (link->fromnode && nodes_to_move.contains(link->fromnode));
571 const bool toselect = (link->tonode && nodes_to_move.contains(link->tonode));
572
573 if (make_copy) {
574 /* make a copy of internal links */
575 if (fromselect && toselect) {
576 bke::node_add_link(&ntree,
577 node_map.lookup(link->fromnode),
578 socket_map.lookup(link->fromsock),
579 node_map.lookup(link->tonode),
580 socket_map.lookup(link->tosock));
581 }
582 }
583 else {
584 /* move valid links over, delete broken links */
585 if (fromselect && toselect) {
586 BLI_remlink(&ngroup.links, link);
587 BLI_addtail(&ntree.links, link);
588 }
589 else if (fromselect || toselect) {
590 bke::node_remove_link(&ngroup, link);
591 }
592 }
593 }
594
595 remap_pairing(ntree, nodes_to_move, node_identifier_map);
596
597 for (bNode *node : node_map.values()) {
598 bke::node_declaration_ensure(&ntree, node);
599 }
600
601 /* and copy across the animation,
602 * note that the animation data's action can be nullptr here */
603 if (ngroup.adt) {
604 /* now perform the moving */
605 BKE_animdata_transfer_by_basepath(&bmain, &ngroup.id, &ntree.id, &anim_basepaths);
606
607 /* paths + their wrappers need to be freed */
608 LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
609 animation_basepath_change_free(basepath_change);
610 }
611 }
612
614 if (!make_copy) {
616 }
617
618 return true;
619}
620
625
626/* Operator Property */
628 {NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"},
629 {NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"},
630 {0, nullptr, 0, nullptr, nullptr},
631};
632
634{
635 Main *bmain = CTX_data_main(C);
636 SpaceNode *snode = CTX_wm_space_node(C);
637 int type = RNA_enum_get(op->ptr, "type");
638
640
641 /* are we inside of a group? */
642 bNodeTree *ngroup = snode->edittree;
643 bNodeTree *nparent = ED_node_tree_get(snode, 1);
644 if (!nparent) {
645 BKE_report(op->reports, RPT_WARNING, "Not inside node group");
646 return OPERATOR_CANCELLED;
647 }
648 /* get node tree offset */
649 const float2 offset = space_node_group_offset(*snode);
650
651 switch (type) {
652 case NODE_GS_COPY:
653 if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, true)) {
654 BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
655 return OPERATOR_CANCELLED;
656 }
657 break;
658 case NODE_GS_MOVE:
659 if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, false)) {
660 BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
661 return OPERATOR_CANCELLED;
662 }
663 break;
664 }
665
666 /* switch to parent tree */
667 ED_node_tree_pop(snode);
668
670
671 return OPERATOR_FINISHED;
672}
673
674static int node_group_separate_invoke(bContext *C, wmOperator * /*op*/, const wmEvent * /*event*/)
675{
677 C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE);
678 uiLayout *layout = UI_popup_menu_layout(pup);
679
681 uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, ICON_NONE, "type", NODE_GS_COPY);
682 uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, ICON_NONE, "type", NODE_GS_MOVE);
683
684 UI_popup_menu_end(C, pup);
685
686 return OPERATOR_INTERFACE;
687}
688
690{
691 /* identifiers */
692 ot->name = "Separate";
693 ot->description = "Separate selected nodes from the node group";
694 ot->idname = "NODE_OT_group_separate";
695
696 /* api callbacks */
700
701 /* flags */
703
705}
706
709/* -------------------------------------------------------------------- */
714{
716 nodes_to_group.remove_if(
717 [](bNode *node) { return node->is_group_input() || node->is_group_output(); });
718 nodes_to_group.remove(group_node);
719 return nodes_to_group;
720}
721
723 const VectorSet<bNode *> &nodes_to_group,
724 const char *ntree_idname,
725 ReportList &reports)
726{
727 if (nodes_to_group.is_empty()) {
728 return false;
729 }
730 /* make a local pseudo node tree to pass to the node poll functions */
731 bNodeTree *ngroup = bke::node_tree_add_tree(nullptr, "Pseudo Node Group", ntree_idname);
732 BLI_SCOPED_DEFER([&]() {
734 MEM_freeN(ngroup);
735 });
736
737 /* check poll functions for selected nodes */
738 for (bNode *node : nodes_to_group) {
739 const char *disabled_hint = nullptr;
740 if (node->typeinfo->poll_instance &&
741 !node->typeinfo->poll_instance(node, ngroup, &disabled_hint))
742 {
743 if (disabled_hint) {
744 BKE_reportf(&reports,
746 "Cannot add node '%s' in a group:\n %s",
747 node->name,
748 disabled_hint);
749 }
750 else {
751 BKE_reportf(&reports, RPT_WARNING, "Cannot add node '%s' in a group", node->name);
752 }
753 return false;
754 }
755 }
756
757 /* check if all connections are OK, no unselected node has both
758 * inputs and outputs to a selection */
759 ntree.ensure_topology_cache();
760 for (bNode *node : ntree.all_nodes()) {
761 if (nodes_to_group.contains(node)) {
762 continue;
763 }
764 auto sockets_connected_to_group = [&](const Span<bNodeSocket *> sockets) {
765 for (const bNodeSocket *socket : sockets) {
766 for (const bNodeSocket *other_socket : socket->directly_linked_sockets()) {
767 if (nodes_to_group.contains(const_cast<bNode *>(&other_socket->owner_node()))) {
768 return true;
769 }
770 }
771 }
772 return false;
773 };
774 if (sockets_connected_to_group(node->input_sockets()) &&
775 sockets_connected_to_group(node->output_sockets()))
776 {
777 return false;
778 }
779 }
780 /* Check if zone pairs are fully selected.
781 * Zone input or output nodes can only be grouped together with the paired node. */
782 for (const bke::bNodeZoneType *zone_type : bke::all_zone_types()) {
783 for (bNode *input_node : ntree.nodes_by_type(zone_type->input_idname)) {
784 if (bNode *output_node = zone_type->get_corresponding_output(ntree, *input_node)) {
785 const bool input_selected = nodes_to_group.contains(input_node);
786 const bool output_selected = nodes_to_group.contains(output_node);
787 if (input_selected && !output_selected) {
788 BKE_reportf(&reports,
790 "Cannot add zone input node '%s' to a group without its paired output '%s'",
791 input_node->name,
792 output_node->name);
793 return false;
794 }
795 if (output_selected && !input_selected) {
796 BKE_reportf(&reports,
798 "Cannot add zone output node '%s' to a group without its paired input '%s'",
799 output_node->name,
800 input_node->name);
801 return false;
802 }
803 }
804 }
805 }
806
807 return true;
808}
809
810static void get_min_max_of_nodes(const Span<bNode *> nodes,
811 const bool use_size,
812 float2 &min,
813 float2 &max)
814{
815 if (nodes.is_empty()) {
816 min = float2(0);
817 max = float2(0);
818 return;
819 }
820
821 INIT_MINMAX2(min, max);
822 for (const bNode *node : nodes) {
823 const float2 node_offset = {node->offsetx, node->offsety};
824 float2 loc = bke::node_to_view(node, node_offset);
825 math::min_max(loc, min, max);
826 if (use_size) {
827 loc.x += node->width;
828 loc.y -= node->height;
829 math::min_max(loc, min, max);
830 }
831 }
832}
833
841 const bNodeSocket &socket)
842{
843 if (node_tree.has_available_link_cycle()) {
844 return socket;
845 }
846 const bNode &node = socket.owner_node();
847 if (!node.is_reroute()) {
848 return socket;
849 }
850 const bNodeSocket &other_socket = socket.in_out == SOCK_IN ? node.output_socket(0) :
851 node.input_socket(0);
852 if (!other_socket.is_logically_linked()) {
853 return socket;
854 }
855 return *other_socket.logically_linked_sockets().first();
856}
857
862static bool prefer_node_for_interface_name(const bNode &node)
863{
864 return node.is_group() || node.is_group_input() || node.is_group_output();
865}
866
868 bNodeTree &tree_for_interface,
869 const bNodeSocket &socket)
870{
871 /* The "example socket" has to have the same `in_out` status as the new interface socket. */
872 const bNodeSocket &socket_for_io = find_socket_to_use_for_interface(original_tree, socket);
873 const bNode &node_for_io = socket_for_io.owner_node();
874 const bNodeSocket &socket_for_name = prefer_node_for_interface_name(socket.owner_node()) ?
875 socket :
876 socket_for_io;
878 tree_for_interface, node_for_io, socket_for_io, socket_for_io.idname, socket_for_name.name);
879}
880
882 bNodeTree &ntree,
883 bNodeTree &group,
884 bNode &gnode,
885 const Map<int32_t, int32_t> &node_identifier_map)
886{
887 /* Update nested node references in the parent and child node tree. */
889 Vector<bNestedNodeRef> new_nested_node_refs;
890 /* Keep all nested node references that were in the group before. */
891 for (const bNestedNodeRef &ref : group.nested_node_refs_span()) {
892 new_nested_node_refs.append(ref);
893 }
894 Set<int32_t> used_nested_node_ref_ids;
895 for (const bNestedNodeRef &ref : group.nested_node_refs_span()) {
896 used_nested_node_ref_ids.add(ref.id);
897 }
898 Map<bNestedNodePath, int32_t> new_id_by_old_path;
899 for (bNestedNodeRef &ref : ntree.nested_node_refs_span()) {
900 const int32_t new_node_id = node_identifier_map.lookup_default(ref.path.node_id, -1);
901 if (new_node_id == -1) {
902 /* The node was not moved between node groups. */
903 continue;
904 }
905 bNestedNodeRef new_ref = ref;
906 new_ref.path.node_id = new_node_id;
907 /* Find new unique identifier for the nested node ref. */
908 while (true) {
909 const int32_t new_id = rng.get_int32(INT32_MAX);
910 if (used_nested_node_ref_ids.add(new_id)) {
911 new_ref.id = new_id;
912 break;
913 }
914 }
915 new_id_by_old_path.add_new(ref.path, new_ref.id);
916 new_nested_node_refs.append(new_ref);
917 /* Updated the nested node ref in the parent so that it points to the same node that is now
918 * inside of a nested group. */
919 ref.path.node_id = gnode.identifier;
920 ref.path.id_in_node = new_ref.id;
921 }
922 MEM_SAFE_FREE(group.nested_node_refs);
923 group.nested_node_refs = static_cast<bNestedNodeRef *>(
924 MEM_malloc_arrayN(new_nested_node_refs.size(), sizeof(bNestedNodeRef), __func__));
926 new_nested_node_refs.data(), new_nested_node_refs.size(), group.nested_node_refs);
927 group.nested_node_refs_num = new_nested_node_refs.size();
928}
929
931 bNodeTree &ntree,
932 bNode *gnode,
933 const VectorSet<bNode *> &nodes_to_move)
934{
935 Main *bmain = CTX_data_main(&C);
936 bNodeTree &group = *reinterpret_cast<bNodeTree *>(gnode->id);
937 BLI_assert(!nodes_to_move.contains(gnode));
938
939 node_deselect_all(group);
940
941 float2 min, max;
942 get_min_max_of_nodes(nodes_to_move, false, min, max);
943 const float2 center = math::midpoint(min, max);
944
945 float2 real_min, real_max;
946 get_min_max_of_nodes(nodes_to_move, true, real_min, real_max);
947
948 /* If only one node is selected expose all its sockets regardless of links. */
949 const bool expose_visible = nodes_to_move.size() == 1;
950
951 /* Reuse an existing output node or create a new one. */
952 group.ensure_topology_cache();
953 bNode *output_node = [&]() {
954 if (bNode *node = group.group_output_node()) {
955 return node;
956 }
957 bNode *output_node = bke::node_add_static_node(&C, &group, NODE_GROUP_OUTPUT);
958 output_node->locx = real_max[0] - center[0] + 50.0f;
959 return output_node;
960 }();
961
962 /* Create new group input node for easier organization of the new nodes inside the group. */
963 bNode *input_node = bke::node_add_static_node(&C, &group, NODE_GROUP_INPUT);
964 input_node->locx = real_min[0] - center[0] - 200.0f;
965
966 struct InputSocketInfo {
967 /* The unselected node the original link came from. */
968 bNode *from_node;
969 /* All the links that came from the socket on the unselected node. */
971 const bNodeTreeInterfaceSocket *interface_socket;
972 };
973
974 struct OutputLinkInfo {
975 bNodeLink *link;
976 const bNodeTreeInterfaceSocket *interface_socket;
977 };
978
979 struct NewInternalLinkInfo {
980 bNode *node;
981 bNodeSocket *socket;
982 const bNodeTreeInterfaceSocket *interface_socket;
983 };
984
985 /* Map from single non-selected output sockets to potentially many selected input sockets. */
987 Vector<OutputLinkInfo> output_links;
988 Set<bNodeLink *> internal_links_to_move;
989 Set<bNodeLink *> links_to_remove;
990 /* Map old to new node identifiers. */
991 Map<int32_t, int32_t> node_identifier_map;
992 Vector<NewInternalLinkInfo> new_internal_links;
993
994 ntree.ensure_topology_cache();
995 /* Add all outputs first. */
996 for (bNode *node : nodes_to_move) {
997 for (bNodeSocket *output_socket : node->output_sockets()) {
998 if (!output_socket->is_available() || output_socket->is_hidden()) {
999 for (bNodeLink *link : output_socket->directly_linked_links()) {
1000 links_to_remove.add(link);
1001 }
1002 continue;
1003 }
1004
1005 for (bNodeLink *link : output_socket->directly_linked_links()) {
1006 if (bke::node_link_is_hidden(link)) {
1007 links_to_remove.add(link);
1008 continue;
1009 }
1010 if (link->tonode == gnode) {
1011 links_to_remove.add(link);
1012 continue;
1013 }
1014 if (nodes_to_move.contains(link->tonode)) {
1015 internal_links_to_move.add(link);
1016 continue;
1017 }
1019 ntree, group, *link->fromsock);
1020 if (io_socket) {
1021 output_links.append({link, io_socket});
1022 }
1023 else {
1024 links_to_remove.add(link);
1025 }
1026 }
1027 if (expose_visible && !output_socket->is_directly_linked()) {
1029 group, *node, *output_socket);
1030 if (io_socket) {
1031 new_internal_links.append({node, output_socket, io_socket});
1032 }
1033 }
1034 }
1035 }
1036 /* Now add all inputs. */
1037 for (bNode *node : nodes_to_move) {
1038 for (bNodeSocket *input_socket : node->input_sockets()) {
1039 if (!input_socket->is_available() || input_socket->is_hidden()) {
1040 for (bNodeLink *link : input_socket->directly_linked_links()) {
1041 links_to_remove.add(link);
1042 }
1043 continue;
1044 }
1045
1046 for (bNodeLink *link : input_socket->directly_linked_links()) {
1047 if (bke::node_link_is_hidden(link)) {
1048 links_to_remove.add(link);
1049 continue;
1050 }
1051 if (link->fromnode == gnode) {
1052 links_to_remove.add(link);
1053 continue;
1054 }
1055 if (nodes_to_move.contains(link->fromnode)) {
1056 internal_links_to_move.add(link);
1057 continue;
1058 }
1059 InputSocketInfo &info = input_links.lookup_or_add_default(link->fromsock);
1060 info.from_node = link->fromnode;
1061 info.links.append(link);
1062 if (!info.interface_socket) {
1063 info.interface_socket = add_interface_from_socket(ntree, group, *link->tosock);
1064 }
1065 }
1066 if (expose_visible && !input_socket->is_directly_linked()) {
1068 group, *node, *input_socket);
1069 if (io_socket) {
1070 new_internal_links.append({node, input_socket, io_socket});
1071 }
1072 }
1073 }
1074 }
1075
1076 /* Un-parent nodes when only the parent or child moves into the group. */
1077 for (bNode *node : ntree.all_nodes()) {
1078 if (node->parent && nodes_to_move.contains(node->parent) && !nodes_to_move.contains(node)) {
1079 bke::node_detach_node(&ntree, node);
1080 }
1081 }
1082 for (bNode *node : nodes_to_move) {
1083 if (node->parent && !nodes_to_move.contains(node->parent)) {
1084 bke::node_detach_node(&ntree, node);
1085 }
1086 }
1087
1088 /* Move animation data from the parent tree to the group. */
1089 if (ntree.adt) {
1090 ListBase anim_basepaths = {nullptr, nullptr};
1091 for (bNode *node : nodes_to_move) {
1092 PointerRNA ptr = RNA_pointer_create(&ntree.id, &RNA_Node, node);
1093 if (const std::optional<std::string> path = RNA_path_from_ID_to_struct(&ptr)) {
1094 BLI_addtail(&anim_basepaths, animation_basepath_change_new(*path, *path));
1095 }
1096 }
1097 BKE_animdata_transfer_by_basepath(bmain, &ntree.id, &group.id, &anim_basepaths);
1098
1099 LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
1100 animation_basepath_change_free(basepath_change);
1101 }
1102 }
1103
1104 /* Move nodes into the group. */
1105 for (bNode *node : nodes_to_move) {
1106 const int32_t old_identifier = node->identifier;
1107
1108 BLI_remlink(&ntree.nodes, node);
1109 BLI_addtail(&group.nodes, node);
1110 bke::node_unique_id(&group, node);
1111 bke::node_unique_name(&group, node);
1112
1113 node_identifier_map.add(old_identifier, node->identifier);
1114
1116 BKE_ntree_update_tag_node_new(&group, node);
1117 }
1119
1120 /* Update input and output node first, since the group node declaration can depend on them. */
1122 nodes::update_node_declaration_and_sockets(group, *output_node);
1123
1124 /* move nodes in the group to the center */
1125 for (bNode *node : nodes_to_move) {
1126 if (!node->parent) {
1127 node->locx -= center[0];
1128 node->locy -= center[1];
1129 }
1130 }
1131
1132 for (bNodeLink *link : internal_links_to_move) {
1133 BLI_remlink(&ntree.links, link);
1134 BLI_addtail(&group.links, link);
1136 BKE_ntree_update_tag_link_added(&group, link);
1137 }
1138
1139 for (bNodeLink *link : links_to_remove) {
1140 bke::node_remove_link(&ntree, link);
1141 }
1142
1143 /* Handle links to the new group inputs. */
1144 for (const auto item : input_links.items()) {
1145 const StringRefNull interface_identifier = item.value.interface_socket->identifier;
1146 bNodeSocket *input_socket = node_group_input_find_socket(input_node,
1147 interface_identifier.c_str());
1148
1149 for (bNodeLink *link : item.value.links) {
1150 /* Move the link into the new group, connected from the input node to the original socket. */
1151 BLI_remlink(&ntree.links, link);
1152 BLI_addtail(&group.links, link);
1154 BKE_ntree_update_tag_link_added(&group, link);
1155 link->fromnode = input_node;
1156 link->fromsock = input_socket;
1157 }
1158 }
1159
1160 /* Handle links to new group outputs. */
1161 for (const OutputLinkInfo &info : output_links) {
1162 /* Create a new link inside of the group. */
1163 const StringRefNull io_identifier = info.interface_socket->identifier;
1164 bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier.c_str());
1165 bke::node_add_link(&group, info.link->fromnode, info.link->fromsock, output_node, output_sock);
1166 }
1167
1168 /* Handle new links inside the group. */
1169 for (const NewInternalLinkInfo &info : new_internal_links) {
1170 const StringRefNull io_identifier = info.interface_socket->identifier;
1171 if (info.socket->in_out == SOCK_IN) {
1172 bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier.c_str());
1173 bke::node_add_link(&group, input_node, input_socket, info.node, info.socket);
1174 }
1175 else {
1176 bNodeSocket *output_socket = node_group_output_find_socket(output_node,
1177 io_identifier.c_str());
1178 bke::node_add_link(&group, info.node, info.socket, output_node, output_socket);
1179 }
1180 }
1181
1182 remap_pairing(group, nodes_to_move, node_identifier_map);
1183
1184 if (group.type == NTREE_GEOMETRY) {
1186 }
1188
1189 /* Add new links to inputs outside of the group. */
1190 for (const auto item : input_links.items()) {
1191 const StringRefNull interface_identifier = item.value.interface_socket->identifier;
1192 bNodeSocket *group_node_socket = node_group_find_input_socket(gnode,
1193 interface_identifier.c_str());
1194 bke::node_add_link(&ntree, item.value.from_node, item.key, gnode, group_node_socket);
1195 }
1196
1197 /* Add new links to outputs outside the group. */
1198 for (const OutputLinkInfo &info : output_links) {
1199 /* Reconnect the link to the group node instead of the node now inside the group. */
1200 info.link->fromnode = gnode;
1201 info.link->fromsock = node_group_find_output_socket(gnode, info.interface_socket->identifier);
1202 }
1203
1204 update_nested_node_refs_after_moving_nodes_into_group(ntree, group, *gnode, node_identifier_map);
1205
1206 ED_node_tree_propagate_change(&C, bmain, nullptr);
1207}
1208
1210 bNodeTree &ntree,
1211 const VectorSet<bNode *> &nodes_to_group,
1212 const char *ntype,
1213 const char *ntreetype)
1214{
1215 Main *bmain = CTX_data_main(&C);
1216
1217 float2 min, max;
1218 get_min_max_of_nodes(nodes_to_group, false, min, max);
1219
1220 /* New node-tree. */
1221 bNodeTree *ngroup = bke::node_tree_add_tree(bmain, "NodeGroup", ntreetype);
1222
1223 BKE_id_move_to_same_lib(*bmain, ngroup->id, ntree.id);
1224
1225 /* make group node */
1226 bNode *gnode = bke::node_add_node(&C, &ntree, ntype);
1227 gnode->id = (ID *)ngroup;
1228
1229 gnode->locx = 0.5f * (min[0] + max[0]);
1230 gnode->locy = 0.5f * (min[1] + max[1]);
1231
1232 node_group_make_insert_selected(C, ntree, gnode, nodes_to_group);
1233
1234 return gnode;
1235}
1236
1238{
1239 SpaceNode &snode = *CTX_wm_space_node(C);
1240 bNodeTree &ntree = *snode.edittree;
1241 const char *ntree_idname = group_ntree_idname(C);
1242 const char *node_idname = node_group_idname(C);
1243 Main *bmain = CTX_data_main(C);
1244
1246
1247 VectorSet<bNode *> nodes_to_group = get_nodes_to_group(ntree, nullptr);
1248 if (!node_group_make_test_selected(ntree, nodes_to_group, ntree_idname, *op->reports)) {
1249 return OPERATOR_CANCELLED;
1250 }
1251
1252 bNode *gnode = node_group_make_from_nodes(*C, ntree, nodes_to_group, node_idname, ntree_idname);
1253
1254 if (gnode) {
1255 bNodeTree *ngroup = (bNodeTree *)gnode->id;
1256
1257 bke::node_set_active(&ntree, gnode);
1258 if (ngroup) {
1259 ED_node_tree_push(&snode, ngroup, gnode);
1260 }
1261 }
1262
1263 WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr);
1264
1265 /* We broke relations in node tree, need to rebuild them in the graphs. */
1267
1268 return OPERATOR_FINISHED;
1269}
1270
1272{
1273 /* identifiers */
1274 ot->name = "Make Group";
1275 ot->description = "Make group from selected nodes";
1276 ot->idname = "NODE_OT_group_make";
1277
1278 /* api callbacks */
1281
1282 /* flags */
1284}
1285
1288/* -------------------------------------------------------------------- */
1293{
1294 SpaceNode *snode = CTX_wm_space_node(C);
1295 bNodeTree *ntree = snode->edittree;
1296 const char *node_idname = node_group_idname(C);
1297
1299
1300 bNode *gnode = node_group_get_active(C, node_idname);
1301 if (!gnode || !gnode->id) {
1302 return OPERATOR_CANCELLED;
1303 }
1304
1305 bNodeTree *ngroup = reinterpret_cast<bNodeTree *>(gnode->id);
1306 VectorSet<bNode *> nodes_to_group = get_nodes_to_group(*ntree, gnode);
1307
1308 /* Make sure that there won't be a node group containing itself afterwards. */
1309 for (const bNode *group : nodes_to_group) {
1310 if (!group->is_group() || group->id == nullptr) {
1311 continue;
1312 }
1313 if (bke::node_tree_contains_tree(reinterpret_cast<bNodeTree *>(group->id), ngroup)) {
1315 op->reports, RPT_WARNING, "Cannot insert group '%s' in '%s'", group->name, gnode->name);
1316 return OPERATOR_CANCELLED;
1317 }
1318 }
1319
1320 if (!node_group_make_test_selected(*ntree, nodes_to_group, ngroup->idname, *op->reports)) {
1321 return OPERATOR_CANCELLED;
1322 }
1323
1324 node_group_make_insert_selected(*C, *ntree, gnode, nodes_to_group);
1325
1326 bke::node_set_active(ntree, gnode);
1327 ED_node_tree_push(snode, ngroup, gnode);
1328
1329 return OPERATOR_FINISHED;
1330}
1331
1333{
1334 /* identifiers */
1335 ot->name = "Group Insert";
1336 ot->description = "Insert selected nodes into a node group";
1337 ot->idname = "NODE_OT_group_insert";
1338
1339 /* api callbacks */
1342
1343 /* flags */
1345}
1346
1349/* -------------------------------------------------------------------- */
1354{
1355 SpaceNode *snode = CTX_wm_space_node(C);
1356 if (!snode) {
1357 return false;
1358 }
1359 bNodeTree *ntree = snode->edittree;
1360 if (!ntree) {
1361 return false;
1362 }
1363 if (!ID_IS_EDITABLE(ntree)) {
1364 return false;
1365 }
1366 if (snode->nodetree == snode->edittree) {
1367 /* Top-level node group does not have enough context to set the node width. */
1368 CTX_wm_operator_poll_msg_set(C, "There is no parent group node in this context");
1369 return false;
1370 }
1371 return true;
1372}
1373
1375{
1376 SpaceNode *snode = CTX_wm_space_node(C);
1377 bNodeTree *ntree = snode->edittree;
1378
1379 bNodeTreePath *last_path_item = static_cast<bNodeTreePath *>(snode->treepath.last);
1380 bNodeTreePath *parent_path_item = last_path_item->prev;
1381 if (!parent_path_item) {
1382 return OPERATOR_CANCELLED;
1383 }
1384 bNodeTree *parent_ntree = parent_path_item->nodetree;
1385 if (!parent_ntree) {
1386 return OPERATOR_CANCELLED;
1387 }
1388 parent_ntree->ensure_topology_cache();
1389 bNode *parent_node = bke::node_find_node_by_name(parent_ntree, last_path_item->node_name);
1390 if (!parent_node) {
1391 return OPERATOR_CANCELLED;
1392 }
1393 ntree->default_group_node_width = parent_node->width;
1395 return OPERATOR_CANCELLED;
1396}
1397
1399{
1400 /* identifiers */
1401 ot->name = "Set Default Group Node Width";
1402 ot->description = "Set the width based on the parent group node in the current context";
1403 ot->idname = "NODE_OT_default_group_width_set";
1404
1405 /* api callbacks */
1408
1409 /* flags */
1411}
1412
1415} // namespace blender::ed::space_node
Functions and classes to work with Actions.
Blender kernel action and pose functionality.
void BKE_animdata_transfer_by_basepath(struct Main *bmain, struct ID *srcID, struct ID *dstID, struct ListBase *basepaths)
Definition anim_data.cc:694
SpaceNode * CTX_wm_space_node(const bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
@ LIB_ID_COPY_DEFAULT
void BKE_id_free(Main *bmain, void *idv)
void BKE_id_move_to_same_lib(Main &bmain, ID &id, const ID &owner_id)
Definition lib_id.cc:862
ID * BKE_id_copy(Main *bmain, const ID *id)
Definition lib_id.cc:765
#define NODE_GROUP_OUTPUT
Definition BKE_node.hh:806
#define NODE_GROUP_INPUT
Definition BKE_node.hh:805
void BKE_ntree_update_tag_node_new(bNodeTree *ntree, bNode *node)
void BKE_ntree_update_tag_all(bNodeTree *ntree)
void BKE_ntree_update_tag_link_removed(bNodeTree *ntree)
void BKE_ntree_update_tag_node_removed(bNodeTree *ntree)
void BKE_ntree_update_tag_link_added(bNodeTree *ntree, bNodeLink *link)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
#define BLI_SCOPED_DEFER(function_to_defer)
#define STR_ELEM(...)
Definition BLI_string.h:653
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.c:29
#define INIT_MINMAX2(min, max)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define AT
#define STREQ(a, b)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
void DEG_relations_tag_update(Main *bmain)
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
@ NTREE_GEOMETRY
@ NODE_DO_OUTPUT
@ NODE_SELECT
@ SOCK_IN
void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *ntree)
Definition node_edit.cc:492
bNodeTree * ED_node_tree_get(SpaceNode *snode, int level)
bool ED_node_is_compositor(const SpaceNode *snode)
Definition node_edit.cc:523
void ED_node_tree_push(SpaceNode *snode, bNodeTree *ntree, bNode *gnode)
bool ED_node_is_shader(SpaceNode *snode)
Definition node_edit.cc:528
void ED_node_tree_pop(SpaceNode *snode)
bool ED_node_is_geometry(SpaceNode *snode)
Definition node_edit.cc:538
bool ED_node_is_texture(SpaceNode *snode)
Definition node_edit.cc:533
void ED_preview_kill_jobs(wmWindowManager *wm, Main *bmain)
bool ED_operator_node_editable(bContext *C)
bool ED_operator_node_active(bContext *C)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
struct blender::bke::bNodeTreeType * ntreeType_Shader
struct blender::bke::bNodeTreeType * ntreeType_Texture
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
void uiItemEnumO(uiLayout *layout, const char *opname, const char *name, int icon, const char *propname, int value)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_NODE
Definition WM_types.hh:361
#define NC_SCENE
Definition WM_types.hh:345
#define NA_ADDED
Definition WM_types.hh:552
#define ND_NODES
Definition WM_types.hh:403
#define NA_EDITED
Definition WM_types.hh:550
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:225
#define ND_NODE_GIZMO
Definition WM_types.hh:482
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:601
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:531
ValueIterator values() const
Definition BLI_map.hh:846
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:241
ItemIterator items() const
Definition BLI_map.hh:864
static RandomNumberGenerator from_random_seed()
Definition rand.cc:365
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr bool contains(const T &value) const
Definition BLI_span.hh:278
constexpr int64_t size() const
constexpr const char * data() const
constexpr const char * c_str() const
int64_t size() const
bool contains(const Key &key) const
bool remove(const Key &key)
int64_t remove_if(Predicate &&predicate)
int64_t size() const
void append(const T &value)
bool is_empty() const
virtual const int & get_corresponding_output_id(const bNode &input_bnode) const =0
OperationNode * node
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
bool unassign_action(ID &animated_id)
bool assign_action(bAction *action, ID &animated_id)
bNodeTreeInterfaceSocket * add_interface_socket_from_node(bNodeTree &ntree, const bNode &from_node, const bNodeSocket &from_sock, const StringRef socket_type, const StringRef name)
const bNodeZoneType * zone_type_by_node_type(const int node_type)
float2 node_to_view(const bNode *node, float2 loc)
Definition node.cc:3089
void node_set_active(bNodeTree *ntree, bNode *node)
Definition node.cc:3896
void node_detach_node(bNodeTree *ntree, bNode *node)
Definition node.cc:3122
bool node_link_is_hidden(const bNodeLink *link)
Definition node.cc:2993
bNodeTree * node_tree_copy_tree(Main *bmain, const bNodeTree *ntree)
Definition node.cc:3255
bNode * node_add_static_node(const bContext *C, bNodeTree *ntree, int type)
Definition node.cc:2642
void node_remove_node(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
Definition node.cc:3545
bNode * node_copy_with_mapping(bNodeTree *dst_tree, const bNode &node_src, int flag, bool use_unique, Map< const bNodeSocket *, bNodeSocket * > &new_socket_map)
Definition node.cc:2696
bNodeLink * node_add_link(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
Definition node.cc:2912
bNodeTree * node_tree_add_tree(Main *bmain, const char *name, const char *idname)
Definition node.cc:3226
bNode * node_get_active(bNodeTree *ntree)
Definition node.cc:3849
bNode * node_add_node(const bContext *C, bNodeTree *ntree, const char *idname)
Definition node.cc:2617
void node_rebuild_id_vector(bNodeTree *node_tree)
Definition node.cc:3464
void node_remove_link(bNodeTree *ntree, bNodeLink *link)
Definition node.cc:2958
void node_tree_free_tree(bNodeTree *ntree)
Definition node.cc:3626
Span< const bNodeZoneType * > all_zone_types()
Span< int > all_zone_input_node_types()
bool node_tree_contains_tree(const bNodeTree *tree_to_search_in, const bNodeTree *tree_to_search_for)
Definition node.cc:3827
bool node_declaration_ensure(bNodeTree *ntree, bNode *node)
Definition node.cc:3992
void node_unique_id(bNodeTree *ntree, bNode *node)
Definition node.cc:2599
bNode * node_find_node_by_name(bNodeTree *ntree, const char *name)
Definition node.cc:2437
void node_unique_name(bNodeTree *ntree, bNode *node)
Definition node.cc:2593
static bool node_group_make_test_selected(bNodeTree &ntree, const VectorSet< bNode * > &nodes_to_group, const char *ntree_idname, ReportList &reports)
static bool node_default_group_width_set_poll(bContext *C)
static int node_group_make_exec(bContext *C, wmOperator *op)
static bool prefer_node_for_interface_name(const bNode &node)
static bool node_group_operator_active_poll(bContext *C)
Definition node_group.cc:71
static bNodeTreeInterfaceSocket * add_interface_from_socket(const bNodeTree &original_tree, bNodeTree &tree_for_interface, const bNodeSocket &socket)
static AnimationBasePathChange * animation_basepath_change_new(const StringRef src_basepath, const StringRef dst_basepath)
static VectorSet< bNode * > get_nodes_to_group(bNodeTree &node_tree, bNode *group_node)
static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, bNode *gnode, const VectorSet< bNode * > &nodes_to_move)
static int node_group_insert_exec(bContext *C, wmOperator *op)
static bNode * node_group_get_active(bContext *C, const char *node_idname)
static int node_group_separate_invoke(bContext *C, wmOperator *, const wmEvent *)
static const char * group_ntree_idname(bContext *C)
bool node_deselect_all(bNodeTree &node_tree)
static void node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
void NODE_OT_group_make(wmOperatorType *ot)
static void update_nested_node_refs_after_moving_nodes_into_group(bNodeTree &ntree, bNodeTree &group, bNode &gnode, const Map< int32_t, int32_t > &node_identifier_map)
static const EnumPropertyItem node_group_separate_types[]
void NODE_OT_group_insert(wmOperatorType *ot)
static int node_group_ungroup_exec(bContext *C, wmOperator *)
static bool node_group_separate_selected(Main &bmain, bNodeTree &ntree, bNodeTree &ngroup, const float2 &offset, const bool make_copy)
static int node_group_edit_exec(bContext *C, wmOperator *op)
static int node_default_group_width_set_exec(bContext *C, wmOperator *)
static const bNodeSocket & find_socket_to_use_for_interface(const bNodeTree &node_tree, const bNodeSocket &socket)
VectorSet< bNode * > get_selected_nodes(bNodeTree &node_tree)
const char * node_group_idname(bContext *C)
static bool node_group_operator_editable(bContext *C)
Definition node_group.cc:91
float2 space_node_group_offset(const SpaceNode &snode)
static void get_min_max_of_nodes(const Span< bNode * > nodes, const bool use_size, float2 &min, float2 &max)
void NODE_OT_group_edit(wmOperatorType *ot)
static void update_nested_node_refs_after_ungroup(bNodeTree &ntree, const bNodeTree &ngroup, const bNode &gnode, const Map< int32_t, int32_t > &node_identifier_map)
void NODE_OT_group_separate(wmOperatorType *ot)
static int node_group_separate_exec(bContext *C, wmOperator *op)
static void remap_pairing(bNodeTree &dst_tree, Span< bNode * > nodes, const Map< int32_t, int32_t > &identifier_map)
void NODE_OT_default_group_width_set(wmOperatorType *ot)
void NODE_OT_group_ungroup(wmOperatorType *ot)
static bNode * node_group_make_from_nodes(const bContext &C, bNodeTree &ntree, const VectorSet< bNode * > &nodes_to_group, const char *ntype, const char *ntreetype)
static void animation_basepath_change_free(AnimationBasePathChange *basepath_change)
T midpoint(const T &a, const T &b)
void min_max(const T &value, T &min, T &max)
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node)
VecBase< float, 2 > float2
void uninitialized_copy_n(const T *src, int64_t n, T *dst)
bNodeSocket * node_group_input_find_socket(bNode *node, const char *identifier)
bNodeSocket * node_group_find_output_socket(bNode *groupnode, const char *identifier)
bNodeSocket * node_group_output_find_socket(bNode *node, const char *identifier)
bNodeSocket * node_group_find_input_socket(bNode *groupnode, const char *identifier)
blender::bke::bNodeTreeType * ntreeType_Composite
blender::bke::bNodeTreeType * ntreeType_Geometry
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
std::optional< std::string > RNA_path_from_ID_to_struct(const PointerRNA *ptr)
Definition rna_path.cc:1007
#define min(a, b)
Definition sort.c:32
#define INT32_MAX
Definition stdint.h:137
signed int int32_t
Definition stdint.h:77
bAction * action
const char * dst_basepath
const char * src_basepath
Definition DNA_ID.h:413
void * last
void * first
char tree_idname[64]
ListBase treepath
struct bNodeTree * edittree
struct bNodeTree * nodetree
bNestedNodePath path
char idname[64]
struct bNodeTree * nodetree
struct bNodeTreePath * prev
bNodeTreeRuntimeHandle * runtime
char idname[64]
int default_group_node_width
ListBase nodes
ListBase links
struct AnimData * adt
float locy
float width
struct ID * id
struct bNode * parent
char name[64]
float locx
int32_t identifier
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
float max
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125