stop script execution on error

implement dynamic types to MathNode
This commit is contained in:
sshumakov3
2020-12-24 16:28:59 +03:00
parent d4da1e95cd
commit fc2ce7e942
12 changed files with 444 additions and 70 deletions

View File

@@ -315,6 +315,6 @@ recalculateVisuals() const
//Recalculate the nodes visuals. A data change can result in the node taking more space than before, so this forces a recalculate+repaint on the affected node
_nodeGraphicsObject->setGeometryChanged();
_nodeGeometry.recalculateSize();
_nodeGraphicsObject->update();
_nodeGraphicsObject->update(_nodeGeometry.boundingRect());
_nodeGraphicsObject->moveConnections();
}

View File

@@ -167,7 +167,11 @@ QWidget* BaseNode::portDefaultValueWidget(PortType port_type, PortIndex port_ind
void BaseNode::inputConnectionDeleted(const Connection& connection)
{
auto default_widget = _in_ports[connection.getPortIndex(PortType::In)].default_widget;
auto& port = _in_ports[connection.getPortIndex(PortType::In)];
port.connected = false;
auto default_widget = port.default_widget;
if (default_widget)
default_widget->setVisible(true);
@@ -175,12 +179,29 @@ void BaseNode::inputConnectionDeleted(const Connection& connection)
void BaseNode::inputConnectionCreated(const Connection& connection)
{
auto default_widget = _in_ports[connection.getPortIndex(PortType::In)].default_widget;
auto& port = _in_ports[connection.getPortIndex(PortType::In)];
port.connected = true;
auto default_widget = port.default_widget;
if (default_widget)
default_widget->setVisible(false);
}
void BaseNode::outputConnectionCreated(const Connection& connection)
{
auto& port = _out_ports[connection.getPortIndex(PortType::Out)];
port.connected = true;
}
void BaseNode::outputConnectionDeleted(const Connection& connection)
{
auto& port = _out_ports[connection.getPortIndex(PortType::Out)];
port.connected = false;
}
QJsonObject BaseNode::save() const
{
QJsonObject json_obj;

View File

@@ -114,6 +114,8 @@ namespace noggit
void inputConnectionCreated(Connection const& connection) override;
void inputConnectionDeleted(Connection const& connection) override;
void outputConnectionCreated(Connection const& connection) override;
void outputConnectionDeleted(Connection const& connection) override;
protected:
@@ -132,6 +134,13 @@ namespace noggit
bool caption_visible,
ConnectionPolicy out_policy = ConnectionPolicy::Many);
template<typename T>
void addPort(PortType port_type,
PortIndex port_index,
QString const& caption,
bool caption_visible,
ConnectionPolicy out_policy = ConnectionPolicy::Many);
void deletePort(PortType port_type, PortIndex port_index);
void deleteDefaultWidget(PortType port_type, PortIndex port_index);

View File

@@ -26,4 +26,28 @@ void noggit::Red::NodeEditor::Nodes::BaseNode::addPort(PortType port_type,
}
}
template<typename T>
void noggit::Red::NodeEditor::Nodes::BaseNode::addPort(PortType port_type,
PortIndex port_index,
const QString &caption,
bool caption_visible,
ConnectionPolicy out_policy)
{
if (port_type == PortType::In)
{
auto port = _in_ports.emplace(_in_ports.begin() + port_index, caption, caption_visible);
port->data_type = std::make_unique<T>();
}
else if (port_type == PortType::Out)
{
auto port = _out_ports.emplace(_out_ports.begin() + port_index, caption, caption_visible);
port->data_type = std::make_unique<T>();
port->connection_policy = out_policy;
}
else
{
throw std::logic_error("Incorrect port type or port type None.");
}
}
#endif // NOGGIT_BASENODE_INL

View File

@@ -255,6 +255,7 @@ private:
template<> TypeFactory::Creator_t* TypeFactory::Register<TYPE_NAME##Data>::creator = TypeFactory::Register<TYPE_NAME##Data>::init_creator(#TYPE_ID);
DECLARE_NODE_DATA_TYPE(int, Integer, int, DefaultIntWidget)
DECLARE_NODE_DATA_TYPE(uint, UnsignedInteger, unsigned int, DefaultIntWidget)
DECLARE_NODE_DATA_TYPE(double, Decimal, double, DefaultDecimalWidget)
DECLARE_NODE_DATA_TYPE(bool, Boolean, bool, DefaultBooleanWidget)
@@ -270,6 +271,7 @@ DECLARE_NODE_DATA_TYPE(mat3, Matrix3x3, glm::mat3, NoDefaultWidget)
DECLARE_NODE_DATA_TYPE(quat, Quaternion, glm::quat, NoDefaultWidget)
DECLARE_NODE_DATA_TYPE(any, Any, std::nullptr_t, NoDefaultWidget);
DECLARE_NODE_DATA_TYPE(undefined, Undefined, std::nullptr_t, NoDefaultWidget);
DECLARE_NODE_DATA_TYPE(procedure, Procedure, std::string, DefaultProcedureWidget);

View File

@@ -90,7 +90,14 @@ void LogicProcedureNode::compute()
sig_index++;
}
_scene->execute();
if (!_scene->execute())
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: Some error occured while executing procedure.");
delete _scene;
_scene = nullptr;
return;
}
auto return_node = _scene->getReturnNode();
@@ -177,14 +184,22 @@ void LogicProcedureNode::restore(const QJsonObject& json_obj)
for (int i = 0; i < json_obj["n_dynamic_in_ports"].toInt(); ++i)
{
addPort<LogicData>(PortType::In, json_obj[("in_port_" + std::to_string(i + 2) + "_caption").c_str()].toString(), true);
_in_ports[_in_ports.size() - 1].data_type = TypeFactory::create(json_obj[("in_port_" + std::to_string(i + 2)).c_str()].toString().toStdString())->instantiate();
std::unique_ptr<NodeData> type;
type.reset(TypeFactory::create(json_obj[("in_port_" + std::to_string(i + 2)).c_str()].toString().toStdString()));
_in_ports[_in_ports.size() - 1].data_type = std::move(type);
emit portAdded(PortType::In, _in_ports.size() - 1);
}
for (int i = 0; i < json_obj["n_dynamic_out_ports"].toInt(); ++i)
{
addPort<LogicData>(PortType::Out, json_obj[("out_port_" + std::to_string(i + 1) + "_caption").c_str()].toString(), true);
_out_ports[_out_ports.size() - 1].data_type = TypeFactory::create(json_obj[("out_port_" + std::to_string(i + 1)).c_str()].toString().toStdString())->instantiate();
std::unique_ptr<NodeData> type;
type.reset(TypeFactory::create(json_obj[("out_port_" + std::to_string(i + 1)).c_str()].toString().toStdString()));
_out_ports[_out_ports.size() - 1].data_type = std::move(type);
emit portAdded(PortType::Out, _out_ports.size() - 1);
}

View File

@@ -2,9 +2,11 @@
#include "BaseNode.inl"
#include "noggit/Red/NodeEditor/Nodes/Data/GenericData.hpp"
#include <cmath>
#include <stdexcept>
using namespace noggit::Red::NodeEditor::Nodes;
MathNode::MathNode()
: BaseNode()
{
@@ -27,15 +29,11 @@ MathNode::MathNode()
}
);
addPort<DecimalData>(PortType::In, "Value", true);
_first = new QDoubleSpinBox(&_embedded_widget);
addDefaultWidget(_first, PortType::In, 0);
addPort<AnyData>(PortType::In, "Any*", true);
addPort<DecimalData>(PortType::In, "Value", true);
_second = new QDoubleSpinBox(&_embedded_widget);
addDefaultWidget(_second, PortType::In, 1);
addPort<AnyData>(PortType::In, "Any*", true);
addPort<DecimalData>(PortType::Out, "Value", true);
addPort<UndefinedData>(PortType::Out, "Undefined", true);
}
void MathNode::compute()
@@ -43,63 +41,333 @@ void MathNode::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());
auto first_type_id = _in_ports[0].data_type->type().id;
auto second_type_id = _in_ports[1].data_type->type().id;
// handle defaults
double first_number = first ? first->value() : _first->value();
double second_number = second ? second->value() : _second->value();
int winning_type = std::max(_type_map[first_type_id.toStdString()], _type_map[second_type_id.toStdString()]);
int winning_port = first_type_id < second_type_id;
setValidationState(NodeValidationState::Warning);
double result = 0.0;
switch (_operation->currentIndex())
switch (winning_type)
{
case 0:
result = first_number + second_number;
{
auto first = commonCast<int>(first_shared.get());
auto second = commonCast<int>(second_shared.get());
handleOperation(first, second);
break;
}
case 1:
result = first_number - second_number;
{
auto first = commonCast<unsigned int>(first_shared.get());
auto second = commonCast<unsigned int>(second_shared.get());
handleOperation(first, second);
break;
}
case 2:
result = first_number * second_number;
{
auto first = commonCast<double>(first_shared.get());
auto second = commonCast<double>(second_shared.get());
handleOperation(first, second);
break;
}
case 3:
result = first_number / second_number;
{
auto first = commonCast<std::string>(first_shared.get());
auto second = commonCast<std::string>(second_shared.get());
handleOperation(first, second);
break;
case 4:
result = std::fmod(first_number, second_number);
}
default:
break;
}
_out_ports[0].out_value = std::make_shared<DecimalData>(result);
setValidationMessage(("Debug: " + std::to_string(result)).c_str());
Q_EMIT dataUpdated(0);
}
QJsonObject MathNode::save() const
{
QJsonObject modelJson;
QJsonObject json_obj = BaseNode::save();
modelJson["name"] = name();
modelJson["caption"] = caption();
modelJson["default_first"] = _first->value();
modelJson["default_second"] = _second->value();
modelJson["operation"] = _operation->currentIndex();
auto first_type_id = _in_ports[0].data_type->type().id;
json_obj["first_type_id"] = first_type_id;
return modelJson;
auto second_type_id = _in_ports[1].data_type->type().id;
json_obj["second_type_id"] = second_type_id;
json_obj["ret_type_id"] = _out_ports[0].data_type->type().id;
json_obj["operation"] = _operation->currentIndex();
return json_obj;
}
void MathNode::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());
BaseNode::restore(json_obj);
auto first_type_id = json_obj["first_type_id"].toString();
_in_ports[0].data_type.reset(TypeFactory::create(first_type_id.toStdString()));
_in_ports[0].caption = _in_ports[0].data_type->type().name;
auto second_type_id = json_obj["second_type_id"].toString();
_in_ports[1].data_type.reset(TypeFactory::create(second_type_id.toStdString()));
_in_ports[1].caption = _in_ports[1].data_type->type().name;
auto ret_type_id = json_obj["ret_type_id"].toString().toStdString();
_out_ports[0].data_type.reset(TypeFactory::create(ret_type_id));
_out_ports[0].caption = _out_ports[0].data_type->type().name;
_operation->setCurrentIndex(json_obj["operation"].toInt());
}
void MathNode::inputConnectionCreated(const Connection& connection)
{
BaseNode::inputConnectionCreated(connection);
PortIndex port_index = connection.getPortIndex(PortType::In);
PortIndex other_port_index = port_index ? 0 : 1;
bool supported_type = false;
// Disconnect types which are not supported by the node.
if (_type_map.find(connection.dataType(PortType::Out).id.toStdString()) == _type_map.end())
{
deletePort(PortType::In, port_index);
addPort<AnyData>(PortType::In, port_index, "Any*", true);
emit portAdded(PortType::In, port_index);
deletePort(PortType::Out, 0);
addPort<UndefinedData>(PortType::Out, "Undefined", true);
emit portAdded(PortType::Out, 0);
return;
}
_in_ports[port_index].data_type.reset(TypeFactory::create(connection.dataType(PortType::Out).id.toStdString()));
_in_ports[port_index].caption = _in_ports[port_index].data_type->type().name;
auto other_type_id = _in_ports[other_port_index].data_type->type().id.toStdString();
if (other_type_id != "any")
{
auto result_type_id = std::max(_type_map[_in_ports[port_index].data_type->type().id.toStdString()], _type_map[other_type_id]);
std::string result_type_id_str;
for (auto& it : _type_map)
{
if (it.second == result_type_id)
{
result_type_id_str = it.first;
break;
}
}
if (result_type_id_str == "string" && _operation->currentIndex())
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: String type only supports concatenation (Add).");
deletePort(PortType::Out, 0);
addPort<UndefinedData>(PortType::Out, 0, "Undefined", true);
emit portAdded(PortType::Out, 0);
return;
}
if (_out_ports[0].data_type->type().id.toStdString() != result_type_id_str)
{
deletePort(PortType::Out, 0);
auto new_type = TypeFactory::create(result_type_id_str);
addPort<UndefinedData>(PortType::Out, 0, new_type->type().name, true);
_out_ports[0].data_type.reset(new_type);
emit portAdded(PortType::Out, 0);
}
}
}
void MathNode::inputConnectionDeleted(const Connection& connection)
{
BaseNode::inputConnectionDeleted(connection);
PortIndex port_index = connection.getPortIndex(PortType::In);
deletePort(PortType::In, port_index);
addPort<AnyData>(PortType::In, port_index, "Any*", true);
emit portAdded(PortType::In, port_index);
deletePort(PortType::Out, 0);
addPort<UndefinedData>(PortType::Out, "Undefined", true);
emit portAdded(PortType::Out, 0);
}
NodeValidationState MathNode::validate()
{
setValidationState(NodeValidationState::Valid);
if (_out_ports[0].data_type->type().id == "undefined")
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: result of operation is of undefined type.");
return _validation_state;
}
return _validation_state;
}
template<typename T>
T MathNode::commonCast(NodeData* data)
{
int type_id_to;
if constexpr (std::is_same<T, int>::value)
type_id_to = 0;
else if constexpr (std::is_same<T, unsigned int>::value)
type_id_to = 1;
else if constexpr (std::is_same<T, double>::value)
type_id_to = 2;
else if constexpr (std::is_same<T, std::string>::value)
type_id_to = 3;
if constexpr (std::is_same<T, std::string>::value)
{
switch (_type_map[data->type().id.toStdString()])
{
case 0:
return std::move(std::to_string(static_cast<IntegerData*>(data)->value()));
break;
case 1:
return std::move(std::to_string(static_cast<UnsignedIntegerData*>(data)->value()));
break;
case 2:
return std::move(std::to_string(static_cast<DecimalData*>(data)->value()));
break;
case 3:
return static_cast<StringData*>(data)->value();
break;
}
}
else
{
switch (_type_map[data->type().id.toStdString()])
{
case 0:
return static_cast<T>(static_cast<IntegerData*>(data)->value());
break;
case 1:
return static_cast<T>(static_cast<UnsignedIntegerData*>(data)->value());
break;
case 2:
return static_cast<T>(static_cast<DecimalData*>(data)->value());
break;
case 3:
throw std::logic_error("Casting String type to numeric types is not supported in MathNode.");
break;
}
}
throw std::logic_error("Invalid types for MathNode operation.");
}
template<typename T>
void MathNode::handleOperation(T first, T second)
{
setValidationState(NodeValidationState::Warning);
T result;
if constexpr (!std::is_same<T, std::string>::value)
{
switch (_operation->currentIndex())
{
case 0:
result = first + first;
break;
case 1:
result = first - first;
break;
case 2:
result = first * first;
break;
case 3:
if constexpr (std::is_same<T, int>::value || std::is_same<T, unsigned int>::value)
{
if (!second)
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: division by zero");
return;
}
result = first / first;
}
else
{
result = first / first;
}
break;
case 4:
if constexpr (std::is_same<T, int>::value || std::is_same<T, unsigned int>::value)
{
if (!second)
{
setValidationState(NodeValidationState::Error);
setValidationMessage("Error: modulo by zero");
return;
}
result = first % second;
}
else
{
result = std::fmod(first, first);
}
break;
}
}
else
{
switch (_operation->currentIndex())
{
case 0:
result = first + first;
break;
default:
throw std::logic_error("String type only supports concatenation in MathNode.");
break;
}
}
if constexpr (std::is_same<T, int>::value)
{
_out_ports[0].out_value = std::make_shared<IntegerData>(result);
setValidationMessage(("Debug: " + std::to_string(result)).c_str());
Q_EMIT dataUpdated(0);
}
else if constexpr (std::is_same<T, unsigned int>::value)
{
_out_ports[0].out_value = std::make_shared<UnsignedIntegerData>(result);
setValidationMessage(("Debug: " + std::to_string(result)).c_str());
Q_EMIT dataUpdated(0);
}
else if constexpr (std::is_same<T, double>::value)
{
_out_ports[0].out_value = std::make_shared<DecimalData>(result);
setValidationMessage(("Debug: " + std::to_string(result)).c_str());
Q_EMIT dataUpdated(0);
}
else if constexpr (std::is_same<T, std::string>::value)
{
_out_ports[0].out_value = std::make_shared<StringData>(result);
setValidationMessage(("Debug: " + result).c_str());
Q_EMIT dataUpdated(0);
}
}

View File

@@ -3,7 +3,7 @@
#include "BaseNode.hpp"
#include <QDoubleSpinBox>
#include <unordered_map>
#include <QComboBox>
using QtNodes::PortType;
@@ -26,12 +26,25 @@ namespace noggit
MathNode();
void compute() override;
QJsonObject save() const override;
NodeValidationState validate() override;
void restore(QJsonObject const& json_obj) override;
public Q_SLOTS:
void inputConnectionCreated(const Connection& connection) override;
void inputConnectionDeleted(const Connection& connection) override;
protected:
QDoubleSpinBox* _first;
QDoubleSpinBox* _second;
QComboBox* _operation;
std::unordered_map<std::string, int> _type_map = {{"int", 0},
{"uint", 1},
{"double", 2},
{"string", 3}};
template <typename T>
T commonCast(NodeData* data);
template <typename T>
void handleOperation(T first, T second);
};

View File

@@ -15,22 +15,22 @@ LogicBranch::LogicBranch(Node* logic_node)
}
void LogicBranch::execute()
bool LogicBranch::execute()
{
_return = false;
executeNode(_logic_node, nullptr);
return executeNode(_logic_node, nullptr);
}
void LogicBranch::executeNode(Node* node, Node* source_node)
bool LogicBranch::executeNode(Node* node, Node* source_node)
{
if (_return)
return;
return true;
auto model = static_cast<BaseNode*>(node->nodeDataModel());
auto nodeState = node->nodeState();
if (model->isComputed())
return;
return true;
model->compute();
model->setComputed(true);
@@ -75,16 +75,16 @@ void LogicBranch::executeNode(Node* node, Node* source_node)
loop_model->setComputed(false);
}
}
else if (logic_node_model->name() == "LogicReturnNoDataNode")
else if (logic_node_model->name() == "LogicReturnNoDataNode" || logic_node_model->name() == "LogicReturnNode")
{
_return = true;
return;
return true;
}
}
// do not continue further if validation or execution has found a problem
if (model->validationState() == NodeValidationState::Error)
return;
return false;
// Handle dependant nodes
for (int i = 0; i < model->nPorts(PortType::Out); ++i)
@@ -108,7 +108,14 @@ void LogicBranch::executeNode(Node* node, Node* source_node)
auto connected_model = static_cast<BaseNode*>(connected_node->nodeDataModel());
executeNodeLeaves(connected_node, node); // Execute data node leaves
// Execute data node leaves
if (!executeNodeLeaves(connected_node, node))
{
connected_model->setValidationState(NodeValidationState::Error);
connected_model->setValidationMessage("Error: dependant leave nodes failed to execute.");
return false;
}
if (connected_model->validate() != NodeValidationState::Error)
{
auto logic_model = static_cast<LogicNodeBase*>(connected_node->nodeDataModel());
@@ -120,7 +127,10 @@ void LogicBranch::executeNode(Node* node, Node* source_node)
while (it_index >= 0 && it_index < logic_model->getNIteraitons() && !_return)
{
markNodesComputed(connected_node, false);
executeNode(connected_node, node);
if (!executeNode(connected_node, node))
return false;
logic_model->setComputed(true);
it_index = logic_model->getIterationindex();
}
@@ -129,21 +139,24 @@ void LogicBranch::executeNode(Node* node, Node* source_node)
}
else // haandle regular nodes
{
executeNode(connected_node, node);
if (!executeNode(connected_node, node))
return false;
}
}
}
}
return true;
}
void LogicBranch::executeNodeLeaves(Node* node, Node* source_node)
bool LogicBranch::executeNodeLeaves(Node* node, Node* source_node)
{
auto model = static_cast<BaseNode*>(node->nodeDataModel());
auto nodeState = node->nodeState();
if (model->isComputed())
return;
return true;
for (int i = 0; i < model->nPorts(PortType::In); ++i)
{
@@ -168,10 +181,20 @@ void LogicBranch::executeNodeLeaves(Node* node, Node* source_node)
continue;
}
executeNodeLeaves(connected_node, node);
if (!executeNodeLeaves(connected_node, node))
return false;
if (connected_model->validate() == NodeValidationState::Error)
return false;
connected_model->compute();
if (connected_model->validationState() == NodeValidationState::Error)
return false;
}
}
return true;
}
void LogicBranch::markNodesComputed(Node* start_node, bool state)
@@ -195,7 +218,6 @@ void LogicBranch::markNodesComputed(Node* start_node, bool state)
markNodeLeavesComputed(connected_node, start_node, state);
markNodesComputed(connected_node, state);
}
}

View File

@@ -23,14 +23,14 @@ namespace noggit
{
public:
explicit LogicBranch(Node* logic_node);
void executeNode(Node* node, Node* source_node);
void executeNodeLeaves(Node* node, Node* source_node);
bool executeNode(Node* node, Node* source_node);
bool executeNodeLeaves(Node* node, Node* source_node);
void markNodesComputed(Node* start_node, bool state);
void markNodeLeavesComputed(Node* start_node, Node* source_node, bool state);
void setCurrentLoop(Node* node) { _loop_stack.push(node); };
void unsetCurrentLoop() { _loop_stack.pop(); };
Node* getCurrentLoop() { return _loop_stack.empty() ? nullptr : _loop_stack.top(); };
void execute();
bool execute();
private:
Node* _logic_node;

View File

@@ -8,13 +8,13 @@
using namespace noggit::Red::NodeEditor::Nodes;
void NodeScene::execute()
bool NodeScene::execute()
{
if (!validate())
return;
return false;
auto main_branch = LogicBranch(_begin_node);
main_branch.execute();
return main_branch.execute();
}
bool NodeScene::validate()

View File

@@ -16,7 +16,7 @@ namespace noggit
public:
NodeScene(std::shared_ptr<DataModelRegistry> registry,
QObject* parent = Q_NULLPTR) : FlowScene(std::move(registry), parent) {};
void execute();
bool execute();
bool validate();
Node* getBeginNode() {return _begin_node; };