Blender  V3.3
node_tree_ref.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <mutex>
4 
5 #include "NOD_node_tree_ref.hh"
6 
7 #include "BLI_dot_export.hh"
8 #include "BLI_stack.hh"
9 
10 #include "RNA_prototypes.h"
11 
12 namespace blender::nodes {
13 
14 NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
15 {
16  Map<bNode *, NodeRef *> node_mapping;
17 
18  LISTBASE_FOREACH (bNode *, bnode, &btree->nodes) {
19  NodeRef &node = *allocator_.construct<NodeRef>().release();
20 
21  node.tree_ = this;
22  node.bnode_ = bnode;
23  node.id_ = nodes_by_id_.append_and_get_index(&node);
24 
25  LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->inputs) {
26  InputSocketRef &socket = *allocator_.construct<InputSocketRef>().release();
27  socket.node_ = &node;
28  socket.index_ = node.inputs_.append_and_get_index(&socket);
29  socket.is_input_ = true;
30  socket.bsocket_ = bsocket;
31  socket.id_ = sockets_by_id_.append_and_get_index(&socket);
32  }
33 
34  LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->outputs) {
35  OutputSocketRef &socket = *allocator_.construct<OutputSocketRef>().release();
36  socket.node_ = &node;
37  socket.index_ = node.outputs_.append_and_get_index(&socket);
38  socket.is_input_ = false;
39  socket.bsocket_ = bsocket;
40  socket.id_ = sockets_by_id_.append_and_get_index(&socket);
41  }
42 
43  LISTBASE_FOREACH (bNodeLink *, blink, &bnode->internal_links) {
44  InternalLinkRef &internal_link = *allocator_.construct<InternalLinkRef>().release();
45  internal_link.blink_ = blink;
46  for (InputSocketRef *socket_ref : node.inputs_) {
47  if (socket_ref->bsocket_ == blink->fromsock) {
48  internal_link.from_ = socket_ref;
49  break;
50  }
51  }
52  for (OutputSocketRef *socket_ref : node.outputs_) {
53  if (socket_ref->bsocket_ == blink->tosock) {
54  internal_link.to_ = socket_ref;
55  break;
56  }
57  }
58  BLI_assert(internal_link.from_ != nullptr);
59  BLI_assert(internal_link.to_ != nullptr);
60  node.internal_links_.append(&internal_link);
61  }
62 
63  input_sockets_.extend(node.inputs_.as_span());
64  output_sockets_.extend(node.outputs_.as_span());
65 
66  node_mapping.add_new(bnode, &node);
67  }
68 
69  LISTBASE_FOREACH (bNodeLink *, blink, &btree->links) {
70  OutputSocketRef &from_socket = this->find_output_socket(
71  node_mapping, blink->fromnode, blink->fromsock);
72  InputSocketRef &to_socket = this->find_input_socket(
73  node_mapping, blink->tonode, blink->tosock);
74 
75  LinkRef &link = *allocator_.construct<LinkRef>().release();
76  link.from_ = &from_socket;
77  link.to_ = &to_socket;
78  link.blink_ = blink;
79 
80  links_.append(&link);
81 
82  from_socket.directly_linked_links_.append(&link);
83  to_socket.directly_linked_links_.append(&link);
84  }
85 
86  for (InputSocketRef *input_socket : input_sockets_) {
87  if (input_socket->is_multi_input_socket()) {
88  std::sort(input_socket->directly_linked_links_.begin(),
89  input_socket->directly_linked_links_.end(),
90  [&](const LinkRef *a, const LinkRef *b) -> bool {
91  int index_a = a->blink()->multi_input_socket_index;
92  int index_b = b->blink()->multi_input_socket_index;
93  return index_a > index_b;
94  });
95  }
96  }
97 
98  this->create_socket_identifier_maps();
99  this->create_linked_socket_caches();
100 
101  for (NodeRef *node : nodes_by_id_) {
102  const bNodeType *nodetype = node->bnode_->typeinfo;
103  nodes_by_type_.add(nodetype, node);
104  }
105 
106  const Span<const NodeRef *> group_output_nodes = this->nodes_by_type("NodeGroupOutput");
107  if (group_output_nodes.is_empty()) {
108  group_output_node_ = nullptr;
109  }
110  else if (group_output_nodes.size() == 1) {
111  group_output_node_ = group_output_nodes.first();
112  }
113  else {
114  for (const NodeRef *group_output : group_output_nodes) {
115  if (group_output->bnode_->flag & NODE_DO_OUTPUT) {
116  group_output_node_ = group_output;
117  break;
118  }
119  }
120  }
121 }
122 
124 {
125  /* The destructor has to be called manually, because these types are allocated in a linear
126  * allocator. */
127  for (NodeRef *node : nodes_by_id_) {
128  node->~NodeRef();
129  }
130  for (InputSocketRef *socket : input_sockets_) {
131  socket->~InputSocketRef();
132  }
133  for (OutputSocketRef *socket : output_sockets_) {
134  socket->~OutputSocketRef();
135  }
136  for (LinkRef *link : links_) {
137  link->~LinkRef();
138  }
139 }
140 
141 InputSocketRef &NodeTreeRef::find_input_socket(Map<bNode *, NodeRef *> &node_mapping,
142  bNode *bnode,
143  bNodeSocket *bsocket)
144 {
145  NodeRef *node = node_mapping.lookup(bnode);
146  for (InputSocketRef *socket : node->inputs_) {
147  if (socket->bsocket_ == bsocket) {
148  return *socket;
149  }
150  }
152  return *node->inputs_[0];
153 }
154 
155 OutputSocketRef &NodeTreeRef::find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
156  bNode *bnode,
157  bNodeSocket *bsocket)
158 {
159  NodeRef *node = node_mapping.lookup(bnode);
160  for (OutputSocketRef *socket : node->outputs_) {
161  if (socket->bsocket_ == bsocket) {
162  return *socket;
163  }
164  }
166  return *node->outputs_[0];
167 }
168 
169 void NodeTreeRef::create_linked_socket_caches()
170 {
171  for (InputSocketRef *socket : input_sockets_) {
172  /* Find directly linked socket based on incident links. */
173  Vector<const SocketRef *> directly_linked_sockets;
174  for (LinkRef *link : socket->directly_linked_links_) {
175  directly_linked_sockets.append(link->from_);
176  }
177  socket->directly_linked_sockets_ = allocator_.construct_array_copy(
178  directly_linked_sockets.as_span());
179 
180  /* Find logically linked sockets. */
181  Vector<const SocketRef *> logically_linked_sockets;
182  Vector<const SocketRef *> logically_linked_skipped_sockets;
183  Vector<const InputSocketRef *> seen_sockets_stack;
184  socket->foreach_logical_origin(
185  [&](const OutputSocketRef &origin) { logically_linked_sockets.append(&origin); },
186  [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
187  false,
188  seen_sockets_stack);
189  if (logically_linked_sockets == directly_linked_sockets) {
190  socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
191  }
192  else {
193  socket->logically_linked_sockets_ = allocator_.construct_array_copy(
194  logically_linked_sockets.as_span());
195  }
196  socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
197  logically_linked_skipped_sockets.as_span());
198  }
199 
200  for (OutputSocketRef *socket : output_sockets_) {
201  /* Find directly linked socket based on incident links. */
202  Vector<const SocketRef *> directly_linked_sockets;
203  for (LinkRef *link : socket->directly_linked_links_) {
204  directly_linked_sockets.append(link->to_);
205  }
206  socket->directly_linked_sockets_ = allocator_.construct_array_copy(
207  directly_linked_sockets.as_span());
208 
209  /* Find logically linked sockets. */
210  Vector<const SocketRef *> logically_linked_sockets;
211  Vector<const SocketRef *> logically_linked_skipped_sockets;
212  Vector<const OutputSocketRef *> handled_sockets;
213  socket->foreach_logical_target(
214  [&](const InputSocketRef &target) { logically_linked_sockets.append(&target); },
215  [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
216  handled_sockets);
217  if (logically_linked_sockets == directly_linked_sockets) {
218  socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
219  }
220  else {
221  socket->logically_linked_sockets_ = allocator_.construct_array_copy(
222  logically_linked_sockets.as_span());
223  }
224  socket->logically_linked_skipped_sockets_ = allocator_.construct_array_copy(
225  logically_linked_skipped_sockets.as_span());
226  }
227 }
228 
229 void InputSocketRef::foreach_logical_origin(
230  FunctionRef<void(const OutputSocketRef &)> origin_fn,
231  FunctionRef<void(const SocketRef &)> skipped_fn,
232  bool only_follow_first_input_link,
233  Vector<const InputSocketRef *> &seen_sockets_stack) const
234 {
235  /* Protect against loops. */
236  if (seen_sockets_stack.contains(this)) {
237  return;
238  }
239  seen_sockets_stack.append(this);
240 
241  Span<const LinkRef *> links_to_check = this->directly_linked_links();
242  if (only_follow_first_input_link) {
243  links_to_check = links_to_check.take_front(1);
244  }
245  for (const LinkRef *link : links_to_check) {
246  if (link->is_muted()) {
247  continue;
248  }
249  const OutputSocketRef &origin = link->from();
250  const NodeRef &origin_node = origin.node();
251  if (!origin.is_available()) {
252  /* Non available sockets are ignored. */
253  }
254  else if (origin_node.is_reroute_node()) {
255  const InputSocketRef &reroute_input = origin_node.input(0);
256  const OutputSocketRef &reroute_output = origin_node.output(0);
257  skipped_fn.call_safe(reroute_input);
258  skipped_fn.call_safe(reroute_output);
259  reroute_input.foreach_logical_origin(origin_fn, skipped_fn, false, seen_sockets_stack);
260  }
261  else if (origin_node.is_muted()) {
262  for (const InternalLinkRef *internal_link : origin_node.internal_links()) {
263  if (&internal_link->to() == &origin) {
264  const InputSocketRef &mute_input = internal_link->from();
265  skipped_fn.call_safe(origin);
266  skipped_fn.call_safe(mute_input);
267  mute_input.foreach_logical_origin(origin_fn, skipped_fn, true, seen_sockets_stack);
268  }
269  }
270  }
271  else {
272  origin_fn(origin);
273  }
274  }
275 
276  seen_sockets_stack.pop_last();
277 }
278 
279 void OutputSocketRef::foreach_logical_target(
280  FunctionRef<void(const InputSocketRef &)> target_fn,
281  FunctionRef<void(const SocketRef &)> skipped_fn,
282  Vector<const OutputSocketRef *> &seen_sockets_stack) const
283 {
284  /* Protect against loops. */
285  if (seen_sockets_stack.contains(this)) {
286  return;
287  }
288  seen_sockets_stack.append(this);
289 
290  for (const LinkRef *link : this->directly_linked_links()) {
291  if (link->is_muted()) {
292  continue;
293  }
294  const InputSocketRef &target = link->to();
295  const NodeRef &target_node = target.node();
296  if (!target.is_available()) {
297  /* Non available sockets are ignored. */
298  }
299  else if (target_node.is_reroute_node()) {
300  const OutputSocketRef &reroute_output = target_node.output(0);
301  skipped_fn.call_safe(target);
302  skipped_fn.call_safe(reroute_output);
303  reroute_output.foreach_logical_target(target_fn, skipped_fn, seen_sockets_stack);
304  }
305  else if (target_node.is_muted()) {
306  skipped_fn.call_safe(target);
307  for (const InternalLinkRef *internal_link : target_node.internal_links()) {
308  if (&internal_link->from() == &target) {
309  /* The internal link only forwards the first incoming link. */
310  if (target.is_multi_input_socket()) {
311  if (target.directly_linked_links()[0] != link) {
312  continue;
313  }
314  }
315  const OutputSocketRef &mute_output = internal_link->to();
316  skipped_fn.call_safe(target);
317  skipped_fn.call_safe(mute_output);
318  mute_output.foreach_logical_target(target_fn, skipped_fn, seen_sockets_stack);
319  }
320  }
321  }
322  else {
323  target_fn(target);
324  }
325  }
326 
327  seen_sockets_stack.pop_last();
328 }
329 
330 namespace {
331 struct SocketByIdentifierMap {
333  std::unique_ptr<SocketIndexByIdentifierMap> owned_map;
334 };
335 } // namespace
336 
337 static std::unique_ptr<SocketIndexByIdentifierMap> create_identifier_map(const ListBase &sockets)
338 {
339  std::unique_ptr<SocketIndexByIdentifierMap> map = std::make_unique<SocketIndexByIdentifierMap>();
340  int index;
341  LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &sockets, index) {
342  map->add_new(socket->identifier, index);
343  }
344  return map;
345 }
346 
347 /* This function is not threadsafe. */
348 static SocketByIdentifierMap get_or_create_identifier_map(
349  const bNode &node, const ListBase &sockets, const bNodeSocketTemplate *sockets_template)
350 {
351  SocketByIdentifierMap map;
352  if (sockets_template == nullptr) {
353  if (BLI_listbase_is_empty(&sockets)) {
354  static SocketIndexByIdentifierMap empty_map;
355  map.map = &empty_map;
356  }
357  else if (node.type == NODE_REROUTE) {
358  if (&node.inputs == &sockets) {
359  static SocketIndexByIdentifierMap reroute_input_map = [] {
361  map.add_new("Input", 0);
362  return map;
363  }();
364  map.map = &reroute_input_map;
365  }
366  else {
367  static SocketIndexByIdentifierMap reroute_output_map = [] {
369  map.add_new("Output", 0);
370  return map;
371  }();
372  map.map = &reroute_output_map;
373  }
374  }
375  else {
376  /* The node has a dynamic amount of sockets. Therefore we need to create a new map. */
377  map.owned_map = create_identifier_map(sockets);
378  map.map = &*map.owned_map;
379  }
380  }
381  else {
382  /* Cache only one map for nodes that have the same sockets. */
384  map.map = &*maps.lookup_or_add_cb(sockets_template,
385  [&]() { return create_identifier_map(sockets); });
386  }
387  return map;
388 }
389 
390 void NodeTreeRef::create_socket_identifier_maps()
391 {
392  /* `get_or_create_identifier_map` is not threadsafe, therefore we have to hold a lock here. */
393  static std::mutex mutex;
394  std::lock_guard lock{mutex};
395 
396  for (NodeRef *node : nodes_by_id_) {
397  bNode &bnode = *node->bnode_;
398  SocketByIdentifierMap inputs_map = get_or_create_identifier_map(
399  bnode, bnode.inputs, bnode.typeinfo->inputs);
400  SocketByIdentifierMap outputs_map = get_or_create_identifier_map(
401  bnode, bnode.outputs, bnode.typeinfo->outputs);
402  node->input_index_by_identifier_ = inputs_map.map;
403  node->output_index_by_identifier_ = outputs_map.map;
404  if (inputs_map.owned_map) {
405  owned_identifier_maps_.append(std::move(inputs_map.owned_map));
406  }
407  if (outputs_map.owned_map) {
408  owned_identifier_maps_.append(std::move(outputs_map.owned_map));
409  }
410  }
411 }
412 
415  MutableSpan<bool> is_in_stack)
416 {
417  const int node_id = node.id();
418  if (is_in_stack[node_id]) {
419  return true;
420  }
421  if (visited[node_id]) {
422  return false;
423  }
424 
425  visited[node_id] = true;
426  is_in_stack[node_id] = true;
427 
428  for (const OutputSocketRef *from_socket : node.outputs()) {
429  if (!from_socket->is_available()) {
430  continue;
431  }
432  for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
433  if (!to_socket->is_available()) {
434  continue;
435  }
436  const NodeRef &to_node = to_socket->node();
437  if (has_link_cycles_recursive(to_node, visited, is_in_stack)) {
438  return true;
439  }
440  }
441  }
442 
443  is_in_stack[node_id] = false;
444  return false;
445 }
446 
448 {
449  const int node_amount = nodes_by_id_.size();
450  Array<bool> visited(node_amount, false);
451  Array<bool> is_in_stack(node_amount, false);
452 
453  for (const NodeRef *node : nodes_by_id_) {
454  if (has_link_cycles_recursive(*node, visited, is_in_stack)) {
455  return true;
456  }
457  }
458  return false;
459 }
460 
462 {
463  for (const NodeRef *node : nodes_by_id_) {
464  if (node->is_undefined()) {
465  return true;
466  }
467  }
468  for (const SocketRef *socket : sockets_by_id_) {
469  if (socket->is_undefined()) {
470  return true;
471  }
472  }
473  return false;
474 }
475 
477 {
478  for (const SocketRef *socket : inputs_) {
479  if (!socket->directly_linked_sockets().is_empty()) {
480  return true;
481  }
482  }
483  return false;
484 }
485 
487 {
488  for (const SocketRef *socket : outputs_) {
489  if (!socket->directly_linked_sockets().is_empty()) {
490  return true;
491  }
492  }
493  return false;
494 }
495 
497 {
498  if (in_out == SOCK_IN) {
499  return this->any_input_is_directly_linked();
500  }
501  return this->any_output_is_directly_linked();
502 }
503 
505  bool is_done = false;
506  bool is_in_stack = false;
507 };
508 
510  const NodeRef &start_node,
511  MutableSpan<ToposortNodeState> node_states,
513 {
514  struct Item {
515  const NodeRef *node;
516  /* Index of the next socket that is checked in the depth-first search. */
517  int socket_index = 0;
518  /* Link index in the next socket that is checked in the depth-first search. */
519  int link_index = 0;
520  };
521 
522  /* Do a depth-first search to sort nodes topologically. */
523  Stack<Item, 64> nodes_to_check;
524  nodes_to_check.push({&start_node});
525  node_states[start_node.id()].is_in_stack = true;
526  while (!nodes_to_check.is_empty()) {
527  Item &item = nodes_to_check.peek();
528  const NodeRef &node = *item.node;
529  const Span<const SocketRef *> sockets = node.sockets(
531 
532  while (true) {
533  if (item.socket_index == sockets.size()) {
534  /* All sockets have already been visited. */
535  break;
536  }
537  const SocketRef &socket = *sockets[item.socket_index];
538  const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets();
539  if (item.link_index == linked_sockets.size()) {
540  /* All links connected to this socket have already been visited. */
541  item.socket_index++;
542  item.link_index = 0;
543  continue;
544  }
545  const SocketRef &linked_socket = *linked_sockets[item.link_index];
546  const NodeRef &linked_node = linked_socket.node();
547  ToposortNodeState &linked_node_state = node_states[linked_node.id()];
548  if (linked_node_state.is_done) {
549  /* The linked node has already been visited. */
550  item.link_index++;
551  continue;
552  }
553  if (linked_node_state.is_in_stack) {
554  result.has_cycle = true;
555  }
556  else {
557  nodes_to_check.push({&linked_node});
558  linked_node_state.is_in_stack = true;
559  }
560  break;
561  }
562 
563  /* If no other element has been pushed, the current node can be pushed to the sorted list. */
564  if (&item == &nodes_to_check.peek()) {
565  ToposortNodeState &node_state = node_states[node.id()];
566  node_state.is_done = true;
567  node_state.is_in_stack = false;
568  result.sorted_nodes.append(&node);
569  nodes_to_check.pop();
570  }
571  }
572 }
573 
575 {
577  result.sorted_nodes.reserve(nodes_by_id_.size());
578 
579  Array<ToposortNodeState> node_states(nodes_by_id_.size());
580 
581  for (const NodeRef *node : nodes_by_id_) {
582  if (node_states[node->id()].is_done) {
583  /* Ignore nodes that are done already. */
584  continue;
585  }
586  if (node->any_socket_is_directly_linked(
587  direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) {
588  /* Ignore non-start nodes. */
589  continue;
590  }
591 
592  toposort_from_start_node(direction, *node, node_states, result);
593  }
594 
595  /* Check if the loop above forgot some nodes because there is a cycle. */
596  if (result.sorted_nodes.size() < nodes_by_id_.size()) {
597  result.has_cycle = true;
598  for (const NodeRef *node : nodes_by_id_) {
599  if (node_states[node->id()].is_done) {
600  /* Ignore nodes that are done already. */
601  continue;
602  }
603  /* Start toposort at this node which is somewhere in the middle of a loop. */
604  toposort_from_start_node(direction, *node, node_states, result);
605  }
606  }
607 
608  BLI_assert(result.sorted_nodes.size() == nodes_by_id_.size());
609  return result;
610 }
611 
612 const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const
613 {
614  for (const NodeRef *node : this->nodes_by_type(bnode.typeinfo)) {
615  if (node->bnode_ == &bnode) {
616  return node;
617  }
618  }
619  return nullptr;
620 }
621 
622 std::string NodeTreeRef::to_dot() const
623 {
624  dot::DirectedGraph digraph;
625  digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
626 
628 
629  for (const NodeRef *node : nodes_by_id_) {
630  dot::Node &dot_node = digraph.new_node("");
631  dot_node.set_background_color("white");
632 
633  Vector<std::string> input_names;
634  Vector<std::string> output_names;
635  for (const InputSocketRef *socket : node->inputs()) {
636  input_names.append(socket->name());
637  }
638  for (const OutputSocketRef *socket : node->outputs()) {
639  output_names.append(socket->name());
640  }
641 
642  dot_nodes.add_new(node,
643  dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
644  }
645 
646  for (const OutputSocketRef *from_socket : output_sockets_) {
647  for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
648  dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&from_socket->node());
649  dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&to_socket->node());
650 
651  digraph.new_edge(from_dot_node.output(from_socket->index()),
652  to_dot_node.input(to_socket->index()));
653  }
654  }
655 
656  return digraph.to_dot_string();
657 }
658 
660 {
661  return *node_tree_refs.lookup_or_add_cb(&btree,
662  [&]() { return std::make_unique<NodeTreeRef>(&btree); });
663 }
664 
666 {
667  PointerRNA rna;
668  RNA_pointer_create(&tree_->btree()->id, &RNA_Node, bnode_, &rna);
669  return rna;
670 }
671 
673 {
674  PointerRNA rna;
675  RNA_pointer_create(&this->tree().btree()->id, &RNA_NodeSocket, bsocket_, &rna);
676  return rna;
677 }
678 
679 } // namespace blender::nodes
#define NODE_REROUTE
Definition: BKE_node.h:986
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
Definition: BLI_listbase.h:344
ThreadMutex mutex
#define NODE_DO_OUTPUT
eNodeSocketInOut
@ SOCK_OUT
@ SOCK_IN
volatile int lock
void sort(btMatrix3x3 &U, btVector3 &sigma, btMatrix3x3 &V, int t)
Helper function of 3X3 SVD for sorting singular values.
MutableSpan< T > construct_array_copy(Span< T > src)
destruct_ptr< T > construct(Args &&...args)
const Value & lookup(const Key &key) const
Definition: BLI_map.hh:485
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition: BLI_map.hh:561
void add_new(const Key &key, const Value &value)
Definition: BLI_map.hh:220
constexpr const T & first() const
Definition: BLI_span.hh:303
constexpr int64_t size() const
Definition: BLI_span.hh:240
constexpr bool is_empty() const
Definition: BLI_span.hh:248
bool is_empty() const
Definition: BLI_stack.hh:308
void push(const T &value)
Definition: BLI_stack.hh:213
void append(const T &value)
Definition: BLI_vector.hh:433
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition: dot_export.cc:37
std::string to_dot_string() const
Definition: dot_export.cc:121
Node & new_node(StringRef label)
Definition: dot_export.cc:12
void set_rankdir(Attr_rankdir rankdir)
NodePort output(int index) const
NodePort input(int index) const
void set_background_color(StringRef name)
Span< const OutputSocketRef * > directly_linked_sockets() const
bool any_output_is_directly_linked() const
PointerRNA rna() const
bool any_socket_is_directly_linked(eNodeSocketInOut in_out) const
bool any_input_is_directly_linked() const
ToposortResult toposort(ToposortDirection direction) const
bool has_undefined_nodes_or_sockets() const
const NodeRef * find_node(const bNode &bnode) const
NodeTreeRef(bNodeTree *btree)
std::string to_dot() const
Span< const NodeRef * > nodes_by_type(StringRefNull idname) const
const NodeRef & node() const
Vector< LinkRef * > directly_linked_links_
PointerRNA rna() const
Span< const SocketRef * > directly_linked_sockets() const
const NodeTreeRef & tree() const
Span< const LinkRef * > directly_linked_links() const
OperationNode * node
Set< ComponentNode * > visited
static unsigned a[3]
Definition: RandGen.cpp:78
Map< std::string, int > SocketIndexByIdentifierMap
static bool has_link_cycles_recursive(const NodeRef &node, MutableSpan< bool > visited, MutableSpan< bool > is_in_stack)
const NodeTreeRef & get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree)
static std::unique_ptr< SocketIndexByIdentifierMap > create_identifier_map(const ListBase &sockets)
static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direction, const NodeRef &start_node, MutableSpan< ToposortNodeState > node_states, NodeTreeRef::ToposortResult &result)
static SocketByIdentifierMap get_or_create_identifier_map(const bNode &node, const ListBase &sockets, const bNodeSocketTemplate *sockets_template)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
std::unique_ptr< SocketIndexByIdentifierMap > owned_map
SocketIndexByIdentifierMap * map
void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
Definition: rna_access.c:136
Compact definition of a node socket.
Definition: BKE_node.h:84
ListBase nodes
ListBase links
Defines a node type.
Definition: BKE_node.h:226
bNodeSocketTemplate * outputs
Definition: BKE_node.h:239
bNodeSocketTemplate * inputs
Definition: BKE_node.h:239
ListBase inputs
struct bNodeType * typeinfo
ListBase outputs