Blender V5.0
node_tree_zones.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 <iostream>
6
7#include "BKE_node.hh"
8#include "BKE_node_runtime.hh"
10
12#include "BLI_bit_span_ops.hh"
13#include "BLI_set.hh"
15
16namespace blender::bke {
17
19{
20 if (zone.depth >= 0) {
21 return;
22 }
23 if (zone.parent_zone == nullptr) {
24 zone.depth = 0;
25 return;
26 }
28 zone.depth = zone.parent_zone->depth + 1;
29}
30
32 const bNodeTree &tree,
33 bNodeTreeZones &owner,
34 Map<const bNode *, bNodeTreeZone *> &r_zone_by_inout_node)
35{
37
39 Vector<const bNode *> zone_output_nodes;
40 for (const bNodeZoneType *zone_type : zone_types) {
41 zone_output_nodes.extend(tree.nodes_by_type(zone_type->output_idname));
42 }
43 for (const bNode *node : zone_output_nodes) {
44 auto zone = std::make_unique<bNodeTreeZone>();
45 zone->owner = &owner;
46 zone->index = zones.size();
47 zone->output_node_id = node->identifier;
48 r_zone_by_inout_node.add(node, zone.get());
49 zones.append(std::move(zone));
50 }
51 for (const bNodeZoneType *zone_type : zone_types) {
52 for (const bNode *input_node : tree.nodes_by_type(zone_type->input_idname)) {
53 if (const bNode *output_node = zone_type->get_corresponding_output(tree, *input_node)) {
54 if (bNodeTreeZone *zone = r_zone_by_inout_node.lookup_default(output_node, nullptr)) {
55 zone->input_node_id = input_node->identifier;
56 r_zone_by_inout_node.add(input_node, zone);
57 }
58 }
59 }
60 }
61 /* Avoid incomplete zones, all zones must have a valid input and output node. */
62 for (const std::unique_ptr<bNodeTreeZone> &zone : zones) {
63 if (!zone->input_node_id || !zone->output_node_id) {
64 r_zone_by_inout_node.clear();
65 return {};
66 }
67 }
68 return zones;
69}
70
74
75 uint64_t hash() const
76 {
77 return get_default_hash(this->parent, this->child);
78 }
79
81};
82
83static std::optional<Vector<ZoneRelation>> get_direct_zone_relations(
84 const Span<bNodeTreeZone *> all_zones, const BitGroupVector<> &depend_on_input_flag_array)
85{
86 VectorSet<ZoneRelation> all_zone_relations;
87
88 /* Gather all relations, even the transitive once. */
89 for (bNodeTreeZone *zone : all_zones) {
90 const int zone_i = zone->index;
91 for (const bNode *node : {zone->output_node()}) {
92 if (node == nullptr) {
93 continue;
94 }
95 const BoundedBitSpan depend_on_input_flags = depend_on_input_flag_array[node->index()];
96 bits::foreach_1_index(depend_on_input_flags, [&](const int parent_zone_i) {
97 if (parent_zone_i != zone_i) {
98 all_zone_relations.add_new({all_zones[parent_zone_i], zone});
99 }
100 });
101 }
102 }
103
104 for (const ZoneRelation &relation : all_zone_relations) {
105 const ZoneRelation reverse_relation{relation.child, relation.parent};
106 if (all_zone_relations.contains(reverse_relation)) {
107 /* There is a cyclic zone dependency. */
108 return std::nullopt;
109 }
110 }
111
112 /* Remove transitive relations. This is a brute force algorithm currently. */
113 Vector<int> transitive_relations;
114 for (const int a : all_zone_relations.index_range()) {
115 const ZoneRelation &relation_a = all_zone_relations[a];
116 for (const int b : all_zone_relations.index_range()) {
117 if (a == b) {
118 continue;
119 }
120 const ZoneRelation &relation_b = all_zone_relations[b];
121 if (relation_a.child != relation_b.parent) {
122 continue;
123 }
124 const ZoneRelation transitive_relation{relation_a.parent, relation_b.child};
125 const int transitive_relation_i = all_zone_relations.index_of_try(transitive_relation);
126 if (transitive_relation_i != -1) {
127 transitive_relations.append_non_duplicates(transitive_relation_i);
128 }
129 }
130 }
131 std::sort(transitive_relations.begin(), transitive_relations.end(), std::greater<>());
132
133 Vector<ZoneRelation> zone_relations = all_zone_relations.as_span();
134 for (const int i : transitive_relations) {
135 zone_relations.remove_and_reorder(i);
136 }
137
138 return zone_relations;
139}
140
141static bool update_zone_per_node(const Span<const bNode *> all_nodes,
142 const Span<bNodeTreeZone *> all_zones,
143 const BitGroupVector<> &depend_on_input_flag_array,
144 const Map<const bNode *, bNodeTreeZone *> &zone_by_inout_node,
145 Map<int, int> &r_zone_by_node_id,
146 Vector<int> &r_node_outside_zones)
147{
148 bool found_node_in_multiple_zones = false;
149 for (const int node_i : all_nodes.index_range()) {
150 const bNode &node = *all_nodes[node_i];
151 const BoundedBitSpan depend_on_input_flags = depend_on_input_flag_array[node_i];
152 bNodeTreeZone *parent_zone = nullptr;
153 bits::foreach_1_index(depend_on_input_flags, [&](const int parent_zone_i) {
154 bNodeTreeZone *zone = all_zones[parent_zone_i];
155 if (ELEM(&node, zone->input_node(), zone->output_node())) {
156 return;
157 }
158 if (parent_zone == nullptr) {
159 parent_zone = zone;
160 return;
161 }
162 for (bNodeTreeZone *iter_zone = zone->parent_zone; iter_zone;
163 iter_zone = iter_zone->parent_zone)
164 {
165 if (iter_zone == parent_zone) {
166 /* This zone is nested in the parent zone, so it becomes the new parent of the node. */
167 parent_zone = zone;
168 return;
169 }
170 }
171 for (bNodeTreeZone *iter_zone = parent_zone->parent_zone; iter_zone;
172 iter_zone = iter_zone->parent_zone)
173 {
174 if (iter_zone == zone) {
175 /* This zone is a parent of the current parent of the node, do nothing. */
176 return;
177 }
178 }
179 found_node_in_multiple_zones = true;
180 });
181 if (parent_zone == nullptr) {
182 if (!zone_by_inout_node.contains(&node)) {
183 r_node_outside_zones.append(node.identifier);
184 }
185 }
186 else {
187 r_zone_by_node_id.add(node.identifier, parent_zone->index);
188 }
189 }
190 for (const MapItem<const bNode *, bNodeTreeZone *> item : zone_by_inout_node.items()) {
191 r_zone_by_node_id.add_overwrite(item.key->identifier, item.value->index);
192 }
193 return found_node_in_multiple_zones;
194}
195
197{
198 tree.ensure_topology_cache();
199 for (const bNodeLink *link : tree.all_links()) {
200 if (!link->is_available()) {
201 continue;
202 }
203 if (link->is_muted()) {
204 continue;
205 }
206 if (link->fromnode->is_dangling_reroute()) {
207 continue;
208 }
209 bNodeTreeZone *from_zone = const_cast<bNodeTreeZone *>(
210 tree_zones.get_zone_by_socket(*link->fromsock));
211 bNodeTreeZone *to_zone = const_cast<bNodeTreeZone *>(
212 tree_zones.get_zone_by_socket(*link->tosock));
213 if (from_zone == to_zone) {
214 continue;
215 }
216 BLI_assert(from_zone == nullptr || from_zone->contains_zone_recursively(*to_zone));
217 for (bNodeTreeZone *zone = to_zone; zone != from_zone; zone = zone->parent_zone) {
218 zone->border_links.append(link);
219 }
220 }
221}
222
223static std::unique_ptr<bNodeTreeZones> discover_tree_zones(const bNodeTree &tree)
224{
225 tree.ensure_topology_cache();
226 if (tree.has_available_link_cycle()) {
227 return {};
228 }
229
230 const Span<int> input_types = all_zone_input_node_types();
231 const Span<int> output_types = all_zone_output_node_types();
232
233 std::unique_ptr<bNodeTreeZones> tree_zones = std::make_unique<bNodeTreeZones>();
234 tree_zones->tree = &tree;
235
236 const Span<const bNode *> all_nodes = tree.all_nodes();
237 Map<const bNode *, bNodeTreeZone *> zone_by_inout_node;
238 tree_zones->zones_ptrs = find_zone_nodes(tree, *tree_zones, zone_by_inout_node);
239 for (const std::unique_ptr<bNodeTreeZone> &zone : tree_zones->zones_ptrs) {
240 tree_zones->zones.append(zone.get());
241 }
242
243 const int zones_num = tree_zones->zones.size();
244 const int nodes_num = all_nodes.size();
245 /* A bit for every node-zone-combination. The bit is set when the node is in the zone. */
246 BitGroupVector<> depend_on_input_flag_array(nodes_num, zones_num, false);
247 /* The bit is set when the node depends on the output of the zone. */
248 BitGroupVector<> depend_on_output_flag_array(nodes_num, zones_num, false);
249
250 const Span<const bNode *> sorted_nodes = tree.toposort_left_to_right();
251 for (const bNode *node : sorted_nodes) {
252 const int node_i = node->index();
253 MutableBoundedBitSpan depend_on_input_flags = depend_on_input_flag_array[node_i];
254 MutableBoundedBitSpan depend_on_output_flags = depend_on_output_flag_array[node_i];
255
256 /* Forward all bits from the nodes to the left. */
257 for (const bNodeSocket *input_socket : node->input_sockets()) {
258 if (!input_socket->is_available()) {
259 continue;
260 }
261 for (const bNodeLink *link : input_socket->directly_linked_links()) {
262 if (link->is_muted()) {
263 continue;
264 }
265 const bNode &from_node = *link->fromnode;
266 const int from_node_i = from_node.index();
267 depend_on_input_flags |= depend_on_input_flag_array[from_node_i];
268 depend_on_output_flags |= depend_on_output_flag_array[from_node_i];
269 }
270 }
271 if (input_types.contains(node->type_legacy)) {
272 if (const bNodeTreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) {
273 /* Now entering a zone, so set the corresponding bit. */
274 depend_on_input_flags[zone->index].set();
275 }
276 }
277 else if (output_types.contains(node->type_legacy)) {
278 if (const bNodeTreeZone *zone = zone_by_inout_node.lookup_default(node, nullptr)) {
279 /* The output is implicitly linked to the input, so also propagate the bits from there. */
280 if (const bNode *zone_input_node = zone->input_node()) {
281 const int input_node_i = zone_input_node->index();
282 depend_on_input_flags |= depend_on_input_flag_array[input_node_i];
283 depend_on_output_flags |= depend_on_output_flag_array[input_node_i];
284 }
285 /* Now exiting a zone, so change the bits accordingly. */
286 depend_on_input_flags[zone->index].reset();
287 depend_on_output_flags[zone->index].set();
288 }
289 }
290
291 if (bits::has_common_set_bits(depend_on_input_flags, depend_on_output_flags)) {
292 /* A node can not be inside and after a zone at the same time. */
293 return {};
294 }
295 }
296
297 const std::optional<Vector<ZoneRelation>> zone_relations = get_direct_zone_relations(
298 tree_zones->zones, depend_on_input_flag_array);
299 if (!zone_relations) {
300 /* Found cyclic relations. */
301 return {};
302 }
303
304 /* Set parent and child pointers in zones. */
305 for (const ZoneRelation &relation : *zone_relations) {
306 relation.parent->child_zones.append(relation.child);
307 BLI_assert(relation.child->parent_zone == nullptr);
308 relation.child->parent_zone = relation.parent;
309 }
310
311 Set<const bNodeTreeZone *> found_zones;
312 for (bNodeTreeZone *main_zone : tree_zones->zones) {
313 found_zones.clear();
314 for (bNodeTreeZone *zone = main_zone; zone; zone = zone->parent_zone) {
315 if (!found_zones.add(zone)) {
316 /* Found cyclic parent relationships between zones. */
317 return {};
318 }
319 }
320 }
321
322 /* Update depths. */
323 for (bNodeTreeZone *zone : tree_zones->zones) {
324 update_zone_depths(*zone);
325 }
326
327 for (bNodeTreeZone *zone : tree_zones->zones) {
328 if (zone->depth == 0) {
329 tree_zones->root_zones.append(zone);
330 }
331 }
332
333 const bool found_node_in_multiple_zones = update_zone_per_node(
334 all_nodes,
335 tree_zones->zones,
336 depend_on_input_flag_array,
337 zone_by_inout_node,
338 tree_zones->zone_by_node_id,
339 tree_zones->node_ids_outside_zones);
340 if (found_node_in_multiple_zones) {
341 return {};
342 }
343
344 for (const StringRefNull output_idname : {"NodeGroupOutput",
345 "ShaderNodeOutputMaterial",
346 "ShaderNodeOutputLight",
347 "ShaderNodeOutputWorld",
348 "ShaderNodeOutputAOV"})
349 {
350 for (const bNode *node : tree.nodes_by_type(output_idname)) {
351 if (tree_zones->zone_by_node_id.contains(node->identifier)) {
352 /* Output nodes must not be in a zone. */
353 return {};
354 }
355 }
356 }
357
358 for (const int node_i : all_nodes.index_range()) {
359 const bNode *node = all_nodes[node_i];
360 const int zone_i = tree_zones->zone_by_node_id.lookup_default(node->identifier, -1);
361 if (zone_i == -1) {
362 continue;
363 }
364 bNodeTreeZone &zone = *tree_zones->zones[zone_i];
365 if (ELEM(node->identifier, zone.input_node_id, zone.output_node_id)) {
366 continue;
367 }
368 zone.child_node_ids.append(node->identifier);
369 }
370
371 update_zone_border_links(tree, *tree_zones);
372
373 // std::cout << *tree_zones << std::endl;
374 return tree_zones;
375}
376
378{
379 tree.ensure_topology_cache();
380 tree.runtime->tree_zones_cache_mutex.ensure([&]() {
381 tree.runtime->tree_zones = discover_tree_zones(tree);
382 if (tree.runtime->tree_zones) {
383 tree.runtime->last_valid_zones = tree.runtime->tree_zones;
384 }
385 });
386 return tree.runtime->tree_zones.get();
387}
388
390{
391 const bNodeTreeZones *zones = this->owner;
392 const int zone_i = zones->zone_by_node_id.lookup_default(node.identifier, -1);
393 if (zone_i == -1) {
394 return false;
395 }
396 for (const bNodeTreeZone *zone = zones->zones[zone_i]; zone; zone = zone->parent_zone) {
397 if (zone == this) {
398 return true;
399 }
400 }
401 return false;
402}
403
405{
406 for (const bNodeTreeZone *zone = other_zone.parent_zone; zone; zone = zone->parent_zone) {
407 if (zone == this) {
408 return true;
409 }
410 }
411 return false;
412}
413
415{
416 const bNode &node = socket.owner_node();
417 const bNodeTreeZone *zone = this->get_zone_by_node(node.identifier);
418 if (zone == nullptr) {
419 return zone;
420 }
421 if (zone->input_node_id == node.identifier) {
422 if (socket.is_input()) {
423 return zone->parent_zone;
424 }
425 }
426 if (zone->output_node_id == node.identifier) {
427 if (socket.is_output()) {
428 return zone->parent_zone;
429 }
430 }
431 return zone;
432}
433
435{
436 const int zone_i = this->zone_by_node_id.lookup_default(node_id, -1);
437 if (zone_i == -1) {
438 return nullptr;
439 }
440 return this->zones[zone_i];
441}
442
444 const bNodeTreeZone *to_zone) const
445{
446 if (from_zone == to_zone) {
447 /* Links between zones in the same zone are always allowed. */
448 return true;
449 }
450 if (!from_zone) {
451 /* Links from the root tree can go to any zone. */
452 return true;
453 }
454 if (!to_zone) {
455 /* Links can not leave a zone and connect to a socket in the root tree. */
456 return false;
457 }
458 return from_zone->contains_zone_recursively(*to_zone);
459}
460
462 const bNodeSocket &to) const
463{
464 BLI_assert(from.in_out == SOCK_OUT);
466 const bNodeTreeZone *from_zone = this->get_zone_by_socket(from);
467 const bNodeTreeZone *to_zone = this->get_zone_by_socket(to);
468 return this->link_between_zones_is_allowed(from_zone, to_zone);
469}
470
472 const bNodeTreeZone *outer_zone, const bNodeTreeZone *inner_zone) const
473{
474 BLI_assert(this->link_between_zones_is_allowed(outer_zone, inner_zone));
475 Vector<const bNodeTreeZone *> zones_to_enter;
476 for (const bNodeTreeZone *zone = inner_zone; zone != outer_zone; zone = zone->parent_zone) {
477 zones_to_enter.append(zone);
478 }
479 std::reverse(zones_to_enter.begin(), zones_to_enter.end());
480 return zones_to_enter;
481}
482
488
490 const bNode &output_bnode) const
491{
492 for (const bNode *node : tree.nodes_by_type(this->input_idname)) {
493 if (this->get_corresponding_output_id(*node) == output_bnode.identifier) {
494 return node;
495 }
496 }
497 return nullptr;
498}
499
501 const bNode &input_bnode) const
502{
503 return tree.node_by_id(this->get_corresponding_output_id(input_bnode));
504}
505
507{
508 return const_cast<bNode *>(
509 this->get_corresponding_input(const_cast<const bNodeTree &>(tree), output_bnode));
510}
511
513{
514 return const_cast<bNode *>(
515 this->get_corresponding_output(const_cast<const bNodeTree &>(tree), input_bnode));
516}
517
519{
520 static Vector<const bNodeZoneType *> zone_types;
521 return zone_types;
522};
523
525{
526 get_zone_types_vector().append(&zone_type);
527}
528
533
535{
536 static const Vector<int> node_types = []() {
537 Vector<int> node_types;
538 for (const bNodeZoneType *zone_type : all_zone_types()) {
539 node_types.append(zone_type->input_type);
540 node_types.append(zone_type->output_type);
541 }
542 return node_types;
543 }();
544 return node_types;
545}
546
548{
549 static const Vector<int> node_types = []() {
550 Vector<int> node_types;
551 for (const bNodeZoneType *zone_type : all_zone_types()) {
552 node_types.append(zone_type->input_type);
553 }
554 return node_types;
555 }();
556 return node_types;
557}
558
560{
561 static const Vector<int> node_types = []() {
562 Vector<int> node_types;
563 for (const bNodeZoneType *zone_type : all_zone_types()) {
564 node_types.append(zone_type->output_type);
565 }
566 return node_types;
567 }();
568 return node_types;
569}
570
571const bNodeZoneType *zone_type_by_node_type(const int node_type)
572{
573 for (const bNodeZoneType *zone_type : all_zone_types()) {
574 if (ELEM(node_type, zone_type->input_type, zone_type->output_type)) {
575 return zone_type;
576 }
577 }
578 return nullptr;
579}
580
582{
583 if (!this->input_node_id) {
584 return nullptr;
585 }
586 return this->owner->tree->node_by_id(*this->input_node_id);
587}
588
590{
591 if (!this->output_node_id) {
592 return nullptr;
593 }
594 return this->owner->tree->node_by_id(*this->output_node_id);
595}
596
598{
599 Vector<const bNode *> nodes(node_ids.size());
600 for (const int i : nodes.index_range()) {
601 nodes[i] = tree.node_by_id(node_ids[i]);
602 }
603 return nodes;
604}
605
607{
608 return node_ids_to_vector(*this->owner->tree, this->child_node_ids);
609}
610
615
616std::ostream &operator<<(std::ostream &stream, const bNodeTreeZones &zones)
617{
618 for (const bNodeTreeZone *zone : zones.zones) {
619 stream << *zone;
620 if (zones.zones.last() != zone) {
621 stream << "\n";
622 }
623 }
624 return stream;
625}
626
627std::ostream &operator<<(std::ostream &stream, const bNodeTreeZone &zone)
628{
629 stream << zone.index << ": Parent index: ";
630 if (zone.parent_zone != nullptr) {
631 stream << zone.parent_zone->index;
632 }
633 else {
634 stream << "*";
635 }
636
637 stream << "; Input: " << (zone.input_node() ? zone.input_node()->name : "null");
638 stream << ", Output: " << (zone.output_node() ? zone.output_node()->name : "null");
639
640 stream << "; Border Links: {\n";
641 for (const bNodeLink *border_link : zone.border_links) {
642 stream << " " << border_link->fromnode->name << ": " << border_link->fromsock->name << " -> ";
643 stream << border_link->tonode->name << ": " << border_link->tosock->name << ";\n";
644 }
645 stream << "}.";
646 return stream;
647}
648
649} // namespace blender::bke
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_STRUCT_EQUALITY_OPERATORS_2(Type, m1, m2)
#define ELEM(...)
@ SOCK_OUT
@ SOCK_IN
unsigned long long int uint64_t
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
void clear()
Definition BLI_map.hh:1038
bool add_overwrite(const Key &key, const Value &value)
Definition BLI_map.hh:325
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
bool contains(const Key &key) const
Definition BLI_map.hh:353
ItemIterator items() const &
Definition BLI_map.hh:902
bool add(const Key &key)
Definition BLI_set.hh:248
void clear()
Definition BLI_set.hh:551
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
int64_t index_of_try(const Key &key) const
IndexRange index_range() const
bool contains(const Key &key) const
Span< Key > as_span() const
void add_new(const Key &key)
int64_t size() const
void remove_and_reorder(const int64_t index)
void append(const T &value)
void extend(Span< T > array)
void append_non_duplicates(const T &value)
bool contains_zone_recursively(const bNodeTreeZone &other_zone) const
Vector< const bNode * > child_nodes() const
std::optional< int > output_node_id
const bNode * output_node() const
Vector< const bNodeLink * > border_links
bool contains_node_recursively(const bNode &node) const
std::optional< int > input_node_id
const bNode * input_node() const
Vector< const bNodeTreeZone * > get_zones_to_enter_from_root(const bNodeTreeZone *zone) const
Vector< const bNodeTreeZone * > get_zones_to_enter(const bNodeTreeZone *outer_zone, const bNodeTreeZone *inner_zone) const
bool link_between_zones_is_allowed(const bNodeTreeZone *from_zone, const bNodeTreeZone *to_zone) const
Vector< const bNode * > nodes_outside_zones() const
Vector< bNodeTreeZone * > zones
const bNodeTreeZone * get_zone_by_node(const int32_t node_id) const
const bNodeTreeZone * get_zone_by_socket(const bNodeSocket &socket) const
bool link_between_sockets_is_allowed(const bNodeSocket &from, const bNodeSocket &to) const
const bNode * get_corresponding_input(const bNodeTree &tree, const bNode &output_bnode) const
virtual const int & get_corresponding_output_id(const bNode &input_bnode) const =0
const bNode * get_corresponding_output(const bNodeTree &tree, const bNode &input_bnode) const
KDTree_3d * tree
bool has_common_set_bits(const BitSpanT &...args)
void foreach_1_index(const BitSpanT &data, Fn &&fn)
const bNodeZoneType * zone_type_by_node_type(const int node_type)
static void update_zone_depths(bNodeTreeZone &zone)
static std::optional< Vector< ZoneRelation > > get_direct_zone_relations(const Span< bNodeTreeZone * > all_zones, const BitGroupVector<> &depend_on_input_flag_array)
static Vector< const bNodeZoneType * > & get_zone_types_vector()
static Vector< std::unique_ptr< bNodeTreeZone > > find_zone_nodes(const bNodeTree &tree, bNodeTreeZones &owner, Map< const bNode *, bNodeTreeZone * > &r_zone_by_inout_node)
Span< int > all_zone_output_node_types()
static void update_zone_border_links(const bNodeTree &tree, bNodeTreeZones &tree_zones)
Span< const bNodeZoneType * > all_zone_types()
static Vector< const bNode * > node_ids_to_vector(const bNodeTree &tree, const Vector< int > &node_ids)
Span< int > all_zone_node_types()
void register_node_zone_type(const bNodeZoneType &zone_type)
Span< int > all_zone_input_node_types()
static std::unique_ptr< bNodeTreeZones > discover_tree_zones(const bNodeTree &tree)
std::ostream & operator<<(std::ostream &stream, const GeometrySet &geometry_set)
const bNodeTreeZones * get_tree_zones(const bNodeTree &tree)
static bool update_zone_per_node(const Span< const bNode * > all_nodes, const Span< bNodeTreeZone * > all_zones, const BitGroupVector<> &depend_on_input_flag_array, const Map< const bNode *, bNodeTreeZone * > &zone_by_inout_node, Map< int, int > &r_zone_by_node_id, Vector< int > &r_node_outside_zones)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
char name[64]
int32_t identifier
i
Definition text_draw.cc:230