Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/functions/FN_lazy_function_graph.hh')
-rw-r--r--source/blender/functions/FN_lazy_function_graph.hh421
1 files changed, 421 insertions, 0 deletions
diff --git a/source/blender/functions/FN_lazy_function_graph.hh b/source/blender/functions/FN_lazy_function_graph.hh
new file mode 100644
index 00000000000..4ede28c4f26
--- /dev/null
+++ b/source/blender/functions/FN_lazy_function_graph.hh
@@ -0,0 +1,421 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup fn
+ *
+ * This file contains a graph data structure that allows composing multiple lazy-functions into a
+ * combined lazy-function.
+ *
+ * There are two types of nodes in the graph:
+ * - #FunctionNode: Corresponds to a #LazyFunction. The inputs and outputs of the function become
+ * input and output sockets of the node.
+ * - #DummyNode: Is used to indicate inputs and outputs of the entire graph. It can have an
+ * arbitrary number of sockets.
+ */
+
+#include "BLI_linear_allocator.hh"
+
+#include "FN_lazy_function.hh"
+
+namespace blender::fn::lazy_function {
+
+class Socket;
+class InputSocket;
+class OutputSocket;
+class Node;
+class Graph;
+
+/**
+ * A #Socket is the interface of a #Node. Every #Socket is either an #InputSocket or #OutputSocket.
+ * Links can be created from output sockets to input sockets.
+ */
+class Socket : NonCopyable, NonMovable {
+ protected:
+ /**
+ * The node the socket belongs to.
+ */
+ Node *node_;
+ /**
+ * Data type of the socket. Only sockets with the same type can be linked.
+ */
+ const CPPType *type_;
+ /**
+ * Indicates whether this is an #InputSocket or #OutputSocket.
+ */
+ bool is_input_;
+ /**
+ * Index of the socket. E.g. 0 for the first input and the first output socket.
+ */
+ int index_in_node_;
+
+ friend Graph;
+
+ public:
+ bool is_input() const;
+ bool is_output() const;
+
+ int index() const;
+
+ InputSocket &as_input();
+ OutputSocket &as_output();
+ const InputSocket &as_input() const;
+ const OutputSocket &as_output() const;
+
+ const Node &node() const;
+ Node &node();
+
+ const CPPType &type() const;
+
+ std::string name() const;
+};
+
+class InputSocket : public Socket {
+ private:
+ /**
+ * An input can have at most one link connected to it. The linked socket is the "origin" because
+ * it's where the data is coming from. The type of the origin must be the same as the type of
+ * this socket.
+ */
+ OutputSocket *origin_;
+ /**
+ * Can be null or a non-owning pointer to a value of the type of the socket. This value will be
+ * used when the input is used but not linked.
+ *
+ * This is technically not needed, because one could just create a separate node that just
+ * outputs the value, but that would have more overhead. Especially because it's commonly the
+ * case that most inputs are unlinked.
+ */
+ const void *default_value_ = nullptr;
+
+ friend Graph;
+
+ public:
+ OutputSocket *origin();
+ const OutputSocket *origin() const;
+
+ const void *default_value() const;
+ void set_default_value(const void *value);
+};
+
+class OutputSocket : public Socket {
+ private:
+ /**
+ * An output can be linked to an arbitrary number of inputs of the same type.
+ */
+ Vector<InputSocket *> targets_;
+
+ friend Graph;
+
+ public:
+ Span<InputSocket *> targets();
+ Span<const InputSocket *> targets() const;
+};
+
+/**
+ * A #Node has input and output sockets. Every node is either a #FunctionNode or a #DummyNode.
+ */
+class Node : NonCopyable, NonMovable {
+ protected:
+ /**
+ * The function this node corresponds to. If this is null, the node is a #DummyNode.
+ * The function is not owned by this #Node nor by the #Graph.
+ */
+ const LazyFunction *fn_ = nullptr;
+ /**
+ * Input sockets of the node.
+ */
+ Span<InputSocket *> inputs_;
+ /**
+ * Output sockets of the node.
+ */
+ Span<OutputSocket *> outputs_;
+ /**
+ * An index that is set when calling #Graph::update_node_indices. This can be used to create
+ * efficient mappings from nodes to other data using just an array instead of a hash map.
+ *
+ * This is technically not necessary but has better performance than always using hash maps.
+ */
+ int index_in_graph_ = -1;
+
+ friend Graph;
+
+ public:
+ bool is_dummy() const;
+ bool is_function() const;
+ int index_in_graph() const;
+
+ Span<const InputSocket *> inputs() const;
+ Span<const OutputSocket *> outputs() const;
+ Span<InputSocket *> inputs();
+ Span<OutputSocket *> outputs();
+
+ const InputSocket &input(int index) const;
+ const OutputSocket &output(int index) const;
+ InputSocket &input(int index);
+ OutputSocket &output(int index);
+
+ std::string name() const;
+};
+
+/**
+ * A #Node that corresponds to a specific #LazyFunction.
+ */
+class FunctionNode : public Node {
+ public:
+ const LazyFunction &function() const;
+};
+
+/**
+ * A #Node that does *not* correspond to a #LazyFunction. Instead it can be used to indicate inputs
+ * and outputs of the entire graph. It can have an arbitrary number of inputs and outputs.
+ */
+class DummyNode : public Node {
+ private:
+ std::string name_;
+
+ friend Node;
+};
+
+/**
+ * A container for an arbitrary number of nodes and links between their sockets.
+ */
+class Graph : NonCopyable, NonMovable {
+ private:
+ /**
+ * Used to allocate nodes and sockets in the graph.
+ */
+ LinearAllocator<> allocator_;
+ /**
+ * Contains all nodes in the graph so that it is efficient to iterate over them.
+ */
+ Vector<Node *> nodes_;
+
+ public:
+ ~Graph();
+
+ /**
+ * Get all nodes in the graph. The index in the span corresponds to #Node::index_in_graph.
+ */
+ Span<const Node *> nodes() const;
+
+ /**
+ * Add a new function node with sockets that match the passed in #LazyFunction.
+ */
+ FunctionNode &add_function(const LazyFunction &fn);
+
+ /**
+ * Add a new dummy node with the given socket types.
+ */
+ DummyNode &add_dummy(Span<const CPPType *> input_types, Span<const CPPType *> output_types);
+
+ /**
+ * Add a link between the two given sockets.
+ * This has undefined behavior when the input is linked to something else already.
+ */
+ void add_link(OutputSocket &from, InputSocket &to);
+
+ /**
+ * Make sure that #Node::index_in_graph is up to date.
+ */
+ void update_node_indices();
+
+ /**
+ * Can be used to assert that #update_node_indices has been called.
+ */
+ bool node_indices_are_valid() const;
+
+ /**
+ * Utility to generate a dot graph string for the graph. This can be used for debugging.
+ */
+ std::string to_dot() const;
+};
+
+/* -------------------------------------------------------------------- */
+/** \name #Socket Inline Methods
+ * \{ */
+
+inline bool Socket::is_input() const
+{
+ return is_input_;
+}
+
+inline bool Socket::is_output() const
+{
+ return !is_input_;
+}
+
+inline int Socket::index() const
+{
+ return index_in_node_;
+}
+
+inline InputSocket &Socket::as_input()
+{
+ BLI_assert(this->is_input());
+ return *static_cast<InputSocket *>(this);
+}
+
+inline OutputSocket &Socket::as_output()
+{
+ BLI_assert(this->is_output());
+ return *static_cast<OutputSocket *>(this);
+}
+
+inline const InputSocket &Socket::as_input() const
+{
+ BLI_assert(this->is_input());
+ return *static_cast<const InputSocket *>(this);
+}
+
+inline const OutputSocket &Socket::as_output() const
+{
+ BLI_assert(this->is_output());
+ return *static_cast<const OutputSocket *>(this);
+}
+
+inline const Node &Socket::node() const
+{
+ return *node_;
+}
+
+inline Node &Socket::node()
+{
+ return *node_;
+}
+
+inline const CPPType &Socket::type() const
+{
+ return *type_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #InputSocket Inline Methods
+ * \{ */
+
+inline const OutputSocket *InputSocket::origin() const
+{
+ return origin_;
+}
+
+inline OutputSocket *InputSocket::origin()
+{
+ return origin_;
+}
+
+inline const void *InputSocket::default_value() const
+{
+ return default_value_;
+}
+
+inline void InputSocket::set_default_value(const void *value)
+{
+ default_value_ = value;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #OutputSocket Inline Methods
+ * \{ */
+
+inline Span<const InputSocket *> OutputSocket::targets() const
+{
+ return targets_;
+}
+
+inline Span<InputSocket *> OutputSocket::targets()
+{
+ return targets_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #Node Inline Methods
+ * \{ */
+
+inline bool Node::is_dummy() const
+{
+ return fn_ == nullptr;
+}
+
+inline bool Node::is_function() const
+{
+ return fn_ != nullptr;
+}
+
+inline int Node::index_in_graph() const
+{
+ return index_in_graph_;
+}
+
+inline Span<const InputSocket *> Node::inputs() const
+{
+ return inputs_;
+}
+
+inline Span<const OutputSocket *> Node::outputs() const
+{
+ return outputs_;
+}
+
+inline Span<InputSocket *> Node::inputs()
+{
+ return inputs_;
+}
+
+inline Span<OutputSocket *> Node::outputs()
+{
+ return outputs_;
+}
+
+inline const InputSocket &Node::input(const int index) const
+{
+ return *inputs_[index];
+}
+
+inline const OutputSocket &Node::output(const int index) const
+{
+ return *outputs_[index];
+}
+
+inline InputSocket &Node::input(const int index)
+{
+ return *inputs_[index];
+}
+
+inline OutputSocket &Node::output(const int index)
+{
+ return *outputs_[index];
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #FunctionNode Inline Methods
+ * \{ */
+
+inline const LazyFunction &FunctionNode::function() const
+{
+ BLI_assert(fn_ != nullptr);
+ return *fn_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #Graph Inline Methods
+ * \{ */
+
+inline Span<const Node *> Graph::nodes() const
+{
+ return nodes_;
+}
+
+/** \} */
+
+} // namespace blender::fn::lazy_function