diff options
Diffstat (limited to 'app/source/Cplt/Model/Workflow/Workflow.hpp')
-rw-r--r-- | app/source/Cplt/Model/Workflow/Workflow.hpp | 316 |
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; +}; |