diff options
Diffstat (limited to 'core/src/Model/Workflow/Workflow.cpp')
-rw-r--r-- | core/src/Model/Workflow/Workflow.cpp | 172 |
1 files changed, 112 insertions, 60 deletions
diff --git a/core/src/Model/Workflow/Workflow.cpp b/core/src/Model/Workflow/Workflow.cpp index 7afcf0b..a1af44a 100644 --- a/core/src/Model/Workflow/Workflow.cpp +++ b/core/src/Model/Workflow/Workflow.cpp @@ -78,6 +78,14 @@ WorkflowNode::WorkflowNode(Type type, Kind kind) , mDepth{ -1 } { } +Vec2i WorkflowNode::GetPosition() const { + return mPosition; +} + +void WorkflowNode::SetPosition(const Vec2i& position) { + mPosition = position; +} + size_t WorkflowNode::GetId() const { return mId; } @@ -90,6 +98,10 @@ WorkflowNode::Kind WorkflowNode::GetKind() const { return mKind; } +int WorkflowNode::GetDepth() const { + return mDepth; +} + void WorkflowNode::ConnectInput(int nodeId, WorkflowNode& output, int outputNodeId) { mWorkflow->Connect(*this, nodeId, output, outputNodeId); } @@ -236,6 +248,25 @@ void Workflow::RemoveStep(size_t id) { step->mId = WorkflowNode::kInvalidId; } +void Workflow::RemoveConnection(size_t id) { + auto& conn = mConnections[id]; + if (!conn.IsValid()) return; + + for (auto& point : conn.GetSourcePoints()) { + auto& node = *mNodes[point.Node]; + auto& pin = node.mOutputs[point.Pin]; + pin.Connection = WorkflowNode::kInvalidId; + } + for (auto& point : conn.GetDestinationPoints()) { + auto& node = *mNodes[point.Node]; + auto& pin = node.mInputs[point.Pin]; + pin.Connection = WorkflowNode::kInvalidId; + } + + conn = {}; + mDepthsDirty = true; +} + bool Workflow::Connect(WorkflowNode& sourceNode, int sourcePin, WorkflowNode& destinationNode, int destinationPin) { auto& src = sourceNode.mOutputs[sourcePin]; auto& dst = destinationNode.mInputs[destinationPin]; @@ -304,6 +335,8 @@ bool Workflow::Connect(WorkflowNode& sourceNode, int sourcePin, WorkflowNode& de return true; } +// TODO cleanup these two implementation + bool Workflow::DisconnectBySource(WorkflowNode& sourceNode, int sourcePin) { auto& sp = sourceNode.mOutputs[sourcePin]; if (!sp.IsConnected()) return false; @@ -318,7 +351,7 @@ bool Workflow::DisconnectBySource(WorkflowNode& sourceNode, int sourcePin) { auto& vec = conn.MultiConnections; vec.erase(std::remove(vec.begin(), vec.end(), Pt{ sourceNode.GetId(), sourcePin }), vec.end()); - sp.Connection = WorkflowConnection::kInvalidId; + sp.Connection = WorkflowNode::kInvalidId; } break; case WorkflowConnection::OneToMany: { @@ -326,9 +359,9 @@ bool Workflow::DisconnectBySource(WorkflowNode& sourceNode, int sourcePin) { for (auto& pt : conn.MultiConnections) { auto& node = *mNodes[pt.Node]; - node.mInputs[pt.Pin].Connection = WorkflowConnection::kInvalidId; + node.mInputs[pt.Pin].Connection = WorkflowNode::kInvalidId; } - sp.Connection = WorkflowConnection::kInvalidId; + sp.Connection = WorkflowNode::kInvalidId; conn = {}; } break; @@ -343,7 +376,7 @@ bool Workflow::DisconnectByDestination(WorkflowNode& destinationNode, int destin if (!dp.IsConnected()) return false; if (dp.IsConstantConnection()) { dp.ConnectionToConst = false; - dp.Connection = WorkflowConnection::kInvalidId; + dp.Connection = WorkflowNode::kInvalidId; return true; } @@ -356,9 +389,9 @@ bool Workflow::DisconnectByDestination(WorkflowNode& destinationNode, int destin for (auto& pt : conn.MultiConnections) { auto& node = *mNodes[pt.Node]; - node.mOutputs[pt.Pin].Connection = WorkflowConnection::kInvalidId; + node.mOutputs[pt.Pin].Connection = WorkflowNode::kInvalidId; } - dp.Connection = WorkflowConnection::kInvalidId; + dp.Connection = WorkflowNode::kInvalidId; conn = {}; } break; @@ -369,7 +402,7 @@ bool Workflow::DisconnectByDestination(WorkflowNode& destinationNode, int destin auto& vec = conn.MultiConnections; vec.erase(std::remove(vec.begin(), vec.end(), Pt{ destinationNode.GetId(), destinationPin }), vec.end()); - dp.Connection = WorkflowConnection::kInvalidId; + dp.Connection = WorkflowNode::kInvalidId; } break; } @@ -377,63 +410,76 @@ bool Workflow::DisconnectByDestination(WorkflowNode& destinationNode, int destin return true; } -void Workflow::RemoveConnection(size_t id) { - auto& conn = mConnections[id]; - if (!conn.IsValid()) return; - - switch (conn.ConnectionDirection) { - case WorkflowConnection::ManyToOne: { - - } break; - case WorkflowConnection::OneToMany: { - - } break; - } - - // TODO - - mDepthsDirty = true; +const std::vector<std::vector<size_t>>& Workflow::GetDepthGroups() const { + return mDepthGroups; } bool Workflow::DoesDepthNeedsUpdate() const { return mDepthsDirty; } -Workflow::GraphUpdateResult Workflow::UpdateGraph() { +Workflow::GraphUpdateResult Workflow::UpdateGraph(bool getInfo) { + if (!mDepthsDirty) { + return GraphUpdate_NoWorkToDo{}; + } + // Terminology: - // - Input pin <=> dependency nodes + // - Dependency = nodes its input pins are connected to + // - Dependents = nodes its output pins are connected to struct WorkingNode { // The max depth out of all dependency nodes, maintained during the traversal and committed as the actual depth - // when all dependencies of this node has been resolved + // when all dependencies of this node has been resolved. Add 1 to get the depth that will be assigned to the node. int MaximumDepth = 0; int FulfilledInputCount = 0; }; + std::vector<WorkingNode> workingNodes; - tsl::robin_set<size_t> workingSet; // The set of valid (known to has depth) nodes, used for iterating - tsl::robin_set<size_t> backWorkingSet; // The set of valid nodes built while iterating `workingSet`, and will be moved into it after done iterating + std::queue<size_t> q; + + // Check if all dependencies of this node is satisfied + auto CheckNodeDependencies = [&](WorkflowNode& node) -> bool { + for (auto& pin : node.mInputs) { + if (!pin.IsConnected()) { + return false; + } + } + return true; + }; workingNodes.reserve(mNodes.size()); - for (size_t i = 0; i < mNodes.size(); ++i) { - auto& node = mNodes[i]; - workingNodes.push_back(WorkingNode{}); + { + std::vector<size_t> unsatisfiedNodes; + for (size_t i = 0; i < mNodes.size(); ++i) { + auto& node = mNodes[i]; + workingNodes.push_back(WorkingNode{}); + + if (!node) continue; - if (!node) continue; - node->mDepth = -1; + if (!CheckNodeDependencies(*node)) { + if (getInfo) unsatisfiedNodes.push_back(i); + } + + node->mDepth = -1; - // Start traversing with the input nodes - if (node->GetType() == WorkflowNode::InputType) { - workingSet.insert(i); + // Start traversing with the input nodes + if (node->GetType() == WorkflowNode::InputType) { + q.push(i); + } + } + + if (!unsatisfiedNodes.empty()) { + return GraphUpdate_UnsatisfiedDependencies{ std::move(unsatisfiedNodes) }; } } - auto HasCyclicReference = [&](WorkflowNode* node) -> bool { + auto HasCyclicReference = [&](WorkflowNode& node) -> bool { // TODO return false; }; - auto AddOutputsToWorkingSet = [&](WorkflowNode* node) -> void { - for (auto& pin : node->mOutputs) { + auto ProcessNode = [&](WorkflowNode& node) -> void { + for (auto& pin : node.mOutputs) { if (!pin.IsConnected()) continue; auto& conn = mConnections[pin.Connection]; @@ -441,48 +487,54 @@ Workflow::GraphUpdateResult Workflow::UpdateGraph() { auto& wn = workingNodes[point.Node]; auto& n = *mNodes[point.Node].get(); - wn.FulfilledInputCount++; - if (HasCyclicReference(n)) { // TODO break; } - // Fulfilled node + wn.FulfilledInputCount++; + wn.MaximumDepth = std::max(node.mDepth, wn.MaximumDepth); + + // Node's dependency is fulfilled, we can process its dependents next // We use >= here because for a many-to-one pin, the dependency is an "or" relation ship, i.e. any of the nodes firing before this will fulfill the requirement - // TODO calc depth based on previous dependencies if (n.mInputs.size() >= wn.FulfilledInputCount) { - backWorkingSet.insert(point.Node); + n.mDepth = wn.MaximumDepth + 1; } } } }; int processedNodes = 0; - int currentDepth = 0; - while (true) { - for (size_t idx : workingSet) { - auto& wn = workingNodes[idx]; - auto& n = *mNodes[idx]; - if (n.mInputs.size() == wn.FulfilledInputCount) { - n.mDepth = currentDepth; + while (!q.empty()) { + auto& wn = workingNodes[q.front()]; + auto& n = *mNodes[q.front()]; + q.pop(); + processedNodes++; - AddOutputsToWorkingSet(n); - processedNodes++; - } + ProcessNode(n); + } + + if (processedNodes < mNodes.size()) { + // There is unreachable nodes + if (!getInfo) { + return GraphUpdate_UnreachableNodes{}; } - workingSet = std::move(backWorkingSet); - backWorkingSet.clear(); + std::vector<size_t> unreachableNodes; + for (size_t i = 0; i < mNodes.size(); ++i) { + auto& wn = workingNodes[i]; + auto& n = *mNodes[i]; - currentDepth++; + // This is a reachable node + if (n.mDepth != -1) continue; - if (processedNodes == mNodes.size()) { - break; + unreachableNodes.push_back(i); } + + return GraphUpdate_UnreachableNodes{ std::move(unreachableNodes) }; } - return GraphUpdateResult::Success; + return GraphUpdate_Success{}; } std::pair<WorkflowConnection&, size_t> Workflow::AllocWorkflowConnection() { |