summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/Model/Workflow.cpp238
-rw-r--r--core/src/Model/Workflow.hpp36
-rw-r--r--core/src/Model/WorkflowNodes.cpp5
-rw-r--r--core/src/UI/UI_MainWindow.cpp2
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();
}