Blender V5.0
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 <optional>
21
22#include "BLI_string.h"
23#include "BLI_string_utils.hh"
24
25#include "BKE_node.hh"
26#include "BKE_node_runtime.hh"
28
29#include "DNA_array_utils.hh"
30
31#include "NOD_socket.hh"
32
34
36 static constexpr bool has_single_identifier_str = true;
37 static constexpr bool has_name_validation = false;
38 static constexpr bool has_custom_initial_name = false;
39 static constexpr bool has_vector_dimensions = false;
40 static constexpr bool can_have_empty_name = false;
41 static constexpr char unique_name_separator = '.';
42};
43
48template<typename T> struct SocketItemsRef {
52};
53
57template<typename Accessor>
58inline bNode *find_node_by_item(bNodeTree &ntree, const typename Accessor::ItemT &item)
59{
60 ntree.ensure_topology_cache();
61 for (bNode *node : ntree.nodes_by_type(Accessor::node_idname)) {
62 SocketItemsRef array = Accessor::get_items_from_node(*node);
63 if (&item >= *array.items && &item < *array.items + *array.items_num) {
64 return node;
65 }
66 }
67 return nullptr;
68}
69
71template<typename Accessor>
72inline typename Accessor::ItemT *find_item_by_identifier(bNode &node, const StringRef identifier)
73{
74
75 SocketItemsRef array = Accessor::get_items_from_node(node);
76 for (const int i : IndexRange(*array.items_num)) {
77 typename Accessor::ItemT &item = (*array.items)[i];
78 if (Accessor::socket_identifier_for_item(item) == identifier) {
79 return &item;
80 }
81 }
82 return nullptr;
83}
84
88template<typename Accessor> inline void destruct_array(bNode &node)
89{
90 using ItemT = typename Accessor::ItemT;
91 SocketItemsRef ref = Accessor::get_items_from_node(node);
92 for (const int i : IndexRange(*ref.items_num)) {
93 ItemT &item = (*ref.items)[i];
94 Accessor::destruct_item(&item);
95 }
96 MEM_SAFE_FREE(*ref.items);
97}
98
102template<typename Accessor> inline void clear(bNode &node)
103{
105 SocketItemsRef ref = Accessor::get_items_from_node(node);
106 *ref.items_num = 0;
107 *ref.active_index = 0;
108}
109
113template<typename Accessor> inline void copy_array(const bNode &src_node, bNode &dst_node)
114{
115 using ItemT = typename Accessor::ItemT;
116 SocketItemsRef src_ref = Accessor::get_items_from_node(const_cast<bNode &>(src_node));
117 SocketItemsRef dst_ref = Accessor::get_items_from_node(dst_node);
118 const int items_num = *src_ref.items_num;
119 *dst_ref.items = MEM_calloc_arrayN<ItemT>(items_num, __func__);
120 for (const int i : IndexRange(items_num)) {
121 Accessor::copy_item((*src_ref.items)[i], (*dst_ref.items)[i]);
122 }
123}
124
128template<typename Accessor> inline std::string get_validated_name(const StringRef name)
129{
130 if constexpr (Accessor::has_name_validation) {
131 return Accessor::validate_name(name);
132 }
133 else {
134 return name;
135 }
136}
137
142template<typename Accessor>
144 typename Accessor::ItemT &item,
145 const char *value)
146{
147 using ItemT = typename Accessor::ItemT;
148 SocketItemsRef array = Accessor::get_items_from_node(node);
149
150 std::string name = value;
151 if constexpr (!Accessor::can_have_empty_name) {
152 if (name.empty()) {
153 if constexpr (Accessor::has_type) {
154 name = *bke::node_static_socket_label(Accessor::get_socket_type(item), 0);
155 }
156 else {
157 name = "Item";
158 }
159 }
160 }
161
162 const std::string validated_name = get_validated_name<Accessor>(name);
163
164 const std::string unique_name = BLI_uniquename_cb(
165 [&](const StringRef name) {
166 for (ItemT &item_iter : blender::MutableSpan(*array.items, *array.items_num)) {
167 if (&item_iter != &item) {
168 if (*Accessor::get_name(item_iter) == name) {
169 return true;
170 }
171 }
172 }
173 return false;
174 },
175 Accessor::unique_name_separator,
176 validated_name);
177
178 /* The unique name should still be valid. */
180
181 char **item_name = Accessor::get_name(item);
182 MEM_SAFE_FREE(*item_name);
183 *item_name = BLI_strdup(unique_name.c_str());
184}
185
186namespace detail {
187
188template<typename Accessor> inline typename Accessor::ItemT &add_item_to_array(bNode &node)
189{
190 using ItemT = typename Accessor::ItemT;
191 SocketItemsRef array = Accessor::get_items_from_node(node);
192
193 ItemT *old_items = *array.items;
194 const int old_items_num = *array.items_num;
195 const int new_items_num = old_items_num + 1;
196
197 ItemT *new_items = MEM_calloc_arrayN<ItemT>(new_items_num, __func__);
198 std::copy_n(old_items, old_items_num, new_items);
199 ItemT &new_item = new_items[old_items_num];
200
201 MEM_SAFE_FREE(old_items);
202 *array.items = new_items;
203 *array.items_num = new_items_num;
204 if (array.active_index) {
205 *array.active_index = old_items_num;
206 }
207
208 return new_item;
209}
210
211} // namespace detail
212
218template<typename Accessor>
219inline typename Accessor::ItemT *add_item_with_socket_type_and_name(
220 bNodeTree &ntree,
221 bNode &node,
222 const eNodeSocketDatatype socket_type,
223 const char *name,
224 std::optional<int> dimensions = std::nullopt)
225{
226 using ItemT = typename Accessor::ItemT;
227 BLI_assert(Accessor::supports_socket_type(socket_type, ntree.type));
228 BLI_assert(!(dimensions.has_value() && socket_type != SOCK_VECTOR));
229 BLI_assert(ELEM(dimensions.value_or(3), 2, 3, 4));
230 UNUSED_VARS_NDEBUG(ntree);
231 ItemT &new_item = detail::add_item_to_array<Accessor>(node);
232 if constexpr (Accessor::has_vector_dimensions) {
233 Accessor::init_with_socket_type_and_name(node, new_item, socket_type, name, dimensions);
234 }
235 else {
236 Accessor::init_with_socket_type_and_name(node, new_item, socket_type, name);
237 }
238 return &new_item;
239}
240
244template<typename Accessor>
245inline typename Accessor::ItemT *add_item_with_name(bNode &node, const char *name)
246{
247 using ItemT = typename Accessor::ItemT;
248 ItemT &new_item = detail::add_item_to_array<Accessor>(node);
249 Accessor::init_with_name(node, new_item, name);
250 return &new_item;
251}
252
256template<typename Accessor> inline typename Accessor::ItemT *add_item(bNode &node)
257{
258 using ItemT = typename Accessor::ItemT;
259 ItemT &new_item = detail::add_item_to_array<Accessor>(node);
260 Accessor::init(node, new_item);
261 return &new_item;
262}
263
264template<typename Accessor>
265inline std::string get_socket_identifier(const typename Accessor::ItemT &item,
266 const eNodeSocketInOut in_out)
267{
268 if constexpr (Accessor::has_single_identifier_str) {
269 return Accessor::socket_identifier_for_item(item);
270 }
271 else {
272 if (in_out == SOCK_IN) {
273 return Accessor::input_socket_identifier_for_item(item);
274 }
275 return Accessor::output_socket_identifier_for_item(item);
276 }
277}
278
279inline std::optional<eNodeSocketDatatype> get_socket_item_type_to_add(
280 const eNodeSocketDatatype linked_type,
281 const FunctionRef<bool(eNodeSocketDatatype type)> is_supported)
282{
283 if (is_supported(linked_type)) {
284 return linked_type;
285 }
286 if (linked_type == SOCK_RGBA) {
287 if (is_supported(SOCK_VECTOR)) {
288 return SOCK_VECTOR;
289 }
290 }
291 return std::nullopt;
292}
293
299template<typename Accessor>
300[[nodiscard]] inline bool try_add_item_via_extend_socket(
301 bNodeTree &ntree,
302 bNode &extend_node,
303 bNodeSocket &extend_socket,
304 bNode &storage_node,
305 bNodeLink &link,
306 typename Accessor::ItemT **r_new_item = nullptr)
307{
308 using ItemT = typename Accessor::ItemT;
309 bNodeSocket *src_socket = nullptr;
310 if (link.tosock == &extend_socket) {
311 src_socket = link.fromsock;
312 }
313 else if (link.fromsock == &extend_socket) {
314 src_socket = link.tosock;
315 }
316 else {
317 return false;
318 }
319
320 ItemT *item = nullptr;
321 if constexpr (Accessor::has_name && Accessor::has_type) {
322 const eNodeSocketDatatype src_socket_type = eNodeSocketDatatype(src_socket->type);
323 const std::optional<eNodeSocketDatatype> added_socket_type = get_socket_item_type_to_add(
324 src_socket_type, [&](const eNodeSocketDatatype type) {
325 return Accessor::supports_socket_type(type, ntree.type);
326 });
327 if (!added_socket_type) {
328 return false;
329 }
330 std::string name = src_socket->name;
331 if constexpr (Accessor::has_custom_initial_name) {
332 name = Accessor::custom_initial_name(storage_node, name);
333 }
334 std::optional<int> dimensions = std::nullopt;
335 if (src_socket_type == SOCK_VECTOR && added_socket_type == SOCK_VECTOR) {
336 dimensions = src_socket->default_value_typed<bNodeSocketValueVector>()->dimensions;
337 }
339 ntree, storage_node, *added_socket_type, name.c_str(), dimensions);
340 }
341 else if constexpr (Accessor::has_name && !Accessor::has_type) {
342 item = add_item_with_name<Accessor>(storage_node, src_socket->name);
343 }
344 else {
345 item = add_item<Accessor>(storage_node);
346 }
347 if (item == nullptr) {
348 return false;
349 }
350 if (r_new_item) {
351 *r_new_item = item;
352 }
353
354 update_node_declaration_and_sockets(ntree, extend_node);
355 if (extend_socket.is_input()) {
356 const std::string item_identifier = get_socket_identifier<Accessor>(*item, SOCK_IN);
357 bNodeSocket *new_socket = bke::node_find_socket(extend_node, SOCK_IN, item_identifier.c_str());
358 link.tosock = new_socket;
359 }
360 else {
361 const std::string item_identifier = get_socket_identifier<Accessor>(*item, SOCK_OUT);
363 extend_node, SOCK_OUT, item_identifier.c_str());
364 link.fromsock = new_socket;
365 }
366 BKE_ntree_update_tag_node_property(&ntree, &storage_node);
367 return true;
368}
369
374template<typename Accessor>
375[[nodiscard]] inline bool try_add_item_via_any_extend_socket(
376 bNodeTree &ntree,
377 bNode &extend_node,
378 bNode &storage_node,
379 bNodeLink &link,
380 const std::optional<StringRef> socket_identifier = std::nullopt,
381 typename Accessor::ItemT **r_new_item = nullptr)
382{
383 bNodeSocket *possible_extend_socket = nullptr;
384 if (link.fromnode == &extend_node) {
385 possible_extend_socket = link.fromsock;
386 }
387 if (link.tonode == &extend_node) {
388 possible_extend_socket = link.tosock;
389 }
390 if (possible_extend_socket == nullptr) {
391 return true;
392 }
393 if (!STREQ(possible_extend_socket->idname, "NodeSocketVirtual")) {
394 return true;
395 }
396 if (socket_identifier.has_value()) {
397 if (possible_extend_socket->identifier != socket_identifier) {
398 return true;
399 }
400 }
402 ntree, extend_node, *possible_extend_socket, storage_node, link, r_new_item);
403}
404
405} // namespace blender::nodes::socket_items
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
#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
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 UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define STREQ(a, b)
eNodeSocketInOut
@ SOCK_OUT
@ SOCK_IN
eNodeSocketDatatype
@ SOCK_VECTOR
@ SOCK_RGBA
#define MEM_SAFE_FREE(v)
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:2532
std::optional< StringRefNull > node_static_socket_label(int type, int subtype)
Definition node.cc:3152
Accessor::ItemT & add_item_to_array(bNode &node)
std::optional< eNodeSocketDatatype > get_socket_item_type_to_add(const eNodeSocketDatatype linked_type, const FunctionRef< bool(eNodeSocketDatatype type)> is_supported)
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 * find_item_by_identifier(bNode &node, const StringRef identifier)
bNode * find_node_by_item(bNodeTree &ntree, const typename Accessor::ItemT &item)
Accessor::ItemT * add_item_with_socket_type_and_name(bNodeTree &ntree, bNode &node, const eNodeSocketDatatype socket_type, const char *name, std::optional< int > dimensions=std::nullopt)
bool try_add_item_via_extend_socket(bNodeTree &ntree, bNode &extend_node, bNodeSocket &extend_socket, bNode &storage_node, bNodeLink &link, typename Accessor::ItemT **r_new_item=nullptr)
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, typename Accessor::ItemT **r_new_item=nullptr)
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)
const char * name
char identifier[64]
char idname[64]
i
Definition text_draw.cc:230