#include "NodeGeometry.hpp" #include #include #include "PortType.hpp" #include "NodeState.hpp" #include "NodeDataModel.hpp" #include "Node.hpp" #include "NodeGraphicsObject.hpp" #include "StyleCollection.hpp" using QtNodes::NodeGeometry; using QtNodes::NodeDataModel; using QtNodes::PortIndex; using QtNodes::PortType; using QtNodes::Node; NodeGeometry:: NodeGeometry(std::unique_ptr const &dataModel) : _width(100) , _height(150) , _inputPortWidth(70) , _outputPortWidth(70) , _entryHeight(20) , _spacing(8) , _hovered(false) , _nSources(dataModel->nPorts(PortType::Out)) , _nSinks(dataModel->nPorts(PortType::In)) , _draggingPos(-1000, -1000) , _dataModel(dataModel) , _fontMetrics(QFont()) , _boldFontMetrics(QFont()) { QFont f; f.setBold(true); _boldFontMetrics = QFontMetrics(f); } unsigned int NodeGeometry::nSources() const { return _dataModel->nPorts(PortType::Out); } unsigned int NodeGeometry::nSinks() const { return _dataModel->nPorts(PortType::In); } QRectF NodeGeometry:: entryBoundingRect() const { double const addon = 0.0; return QRectF(0 - addon, 0 - addon, _entryWidth + 2 * addon, _entryHeight + 2 * addon); } QRectF NodeGeometry:: boundingRect() const { auto const &nodeStyle = StyleCollection::nodeStyle(); double addon = 4 * nodeStyle.ConnectionPointDiameter; return QRectF(0 - addon, 0 - addon, _width + 2 * addon, _height + 2 * addon); } void NodeGeometry:: recalculateSize() const { _entryHeight = _fontMetrics.height(); { unsigned int maxNumOfEntries = std::max(_nSinks, _nSources); unsigned int step = _entryHeight + _spacing; _height = step * maxNumOfEntries; } if (auto w = _dataModel->embeddedWidget()) { _height = std::max(_height, static_cast(w->height())); } _height += captionHeight(); _inputPortWidth = portWidth(PortType::In); _outputPortWidth = portWidth(PortType::Out); _width = _inputPortWidth + _outputPortWidth + 2 * _spacing; if (auto w = _dataModel->embeddedWidget()) { _width += w->width(); } _width = std::max(_width, captionWidth()); if (_dataModel->validationState() != NodeValidationState::Valid) { _width = std::max(_width, validationWidth()); _height += validationHeight() + _spacing; } } void NodeGeometry:: recalculateSize(QFont const & font) const { QFontMetrics fontMetrics(font); QFont boldFont = font; boldFont.setBold(true); QFontMetrics boldFontMetrics(boldFont); if (_boldFontMetrics != boldFontMetrics) { _fontMetrics = fontMetrics; _boldFontMetrics = boldFontMetrics; recalculateSize(); } } QPointF NodeGeometry:: portScenePosition(PortIndex index, PortType portType, QTransform const & t) const { auto const &nodeStyle = StyleCollection::nodeStyle(); unsigned int step = _entryHeight + _spacing; QPointF result; double totalHeight = 0.0; totalHeight += captionHeight(); totalHeight += step * index; // TODO: why? totalHeight += step / 2.0; switch (portType) { case PortType::Out: { double x = _width + nodeStyle.ConnectionPointDiameter; result = QPointF(x, totalHeight); break; } case PortType::In: { double x = 0.0 - nodeStyle.ConnectionPointDiameter; result = QPointF(x, totalHeight); break; } default: break; } return t.map(result); } PortIndex NodeGeometry:: checkHitScenePoint(PortType portType, QPointF const scenePoint, QTransform const & sceneTransform) const { auto const &nodeStyle = StyleCollection::nodeStyle(); PortIndex result = INVALID; if (portType == PortType::None) return result; double const tolerance = 2.0 * nodeStyle.ConnectionPointDiameter; unsigned int const nItems = _dataModel->nPorts(portType); for (unsigned int i = 0; i < nItems; ++i) { auto pp = portScenePosition(i, portType, sceneTransform); QPointF p = pp - scenePoint; auto distance = std::sqrt(QPointF::dotProduct(p, p)); if (distance < tolerance) { result = PortIndex(i); break; } } return result; } QRect NodeGeometry:: resizeRect() const { unsigned int rectSize = 7; return QRect(_width - rectSize, _height - rectSize, rectSize, rectSize); } QPointF NodeGeometry:: widgetPosition() const { if (auto w = _dataModel->embeddedWidget()) { if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) { // If the widget wants to use as much vertical space as possible, place it immediately after the caption. return QPointF(_spacing + portWidth(PortType::In), captionHeight()); } else { if (_dataModel->validationState() != NodeValidationState::Valid) { return QPointF(_spacing + portWidth(PortType::In), (captionHeight() + _height - validationHeight() - _spacing - w->height()) / 2.0); } return QPointF(_spacing + portWidth(PortType::In), (captionHeight() + _height - w->height()) / 2.0); } } return QPointF(); } int NodeGeometry:: equivalentWidgetHeight() const { if (_dataModel->validationState() != NodeValidationState::Valid) { return height() - captionHeight() + validationHeight(); } return height() - captionHeight(); } unsigned int NodeGeometry:: captionHeight() const { if (!_dataModel->captionVisible()) return 0; QString name = _dataModel->caption(); return _boldFontMetrics.boundingRect(name).height(); } unsigned int NodeGeometry:: captionWidth() const { if (!_dataModel->captionVisible()) return 0; QString name = _dataModel->caption(); return _boldFontMetrics.boundingRect(name).width(); } unsigned int NodeGeometry:: validationHeight() const { QString msg = _dataModel->validationMessage(); return _boldFontMetrics.boundingRect(msg).height(); } unsigned int NodeGeometry:: validationWidth() const { QString msg = _dataModel->validationMessage(); return _boldFontMetrics.boundingRect(msg).width(); } QPointF NodeGeometry:: calculateNodePositionBetweenNodePorts(PortIndex targetPortIndex, PortType targetPort, Node* targetNode, PortIndex sourcePortIndex, PortType sourcePort, Node* sourceNode, Node& newNode) { //Calculating the nodes position in the scene. It'll be positioned half way between the two ports that it "connects". //The first line calculates the halfway point between the ports (node position + port position on the node for both nodes averaged). //The second line offsets this coordinate with the size of the new node, so that the new nodes center falls on the originally //calculated coordinate, instead of it's upper left corner. auto converterNodePos = (sourceNode->nodeGraphicsObject().pos() + sourceNode->nodeGeometry().portScenePosition(sourcePortIndex, sourcePort) + targetNode->nodeGraphicsObject().pos() + targetNode->nodeGeometry().portScenePosition(targetPortIndex, targetPort)) / 2.0f; converterNodePos.setX(converterNodePos.x() - newNode.nodeGeometry().width() / 2.0f); converterNodePos.setY(converterNodePos.y() - newNode.nodeGeometry().height() / 2.0f); return converterNodePos; } unsigned int NodeGeometry:: portWidth(PortType portType) const { unsigned width = 0; for (auto i = 0ul; i < _dataModel->nPorts(portType); ++i) { QString name; if (_dataModel->portCaptionVisible(portType, i)) { name = _dataModel->portCaption(portType, i); } else { name = _dataModel->dataType(portType, i).name; } width = std::max(static_cast(_fontMetrics.horizontalAdvance(name)), width); } return width; }