Blender V4.3
dot_export.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 <iomanip>
6
7#include "BLI_dot_export.hh"
8
9#include <sstream>
10
11namespace blender::dot {
12
13/* Graph Building
14 ************************************************/
15
17{
18 Node *node = new Node(*this);
19 nodes_.append(std::unique_ptr<Node>(node));
20 top_level_nodes_.add_new(node);
21 node->attributes.set("label", label);
22 return *node;
23}
24
26{
27 Cluster *cluster = new Cluster(*this);
28 clusters_.append(std::unique_ptr<Cluster>(cluster));
29 top_level_clusters_.add_new(cluster);
30 cluster->attributes.set("label", label);
31 return *cluster;
32}
33
35{
36 UndirectedEdge *edge = new UndirectedEdge(a, b);
37 edges_.append(std::unique_ptr<UndirectedEdge>(edge));
38 return *edge;
39}
40
42{
43 DirectedEdge *edge = new DirectedEdge(from, to);
44 edges_.append(std::unique_ptr<DirectedEdge>(edge));
45 return *edge;
46}
47
49{
50 if (parent_ == new_parent) {
51 return;
52 }
53 if (parent_ == nullptr) {
54 graph_.top_level_clusters_.remove(this);
55 new_parent->children_.add_new(this);
56 }
57 else if (new_parent == nullptr) {
58 parent_->children_.remove(this);
59 graph_.top_level_clusters_.add_new(this);
60 }
61 else {
62 parent_->children_.remove(this);
63 new_parent->children_.add_new(this);
64 }
65 parent_ = new_parent;
66}
67
69{
70 if (cluster_ == cluster) {
71 return;
72 }
73 if (cluster_ == nullptr) {
74 graph_.top_level_nodes_.remove(this);
75 cluster->nodes_.add_new(this);
76 }
77 else if (cluster == nullptr) {
78 cluster_->nodes_.remove(this);
79 graph_.top_level_nodes_.add_new(this);
80 }
81 else {
82 cluster_->nodes_.remove(this);
83 cluster->nodes_.add_new(this);
84 }
85 cluster_ = cluster;
86}
87
88/* Utility methods
89 **********************************************/
90
92{
93 for (Cluster *cluster : top_level_clusters_) {
95 }
96}
97
99{
100 float hue = rand() / float(RAND_MAX);
101 float staturation = 0.3f;
102 float value = 0.8f;
103 this->attributes.set("bgcolor", color_attr_from_hsv(hue, staturation, value));
104
105 for (Cluster *cluster : children_) {
106 cluster->set_random_cluster_bgcolors();
107 }
108}
109
110bool Cluster::contains(Node &node) const
111{
112 Cluster *current = node.parent_cluster();
113 while (current != nullptr) {
114 if (current == this) {
115 return true;
116 }
117 current = current->parent_;
118 }
119 return false;
120}
121
122/* Dot Generation
123 **********************************************/
124
126{
127 std::stringstream ss;
128 ss << "digraph {\n";
130 ss << "\n";
131
132 for (const std::unique_ptr<DirectedEdge> &edge : edges_) {
133 edge->export__as_edge_statement(ss);
134 ss << "\n";
135 }
136
137 ss << "}\n";
138 return ss.str();
139}
140
142{
143 std::stringstream ss;
144 ss << "graph {\n";
146 ss << "\n";
147
148 for (const std::unique_ptr<UndirectedEdge> &edge : edges_) {
149 edge->export__as_edge_statement(ss);
150 ss << "\n";
151 }
152
153 ss << "}\n";
154 return ss.str();
155}
156
157void Graph::export__declare_nodes_and_clusters(std::stringstream &ss) const
158{
159 ss << "graph ";
160 attributes.export__as_bracket_list(ss);
161 ss << "\n\n";
162
163 for (Node *node : top_level_nodes_) {
164 node->export__as_declaration(ss);
165 }
166
167 for (Cluster *cluster : top_level_clusters_) {
168 cluster->export__declare_nodes_and_clusters(ss);
169 }
170}
171
172void Cluster::export__declare_nodes_and_clusters(std::stringstream &ss) const
173{
174 ss << "subgraph " << this->name() << " {\n";
175
176 ss << "graph ";
177 attributes.export__as_bracket_list(ss);
178 ss << "\n\n";
179
180 for (Node *node : nodes_) {
181 node->export__as_declaration(ss);
182 }
183
184 for (Cluster *cluster : children_) {
185 cluster->export__declare_nodes_and_clusters(ss);
186 }
187
188 ss << "}\n";
189}
190
191void DirectedEdge::export__as_edge_statement(std::stringstream &ss) const
192{
193 a_.to_dot_string(ss);
194 ss << " -> ";
195 b_.to_dot_string(ss);
196 ss << " ";
197 attributes.export__as_bracket_list(ss);
198}
199
200void UndirectedEdge::export__as_edge_statement(std::stringstream &ss) const
201{
202 a_.to_dot_string(ss);
203 ss << " -- ";
204 b_.to_dot_string(ss);
205 ss << " ";
206 attributes.export__as_bracket_list(ss);
207}
208
209void Attributes::export__as_bracket_list(std::stringstream &ss) const
210{
211 ss << "[";
212 attributes_.foreach_item([&](StringRef key, StringRef value) {
213 if (StringRef(value).startswith("<")) {
214 /* Don't draw the quotes, this is an HTML-like value. */
215 ss << key << "=" << value << ", ";
216 }
217 else {
218 ss << key << "=\"";
219 for (char c : value) {
220 if (c == '\"') {
221 /* Escape double quotes. */
222 ss << '\\';
223 }
224 ss << c;
225 }
226 ss << "\", ";
227 }
228 });
229 ss << "]";
230}
231
232void Node::export__as_id(std::stringstream &ss) const
233{
234 ss << '"' << uintptr_t(this) << '"';
235}
236
237void Node::export__as_declaration(std::stringstream &ss) const
238{
239 this->export__as_id(ss);
240 ss << " ";
241 attributes.export__as_bracket_list(ss);
242 ss << "\n";
243}
244
245void NodePort::to_dot_string(std::stringstream &ss) const
246{
247 node_->export__as_id(ss);
248 if (port_name_.has_value()) {
249 ss << ":" << *port_name_;
250 }
251 if (port_position_.has_value()) {
252 ss << ":" << *port_position_;
253 }
254}
255
256std::string color_attr_from_hsv(float h, float s, float v)
257{
258 std::stringstream ss;
259 ss << std::setprecision(4) << h << ' ' << s << ' ' << v;
260 return ss.str();
261}
262
264{
265 std::stringstream ss;
266
267 ss << R"(<<table border="0" cellspacing="3">)";
268
269 /* Header */
270 ss << R"(<tr><td colspan="3" align="center"><b>)";
271 ss << (data.node_name.empty() ? "No Name" : data.node_name);
272 ss << "</b></td></tr>";
273
274 /* Sockets */
275 int socket_max_amount = std::max(data.inputs.size(), data.outputs.size());
276 for (int i = 0; i < socket_max_amount; i++) {
277 ss << "<tr>";
278 if (i < data.inputs.size()) {
279 const NodeWithSockets::Input &input = data.inputs[i];
280 ss << R"(<td align="left" port="in)" << i << "\">";
281 if (input.fontcolor) {
282 ss << R"(<font color=")" << *input.fontcolor << "\">";
283 }
284 ss << (input.name.empty() ? "No Name" : input.name);
285 if (input.fontcolor) {
286 ss << "</font>";
287 }
288 ss << "</td>";
289 }
290 else {
291 ss << "<td></td>";
292 }
293 ss << "<td></td>";
294 if (i < data.outputs.size()) {
295 const NodeWithSockets::Output &output = data.outputs[i];
296 ss << R"(<td align="right" port="out)" << i << "\">";
297 if (output.fontcolor) {
298 ss << R"(<font color=")" << *output.fontcolor << "\">";
299 }
300 ss << (output.name.empty() ? "No Name" : output.name);
301 if (output.fontcolor) {
302 ss << "</font>";
303 }
304 ss << "</td>";
305 }
306 else {
307 ss << "<td></td>";
308 }
309 ss << "</tr>";
310 }
311
312 ss << "</table>>";
313
314 node_->attributes.set("label", ss.str());
315 node_->set_shape(Attr_shape::Rectangle);
316}
317
318} // namespace blender::dot
ATTR_WARN_UNUSED_RESULT const BMVert * v
void foreach_item(const FuncT &func) const
Definition BLI_map.hh:649
void export__as_bracket_list(std::stringstream &ss) const
void set(StringRef key, StringRef value)
void set_parent_cluster(Cluster *new_parent)
Definition dot_export.cc:48
bool contains(Node &node) const
std::string name() const
void export__declare_nodes_and_clusters(std::stringstream &ss) const
void set_random_cluster_bgcolors()
Definition dot_export.cc:98
void export__as_edge_statement(std::stringstream &ss) const
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition dot_export.cc:41
std::string to_dot_string() const
Cluster & new_cluster(StringRef label="")
Definition dot_export.cc:25
void set_random_cluster_bgcolors()
Definition dot_export.cc:91
Node & new_node(StringRef label)
Definition dot_export.cc:16
void export__declare_nodes_and_clusters(std::stringstream &ss) const
void to_dot_string(std::stringstream &ss) const
NodeWithSocketsRef(Node &node, const NodeWithSockets &data)
void set_shape(Attr_shape shape)
void export__as_declaration(std::stringstream &ss) const
void export__as_id(std::stringstream &ss) const
void set_parent_cluster(Cluster *cluster)
Definition dot_export.cc:68
void export__as_edge_statement(std::stringstream &ss) const
UndirectedEdge & new_edge(NodePort a, NodePort b)
Definition dot_export.cc:34
std::string to_dot_string() const
local_group_size(16, 16) .push_constant(Type b
OperationNode * node
const char * label
draw_view in_light_buf[] float
std::string color_attr_from_hsv(float h, float s, float v)
_W64 unsigned int uintptr_t
Definition stdint.h:119
std::optional< std::string > fontcolor