301 lines
6.0 KiB
C++
301 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
|