diff options
author | rtk0c <[email protected]> | 2021-05-09 18:00:18 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2021-05-09 18:00:18 -0700 |
commit | 6ec8cc216282396ece535941ea6ca4a63d924e8f (patch) | |
tree | 10b1de27d66dc81a9fc8b6e8128228651961debd /core/src/Model/Workflow | |
parent | 8fbe7f89c808d8eb28d8615ddda219208f936956 (diff) |
Restore connection mechanism to simple one-to-one mode
Diffstat (limited to 'core/src/Model/Workflow')
-rw-r--r-- | core/src/Model/Workflow/Workflow.hpp | 28 | ||||
-rw-r--r-- | core/src/Model/Workflow/Workflow_Main.cpp | 324 |
2 files changed, 58 insertions, 294 deletions
diff --git a/core/src/Model/Workflow/Workflow.hpp b/core/src/Model/Workflow/Workflow.hpp index 77341fa..7bcd349 100644 --- a/core/src/Model/Workflow/Workflow.hpp +++ b/core/src/Model/Workflow/Workflow.hpp @@ -19,34 +19,17 @@ class WorkflowConnection public: static constexpr auto kInvalidId = std::numeric_limits<size_t>::max(); - enum Direction - { - ManyToOne, - OneToMany, - }; - - struct ConnectionPoint - { - size_t Node; - int Pin; - - bool operator==(const ConnectionPoint&) const = default; - }; - - std::vector<ConnectionPoint> MultiConnections; /// Used for `LinkId` when interfacing with imgui node editor. Runtime only (not saved to disk and generated when loading). size_t UniqueId; - ConnectionPoint SingleConnection; - Direction ConnectionDirection; + size_t SourceNode; + size_t DestinationNode; + int SourcePin; + int DestinationPin; public: WorkflowConnection(); bool IsValid() const; - std::span<ConnectionPoint> GetSourcePoints(); - std::span<const ConnectionPoint> GetSourcePoints() const; - std::span<ConnectionPoint> GetDestinationPoints(); - std::span<const ConnectionPoint> GetDestinationPoints() const; void DrawDebugInfo() const; void ReadFrom(std::istream& stream); @@ -89,13 +72,11 @@ protected: size_t Connection = WorkflowConnection::kInvalidId; BaseValue::Kind MatchingType = BaseValue::InvalidKind; bool ConnectionToConst = false; - bool AllowsMultipleConnections = false; /// A constant connection connects from a user-specified constant value, feeding to a valid \c Destination and \c DestinationPin (i.e. input pins). bool IsConstantConnection() const; bool IsConnected() const; BaseValue::Kind GetMatchingType() const; - WorkflowConnection::Direction GetSupportedDirection() const; }; struct OutputPin @@ -108,7 +89,6 @@ protected: bool IsConnected() const; BaseValue::Kind GetMatchingType() const; - WorkflowConnection::Direction GetSupportedDirection() const; }; friend class Workflow; diff --git a/core/src/Model/Workflow/Workflow_Main.cpp b/core/src/Model/Workflow/Workflow_Main.cpp index 39a08df..cfa7cea 100644 --- a/core/src/Model/Workflow/Workflow_Main.cpp +++ b/core/src/Model/Workflow/Workflow_Main.cpp @@ -12,128 +12,38 @@ namespace ImNodes = ax::NodeEditor; WorkflowConnection::WorkflowConnection() - : MultiConnections{} - , SingleConnection{ WorkflowNode::kInvalidId, -1 } - , ConnectionDirection{ OneToMany } + : SourceNode{ WorkflowNode::kInvalidId } + , DestinationNode{ WorkflowNode::kInvalidId } + , SourcePin{ -1 } + , DestinationPin{ -1 } { } 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; - } -} - -static void DrawConnectionPoints(const std::vector<WorkflowConnection::ConnectionPoint>& points, const char* pinHint) -{ - ImGui::Indent(32.0f); - for (auto& pt : points) { - ImGui::Text("{ Node = %llu, Pin (%s) = %d }", pt.Node, pinHint, pt.Pin); - if (ImGui::IsItemHovered()) { - // TODO highlight node - } - } - ImGui::Unindent(); -} - -static void DrawConnectionPoint(const WorkflowConnection::ConnectionPoint& point, const char* pinHint) -{ - ImGui::Indent(32.0f); - ImGui::Text("{ Node = %llu, Pin (%s) = %d }", point.Node, pinHint, point.Pin); - ImGui::Unindent(); + return SourceNode != WorkflowNode::kInvalidId; } void WorkflowConnection::DrawDebugInfo() const { ImGui::BeginTooltip(); - switch (ConnectionDirection) { - case ManyToOne: { - ImGui::Text("Type: many-to-one"); - ImGui::Text("Sources:"); - ::DrawConnectionPoints(MultiConnections, "output"); - ImGui::Text("Destination:"); - ::DrawConnectionPoint(SingleConnection, "input"); - } break; - - case OneToMany: { - ImGui::Text("Type: one-to-many"); - ImGui::Text("Source:"); - ::DrawConnectionPoint(SingleConnection, "output"); - ImGui::Text("Destinations:"); - ::DrawConnectionPoints(MultiConnections, "input"); - } break; - } + ImGui::Text("Source (node with output pin):"); + ImGui::Text("{ Node = %llu, Pin = %d }", SourceNode, SourcePin); + ImGui::Text("Destination (node with input pin):"); + ImGui::Text("{ Node = %llu, Pin = %d }", DestinationNode, DestinationPin); ImGui::EndTooltip(); } -static WorkflowConnection::ConnectionPoint ReadConnectionPoint(std::istream& stream) -{ - WorkflowConnection::ConnectionPoint pt; - stream >> pt.Node; - stream >> pt.Pin; - return pt; -} - void WorkflowConnection::ReadFrom(std::istream& stream) { - int n; - stream >> n; - ConnectionDirection = (Direction)n; - - SingleConnection = ::ReadConnectionPoint(stream); - - size_t size; - stream >> size; - for (size_t i = 0; i < size; ++i) { - MultiConnections.push_back(::ReadConnectionPoint(stream)); - } -} - -static void WriteConnectionPoint(std::ostream& stream, const WorkflowConnection::ConnectionPoint& pt) -{ - stream << pt.Node; - stream << pt.Pin; + stream >> SourceNode >> DestinationNode; + stream >> SourcePin >> DestinationPin; } void WorkflowConnection::WriteTo(std::ostream& stream) { - stream << (int)ConnectionDirection; - ::WriteConnectionPoint(stream, SingleConnection); - stream << (size_t)MultiConnections.size(); - for (auto& pt : MultiConnections) { - ::WriteConnectionPoint(stream, pt); - } + stream << SourceNode << DestinationNode; + stream << SourcePin << DestinationPin; } bool WorkflowNode::InputPin::IsConstantConnection() const @@ -151,11 +61,6 @@ 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; @@ -166,11 +71,6 @@ BaseValue::Kind WorkflowNode::OutputPin::GetMatchingType() const return MatchingType; } -WorkflowConnection::Direction WorkflowNode::OutputPin::GetSupportedDirection() const -{ - return AllowsMultipleConnections ? WorkflowConnection::OneToMany : WorkflowConnection::ManyToOne; -} - WorkflowNode::WorkflowNode(Kind kind) : mKind{ kind } , mDepth{ -1 } @@ -319,24 +219,14 @@ void WorkflowNode::SwapInputPin(int a, int b) 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; + auto& conn = *mWorkflow->GetConnectionById(pinA.Connection); + conn.DestinationPin = b; } 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; + auto& conn = *mWorkflow->GetConnectionById(pinB.Connection); + conn.DestinationPin = a; } - - if (pointA) pointA->Pin = b; - if (pointB) pointB->Pin = a; } std::swap(pinA, pinB); @@ -369,23 +259,14 @@ void WorkflowNode::SwapOutputPin(int a, int b) 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; + auto& conn = *mWorkflow->GetConnectionById(pinA.Connection); + conn.SourcePin = b; } 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; + auto& conn = *mWorkflow->GetConnectionById(pinB.Connection); + conn.SourcePin = a; } - - if (pointA) pointA->Pin = b; - if (pointB) pointB->Pin = a; } std::swap(pinA, pinB); @@ -470,16 +351,8 @@ 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; - } + mNodes[conn.SourceNode]->mInputs[conn.SourcePin].Connection = WorkflowNode::kInvalidId; + mNodes[conn.DestinationNode]->mInputs[conn.DestinationPin].Connection = WorkflowNode::kInvalidId; conn = {}; mDepthsDirty = true; @@ -490,102 +363,39 @@ bool Workflow::Connect(WorkflowNode& sourceNode, int sourcePin, WorkflowNode& de 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; + if (src.IsConnected()) { + DisconnectBySource(sourceNode, sourcePin); } - srcConnPt->Node = sourceNode.GetId(); - srcConnPt->Pin = sourcePin; - dstConnPt->Node = destinationNode.GetId(); - dstConnPt->Pin = destinationPin; + auto [conn, id] = AllocWorkflowConnection(); + conn.SourceNode = sourceNode.GetId(); + conn.SourcePin = sourcePin; + conn.DestinationNode = destinationNode.GetId(); + conn.DestinationPin = destinationPin; - src.Connection = connId; - dst.Connection = connId; + src.Connection = id; + dst.Connection = id; mDepthsDirty = true; return true; } -// TODO cleanup these two implementation - 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 = WorkflowNode::kInvalidId; - } break; + auto& sn = sourceNode.mOutputs[sourcePin]; + if (!sn.IsConnected()) return false; - case WorkflowConnection::OneToMany: { - // Dominate pin, removes whole connection + auto& conn = mConnections[sn.Connection]; + auto& dn = mNodes[conn.DestinationNode]->mInputs[conn.DestinationPin]; - for (auto& pt : conn.MultiConnections) { - auto& node = *mNodes[pt.Node]; - node.mInputs[pt.Pin].Connection = WorkflowNode::kInvalidId; - } - sp.Connection = WorkflowNode::kInvalidId; - - conn = {}; - } break; - } + sn.Connection = WorkflowConnection::kInvalidId; + dn.Connection = WorkflowConnection::kInvalidId; + conn = {}; mDepthsDirty = true; return true; @@ -593,39 +403,15 @@ bool Workflow::DisconnectBySource(WorkflowNode& sourceNode, int sourcePin) 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 = WorkflowNode::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 = WorkflowNode::kInvalidId; - } - dp.Connection = WorkflowNode::kInvalidId; + auto& dn = destinationNode.mOutputs[destinationPin]; + if (!dn.IsConnected()) return false; - conn = {}; - } break; + auto& conn = mConnections[dn.Connection]; + auto& sn = mNodes[conn.SourceNode]->mInputs[conn.SourcePin]; - 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 = WorkflowNode::kInvalidId; - } break; - } + sn.Connection = WorkflowConnection::kInvalidId; + dn.Connection = WorkflowConnection::kInvalidId; + conn = {}; mDepthsDirty = true; return true; @@ -693,18 +479,16 @@ Workflow::GraphUpdateResult::T Workflow::UpdateGraph() if (!pin.IsConnected()) continue; auto& conn = mConnections[pin.Connection]; - for (auto& point : conn.GetDestinationPoints()) { - auto& wn = workingNodes[point.Node]; - auto& n = *mNodes[point.Node].get(); + auto& wn = workingNodes[conn.DestinationNode]; + auto& n = *mNodes[conn.DestinationPin].get(); - wn.FulfilledInputCount++; - wn.MaximumDepth = std::max(node.mDepth, wn.MaximumDepth); + 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 - if (n.mInputs.size() >= wn.FulfilledInputCount) { - n.mDepth = wn.MaximumDepth + 1; - } + // 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 + if (n.mInputs.size() >= wn.FulfilledInputCount) { + n.mDepth = wn.MaximumDepth + 1; } } }; |