From 31f366ab8f71e35034b468a1d4305ef28a152640 Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Fri, 1 Apr 2016 15:26:44 +0200 Subject: [PATCH] ui: Increased graph edge drawing performance * only draw lines within the redrawn rectangle * cache polygon points --- src/lib_gui/qt/graphics/QtAngledLineItem.cpp | 244 +++++++++++++----- src/lib_gui/qt/graphics/QtAngledLineItem.h | 5 + src/lib_gui/qt/view/QtGraphView.cpp | 5 +- .../qt/view/graphElements/QtGraphEdge.cpp | 11 +- .../qt/view/graphElements/QtGraphEdge.h | 8 +- 5 files changed, 198 insertions(+), 75 deletions(-) diff --git a/src/lib_gui/qt/graphics/QtAngledLineItem.cpp b/src/lib_gui/qt/graphics/QtAngledLineItem.cpp index e1ca0735..caef62f7 100644 --- a/src/lib_gui/qt/graphics/QtAngledLineItem.cpp +++ b/src/lib_gui/qt/graphics/QtAngledLineItem.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "utility/math/Vector2.h" @@ -12,6 +13,7 @@ QtAngledLineItem::QtAngledLineItem(QGraphicsItem* parent) , m_horizontalIn(false) { this->setAcceptHoverEvents(true); + this->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true); } QtAngledLineItem::~QtAngledLineItem() @@ -24,6 +26,7 @@ void QtAngledLineItem::updateLine( GraphViewStyle::EdgeStyle style ){ prepareGeometryChange(); + m_polygon.clear(); m_ownerRect = ownerRect; m_targetRect = targetRect; @@ -52,13 +55,16 @@ void QtAngledLineItem::setHorizontalIn(bool horizontal) QPainterPath QtAngledLineItem::shape() const { - int w = m_style.arrowWidth / 2 + 1; QPainterPath path; QPolygon poly = getPath(); + for (int i = 0; i < poly.size() - 1; i++) { - path.addRect(QRectF(poly.at(i), poly.at(i + 1)).normalized().adjusted(-w, -w, w, w)); + path.addRect(QRectF(poly.at(i), poly.at(i + 1)).normalized().adjusted(-3, -3, 3, 3)); } + + path.addRect(getArrowBoundingRect(poly).adjusted(-3, -3, 3, 3)); + return path; } @@ -73,6 +79,9 @@ void QtAngledLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem* path.moveTo(poly.at(i)); + QRectF drawRect = options->exposedRect; + QRectF partRect; + int radius = m_style.cornerRadius; int dir = getDirection(poly.at(i), poly.at(i - 1)); while (i > 1) @@ -115,85 +124,96 @@ void QtAngledLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem* case 3: b.setX(b.x() - br); break; } - switch (dir) + partRect = QRectF(poly.at(i + 1), poly.at(i)).adjusted(-1, -1, 1, 1); + if (drawRect.intersects(partRect)) { - case 0: - if (newDir == 1) + path.lineTo(a); + } + else + { + path.moveTo(a); + } + + partRect = QRectF(a, b).normalized().adjusted(-1, -1, 1, 1); + if (drawRect.intersects(partRect)) + { + switch (dir) { - path.arcTo(a.x(), b.y(), 2 * br, 2 * ar, 180, -90); + case 0: + if (newDir == 1) + { + path.arcTo(a.x(), b.y(), 2 * br, 2 * ar, 180, -90); + } + else if (newDir == 3) + { + path.arcTo(b.x() - br, a.y() - ar, 2 * br, 2 * ar, 0, 90); + } + break; + case 1: + if (newDir == 0) + { + path.arcTo(a.x() - ar, b.y() - br, 2 * ar, 2 * br, -90, 90); + } + else if (newDir == 2) + { + path.arcTo(a.x() - ar, a.y(), 2 * ar, 2 * br, 90, -90); + } + break; + case 2: + if (newDir == 1) + { + path.arcTo(a.x(), a.y() - ar, 2 * br, 2 * ar, 180, 90); + } + else if (newDir == 3) + { + path.arcTo(b.x() - br, a.y() - ar, 2 * br, 2 * ar, 0, -90); + } + break; + case 3: + if (newDir == 0) + { + path.arcTo(b.x(), b.y() - br, 2 * ar, 2 * br, -90, -90); + } + else if (newDir == 2) + { + path.arcTo(b.x(), a.y(), 2 * ar, 2 * br, 90, 90); + } + break; } - else if (newDir == 3) - { - path.arcTo(b.x() - br, a.y() - ar, 2 * br, 2 * ar, 0, 90); - } - break; - case 1: - if (newDir == 0) - { - path.arcTo(a.x() - ar, b.y() - br, 2 * ar, 2 * br, -90, 90); - } - else if (newDir == 2) - { - path.arcTo(a.x() - ar, a.y(), 2 * ar, 2 * br, 90, -90); - } - break; - case 2: - if (newDir == 1) - { - path.arcTo(a.x(), a.y() - ar, 2 * br, 2 * ar, 180, 90); - } - else if (newDir == 3) - { - path.arcTo(b.x() - br, a.y() - ar, 2 * br, 2 * ar, 0, -90); - } - break; - case 3: - if (newDir == 0) - { - path.arcTo(b.x(), b.y() - br, 2 * ar, 2 * br, -90, -90); - } - else if (newDir == 2) - { - path.arcTo(b.x(), a.y(), 2 * ar, 2 * br, 90, 90); - } - break; + } + else + { + path.moveTo(b); } dir = newDir; radius = br; } - QPointF arrow = poly.at(0) + QPointF((poly.at(0).x() - poly.at(1).x() > 0 ? -1 : 1) * m_style.arrowLength, 0); - - if (m_style.arrowClosed) + partRect = QRectF(poly.at(0), poly.at(1)).adjusted(-1, -1, 1, 1); + if (drawRect.intersects(partRect)) { - path.lineTo(arrow); - path.moveTo(poly.at(0)); + partRect = getArrowBoundingRect(poly); + if (drawRect.intersects(partRect)) + { + drawArrow(poly, &path); + } + else + { + path.lineTo(poly.at(0)); + } } - else - { - path.lineTo(poly.at(0)); - } - - arrow.setY(arrow.y() - m_style.arrowWidth / 2); - path.lineTo(arrow); - arrow.setY(arrow.y() + m_style.arrowWidth); - - if (m_style.arrowClosed) - { - path.lineTo(arrow); - } - else - { - path.moveTo(arrow); - } - path.lineTo(poly.at(0)); painter->drawPath(path); } QPolygon QtAngledLineItem::getPath() const { + if (m_polygon.size() > 0) + { + return m_polygon; + } + const Vec4i& oR = m_ownerRect; const Vec4i& tR = m_targetRect; @@ -287,6 +307,9 @@ QPolygon QtAngledLineItem::getPath() const poly << tp; poly << op; poly << QPoint(o[io].x, o[io].y + m_style.originOffset.y); + + m_polygon = poly; + return poly; } @@ -296,22 +319,107 @@ int QtAngledLineItem::getDirection(const QPointF& a, const QPointF& b) const { if (a.x() < b.x()) { - return 1; + return 1; // right } else { - return 3; + return 3; // left } } else { if (a.y() < b.y()) { - return 2; + return 2; // down } else { - return 0; + return 0; // up } } } + +QRectF QtAngledLineItem::getArrowBoundingRect(const QPolygon& poly) const +{ + int dir = getDirection(poly.at(1), poly.at(0)); + + QRectF rect( + poly.at(0).x(), + poly.at(0).y(), + (dir % 2 == 1 ? m_style.arrowLength : m_style.arrowWidth), + (dir % 2 == 0 ? m_style.arrowLength : m_style.arrowWidth) + ); + + switch (dir) + { + case 0: + rect.moveLeft(rect.left() - rect.width() / 2); + break; + case 1: + rect.moveLeft(rect.left() - rect.width()); + rect.moveTop(rect.top() - rect.height() / 2); + break; + case 2: + rect.moveLeft(rect.left() - rect.width() / 2); + rect.moveTop(rect.top() - rect.height()); + break; + case 3: + rect.moveTop(rect.top() - rect.height() / 2); + break; + } + + return rect; +} + +void QtAngledLineItem::drawArrow(const QPolygon& poly, QPainterPath* path) const +{ + int dir = getDirection(poly.at(1), poly.at(0)); + + QPointF tip = poly.at(0); + QPointF toBack; + QPointF toLeft; + + switch (dir) + { + case 0: + toBack.setY(m_style.arrowLength); + toLeft.setX(-m_style.arrowWidth / 2); + break; + case 1: + toBack.setX(-m_style.arrowLength); + toLeft.setY(-m_style.arrowWidth / 2); + break; + case 2: + toBack.setY(-m_style.arrowLength); + toLeft.setX(m_style.arrowWidth / 2); + break; + case 3: + toBack.setX(m_style.arrowLength); + toLeft.setY(m_style.arrowWidth / 2); + break; + } + + + if (m_style.arrowClosed) + { + path->lineTo(tip + toBack); + path->moveTo(tip); + } + else + { + path->lineTo(tip); + } + + path->lineTo(tip + toBack + toLeft); + + if (m_style.arrowClosed) + { + path->lineTo(tip + toBack - toLeft); + } + else + { + path->moveTo(tip + toBack - toLeft); + } + + path->lineTo(tip); +} diff --git a/src/lib_gui/qt/graphics/QtAngledLineItem.h b/src/lib_gui/qt/graphics/QtAngledLineItem.h index d98ff0bb..e7b36d97 100644 --- a/src/lib_gui/qt/graphics/QtAngledLineItem.h +++ b/src/lib_gui/qt/graphics/QtAngledLineItem.h @@ -29,6 +29,9 @@ private: QPolygon getPath() const; int getDirection(const QPointF& a, const QPointF& b) const; + QRectF getArrowBoundingRect(const QPolygon& poly) const; + void drawArrow(const QPolygon& poly, QPainterPath* path) const; + Vec4i m_ownerRect; Vec4i m_targetRect; @@ -39,6 +42,8 @@ private: bool m_onBack; bool m_horizontalIn; + + mutable QPolygon m_polygon; }; #endif // QT_ANGLED_LINE_ITEM_H diff --git a/src/lib_gui/qt/view/QtGraphView.cpp b/src/lib_gui/qt/view/QtGraphView.cpp index 35dc4c17..add9b2fd 100644 --- a/src/lib_gui/qt/view/QtGraphView.cpp +++ b/src/lib_gui/qt/view/QtGraphView.cpp @@ -365,9 +365,8 @@ std::shared_ptr QtGraphView::createEdge(QGraphicsView* view, const if (owner != NULL && target != NULL) { - std::shared_ptr qtEdge = std::make_shared(owner, target, edge.data, edge.getWeight()); - qtEdge->setIsActive(edge.active); - qtEdge->setDirection(edge.getDirection()); + std::shared_ptr qtEdge = + std::make_shared(owner, target, edge.data, edge.getWeight(), edge.active, edge.getDirection()); owner->addOutEdge(qtEdge); target->addInEdge(qtEdge); diff --git a/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp b/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp index c3ee3603..4ae034ae 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp +++ b/src/lib_gui/qt/view/graphElements/QtGraphEdge.cpp @@ -15,17 +15,22 @@ #include "qt/view/graphElements/QtGraphNode.h" QtGraphEdge::QtGraphEdge( - const std::weak_ptr& owner, const std::weak_ptr& target, const Edge* data, size_t weight + const std::weak_ptr& owner, + const std::weak_ptr& target, + const Edge* data, + size_t weight, + bool isActive, + TokenComponentAggregation::Direction direction ) : m_data(data) , m_owner(owner) , m_target(target) , m_child(nullptr) - , m_isActive(false) + , m_isActive(isActive) , m_fromActive(false) , m_toActive(false) , m_weight(weight) - , m_direction(TokenComponentAggregation::DIRECTION_NONE) + , m_direction(direction) , m_mousePos(0.0f, 0.0f) , m_mouseMoved(false) { diff --git a/src/lib_gui/qt/view/graphElements/QtGraphEdge.h b/src/lib_gui/qt/view/graphElements/QtGraphEdge.h index 13282936..d886bbe8 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphEdge.h +++ b/src/lib_gui/qt/view/graphElements/QtGraphEdge.h @@ -20,7 +20,13 @@ class QtGraphEdge Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) public: - QtGraphEdge(const std::weak_ptr& owner, const std::weak_ptr& target, const Edge* data, size_t weight); + QtGraphEdge( + const std::weak_ptr& owner, + const std::weak_ptr& target, + const Edge* data, + size_t weight, + bool isActive, + TokenComponentAggregation::Direction direction); virtual ~QtGraphEdge(); const Edge* getData() const;