Blender V4.3
lazy_function_graph.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 "BLI_dot_export.hh"
6
8
9#include <sstream>
10
12
14{
15 name_ = allocator_.copy_string(name);
16 graph_input_node_ = allocator_.construct<InterfaceNode>().release();
17 graph_output_node_ = allocator_.construct<InterfaceNode>().release();
18 nodes_.append(graph_input_node_);
19 nodes_.append(graph_output_node_);
20}
21
23{
24 for (FunctionNode *node : this->function_nodes()) {
25 for (InputSocket *socket : node->inputs_) {
26 std::destroy_at(socket);
27 }
28 for (OutputSocket *socket : node->outputs_) {
29 std::destroy_at(socket);
30 }
31 std::destroy_at(node);
32 }
33 for (const InterfaceNode *node : {graph_input_node_, graph_output_node_}) {
34 for (InputSocket *socket : node->inputs_) {
35 std::destroy_at(socket);
36 }
37 for (OutputSocket *socket : node->outputs_) {
38 std::destroy_at(socket);
39 }
40 std::destroy_at(node);
41 }
42}
43
45{
46 const Span<Input> inputs = fn.inputs();
47 const Span<Output> outputs = fn.outputs();
48
49 FunctionNode &node = *allocator_.construct<FunctionNode>().release();
50 node.fn_ = &fn;
51 node.inputs_ = allocator_.construct_elements_and_pointer_array<InputSocket>(inputs.size());
52 node.outputs_ = allocator_.construct_elements_and_pointer_array<OutputSocket>(outputs.size());
53
54 for (const int i : inputs.index_range()) {
55 InputSocket &socket = *node.inputs_[i];
56 socket.index_in_node_ = i;
57 socket.is_input_ = true;
58 socket.node_ = &node;
59 socket.type_ = inputs[i].type;
60 }
61 for (const int i : outputs.index_range()) {
62 OutputSocket &socket = *node.outputs_[i];
63 socket.index_in_node_ = i;
64 socket.is_input_ = false;
65 socket.node_ = &node;
66 socket.type_ = outputs[i].type;
67 }
68
69 nodes_.append(&node);
70 return node;
71}
72
73GraphInputSocket &Graph::add_input(const CPPType &type, std::string name)
74{
75 GraphInputSocket &socket = *allocator_.construct<GraphInputSocket>().release();
76 socket.is_input_ = false;
77 socket.node_ = graph_input_node_;
78 socket.type_ = &type;
79 socket.index_in_node_ = graph_inputs_.append_and_get_index(&socket);
80 graph_input_node_->outputs_ = graph_inputs_;
81
82 graph_input_node_->socket_names_.append(std::move(name));
83 return socket;
84}
85
86GraphOutputSocket &Graph::add_output(const CPPType &type, std::string name)
87{
88 GraphOutputSocket &socket = *allocator_.construct<GraphOutputSocket>().release();
89 socket.is_input_ = true;
90 socket.node_ = graph_output_node_;
91 socket.type_ = &type;
92 socket.index_in_node_ = graph_outputs_.append_and_get_index(&socket);
93 graph_output_node_->inputs_ = graph_outputs_;
94
95 graph_output_node_->socket_names_.append(std::move(name));
96 return socket;
97}
98
100{
101 BLI_assert(to.origin_ == nullptr);
102 BLI_assert(from.type_ == to.type_);
103 to.origin_ = &from;
104 from.targets_.append(&to);
105}
106
108{
109 if (socket.origin_ != nullptr) {
110 socket.origin_->targets_.remove_first_occurrence_and_reorder(&socket);
111 socket.origin_ = nullptr;
112 }
113}
114
116{
117 for (const int i : nodes_.index_range()) {
118 nodes_[i]->index_in_graph_ = i;
119 }
120}
121
123{
124 int socket_counter = 0;
125 for (const int i : nodes_.index_range()) {
126 for (InputSocket *socket : nodes_[i]->inputs()) {
127 socket->index_in_graph_ = socket_counter++;
128 }
129 for (OutputSocket *socket : nodes_[i]->outputs()) {
130 socket->index_in_graph_ = socket_counter++;
131 }
132 }
133 socket_num_ = socket_counter;
134}
135
137{
138 for (const int i : nodes_.index_range()) {
139 if (nodes_[i]->index_in_graph_ != i) {
140 return false;
141 }
142 }
143 return true;
144}
145
146std::string Socket::name() const
147{
148 if (node_->is_function()) {
149 const FunctionNode &fn_node = static_cast<const FunctionNode &>(*node_);
150 const LazyFunction &fn = fn_node.function();
151 if (is_input_) {
152 return fn.input_name(index_in_node_);
153 }
154 return fn.output_name(index_in_node_);
155 }
156 const InterfaceNode &interface_node = *static_cast<const InterfaceNode *>(node_);
157 return interface_node.socket_names_[index_in_node_];
158}
159
160std::string Socket::detailed_name() const
161{
162 std::stringstream ss;
163 ss << node_->name() << ":" << (is_input_ ? "IN" : "OUT") << ":" << index_in_node_ << ":"
164 << this->name();
165 return ss.str();
166}
167
168std::string Node::name() const
169{
170 if (this->is_function()) {
171 return fn_->name();
172 }
173 return "Interface";
174}
175
176std::string Graph::ToDotOptions::socket_name(const Socket &socket) const
177{
178 return socket.name();
179}
180
181std::optional<std::string> Graph::ToDotOptions::socket_font_color(const Socket & /*socket*/) const
182{
183 return std::nullopt;
184}
185
187 const InputSocket & /*to*/,
188 dot::DirectedEdge & /*dot_edge*/) const
189{
190}
191
192std::string Graph::to_dot(const ToDotOptions &options) const
193{
194 dot::DirectedGraph digraph;
195 digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
196
198
199 for (const Node *node : nodes_) {
200 dot::Node &dot_node = digraph.new_node("");
201 if (node->is_interface()) {
202 dot_node.set_background_color("lightblue");
203 }
204 else {
205 dot_node.set_background_color("white");
206 }
207
208 dot::NodeWithSockets dot_node_with_sockets;
209 dot_node_with_sockets.node_name = node->name();
210 for (const InputSocket *socket : node->inputs()) {
211 dot::NodeWithSockets::Input &dot_input = dot_node_with_sockets.add_input(
212 options.socket_name(*socket));
213 dot_input.fontcolor = options.socket_font_color(*socket);
214 }
215 for (const OutputSocket *socket : node->outputs()) {
216 dot::NodeWithSockets::Output &dot_output = dot_node_with_sockets.add_output(
217 options.socket_name(*socket));
218 dot_output.fontcolor = options.socket_font_color(*socket);
219 }
220
221 dot_nodes.add_new(node, dot::NodeWithSocketsRef(dot_node, dot_node_with_sockets));
222 }
223
224 for (const Node *node : nodes_) {
225 for (const InputSocket *socket : node->inputs()) {
226 const dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&socket->node());
227 const dot::NodePort to_dot_port = to_dot_node.input(socket->index());
228
229 if (const OutputSocket *origin = socket->origin()) {
230 dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&origin->node());
231 dot::DirectedEdge &dot_edge = digraph.new_edge(from_dot_node.output(origin->index()),
232 to_dot_port);
233 options.add_edge_attributes(*origin, *socket, dot_edge);
234 }
235 else if (const void *default_value = socket->default_value()) {
236 const CPPType &type = socket->type();
237 std::string value_string;
238 if (type.is_printable()) {
239 value_string = type.to_string(default_value);
240 }
241 else {
242 value_string = type.name();
243 }
244 dot::Node &default_value_dot_node = digraph.new_node(value_string);
245 default_value_dot_node.set_shape(dot::Attr_shape::Ellipse);
246 default_value_dot_node.attributes.set("color", "#00000055");
247 digraph.new_edge(default_value_dot_node, to_dot_port);
248 }
249 }
250 }
251
252 return digraph.to_dot_string();
253}
254
255} // namespace blender::fn::lazy_function
#define BLI_assert(a)
Definition BLI_assert.h:50
std::string to_string(const void *value) const
Definition cpp_type.cc:11
destruct_ptr< T > construct(Args &&...args)
StringRefNull copy_string(StringRef str)
Span< T * > construct_elements_and_pointer_array(int64_t n, Args &&...args)
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:241
void append(const T &value)
void set(StringRef key, StringRef value)
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition dot_export.cc:41
std::string to_dot_string() const
Node & new_node(StringRef label)
Definition dot_export.cc:16
void set_rankdir(Attr_rankdir rankdir)
NodePort output(int index) const
NodePort input(int index) const
void set_shape(Attr_shape shape)
void set_background_color(StringRef name)
virtual std::string socket_name(const Socket &socket) const
virtual void add_edge_attributes(const OutputSocket &from, const InputSocket &to, dot::DirectedEdge &dot_edge) const
virtual std::optional< std::string > socket_font_color(const Socket &socket) const
Span< const FunctionNode * > function_nodes() const
FunctionNode & add_function(const LazyFunction &fn)
void add_link(OutputSocket &from, InputSocket &to)
GraphOutputSocket & add_output(const CPPType &type, std::string name="")
void clear_origin(InputSocket &socket)
std::string to_dot(const ToDotOptions &options={}) const
GraphInputSocket & add_input(const CPPType &type, std::string name="")
virtual std::string output_name(int index) const
virtual std::string input_name(int index) const
CCL_NAMESPACE_BEGIN struct Options options
OperationNode * node
StackEntry * from
std::optional< std::string > fontcolor
Input & add_input(std::string name)
Output & add_output(std::string name)