aboutsummaryrefslogtreecommitdiff
path: root/app/source/Cplt/Model/Workflow/Workflow.hpp
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2022-06-30 21:38:53 -0700
committerrtk0c <[email protected]>2022-06-30 21:38:53 -0700
commit7fe47a9d5b1727a61dc724523b530762f6d6ba19 (patch)
treee95be6e66db504ed06d00b72c579565bab873277 /app/source/Cplt/Model/Workflow/Workflow.hpp
parent2cf952088d375ac8b2f45b144462af0953436cff (diff)
Restructure project
Diffstat (limited to 'app/source/Cplt/Model/Workflow/Workflow.hpp')
-rw-r--r--app/source/Cplt/Model/Workflow/Workflow.hpp316
1 files changed, 316 insertions, 0 deletions
diff --git a/app/source/Cplt/Model/Workflow/Workflow.hpp b/app/source/Cplt/Model/Workflow/Workflow.hpp
new file mode 100644
index 0000000..e075e3c
--- /dev/null
+++ b/app/source/Cplt/Model/Workflow/Workflow.hpp
@@ -0,0 +1,316 @@
+#pragma once
+
+#include <Cplt/Model/Assets.hpp>
+#include <Cplt/Model/Workflow/Value.hpp>
+#include <Cplt/Utils/Vector.hpp>
+#include <Cplt/fwd.hpp>
+
+#include <imgui_node_editor.h>
+#include <cstddef>
+#include <cstdint>
+#include <filesystem>
+#include <functional>
+#include <iosfwd>
+#include <limits>
+#include <memory>
+#include <span>
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace ImNodes = ax::NodeEditor;
+
+class WorkflowConnection
+{
+public:
+ static constexpr auto kInvalidId = std::numeric_limits<uint32_t>::max();
+
+ uint32_t Id;
+ uint32_t SourceNode;
+ uint32_t SourcePin;
+ uint32_t DestinationNode;
+ uint32_t DestinationPin;
+
+public:
+ WorkflowConnection();
+
+ bool IsValid() const;
+
+ /// Used for `LinkId` when interfacing with imgui node editor. Runtime only (not saved to disk and generated when loading).
+ ImNodes::LinkId GetLinkId() const;
+
+ void DrawDebugInfo() const;
+ void ReadFrom(std::istream& stream);
+ void WriteTo(std::ostream& stream) const;
+};
+
+class WorkflowNode
+{
+public:
+ static constexpr auto kInvalidId = std::numeric_limits<uint32_t>::max();
+ static constexpr auto kInvalidPinId = std::numeric_limits<uint32_t>::max();
+
+ enum Type
+ {
+ InputType,
+ TransformType,
+ OutputType,
+ };
+
+ enum Kind
+ {
+ KD_NumericAddition,
+ KD_NumericSubtraction,
+ KD_NumericMultiplication,
+ KD_NumericDivision,
+ KD_NumericExpression,
+ KD_TextFormatting,
+ KD_DocumentTemplateExpansion,
+ KD_FormInput,
+ KD_DatabaseRowsInput,
+
+ InvalidKind,
+ KindCount = InvalidKind,
+ };
+
+ enum Category
+ {
+ CG_Numeric,
+ CG_Text,
+ CG_Document,
+ CG_UserInput,
+ CG_SystemInput,
+ CG_Output,
+
+ InvalidCategory,
+ CategoryCount = InvalidCategory,
+ };
+
+ struct InputPin
+ {
+ uint32_t Connection = WorkflowConnection::kInvalidId;
+ BaseValue::Kind MatchingType = BaseValue::InvalidKind;
+ bool ConnectionToConst = false;
+
+ /// A constant connection connects from a user-specified constant value, feeding to a valid \c DestinationNode and \c DestinationPin (i.e. input pins).
+ bool IsConstantConnection() const;
+ bool IsConnected() const;
+ BaseValue::Kind GetMatchingType() const;
+ };
+
+ struct OutputPin
+ {
+ uint32_t Connection = WorkflowConnection::kInvalidId;
+ BaseValue::Kind MatchingType = BaseValue::InvalidKind;
+
+ bool IsConnected() const;
+ BaseValue::Kind GetMatchingType() const;
+ };
+
+protected:
+ friend class Workflow;
+ friend class WorkflowEvaluationContext;
+
+ Workflow* mWorkflow;
+ std::vector<InputPin> mInputs;
+ std::vector<OutputPin> mOutputs;
+ Vec2i mPosition;
+ uint32_t mId;
+ Kind mKind;
+ int mDepth;
+ bool mLocked;
+
+public:
+ static const char* FormatKind(Kind kind);
+ static const char* FormatCategory(Category category);
+ static const char* FormatType(Type type);
+ static Category QueryCategory(Kind kind);
+ static std::span<const Kind> QueryCategoryMembers(Category category);
+ static std::unique_ptr<WorkflowNode> CreateByKind(Kind kind);
+
+ static bool IsInstance(const WorkflowNode* node);
+
+ WorkflowNode(Kind kind, bool locked);
+ virtual ~WorkflowNode() = default;
+
+ WorkflowNode(const WorkflowNode&) = delete;
+ WorkflowNode& operator=(const WorkflowNode&) = delete;
+ WorkflowNode(WorkflowNode&&) = default;
+ WorkflowNode& operator=(WorkflowNode&&) = default;
+
+ void SetPosition(const Vec2i& position);
+ Vec2i GetPosition() const;
+
+ uint32_t GetId() const;
+ /// Used for `NodeId` when interfacing with imgui node editor. Runtime only (not saved to disk and generated when loading).
+ ImNodes::NodeId GetNodeId() const;
+ Kind GetKind() const;
+ int GetDepth() const;
+ bool IsLocked() const;
+
+ Type GetType() const;
+ bool IsInputNode() const;
+ bool IsOutputNode() const;
+
+ void ConnectInput(uint32_t pinId, WorkflowNode& srcNode, uint32_t srcPinId);
+ void DisconnectInput(uint32_t pinId);
+
+ void DrawInputPinDebugInfo(uint32_t pinId) const;
+ const InputPin& GetInputPin(uint32_t pinId) const;
+ ImNodes::PinId GetInputPinUniqueId(uint32_t pinId) const;
+
+ void ConnectOutput(uint32_t pinId, WorkflowNode& dstNode, uint32_t dstPinId);
+ void DisconnectOutput(uint32_t pinId);
+
+ void DrawOutputPinDebugInfo(uint32_t pinId) const;
+ const OutputPin& GetOutputPin(uint32_t pinId) const;
+ ImNodes::PinId GetOutputPinUniqueId(uint32_t pinId) const;
+
+ virtual void Evaluate(WorkflowEvaluationContext& ctx) = 0;
+
+ void Draw();
+ virtual void DrawExtra() {}
+
+ void DrawDebugInfo() const;
+ virtual void DrawExtraDebugInfo() const {}
+
+ virtual void ReadFrom(std::istream& istream);
+ virtual void WriteTo(std::ostream& ostream);
+
+protected:
+ InputPin& InsertInputPin(int atIdx);
+ void RemoveInputPin(int pin);
+ void SwapInputPin(int a, int b);
+ OutputPin& InsertOutputPin(int atIdx);
+ void RemoveOutputPin(int pin);
+ void SwapOutputPin(int a, int b);
+
+ /* For \c Workflow to invoke, override by implementations */
+
+ void OnAttach(Workflow& workflow, uint32_t newId);
+ void OnDetach();
+};
+
+class Workflow : public Asset
+{
+ friend class WorkflowNode;
+ friend class WorkflowEvaluationContext;
+ class Private;
+
+public:
+ using CategoryType = WorkflowAssetList;
+ static constinit const WorkflowAssetList Category;
+
+private:
+ std::vector<WorkflowConnection> mConnections;
+ std::vector<std::unique_ptr<WorkflowNode>> mNodes;
+ std::vector<std::unique_ptr<BaseValue>> mConstants;
+ std::vector<std::vector<uint32_t>> mDepthGroups;
+ int mConnectionCount;
+ int mNodeCount;
+ int mConstantCount;
+ bool mDepthsDirty = true;
+
+public:
+ /* Graph access */
+
+ const std::vector<WorkflowConnection>& GetConnections() const;
+ std::vector<WorkflowConnection>& GetConnections();
+ const std::vector<std::unique_ptr<WorkflowNode>>& GetNodes() const;
+ std::vector<std::unique_ptr<WorkflowNode>>& GetNodes();
+ const std::vector<std::unique_ptr<BaseValue>>& GetConstants() const;
+ std::vector<std::unique_ptr<BaseValue>>& GetConstants();
+
+ WorkflowConnection* GetConnectionById(uint32_t id);
+ WorkflowConnection* GetConnectionByLinkId(ImNodes::LinkId linkId);
+ WorkflowNode* GetNodeById(uint32_t id);
+ WorkflowNode* GetNodeByNodeId(ImNodes::NodeId nodeId);
+ BaseValue* GetConstantById(uint32_t id);
+
+ struct GlobalPinId
+ {
+ WorkflowNode* Node;
+ uint32_t PinId;
+ /// true => input pin
+ /// false => output pin
+ bool IsOutput;
+ };
+
+ /// `pinId` should be the `UniqueId` of a pin from a node that's within this workflow.
+ GlobalPinId DisassembleGlobalPinId(ImNodes::PinId id);
+ ImNodes::PinId FabricateGlobalPinId(const WorkflowNode& node, uint32_t pinId, bool isOutput) const;
+
+ const std::vector<std::vector<uint32_t>>& GetDepthGroups() const;
+ bool DoesDepthNeedsUpdate() const;
+
+ /* Graph mutation */
+
+ void AddNode(std::unique_ptr<WorkflowNode> step);
+ void RemoveNode(uint32_t id);
+
+ void RemoveConnection(uint32_t id);
+
+ bool Connect(WorkflowNode& sourceNode, uint32_t sourcePin, WorkflowNode& destinationNode, uint32_t destinationPin);
+ bool DisconnectBySource(WorkflowNode& sourceNode, uint32_t sourcePin);
+ bool DisconnectByDestination(WorkflowNode& destinationNode, uint32_t destinationPin);
+
+ /* Graph rebuild */
+
+ enum GraphUpdateResult
+ {
+ /// Successfully rebuilt graph dependent data.
+ /// Details: nothing is written.
+ GUR_Success,
+ /// Nothing has changed since last time UpdateGraph() was called.
+ /// Details: nothing is written.
+ GUR_NoWorkToDo,
+ /// Details: list of nodes is written.
+ GUR_UnsatisfiedDependencies,
+ /// Details: list of nodes is written.
+ GUR_UnreachableNodes,
+ };
+
+ using GraphUpdateDetails = std::variant<
+ // Case: nothing
+ std::monostate,
+ // Case: list of nodes (ids)
+ std::vector<uint32_t>>;
+
+ GraphUpdateResult UpdateGraph(GraphUpdateDetails* details = nullptr);
+
+ /* Serialization */
+
+ void ReadFromDataStream(InputDataStream& stream);
+ void WriteToDataStream(OutputDataStream& stream) const;
+
+private:
+ std::pair<WorkflowConnection&, uint32_t> AllocWorkflowConnection();
+ std::pair<std::unique_ptr<WorkflowNode>&, uint32_t> AllocWorkflowStep();
+};
+
+class WorkflowAssetList final : public AssetListTyped<Workflow>
+{
+private:
+ // AC = Asset Creator
+ std::string mACNewName;
+ NameSelectionError mACNewNameError = NameSelectionError::Empty;
+
+public:
+ // Inherit constructors
+ using AssetListTyped::AssetListTyped;
+
+protected:
+ void DiscoverFiles(const std::function<void(SavedAsset)>& callback) const override;
+
+ std::string RetrieveNameFromFile(const std::filesystem::path& file) const override;
+ uuids::uuid RetrieveUuidFromFile(const std::filesystem::path& file) const override;
+ std::filesystem::path RetrievePathFromAsset(const SavedAsset& asset) const override;
+
+ bool SaveInstance(const SavedAsset& assetInfo, const Asset* asset) const override;
+ Workflow* LoadInstance(const SavedAsset& assetInfo) const override;
+ Workflow* CreateInstance(const SavedAsset& assetInfo) const override;
+ bool RenameInstanceOnDisk(const SavedAsset& assetInfo, std::string_view oldName) const override;
+
+ void DisplayAssetCreator(ListState& state) override;
+ void DisplayDetailsTable(ListState& state) const override;
+};