From 7fe47a9d5b1727a61dc724523b530762f6d6ba19 Mon Sep 17 00:00:00 2001 From: rtk0c Date: Thu, 30 Jun 2022 21:38:53 -0700 Subject: Restructure project --- app/source/Cplt/Model/Workflow/Workflow.hpp | 316 ++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 app/source/Cplt/Model/Workflow/Workflow.hpp (limited to 'app/source/Cplt/Model/Workflow/Workflow.hpp') 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ImNodes = ax::NodeEditor; + +class WorkflowConnection +{ +public: + static constexpr auto kInvalidId = std::numeric_limits::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::max(); + static constexpr auto kInvalidPinId = std::numeric_limits::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 mInputs; + std::vector 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 QueryCategoryMembers(Category category); + static std::unique_ptr 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 mConnections; + std::vector> mNodes; + std::vector> mConstants; + std::vector> mDepthGroups; + int mConnectionCount; + int mNodeCount; + int mConstantCount; + bool mDepthsDirty = true; + +public: + /* Graph access */ + + const std::vector& GetConnections() const; + std::vector& GetConnections(); + const std::vector>& GetNodes() const; + std::vector>& GetNodes(); + const std::vector>& GetConstants() const; + std::vector>& 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>& GetDepthGroups() const; + bool DoesDepthNeedsUpdate() const; + + /* Graph mutation */ + + void AddNode(std::unique_ptr 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>; + + GraphUpdateResult UpdateGraph(GraphUpdateDetails* details = nullptr); + + /* Serialization */ + + void ReadFromDataStream(InputDataStream& stream); + void WriteToDataStream(OutputDataStream& stream) const; + +private: + std::pair AllocWorkflowConnection(); + std::pair&, uint32_t> AllocWorkflowStep(); +}; + +class WorkflowAssetList final : public AssetListTyped +{ +private: + // AC = Asset Creator + std::string mACNewName; + NameSelectionError mACNewNameError = NameSelectionError::Empty; + +public: + // Inherit constructors + using AssetListTyped::AssetListTyped; + +protected: + void DiscoverFiles(const std::function& 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; +}; -- cgit v1.2.3-70-g09d2