add list node and one operation node for it

This commit is contained in:
sshumakov3
2020-12-26 00:22:53 +03:00
parent cb39191d81
commit ec29d1ce8e
12 changed files with 461 additions and 15 deletions

View File

@@ -13,6 +13,7 @@ struct NodeDataType
{
QString id;
QString name;
QString parameter_type_id;
};
/// Class represents data transferred between nodes.
@@ -29,6 +30,8 @@ public:
return (this->type().id == nodeData.type().id);
}
virtual void set_parameter_type(QString const& type_id) { _parameter_type_id = type_id; };
/// Type for inner use
virtual NodeDataType type() const = 0;
virtual std::unique_ptr<NodeData> instantiate() = 0;
@@ -36,5 +39,8 @@ public:
virtual std::shared_ptr<NodeData> default_widget_data(QWidget* widget) = 0;
virtual void to_json(QWidget* widget, QJsonObject& json_obj, const std::string& name) = 0;
virtual void from_json(QWidget* widget, const QJsonObject& json_obj, const std::string& name) = 0;
protected:
QString _parameter_type_id = "";
};
}

View File

@@ -119,7 +119,7 @@ NodeConnectionInteraction::
converter = _scene->registry().getTypeConverter(candidateNodeDataType , connectionDataType);
}
if (connectionDataType.id == "any" || candidateNodeDataType.id == "any")
if (connectionDataType.id != "logic" && candidateNodeDataType.id == "any")
{
return true;
}
@@ -130,6 +130,20 @@ NodeConnectionInteraction::
if (connectionDataType.id == "any")
return false;
// 5) handle containers
if (candidateNodeDataType.id == "list" || candidateNodeDataType.id == "dict")
{
if (candidateNodeDataType.parameter_type_id.isEmpty() && connectionDataType.parameter_type_id.isEmpty())
return false;
if (candidateNodeDataType.parameter_type_id.isEmpty() && !connectionDataType.parameter_type_id.isEmpty())
return true;
if (connectionDataType.parameter_type_id != candidateNodeDataType.parameter_type_id)
return false;
}
return true;
}

View File

@@ -23,6 +23,8 @@
#include <noggit/Red/NodeEditor/Nodes/LogicChainNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/LogicProcedureNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/DataConstantNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/DataListNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/ListAddNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/BaseNode.hpp>
#include <noggit/Red/NodeEditor/Nodes/Data/GenericTypeConverter.hpp>
#include <noggit/Red/NodeEditor/Nodes/Scene/NodeScene.hpp>
@@ -63,6 +65,10 @@ namespace noggit
ret->registerModel<LogicProcedureNode>("Logic//Flow");
ret->registerModel<PrintNode>("Functions//Generic");
// List
ret->registerModel<DataListNode>("Containers//List");
ret->registerModel<ListAddNode>("Containers//List");
ret->REGISTER_TYPE_CONVERTER(Decimal, Integer);
ret->REGISTER_TYPE_CONVERTER(Decimal, Boolean);
ret->REGISTER_TYPE_CONVERTER(Integer, Decimal);

View File

@@ -45,7 +45,7 @@ public:
[[nodiscard]]
NodeDataType type() const override
{
return NodeDataType {type_id, type_name};
return NodeDataType {type_id, type_name, _parameter_type_id};
}
[[nodiscard]]
@@ -275,6 +275,7 @@ DECLARE_NODE_DATA_TYPE(any, Any, std::nullptr_t, NoDefaultWidget);
DECLARE_NODE_DATA_TYPE(basic, Basic, std::nullptr_t, NoDefaultWidget);
DECLARE_NODE_DATA_TYPE(undefined, Undefined, std::nullptr_t, NoDefaultWidget);
DECLARE_NODE_DATA_TYPE(procedure, Procedure, std::string, DefaultProcedureWidget);
DECLARE_NODE_DATA_TYPE(list, List, std::vector<std::shared_ptr<NodeData>>*, NoDefaultWidget);
#endif //NOGGIT_GENERICDATA_HPP

View File

@@ -41,8 +41,8 @@ DataConstantNode::DataConstantNode()
addPortDynamic<AnyData>(PortType::In, 0, _type->currentText(), true);
addPortDynamic<AnyData>(PortType::Out, 0, _type->currentText(), true);
_in_ports[0].data_type.reset(TypeFactory::create(type_id.c_str()));
_out_ports[0].data_type.reset(TypeFactory::create(type_id.c_str()));
_in_ports[0].data_type.reset(TypeFactory::create(type_id));
_out_ports[0].data_type.reset(TypeFactory::create(type_id));
addDefaultWidget(_in_ports[0].data_type->default_widget(&_embedded_widget), PortType::In, 0);

View File

@@ -4,6 +4,7 @@
#define NOGGIT_DATACONSTANTNODE_HPP
#include "BaseNode.hpp"
#include <external/tsl/robin_map.h>
#include <QComboBox>
@@ -31,14 +32,14 @@ namespace noggit
private:
QComboBox* _type;
std::unordered_map<std::string, std::string> _type_map = {{"Integer", "int"},
{"UnsignedInteger", "uint"},
{"Boolean", "bool"},
{"Decimal", "double"},
{"String", "string"},
{"Vector2D", "vec2"},
{"Vector3D", "vec3"},
{"Vector4D", "vec4"}};
tsl::robin_map<std::string, std::string> _type_map = {{"Integer", "int"},
{"UnsignedInteger", "uint"},
{"Boolean", "bool"},
{"Decimal", "double"},
{"String", "string"},
{"Vector2D", "vec2"},
{"Vector3D", "vec3"},
{"Vector4D", "vec4"}};
};
}

View File

@@ -0,0 +1,99 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include "DataListNode.hpp"
#include "BaseNode.inl"
#include "Data/GenericData.hpp"
#include <vector>
using namespace noggit::Red::NodeEditor::Nodes;
DataListNode::DataListNode()
: LogicNodeBase()
{
setName("DataListNode");
setCaption("List[Integer]");
_validation_state = NodeValidationState::Valid;
addPort<LogicData>(PortType::In, "Logic", true);
addPort<LogicData>(PortType::Out, "Logic", true);
addPort<ListData>(PortType::Out, "List[Integer]", true);
_out_ports[1].data_type->set_parameter_type("int");
_type = new QComboBox(&_embedded_widget);
_type->addItems({"Integer",
"Unsigned Integer",
"Boolean",
"Decimal",
"String",
"Vector2D",
"Vector3D",
"Vector4D",
"Matrix4x4",
"Matrix3x3",
"Quaternion",
"Procedure"
});
QComboBox::connect(_type, qOverload<int>(&QComboBox::currentIndexChanged)
,[this](int index)
{
auto new_type_id = _type_map[_type->currentText().toStdString()].c_str();
if (_out_ports[1].data_type->type().parameter_type_id == new_type_id)
return;
setCaption("List[" + _type->currentText() + "]");
deletePort(PortType::Out, 1);
auto& type_id = _type_map.at(_type->currentText().toStdString());
addPortDynamic<ListData>(PortType::Out, 1, _caption, true);
_out_ports[1].data_type->set_parameter_type(new_type_id);
}
);
addWidgetTop(_type);
}
void DataListNode::compute()
{
auto logic = static_cast<LogicData*>(_in_ports[0].in_value.lock().get());
if (!logic->value())
return;
_data.clear();
_out_ports[0].out_value = std::make_shared<LogicData>(true);
_out_ports[1].out_value = std::make_shared<ListData>(&_data);
Q_EMIT dataUpdated(0);
Q_EMIT dataUpdated(1);
}
QJsonObject DataListNode::save() const
{
QJsonObject json_obj = BaseNode::save();
json_obj["list_type"] = _type->currentText();
return json_obj;
}
void DataListNode::restore(const QJsonObject& json_obj)
{
BaseNode::restore(json_obj);
auto list_type = json_obj["list_type"].toString();
auto new_type_id = _type_map[list_type.toStdString()].c_str();
_out_ports[1].data_type->set_parameter_type(new_type_id);
_out_ports[1].caption = "List[" + list_type + "]";
_type->setCurrentText(list_type);
}

View File

@@ -0,0 +1,59 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#ifndef NOGGIT_DATALISTNODE_HPP
#define NOGGIT_DATALISTNODE_HPP
#include "LogicNodeBase.hpp"
#include <QComboBox>
#include <vector>
#include <external/tsl/robin_map.h>
using QtNodes::PortType;
using QtNodes::PortIndex;
using QtNodes::NodeData;
using QtNodes::NodeDataType;
using QtNodes::NodeDataModel;
using QtNodes::NodeValidationState;
namespace noggit
{
namespace Red::NodeEditor::Nodes
{
class DataListNode : public LogicNodeBase
{
Q_OBJECT
public:
DataListNode();
void compute() override;
QJsonObject save() const override;
void restore(QJsonObject const& json_obj) override;
private:
QComboBox* _type;
tsl::robin_map<std::string, std::string> _type_map = {
{{"Integer", "int"},
{"Unsigned Integer", "uint"},
{"Boolean", "bool"},
{"Decimal", "double"},
{"String", "string"},
{"Vector2D", "vec2"},
{"Vector3D", "vec3"},
{"Vector4D", "vec4"},
{"Matrix4x4", "mat4"},
{"Matrix3x3", "mat3"},
{"Quaternion", "quat"},
{"Procedure", "procedure"},
}
};
std::vector<std::shared_ptr<NodeData>> _data;
};
}
}
#endif //NOGGIT_DATALISTNODE_HPP

View File

@@ -0,0 +1,197 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include "ListAddNode.hpp"
#include "BaseNode.inl"
#include "Data/GenericData.hpp"
#include "Scene/NodeScene.hpp"
#include <external/NodeEditor/include/nodes/Node>
using QtNodes::Node;
using namespace noggit::Red::NodeEditor::Nodes;
ListAddNode::ListAddNode()
: LogicNodeBase()
{
setName("ListAddNode");
setCaption("Append");
_validation_state = NodeValidationState::Valid;
_operation = new QComboBox(&_embedded_widget);
_operation->addItems({"Append", "Prepend", "Insert"});
QComboBox::connect(_operation, qOverload<int>(&QComboBox::currentIndexChanged)
,[this](int index)
{
setCaption(_operation->currentText());
if (_in_ports.size() < 4 && index != 2)
return;
switch (index)
{
case 2:
addPortDynamic<UnsignedIntegerData>(PortType::In, 3, "Index<UInteger>", true);
addDefaultWidget(_in_ports[3].data_type->default_widget(&_embedded_widget), PortType::In, 3);
break;
default:
deletePort(PortType::In, 3);
break;
}
}
);
addWidgetTop(_operation);
addPort<LogicData>(PortType::In, "Logic", true);
addDefaultWidget(new QLabel(&_embedded_widget), PortType::In, 0);
addPort<ListData>(PortType::In, "List[Any]", true);
addDefaultWidget(new QLabel(&_embedded_widget), PortType::In, 1);
addPort<UndefinedData>(PortType::In, "Value<Undefined>", true);
addDefaultWidget(new QLabel(&_embedded_widget), PortType::In, 2);
addPort<LogicData>(PortType::Out, "Logic", true);
}
void ListAddNode::compute()
{
auto logic = static_cast<LogicData*>(_in_ports[0].in_value.lock().get());
if (!logic->value())
return;
auto list = static_cast<ListData*>(_in_ports[1].in_value.lock().get())->value();
auto value = _in_ports[2].in_value.lock();
switch (_operation->currentIndex())
{
case 0:
list->push_back(value);
break;
case 1:
list->insert(list->begin(), value);
break;
case 2:
auto index_ptr = static_cast<UnsignedIntegerData*>(_in_ports[3].in_value.lock().get());
list->insert(list->begin() + (index_ptr ? index_ptr->value() : static_cast<QSpinBox*>(_in_ports[3].default_widget)->value()), value);
break;
}
_out_ports[0].out_value = std::make_shared<LogicData>(true);
Q_EMIT dataUpdated(0);
}
NodeValidationState ListAddNode::validate()
{
LogicNodeBase::validate();
auto list = static_cast<ListData*>(_in_ports[1].in_value.lock().get());
if (!list)
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: Failed to evaluate list input.");
_out_ports[0].out_value = std::make_shared<LogicData>(false);
Q_EMIT dataUpdated(0);
}
auto value = static_cast<UndefinedData*>(_in_ports[2].in_value.lock().get());
if (!value)
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: Failed to evaluate value input.");
_out_ports[0].out_value = std::make_shared<LogicData>(false);
Q_EMIT dataUpdated(0);
}
return _validation_state;
}
QJsonObject ListAddNode::save() const
{
QJsonObject json_obj = LogicNodeBase::save();
json_obj["operation"] = _operation->currentIndex();
json_obj["list_type"] = _in_ports[1].data_type->type().parameter_type_id;
return json_obj;
}
void ListAddNode::restore(const QJsonObject& json_obj)
{
LogicNodeBase::restore(json_obj);
auto type_id = json_obj["list_type"].toString();
auto type = TypeFactory::create(type_id.toStdString());
_in_ports[1].data_type->set_parameter_type(type_id);
_in_ports[1].caption = "List<" + type->type().name + ">";
_in_ports[2].data_type.reset(type);
_in_ports[2].caption = "Value<" + _in_ports[2].data_type->type().name + ">";
_operation->setCurrentIndex(json_obj["operation"].toInt());
}
void ListAddNode::inputConnectionCreated(const Connection& connection)
{
BaseNode::inputConnectionCreated(connection);
auto port_index = connection.getPortIndex(PortType::In);
if (port_index == 1)
{
auto parameter_type = connection.dataType(PortType::Out).parameter_type_id;
_in_ports[1].data_type->set_parameter_type(parameter_type);
_in_ports[1].caption = connection.getNode(PortType::Out)->nodeDataModel()->portCaption(PortType::Out, connection.getPortIndex(PortType::Out));
_in_ports[2].data_type.reset(TypeFactory::create(parameter_type.toStdString()));
_in_ports[2].caption = "Value<" + _in_ports[2].data_type->type().name + ">";
}
else if (port_index == 3 && _operation->currentIndex() == 3)
{
addPortDynamic<UnsignedIntegerData>(PortType::In, 3, "Index", true);
addDefaultWidget(_in_ports[3].data_type->default_widget(&_embedded_widget), PortType::In, 3);
}
}
void ListAddNode::inputConnectionDeleted(const Connection& connection)
{
BaseNode::inputConnectionDeleted(connection);
auto port_index = connection.getPortIndex(PortType::In);
if (port_index == 1)
{
_in_ports[1].data_type->set_parameter_type("");
_in_ports[1].caption = "List[Any]";
_in_ports[2].data_type.reset(TypeFactory::create("undefined"));
_in_ports[2].caption = "Value<Undefined>";
// remove connection if List changes
if (_in_ports[2].connected)
{
auto this_node = connection.getNode(PortType::In);
auto connections = this_node->nodeState().connections(PortType::In, 2);
for (auto& pair : connections)
{
static_cast<NodeScene*>(this_node->nodeGraphicsObject().scene())->deleteConnection(*pair.second);
}
}
}
}

View File

@@ -0,0 +1,45 @@
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#ifndef NOGGIT_LISTADDNODE_HPP
#define NOGGIT_LISTADDNODE_HPP
#include "LogicNodeBase.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::NodeEditor::Nodes
{
class ListAddNode : public LogicNodeBase
{
Q_OBJECT
public:
ListAddNode();
void compute() override;
NodeValidationState validate() override;
QJsonObject save() const override;
void restore(QJsonObject const& json_obj) override;
public Q_SLOTS:
void inputConnectionCreated(const Connection& connection) override;
void inputConnectionDeleted(const Connection& connection) override;
private:
QComboBox* _operation;
};
}
}
#endif //NOGGIT_LISTADDNODE_HPP

View File

@@ -4,6 +4,7 @@
#define NOGGIT_LOGICNODEBASE_HPP
#include "BaseNode.hpp"
#include "Data/GenericData.hpp"
using QtNodes::PortType;
using QtNodes::PortIndex;
@@ -40,6 +41,23 @@ namespace noggit
int getNIteraitons() { return _n_iterations; };
void setIterationIndex(int index) { _iteration_index = index; };
NodeValidationState validate() override
{
setValidationState(NodeValidationState::Valid);
auto logic = static_cast<LogicData*>(_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);
Q_EMIT dataUpdated(0);
}
return _validation_state;
};
protected:

View File

@@ -139,11 +139,11 @@ void LogicProcedureNode::compute()
Q_EMIT dataUpdated(i);
}
delete _scene;
_scene = nullptr;
}
delete _scene;
_scene = nullptr;
_out_ports[0].out_value = std::make_shared<LogicData>(true);
Q_EMIT dataUpdated(0);