See More

#include "NodeConnectionInteraction.hpp" #include "ConnectionGraphicsObject.hpp" #include "NodeGraphicsObject.hpp" #include "NodeDataModel.hpp" #include "DataModelRegistry.hpp" #include "FlowScene.hpp" using QtNodes::NodeConnectionInteraction; using QtNodes::PortType; using QtNodes::PortIndex; using QtNodes::FlowScene; using QtNodes::Node; using QtNodes::Connection; using QtNodes::NodeDataModel; NodeConnectionInteraction:: NodeConnectionInteraction(Node& node, Connection& connection, FlowScene& scene) : _node(&node) , _connection(&connection) , _scene(&scene) {} bool NodeConnectionInteraction:: canConnect(PortIndex &portIndex, bool& typeConversionNeeded, std::unique_ptr & converterModel) const { typeConversionNeeded = false; // 1) Connection requires a port PortType requiredPort = connectionRequiredPort(); if (requiredPort == PortType::None) { return false; } // 2) connection point is on top of the node port QPointF connectionPoint = connectionEndScenePosition(requiredPort); portIndex = nodePortIndexUnderScenePoint(requiredPort, connectionPoint); if (portIndex == INVALID) { return false; } // 3) Node port is vacant // port should be empty if (!nodePortIsEmpty(requiredPort, portIndex)) return false; // 4) Connection type equals node port type, or there is a registered type conversion that can translate between the two auto connectionDataType = _connection->dataType(); auto const &modelTarget = _node->nodeDataModel(); NodeDataType candidateNodeDataType = modelTarget->dataType(requiredPort, portIndex); if (connectionDataType.id != candidateNodeDataType.id) { if (requiredPort == PortType::In) { return typeConversionNeeded = (converterModel = _scene->registry().getTypeConverter(connectionDataType.id, candidateNodeDataType.id)) != nullptr; } return typeConversionNeeded = (converterModel = _scene->registry().getTypeConverter(candidateNodeDataType.id, connectionDataType.id)) != nullptr; } return true; } bool NodeConnectionInteraction:: tryConnect() const { // 1) Check conditions from 'canConnect' PortIndex portIndex = INVALID; bool typeConversionNeeded = false; std::unique_ptr typeConverterModel; if (!canConnect(portIndex, typeConversionNeeded, typeConverterModel)) { return false; } /// 1.5) If the connection is possible but a type conversion is needed, add a converter node to the scene, and connect it properly if (typeConversionNeeded) { //Determining port types PortType requiredPort = connectionRequiredPort(); PortType connectedPort = requiredPort == PortType::Out ? PortType::In : PortType::Out; //Get the node and port from where the connection starts auto outNode = _connection->getNode(connectedPort); auto outNodePortIndex = _connection->getPortIndex(connectedPort); //Creating the converter node Node& converterNode = _scene->createNode(std::move(typeConverterModel)); //Calculate and set the converter node's position auto converterNodePos = NodeGeometry::calculateNodePositionBetweenNodePorts(portIndex, requiredPort, _node, outNodePortIndex, connectedPort, outNode, converterNode); converterNode.nodeGraphicsObject().setPos(converterNodePos); //Connecting the converter node to the two nodes trhat originally supposed to be connected. //The connection order is different based on if the users connection was started from an input port, or an output port. if (requiredPort == PortType::In) { _scene->createConnection(converterNode, 0, *outNode, outNodePortIndex); _scene->createConnection(*_node, portIndex, converterNode, 0); } else { _scene->createConnection(converterNode, 0, *_node, portIndex); _scene->createConnection(*outNode, outNodePortIndex, converterNode, 0); } //Delete the users connection, we already replaced it. _scene->deleteConnection(*_connection); return true; } // 2) Assign node to required port in Connection PortType requiredPort = connectionRequiredPort(); _node->nodeState().setConnection(requiredPort, portIndex, *_connection); // 3) Assign Connection to empty port in NodeState // The port is not longer required after this function _connection->setNodeToPort(*_node, requiredPort, portIndex); // 4) Adjust Connection geometry _node->nodeGraphicsObject().moveConnections(); // 5) Poke model to intiate data transfer auto outNode = _connection->getNode(PortType::Out); if (outNode) { PortIndex outPortIndex = _connection->getPortIndex(PortType::Out); outNode->onDataUpdated(outPortIndex); } return true; } /// 1) Node and Connection should be already connected /// 2) If so, clear Connection entry in the NodeState /// 3) Set Connection end to 'requiring a port' bool NodeConnectionInteraction:: disconnect(PortType portToDisconnect) const { PortIndex portIndex = _connection->getPortIndex(portToDisconnect); NodeState &state = _node->nodeState(); // clear pointer to Connection in the NodeState state.getEntries(portToDisconnect)[portIndex].clear(); // 4) Propagate invalid data to IN node _connection->propagateEmptyData(); // clear Connection side _connection->clearNode(portToDisconnect); _connection->setRequiredPort(portToDisconnect); _connection->getConnectionGraphicsObject().grabMouse(); return true; } // ------------------ util functions below PortType NodeConnectionInteraction:: connectionRequiredPort() const { auto const &state = _connection->connectionState(); return state.requiredPort(); } QPointF NodeConnectionInteraction:: connectionEndScenePosition(PortType portType) const { auto &go = _connection->getConnectionGraphicsObject(); ConnectionGeometry& geometry = _connection->connectionGeometry(); QPointF endPoint = geometry.getEndPoint(portType); return go.mapToScene(endPoint); } QPointF NodeConnectionInteraction:: nodePortScenePosition(PortType portType, PortIndex portIndex) const { NodeGeometry const &geom = _node->nodeGeometry(); QPointF p = geom.portScenePosition(portIndex, portType); NodeGraphicsObject& ngo = _node->nodeGraphicsObject(); return ngo.sceneTransform().map(p); } PortIndex NodeConnectionInteraction:: nodePortIndexUnderScenePoint(PortType portType, QPointF const & scenePoint) const { NodeGeometry const &nodeGeom = _node->nodeGeometry(); QTransform sceneTransform = _node->nodeGraphicsObject().sceneTransform(); PortIndex portIndex = nodeGeom.checkHitScenePoint(portType, scenePoint, sceneTransform); return portIndex; } bool NodeConnectionInteraction:: nodePortIsEmpty(PortType portType, PortIndex portIndex) const { NodeState const & nodeState = _node->nodeState(); auto const & entries = nodeState.getEntries(portType); if (entries[portIndex].empty()) return true; const auto outPolicy = _node->nodeDataModel()->portOutConnectionPolicy(portIndex); return ( portType == PortType::Out && outPolicy == NodeDataModel::ConnectionPolicy::Many); }