From 1e09caaa2980fe901453b4b90985967a51157887 Mon Sep 17 00:00:00 2001 From: rtk0c Date: Mon, 19 Apr 2021 14:00:47 -0700 Subject: Split workflow into multiple files, fix unity build --- core/src/Model/Workflow/Workflow.cpp | 396 +++++++++++++++++++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 core/src/Model/Workflow/Workflow.cpp (limited to 'core/src/Model/Workflow/Workflow.cpp') diff --git a/core/src/Model/Workflow/Workflow.cpp b/core/src/Model/Workflow/Workflow.cpp new file mode 100644 index 0000000..a32149e --- /dev/null +++ b/core/src/Model/Workflow/Workflow.cpp @@ -0,0 +1,396 @@ +#include "Workflow.hpp" + +#include +#include +#include +#include + +WorkflowConnection::WorkflowConnection() + : MultiConnections{} + , SingleConnection{ WorkflowNode::kInvalidId, -1 } + , ConnectionDirection{ OneToMany } { +} + +bool WorkflowConnection::IsValid() const { + return SingleConnection.Node != WorkflowNode::kInvalidId; +} + +std::span WorkflowConnection::GetSourcePoints() { + switch (ConnectionDirection) { + case ManyToOne: return MultiConnections; + case OneToMany: return { &SingleConnection, 1 }; + } +} + +std::span WorkflowConnection::GetSourcePoints() const { + switch (ConnectionDirection) { + case ManyToOne: return MultiConnections; + case OneToMany: return { &SingleConnection, 1 }; + } +} + +std::span WorkflowConnection::GetDestinationPoints() { + switch (ConnectionDirection) { + case ManyToOne: return { &SingleConnection, 1 }; + case OneToMany: return MultiConnections; + } +} + +std::span 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 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 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&, 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()), id }; +} -- cgit v1.2.3-70-g09d2