various changes to nodes

This commit is contained in:
sshumakov3
2020-12-04 21:59:30 +03:00
parent ce5c458cc2
commit eaa5cda65e
13 changed files with 739 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
#include "ConditionNode.hpp"
#include "BaseNode.inl"
#include "Data/GenericData.hpp"
#include <cmath>
#include <limits>
using namespace noggit::Red::PresetEditor::Nodes;
ConditionNode::ConditionNode()
: BaseNode()
{
setName("ConditionNode");
setCaption("Equal");
_validation_state = NodeValidationState::Valid;
_operation = new QComboBox(&_embedded_widget);
_operation->addItems({"Equal",
"Not equal",
"Less",
"Greater",
"And",
"Or",
"Xor"});
addWidget(_operation);
QComboBox::connect(_operation, qOverload<int>(&QComboBox::currentIndexChanged)
,[this](int index)
{
setCaption(_operation->currentText());
}
);
addPort<DecimalData>(PortType::In, "Value", true);
_first = new QDoubleSpinBox(&_embedded_widget);
addWidget(_first, 0);
addPort<DecimalData>(PortType::In, "Value", true);
_second = new QDoubleSpinBox(&_embedded_widget);
addWidget(_second, 1);
addPort<BooleanData>(PortType::Out, "Boolean", true);
}
void ConditionNode::compute()
{
auto first_shared = _in_ports[0].in_value.lock();
auto second_shared = _in_ports[1].in_value.lock();
auto first = static_cast<DecimalData*>(first_shared.get());
auto second = static_cast<DecimalData*>(second_shared.get());
// handle defaults
double first_number = first ? first->value() : _first->value();
double second_number = second ? second->value() : _second->value();
setValidationState(NodeValidationState::Warning);
bool result = false;
switch (_operation->currentIndex())
{
case 0:
result = std::fabs(std::fabs(first_number) - std::fabs(second_number)) < std::numeric_limits<double>::epsilon();
break;
case 1:
result = first_number != second_number;
break;
case 2:
result = first_number < second_number;
break;
case 3:
result = first_number > second_number;
break;
case 4:
result = static_cast<bool>(first_number) && static_cast<bool>(second_number);
break;
case 5:
result = static_cast<bool>(first_number) || static_cast<bool>(second_number);
break;
case 6:
result = static_cast<bool>(first_number) ^ static_cast<bool>(second_number);
break;
}
_out_ports[0].out_value = std::make_shared<BooleanData>(result);
setValidationMessage(("Debug: " + std::to_string(result)).c_str());
Q_EMIT dataUpdated(0);
}
QJsonObject ConditionNode::save() const
{
QJsonObject json_obj;
json_obj["name"] = name();
json_obj["caption"] = caption();
json_obj["default_first"] = _first->value();
json_obj["default_second"] = _second->value();
json_obj["operation"] = _operation->currentIndex();
return json_obj;
}
void ConditionNode::restore(const QJsonObject& json_obj)
{
setName(json_obj["name"].toString());
setCaption(json_obj["caption"].toString());
_first->setValue(json_obj["default_first"].toDouble());
_second->setValue(json_obj["default_second"].toDouble());
_operation->setCurrentIndex(json_obj["operation"].toInt());
}

View File

@@ -0,0 +1,42 @@
#ifndef NOGGIT_CONDITIONNODE_HPP
#define NOGGIT_CONDITIONNODE_HPP
#include "BaseNode.hpp"
#include <QComboBox>
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
namespace noggit
{
namespace Red::PresetEditor::Nodes
{
class ConditionNode : public BaseNode
{
Q_OBJECT
public:
ConditionNode();
void compute() override;
QJsonObject save() const override;
void restore(QJsonObject const& json_obj) override;
private:
QDoubleSpinBox* _first;
QDoubleSpinBox* _second;
QComboBox* _operation;
};
}
}
#endif //NOGGIT_CONDITIONNODE_HPP

View File

@@ -0,0 +1,70 @@
#ifndef NOGGIT_GENERICTYPECONVERTER_HPP
#define NOGGIT_GENERICTYPECONVERTER_HPP
#include <external/NodeEditor/include/nodes/NodeDataModel>
#include "GenericData.hpp"
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
template <typename T_from, typename T_to, typename C>
class GenericTypeConverter
{
public:
std::shared_ptr<NodeData> operator()(std::shared_ptr<NodeData> data)
{
auto data_ptr = static_cast<T_from*>(data.get());
if (data_ptr)
{
_data = std::make_shared<T_to>(C::convert(data_ptr->value()));
}
else
{
_data.reset();
}
return _data;
};
private:
std::shared_ptr<NodeData> _data;
};
template<typename T_from, typename T_to>
struct UnderlyingTypeConvertGeneric
{
static T_to convert(T_from const& value)
{
return static_cast<T_to>(value);
}
};
#define DECLARE_TYPE_CONVERTER(DATA_FROM, DATA_TO, UTYPE_FROM, UTYPE_TO) \
using DATA_FROM##To##DATA_TO##TypeConverter = \
GenericTypeConverter<DATA_FROM##Data, DATA_TO##Data, \
UnderlyingTypeConvertGeneric<UTYPE_FROM, UTYPE_TO>>;
DECLARE_TYPE_CONVERTER(Decimal, Integer, double, int)
DECLARE_TYPE_CONVERTER(Decimal, Boolean, double, bool)
DECLARE_TYPE_CONVERTER(Integer, Decimal, int, double)
DECLARE_TYPE_CONVERTER(Integer, Boolean, int, bool)
DECLARE_TYPE_CONVERTER(Boolean, Integer, bool, int)
DECLARE_TYPE_CONVERTER(Boolean, Decimal, bool, double)
//DECLARE_TYPE_CONVERTER(Decimal, String)
//DECLARE_TYPE_CONVERTER(Integer, String)
#endif //NOGGIT_GENERICTYPECONVERTER_HPP

View File

@@ -0,0 +1,25 @@
#include "LogicBeginNode.hpp"
#include "BaseNode.inl"
#include "Data/GenericData.hpp"
using namespace noggit::Red::PresetEditor::Nodes;
LogicBeginNode::LogicBeginNode()
: BaseNode()
{
setName("LogicBeginNode");
setCaption("Begin");
_validation_state = NodeValidationState::Valid;
addPort<LogicData>(PortType::Out, "Logic", true, ConnectionPolicy::One);
_is_logic_node = true;
}
void LogicBeginNode::compute()
{
_out_ports[0].out_value = std::make_shared<LogicData>(true);
Q_EMIT dataUpdated(0);
setComputed(true);
}

View File

@@ -0,0 +1,32 @@
#ifndef NOGGIT_LOGICBEGINNODE_HPP
#define NOGGIT_LOGICBEGINNODE_HPP
#include "BaseNode.hpp"
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
namespace noggit
{
namespace Red::PresetEditor::Nodes
{
class LogicBeginNode : public BaseNode
{
Q_OBJECT
public:
LogicBeginNode();
void compute() override;
};
}
}
#endif //NOGGIT_LOGICBEGINNODE_HPP

View File

@@ -0,0 +1,83 @@
#include "LogicIfNode.hpp"
#include "BaseNode.inl"
#include "Data/GenericData.hpp"
using namespace noggit::Red::PresetEditor::Nodes;
LogicIfNode::LogicIfNode()
: BaseNode()
{
setName("LogicIfNode");
setCaption("If / Else");
_validation_state = NodeValidationState::Valid;
addPort<LogicData>(PortType::In, "Logic", true);
addPort<BooleanData>(PortType::In, "Boolean", true);
addPort<LogicData>(PortType::Out, "Then", true, ConnectionPolicy::One);
addPort<LogicData>(PortType::Out, "Else", true, ConnectionPolicy::One);
setIsLogicNode(true);
setNLogicBranches(2);
}
void LogicIfNode::compute()
{
auto logic = static_cast<BooleanData*>(_in_ports[0].in_value.lock().get());
if (!logic)
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: Failed to evaluate logic input");
_out_ports[0].out_value = std::make_shared<LogicData>(false);
_out_ports[1].out_value = std::make_shared<LogicData>(false);
Q_EMIT dataUpdated(0);
Q_EMIT dataUpdated(1);
setLogicBranchToExecute(-1);
return;
}
setValidationState(NodeValidationState::Valid);
if(!logic->value())
{
setLogicBranchToExecute(-1);
return;
}
auto in_bool = _in_ports[1].in_value.lock();
auto in_bool_ptr = static_cast<BooleanData*>(in_bool.get());
if (!in_bool_ptr)
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Missing boolean input.");
setLogicBranchToExecute(-1);
return;
}
if (in_bool_ptr->value())
{
_out_ports[0].out_value = std::make_shared<LogicData>(true);
_out_ports[1].out_value = std::make_shared<LogicData>(false);
setLogicBranchToExecute(0);
}
else
{
_out_ports[0].out_value = std::make_shared<LogicData>(false);
_out_ports[1].out_value = std::make_shared<LogicData>(true);
setLogicBranchToExecute(1);
}
Q_EMIT dataUpdated(0);
Q_EMIT dataUpdated(1);
setValidationState(NodeValidationState::Warning);
setValidationMessage(in_bool_ptr->value() ? "Debug: true" : "Debug: false");
}

View File

@@ -0,0 +1,31 @@
#ifndef NOGGIT_LOGICIFNODE_HPP
#define NOGGIT_LOGICIFNODE_HPP
#include "BaseNode.hpp"
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
namespace noggit
{
namespace Red::PresetEditor::Nodes
{
class LogicIfNode : public BaseNode
{
Q_OBJECT
public:
LogicIfNode();
void compute() override;
};
}
}
#endif //NOGGIT_LOGICIFNODE_HPP

View File

@@ -0,0 +1,76 @@
#include "PrintNode.hpp"
#include "BaseNode.inl"
#include "Data/GenericData.hpp"
#include <noggit/Log.h>
using namespace noggit::Red::PresetEditor::Nodes;
PrintNode::PrintNode()
: BaseNode()
{
setName("PrintNode");
setCaption("Print()");
_validation_state = NodeValidationState::Valid;
addPort<LogicData>(PortType::In, "Logic", true);
addWidget(new QLabel(&_embedded_widget), 0);
addPort<DecimalData>(PortType::In, "String", true);
_text = new QLineEdit(&_embedded_widget);
addWidget(_text, 1);
addPort<LogicData>(PortType::Out, "Logic", true, ConnectionPolicy::One);
}
void PrintNode::compute()
{
auto logic = _in_ports[0].in_value.lock();
auto logic_ptr = static_cast<LogicData*>(logic.get());
if (!logic_ptr)
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: Failed to evaluate logic input");
_out_ports[0].out_value = std::make_shared<LogicData>(false);
Q_EMIT dataUpdated(0);
return;
}
setValidationState(NodeValidationState::Valid);
if(!logic_ptr->value())
return;
auto text = _in_ports[1].in_value.lock();
auto text_ptr = static_cast<StringData*>(text.get());
auto msg = text_ptr ? text_ptr->value() : _text->text().toStdString();
LogDebug << msg << std::endl;
_out_ports[0].out_value = std::make_shared<LogicData>(true);
Q_EMIT dataUpdated(0);
}
QJsonObject PrintNode::save() const
{
QJsonObject json_obj;
json_obj["name"] = name();
json_obj["caption"] = caption();
json_obj["text"] = _text->text();
return json_obj;
}
void PrintNode::restore(const QJsonObject& json_obj)
{
setName(json_obj["name"].toString());
setCaption(json_obj["caption"].toString());
_text->setText(json_obj["text"].toString());
}

View File

@@ -0,0 +1,38 @@
#ifndef NOGGIT_PRINTNODE_HPP
#define NOGGIT_PRINTNODE_HPP
#include "BaseNode.hpp"
#include <QLineEdit>
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
namespace noggit
{
namespace Red::PresetEditor::Nodes
{
class PrintNode : public BaseNode
{
Q_OBJECT
public:
PrintNode();
void compute() override;
QJsonObject save() const override;
void restore(QJsonObject const& json_obj) override;
private:
QLineEdit* _text;
};
}
}
#endif //NOGGIT_PRINTNODE_HPP

View File

@@ -0,0 +1,126 @@
#include "LogicBranch.hpp"
#include "../BaseNode.hpp"
#include <stdexcept>
using namespace noggit::Red::PresetEditor::Nodes;
LogicBranch::LogicBranch(Node* logic_node)
: _logic_node(logic_node)
{
processNode(logic_node);
}
void LogicBranch::processNode(Node* node)
{
auto model = static_cast<BaseNode*>(node->nodeDataModel());
auto nodeState = node->nodeState();
for (int i = 0; i < model->nPorts(PortType::Out); ++i)
{
auto const& connections = nodeState.connections(PortType::Out, i);
for (auto const& pair : connections)
{
auto connected_node = pair.second->getNode(PortType::In);
if (!connected_node)
continue;
auto connected_model = static_cast<BaseNode*>(connected_node->nodeDataModel());
if (connected_model->isLogicNode())
{
for (unsigned int j = 0; j < connected_model->nLogicBranches(); ++j)
{
_sub_branches[connected_node].emplace_back(connected_node);
}
}
else
{
_nodes.push_back(connected_node);
processNode(connected_node);
}
}
}
}
void LogicBranch::execute()
{
executeNode(_logic_node);
}
void LogicBranch::executeNode(Node *node)
{
auto model = static_cast<BaseNode*>(node->nodeDataModel());
auto nodeState = node->nodeState();
if (model->isComputed())
return;
executeNodeLeaves(node);
model->compute();
model->setComputed(true);
for (int i = 0; i < model->nPorts(PortType::Out); ++i)
{
auto const& connections = nodeState.connections(PortType::Out, i);
for (auto const& pair : connections)
{
auto connected_node = pair.second->getNode(PortType::In);
if (!connected_node)
continue;
auto connected_model = static_cast<BaseNode*>(connected_node->nodeDataModel());
if (connected_model->isLogicNode())
{
executeNodeLeaves(connected_node);
executeNode(connected_node);
}
else
{
executeNodeLeaves(connected_node);
executeNode(connected_node);
}
}
}
}
void LogicBranch::executeNodeLeaves(Node *node)
{
auto model = static_cast<BaseNode *>(node->nodeDataModel());
auto nodeState = node->nodeState();
for (int i = 0; i < model->nPorts(PortType::In); ++i)
{
auto const& connections = nodeState.connections(PortType::In, i);
for (auto const& pair : connections)
{
auto connected_node = pair.second->getNode(PortType::Out);
if (!connected_node)
continue;
auto connected_model = static_cast<BaseNode*>(connected_node->nodeDataModel());
if (connected_model->isComputed() || connected_node == node)
continue;
if (connected_model->isLogicNode())
{
connected_model->setValidationMessage("Error: A leaf should not represent a logic node!");
connected_model->setValidationState(NodeValidationState::Error);
connected_model->setComputed(true);
continue;
}
else if (!connected_model->isComputed())
{
executeNodeLeaves(connected_node);
connected_model->compute();
connected_model->setComputed(true);
}
}
}
}

View File

@@ -0,0 +1,42 @@
#ifndef NOGGIT_LOGICBRANCH_HPP
#define NOGGIT_LOGICBRANCH_HPP
#include <external/NodeEditor/include/nodes/FlowScene>
#include <external/NodeEditor/include/nodes/Node>
#include <vector>
#include <unordered_map>
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::Node;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
using QtNodes::Connection;
namespace noggit
{
namespace Red::PresetEditor::Nodes
{
class LogicBranch
{
public:
explicit LogicBranch(Node* logic_node);
void processNode(Node* node);
void executeNode(Node* node);
void executeNodeLeaves(Node* node);
void execute();
private:
Node* _logic_node;
std::vector<Node*> _nodes;
std::unordered_map<Node*, std::vector<LogicBranch>> _sub_branches;
};
}
}
#endif //NOGGIT_LOGICBRANCH_HPP

View File

@@ -0,0 +1,35 @@
#include "NodeScene.hpp"
#include "LogicBranch.hpp"
#include "../BaseNode.hpp"
#include <external/NodeEditor/include/nodes/Node>
#include <noggit/Log.h>
using namespace noggit::Red::PresetEditor::Nodes;
using QtNodes::Node;
void NodeScene::execute()
{
Node* begin = nullptr;
for (auto& pair : _nodes)
{
auto model = static_cast<BaseNode*>(pair.second.get()->nodeDataModel());
model->setComputed(false);
if (model->isLogicNode() && model->name() == "LogicBeginNode")
{
begin = pair.second.get();
}
}
if (!begin)
{
LogError << "No entry point found in the executed script. Aborting execution." << std::endl;
return;
}
auto main_branch = LogicBranch(begin);
main_branch.execute();
}

View File

@@ -0,0 +1,24 @@
#ifndef NOGGIT_NODESCENE_HPP
#define NOGGIT_NODESCENE_HPP
#include <external/NodeEditor/include/nodes/FlowScene>
using QtNodes::DataModelRegistry;
using QtNodes::FlowScene;
namespace noggit
{
namespace Red::PresetEditor::Nodes
{
class NodeScene : public FlowScene
{
public:
NodeScene(std::shared_ptr<DataModelRegistry> registry,
QObject* parent = Q_NULLPTR) : FlowScene(std::move(registry), parent) {};
void execute();
};
}
}
#endif //NOGGIT_NODESCENE_HPP