aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/Model/Workflow.cpp150
-rw-r--r--core/src/Model/Workflow.hpp5
-rw-r--r--core/src/Model/WorkflowNodes.cpp30
-rw-r--r--core/src/Model/WorkflowNodes.hpp4
-rw-r--r--core/src/Model/fwd.hpp2
-rw-r--r--core/src/UI/UI_Workflows.cpp70
6 files changed, 225 insertions, 36 deletions
diff --git a/core/src/Model/Workflow.cpp b/core/src/Model/Workflow.cpp
index 588081d..7ca1b9e 100644
--- a/core/src/Model/Workflow.cpp
+++ b/core/src/Model/Workflow.cpp
@@ -2,6 +2,7 @@
#include <algorithm>
#include <cassert>
+#include <queue>
#include <utility>
WorkflowConnection::WorkflowConnection()
@@ -243,7 +244,10 @@ bool Workflow::Connect(WorkflowNode& sourceNode, int sourcePin, WorkflowNode& de
// Would be same as `dst.GetSupportedDirection()` because we validated that they are the same above
auto direction = src.GetSupportedDirection();
- // TODO check type
+ // TODO report error to user?
+ if (src.GetMatchingType() != dst.GetMatchingType()) {
+ return false;
+ }
using Pt = WorkflowConnection::ConnectionPoint;
Pt* srcConnPt;
@@ -391,33 +395,37 @@ std::pair<std::unique_ptr<WorkflowNode>&, size_t> Workflow::AllocWorkflowStep()
return { mNodes.emplace_back(std::unique_ptr<WorkflowNode>()), id };
}
+namespace {
+enum class EvaluationStatus {
+ Unevaluated,
+ Success,
+ Failed,
+};
+} // namespace
+
struct WorkflowEvaluationContext::RuntimeNode {
- WorkflowNode* Node;
+ EvaluationStatus Status = EvaluationStatus::Unevaluated;
};
struct WorkflowEvaluationContext::RuntimeConnection {
- WorkflowConnection* Connection;
std::unique_ptr<BaseValue> Value;
+
+ bool IsAvailableValue() const {
+ return Value != nullptr;
+ }
};
WorkflowEvaluationContext::WorkflowEvaluationContext(Workflow& workflow)
: mWorkflow{ &workflow } {
- mNodes.resize(workflow.mNodes.size());
- for (size_t i = 0; i < workflow.mNodes.size(); ++i) {
- mNodes[i].Node = workflow.mNodes[i].get();
- }
-
- mConnections.resize(workflow.mConnections.size());
- for (size_t i = 0; i < workflow.mConnections.size(); ++i) {
- mConnections[i].Connection = &workflow.mConnections[i];
- }
+ 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 mConnections[id].Value.get();
+ return mRuntimeConnections[id].Value.get();
}
}
@@ -430,7 +438,7 @@ BaseValue* WorkflowEvaluationContext::GetConnectionValue(const WorkflowNode::Inp
}
void WorkflowEvaluationContext::SetConnectionValue(size_t id, std::unique_ptr<BaseValue> value) {
- mConnections[id].Value = std::move(value);
+ mRuntimeConnections[id].Value = std::move(value);
}
void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin& outputPin, std::unique_ptr<BaseValue> value) {
@@ -440,17 +448,129 @@ void WorkflowEvaluationContext::SetConnectionValue(const WorkflowNode::OutputPin
}
void WorkflowEvaluationContext::Run() {
- // TODO
+ 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,
+ });
}
diff --git a/core/src/Model/Workflow.hpp b/core/src/Model/Workflow.hpp
index 62dd9ca..f1459ba 100644
--- a/core/src/Model/Workflow.hpp
+++ b/core/src/Model/Workflow.hpp
@@ -90,7 +90,6 @@ protected:
};
friend class Workflow;
- friend class WorkflowEvaluationError;
friend class WorkflowEvaluationContext;
Workflow* mWorkflow;
@@ -189,8 +188,8 @@ private:
struct RuntimeConnection;
Workflow* mWorkflow;
- std::vector<RuntimeNode> mNodes;
- std::vector<RuntimeConnection> mConnections;
+ std::vector<RuntimeNode> mRuntimeNodes;
+ std::vector<RuntimeConnection> mRuntimeConnections;
std::vector<WorkflowEvaluationError> mErrors;
std::vector<WorkflowEvaluationError> mWarnings;
diff --git a/core/src/Model/WorkflowNodes.cpp b/core/src/Model/WorkflowNodes.cpp
index 14f72cb..f58c8bb 100644
--- a/core/src/Model/WorkflowNodes.cpp
+++ b/core/src/Model/WorkflowNodes.cpp
@@ -82,7 +82,7 @@ NumericExpressionNode::NumericExpressionNode()
: WorkflowNode(TransformType, KD_NumericExpression) {
}
-struct FormatTextNode::Argument {
+struct TextFormatterNode::Argument {
ArgumentType ArgumentType;
int PinIdx;
@@ -108,7 +108,7 @@ struct FormatTextNode::Argument {
}
};
-BaseValue::Kind FormatTextNode::ArgumentTypeToValueKind(FormatTextNode::ArgumentType arg) {
+BaseValue::Kind TextFormatterNode::ArgumentTypeToValueKind(TextFormatterNode::ArgumentType arg) {
switch (arg) {
case NumericArgument: return BaseValue::KD_Numeric;
case TextArgument: return BaseValue::KD_Text;
@@ -117,23 +117,23 @@ BaseValue::Kind FormatTextNode::ArgumentTypeToValueKind(FormatTextNode::Argument
}
}
-bool FormatTextNode::IsInstance(const WorkflowNode* node) {
+bool TextFormatterNode::IsInstance(const WorkflowNode* node) {
return node->GetKind() == KD_TextFormatting;
}
-FormatTextNode::FormatTextNode()
+TextFormatterNode::TextFormatterNode()
: WorkflowNode(TransformType, KD_TextFormatting) {
}
-int FormatTextNode::GetElementCount() const {
+int TextFormatterNode::GetElementCount() const {
return mElements.size();
}
-const FormatTextNode::Element& FormatTextNode::GetElement(int idx) const {
+const TextFormatterNode::Element& TextFormatterNode::GetElement(int idx) const {
return mElements[idx];
}
-void FormatTextNode::SetElement(int idx, std::string text) {
+void TextFormatterNode::SetElement(int idx, std::string text) {
assert(idx >= 0 && idx < mElements.size());
std::visit(
@@ -147,7 +147,7 @@ void FormatTextNode::SetElement(int idx, std::string text) {
mElements[idx] = std::move(text);
}
-void FormatTextNode::SetElement(int idx, ArgumentType argument) {
+void TextFormatterNode::SetElement(int idx, ArgumentType argument) {
assert(idx >= 0 && idx < mElements.size());
std::visit(
@@ -179,7 +179,7 @@ void FormatTextNode::SetElement(int idx, ArgumentType argument) {
mElements[idx]);
}
-void FormatTextNode::InsertElement(int idx, std::string text) {
+void TextFormatterNode::InsertElement(int idx, std::string text) {
assert(idx >= 0);
if (idx >= mElements.size()) AppendElement(std::move(text));
@@ -187,7 +187,7 @@ void FormatTextNode::InsertElement(int idx, std::string text) {
mElements.insert(mElements.begin() + idx, std::move(text));
}
-void FormatTextNode::InsertElement(int idx, ArgumentType argument) {
+void TextFormatterNode::InsertElement(int idx, ArgumentType argument) {
assert(idx >= 0);
if (idx >= mElements.size()) AppendElement(argument);
@@ -206,12 +206,12 @@ void FormatTextNode::InsertElement(int idx, ArgumentType argument) {
});
}
-void FormatTextNode::AppendElement(std::string text) {
+void TextFormatterNode::AppendElement(std::string text) {
mMinOutputChars += text.size();
mElements.push_back(std::move(text));
}
-void FormatTextNode::AppendElement(ArgumentType argument) {
+void TextFormatterNode::AppendElement(ArgumentType argument) {
int pinIdx = mInputs.size();
// Create pin
mInputs.push_back(InputPin{});
@@ -223,7 +223,7 @@ void FormatTextNode::AppendElement(ArgumentType argument) {
});
}
-void FormatTextNode::RemoveElement(int idx) {
+void TextFormatterNode::RemoveElement(int idx) {
assert(idx >= 0 && idx < mElements.size());
PreRemoveElement(idx);
@@ -233,7 +233,7 @@ void FormatTextNode::RemoveElement(int idx) {
mElements.erase(mElements.begin() + idx);
}
-void FormatTextNode::Evaluate(WorkflowEvaluationContext& ctx) {
+void TextFormatterNode::Evaluate(WorkflowEvaluationContext& ctx) {
std::string result;
result.reserve((size_t)(mMinOutputChars * 1.5f));
@@ -274,7 +274,7 @@ void FormatTextNode::Evaluate(WorkflowEvaluationContext& ctx) {
}
}
-void FormatTextNode::PreRemoveElement(int idx) {
+void TextFormatterNode::PreRemoveElement(int idx) {
auto& elm = mElements[idx];
if (auto arg = std::get_if<Argument>(&elm)) {
RemoveInputPin(arg->PinIdx);
diff --git a/core/src/Model/WorkflowNodes.hpp b/core/src/Model/WorkflowNodes.hpp
index e7d4bfc..677158c 100644
--- a/core/src/Model/WorkflowNodes.hpp
+++ b/core/src/Model/WorkflowNodes.hpp
@@ -39,7 +39,7 @@ public:
// TODO
};
-class FormatTextNode : public WorkflowNode {
+class TextFormatterNode : public WorkflowNode {
public:
enum ArgumentType {
NumericArgument,
@@ -57,7 +57,7 @@ private:
public:
static BaseValue::Kind ArgumentTypeToValueKind(ArgumentType arg);
static bool IsInstance(const WorkflowNode* node);
- FormatTextNode();
+ TextFormatterNode();
int GetElementCount() const;
const Element& GetElement(int idx) const;
diff --git a/core/src/Model/fwd.hpp b/core/src/Model/fwd.hpp
index a7df889..5a75394 100644
--- a/core/src/Model/fwd.hpp
+++ b/core/src/Model/fwd.hpp
@@ -40,7 +40,7 @@ class WorkflowEvaluationContext;
// WorkflowSteps.hpp
class NumericOperationNode;
class NumericExpressionNode;
-class FormatTextNode;
+class TextFormatterNode;
class DocumentTemplateExpansionNode;
class FormInputNode;
class DatabaseRowsInputNode;
diff --git a/core/src/UI/UI_Workflows.cpp b/core/src/UI/UI_Workflows.cpp
index 2b0368e..101fa7b 100644
--- a/core/src/UI/UI_Workflows.cpp
+++ b/core/src/UI/UI_Workflows.cpp
@@ -1,8 +1,78 @@
#include "UI.hpp"
+#include "Model/Workflow.hpp"
+#include "Model/WorkflowNodes.hpp"
#include "UI/Localization.hpp"
#include <imgui.h>
+#include <memory>
+#include <vector>
+
+namespace {
+class WorkflowCreationMenu {
+private:
+ using WorkflowNodeConstructor = std::unique_ptr<WorkflowNode> (*)();
+
+ struct Candidate {
+ WorkflowNodeConstructor Constructor;
+ };
+
+ std::vector<Candidate> mCandidates;
+
+public:
+ WorkflowCreationMenu() {
+ SetupCandidates();
+ }
+
+private:
+ void SetupCandidates() {
+ mCandidates.push_back(Candidate{
+ .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Addition); },
+ });
+
+ mCandidates.push_back(Candidate{
+ .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Subtraction); },
+ });
+
+ mCandidates.push_back(Candidate{
+ .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Multiplication); },
+ });
+
+ mCandidates.push_back(Candidate{
+ .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericOperationNode>(NumericOperationNode::Division); },
+ });
+
+// mCandidates.push_back(Candidate{
+// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<NumericExpressionNode>(); },
+// });
+
+ mCandidates.push_back(Candidate{
+ .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<TextFormatterNode>(); },
+ });
+
+// mCandidates.push_back(Candidate{
+// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DocumentTemplateExpansionNode>(); },
+// });
+
+// mCandidates.push_back(Candidate{
+// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<FormInputNode>(); },
+// });
+
+// mCandidates.push_back(Candidate{
+// .Constructor = []() -> std::unique_ptr<WorkflowNode> { return std::make_unique<DatabaseRowsInputNode>(); },
+// });
+ }
+};
+
+class WorkflowUI {
+private:
+ Workflow* mWorkflow;
+
+public:
+ void Draw() {
+ }
+};
+} // namespace
void UI::WorkflowsTab() {
// TODO