aboutsummaryrefslogtreecommitdiff
path: root/core/src/Model/Workflow.cpp
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2021-04-19 14:00:47 -0700
committerrtk0c <[email protected]>2021-04-19 14:00:47 -0700
commit1e09caaa2980fe901453b4b90985967a51157887 (patch)
treedf61974f9a5efa9a6732bd6d7b1ec1e6d1af182a /core/src/Model/Workflow.cpp
parentb00b306de1140cb7b759ed0f639e8210fd7dffa6 (diff)
Split workflow into multiple files, fix unity build
Diffstat (limited to 'core/src/Model/Workflow.cpp')
-rw-r--r--core/src/Model/Workflow.cpp576
1 files changed, 0 insertions, 576 deletions
diff --git a/core/src/Model/Workflow.cpp b/core/src/Model/Workflow.cpp
deleted file mode 100644
index 7ca1b9e..0000000
--- a/core/src/Model/Workflow.cpp
+++ /dev/null
@@ -1,576 +0,0 @@
-#include "Workflow.hpp"
-
-#include <algorithm>
-#include <cassert>
-#include <queue>
-#include <utility>
-
-WorkflowConnection::WorkflowConnection()
- : MultiConnections{}
- , SingleConnection{ WorkflowNode::kInvalidId, -1 }
- , ConnectionDirection{ OneToMany } {
-}
-
-bool WorkflowConnection::IsValid() const {
- return SingleConnection.Node != WorkflowNode::kInvalidId;
-}
-
-std::span<WorkflowConnection::ConnectionPoint> WorkflowConnection::GetSourcePoints() {
- switch (ConnectionDirection) {
- case ManyToOne: return MultiConnections;
- case OneToMany: return { &SingleConnection, 1 };
- }
-}
-
-std::span<const WorkflowConnection::ConnectionPoint> WorkflowConnection::GetSourcePoints() const {
- switch (ConnectionDirection) {
- case ManyToOne: return MultiConnections;
- case OneToMany: return { &SingleConnection, 1 };
- }
-}
-
-std::span<WorkflowConnection::ConnectionPoint> WorkflowConnection::GetDestinationPoints() {
- switch (ConnectionDirection) {
- case ManyToOne: return { &SingleConnection, 1 };
- case OneToMany: return MultiConnections;
- }
-}
-
-std::span<const WorkflowConnection::ConnectionPoint> WorkflowConnection::GetDestinationPoints() const {
- switch (ConnectionDirection) {
- case ManyToOne: return { &SingleConnection, 1 };
- case OneToMany: return MultiConnections;
- }
-}
-
-bool WorkflowNode::InputPin::IsConstantConnection() const {
- return ConnectionToConst && IsConnected();
-}
-
-bool WorkflowNode::InputPin::IsConnected() const {
- return Connection != WorkflowConnection::kInvalidId;
-}
-
-BaseValue::Kind WorkflowNode::InputPin::GetMatchingType() const {
- return MatchingType;
-}
-
-WorkflowConnection::Direction WorkflowNode::InputPin::GetSupportedDirection() const {
- return AllowsMultipleConnections ? WorkflowConnection::ManyToOne : WorkflowConnection::OneToMany;
-}
-
-bool WorkflowNode::OutputPin::IsConnected() const {
- return Connection != WorkflowConnection::kInvalidId;
-}
-
-BaseValue::Kind WorkflowNode::OutputPin::GetMatchingType() const {
- return MatchingType;
-}
-
-WorkflowConnection::Direction WorkflowNode::OutputPin::GetSupportedDirection() const {
- return AllowsMultipleConnections ? WorkflowConnection::OneToMany : WorkflowConnection::ManyToOne;
-}
-
-WorkflowNode::WorkflowNode(Type type, Kind kind)
- : mType{ type }
- , mKind{ kind } {
-}
-
-size_t WorkflowNode::GetId() const {
- return mId;
-}
-
-WorkflowNode::Type WorkflowNode::GetType() const {
- return mType;
-}
-
-WorkflowNode::Kind WorkflowNode::GetKind() const {
- return mKind;
-}
-
-void WorkflowNode::ConnectInput(int nodeId, WorkflowNode& output, int outputNodeId) {
- mWorkflow->Connect(*this, nodeId, output, outputNodeId);
-}
-
-void WorkflowNode::DisconnectInput(int nodeId) {
- mWorkflow->DisconnectByDestination(*this, nodeId);
-}
-
-bool WorkflowNode::IsInputConnected(int nodeId) const {
- return mInputs[nodeId].IsConnected();
-}
-
-void WorkflowNode::ConnectOutput(int nodeId, WorkflowNode& input, int inputNodeId) {
- mWorkflow->Connect(input, inputNodeId, *this, nodeId);
-}
-
-void WorkflowNode::DisconnectOutput(int nodeId) {
- mWorkflow->DisconnectBySource(*this, nodeId);
-}
-
-bool WorkflowNode::IsOutputConnected(int nodeId) const {
- return mOutputs[nodeId].IsConnected();
-}
-
-WorkflowNode::InputPin& WorkflowNode::InsertInputPin(int atIdx) {
- assert(atIdx >= 0 && atIdx < mInputs.size());
-
- mInputs.push_back(InputPin{});
- for (int i = (int)mInputs.size() - 1, end = atIdx + 1; i >= end; --i) {
- SwapInputPin(i, i + 1);
- }
-
- return mInputs[atIdx];
-}
-
-void WorkflowNode::RemoveInputPin(int pin) {
- DisconnectInput(pin);
- for (int i = 0, end = (int)mInputs.size() - 1; i < end; ++i) {
- SwapInputPin(i, i + 1);
- }
- mInputs.resize(mInputs.size() - 1);
-}
-
-void WorkflowNode::SwapInputPin(int a, int b) {
- auto& pinA = mInputs[a];
- auto& pinB = mInputs[b];
-
- if (mWorkflow) {
- // Delay assignment to ConnectionPoint::Pin so that the pinB loop (if it happens to run and looks in the same Connection) doesn't match the point updated in the pinA loop
- using Pt = WorkflowConnection::ConnectionPoint;
- Pt* pointA = nullptr;
- Pt* pointB = nullptr;
-
- if (pinA.IsConnected() && !pinA.IsConstantConnection()) {
- auto pts = mWorkflow->GetConnectionById(pinA.Connection)->GetDestinationPoints();
- auto it = std::find(pts.begin(), pts.end(), Pt{ mId, a });
- pointA = it == pts.end() ? nullptr : &*it;
- }
- if (pinB.IsConnected() && !pinB.IsConstantConnection()) {
- auto pts = mWorkflow->GetConnectionById(pinB.Connection)->GetDestinationPoints();
- auto it = std::find(pts.begin(), pts.end(), Pt{ mId, b });
- pointB = it == pts.end() ? nullptr : &*it;
- }
-
- if (pointA) pointA->Pin = b;
- if (pointB) pointB->Pin = a;
- }
-
- std::swap(pinA, pinB);
-}
-
-WorkflowNode::OutputPin& WorkflowNode::InsertOutputPin(int atIdx) {
- assert(atIdx >= 0 && atIdx < mOutputs.size());
-
- mOutputs.push_back(OutputPin{});
- for (int i = (int)mOutputs.size() - 1, end = atIdx + 1; i >= end; --i) {
- SwapOutputPin(i, i + 1);
- }
-
- return mOutputs[atIdx];
-}
-
-void WorkflowNode::RemoveOutputPin(int pin) {
- DisconnectOutput(pin);
- for (int i = 0, end = (int)mOutputs.size() - 1; i < end; ++i) {
- SwapInputPin(i, i + 1);
- }
- mOutputs.resize(mOutputs.size() - 1);
-}
-
-void WorkflowNode::SwapOutputPin(int a, int b) {
- auto& pinA = mOutputs[a];
- auto& pinB = mOutputs[b];
-
- if (mWorkflow) {
- using Pt = WorkflowConnection::ConnectionPoint;
- Pt* pointA = nullptr;
- Pt* pointB = nullptr;
-
- if (pinA.IsConnected()) {
- auto pts = mWorkflow->GetConnectionById(pinA.Connection)->GetSourcePoints();
- auto it = std::find(pts.begin(), pts.end(), Pt{ mId, a });
- pointA = it == pts.end() ? nullptr : &*it;
- }
- if (pinB.IsConnected()) {
- auto pts = mWorkflow->GetConnectionById(pinB.Connection)->GetSourcePoints();
- auto it = std::find(pts.begin(), pts.end(), Pt{ mId, b });
- pointB = it == pts.end() ? nullptr : &*it;
- }
-
- if (pointA) pointA->Pin = b;
- if (pointB) pointB->Pin = a;
- }
-
- std::swap(pinA, pinB);
-}
-
-WorkflowConnection* Workflow::GetConnectionById(size_t id) {
- return &mConnections[id];
-}
-
-WorkflowNode* Workflow::GetStepById(size_t id) {
- return mNodes[id].get();
-}
-
-BaseValue* Workflow::GetConstantById(size_t id) {
- return mConstants[id].get();
-}
-
-void Workflow::AddStep(std::unique_ptr<WorkflowNode> step) {
- auto [storage, id] = AllocWorkflowStep();
- storage = std::move(step);
- storage->OnAttach(*this, id);
- storage->mWorkflow = this;
- storage->mId = id;
-}
-
-void Workflow::RemoveStep(size_t id) {
- auto& step = mNodes[id];
- if (step == nullptr) return;
-
- step->OnDetach();
- step->mWorkflow = nullptr;
- step->mId = WorkflowNode::kInvalidId;
-}
-
-bool Workflow::Connect(WorkflowNode& sourceNode, int sourcePin, WorkflowNode& destinationNode, int destinationPin) {
- auto& src = sourceNode.mOutputs[sourcePin];
- auto& dst = destinationNode.mInputs[destinationPin];
-
- // Equivalent to `if (o.GetSupportedDirection() != i.GetSupportedDirection()) return false;`
- // Cannot connect two pins of different type
- if (src.AllowsMultipleConnections == dst.AllowsMultipleConnections) return false;
- // Would be same as `dst.GetSupportedDirection()` because we validated that they are the same above
- auto direction = src.GetSupportedDirection();
-
- // TODO report error to user?
- if (src.GetMatchingType() != dst.GetMatchingType()) {
- return false;
- }
-
- using Pt = WorkflowConnection::ConnectionPoint;
- Pt* srcConnPt;
- Pt* dstConnPt;
- size_t connId;
-
- switch (direction) {
- case WorkflowConnection::ManyToOne: {
- if (dst.IsConnected()) return false;
-
- WorkflowConnection* conn;
- if (src.IsConnected()) {
- conn = &mConnections[src.Connection];
- connId = src.Connection;
- } else {
- auto p = AllocWorkflowConnection();
- conn = &p.first;
- connId = p.second;
- }
-
- srcConnPt = &conn->MultiConnections.emplace_back();
- dstConnPt = &conn->SingleConnection;
- } break;
-
- case WorkflowConnection::OneToMany: {
- if (src.IsConnected()) return false;
-
- WorkflowConnection* conn;
- if (dst.IsConnected()) {
- conn = &mConnections[src.Connection];
- connId = src.Connection;
- } else {
- auto p = AllocWorkflowConnection();
- conn = &p.first;
- connId = p.second;
- }
-
- srcConnPt = &conn->SingleConnection;
- dstConnPt = &conn->MultiConnections.emplace_back();
- } break;
- }
-
- srcConnPt->Node = sourceNode.GetId();
- srcConnPt->Pin = sourcePin;
- dstConnPt->Node = destinationNode.GetId();
- dstConnPt->Pin = destinationPin;
-
- src.Connection = connId;
- dst.Connection = connId;
- return true;
-}
-
-bool Workflow::DisconnectBySource(WorkflowNode& sourceNode, int sourcePin) {
- auto& sp = sourceNode.mOutputs[sourcePin];
- if (!sp.IsConnected()) return false;
-
- auto& conn = mConnections[sp.Connection];
-
- using Pt = WorkflowConnection::ConnectionPoint;
- switch (sp.GetSupportedDirection()) {
- case WorkflowConnection::ManyToOne: {
- // Recessive pin, remove ConnectionPoint associated with this pin only
-
- auto& vec = conn.MultiConnections;
- vec.erase(std::remove(vec.begin(), vec.end(), Pt{ sourceNode.GetId(), sourcePin }), vec.end());
-
- sp.Connection = WorkflowConnection::kInvalidId;
- } break;
-
- case WorkflowConnection::OneToMany: {
- // Dominate pin, removes whole connection
-
- for (auto& pt : conn.MultiConnections) {
- auto& node = *mNodes[pt.Node];
- node.mInputs[pt.Pin].Connection = WorkflowConnection::kInvalidId;
- }
- sp.Connection = WorkflowConnection::kInvalidId;
-
- conn = {};
- } break;
- }
-
- return true;
-}
-
-bool Workflow::DisconnectByDestination(WorkflowNode& destinationNode, int destinationPin) {
- auto& dp = destinationNode.mInputs[destinationPin];
- if (!dp.IsConnected()) return false;
- if (dp.IsConstantConnection()) {
- dp.ConnectionToConst = false;
- dp.Connection = WorkflowConnection::kInvalidId;
- return true;
- }
-
- auto& conn = mConnections[dp.Connection];
-
- using Pt = WorkflowConnection::ConnectionPoint;
- switch (dp.GetSupportedDirection()) {
- case WorkflowConnection::ManyToOne: {
- // Dominate pin, removes whole connection
-
- for (auto& pt : conn.MultiConnections) {
- auto& node = *mNodes[pt.Node];
- node.mOutputs[pt.Pin].Connection = WorkflowConnection::kInvalidId;
- }
- dp.Connection = WorkflowConnection::kInvalidId;
-
- conn = {};
- } break;
-
- case WorkflowConnection::OneToMany: {
- // Recessive pin, remove ConnectionPoint associated with this pin only
-
- auto& vec = conn.MultiConnections;
- vec.erase(std::remove(vec.begin(), vec.end(), Pt{ destinationNode.GetId(), destinationPin }), vec.end());
-
- dp.Connection = WorkflowConnection::kInvalidId;
- } break;
- }
-
- return true;
-}
-
-std::pair<WorkflowConnection&, size_t> Workflow::AllocWorkflowConnection() {
- for (size_t idx = 0; idx < mConnections.size(); ++idx) {
- auto& elm = mConnections[idx];
- if (!elm.IsValid()) {
- return { elm, idx };
- }
- }
-
- auto id = mConnections.size();
- return { mConnections.emplace_back(WorkflowConnection{}), id };
-}
-
-std::pair<std::unique_ptr<WorkflowNode>&, size_t> Workflow::AllocWorkflowStep() {
- for (size_t idx = 0; idx < mNodes.size(); ++idx) {
- auto& elm = mNodes[idx];
- if (elm == nullptr) {
- return { elm, idx };
- }
- }
-
- auto id = mNodes.size();
- return { mNodes.emplace_back(std::unique_ptr<WorkflowNode>()), id };
-}
-
-namespace {
-enum class EvaluationStatus {
- Unevaluated,
- Success,
- Failed,
-};
-} // namespace
-
-struct WorkflowEvaluationContext::RuntimeNode {
- EvaluationStatus Status = EvaluationStatus::Unevaluated;
-};
-
-struct WorkflowEvaluationContext::RuntimeConnection {
- std::unique_ptr<BaseValue> Value;
-
- bool IsAvailableValue() const {
- return Value != nullptr;
- }
-};
-
-WorkflowEvaluationContext::WorkflowEvaluationContext(Workflow& workflow)
- : mWorkflow{ &workflow } {
- mRuntimeNodes.resize(workflow.mNodes.size());
- mRuntimeConnections.resize(workflow.mConnections.size());
-}
-
-BaseValue* WorkflowEvaluationContext::GetConnectionValue(size_t id, bool constant) {
- if (constant) {
- return mWorkflow->GetConstantById(id);
- } else {
- return mRuntimeConnections[id].Value.get();
- }
-}
-
-BaseValue* WorkflowEvaluationContext::GetConnectionValue(const WorkflowNode::InputPin& inputPin) {
- if (inputPin.IsConnected()) {
- return GetConnectionValue(inputPin.Connection, inputPin.IsConstantConnection());
- } else {
- return nullptr;
- }
-}
-
-void WorkflowEvaluationContext::SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value) {
- mRuntimeConnections[id].Value = std::move(value);
-}
-
-void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value) {
- if (outputPin.IsConnected()) {
- SetConnectionValue(outputPin.Connection, std::move(value));
- }
-}
-
-void WorkflowEvaluationContext::Run() {
- std::queue<size_t> candidates; // Stores index to nodes
- int evaluatedCount = 0;
- int erroredCount = 0;
-
- // Evaluate all the input nodes first
- for (size_t i = 0; i < mRuntimeNodes.size(); ++i) {
- if (mWorkflow->mNodes[i]->GetType() == WorkflowNode::InputType) {
- candidates.push(i);
- }
- }
-
- auto AddOutputsToCandidates = [&](size_t idx) {
- auto& node = *mWorkflow->mNodes[idx];
- auto& rNode = mRuntimeNodes[idx];
- for (auto& pin : node.mOutputs) {
- if (!pin.IsConnected()) continue;
- // TODO support the other variant
- if (pin.GetSupportedDirection() != WorkflowConnection::OneToMany) continue;
-
- auto& rConn = mRuntimeConnections[pin.Connection];
- auto& conn = mWorkflow->mConnections[pin.Connection];
- if (rConn.IsAvailableValue()) {
- for (WorkflowConnection::ConnectionPoint& cp : conn.MultiConnections) {
- if (rNode.Status != EvaluationStatus::Unevaluated) {
- candidates.push(cp.Node);
- }
- }
- }
- }
- };
- auto FindCandidates = [&]() {
- for (size_t i = 0; i < mWorkflow->mNodes.size(); ++i) {
- auto& node = mWorkflow->mNodes[i];
- auto& rNode = mRuntimeNodes[i];
-
- if (rNode.Status != EvaluationStatus::Unevaluated) {
- continue;
- }
-
- for (auto& pin : node->mInputs) {
- if (!pin.IsConnected()) continue;
-
- auto& rConn = mRuntimeConnections[pin.Connection];
- if (!rConn.IsAvailableValue()) {
- goto skip;
- }
- }
-
- candidates.push(i);
-
- skip:
- continue;
- }
- };
-
- while (true) {
- while (!candidates.empty()) {
- auto idx = candidates.front();
- auto& node = *mWorkflow->mNodes[idx];
- auto& rNode = mRuntimeNodes[idx];
- candidates.pop();
-
- int preEvalErrors = mErrors.size();
- node.Evaluate(*this);
- if (preEvalErrors != mErrors.size()) {
- erroredCount++;
- } else {
- evaluatedCount++;
- AddOutputsToCandidates(idx);
- }
- }
-
- if (evaluatedCount + erroredCount >= mRuntimeNodes.size()) {
- break;
- }
-
- // Candidates empty, but there are still possibly-evaluable nodes
- FindCandidates();
- }
-
- for (size_t i = 0; i < mRuntimeNodes.size(); ++i) {
- if (mWorkflow->mNodes[i]->GetType() == WorkflowNode::OutputType) {
- // TODO
- }
- }
-}
-
-void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node, int pinId, bool inputPin) {
- mErrors.push_back(WorkflowEvaluationError{
- .Message = std::move(message),
- .NodeId = node.GetId(),
- .PinId = pinId,
- .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin,
- .Type = WorkflowEvaluationError::Error,
- });
-}
-
-void WorkflowEvaluationContext::ReportError(std::string message, const WorkflowNode& node) {
- mErrors.push_back(WorkflowEvaluationError{
- .Message = std::move(message),
- .NodeId = node.GetId(),
- .PinId = -1,
- .PinType = WorkflowEvaluationError::NoPin,
- .Type = WorkflowEvaluationError::Error,
- });
-}
-
-void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node, int pinId, bool inputPin) {
- mErrors.push_back(WorkflowEvaluationError{
- .Message = std::move(message),
- .NodeId = node.GetId(),
- .PinId = pinId,
- .PinType = inputPin ? WorkflowEvaluationError::InputPin : WorkflowEvaluationError::OutputPin,
- .Type = WorkflowEvaluationError::Warning,
- });
-}
-
-void WorkflowEvaluationContext::ReportWarning(std::string message, const WorkflowNode& node) {
- mErrors.push_back(WorkflowEvaluationError{
- .Message = std::move(message),
- .NodeId = node.GetId(),
- .PinId = -1,
- .PinType = WorkflowEvaluationError::NoPin,
- .Type = WorkflowEvaluationError::Warning,
- });
-}