123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506 |
- #include "DataFlowGraphModel.hpp"
- #include "ConnectionIdHash.hpp"
- #include <QJsonArray>
- #include <stdexcept>
- namespace QtNodes {
- DataFlowGraphModel::DataFlowGraphModel(std::shared_ptr<NodeDelegateModelRegistry> registry)
- : _registry(std::move(registry)), _nextNodeId { 0 }
- {
- }
- std::unordered_set<NodeId> DataFlowGraphModel::allNodeIds() const
- {
- std::unordered_set<NodeId> nodeIds;
- for_each(_models.begin(), _models.end(), [&nodeIds](auto const &p) { nodeIds.insert(p.first); });
- return nodeIds;
- }
- std::unordered_set<ConnectionId> DataFlowGraphModel::allConnectionIds(NodeId const nodeId) const
- {
- std::unordered_set<ConnectionId> result;
- std::copy_if(_connectivity.begin(), _connectivity.end(), std::inserter(result, std::end(result)),
- [&nodeId](ConnectionId const &cid) { return cid.inNodeId == nodeId || cid.outNodeId == nodeId; });
- return result;
- }
- std::unordered_set<ConnectionId> DataFlowGraphModel::connections(NodeId nodeId, PortType portType,
- PortIndex portIndex) const
- {
- std::unordered_set<ConnectionId> result;
- std::copy_if(_connectivity.begin(), _connectivity.end(), std::inserter(result, std::end(result)),
- [&portType, &portIndex, &nodeId](ConnectionId const &cid) {
- return (getNodeId(portType, cid) == nodeId && getPortIndex(portType, cid) == portIndex);
- });
- return result;
- }
- bool DataFlowGraphModel::connectionExists(ConnectionId const connectionId) const
- {
- return (_connectivity.find(connectionId) != _connectivity.end());
- }
- NodeId DataFlowGraphModel::addNode(QString const nodeType)
- {
- std::unique_ptr<NodeDelegateModel> model = _registry->create(nodeType);
- if (model) {
- NodeId newId = newNodeId();
- connect(model.get(), &NodeDelegateModel::dataUpdated,
- [newId, this](PortIndex const portIndex) { onOutPortDataUpdated(newId, portIndex); });
- connect(model.get(), &NodeDelegateModel::portsAboutToBeDeleted, this,
- [newId, this](PortType const portType, PortIndex const first, PortIndex const last) {
- portsAboutToBeDeleted(newId, portType, first, last);
- });
- connect(model.get(), &NodeDelegateModel::portsDeleted, this, &DataFlowGraphModel::portsDeleted);
- connect(model.get(), &NodeDelegateModel::portsAboutToBeInserted, this,
- [newId, this](PortType const portType, PortIndex const first, PortIndex const last) {
- portsAboutToBeInserted(newId, portType, first, last);
- });
- connect(model.get(), &NodeDelegateModel::portsInserted, this, &DataFlowGraphModel::portsInserted);
- _models[newId] = std::move(model);
- Q_EMIT nodeCreated(newId);
- return newId;
- }
- return InvalidNodeId;
- }
- bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) const
- {
- auto getDataType = [&](PortType const portType) {
- return portData(getNodeId(portType, connectionId), portType, getPortIndex(portType, connectionId),
- PortRole::DataType)
- .value<NodeDataType>();
- };
- auto portVacant = [&](PortType const portType) {
- NodeId const nodeId = getNodeId(portType, connectionId);
- PortIndex const portIndex = getPortIndex(portType, connectionId);
- auto const connected = connections(nodeId, portType, portIndex);
- auto policy = portData(nodeId, portType, portIndex, PortRole::ConnectionPolicyRole).value<ConnectionPolicy>();
- return connected.empty() || (policy == ConnectionPolicy::Many);
- };
- return getDataType(PortType::Out).id == getDataType(PortType::In).id && portVacant(PortType::Out)
- && portVacant(PortType::In);
- }
- void DataFlowGraphModel::addConnection(ConnectionId const connectionId)
- {
- _connectivity.insert(connectionId);
- sendConnectionCreation(connectionId);
- QVariant const portDataToPropagate =
- portData(connectionId.outNodeId, PortType::Out, connectionId.outPortIndex, PortRole::Data);
- setPortData(connectionId.inNodeId, PortType::In, connectionId.inPortIndex, portDataToPropagate, PortRole::Data);
- }
- 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;
- modeli->inputConnectionCreated(connectionId);
- modelo->outputConnectionCreated(connectionId);
- }
- }
- void DataFlowGraphModel::sendConnectionDeletion(ConnectionId const connectionId)
- {
- Q_EMIT connectionDeleted(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;
- modeli->inputConnectionDeleted(connectionId);
- modelo->outputConnectionDeleted(connectionId);
- }
- }
- bool DataFlowGraphModel::nodeExists(NodeId const nodeId) const
- {
- return (_models.find(nodeId) != _models.end());
- }
- QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const
- {
- QVariant result;
- auto it = _models.find(nodeId);
- if (it == _models.end())
- return result;
- auto &model = it->second;
- switch (role) {
- case NodeRole::Type:
- result = model->name();
- break;
- case NodeRole::Position:
- result = _nodeGeometryData[nodeId].pos;
- break;
- case NodeRole::Size:
- result = _nodeGeometryData[nodeId].size;
- break;
- case NodeRole::CaptionVisible:
- result = model->captionVisible();
- break;
- case NodeRole::Caption:
- // result = model->caption();
- result = _captionData[nodeId];
- break;
- case NodeRole::Style: {
- auto style = StyleCollection::nodeStyle();
- result = style.toJson().toVariantMap();
- } break;
- case NodeRole::InternalData: {
- QJsonObject nodeJson;
- nodeJson["internal-data"] = _models.at(nodeId)->save();
- result = nodeJson.toVariantMap();
- break;
- }
- case NodeRole::InPortCount:
- result = model->nPorts(PortType::In);
- // result = _inPorts[nodeId];
- break;
- case NodeRole::OutPortCount:
- result = model->nPorts(PortType::Out);
- // result = _outPorts[nodeId];
- break;
- case NodeRole::Widget: {
- // auto w = model->embeddedWidget();
- QWidget *w = _widgetData[nodeId];
- result = QVariant::fromValue(w);
- } break;
- }
- return result;
- }
- NodeFlags DataFlowGraphModel::nodeFlags(NodeId nodeId) const
- {
- auto it = _models.find(nodeId);
- if (it != _models.end() && it->second->resizable())
- return NodeFlag::Resizable;
- return NodeFlag::NoFlags;
- }
- bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value)
- {
- Q_UNUSED(nodeId);
- Q_UNUSED(role);
- Q_UNUSED(value);
- bool result = false;
- switch (role) {
- case NodeRole::Type:
- break;
- case NodeRole::Position: {
- _nodeGeometryData[nodeId].pos = value.value<QPointF>();
- Q_EMIT nodePositionUpdated(nodeId);
- result = true;
- } break;
- case NodeRole::Size: {
- _nodeGeometryData[nodeId].size = value.value<QSize>();
- result = true;
- } break;
- case NodeRole::CaptionVisible:
- break;
- case NodeRole::Caption:
- _captionData[nodeId] = value.value<QString>();
- Q_EMIT nodeCreated(nodeId);
- result = true;
- break;
- case NodeRole::Style:
- break;
- case NodeRole::InternalData:
- break;
- case NodeRole::InPortCount:
- _inPorts[nodeId] = value.value<unsigned int>();
- Q_EMIT nodeCreated(nodeId);
- break;
- case NodeRole::OutPortCount:
- _outPorts[nodeId] = value.value<unsigned int>();
- Q_EMIT nodeCreated(nodeId);
- break;
- case NodeRole::Widget:
- _widgetData[nodeId] = value.value<QWidget *>();
- Q_EMIT nodeCreated(nodeId);
- return true;
- break;
- }
- return result;
- }
- QVariant DataFlowGraphModel::portData(NodeId nodeId, PortType portType, PortIndex portIndex, PortRole role) const
- {
- QVariant result;
- auto it = _models.find(nodeId);
- if (it == _models.end())
- return result;
- auto &model = it->second;
- switch (role) {
- case PortRole::Data:
- if (portType == PortType::Out)
- result = QVariant::fromValue(model->outData(portIndex));
- break;
- case PortRole::DataType:
- result = QVariant::fromValue(model->dataType(portType, portIndex));
- break;
- case PortRole::ConnectionPolicyRole:
- result = QVariant::fromValue(model->portConnectionPolicy(portType, portIndex));
- break;
- case PortRole::CaptionVisible:
- result = model->portCaptionVisible(portType, portIndex);
- break;
- case PortRole::Caption:
- result = model->portCaption(portType, portIndex);
- break;
- }
- return result;
- }
- bool DataFlowGraphModel::setPortData(NodeId nodeId, PortType portType, PortIndex portIndex, QVariant const &value,
- PortRole role)
- {
- Q_UNUSED(nodeId);
- QVariant result;
- auto it = _models.find(nodeId);
- if (it == _models.end())
- return false;
- auto &model = it->second;
- switch (role) {
- case PortRole::Data:
- if (portType == PortType::In) {
- model->setInData(value.value<std::shared_ptr<NodeData>>(), portIndex);
- // Triggers repainting on the scene.
- Q_EMIT inPortDataWasSet(nodeId, portType, portIndex);
- }
- break;
- default:
- break;
- }
- return false;
- }
- bool DataFlowGraphModel::deleteConnection(ConnectionId const connectionId)
- {
- bool disconnected = false;
- auto it = _connectivity.find(connectionId);
- if (it != _connectivity.end()) {
- disconnected = true;
- _connectivity.erase(it);
- }
- if (disconnected) {
- sendConnectionDeletion(connectionId);
- propagateEmptyDataTo(getNodeId(PortType::In, connectionId), getPortIndex(PortType::In, connectionId));
- }
- return disconnected;
- }
- bool DataFlowGraphModel::deleteNode(NodeId const nodeId)
- {
- // Delete connections to this node first.
- auto connectionIds = allConnectionIds(nodeId);
- for (auto &cId : connectionIds) {
- deleteConnection(cId);
- }
- _nodeGeometryData.erase(nodeId);
- _models.erase(nodeId);
- Q_EMIT nodeDeleted(nodeId);
- return true;
- }
- QJsonObject DataFlowGraphModel::saveNode(NodeId const nodeId) const
- {
- QJsonObject nodeJson;
- nodeJson["id"] = static_cast<qint64>(nodeId);
- nodeJson["internal-data"] = _models.at(nodeId)->save();
- {
- QPointF const pos = nodeData(nodeId, NodeRole::Position).value<QPointF>();
- QJsonObject posJson;
- posJson["x"] = pos.x();
- posJson["y"] = pos.y();
- nodeJson["position"] = posJson;
- }
- return nodeJson;
- }
- QJsonObject DataFlowGraphModel::save() const
- {
- QJsonObject sceneJson;
- QJsonArray nodesJsonArray;
- for (auto const nodeId : allNodeIds()) {
- nodesJsonArray.append(saveNode(nodeId));
- }
- sceneJson["nodes"] = nodesJsonArray;
- QJsonArray connJsonArray;
- for (auto const &cid : _connectivity) {
- connJsonArray.append(toJson(cid));
- }
- sceneJson["connections"] = connJsonArray;
- return sceneJson;
- }
- void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson)
- {
- // Possibility of the id clash when reading it from json and not generating a
- // new value.
- // 1. When restoring a scene from a file.
- // Conflict is not possible because the scene must be cleared by the time of
- // loading.
- // 2. When undoing the deletion command. Conflict is not possible
- // because all the new ids were created past the removed nodes.
- NodeId restoredNodeId = nodeJson["id"].toInt();
- _nextNodeId = std::max(_nextNodeId, restoredNodeId + 1);
- QJsonObject const internalDataJson = nodeJson["internal-data"].toObject();
- QString delegateModelName = internalDataJson["model-name"].toString();
- std::unique_ptr<NodeDelegateModel> model = _registry->create(delegateModelName);
- if (model) {
- connect(model.get(), &NodeDelegateModel::dataUpdated,
- [restoredNodeId, this](PortIndex const portIndex) { onOutPortDataUpdated(restoredNodeId, portIndex); });
- _models[restoredNodeId] = std::move(model);
- Q_EMIT nodeCreated(restoredNodeId);
- QJsonObject posJson = nodeJson["position"].toObject();
- QPointF const pos(posJson["x"].toDouble(), posJson["y"].toDouble());
- setNodeData(restoredNodeId, NodeRole::Position, pos);
- _models[restoredNodeId]->load(internalDataJson);
- } else {
- throw std::logic_error(std::string("No registered model with name ") + delegateModelName.toLocal8Bit().data());
- }
- }
- void DataFlowGraphModel::load(QJsonObject const &jsonDocument)
- {
- QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray();
- for (QJsonValueRef nodeJson : nodesJsonArray) {
- loadNode(nodeJson.toObject());
- }
- QJsonArray connectionJsonArray = jsonDocument["connections"].toArray();
- for (QJsonValueRef connection : connectionJsonArray) {
- QJsonObject connJson = connection.toObject();
- ConnectionId connId = fromJson(connJson);
- // Restore the connection
- addConnection(connId);
- }
- }
- void DataFlowGraphModel::onOutPortDataUpdated(NodeId const nodeId, PortIndex const portIndex)
- {
- std::unordered_set<ConnectionId> const &connected = connections(nodeId, PortType::Out, portIndex);
- QVariant const portDataToPropagate = portData(nodeId, PortType::Out, portIndex, PortRole::Data);
- for (auto const &cn : connected) {
- setPortData(cn.inNodeId, PortType::In, cn.inPortIndex, portDataToPropagate, PortRole::Data);
- }
- }
- void DataFlowGraphModel::propagateEmptyDataTo(NodeId const nodeId, PortIndex const portIndex)
- {
- QVariant emptyData {};
- setPortData(nodeId, PortType::In, portIndex, emptyData, PortRole::Data);
- }
- } // namespace QtNodes
|