diff options
Diffstat (limited to 'source/blender/functions/FN_multi_function_network.hh')
-rw-r--r-- | source/blender/functions/FN_multi_function_network.hh | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh new file mode 100644 index 00000000000..20f8fb2ee43 --- /dev/null +++ b/source/blender/functions/FN_multi_function_network.hh @@ -0,0 +1,539 @@ +/* + * 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. + */ + +#ifndef __FN_MULTI_FUNCTION_NETWORK_HH__ +#define __FN_MULTI_FUNCTION_NETWORK_HH__ + +/** \file + * \ingroup fn + * + * A multi-function network (`MFNetwork`) allows you to connect multiple multi-functions. The + * `MFNetworkEvaluator` is a multi-function that wraps an entire network into a new multi-function + * (which can be used in another network and so on). + * + * A MFNetwork is a graph data structure with two kinds of nodes: + * - MFFunctionNode: Represents a multi-function. Its input and output sockets correspond to + * parameters of the referenced multi-function. + * - MFDummyNode: Does not reference a multi-function. Instead it just has sockets that can be + * used to represent node group inputs and outputs. + * + * Links represent data flow. Unlinked input sockets have no value. In order to execute a function + * node, all its inputs have to be connected to something. + * + * Links are only allowed between sockets with the exact same MFDataType. There are no implicit + * conversions. + * + * Every input and output parameter of a multi-function corresponds to exactly one input or output + * socket respectively. A multiple parameter belongs to exactly one input AND one output socket. + * + * There is an .to_dot() method that generates a graph in dot format for debugging purposes. + */ + +#include "FN_multi_function.hh" + +#include "BLI_vector_set.hh" + +namespace blender::fn { + +class MFNode; +class MFFunctionNode; +class MFDummyNode; +class MFSocket; +class MFInputSocket; +class MFOutputSocket; +class MFNetwork; + +class MFNode : NonCopyable, NonMovable { + protected: + MFNetwork *network_; + Span<MFInputSocket *> inputs_; + Span<MFOutputSocket *> outputs_; + bool is_dummy_; + int id_; + + friend MFNetwork; + + public: + StringRefNull name() const; + + int id() const; + + MFNetwork &network(); + const MFNetwork &network() const; + + bool is_dummy() const; + bool is_function() const; + + MFDummyNode &as_dummy(); + const MFDummyNode &as_dummy() const; + + MFFunctionNode &as_function(); + const MFFunctionNode &as_function() const; + + MFInputSocket &input(int index); + const MFInputSocket &input(int index) const; + + MFOutputSocket &output(int index); + const MFOutputSocket &output(int index) const; + + Span<MFInputSocket *> inputs(); + Span<const MFInputSocket *> inputs() const; + + Span<MFOutputSocket *> outputs(); + Span<const MFOutputSocket *> outputs() const; + + bool has_unlinked_inputs() const; + + private: + void destruct_sockets(); +}; + +class MFFunctionNode : public MFNode { + private: + const MultiFunction *function_; + Span<int> input_param_indices_; + Span<int> output_param_indices_; + + friend MFNetwork; + + public: + StringRefNull name() const; + + const MultiFunction &function() const; + + const MFInputSocket &input_for_param(int param_index) const; + const MFOutputSocket &output_for_param(int param_index) const; +}; + +class MFDummyNode : public MFNode { + private: + StringRefNull name_; + MutableSpan<StringRefNull> input_names_; + MutableSpan<StringRefNull> output_names_; + + friend MFNetwork; + + public: + StringRefNull name() const; + + Span<StringRefNull> input_names() const; + Span<StringRefNull> output_names() const; +}; + +class MFSocket : NonCopyable, NonMovable { + protected: + MFNode *node_; + bool is_output_; + int index_; + MFDataType data_type_; + int id_; + StringRefNull name_; + + friend MFNetwork; + + public: + StringRefNull name() const; + + int id() const; + int index() const; + + const MFDataType &data_type() const; + + MFNode &node(); + const MFNode &node() const; + + bool is_input() const; + bool is_output() const; + + MFInputSocket &as_input(); + const MFInputSocket &as_input() const; + + MFOutputSocket &as_output(); + const MFOutputSocket &as_output() const; +}; + +class MFInputSocket : public MFSocket { + private: + MFOutputSocket *origin_; + + friend MFNetwork; + + public: + MFOutputSocket *origin(); + const MFOutputSocket *origin() const; +}; + +class MFOutputSocket : public MFSocket { + private: + Vector<MFInputSocket *, 1> targets_; + + friend MFNetwork; + + public: + Span<MFInputSocket *> targets(); + Span<const MFInputSocket *> targets() const; +}; + +class MFNetwork : NonCopyable, NonMovable { + private: + LinearAllocator<> allocator_; + + VectorSet<MFFunctionNode *> function_nodes_; + VectorSet<MFDummyNode *> dummy_nodes_; + + Vector<MFNode *> node_or_null_by_id_; + Vector<MFSocket *> socket_or_null_by_id_; + + public: + MFNetwork() = default; + ~MFNetwork(); + + MFFunctionNode &add_function(const MultiFunction &function); + MFDummyNode &add_dummy(StringRef name, + Span<MFDataType> input_types, + Span<MFDataType> output_types, + Span<StringRef> input_names, + Span<StringRef> output_names); + void add_link(MFOutputSocket &from, MFInputSocket &to); + + MFOutputSocket &add_input(StringRef name, MFDataType data_type); + MFInputSocket &add_output(StringRef name, MFDataType data_type); + + void relink(MFOutputSocket &old_output, MFOutputSocket &new_output); + + void remove(MFNode &node); + void remove(Span<MFNode *> nodes); + + int socket_id_amount() const; + int node_id_amount() const; + + Span<MFDummyNode *> dummy_nodes(); + Span<MFFunctionNode *> function_nodes(); + + MFNode *node_or_null_by_id(int id); + const MFNode *node_or_null_by_id(int id) const; + + MFSocket *socket_or_null_by_id(int id); + const MFSocket *socket_or_null_by_id(int id) const; + + void find_dependencies(Span<const MFInputSocket *> sockets, + VectorSet<const MFOutputSocket *> &r_dummy_sockets, + VectorSet<const MFInputSocket *> &r_unlinked_inputs) const; + + bool have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const; + + std::string to_dot(Span<const MFNode *> marked_nodes = {}) const; +}; + +/* -------------------------------------------------------------------- + * MFNode inline methods. + */ + +inline StringRefNull MFNode::name() const +{ + if (is_dummy_) { + return this->as_dummy().name(); + } + else { + return this->as_function().name(); + } +} + +inline int MFNode::id() const +{ + return id_; +} + +inline MFNetwork &MFNode::network() +{ + return *network_; +} + +inline const MFNetwork &MFNode::network() const +{ + return *network_; +} + +inline bool MFNode::is_dummy() const +{ + return is_dummy_; +} + +inline bool MFNode::is_function() const +{ + return !is_dummy_; +} + +inline MFDummyNode &MFNode::as_dummy() +{ + BLI_assert(is_dummy_); + return *(MFDummyNode *)this; +} + +inline const MFDummyNode &MFNode::as_dummy() const +{ + BLI_assert(is_dummy_); + return *(const MFDummyNode *)this; +} + +inline MFFunctionNode &MFNode::as_function() +{ + BLI_assert(!is_dummy_); + return *(MFFunctionNode *)this; +} + +inline const MFFunctionNode &MFNode::as_function() const +{ + BLI_assert(!is_dummy_); + return *(const MFFunctionNode *)this; +} + +inline MFInputSocket &MFNode::input(int index) +{ + return *inputs_[index]; +} + +inline const MFInputSocket &MFNode::input(int index) const +{ + return *inputs_[index]; +} + +inline MFOutputSocket &MFNode::output(int index) +{ + return *outputs_[index]; +} + +inline const MFOutputSocket &MFNode::output(int index) const +{ + return *outputs_[index]; +} + +inline Span<MFInputSocket *> MFNode::inputs() +{ + return inputs_; +} + +inline Span<const MFInputSocket *> MFNode::inputs() const +{ + return inputs_; +} + +inline Span<MFOutputSocket *> MFNode::outputs() +{ + return outputs_; +} + +inline Span<const MFOutputSocket *> MFNode::outputs() const +{ + return outputs_; +} + +inline bool MFNode::has_unlinked_inputs() const +{ + for (const MFInputSocket *socket : inputs_) { + if (socket->origin() == nullptr) { + return true; + } + } + return false; +} + +/* -------------------------------------------------------------------- + * MFFunctionNode inline methods. + */ + +inline StringRefNull MFFunctionNode::name() const +{ + return function_->name(); +} + +inline const MultiFunction &MFFunctionNode::function() const +{ + return *function_; +} + +inline const MFInputSocket &MFFunctionNode::input_for_param(int param_index) const +{ + return this->input(input_param_indices_.first_index(param_index)); +} + +inline const MFOutputSocket &MFFunctionNode::output_for_param(int param_index) const +{ + return this->output(output_param_indices_.first_index(param_index)); +} + +/* -------------------------------------------------------------------- + * MFDummyNode inline methods. + */ + +inline StringRefNull MFDummyNode::name() const +{ + return name_; +} + +inline Span<StringRefNull> MFDummyNode::input_names() const +{ + return input_names_; +} + +inline Span<StringRefNull> MFDummyNode::output_names() const +{ + return output_names_; +} + +/* -------------------------------------------------------------------- + * MFSocket inline methods. + */ + +inline StringRefNull MFSocket::name() const +{ + return name_; +} + +inline int MFSocket::id() const +{ + return id_; +} + +inline int MFSocket::index() const +{ + return index_; +} + +inline const MFDataType &MFSocket::data_type() const +{ + return data_type_; +} + +inline MFNode &MFSocket::node() +{ + return *node_; +} + +inline const MFNode &MFSocket::node() const +{ + return *node_; +} + +inline bool MFSocket::is_input() const +{ + return !is_output_; +} + +inline bool MFSocket::is_output() const +{ + return is_output_; +} + +inline MFInputSocket &MFSocket::as_input() +{ + BLI_assert(this->is_input()); + return *(MFInputSocket *)this; +} + +inline const MFInputSocket &MFSocket::as_input() const +{ + BLI_assert(this->is_input()); + return *(const MFInputSocket *)this; +} + +inline MFOutputSocket &MFSocket::as_output() +{ + BLI_assert(this->is_output()); + return *(MFOutputSocket *)this; +} + +inline const MFOutputSocket &MFSocket::as_output() const +{ + BLI_assert(this->is_output()); + return *(const MFOutputSocket *)this; +} + +/* -------------------------------------------------------------------- + * MFInputSocket inline methods. + */ + +inline MFOutputSocket *MFInputSocket::origin() +{ + return origin_; +} + +inline const MFOutputSocket *MFInputSocket::origin() const +{ + return origin_; +} + +/* -------------------------------------------------------------------- + * MFOutputSocket inline methods. + */ + +inline Span<MFInputSocket *> MFOutputSocket::targets() +{ + return targets_; +} + +inline Span<const MFInputSocket *> MFOutputSocket::targets() const +{ + return targets_; +} + +/* -------------------------------------------------------------------- + * MFNetwork inline methods. + */ + +inline Span<MFDummyNode *> MFNetwork::dummy_nodes() +{ + return dummy_nodes_; +} + +inline Span<MFFunctionNode *> MFNetwork::function_nodes() +{ + return function_nodes_; +} + +inline MFNode *MFNetwork::node_or_null_by_id(int id) +{ + return node_or_null_by_id_[id]; +} + +inline const MFNode *MFNetwork::node_or_null_by_id(int id) const +{ + return node_or_null_by_id_[id]; +} + +inline MFSocket *MFNetwork::socket_or_null_by_id(int id) +{ + return socket_or_null_by_id_[id]; +} + +inline const MFSocket *MFNetwork::socket_or_null_by_id(int id) const +{ + return socket_or_null_by_id_[id]; +} + +inline int MFNetwork::socket_id_amount() const +{ + return socket_or_null_by_id_.size(); +} + +inline int MFNetwork::node_id_amount() const +{ + return node_or_null_by_id_.size(); +} + +} // namespace blender::fn + +#endif /* __FN_MULTI_FUNCTION_NETWORK_HH__ */ |