/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "BLI_dot_export.hh" #include "FN_multi_function_network.hh" namespace blender { namespace fn { MFNetwork::~MFNetwork() { for (MFFunctionNode *node : m_function_nodes) { node->destruct_sockets(); node->~MFFunctionNode(); } for (MFDummyNode *node : m_dummy_nodes) { node->destruct_sockets(); node->~MFDummyNode(); } } void MFNode::destruct_sockets() { for (MFInputSocket *socket : m_inputs) { socket->~MFInputSocket(); } for (MFOutputSocket *socket : m_outputs) { socket->~MFOutputSocket(); } } /** * Add a new function node to the network. The caller keeps the ownership of the function. The * function should not be freed before the network. A reference to the new node is returned. The * node is owned by the network. */ MFFunctionNode &MFNetwork::add_function(const MultiFunction &function) { Vector input_param_indices, output_param_indices; for (uint param_index : function.param_indices()) { switch (function.param_type(param_index).interface_type()) { case MFParamType::Input: { input_param_indices.append(param_index); break; } case MFParamType::Output: { output_param_indices.append(param_index); break; } case MFParamType::Mutable: { input_param_indices.append(param_index); output_param_indices.append(param_index); break; } } } MFFunctionNode &node = *m_allocator.construct(); m_function_nodes.add_new(&node); node.m_network = this; node.m_is_dummy = false; node.m_id = m_node_or_null_by_id.append_and_get_index(&node); node.m_function = &function; node.m_input_param_indices = m_allocator.construct_array_copy(input_param_indices); node.m_output_param_indices = m_allocator.construct_array_copy(output_param_indices); node.m_inputs = m_allocator.construct_elements_and_pointer_array( input_param_indices.size()); node.m_outputs = m_allocator.construct_elements_and_pointer_array( output_param_indices.size()); for (uint i : input_param_indices.index_range()) { uint param_index = input_param_indices[i]; MFParamType param = function.param_type(param_index); BLI_assert(param.is_input_or_mutable()); MFInputSocket &socket = *node.m_inputs[i]; socket.m_data_type = param.data_type(); socket.m_node = &node; socket.m_index = i; socket.m_is_output = false; socket.m_name = function.param_name(param_index); socket.m_origin = nullptr; socket.m_id = m_socket_or_null_by_id.append_and_get_index(&socket); } for (uint i : output_param_indices.index_range()) { uint param_index = output_param_indices[i]; MFParamType param = function.param_type(param_index); BLI_assert(param.is_output_or_mutable()); MFOutputSocket &socket = *node.m_outputs[i]; socket.m_data_type = param.data_type(); socket.m_node = &node; socket.m_index = i; socket.m_is_output = true; socket.m_name = function.param_name(param_index); socket.m_id = m_socket_or_null_by_id.append_and_get_index(&socket); } return node; } /** * Add a dummy node with the given input and output sockets. */ MFDummyNode &MFNetwork::add_dummy(StringRef name, Span input_types, Span output_types, Span input_names, Span output_names) { assert_same_size(input_types, input_names); assert_same_size(output_types, output_names); MFDummyNode &node = *m_allocator.construct(); m_dummy_nodes.add_new(&node); node.m_network = this; node.m_is_dummy = true; node.m_name = m_allocator.copy_string(name); node.m_id = m_node_or_null_by_id.append_and_get_index(&node); node.m_inputs = m_allocator.construct_elements_and_pointer_array( input_types.size()); node.m_outputs = m_allocator.construct_elements_and_pointer_array( output_types.size()); node.m_input_names = m_allocator.allocate_array(input_types.size()); node.m_output_names = m_allocator.allocate_array(output_types.size()); for (uint i : input_types.index_range()) { MFInputSocket &socket = *node.m_inputs[i]; socket.m_data_type = input_types[i]; socket.m_node = &node; socket.m_index = i; socket.m_is_output = false; socket.m_name = m_allocator.copy_string(input_names[i]); socket.m_id = m_socket_or_null_by_id.append_and_get_index(&socket); node.m_input_names[i] = socket.m_name; } for (uint i : output_types.index_range()) { MFOutputSocket &socket = *node.m_outputs[i]; socket.m_data_type = output_types[i]; socket.m_node = &node; socket.m_index = i; socket.m_is_output = true; socket.m_name = m_allocator.copy_string(output_names[i]); socket.m_id = m_socket_or_null_by_id.append_and_get_index(&socket); node.m_output_names[i] = socket.m_name; } return node; } /** * Connect two sockets. This invokes undefined behavior if the sockets belong to different * networks, the sockets have a different data type, or the `to` socket is connected to something * else already. */ void MFNetwork::add_link(MFOutputSocket &from, MFInputSocket &to) { BLI_assert(to.m_origin == nullptr); BLI_assert(from.m_node->m_network == to.m_node->m_network); BLI_assert(from.m_data_type == to.m_data_type); from.m_targets.append(&to); to.m_origin = &from; } MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type) { return this->add_dummy(name, {}, {data_type}, {}, {name}).output(0); } MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type) { return this->add_dummy(name, {data_type}, {}, {name}, {}).input(0); } std::string MFNetwork::to_dot() const { namespace Dot = blender::DotExport; Dot::DirectedGraph digraph; digraph.set_rankdir(Dot::Attr_rankdir::LeftToRight); Map dot_nodes; Vector all_nodes; all_nodes.extend(m_function_nodes.as_span()); all_nodes.extend(m_dummy_nodes.as_span()); for (const MFNode *node : all_nodes) { Dot::Node &dot_node = digraph.new_node(""); Vector input_names, output_names; for (const MFInputSocket *socket : node->m_inputs) { input_names.append(socket->name() + "(" + socket->data_type().to_string() + ")"); } for (const MFOutputSocket *socket : node->m_outputs) { output_names.append(socket->name() + " (" + socket->data_type().to_string() + ")"); } Dot::NodeWithSocketsRef dot_node_ref{dot_node, node->name(), input_names, output_names}; dot_nodes.add_new(node, dot_node_ref); } for (const MFNode *to_node : all_nodes) { Dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node); for (const MFInputSocket *to_socket : to_node->m_inputs) { const MFOutputSocket *from_socket = to_socket->m_origin; if (from_socket != nullptr) { const MFNode *from_node = from_socket->m_node; Dot::NodeWithSocketsRef from_dot_node = dot_nodes.lookup(from_node); digraph.new_edge(from_dot_node.output(from_socket->m_index), to_dot_node.input(to_socket->m_index)); } } } return digraph.to_dot_string(); } } // namespace fn } // namespace blender