diff options
author | rtk0c <[email protected]> | 2021-04-17 17:31:47 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2021-04-17 17:31:47 -0700 |
commit | dca1286661f61e51943863de8ce68849a9578363 (patch) | |
tree | ada6c32fdf9ebf11a2b302398e8de28f57e3de8a /core | |
parent | 4e5730e1fcef150ce2f13f52a278890589ca96ad (diff) |
Initial work on one-to-many/many-to-one connections
Diffstat (limited to 'core')
-rw-r--r-- | core/src/Model/Workflow.cpp | 238 | ||||
-rw-r--r-- | core/src/Model/Workflow.hpp | 36 | ||||
-rw-r--r-- | core/src/Model/WorkflowNodes.cpp | 5 | ||||
-rw-r--r-- | core/src/UI/UI_MainWindow.cpp | 2 |
4 files changed, 232 insertions, 49 deletions
diff --git a/core/src/Model/Workflow.cpp b/core/src/Model/Workflow.cpp index 385544e..588081d 100644 --- a/core/src/Model/Workflow.cpp +++ b/core/src/Model/Workflow.cpp @@ -1,31 +1,75 @@ #include "Workflow.hpp" +#include <algorithm> #include <cassert> #include <utility> WorkflowConnection::WorkflowConnection() - : Source{ WorkflowNode::kInvalidId } - , Destination{ WorkflowNode::kInvalidId } - , SourcePin{ -1 } - , DestinationPin{ -1 } { + : MultiConnections{} + , SingleConnection{ WorkflowNode::kInvalidId, -1 } + , ConnectionDirection{ OneToMany } { } bool WorkflowConnection::IsValid() const { - return Source != WorkflowNode::kInvalidId; + 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; + 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 } { @@ -91,14 +135,24 @@ 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& conn = *mWorkflow->GetConnectionById(pinA.Connection); - conn.DestinationPin = b; + 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& conn = *mWorkflow->GetConnectionById(pinB.Connection); - conn.DestinationPin = a; + 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); @@ -128,14 +182,23 @@ 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& conn = *mWorkflow->GetConnectionById(pinA.Connection); - conn.SourcePin = b; + 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& conn = *mWorkflow->GetConnectionById(pinB.Connection); - conn.SourcePin = a; + 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); @@ -170,43 +233,138 @@ void Workflow::RemoveStep(size_t id) { step->mId = WorkflowNode::kInvalidId; } -void Workflow::Connect(WorkflowNode& source, int sourceNode, WorkflowNode& destination, int destinationNode) { - if (source.mInputs[sourceNode].IsConnected()) { - DisconnectBySource(source, sourceNode); +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 check type + + 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; } - auto [conn, id] = AllocWorkflowConnection(); - conn.Source = source.GetId(); - conn.SourcePin = sourceNode; - conn.Destination = destination.GetId(); - conn.DestinationPin = destinationNode; + srcConnPt->Node = sourceNode.GetId(); + srcConnPt->Pin = sourcePin; + dstConnPt->Node = destinationNode.GetId(); + dstConnPt->Pin = destinationPin; - source.mInputs[sourceNode].Connection = id; - destination.mInputs[destinationNode].Connection = id; + src.Connection = connId; + dst.Connection = connId; + return true; } -void Workflow::DisconnectBySource(WorkflowNode& source, int sourceNode) { - auto& sn = source.mOutputs[sourceNode]; - if (!sn.IsConnected()) return; +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()); - auto& conn = mConnections[sn.Connection]; - auto& dn = mNodes[conn.Destination]->mInputs[conn.DestinationPin]; + sp.Connection = WorkflowConnection::kInvalidId; + } break; - sn.Connection = WorkflowConnection::kInvalidId; - dn.Connection = WorkflowConnection::kInvalidId; - conn = {}; + 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; } -void Workflow::DisconnectByDestination(WorkflowNode& destination, int destinationNode) { - auto& dn = destination.mOutputs[destinationNode]; - if (!dn.IsConnected()) return; +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]; - auto& conn = mConnections[dn.Connection]; - auto& sn = mNodes[conn.Source]->mInputs[conn.SourcePin]; + 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; + } - sn.Connection = WorkflowConnection::kInvalidId; - dn.Connection = WorkflowConnection::kInvalidId; - conn = {}; + return true; } std::pair<WorkflowConnection&, size_t> Workflow::AllocWorkflowConnection() { diff --git a/core/src/Model/Workflow.hpp b/core/src/Model/Workflow.hpp index 2c83816..62dd9ca 100644 --- a/core/src/Model/Workflow.hpp +++ b/core/src/Model/Workflow.hpp @@ -7,21 +7,37 @@ #include <cstdint> #include <limits> #include <memory> +#include <span> #include <vector> class WorkflowConnection { public: static constexpr auto kInvalidId = std::numeric_limits<size_t>::max(); - size_t Source; - size_t Destination; - int SourcePin; - int DestinationPin; + enum Direction { + ManyToOne, + OneToMany, + }; + + struct ConnectionPoint { + size_t Node; + int Pin; + + bool operator==(const ConnectionPoint&) const = default; + }; + + std::vector<ConnectionPoint> MultiConnections; + ConnectionPoint SingleConnection; + Direction ConnectionDirection; 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; }; class WorkflowNode { @@ -54,17 +70,23 @@ protected: size_t Connection = WorkflowConnection::kInvalidId; BaseValue::Kind MatchingType = BaseValue::KindInvalid; 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 { size_t Connection = WorkflowConnection::kInvalidId; BaseValue::Kind MatchingType = BaseValue::KindInvalid; + bool AllowsMultipleConnections = false; bool IsConnected() const; + BaseValue::Kind GetMatchingType() const; + WorkflowConnection::Direction GetSupportedDirection() const; }; friend class Workflow; @@ -131,9 +153,9 @@ public: void AddStep(std::unique_ptr<WorkflowNode> step); void RemoveStep(size_t id); - void Connect(WorkflowNode& source, int sourceNode, WorkflowNode& destination, int destinationNode); - void DisconnectBySource(WorkflowNode& source, int sourceNode); - void DisconnectByDestination(WorkflowNode& destination, int destinationNode); + bool Connect(WorkflowNode& sourceNode, int sourcePin, WorkflowNode& destinationNode, int destinationPin); + bool DisconnectBySource(WorkflowNode& sourceNode, int sourcePin); + bool DisconnectByDestination(WorkflowNode& destinationNode, int destinationPin); private: std::pair<WorkflowConnection&, size_t> AllocWorkflowConnection(); diff --git a/core/src/Model/WorkflowNodes.cpp b/core/src/Model/WorkflowNodes.cpp index 8c315b6..14f72cb 100644 --- a/core/src/Model/WorkflowNodes.cpp +++ b/core/src/Model/WorkflowNodes.cpp @@ -5,6 +5,7 @@ #include "Utils/Variant.hpp" #include <cassert> +#include <utility> #include <variant> WorkflowNode::Kind NumericOperationNode::OperationTypeToNodeKind(OperationType type) { @@ -68,7 +69,9 @@ void NumericOperationNode::Evaluate(WorkflowEvaluationContext& ctx) { default: return; } - ctx.SetConnectionValue(mOutputs[0], res); + auto value = std::make_unique<NumericValue>(); + value->SetValue(res); + ctx.SetConnectionValue(mOutputs[0], std::move(value)); } bool NumericExpressionNode::IsInstance(const WorkflowNode* node) { diff --git a/core/src/UI/UI_MainWindow.cpp b/core/src/UI/UI_MainWindow.cpp index f1d1e5e..001c2b8 100644 --- a/core/src/UI/UI_MainWindow.cpp +++ b/core/src/UI/UI_MainWindow.cpp @@ -223,7 +223,7 @@ void UI::MainWindow() { ImGui::EndTabItem(); } - if (ImGui::BeginTabItem()) { + if (ImGui::BeginTabItem(ls->WorkflowsTab.Get())) { UI::WorkflowsTab(); ImGui::EndTabItem(); } |