Including <iostream> or similar headers is quite expensive, since it also pulls in things like <locale> and so on. In many BLI headers, iostreams are only used to implement some sort of "debug print", or an operator<< for ostream. Change some of the commonly used places to instead include <iosfwd>, which is the standard way of forward-declaring iostreams related classes, and move the actual debug-print / operator<< implementations into .cc files. This is not done for templated classes though (it would be possible to provide explicit operator<< instantiations somewhere in the source file, but that would lead to hard-to-figure-out linker error whenever someone would add a different template type). There, where possible, I changed from full <iostream> include to only the needed <ostream> part. For Span<T>, I just removed print_as_lines since it's not used by anything. It could be moved into a .cc file using a similar approach as above if needed. Doing full blender build changes include counts this way: - <iostream> 1986 -> 978 - <sstream> 2880 -> 925 It does not affect the total build time much though, mostly because towards the end of it there's just several CPU cores finishing compiling OpenVDB related source files. Pull Request: https://projects.blender.org/blender/blender/pulls/111046
303 lines
6.0 KiB
C++
303 lines
6.0 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
/**
|
|
* Language grammar: https://www.graphviz.org/doc/info/lang.html
|
|
* Attributes: https://www.graphviz.org/doc/info/attrs.html
|
|
* Node Shapes: https://www.graphviz.org/doc/info/shapes.html
|
|
* Preview: https://dreampuf.github.io/GraphvizOnline
|
|
*/
|
|
|
|
#include "BLI_map.hh"
|
|
#include "BLI_set.hh"
|
|
#include "BLI_utility_mixins.hh"
|
|
#include "BLI_vector.hh"
|
|
|
|
#include "BLI_dot_export_attribute_enums.hh"
|
|
|
|
#include <iosfwd>
|
|
#include <optional>
|
|
|
|
namespace blender::dot {
|
|
|
|
class Graph;
|
|
class DirectedGraph;
|
|
class UndirectedGraph;
|
|
class Node;
|
|
class NodePort;
|
|
class DirectedEdge;
|
|
class UndirectedEdge;
|
|
class Cluster;
|
|
|
|
class Attributes {
|
|
private:
|
|
Map<std::string, std::string> attributes_;
|
|
|
|
public:
|
|
void export__as_bracket_list(std::stringstream &ss) const;
|
|
|
|
void set(StringRef key, StringRef value)
|
|
{
|
|
attributes_.add_overwrite(key, value);
|
|
}
|
|
|
|
void set(StringRef key, float value)
|
|
{
|
|
attributes_.add_overwrite(key, std::to_string(value));
|
|
}
|
|
};
|
|
|
|
class Graph {
|
|
private:
|
|
Vector<std::unique_ptr<Node>> nodes_;
|
|
Vector<std::unique_ptr<Cluster>> clusters_;
|
|
|
|
Set<Node *> top_level_nodes_;
|
|
Set<Cluster *> top_level_clusters_;
|
|
|
|
friend Cluster;
|
|
friend Node;
|
|
|
|
public:
|
|
Attributes attributes;
|
|
|
|
public:
|
|
Node &new_node(StringRef label);
|
|
Cluster &new_cluster(StringRef label = "");
|
|
|
|
void export__declare_nodes_and_clusters(std::stringstream &ss) const;
|
|
|
|
void set_rankdir(Attr_rankdir rankdir)
|
|
{
|
|
attributes.set("rankdir", rankdir_to_string(rankdir));
|
|
}
|
|
|
|
void set_random_cluster_bgcolors();
|
|
};
|
|
|
|
class Cluster {
|
|
private:
|
|
Graph &graph_;
|
|
Cluster *parent_ = nullptr;
|
|
Set<Cluster *> children_;
|
|
Set<Node *> nodes_;
|
|
|
|
friend Graph;
|
|
friend Node;
|
|
|
|
public:
|
|
Attributes attributes;
|
|
|
|
Cluster(Graph &graph) : graph_(graph) {}
|
|
|
|
public:
|
|
void export__declare_nodes_and_clusters(std::stringstream &ss) const;
|
|
|
|
std::string name() const
|
|
{
|
|
return "cluster_" + std::to_string(uintptr_t(this));
|
|
}
|
|
|
|
void set_parent_cluster(Cluster *new_parent);
|
|
void set_parent_cluster(Cluster &cluster)
|
|
{
|
|
this->set_parent_cluster(&cluster);
|
|
}
|
|
|
|
Cluster *parent_cluster()
|
|
{
|
|
return parent_;
|
|
}
|
|
|
|
void set_random_cluster_bgcolors();
|
|
|
|
bool contains(Node &node) const;
|
|
};
|
|
|
|
class Node {
|
|
private:
|
|
Graph &graph_;
|
|
Cluster *cluster_ = nullptr;
|
|
|
|
friend Graph;
|
|
|
|
public:
|
|
Attributes attributes;
|
|
|
|
Node(Graph &graph) : graph_(graph) {}
|
|
|
|
public:
|
|
void set_parent_cluster(Cluster *cluster);
|
|
void set_parent_cluster(Cluster &cluster)
|
|
{
|
|
this->set_parent_cluster(&cluster);
|
|
}
|
|
|
|
Cluster *parent_cluster()
|
|
{
|
|
return cluster_;
|
|
}
|
|
|
|
void set_shape(Attr_shape shape)
|
|
{
|
|
attributes.set("shape", shape_to_string(shape));
|
|
}
|
|
|
|
/* See https://www.graphviz.org/doc/info/attrs.html#k:color. */
|
|
void set_background_color(StringRef name)
|
|
{
|
|
attributes.set("fillcolor", name);
|
|
attributes.set("style", "filled");
|
|
}
|
|
|
|
void export__as_id(std::stringstream &ss) const;
|
|
|
|
void export__as_declaration(std::stringstream &ss) const;
|
|
};
|
|
|
|
class UndirectedGraph final : public Graph {
|
|
private:
|
|
Vector<std::unique_ptr<UndirectedEdge>> edges_;
|
|
|
|
public:
|
|
std::string to_dot_string() const;
|
|
|
|
UndirectedEdge &new_edge(NodePort a, NodePort b);
|
|
};
|
|
|
|
class DirectedGraph final : public Graph {
|
|
private:
|
|
Vector<std::unique_ptr<DirectedEdge>> edges_;
|
|
|
|
public:
|
|
std::string to_dot_string() const;
|
|
|
|
DirectedEdge &new_edge(NodePort from, NodePort to);
|
|
};
|
|
|
|
class NodePort {
|
|
private:
|
|
Node *node_;
|
|
std::optional<std::string> port_name_;
|
|
std::optional<std::string> port_position_;
|
|
|
|
public:
|
|
NodePort(Node &node,
|
|
std::optional<std::string> port_name = {},
|
|
std::optional<std::string> port_position = {})
|
|
: node_(&node), port_name_(std::move(port_name)), port_position_(std::move(port_position))
|
|
{
|
|
}
|
|
|
|
void to_dot_string(std::stringstream &ss) const;
|
|
};
|
|
|
|
class Edge : blender::NonCopyable, blender::NonMovable {
|
|
protected:
|
|
NodePort a_;
|
|
NodePort b_;
|
|
|
|
public:
|
|
Attributes attributes;
|
|
|
|
public:
|
|
Edge(NodePort a, NodePort b) : a_(std::move(a)), b_(std::move(b)) {}
|
|
|
|
void set_arrowhead(Attr_arrowType type)
|
|
{
|
|
attributes.set("arrowhead", arrowType_to_string(type));
|
|
}
|
|
|
|
void set_arrowtail(Attr_arrowType type)
|
|
{
|
|
attributes.set("arrowtail", arrowType_to_string(type));
|
|
}
|
|
|
|
void set_dir(Attr_dirType type)
|
|
{
|
|
attributes.set("dir", dirType_to_string(type));
|
|
}
|
|
|
|
void set_label(StringRef label)
|
|
{
|
|
attributes.set("label", label);
|
|
}
|
|
};
|
|
|
|
class DirectedEdge : public Edge {
|
|
public:
|
|
DirectedEdge(NodePort from, NodePort to) : Edge(std::move(from), std::move(to)) {}
|
|
|
|
void export__as_edge_statement(std::stringstream &ss) const;
|
|
};
|
|
|
|
class UndirectedEdge : public Edge {
|
|
public:
|
|
UndirectedEdge(NodePort a, NodePort b) : Edge(std::move(a), std::move(b)) {}
|
|
|
|
void export__as_edge_statement(std::stringstream &ss) const;
|
|
};
|
|
|
|
std::string color_attr_from_hsv(float h, float s, float v);
|
|
|
|
struct NodeWithSockets {
|
|
struct Socket {
|
|
std::string name;
|
|
std::optional<std::string> fontcolor;
|
|
};
|
|
struct Input : public Socket {
|
|
};
|
|
struct Output : public Socket {
|
|
};
|
|
|
|
std::string node_name;
|
|
Vector<Input> inputs;
|
|
Vector<Output> outputs;
|
|
|
|
Input &add_input(std::string name)
|
|
{
|
|
this->inputs.append({});
|
|
Input &input = this->inputs.last();
|
|
input.name = std::move(name);
|
|
return input;
|
|
}
|
|
|
|
Output &add_output(std::string name)
|
|
{
|
|
this->outputs.append({});
|
|
Output &output = this->outputs.last();
|
|
output.name = std::move(name);
|
|
return output;
|
|
}
|
|
};
|
|
|
|
class NodeWithSocketsRef {
|
|
private:
|
|
Node *node_;
|
|
|
|
public:
|
|
NodeWithSocketsRef(Node &node, const NodeWithSockets &data);
|
|
|
|
Node &node()
|
|
{
|
|
return *node_;
|
|
}
|
|
|
|
NodePort input(int index) const
|
|
{
|
|
std::string port = "\"in" + std::to_string(index) + "\"";
|
|
return NodePort(*node_, port, "w");
|
|
}
|
|
|
|
NodePort output(int index) const
|
|
{
|
|
std::string port = "\"out" + std::to_string(index) + "\"";
|
|
return NodePort(*node_, port, "e");
|
|
}
|
|
};
|
|
|
|
} // namespace blender::dot
|