Blender V4.5
NOD_socket_items.hh
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#pragma once
6
19
20#include "BLI_string.h"
21#include "BLI_string_utils.hh"
22
23#include "BKE_node.hh"
24#include "BKE_node_runtime.hh"
25
26#include "DNA_array_utils.hh"
27
28#include "NOD_socket.hh"
29
31
33 static constexpr bool has_single_identifier_str = true;
34 static constexpr bool has_name_validation = false;
35 static constexpr bool has_custom_initial_name = false;
36 static constexpr char unique_name_separator = '.';
37};
38
43template<typename T> struct SocketItemsRef {
47};
48
52template<typename Accessor>
53inline bNode *find_node_by_item(bNodeTree &ntree, const typename Accessor::ItemT &item)
54{
55 ntree.ensure_topology_cache();
56 for (bNode *node : ntree.nodes_by_type(Accessor::node_idname)) {
57 SocketItemsRef array = Accessor::get_items_from_node(*node);
58 if (&item >= *array.items && &item < *array.items + *array.items_num) {
59 return node;
60 }
61 }
62 return nullptr;
63}
64
68template<typename Accessor> inline void destruct_array(bNode &node)
69{
70 using ItemT = typename Accessor::ItemT;
71 SocketItemsRef ref = Accessor::get_items_from_node(node);
72 for (const int i : IndexRange(*ref.items_num)) {
73 ItemT &item = (*ref.items)[i];
74 Accessor::destruct_item(&item);
75 }
76 MEM_SAFE_FREE(*ref.items);
77}
78
82template<typename Accessor> inline void clear(bNode &node)
83{
85 SocketItemsRef ref = Accessor::get_items_from_node(node);
86 *ref.items_num = 0;
87 *ref.active_index = 0;
88}
89
93template<typename Accessor> inline void copy_array(const bNode &src_node, bNode &dst_node)
94{
95 using ItemT = typename Accessor::ItemT;
96 SocketItemsRef src_ref = Accessor::get_items_from_node(const_cast<bNode &>(src_node));
97 SocketItemsRef dst_ref = Accessor::get_items_from_node(dst_node);
98 const int items_num = *src_ref.items_num;
99 *dst_ref.items = MEM_calloc_arrayN<ItemT>(items_num, __func__);
100 for (const int i : IndexRange(items_num)) {
101 Accessor::copy_item((*src_ref.items)[i], (*dst_ref.items)[i]);
102 }
103}
104
108template<typename Accessor> inline std::string get_validated_name(const StringRef name)
109{
110 if constexpr (Accessor::has_name_validation) {
111 return Accessor::validate_name(name);
112 }
113 else {
114 return name;
115 }
116}
117
122template<typename Accessor>
124 typename Accessor::ItemT &item,
125 const char *value)
126{
127 using ItemT = typename Accessor::ItemT;
128 SocketItemsRef array = Accessor::get_items_from_node(node);
129 StringRefNull default_name = "Item";
130 if constexpr (Accessor::has_type) {
131 default_name = *bke::node_static_socket_label(Accessor::get_socket_type(item), 0);
132 }
133
134 const std::string validated_name = get_validated_name<Accessor>(value);
135
136 char unique_name[MAX_NAME + 4];
137 STRNCPY(unique_name, validated_name.c_str());
138
140 [&](const StringRef name) {
141 for (ItemT &item_iter : blender::MutableSpan(*array.items, *array.items_num)) {
142 if (&item_iter != &item) {
143 if (*Accessor::get_name(item_iter) == name) {
144 return true;
145 }
146 }
147 }
148 return false;
149 },
150 default_name.c_str(),
151 Accessor::unique_name_separator,
154
155 /* The unique name should still be valid. */
157
158 char **item_name = Accessor::get_name(item);
159 MEM_SAFE_FREE(*item_name);
160 *item_name = BLI_strdup(unique_name);
161}
162
163namespace detail {
164
165template<typename Accessor> inline typename Accessor::ItemT &add_item_to_array(bNode &node)
166{
167 using ItemT = typename Accessor::ItemT;
168 SocketItemsRef array = Accessor::get_items_from_node(node);
169
170 ItemT *old_items = *array.items;
171 const int old_items_num = *array.items_num;
172 const int new_items_num = old_items_num + 1;
173
174 ItemT *new_items = MEM_calloc_arrayN<ItemT>(new_items_num, __func__);
175 std::copy_n(old_items, old_items_num, new_items);
176 ItemT &new_item = new_items[old_items_num];
177
178 MEM_SAFE_FREE(old_items);
179 *array.items = new_items;
180 *array.items_num = new_items_num;
181 if (array.active_index) {
182 *array.active_index = old_items_num;
183 }
184
185 return new_item;
186}
187
188} // namespace detail
189
193template<typename Accessor>
194inline typename Accessor::ItemT *add_item_with_socket_type_and_name(
195 bNode &node, const eNodeSocketDatatype socket_type, const char *name)
196{
197 using ItemT = typename Accessor::ItemT;
198 BLI_assert(Accessor::supports_socket_type(socket_type));
199 ItemT &new_item = detail::add_item_to_array<Accessor>(node);
200 Accessor::init_with_socket_type_and_name(node, new_item, socket_type, name);
201 return &new_item;
202}
203
207template<typename Accessor>
208inline typename Accessor::ItemT *add_item_with_name(bNode &node, const char *name)
209{
210 using ItemT = typename Accessor::ItemT;
211 ItemT &new_item = detail::add_item_to_array<Accessor>(node);
212 Accessor::init_with_name(node, new_item, name);
213 return &new_item;
214}
215
219template<typename Accessor> inline typename Accessor::ItemT *add_item(bNode &node)
220{
221 using ItemT = typename Accessor::ItemT;
222 ItemT &new_item = detail::add_item_to_array<Accessor>(node);
223 Accessor::init(node, new_item);
224 return &new_item;
225}
226
227template<typename Accessor>
228inline std::string get_socket_identifier(const typename Accessor::ItemT &item,
229 const eNodeSocketInOut in_out)
230{
231 if constexpr (Accessor::has_single_identifier_str) {
232 return Accessor::socket_identifier_for_item(item);
233 }
234 else {
235 if (in_out == SOCK_IN) {
236 return Accessor::input_socket_identifier_for_item(item);
237 }
238 return Accessor::output_socket_identifier_for_item(item);
239 }
240}
241
247template<typename Accessor>
248[[nodiscard]] inline bool try_add_item_via_extend_socket(bNodeTree &ntree,
249 bNode &extend_node,
250 bNodeSocket &extend_socket,
251 bNode &storage_node,
252 bNodeLink &link)
253{
254 using ItemT = typename Accessor::ItemT;
255 bNodeSocket *src_socket = nullptr;
256 if (link.tosock == &extend_socket) {
257 src_socket = link.fromsock;
258 }
259 else if (link.fromsock == &extend_socket) {
260 src_socket = link.tosock;
261 }
262 else {
263 return false;
264 }
265
266 const ItemT *item = nullptr;
267 if constexpr (Accessor::has_name && Accessor::has_type) {
268 const eNodeSocketDatatype socket_type = eNodeSocketDatatype(src_socket->type);
269 if (!Accessor::supports_socket_type(socket_type)) {
270 return false;
271 }
272 std::string name = src_socket->name;
273 if constexpr (Accessor::has_custom_initial_name) {
274 name = Accessor::custom_initial_name(storage_node, name);
275 }
276 item = add_item_with_socket_type_and_name<Accessor>(storage_node, socket_type, name.c_str());
277 }
278 else if constexpr (Accessor::has_name && !Accessor::has_type) {
279 item = add_item_with_name<Accessor>(storage_node, src_socket->name);
280 }
281 else {
282 item = add_item<Accessor>(storage_node);
283 }
284 if (item == nullptr) {
285 return false;
286 }
287
288 update_node_declaration_and_sockets(ntree, extend_node);
289 if (extend_socket.is_input()) {
290 const std::string item_identifier = get_socket_identifier<Accessor>(*item, SOCK_IN);
291 bNodeSocket *new_socket = bke::node_find_socket(extend_node, SOCK_IN, item_identifier.c_str());
292 link.tosock = new_socket;
293 }
294 else {
295 const std::string item_identifier = get_socket_identifier<Accessor>(*item, SOCK_OUT);
297 extend_node, SOCK_OUT, item_identifier.c_str());
298 link.fromsock = new_socket;
299 }
300 return true;
301}
302
307template<typename Accessor>
308[[nodiscard]] inline bool try_add_item_via_any_extend_socket(
309 bNodeTree &ntree,
310 bNode &extend_node,
311 bNode &storage_node,
312 bNodeLink &link,
313 const std::optional<StringRef> socket_identifier = std::nullopt)
314{
315 bNodeSocket *possible_extend_socket = nullptr;
316 if (link.fromnode == &extend_node) {
317 possible_extend_socket = link.fromsock;
318 }
319 if (link.tonode == &extend_node) {
320 possible_extend_socket = link.tosock;
321 }
322 if (possible_extend_socket == nullptr) {
323 return true;
324 }
325 if (!STREQ(possible_extend_socket->idname, "NodeSocketVirtual")) {
326 return true;
327 }
328 if (socket_identifier.has_value()) {
329 if (possible_extend_socket->identifier != socket_identifier) {
330 return true;
331 }
332 }
334 ntree, extend_node, *possible_extend_socket, storage_node, link);
335}
336
337} // namespace blender::nodes::socket_items
#define BLI_assert(a)
Definition BLI_assert.h:46
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
size_t void BLI_uniquename_cb(blender::FunctionRef< bool(blender::StringRefNull)> unique_check, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(2
#define ARRAY_SIZE(arr)
#define STREQ(a, b)
eNodeSocketInOut
@ SOCK_OUT
@ SOCK_IN
eNodeSocketDatatype
constexpr const char * c_str() const
#define MEM_SAFE_FREE(v)
#define MAX_NAME
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
#define T
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2864
std::optional< StringRefNull > node_static_socket_label(int type, int subtype)
Definition node.cc:3484
Accessor::ItemT & add_item_to_array(bNode &node)
void set_item_name_and_make_unique(bNode &node, typename Accessor::ItemT &item, const char *value)
Accessor::ItemT * add_item_with_name(bNode &node, const char *name)
Accessor::ItemT * add_item(bNode &node)
void copy_array(const bNode &src_node, bNode &dst_node)
std::string get_validated_name(const StringRef name)
Accessor::ItemT * add_item_with_socket_type_and_name(bNode &node, const eNodeSocketDatatype socket_type, const char *name)
bool try_add_item_via_any_extend_socket(bNodeTree &ntree, bNode &extend_node, bNode &storage_node, bNodeLink &link, const std::optional< StringRef > socket_identifier=std::nullopt)
bool try_add_item_via_extend_socket(bNodeTree &ntree, bNode &extend_node, bNodeSocket &extend_socket, bNode &storage_node, bNodeLink &link)
bNode * find_node_by_item(bNodeTree &ntree, const typename Accessor::ItemT &item)
std::string get_socket_identifier(const typename Accessor::ItemT &item, const eNodeSocketInOut in_out)
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node)
static void unique_name(bNode *node)
char identifier[64]
char idname[64]
i
Definition text_draw.cc:230