Blender V5.0
node_tree_interface.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <queue>
6
7#include "BKE_idprop.hh"
8#include "BKE_lib_id.hh"
9#include "BKE_lib_query.hh"
10#include "BKE_node.hh"
11#include "BKE_node_enum.hh"
12#include "BKE_node_runtime.hh"
14
15#include "BLI_math_vector.h"
16#include "BLI_stack.hh"
17#include "BLI_string.h"
18
19#include "BLO_read_write.hh"
20
22#include "DNA_material_types.h"
24#include "DNA_node_types.h"
25
28
30
32
33namespace socket_types {
34
35/* Try to get a supported socket type from some final type.
36 * Built-in socket can have multiple registered RNA types for the base type, e.g.
37 * `NodeSocketFloatUnsigned`, `NodeSocketFloatFactor`. Only the "base type" (`NodeSocketFloat`)
38 * is considered valid for interface sockets.
39 */
40static std::optional<StringRef> try_get_supported_socket_type(const StringRef socket_type)
41{
42 const blender::bke::bNodeSocketType *typeinfo = bke::node_socket_type_find(socket_type);
43 if (typeinfo == nullptr) {
44 return std::nullopt;
45 }
46 /* For builtin socket types only the base type is supported. */
47 if (node_is_static_socket_type(*typeinfo)) {
48 if (const std::optional<StringRefNull> type_name = bke::node_static_socket_type(typeinfo->type,
49 PROP_NONE))
50 {
51 return *type_name;
52 }
53 return std::nullopt;
54 }
55 return typeinfo->idname;
56}
57
58/* -------------------------------------------------------------------- */
61
62template<typename T> void socket_data_id_user_increment(T & /*data*/) {}
64{
65 id_us_plus(reinterpret_cast<ID *>(data.value));
66}
68{
69 id_us_plus(reinterpret_cast<ID *>(data.value));
70}
72{
73 id_us_plus(reinterpret_cast<ID *>(data.value));
74}
76{
77 id_us_plus(reinterpret_cast<ID *>(data.value));
78}
80{
81 id_us_plus(reinterpret_cast<ID *>(data.value));
82}
83
85
86/* -------------------------------------------------------------------- */
89
90template<typename T> void socket_data_id_user_decrement(T & /*data*/) {}
92{
93 id_us_min(reinterpret_cast<ID *>(data.value));
94}
96{
97 id_us_min(reinterpret_cast<ID *>(data.value));
98}
100{
101 id_us_min(reinterpret_cast<ID *>(data.value));
102}
104{
105 id_us_min(reinterpret_cast<ID *>(data.value));
106}
108{
109 id_us_min(reinterpret_cast<ID *>(data.value));
110}
111
113
114/* -------------------------------------------------------------------- */
117
118template<typename T> void socket_data_init_impl(T & /*data*/) {}
120{
121 data.subtype = PROP_NONE;
122 data.value = 0.0f;
123 data.min = -FLT_MAX;
124 data.max = FLT_MAX;
125}
127{
128 data.subtype = PROP_NONE;
129 data.value = 0;
130 data.min = INT_MIN;
131 data.max = INT_MAX;
132}
134{
135 data.value = false;
136}
139{
140 static float default_value[] = {0.0f, 0.0f, 0.0f};
141 data.subtype = PROP_NONE;
142 data.dimensions = 3;
143 copy_v3_v3(data.value, default_value);
144 data.min = -FLT_MAX;
145 data.max = FLT_MAX;
146}
148{
149 static float default_value[] = {0.0f, 0.0f, 0.0f, 1.0f};
150 copy_v4_v4(data.value, default_value);
151}
153{
154 data.subtype = PROP_NONE;
155 data.value[0] = '\0';
156}
158{
159 data.value = nullptr;
160}
162{
163 data.value = nullptr;
164}
166{
167 data.value = nullptr;
168}
170{
171 data.value = nullptr;
172}
174{
175 data.value = nullptr;
176}
178{
179 data.value = -1;
180 data.enum_items = nullptr;
181 data.runtime_flag = 0;
182}
183
184static void *make_socket_data(const StringRef socket_type)
185{
186 void *socket_data = nullptr;
187 socket_data_to_static_type_tag(socket_type, [&socket_data](auto type_tag) {
188 using SocketDataType = typename decltype(type_tag)::type;
189 SocketDataType *new_socket_data = MEM_callocN<SocketDataType>(__func__);
190 socket_data_init_impl(*new_socket_data);
191 socket_data = new_socket_data;
192 });
193 return socket_data;
194}
195
197
198/* -------------------------------------------------------------------- */
201
202template<typename T> void socket_data_free_impl(T & /*data*/, const bool /*do_id_user*/) {}
203template<> void socket_data_free_impl(bNodeSocketValueMenu &dst, const bool /*do_id_user*/)
204{
205 if (dst.enum_items) {
206 /* Release shared data pointer. */
207 dst.enum_items->remove_user_and_delete_if_last();
208 }
209}
210
211static void socket_data_free(bNodeTreeInterfaceSocket &socket, const bool do_id_user)
212{
213 socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
214 using SocketDataType = typename decltype(type_tag)::type;
215 if (do_id_user) {
216 socket_data_id_user_decrement(get_socket_data_as<SocketDataType>(socket));
217 }
219 });
220}
221
223
224/* -------------------------------------------------------------------- */
227
228template<typename T> void socket_data_copy_impl(T & /*dst*/, const T & /*src*/) {}
229template<>
231{
232 /* Copy of shared data pointer. */
233 if (dst.enum_items) {
234 dst.enum_items->add_user();
235 }
236}
237
239 const bNodeTreeInterfaceSocket &src,
240 int flag)
241{
242 socket_data_to_static_type_tag(dst.socket_type, [&](auto type_tag) {
243 using SocketDataType = typename decltype(type_tag)::type;
244 dst.socket_data = MEM_dupallocN(src.socket_data);
245 socket_data_copy_impl(get_socket_data_as<SocketDataType>(dst),
246 get_socket_data_as<SocketDataType>(src));
247 if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
248 socket_data_id_user_increment(get_socket_data_as<SocketDataType>(dst));
249 }
250 });
251}
252
253/* Copy socket data from a raw pointer, e.g. from a #bNodeSocket. */
255 const void *src_socket_data,
256 int flag)
257{
258 socket_data_to_static_type_tag(dst.socket_type, [&](auto type_tag) {
259 using SocketDataType = typename decltype(type_tag)::type;
260
261 if (dst.socket_data != nullptr) {
262 socket_data_free(dst, true);
263 MEM_SAFE_FREE(dst.socket_data);
264 }
265
266 dst.socket_data = MEM_dupallocN(src_socket_data);
268 *static_cast<const SocketDataType *>(src_socket_data));
271 }
272 });
273}
274
276
277/* -------------------------------------------------------------------- */
280
281/* NOTE: no default implementation, every used type must write at least the base struct. */
282
335
337{
338 socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
339 using SocketDataType = typename decltype(type_tag)::type;
340 socket_data_write_impl(writer, get_socket_data_as<SocketDataType>(socket));
341 });
342}
343
345
346/* -------------------------------------------------------------------- */
349
350template<typename T> void socket_data_read_data_impl(BlendDataReader *reader, T **data)
351{
352 /* FIXME Avoid using low-level untyped read function here. Cannot use the BLO_read_struct
353 * currently (macro expansion would process `T` instead of the actual type). */
355}
357{
358 /* FIXME Avoid using low-level untyped read function here. No type info available here currently.
359 */
361 /* Clear runtime data. */
362 (*data)->enum_items = nullptr;
363 (*data)->runtime_flag = 0;
364}
365
367{
368 bool data_read = false;
369 socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
370 using SocketDataType = typename decltype(type_tag)::type;
371 socket_data_read_data_impl(reader, reinterpret_cast<SocketDataType **>(&socket.socket_data));
372 data_read = true;
373 });
374 if (!data_read && socket.socket_data) {
375 /* Not sure how this can happen exactly, but it did happen in #127855. */
376 socket.socket_data = nullptr;
377 }
378}
379
381
382/* -------------------------------------------------------------------- */
385
386template<typename T>
388{
389}
398template<>
403template<>
408template<>
413
415{
416 socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
417 using SocketDataType = typename decltype(type_tag)::type;
418 socket_data_foreach_id_impl(data, get_socket_data_as<SocketDataType>(socket));
419 });
420}
421
423
424} // namespace socket_types
425
426namespace item_types {
427
429
430static void item_copy(bNodeTreeInterfaceItem &dst,
431 const bNodeTreeInterfaceItem &src,
432 int flag,
433 UidGeneratorFn generate_uid);
434
442 const int flag,
443 UidGeneratorFn generate_uid)
444{
445 panel.items_num = items_src.size();
447
448 /* Copy buffers. */
449 for (const int i : items_src.index_range()) {
450 const bNodeTreeInterfaceItem *item_src = items_src[i];
451 panel.items_array[i] = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(item_src));
452 item_types::item_copy(*panel.items_array[i], *item_src, flag, generate_uid);
453 }
454}
455
462 const bNodeTreeInterfaceItem &src,
463 const int flag,
464 UidGeneratorFn generate_uid)
465{
468 bNodeTreeInterfaceSocket &dst_socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(dst);
469 const bNodeTreeInterfaceSocket &src_socket =
470 reinterpret_cast<const bNodeTreeInterfaceSocket &>(src);
471 BLI_assert(src_socket.socket_type != nullptr);
472
473 dst_socket.name = BLI_strdup_null(src_socket.name);
474 dst_socket.description = BLI_strdup_null(src_socket.description);
475 dst_socket.socket_type = BLI_strdup(src_socket.socket_type);
477 dst_socket.identifier = generate_uid ? BLI_sprintfN("Socket_%d", generate_uid()) :
478 BLI_strdup(src_socket.identifier);
479 if (src_socket.properties) {
480 dst_socket.properties = IDP_CopyProperty_ex(src_socket.properties, flag);
481 }
482 if (src_socket.socket_data != nullptr) {
483 socket_types::socket_data_copy(dst_socket, src_socket, flag);
484 }
485 break;
486 }
488 bNodeTreeInterfacePanel &dst_panel = reinterpret_cast<bNodeTreeInterfacePanel &>(dst);
489 const bNodeTreeInterfacePanel &src_panel = reinterpret_cast<const bNodeTreeInterfacePanel &>(
490 src);
491
492 dst_panel.name = BLI_strdup_null(src_panel.name);
493 dst_panel.description = BLI_strdup_null(src_panel.description);
494 dst_panel.identifier = generate_uid ? generate_uid() : src_panel.identifier;
495
496 panel_init(dst_panel, src_panel.items(), flag, generate_uid);
497 break;
498 }
499 }
500}
501
502static void item_free(bNodeTreeInterfaceItem &item, const bool do_id_user)
503{
504 switch (NodeTreeInterfaceItemType(item.item_type)) {
506 bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
507
508 if (socket.socket_data != nullptr) {
509 socket_types::socket_data_free(socket, do_id_user);
511 }
512
513 MEM_SAFE_FREE(socket.name);
518 if (socket.properties) {
519 IDP_FreePropertyContent_ex(socket.properties, do_id_user);
520 MEM_freeN(socket.properties);
521 }
522 break;
523 }
525 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
526
527 panel.clear(do_id_user);
528 MEM_SAFE_FREE(panel.name);
530 break;
531 }
532 }
533
534 MEM_freeN(&item);
535}
536
538
540{
541 switch (NodeTreeInterfaceItemType(item.item_type)) {
543 bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
544 BLO_write_string(writer, socket.name);
545 BLO_write_string(writer, socket.identifier);
546 BLO_write_string(writer, socket.description);
547 BLO_write_string(writer, socket.socket_type);
549 if (socket.properties) {
550 IDP_BlendWrite(writer, socket.properties);
551 }
552
553 socket_types::socket_data_write(writer, socket);
554 break;
555 }
557 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
558 BLO_write_string(writer, panel.name);
559 BLO_write_string(writer, panel.description);
560 BLO_write_pointer_array(writer, panel.items_num, panel.items_array);
561 for (bNodeTreeInterfaceItem *child_item : panel.items()) {
562 item_write_struct(writer, *child_item);
563 }
564 break;
565 }
566 }
567}
568
570{
571 switch (NodeTreeInterfaceItemType(item.item_type)) {
573 /* Forward compatible writing of older single value only flag. To be removed in 5.0. */
578
580 break;
581 }
584 break;
585 }
586 }
587
588 item_write_data(writer, item);
589}
590
592{
593 switch (NodeTreeInterfaceItemType(item.item_type)) {
595 bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
596 BLO_read_string(reader, &socket.name);
597 BLO_read_string(reader, &socket.description);
598 BLO_read_string(reader, &socket.socket_type);
600 BLO_read_string(reader, &socket.identifier);
601 BLO_read_struct(reader, IDProperty, &socket.properties);
602 IDP_BlendDataRead(reader, &socket.properties);
603
604 /* Improve forward compatibility for unknown default input types. */
605 const bNodeSocketType *stype = socket.socket_typeinfo();
607 *stype, NodeDefaultInputType(socket.default_input)))
608 {
610 }
611
613 break;
614 }
616 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
617 BLO_read_string(reader, &panel.name);
618 BLO_read_string(reader, &panel.description);
620 reader, panel.items_num, reinterpret_cast<void **>(&panel.items_array));
621
622 /* Read the direct-data for each interface item if possible. The pointer becomes null if the
623 * struct type is not known. */
624 for (const int i : blender::IndexRange(panel.items_num)) {
626 }
627 /* Forward compatibility: Discard unknown tree interface item types that may be introduced in
628 * the future. Their pointer is set to null above. */
629 panel.items_num = std::remove_if(
630 panel.items_array,
631 panel.items_array + panel.items_num,
632 [&](const bNodeTreeInterfaceItem *item) { return item == nullptr; }) -
633 panel.items_array;
634 /* Now read the actual data if the known interface items. */
635 for (const int i : blender::IndexRange(panel.items_num)) {
636 item_read_data(reader, *panel.items_array[i]);
637 }
638 break;
639 }
640 }
641}
642
644{
645 switch (NodeTreeInterfaceItemType(item.item_type)) {
647 bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
648
651 BKE_lib_query_idpropertiesForeachIDLink_callback(prop, data);
652 }));
653
655 break;
656 }
658 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
659 for (bNodeTreeInterfaceItem *item : panel.items()) {
660 item_foreach_id(data, *item);
661 }
662 break;
663 }
664 }
665}
666
667/* Move all child items to the new parent. */
669{
670 switch (NodeTreeInterfaceItemType(item.item_type)) {
672 return {};
673 }
675 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
676 return panel.items();
677 }
678 }
679 return {};
680}
681
682} // namespace item_types
683
684} // namespace blender::bke::node_interface
685
686using namespace blender::bke::node_interface;
687
688blender::bke::bNodeSocketType *bNodeTreeInterfaceSocket::socket_typeinfo() const
689{
691}
692
693blender::ColorGeometry4f bNodeTreeInterfaceSocket::socket_color() const
694{
695 blender::bke::bNodeSocketType *typeinfo = this->socket_typeinfo();
696 if (typeinfo && typeinfo->draw_color_simple) {
697 float color[4];
698 typeinfo->draw_color_simple(typeinfo, color);
700 }
701 return blender::ColorGeometry4f(1.0f, 0.0f, 1.0f, 1.0f);
702}
703
704bool bNodeTreeInterfaceSocket::set_socket_type(const StringRef new_socket_type)
705{
706 const std::optional<StringRef> idname = socket_types::try_get_supported_socket_type(
707 new_socket_type);
708 if (!idname) {
709 return false;
710 }
711
712 if (this->socket_data != nullptr) {
715 }
717
718 this->socket_type = BLI_strdupn(new_socket_type.data(), new_socket_type.size());
719 this->socket_data = socket_types::make_socket_data(new_socket_type);
720
721 blender::bke::bNodeSocketType *stype = this->socket_typeinfo();
723 *stype, NodeDefaultInputType(this->default_input)))
724 {
726 }
727
728 return true;
729}
730
731void bNodeTreeInterfaceSocket::init_from_socket_instance(const bNodeSocket *socket)
732{
733 const std::optional<StringRef> idname = socket_types::try_get_supported_socket_type(
734 socket->idname);
735 BLI_assert(idname.has_value());
736
737 if (this->socket_data != nullptr) {
740 }
742 if (socket->flag & SOCK_HIDE_VALUE) {
744 }
745
746 this->socket_type = BLI_strdupn(idname->data(), idname->size());
749}
750
751blender::IndexRange bNodeTreeInterfacePanel::items_range() const
752{
753 return blender::IndexRange(items_num);
754}
755
756blender::Span<const bNodeTreeInterfaceItem *> bNodeTreeInterfacePanel::items() const
757{
758 return blender::Span(items_array, items_num);
759}
760
761blender::MutableSpan<bNodeTreeInterfaceItem *> bNodeTreeInterfacePanel::items()
762{
763 return blender::MutableSpan(items_array, items_num);
764}
765
766bool bNodeTreeInterfacePanel::contains(const bNodeTreeInterfaceItem &item) const
767{
768 return items().contains(&item);
769}
770
771bool bNodeTreeInterfacePanel::contains_recursive(const bNodeTreeInterfaceItem &item) const
772{
773 bool is_child = false;
774 /* Have to capture item address here instead of just a reference,
775 * otherwise pointer comparison will not work. */
776 this->foreach_item(
777 [&](const bNodeTreeInterfaceItem &titem) {
778 if (&titem == &item) {
779 is_child = true;
780 return false;
781 }
782 return true;
783 },
784 true);
785 return is_child;
786}
787
788int bNodeTreeInterfacePanel::item_position(const bNodeTreeInterfaceItem &item) const
789{
790 return items().first_index_try(&item);
791}
792
793int bNodeTreeInterfacePanel::item_index(const bNodeTreeInterfaceItem &item) const
794{
795 int index = 0;
796 bool found = false;
797 /* Have to capture item address here instead of just a reference,
798 * otherwise pointer comparison will not work. */
799 this->foreach_item([&](const bNodeTreeInterfaceItem &titem) {
800 if (&titem == &item) {
801 found = true;
802 return false;
803 }
804 ++index;
805 return true;
806 });
807 return found ? index : -1;
808}
809
810const bNodeTreeInterfaceItem *bNodeTreeInterfacePanel::item_at_index(int index) const
811{
812 int i = 0;
813 const bNodeTreeInterfaceItem *result = nullptr;
814 this->foreach_item([&](const bNodeTreeInterfaceItem &item) {
815 if (i == index) {
816 result = &item;
817 return false;
818 }
819 ++i;
820 return true;
821 });
822 return result;
823}
824
825bNodeTreeInterfacePanel *bNodeTreeInterfacePanel::find_parent_recursive(
826 const bNodeTreeInterfaceItem &item)
827{
828 std::queue<bNodeTreeInterfacePanel *> queue;
829
830 if (this->contains(item)) {
831 return this;
832 }
833 queue.push(this);
834
835 while (!queue.empty()) {
836 bNodeTreeInterfacePanel *parent = queue.front();
837 queue.pop();
838
839 for (bNodeTreeInterfaceItem *titem : parent->items()) {
840 if (titem->item_type != NODE_INTERFACE_PANEL) {
841 continue;
842 }
843
845 if (tpanel->contains(item)) {
846 return tpanel;
847 }
848 queue.push(tpanel);
849 }
850 }
851
852 return nullptr;
853}
854
855int bNodeTreeInterfacePanel::find_valid_insert_position_for_item(
856 const bNodeTreeInterfaceItem &item, const int initial_pos) const
857{
858 const bool sockets_above_panels = !(this->flag &
860 const blender::Span<const bNodeTreeInterfaceItem *> items = this->items();
861
862 /* True if item a should be above item b. */
863 auto must_be_before = [sockets_above_panels](const bNodeTreeInterfaceItem &a,
864 const bNodeTreeInterfaceItem &b) -> bool {
865 /* Keep sockets above panels. */
866 if (sockets_above_panels) {
867 if (a.item_type == NODE_INTERFACE_SOCKET && b.item_type == NODE_INTERFACE_PANEL) {
868 return true;
869 }
870 }
871 /* Keep outputs above inputs. */
872 if (a.item_type == NODE_INTERFACE_SOCKET && b.item_type == NODE_INTERFACE_SOCKET) {
873 const auto &sa = reinterpret_cast<const bNodeTreeInterfaceSocket &>(a);
874 const auto &sb = reinterpret_cast<const bNodeTreeInterfaceSocket &>(b);
875 const bool is_output_a = sa.flag & NODE_INTERFACE_SOCKET_OUTPUT;
876 const bool is_output_b = sb.flag & NODE_INTERFACE_SOCKET_OUTPUT;
877 if ((sa.flag & NODE_INTERFACE_SOCKET_PANEL_TOGGLE) ||
879 {
880 /* Panel toggle inputs are allowed to be above outputs. */
881 return false;
882 }
883 if (is_output_a && !is_output_b) {
884 return true;
885 }
886 }
887 return false;
888 };
889
890 int min_pos = 0;
891 for (const int i : items.index_range()) {
892 if (must_be_before(*items[i], item)) {
893 min_pos = i + 1;
894 }
895 }
896 int max_pos = items.size();
897 for (const int i : items.index_range()) {
898 if (must_be_before(item, *items[i])) {
899 max_pos = i;
900 break;
901 }
902 }
903 BLI_assert(min_pos <= max_pos);
904 return std::clamp(initial_pos, min_pos, max_pos);
905}
906
907void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item)
908{
909 /* Same as inserting at the end. */
910 insert_item(item, this->items_num);
911}
912
913void bNodeTreeInterfacePanel::insert_item(bNodeTreeInterfaceItem &item, int position)
914{
915 /* Apply any constraints on the item positions. */
916 position = find_valid_insert_position_for_item(item, position);
917 position = std::min(std::max(position, 0), items_num);
918
919 blender::MutableSpan<bNodeTreeInterfaceItem *> old_items = this->items();
920 items_num++;
922 this->items().take_front(position).copy_from(old_items.take_front(position));
923 this->items().drop_front(position + 1).copy_from(old_items.drop_front(position));
924 this->items()[position] = &item;
925
926 if (old_items.data()) {
927 MEM_freeN(old_items.data());
928 }
929}
930
931bool bNodeTreeInterfacePanel::remove_item(bNodeTreeInterfaceItem &item, const bool free)
932{
933 const int position = this->item_position(item);
934 if (!this->items().index_range().contains(position)) {
935 return false;
936 }
937
938 blender::MutableSpan<bNodeTreeInterfaceItem *> old_items = this->items();
939 items_num--;
941 this->items().take_front(position).copy_from(old_items.take_front(position));
942 this->items().drop_front(position).copy_from(old_items.drop_front(position + 1));
943
944 /* Guaranteed not empty, contains at least the removed item */
945 MEM_freeN(old_items.data());
946
947 if (free) {
949 }
950
951 return true;
952}
953
954void bNodeTreeInterfacePanel::clear(bool do_id_user)
955{
956 for (bNodeTreeInterfaceItem *item : this->items()) {
957 item_types::item_free(*item, do_id_user);
958 }
960 items_array = nullptr;
961 items_num = 0;
962}
963
964bool bNodeTreeInterfacePanel::move_item(bNodeTreeInterfaceItem &item, int new_position)
965{
966 const int old_position = this->item_position(item);
967 if (!this->items().index_range().contains(old_position)) {
968 return false;
969 }
970 if (old_position == new_position) {
971 /* Nothing changes. */
972 return true;
973 }
974
975 new_position = find_valid_insert_position_for_item(item, new_position);
976 new_position = std::min(std::max(new_position, 0), items_num);
977
978 if (old_position < new_position) {
979 /* Actual target position and all existing items shifted by 1. */
980 const blender::Span<bNodeTreeInterfaceItem *> moved_items = this->items().slice(
981 old_position + 1, new_position - old_position - 1);
982 bNodeTreeInterfaceItem *tmp = this->items()[old_position];
983 std::copy(
984 moved_items.begin(), moved_items.end(), this->items().drop_front(old_position).data());
985 this->items()[new_position - 1] = tmp;
986 }
987 else /* old_position > new_position */ {
988 const blender::Span<bNodeTreeInterfaceItem *> moved_items = this->items().slice(
989 new_position, old_position - new_position);
990 bNodeTreeInterfaceItem *tmp = this->items()[old_position];
991 std::copy_backward(
992 moved_items.begin(), moved_items.end(), this->items().drop_front(old_position + 1).data());
993 this->items()[new_position] = tmp;
994 }
995
996 return true;
997}
998
999void bNodeTreeInterfacePanel::foreach_item(
1000 blender::FunctionRef<bool(bNodeTreeInterfaceItem &item)> fn, bool include_self)
1001{
1002 using ItemSpan = blender::Span<bNodeTreeInterfaceItem *>;
1003 blender::Stack<ItemSpan> stack;
1004
1005 if (include_self && fn(this->item) == false) {
1006 return;
1007 }
1008 stack.push(this->items());
1009
1010 while (!stack.is_empty()) {
1011 const ItemSpan current_items = stack.pop();
1012
1013 for (const int index : current_items.index_range()) {
1014 bNodeTreeInterfaceItem *item = current_items[index];
1015 if (fn(*item) == false) {
1016 return;
1017 }
1018
1019 if (item->item_type == NODE_INTERFACE_PANEL) {
1020 bNodeTreeInterfacePanel *panel = reinterpret_cast<bNodeTreeInterfacePanel *>(item);
1021 /* Reinsert remaining items. */
1022 if (index < current_items.size() - 1) {
1023 const ItemSpan remaining_items = current_items.drop_front(index + 1);
1024 stack.push(remaining_items);
1025 }
1026 /* Handle child items first before continuing with current span. */
1027 stack.push(panel->items());
1028 break;
1029 }
1030 }
1031 }
1032}
1033
1034void bNodeTreeInterfacePanel::foreach_item(
1035 blender::FunctionRef<bool(const bNodeTreeInterfaceItem &item)> fn, bool include_self) const
1036{
1037 using ItemSpan = blender::Span<const bNodeTreeInterfaceItem *>;
1038 blender::Stack<ItemSpan> stack;
1039
1040 if (include_self && fn(this->item) == false) {
1041 return;
1042 }
1043 stack.push(this->items());
1044
1045 while (!stack.is_empty()) {
1046 const ItemSpan current_items = stack.pop();
1047
1048 for (const int index : current_items.index_range()) {
1049 const bNodeTreeInterfaceItem *item = current_items[index];
1050 if (fn(*item) == false) {
1051 return;
1052 }
1053
1054 if (item->item_type == NODE_INTERFACE_PANEL) {
1055 const bNodeTreeInterfacePanel *panel = reinterpret_cast<const bNodeTreeInterfacePanel *>(
1056 item);
1057 /* Reinsert remaining items. */
1058 if (index < current_items.size() - 1) {
1059 const ItemSpan remaining_items = current_items.drop_front(index + 1);
1060 stack.push(remaining_items);
1061 }
1062 /* Handle child items first before continuing with current span. */
1063 stack.push(panel->items());
1064 break;
1065 }
1066 }
1067 }
1068}
1069
1070const bNodeTreeInterfaceSocket *bNodeTreeInterfacePanel::header_toggle_socket() const
1071{
1072 if (this->items().is_empty()) {
1073 return nullptr;
1074 }
1075 const bNodeTreeInterfaceItem *first_item = this->items().first();
1076 if (first_item->item_type != NODE_INTERFACE_SOCKET) {
1077 return nullptr;
1078 }
1079 const auto &socket = *reinterpret_cast<const bNodeTreeInterfaceSocket *>(first_item);
1080 if (!(socket.flag & NODE_INTERFACE_SOCKET_INPUT) ||
1082 {
1083 return nullptr;
1084 }
1085 const blender::bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
1086 if (!typeinfo || typeinfo->type != SOCK_BOOLEAN) {
1087 return nullptr;
1088 }
1089 return &socket;
1090}
1091bNodeTreeInterfaceSocket *bNodeTreeInterfacePanel::header_toggle_socket()
1092{
1093 return const_cast<bNodeTreeInterfaceSocket *>(
1094 const_cast<const bNodeTreeInterfacePanel *>(this)->header_toggle_socket());
1095}
1096
1098
1100 const StringRef name,
1101 const StringRef description,
1102 const StringRef socket_type,
1104{
1105 BLI_assert(!socket_type.is_empty());
1106
1107 const std::optional<StringRef> idname = socket_types::try_get_supported_socket_type(socket_type);
1108 if (!idname) {
1109 return nullptr;
1110 }
1111
1113 BLI_assert(new_socket);
1114
1115 /* Init common socket properties. */
1116 new_socket->identifier = BLI_sprintfN("Socket_%d", uid);
1117 new_socket->item.item_type = NODE_INTERFACE_SOCKET;
1118 new_socket->name = BLI_strdupn(name.data(), name.size());
1119 new_socket->description = description.is_empty() ?
1120 nullptr :
1121 BLI_strdupn(description.data(), description.size());
1122 new_socket->socket_type = BLI_strdupn(socket_type.data(), socket_type.size());
1123 new_socket->flag = flag;
1124
1125 new_socket->socket_data = socket_types::make_socket_data(socket_type);
1126
1127 return new_socket;
1128}
1129
1131 const bNode &from_node,
1132 const bNodeSocket &from_sock,
1133 const StringRef socket_type,
1134 const StringRef name)
1135{
1136 ntree.ensure_topology_cache();
1137 bNodeTreeInterfaceSocket *iosock = nullptr;
1138 if (from_node.is_group()) {
1139 if (const bNodeTree *group = reinterpret_cast<const bNodeTree *>(from_node.id)) {
1140 /* Copy interface socket directly from source group to avoid loosing data in the process. */
1141 group->ensure_interface_cache();
1142 const bNodeTreeInterfaceSocket &src_io_socket =
1143 from_sock.is_input() ? *group->interface_inputs()[from_sock.index()] :
1144 *group->interface_outputs()[from_sock.index()];
1145 iosock = reinterpret_cast<bNodeTreeInterfaceSocket *>(
1146 ntree.tree_interface.add_item_copy(src_io_socket.item, nullptr));
1147 }
1148 }
1149 if (!iosock) {
1153
1154 const nodes::SocketDeclaration *decl = from_sock.runtime->declaration;
1155 StringRef description = from_sock.description;
1156 if (decl) {
1157 if (!decl->description.empty()) {
1158 description = decl->description;
1159 }
1161 if (socket_type == "NodeSocketMenu" && from_sock.type == SOCK_MENU) {
1162 if (const auto *menu_decl = dynamic_cast<const nodes::decl::Menu *>(decl)) {
1164 }
1165 }
1166 }
1167
1168 iosock = ntree.tree_interface.add_socket(name, description, socket_type, flag, nullptr);
1169
1170 if (iosock) {
1171 if (decl) {
1172 iosock->default_input = decl->default_input_type;
1173 }
1174 }
1175 }
1176 if (iosock == nullptr) {
1177 return nullptr;
1178 }
1179 const blender::bke::bNodeSocketType *typeinfo = iosock->socket_typeinfo();
1180 if (typeinfo->interface_from_socket) {
1181 typeinfo->interface_from_socket(&ntree.id, iosock, &from_node, &from_sock);
1182 }
1183 return iosock;
1184}
1185
1188 const blender::StringRef description,
1190{
1191 BLI_assert(!name.is_empty());
1192
1194 new_panel->item.item_type = NODE_INTERFACE_PANEL;
1195 new_panel->name = BLI_strdupn(name.data(), name.size());
1196 new_panel->description = description.is_empty() ?
1197 nullptr :
1198 BLI_strdupn(description.data(), description.size());
1199 new_panel->identifier = uid;
1200 new_panel->flag = flag;
1201 return new_panel;
1202}
1203
1204} // namespace blender::bke::node_interface
1205
1206void bNodeTreeInterface::init_data()
1207{
1208 this->runtime = MEM_new<blender::bke::bNodeTreeInterfaceRuntime>(__func__);
1209 this->tag_missing_runtime_data();
1210}
1211
1212void bNodeTreeInterface::copy_data(const bNodeTreeInterface &src, int flag)
1213{
1214 item_types::panel_init(this->root_panel, src.root_panel.items(), flag, nullptr);
1215 this->active_index = src.active_index;
1216
1217 this->runtime = MEM_new<blender::bke::bNodeTreeInterfaceRuntime>(__func__);
1218 this->tag_missing_runtime_data();
1219}
1220
1221void bNodeTreeInterface::free_data()
1222{
1223 MEM_delete(this->runtime);
1224
1225 /* Called when freeing the main database, don't do user refcount here. */
1226 this->root_panel.clear(false);
1227}
1228
1229void bNodeTreeInterface::write(BlendWriter *writer)
1230{
1231 /* Don't write the root panel struct itself, it's nested in the interface struct. */
1232 item_types::item_write_data(writer, this->root_panel.item);
1233}
1234
1235void bNodeTreeInterface::read_data(BlendDataReader *reader)
1236{
1237 item_types::item_read_data(reader, this->root_panel.item);
1238
1239 this->runtime = MEM_new<blender::bke::bNodeTreeInterfaceRuntime>(__func__);
1240 this->tag_missing_runtime_data();
1241}
1242
1243bNodeTreeInterfaceItem *bNodeTreeInterface::active_item()
1244{
1245 bNodeTreeInterfaceItem *active = nullptr;
1246 int count = this->active_index;
1247 this->foreach_item([&](bNodeTreeInterfaceItem &item) {
1248 if (count == 0) {
1249 active = &item;
1250 return false;
1251 }
1252 --count;
1253 return true;
1254 });
1255 return active;
1256}
1257
1258const bNodeTreeInterfaceItem *bNodeTreeInterface::active_item() const
1259{
1260 const bNodeTreeInterfaceItem *active = nullptr;
1261 int count = this->active_index;
1262 this->foreach_item([&](const bNodeTreeInterfaceItem &item) {
1263 if (count == 0) {
1264 active = &item;
1265 return false;
1266 }
1267 --count;
1268 return true;
1269 });
1270 return active;
1271}
1272
1273void bNodeTreeInterface::active_item_set(bNodeTreeInterfaceItem *item)
1274{
1275 this->active_index = 0;
1276 int count = 0;
1277 this->foreach_item([&](bNodeTreeInterfaceItem &titem) {
1278 if (&titem == item) {
1279 this->active_index = count;
1280 return false;
1281 }
1282 ++count;
1283 return true;
1284 });
1285}
1286
1287bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(const blender::StringRef name,
1288 const blender::StringRef description,
1289 const blender::StringRef socket_type,
1292{
1293 /* Check that each interface socket is either an input or an output. Technically, it can be both
1294 * at the same time, but we don't want that for the time being. */
1297 if (parent == nullptr) {
1298 parent = &root_panel;
1299 }
1300 BLI_assert(this->find_item(parent->item));
1301
1303 this->next_uid++, name, description, socket_type, flag);
1304 if (new_socket) {
1305 parent->add_item(new_socket->item);
1306 }
1307
1308 this->tag_items_changed();
1309 return new_socket;
1310}
1311
1312bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(const blender::StringRef name,
1313 const blender::StringRef description,
1314 const blender::StringRef socket_type,
1317 const int position)
1318{
1319 if (parent == nullptr) {
1320 parent = &root_panel;
1321 }
1322 BLI_assert(this->find_item(parent->item));
1323
1325 this->next_uid++, name, description, socket_type, flag);
1326 if (new_socket) {
1327 parent->insert_item(new_socket->item, position);
1328 }
1329
1330 this->tag_items_changed();
1331 return new_socket;
1332}
1333
1334bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(const blender::StringRef name,
1335 const blender::StringRef description,
1338{
1339 if (parent == nullptr) {
1340 parent = &root_panel;
1341 }
1342 BLI_assert(this->find_item(parent->item));
1343
1344 bNodeTreeInterfacePanel *new_panel = make_panel(this->next_uid++, name, description, flag);
1345 if (new_panel) {
1346 parent->add_item(new_panel->item);
1347 }
1348
1349 this->tag_items_changed();
1350 return new_panel;
1351}
1352
1353bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(const blender::StringRef name,
1354 const blender::StringRef description,
1357 const int position)
1358{
1359 if (parent == nullptr) {
1360 parent = &root_panel;
1361 }
1362 BLI_assert(this->find_item(parent->item));
1363
1364 bNodeTreeInterfacePanel *new_panel = make_panel(this->next_uid++, name, description, flag);
1365 if (new_panel) {
1366 parent->insert_item(new_panel->item, position);
1367 }
1368
1369 this->tag_items_changed();
1370 return new_panel;
1371}
1372
1373bNodeTreeInterfaceItem *bNodeTreeInterface::add_item_copy(const bNodeTreeInterfaceItem &item,
1375{
1376 if (parent == nullptr) {
1377 parent = &root_panel;
1378 }
1379 BLI_assert(this->find_item(parent->item));
1380
1381 bNodeTreeInterfaceItem *citem = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(&item));
1382 item_types::item_copy(*citem, item, 0, [&]() { return this->next_uid++; });
1383 parent->add_item(*citem);
1384
1385 this->tag_items_changed();
1386 return citem;
1387}
1388
1389bNodeTreeInterfaceItem *bNodeTreeInterface::insert_item_copy(const bNodeTreeInterfaceItem &item,
1391 int position)
1392{
1393 if (parent == nullptr) {
1394 parent = &root_panel;
1395 }
1396 BLI_assert(this->find_item(item));
1397 BLI_assert(this->find_item(parent->item));
1398
1399 bNodeTreeInterfaceItem *citem = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(&item));
1400 item_types::item_copy(*citem, item, 0, [&]() { return this->next_uid++; });
1401 parent->insert_item(*citem, position);
1402
1403 this->tag_items_changed();
1404 return citem;
1405}
1406
1407bool bNodeTreeInterface::remove_item(bNodeTreeInterfaceItem &item, bool move_content_to_parent)
1408{
1409 bNodeTreeInterfacePanel *parent = this->find_item_parent(item, true);
1410 if (parent == nullptr) {
1411 return false;
1412 }
1413 if (move_content_to_parent) {
1414 int position = parent->item_position(item);
1415 /* Cache children to avoid invalidating the iterator. */
1416 blender::Array<bNodeTreeInterfaceItem *> children(item_types::item_children(item));
1417 for (bNodeTreeInterfaceItem *child : children) {
1418 this->move_item_to_parent(*child, parent, position++);
1419 }
1420 }
1421 if (parent->remove_item(item, true)) {
1422 this->tag_items_changed();
1423 return true;
1424 }
1425
1426 return false;
1427}
1428
1429void bNodeTreeInterface::clear_items()
1430{
1431 root_panel.clear(true);
1432 this->tag_items_changed();
1433}
1434
1435bool bNodeTreeInterface::move_item(bNodeTreeInterfaceItem &item, const int new_position)
1436{
1437 bNodeTreeInterfacePanel *parent = this->find_item_parent(item, true);
1438 if (parent == nullptr) {
1439 return false;
1440 }
1441
1442 if (parent->move_item(item, new_position)) {
1443 this->tag_items_changed();
1444 return true;
1445 }
1446 return false;
1447}
1448
1449bool bNodeTreeInterface::move_item_to_parent(bNodeTreeInterfaceItem &item,
1450 bNodeTreeInterfacePanel *new_parent,
1451 int new_position)
1452{
1453 if (new_parent == nullptr) {
1454 new_parent = &this->root_panel;
1455 }
1456
1457 if (item.item_type == NODE_INTERFACE_PANEL) {
1458 bNodeTreeInterfacePanel &src_item = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
1459 if (src_item.contains_recursive(new_parent->item)) {
1460 return false;
1461 }
1462 }
1463
1464 bNodeTreeInterfacePanel *parent = this->find_item_parent(item, true);
1465 if (parent == nullptr) {
1466 return false;
1467 }
1468
1469 if (parent == new_parent) {
1470 if (parent->move_item(item, new_position)) {
1471 this->tag_items_changed();
1472 return true;
1473 }
1474 }
1475 else {
1476 /* NOTE: only remove and reinsert when parents different, otherwise removing the item can
1477 * change the desired target position! */
1478 if (parent->remove_item(item, false)) {
1479 new_parent->insert_item(item, new_position);
1480 this->tag_items_changed();
1481 return true;
1482 }
1483 }
1484 return false;
1485}
1486
1487void bNodeTreeInterface::foreach_id(LibraryForeachIDData *cb)
1488{
1490}
1491
1492bool bNodeTreeInterface::items_cache_is_available() const
1493{
1494 return !this->runtime->items_cache_mutex_.is_dirty();
1495}
1496
1497void bNodeTreeInterface::ensure_items_cache() const
1498{
1499 blender::bke::bNodeTreeInterfaceRuntime &runtime = *this->runtime;
1500
1501 runtime.items_cache_mutex_.ensure([&]() {
1502 /* Rebuild draw-order list of interface items for linear access. */
1503 runtime.items_.clear();
1504 runtime.inputs_.clear();
1505 runtime.outputs_.clear();
1506
1507 /* Items in the cache are mutable pointers, but node tree update considers ID data to be
1508 * immutable when caching. DNA ListBase pointers can be mutable even if their container is
1509 * const, but the items returned by #foreach_item inherit qualifiers from the container. */
1510 bNodeTreeInterface &mutable_self = const_cast<bNodeTreeInterface &>(*this);
1511
1512 mutable_self.foreach_item([&](bNodeTreeInterfaceItem &item) {
1513 runtime.items_.add_new(&item);
1515 if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) {
1516 runtime.inputs_.add_new(socket);
1517 }
1518 if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) {
1519 runtime.outputs_.add_new(socket);
1520 }
1521 }
1522 return true;
1523 });
1524 });
1525}
1526
1527void bNodeTreeInterface::tag_interface_changed()
1528{
1529 this->runtime->interface_changed_.store(true);
1530}
1531
1532bool bNodeTreeInterface::requires_dependent_tree_updates() const
1533{
1534 return this->runtime->interface_changed_.load(std::memory_order_relaxed);
1535}
1536
1537void bNodeTreeInterface::tag_items_changed()
1538{
1539 this->tag_interface_changed();
1540 this->runtime->items_cache_mutex_.tag_dirty();
1541}
1542
1543void bNodeTreeInterface::tag_items_changed_generic()
1544{
1545 /* Perform a full update since we don't know what changed exactly. */
1546 this->tag_items_changed();
1547}
1548
1549void bNodeTreeInterface::tag_item_property_changed()
1550{
1551 this->tag_interface_changed();
1552}
1553
1554void bNodeTreeInterface::tag_missing_runtime_data()
1555{
1556 this->tag_items_changed();
1557}
1558
1559void bNodeTreeInterface::reset_interface_changed()
1560{
1561 this->runtime->interface_changed_.store(false);
1562}
void IDP_foreach_property(IDProperty *id_property_root, int type_filter, blender::FunctionRef< void(IDProperty *id_property)> callback)
#define IDP_BlendDataRead(reader, prop)
void IDP_FreePropertyContent_ex(IDProperty *prop, bool do_id_user)
Definition idprop.cc:1213
IDProperty * IDP_CopyProperty_ex(const IDProperty *prop, int flag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:845
void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
Definition idprop.cc:1461
void id_us_plus(ID *id)
Definition lib_id.cc:358
@ LIB_ID_CREATE_NO_USER_REFCOUNT
void id_us_min(ID *id)
Definition lib_id.cc:366
#define BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data_, func_call_)
#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(data_, id_super_, cb_flag_)
@ IDWALK_CB_USER
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_kdtree_nd_ free(KDTree *tree)
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v3_v3(float r[3], const float a[3])
char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
char * BLI_strdup_null(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_MALLOC
Definition string.cc:46
#define SET_FLAG_FROM_TEST(value, test, flag)
#define BLO_read_data_address(reader, ptr_p)
#define BLO_write_struct(writer, struct_name, data_ptr)
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5828
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define BLO_read_struct(reader, struct_name, ptr_p)
void BLO_write_pointer_array(BlendWriter *writer, int64_t num, const void *data_ptr)
void BLO_read_pointer_array(BlendDataReader *reader, int64_t array_size, void **ptr_p)
Definition readfile.cc:5880
@ IDP_TYPE_FILTER_ID
Object groups, one object can be in many groups at once.
@ NODE_INTERFACE_PANEL_ALLOW_SOCKETS_AFTER_PANELS
@ NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY_LEGACY
@ NODE_INTERFACE_SOCKET_PANEL_TOGGLE
@ NODE_INTERFACE_SOCKET_OPTIONAL_LABEL
@ NODE_INTERFACE_SOCKET_MENU_EXPANDED
@ NODE_INTERFACE_SOCKET_HIDE_VALUE
struct bNodeTreeInterface bNodeTreeInterface
struct bNodeTreeInterfaceSocket bNodeTreeInterfaceSocket
struct bNodeTreeInterfacePanel bNodeTreeInterfacePanel
@ NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_SINGLE
struct bNodeTreeInterfaceItem bNodeTreeInterfaceItem
@ SOCK_OUT
@ SOCK_IN
@ SOCK_HIDE_VALUE
@ SOCK_BOOLEAN
@ SOCK_MENU
#define MEM_SAFE_FREE(v)
@ PROP_NONE
Definition RNA_types.hh:233
BMesh const char void * data
constexpr T * data() const
Definition BLI_span.hh:539
constexpr MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:607
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:629
constexpr Span drop_front(int64_t n) const
Definition BLI_span.hh:171
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr Span take_front(int64_t n) const
Definition BLI_span.hh:193
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
constexpr bool is_empty() const
constexpr int64_t size() const
constexpr const char * data() const
#define active
int count
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
bool contains(const VArray< bool > &varray, const IndexMask &indices_to_check, bool value)
void item_write_struct(BlendWriter *writer, bNodeTreeInterfaceItem &item)
static void item_copy(bNodeTreeInterfaceItem &dst, const bNodeTreeInterfaceItem &src, int flag, UidGeneratorFn generate_uid)
static void item_free(bNodeTreeInterfaceItem &item, const bool do_id_user)
static void item_write_data(BlendWriter *writer, bNodeTreeInterfaceItem &item)
static Span< bNodeTreeInterfaceItem * > item_children(bNodeTreeInterfaceItem &item)
static void item_read_data(BlendDataReader *reader, bNodeTreeInterfaceItem &item)
static void item_foreach_id(LibraryForeachIDData *data, bNodeTreeInterfaceItem &item)
static void panel_init(bNodeTreeInterfacePanel &panel, const Span< const bNodeTreeInterfaceItem * > items_src, const int flag, UidGeneratorFn generate_uid)
static std::optional< StringRef > try_get_supported_socket_type(const StringRef socket_type)
static void socket_data_read_data(BlendDataReader *reader, bNodeTreeInterfaceSocket &socket)
void socket_data_foreach_id_impl(LibraryForeachIDData *, T &)
static void * make_socket_data(const StringRef socket_type)
static void socket_data_free(bNodeTreeInterfaceSocket &socket, const bool do_id_user)
void socket_data_to_static_type_tag(const StringRef socket_type, const Fn &fn)
static void socket_data_write(BlendWriter *writer, bNodeTreeInterfaceSocket &socket)
static void socket_data_foreach_id(LibraryForeachIDData *data, bNodeTreeInterfaceSocket &socket)
static void socket_data_copy(bNodeTreeInterfaceSocket &dst, const bNodeTreeInterfaceSocket &src, int flag)
static void socket_data_copy_ptr(bNodeTreeInterfaceSocket &dst, const void *src_socket_data, int flag)
void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueFloat &data)
void socket_data_read_data_impl(BlendDataReader *reader, T **data)
T & get_item_as(bNodeTreeInterfaceItem &item)
static bNodeTreeInterfaceSocket * make_socket(const int uid, const StringRef name, const StringRef description, const StringRef socket_type, const NodeTreeInterfaceSocketFlag flag)
bNodeTreeInterfaceSocket * add_interface_socket_from_node(bNodeTree &ntree, const bNode &from_node, const bNodeSocket &from_sock, StringRef socket_type, StringRef name)
static bNodeTreeInterfacePanel * make_panel(const int uid, const blender::StringRef name, const blender::StringRef description, const NodeTreeInterfacePanelFlag flag)
T & get_socket_data_as(bNodeTreeInterfaceSocket &item)
bNodeSocketType * node_socket_type_find(StringRef idname)
Definition node.cc:2462
std::optional< StringRefNull > node_static_socket_type(int type, int subtype, std::optional< int > dimensions=std::nullopt)
Definition node.cc:2835
bool node_is_static_socket_type(const bNodeSocketType &stype)
Definition node.cc:2826
bool socket_type_supports_default_input_type(const bke::bNodeSocketType &socket_type, const NodeDefaultInputType input_type)
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
static bool is_child(const Object *ob, const Object *parent)
const char * name
#define FLT_MAX
Definition stdcycles.h:14
Definition DNA_ID.h:414
const RuntimeNodeEnumItemsHandle * enum_items
bNodeSocketRuntimeHandle * runtime
char description[64]
void * default_value
char idname[64]
bNodeTreeInterfaceItem ** items_array
bNodeTreeInterfacePanel root_panel
bNodeTreeInterfaceRuntimeHandle * runtime
bNodeTreeInterface tree_interface
struct ID * id
Defines a socket type.
Definition BKE_node.hh:158
void(* draw_color_simple)(const bNodeSocketType *socket_type, float *r_color)
Definition BKE_node.hh:172
eNodeSocketDatatype type
Definition BKE_node.hh:193
void(* interface_from_socket)(ID *id, bNodeTreeInterfaceSocket *interface_socket, const bNode *node, const bNodeSocket *socket)
Definition BKE_node.hh:183
i
Definition text_draw.cc:230
uint8_t flag
Definition wm_window.cc:145