Files
test/source/blender/functions/intern/lazy_function_graph.cc
Jacques Lucke 3943a39c08 Cleanup: rename dot to dot_export namespace
Without this, including `BLI_dot_export.hh` in `delaunay_2d.cc` causes a
compile error because the name `dot` is ambiguous.

Pull Request: https://projects.blender.org/blender/blender/pulls/136385
2025-03-23 13:34:07 +01:00

256 lines
7.5 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_dot_export.hh"
#include "FN_lazy_function_graph.hh"
#include <sstream>
namespace blender::fn::lazy_function {
Graph::Graph(const StringRef name)
{
name_ = allocator_.copy_string(name);
graph_input_node_ = allocator_.construct<InterfaceNode>().release();
graph_output_node_ = allocator_.construct<InterfaceNode>().release();
nodes_.append(graph_input_node_);
nodes_.append(graph_output_node_);
}
Graph::~Graph()
{
for (FunctionNode *node : this->function_nodes()) {
for (InputSocket *socket : node->inputs_) {
std::destroy_at(socket);
}
for (OutputSocket *socket : node->outputs_) {
std::destroy_at(socket);
}
std::destroy_at(node);
}
for (const InterfaceNode *node : {graph_input_node_, graph_output_node_}) {
for (InputSocket *socket : node->inputs_) {
std::destroy_at(socket);
}
for (OutputSocket *socket : node->outputs_) {
std::destroy_at(socket);
}
std::destroy_at(node);
}
}
FunctionNode &Graph::add_function(const LazyFunction &fn)
{
const Span<Input> inputs = fn.inputs();
const Span<Output> outputs = fn.outputs();
FunctionNode &node = *allocator_.construct<FunctionNode>().release();
node.fn_ = &fn;
node.inputs_ = allocator_.construct_elements_and_pointer_array<InputSocket>(inputs.size());
node.outputs_ = allocator_.construct_elements_and_pointer_array<OutputSocket>(outputs.size());
for (const int i : inputs.index_range()) {
InputSocket &socket = *node.inputs_[i];
socket.index_in_node_ = i;
socket.is_input_ = true;
socket.node_ = &node;
socket.type_ = inputs[i].type;
}
for (const int i : outputs.index_range()) {
OutputSocket &socket = *node.outputs_[i];
socket.index_in_node_ = i;
socket.is_input_ = false;
socket.node_ = &node;
socket.type_ = outputs[i].type;
}
nodes_.append(&node);
return node;
}
GraphInputSocket &Graph::add_input(const CPPType &type, std::string name)
{
GraphInputSocket &socket = *allocator_.construct<GraphInputSocket>().release();
socket.is_input_ = false;
socket.node_ = graph_input_node_;
socket.type_ = &type;
socket.index_in_node_ = graph_inputs_.append_and_get_index(&socket);
graph_input_node_->outputs_ = graph_inputs_;
graph_input_node_->socket_names_.append(std::move(name));
return socket;
}
GraphOutputSocket &Graph::add_output(const CPPType &type, std::string name)
{
GraphOutputSocket &socket = *allocator_.construct<GraphOutputSocket>().release();
socket.is_input_ = true;
socket.node_ = graph_output_node_;
socket.type_ = &type;
socket.index_in_node_ = graph_outputs_.append_and_get_index(&socket);
graph_output_node_->inputs_ = graph_outputs_;
graph_output_node_->socket_names_.append(std::move(name));
return socket;
}
void Graph::add_link(OutputSocket &from, InputSocket &to)
{
BLI_assert(to.origin_ == nullptr);
BLI_assert(from.type_ == to.type_);
to.origin_ = &from;
from.targets_.append(&to);
}
void Graph::clear_origin(InputSocket &socket)
{
if (socket.origin_ != nullptr) {
socket.origin_->targets_.remove_first_occurrence_and_reorder(&socket);
socket.origin_ = nullptr;
}
}
void Graph::update_node_indices()
{
for (const int i : nodes_.index_range()) {
nodes_[i]->index_in_graph_ = i;
}
}
void Graph::update_socket_indices()
{
int socket_counter = 0;
for (const int i : nodes_.index_range()) {
for (InputSocket *socket : nodes_[i]->inputs()) {
socket->index_in_graph_ = socket_counter++;
}
for (OutputSocket *socket : nodes_[i]->outputs()) {
socket->index_in_graph_ = socket_counter++;
}
}
socket_num_ = socket_counter;
}
bool Graph::node_indices_are_valid() const
{
for (const int i : nodes_.index_range()) {
if (nodes_[i]->index_in_graph_ != i) {
return false;
}
}
return true;
}
std::string Socket::name() const
{
if (node_->is_function()) {
const FunctionNode &fn_node = static_cast<const FunctionNode &>(*node_);
const LazyFunction &fn = fn_node.function();
if (is_input_) {
return fn.input_name(index_in_node_);
}
return fn.output_name(index_in_node_);
}
const InterfaceNode &interface_node = *static_cast<const InterfaceNode *>(node_);
return interface_node.socket_names_[index_in_node_];
}
std::string Socket::detailed_name() const
{
std::stringstream ss;
ss << node_->name() << ":" << (is_input_ ? "IN" : "OUT") << ":" << index_in_node_ << ":"
<< this->name();
return ss.str();
}
std::string Node::name() const
{
if (this->is_function()) {
return fn_->name();
}
return "Interface";
}
std::string Graph::ToDotOptions::socket_name(const Socket &socket) const
{
return socket.name();
}
std::optional<std::string> Graph::ToDotOptions::socket_font_color(const Socket & /*socket*/) const
{
return std::nullopt;
}
void Graph::ToDotOptions::add_edge_attributes(const OutputSocket & /*from*/,
const InputSocket & /*to*/,
dot_export::DirectedEdge & /*dot_edge*/) const
{
}
std::string Graph::to_dot(const ToDotOptions &options) const
{
dot_export::DirectedGraph digraph;
digraph.set_rankdir(dot_export::Attr_rankdir::LeftToRight);
Map<const Node *, dot_export::NodeWithSocketsRef> dot_nodes;
for (const Node *node : nodes_) {
dot_export::Node &dot_node = digraph.new_node("");
if (node->is_interface()) {
dot_node.set_background_color("lightblue");
}
else {
dot_node.set_background_color("white");
}
dot_export::NodeWithSockets dot_node_with_sockets;
dot_node_with_sockets.node_name = node->name();
for (const InputSocket *socket : node->inputs()) {
dot_export::NodeWithSockets::Input &dot_input = dot_node_with_sockets.add_input(
options.socket_name(*socket));
dot_input.fontcolor = options.socket_font_color(*socket);
}
for (const OutputSocket *socket : node->outputs()) {
dot_export::NodeWithSockets::Output &dot_output = dot_node_with_sockets.add_output(
options.socket_name(*socket));
dot_output.fontcolor = options.socket_font_color(*socket);
}
dot_nodes.add_new(node, dot_export::NodeWithSocketsRef(dot_node, dot_node_with_sockets));
}
for (const Node *node : nodes_) {
for (const InputSocket *socket : node->inputs()) {
const dot_export::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&socket->node());
const dot_export::NodePort to_dot_port = to_dot_node.input(socket->index());
if (const OutputSocket *origin = socket->origin()) {
dot_export::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&origin->node());
dot_export::DirectedEdge &dot_edge = digraph.new_edge(
from_dot_node.output(origin->index()), to_dot_port);
options.add_edge_attributes(*origin, *socket, dot_edge);
}
else if (const void *default_value = socket->default_value()) {
const CPPType &type = socket->type();
std::string value_string;
if (type.is_printable()) {
value_string = type.to_string(default_value);
}
else {
value_string = type.name();
}
dot_export::Node &default_value_dot_node = digraph.new_node(value_string);
default_value_dot_node.set_shape(dot_export::Attr_shape::Ellipse);
default_value_dot_node.attributes.set("color", "#00000055");
digraph.new_edge(default_value_dot_node, to_dot_port);
}
}
}
return digraph.to_dot_string();
}
} // namespace blender::fn::lazy_function