aboutsummaryrefslogtreecommitdiff
path: root/core/src/Model/Workflow/Workflow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/Model/Workflow/Workflow.cpp')
-rw-r--r--core/src/Model/Workflow/Workflow.cpp172
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() {