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