Skip to content

DataFlowGraphModel.hpp , DataFlowGraphModel.cpp , UndoCommands.cpp #451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions include/QtNodes/internal/DataFlowGraphModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,20 @@ class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public
template<typename NodeDelegateModelType>
NodeDelegateModelType *delegateModel(NodeId const nodeId)
{
auto it = _models.find(nodeId);
if (it == _models.end())
auto it = models_.find(nodeId);
if (it == models_.end())
return nullptr;

auto model = dynamic_cast<NodeDelegateModelType *>(it->second.get());
auto model = dynamic_cast<NodeDelegateModelType *>(it->second.nodeModel.get());

return model;
}

//DFS
/* Depth-first search (depth-first search)
* starts at the starting node and recursively visits neighbor nodes along a certain path
* until the target node is found or there are no more neighbors to visit.
*/
bool dfs(ConnectionId const connectionId);
Q_SIGNALS:
void inPortDataWasSet(NodeId const, PortType const, PortIndex const);

Expand Down Expand Up @@ -121,15 +126,22 @@ private Q_SLOTS:
void propagateEmptyDataTo(NodeId const nodeId, PortIndex const portIndex);

private:
//Topology of nodes -- directed acyclic graph
struct NodeTopological
{
std::unique_ptr<NodeDelegateModel> nodeModel;
NodeGeometryData nodeGeometryData;
std::unordered_map<NodeId,int> successorNodes;
};

std::shared_ptr<NodeDelegateModelRegistry> _registry;

NodeId _nextNodeId;

std::unordered_map<NodeId, std::unique_ptr<NodeDelegateModel>> _models;
mutable std::unordered_map<NodeId, NodeTopological> _models;

std::unordered_set<ConnectionId> _connectivity;

mutable std::unordered_map<NodeId, NodeGeometryData> _nodeGeometryData;
};

} // namespace QtNodes
4 changes: 4 additions & 0 deletions include/QtNodes/internal/NodeDelegateModel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable

virtual ~NodeDelegateModel() = default;

/// The creation completes the call initializationIt
/// Inherited classes are not recommended to be initialized in the constructor
virtual void initialize() {}

/// It is possible to hide caption in GUI
virtual bool captionVisible() const { return true; }

Expand Down
121 changes: 108 additions & 13 deletions src/DataFlowGraphModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ NodeId DataFlowGraphModel::addNode(QString const nodeType)
std::unique_ptr<NodeDelegateModel> model = _registry->create(nodeType);

if (model) {
model->initialize();

NodeId newId = newNodeId();

connect(model.get(),
Expand Down Expand Up @@ -93,7 +95,7 @@ NodeId DataFlowGraphModel::addNode(QString const nodeType)
this,
&DataFlowGraphModel::portsInserted);

_models[newId] = std::move(model);
_models[newId].nodeModel = std::move(model);

Q_EMIT nodeCreated(newId);

Expand Down Expand Up @@ -130,6 +132,42 @@ bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) con

void DataFlowGraphModel::addConnection(ConnectionId const connectionId)
{
NodeDataType inType, outType;
ConnectionPolicy inPolicy = ConnectionPolicy::Many, outPolicy = ConnectionPolicy::Many;

// Gets the type and policy of the input node
if (auto inNode = delegateModel<NodeDelegateModel>(connectionId.inNodeId); inNode) {
inType = inNode->dataType(PortType::In, connectionId.inPortIndex);
inPolicy = inNode->portConnectionPolicy(PortType::In, connectionId.inPortIndex);
}

// Gets the type and policy of the output node
if (auto outNode = delegateModel<NodeDelegateModel>(connectionId.outNodeId); outNode) {
outType = outNode->dataType(PortType::Out, connectionId.outPortIndex);
outPolicy = outNode->portConnectionPolicy(PortType::Out, connectionId.outPortIndex);
}
// If the data type is inconsistent, return it directly
if (inType != outType) return;

auto connectionExist = [this](NodeId nodeId, PortType portType, int portIndex, ConnectionPolicy policy) {
return policy == ConnectionPolicy::One && connections(nodeId, portType, portIndex).size() > 0;
};

// Check the input and output connection policies
if (connectionExist(connectionId.inNodeId, PortType::In, connectionId.inPortIndex, inPolicy)) return;
if (connectionExist(connectionId.outNodeId, PortType::Out, connectionId.outPortIndex, outPolicy)) return;

// Check if loop connections are made
if(!dfs(connectionId)) return;

// There is no assignment of 1, there is ++
if(_models.at(connectionId.outNodeId).successorNodes.contains(connectionId.inNodeId)) {
_models.at(connectionId.outNodeId).successorNodes[connectionId.inNodeId]++;
} else {
_models.at(connectionId.outNodeId).successorNodes[connectionId.inNodeId] = 1;
}

// return;
_connectivity.insert(connectionId);

sendConnectionCreation(connectionId);
Expand All @@ -146,15 +184,62 @@ void DataFlowGraphModel::addConnection(ConnectionId const connectionId)
PortRole::Data);
}


// Traversing forest using stack structure loop (non-recursive)
/* 000...0
* / \
* 000..0 00..0
* / \ / \
* 000..0 00 0..0
*
*/
bool DataFlowGraphModel::dfs(ConnectionId const connectionId)
{
auto& successorNodes = _models.at(connectionId.inNodeId).successorNodes;

std::stack<NodeId> nodeStack;
// Prevent duplicate access
std::unordered_set<NodeId> visitedNodes;

// Put the initial node on the stack
for (const auto& node : successorNodes) {
nodeStack.push(node.first);
}

while (!nodeStack.empty()) {
NodeId nodeId = nodeStack.top();
nodeStack.pop();

// If the target node is found, it is a directed ring graph
if (nodeId == connectionId.outNodeId) {
return false;
}

// If the node has been accessed, skip it
if (visitedNodes.find(nodeId) != visitedNodes.end()) {
continue;
}

visitedNodes.insert(nodeId);

// Gets the successor to this node and pushes it onto the stack
auto& nodes = _models.at(nodeId).successorNodes;
for (const auto& nextNode : nodes) {
nodeStack.push(nextNode.first);
}
}
return true;;
}

void DataFlowGraphModel::sendConnectionCreation(ConnectionId const connectionId)
{
Q_EMIT connectionCreated(connectionId);

auto iti = _models.find(connectionId.inNodeId);
auto ito = _models.find(connectionId.outNodeId);
if (iti != _models.end() && ito != _models.end()) {
auto &modeli = iti->second;
auto &modelo = ito->second;
auto &modeli = iti->second.nodeModel;
auto &modelo = ito->second.nodeModel;
modeli->inputConnectionCreated(connectionId);
modelo->outputConnectionCreated(connectionId);
}
Expand All @@ -167,8 +252,8 @@ void DataFlowGraphModel::sendConnectionDeletion(ConnectionId const connectionId)
auto iti = _models.find(connectionId.inNodeId);
auto ito = _models.find(connectionId.outNodeId);
if (iti != _models.end() && ito != _models.end()) {
auto &modeli = iti->second;
auto &modelo = ito->second;
auto &modeli = iti->second.nodeModel;
auto &modelo = ito->second.nodeModel;
modeli->inputConnectionDeleted(connectionId);
modelo->outputConnectionDeleted(connectionId);
}
Expand All @@ -187,7 +272,7 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const
if (it == _models.end())
return result;

auto &model = it->second;
auto &model = it->second.nodeModel;

switch (role) {
case NodeRole::Type:
Expand Down Expand Up @@ -218,7 +303,7 @@ QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const
case NodeRole::InternalData: {
QJsonObject nodeJson;

nodeJson["internal-data"] = _models.at(nodeId)->save();
nodeJson["internal-data"] = _models.at(nodeId).nodeModel->save();

result = nodeJson.toVariantMap();
break;
Expand All @@ -245,7 +330,7 @@ NodeFlags DataFlowGraphModel::nodeFlags(NodeId nodeId) const
{
auto it = _models.find(nodeId);

if (it != _models.end() && it->second->resizable())
if (it != _models.end() && it->second.nodeModel->resizable())
return NodeFlag::Resizable;

return NodeFlag::NoFlags;
Expand Down Expand Up @@ -311,7 +396,7 @@ QVariant DataFlowGraphModel::portData(NodeId nodeId,
if (it == _models.end())
return result;

auto &model = it->second;
auto &model = it->second.nodeModel;

switch (role) {
case PortRole::Data:
Expand Down Expand Up @@ -351,7 +436,7 @@ bool DataFlowGraphModel::setPortData(
if (it == _models.end())
return false;

auto &model = it->second;
auto &model = it->second.nodeModel;

switch (role) {
case PortRole::Data:
Expand Down Expand Up @@ -379,9 +464,17 @@ bool DataFlowGraphModel::deleteConnection(ConnectionId const connectionId)
if (it != _connectivity.end()) {
disconnected = true;

//count == 0 Example Remove a successor node from the topology
int count = --_models.at(connectionId.outNodeId).successorNodes[connectionId.inNodeId];
if(count == 0)
{
_models.at(connectionId.outNodeId).successorNodes.erase(connectionId.inNodeId);
}

_connectivity.erase(it);
}


if (disconnected) {
sendConnectionDeletion(connectionId);

Expand Down Expand Up @@ -414,7 +507,7 @@ QJsonObject DataFlowGraphModel::saveNode(NodeId const nodeId) const

nodeJson["id"] = static_cast<qint64>(nodeId);

nodeJson["internal-data"] = _models.at(nodeId)->save();
nodeJson["internal-data"] = _models.at(nodeId).nodeModel->save();

{
QPointF const pos = nodeData(nodeId, NodeRole::Position).value<QPointF>();
Expand Down Expand Up @@ -467,13 +560,15 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson)
std::unique_ptr<NodeDelegateModel> model = _registry->create(delegateModelName);

if (model) {
model->initialize();

connect(model.get(),
&NodeDelegateModel::dataUpdated,
[restoredNodeId, this](PortIndex const portIndex) {
onOutPortDataUpdated(restoredNodeId, portIndex);
});

_models[restoredNodeId] = std::move(model);
_models[restoredNodeId].nodeModel = std::move(model);

Q_EMIT nodeCreated(restoredNodeId);

Expand All @@ -482,7 +577,7 @@ void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson)

setNodeData(restoredNodeId, NodeRole::Position, pos);

_models[restoredNodeId]->load(internalDataJson);
_models[restoredNodeId].nodeModel->load(internalDataJson);
} else {
throw std::logic_error(std::string("No registered model with name ")
+ delegateModelName.toLocal8Bit().data());
Expand Down
4 changes: 3 additions & 1 deletion src/UndoCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ static void insertSerializedItems(QJsonObject const &json, BasicGraphicsScene *s
// Restore the connection
graphModel.addConnection(connId);

scene->connectionGraphicsObject(connId)->setSelected(true);
if(auto obj = scene->connectionGraphicsObject(connId);obj) {
obj->setSelected(true);
}
}
}

Expand Down