From d6269f9608fc0d0871ed0b77df4e0fe49eb61b4e Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Wed, 17 Jun 2015 22:47:08 +0200 Subject: [PATCH] ui: bundling nodes in the graph view This change bundles similar nodes in the graph view together. When the bundled node or it's aggregation edge are clicked, then the bundle is split. This change also adds an arrow to the aggregation edge based on the direction of it's edges. --- bin/app/data/src/sample/samples.cpp | 38 +++ src/app/CMakeLists.txt | 2 + src/app/qt/element/QtCodeArea.cpp | 10 +- src/app/qt/graphics/QtStraightLineItem.cpp | 47 ++- src/app/qt/graphics/QtStraightLineItem.h | 5 +- src/app/qt/view/QtGraphView.cpp | 33 +- src/app/qt/view/graphElements/QtGraphEdge.cpp | 59 +++- src/app/qt/view/graphElements/QtGraphEdge.h | 15 +- src/app/qt/view/graphElements/QtGraphNode.cpp | 5 + src/app/qt/view/graphElements/QtGraphNode.h | 1 + .../view/graphElements/QtGraphNodeBundle.cpp | 71 +++++ .../qt/view/graphElements/QtGraphNodeBundle.h | 29 ++ src/lib/CMakeLists.txt | 1 + .../component/controller/GraphController.cpp | 297 ++++++++++++++++-- .../component/controller/GraphController.h | 13 +- .../component/controller/helper/DummyEdge.h | 54 +++- .../component/controller/helper/DummyNode.h | 60 +++- src/lib/component/view/GraphViewStyle.cpp | 20 +- src/lib/component/view/GraphViewStyle.h | 2 + src/lib/data/Storage.cpp | 9 +- src/lib/data/graph/Node.cpp | 16 +- src/lib/data/graph/Node.h | 6 +- src/lib/data/graph/StorageGraph.cpp | 3 +- .../TokenComponentAggregation.cpp | 59 +++- .../TokenComponentAggregation.h | 20 +- src/lib/utility/Property.h | 9 +- src/lib/utility/math/VectorBase.h | 5 +- .../messaging/type/MessageActivateTokens.h | 4 +- .../type/MessageGraphNodeBundleSplit.h | 24 ++ src/lib/utility/utility.cpp | 13 + src/lib/utility/utility.h | 2 + 31 files changed, 834 insertions(+), 98 deletions(-) create mode 100644 src/app/qt/view/graphElements/QtGraphNodeBundle.cpp create mode 100644 src/app/qt/view/graphElements/QtGraphNodeBundle.h create mode 100644 src/lib/utility/messaging/type/MessageGraphNodeBundleSplit.h diff --git a/bin/app/data/src/sample/samples.cpp b/bin/app/data/src/sample/samples.cpp index 96ea4cb0..ab0167fc 100644 --- a/bin/app/data/src/sample/samples.cpp +++ b/bin/app/data/src/sample/samples.cpp @@ -121,3 +121,41 @@ public: Basket apples; Basket pears; + + +class Dog {}; + +class Cat +{ + Dog getDog(); +}; + +class Bird +{ + Dog getDog(); + Cat getCat(); +}; + +class Fish +{ + Dog getDog(); + Cat getCat(); + Bird getBird(); +}; + +class Horse + : public Dog +{ + Dog getDog(); + Cat getCat(); + Bird getBird(); + Fish getFish(); +}; + + +class Building {}; +class House : public Building {}; +class Tower : public Building {}; +class SkyScrapper : public Building {}; +class Mansion : public Building {}; +class Shard : public House, public Tower, public SkyScrapper, public Mansion {}; diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index dafae3d9..868937dc 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -59,6 +59,8 @@ add_files( qt/view/graphElements/QtGraphNode.h qt/view/graphElements/QtGraphNodeAccess.cpp qt/view/graphElements/QtGraphNodeAccess.h + qt/view/graphElements/QtGraphNodeBundle.cpp + qt/view/graphElements/QtGraphNodeBundle.h qt/view/graphElements/QtGraphNodeData.cpp qt/view/graphElements/QtGraphNodeData.h qt/view/graphElements/QtGraphNodeExpandToggle.cpp diff --git a/src/app/qt/element/QtCodeArea.cpp b/src/app/qt/element/QtCodeArea.cpp index 55de5c1a..bdfb14e2 100644 --- a/src/app/qt/element/QtCodeArea.cpp +++ b/src/app/qt/element/QtCodeArea.cpp @@ -10,6 +10,7 @@ #include "utility/messaging/type/MessageShowFile.h" #include "utility/messaging/type/MessageFocusIn.h" #include "utility/messaging/type/MessageFocusOut.h" +#include "utility/utility.h" #include "data/location/TokenLocation.h" #include "data/location/TokenLocationFile.h" @@ -121,15 +122,8 @@ void QtCodeArea::lineNumberAreaPaintEvent(QPaintEvent *event) int QtCodeArea::lineNumberDigits() const { - int digits = 1; int max = qMax(1, int(m_startLineNumber) + blockCount()); - - while (max >= 10) - { - max /= 10; - digits++; - } - return digits; + return utility::digits(max); } int QtCodeArea::lineNumberAreaWidth() const diff --git a/src/app/qt/graphics/QtStraightLineItem.cpp b/src/app/qt/graphics/QtStraightLineItem.cpp index 2e22fcbc..2a567a5d 100644 --- a/src/app/qt/graphics/QtStraightLineItem.cpp +++ b/src/app/qt/graphics/QtStraightLineItem.cpp @@ -26,14 +26,18 @@ QtStraightLineItem::QtStraightLineItem(QGraphicsItem* parent) m_number = new QGraphicsSimpleTextItem(this); m_number->setFont(font); + + m_arrowLeft = new QGraphicsLineItem(this); + m_arrowRight = new QGraphicsLineItem(this); } QtStraightLineItem::~QtStraightLineItem() { } -void QtStraightLineItem::updateLine(Vec4i ownerRect, Vec4i targetRect, int number, GraphViewStyle::EdgeStyle style) -{ +void QtStraightLineItem::updateLine( + Vec4i ownerRect, Vec4i targetRect, int number, GraphViewStyle::EdgeStyle style, bool showArrow +){ prepareGeometryChange(); const Vec4i& o = ownerRect; @@ -42,7 +46,6 @@ void QtStraightLineItem::updateLine(Vec4i ownerRect, Vec4i targetRect, int numbe Vec2f oc((o.x + o.z) / 2, (o.y + o.w) / 2); Vec2f tc((t.x + t.z) / 2, (t.y + t.w) / 2); - Vec2f mid; Vec2f op; Vec2f tp; @@ -58,6 +61,11 @@ void QtStraightLineItem::updateLine(Vec4i ownerRect, Vec4i targetRect, int numbe } } + if (!intersects) + { + op = oc; + } + intersects = false; for (int i = 0; !intersects && i < 4; i++) { @@ -70,12 +78,41 @@ void QtStraightLineItem::updateLine(Vec4i ownerRect, Vec4i targetRect, int numbe } } - mid = (op + tp) / 2; + if (!intersects) + { + tp = tc; + } this->setLine(oc.x, oc.y, tc.x, tc.y); + Vec2f mid = (op + tp) / 2; + + if (showArrow) + { + Vec2f unit = (tp - op).normalize(); + Vec2f nUnit(-unit.y, unit.x); + Vec2f arrow = mid + unit * 22; + + Vec2f arrowSide = mid + unit * 15 + nUnit * 7; + m_arrowRight->setLine(arrow.x, arrow.y, arrowSide.x, arrowSide.y); + + arrowSide -= nUnit * 14; + m_arrowLeft->setLine(arrow.x, arrow.y, arrowSide.x, arrowSide.y); + + m_arrowLeft->setPen(QPen(QBrush(QColor("#E0E0E0")), 2, Qt::SolidLine, Qt::RoundCap)); + m_arrowRight->setPen(QPen(QBrush(QColor("#E0E0E0")), 2, Qt::SolidLine, Qt::RoundCap)); + + m_arrowLeft->show(); + m_arrowRight->show(); + } + else + { + m_arrowLeft->hide(); + m_arrowRight->hide(); + } + m_circle->setRect(mid.x - 10, mid.y - 10, 20, 20); - m_circle->setPen(QPen(QColor(style.color.c_str()), 2)); + m_circle->setPen(QPen(QColor("#E0E0E0"), 2)); QString numberStr = QString::number(number); m_number->setText(numberStr); diff --git a/src/app/qt/graphics/QtStraightLineItem.h b/src/app/qt/graphics/QtStraightLineItem.h index 6f79934e..7d99ac60 100644 --- a/src/app/qt/graphics/QtStraightLineItem.h +++ b/src/app/qt/graphics/QtStraightLineItem.h @@ -16,11 +16,14 @@ public: QtStraightLineItem(QGraphicsItem* parent); virtual ~QtStraightLineItem(); - void updateLine(Vec4i ownerRect, Vec4i targetRect, int number, GraphViewStyle::EdgeStyle style); + void updateLine(Vec4i ownerRect, Vec4i targetRect, int number, GraphViewStyle::EdgeStyle style, bool showArrow); private: QtRoundedRectItem* m_circle; QGraphicsSimpleTextItem* m_number; + + QGraphicsLineItem* m_arrowLeft; + QGraphicsLineItem* m_arrowRight; }; #endif // QT_STRAIGHT_LINE_ITEM_H diff --git a/src/app/qt/view/QtGraphView.cpp b/src/app/qt/view/QtGraphView.cpp index 89ec8a8b..90833c16 100644 --- a/src/app/qt/view/QtGraphView.cpp +++ b/src/app/qt/view/QtGraphView.cpp @@ -19,8 +19,9 @@ #include "qt/view/graphElements/nodeComponents/QtGraphNodeComponentClickable.h" #include "qt/view/graphElements/nodeComponents/QtGraphNodeComponentMoveable.h" #include "qt/view/graphElements/QtGraphEdge.h" -#include "qt/view/graphElements/QtGraphNodeData.h" #include "qt/view/graphElements/QtGraphNodeAccess.h" +#include "qt/view/graphElements/QtGraphNodeBundle.h" +#include "qt/view/graphElements/QtGraphNodeData.h" #include "qt/view/graphElements/QtGraphNodeExpandToggle.h" QtGraphView::QtGraphView(ViewLayout* viewLayout) @@ -275,6 +276,10 @@ std::shared_ptr QtGraphView::createNodeRecursive( { newNode = std::make_shared(node.isExpanded(), node.invisibleSubNodeCount); } + else if (node.isBundleNode()) + { + newNode = std::make_shared(node.tokenId, node.bundledNodes.size(), node.name); + } newNode->setPosition(node.position); newNode->setSize(node.size); @@ -319,8 +324,9 @@ std::shared_ptr QtGraphView::createEdge(QGraphicsView* view, const if (owner != NULL && target != NULL) { - std::shared_ptr qtEdge = std::make_shared(owner, target, edge.data); + std::shared_ptr qtEdge = std::make_shared(owner, target, edge.data, edge.getWeight()); qtEdge->setIsActive(edge.active); + qtEdge->setDirection(edge.getDirection()); owner->addOutEdge(qtEdge); target->addInEdge(qtEdge); @@ -331,7 +337,7 @@ std::shared_ptr QtGraphView::createEdge(QGraphicsView* view, const } else { - LOG_WARNING_STREAM(<< "Couldn't find owner or target node for edge: " << edge.data->getName()); + LOG_WARNING_STREAM(<< "Couldn't find owner or target node for edge: " << (edge.data ? edge.data->getName() : "")); return NULL; } } @@ -364,11 +370,12 @@ void QtGraphView::compareNodesRecursive( for (std::list>::iterator it2 = oldSubNodes.begin(); it2 != oldSubNodes.end(); it2++) { - if (((*it)->getTokenId() && (*it)->getTokenId() == (*it2)->getTokenId()) || + if (((*it)->isDataNode() && (*it2)->isDataNode() && (*it)->getTokenId() == (*it2)->getTokenId()) || ((*it)->isAccessNode() && (*it2)->isAccessNode() && dynamic_cast((*it).get())->getAccessType() == dynamic_cast((*it2).get())->getAccessType()) || - ((*it)->isExpandToggleNode() && (*it2)->isExpandToggleNode())) + ((*it)->isExpandToggleNode() && (*it2)->isExpandToggleNode()) || + ((*it)->isBundleNode() && (*it2)->isBundleNode() && (*it)->getTokenId() == (*it2)->getTokenId())) { remainingNodes->push_back(std::pair((*it).get(), (*it2).get())); compareNodesRecursive((*it)->getSubNodes(), (*it2)->getSubNodes(), appearingNodes, vanishingNodes, remainingNodes); @@ -545,14 +552,15 @@ void QtGraphView::focusToken(Id tokenId) void QtGraphView::doFocusIn(Id tokenId) { std::shared_ptr node = findNodeRecursive(m_oldNodes, tokenId); - if(node) + if (node && node->isDataNode()) { node->focusIn(); return; } - for(std::shared_ptr edge : m_oldEdges) + + for (std::shared_ptr edge : m_oldEdges) { - if(edge->getData()->getId() == tokenId) + if (edge->getData() && edge->getData()->getId() == tokenId) { edge->focusIn(); return; @@ -568,17 +576,18 @@ void QtGraphView::defocusToken(Id tokenId) void QtGraphView::doFocusOut(Id tokenId) { std::shared_ptr node = findNodeRecursive(m_oldNodes, tokenId); - if(node) + if (node && node->isDataNode()) { node->focusOut(); return; } - for(std::shared_ptr edge : m_oldEdges) + + for (std::shared_ptr edge : m_oldEdges) { - if(edge->getData()->getId() == tokenId) + if (edge->getData() && edge->getData()->getId() == tokenId) { edge->focusOut(); return; } } -} \ No newline at end of file +} diff --git a/src/app/qt/view/graphElements/QtGraphEdge.cpp b/src/app/qt/view/graphElements/QtGraphEdge.cpp index ac0d5b97..4e6f95f9 100644 --- a/src/app/qt/view/graphElements/QtGraphEdge.cpp +++ b/src/app/qt/view/graphElements/QtGraphEdge.cpp @@ -5,6 +5,7 @@ #include "utility/messaging/type/MessageActivateTokens.h" #include "utility/messaging/type/MessageFocusIn.h" #include "utility/messaging/type/MessageFocusOut.h" +#include "utility/messaging/type/MessageGraphNodeBundleSplit.h" #include "component/view/GraphViewStyle.h" #include "data/graph/Edge.h" @@ -13,12 +14,16 @@ #include "qt/graphics/QtStraightLineItem.h" #include "qt/view/graphElements/QtGraphNode.h" -QtGraphEdge::QtGraphEdge(const std::weak_ptr& owner, const std::weak_ptr& target, const Edge* data) +QtGraphEdge::QtGraphEdge( + const std::weak_ptr& owner, const std::weak_ptr& target, const Edge* data, size_t weight +) : m_data(data) , m_owner(owner) , m_target(target) , m_child(nullptr) , m_isActive(false) + , m_weight(weight) + , m_direction(TokenComponentAggregation::DIRECTION_NONE) , m_mousePos(0.0f, 0.0f) , m_mouseMoved(false) { @@ -55,7 +60,17 @@ void QtGraphEdge::updateLine() return; } - GraphViewStyle::EdgeStyle style = GraphViewStyle::getStyleForEdgeType(getData()->getType(), m_isActive, false); + Edge::EdgeType type; + if (getData()) + { + type = getData()->getType(); + } + else + { + type = Edge::EDGE_AGGREGATION; + } + + GraphViewStyle::EdgeStyle style = GraphViewStyle::getStyleForEdgeType(type, m_isActive, false); if (style.isStraight) { @@ -64,14 +79,15 @@ void QtGraphEdge::updateLine() m_child = new QtStraightLineItem(this); } - int number = 0; - if (getData()->isType(Edge::EDGE_AGGREGATION)) + bool showArrow = m_direction != TokenComponentAggregation::DIRECTION_NONE; + + if (m_direction == TokenComponentAggregation::DIRECTION_BACKWARD) { - number = getData()->getComponent()->getAggregationCount(); + owner.swap(target); } dynamic_cast(m_child)->updateLine( - owner->getBoundingRect(), target->getBoundingRect(), number, style); + owner->getBoundingRect(), target->getBoundingRect(), m_weight, style, showArrow); } else { @@ -103,12 +119,14 @@ void QtGraphEdge::setIsActive(bool isActive) void QtGraphEdge::onClick() { - if (getData()->isType(Edge::EDGE_AGGREGATION)) + if (!getData()) + { + MessageGraphNodeBundleSplit(m_target.lock()->getTokenId()).dispatch(); + } + else if (getData()->isType(Edge::EDGE_AGGREGATION)) { const std::set& ids = getData()->getComponent()->getAggregationIds(); - MessageActivateTokens message(std::vector(ids.begin(), ids.end())); - message.isAggregation = true; - message.dispatch(); + MessageActivateTokens(std::vector(ids.begin(), ids.end())).dispatch(); } else { @@ -156,10 +174,31 @@ void QtGraphEdge::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) void QtGraphEdge::hoverEnterEvent(QGraphicsSceneHoverEvent* event) { + if (!getData()) + { + focusIn(); + return; + } + MessageFocusIn(getData()->getId()).dispatch(); } void QtGraphEdge::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) { + if (!getData()) + { + focusOut(); + return; + } + MessageFocusOut(getData()->getId()).dispatch(); } + +void QtGraphEdge::setDirection(TokenComponentAggregation::Direction direction) +{ + if (m_direction != direction) + { + m_direction = direction; + updateLine(); + } +} diff --git a/src/app/qt/view/graphElements/QtGraphEdge.h b/src/app/qt/view/graphElements/QtGraphEdge.h index 00fc809c..855bb5da 100644 --- a/src/app/qt/view/graphElements/QtGraphEdge.h +++ b/src/app/qt/view/graphElements/QtGraphEdge.h @@ -7,6 +7,8 @@ #include "utility/math/Vector2.h" +#include "data/graph/token_component/TokenComponentAggregation.h" + class Edge; class QtGraphNode; @@ -18,15 +20,15 @@ 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); + QtGraphEdge(const std::weak_ptr& owner, const std::weak_ptr& target, const Edge* data, size_t weight); virtual ~QtGraphEdge(); const Edge* getData() const; - virtual std::weak_ptr getOwner(); - virtual std::weak_ptr getTarget(); + std::weak_ptr getOwner(); + std::weak_ptr getTarget(); - virtual void updateLine(); + void updateLine(); bool getIsActive() const; void setIsActive(bool isActive); @@ -36,6 +38,8 @@ public: void focusIn(); void focusOut(); + void setDirection(TokenComponentAggregation::Direction direction); + protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent* event); virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event); @@ -53,6 +57,9 @@ private: QGraphicsLineItem* m_child; bool m_isActive; + size_t m_weight; + + TokenComponentAggregation::Direction m_direction; Vec2i m_mousePos; bool m_mouseMoved; diff --git a/src/app/qt/view/graphElements/QtGraphNode.cpp b/src/app/qt/view/graphElements/QtGraphNode.cpp index b17b0ac0..e98aa209 100644 --- a/src/app/qt/view/graphElements/QtGraphNode.cpp +++ b/src/app/qt/view/graphElements/QtGraphNode.cpp @@ -241,6 +241,11 @@ bool QtGraphNode::isExpandToggleNode() const return false; } +bool QtGraphNode::isBundleNode() const +{ + return false; +} + Id QtGraphNode::getTokenId() const { return 0; diff --git a/src/app/qt/view/graphElements/QtGraphNode.h b/src/app/qt/view/graphElements/QtGraphNode.h index 85881163..aa886749 100644 --- a/src/app/qt/view/graphElements/QtGraphNode.h +++ b/src/app/qt/view/graphElements/QtGraphNode.h @@ -73,6 +73,7 @@ public: virtual bool isDataNode() const; virtual bool isAccessNode() const; virtual bool isExpandToggleNode() const; + virtual bool isBundleNode() const; virtual Id getTokenId() const; diff --git a/src/app/qt/view/graphElements/QtGraphNodeBundle.cpp b/src/app/qt/view/graphElements/QtGraphNodeBundle.cpp new file mode 100644 index 00000000..e87cb239 --- /dev/null +++ b/src/app/qt/view/graphElements/QtGraphNodeBundle.cpp @@ -0,0 +1,71 @@ +#include "qt/view/graphElements/QtGraphNodeBundle.h" + +#include + +#include +#include + +#include "utility/messaging/type/MessageGraphNodeBundleSplit.h" + +#include "component/view/GraphViewStyle.h" +#include "qt/graphics/QtRoundedRectItem.h" + +QtGraphNodeBundle::QtGraphNodeBundle(Id tokenId, size_t nodeCount, std::string name) + : QtGraphNode() + , m_tokenId(tokenId) +{ + std::stringstream ss; + ss << nodeCount << " " << name; + this->setName(ss.str()); + + this->setAcceptHoverEvents(true); +} + +QtGraphNodeBundle::~QtGraphNodeBundle() +{ +} + +bool QtGraphNodeBundle::isBundleNode() const +{ + return true; +} + +Id QtGraphNodeBundle::getTokenId() const +{ + return m_tokenId; +} + +void QtGraphNodeBundle::onClick() +{ + MessageGraphNodeBundleSplit(m_tokenId).dispatch(); +} + +void QtGraphNodeBundle::updateStyle() +{ + GraphViewStyle::NodeStyle style = GraphViewStyle::getStyleOfBundleNode(m_isHovering); + setStyle(style); + + if (!m_undefinedRect) + { + m_undefinedRect = new QtRoundedRectItem(this); + setSize(getSize()); + m_undefinedRect->moveBy(7, 7); + } + + m_undefinedRect->setPen(m_rect->pen()); + m_undefinedRect->setBrush(m_rect->brush()); + m_undefinedRect->setRadius(style.cornerRadius); + m_undefinedRect->setZValue(-1); + m_rect->setZValue(0); +} + + +void QtGraphNodeBundle::hoverEnterEvent(QGraphicsSceneHoverEvent* event) +{ + focusIn(); +} + +void QtGraphNodeBundle::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) +{ + focusOut(); +} diff --git a/src/app/qt/view/graphElements/QtGraphNodeBundle.h b/src/app/qt/view/graphElements/QtGraphNodeBundle.h new file mode 100644 index 00000000..a3029097 --- /dev/null +++ b/src/app/qt/view/graphElements/QtGraphNodeBundle.h @@ -0,0 +1,29 @@ +#ifndef QT_GRAPH_NODE_BUNDLE_H +#define QT_GRAPH_NODE_BUNDLE_H + +#include "qt/view/graphElements/QtGraphNode.h" + +class QtGraphNodeBundle + : public QtGraphNode +{ +public: + QtGraphNodeBundle(Id tokenId, size_t nodeCount, std::string name); + virtual ~QtGraphNodeBundle(); + + // QtGraphNode implementation + virtual bool isBundleNode() const; + virtual Id getTokenId() const; + + virtual void onClick(); + + virtual void updateStyle(); + +protected: + virtual void hoverEnterEvent(QGraphicsSceneHoverEvent* event); + virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event); + +private: + Id m_tokenId; +}; + +#endif // QT_GRAPH_NODE_BUNDLE_H diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index d3c9d012..66cc7f4e 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -255,6 +255,7 @@ add_files( utility/messaging/type/MessageFinishedParsing.h utility/messaging/type/MessageFocusIn.h utility/messaging/type/MessageFocusOut.h + utility/messaging/type/MessageGraphNodeBundleSplit.h utility/messaging/type/MessageGraphNodeExpand.h utility/messaging/type/MessageGraphNodeMove.h utility/messaging/type/MessageInterruptTasks.h diff --git a/src/lib/component/controller/GraphController.cpp b/src/lib/component/controller/GraphController.cpp index c0e71531..1702130d 100644 --- a/src/lib/component/controller/GraphController.cpp +++ b/src/lib/component/controller/GraphController.cpp @@ -3,6 +3,7 @@ #include #include "utility/logging/logging.h" +#include "utility/utility.h" #include "component/controller/helper/DummyEdge.h" #include "component/controller/helper/DummyNode.h" @@ -11,7 +12,6 @@ #include "component/view/GraphViewStyle.h" #include "data/access/StorageAccess.h" #include "data/graph/Graph.h" -#include "data/graph/token_component/TokenComponentAggregation.h" GraphController::GraphController(StorageAccess* storageAccess) : m_storageAccess(storageAccess) @@ -33,12 +33,6 @@ void GraphController::handleMessage(MessageActivateTokens* message) return; } - if (message->isAggregation) - { - createDummyGraphForTokenIds(message->tokenIds); - return; - } - createDummyGraphForTokenIds(message->tokenIds); } @@ -57,6 +51,38 @@ void GraphController::handleMessage(MessageFocusOut *message) getView()->defocusToken(message->tokenId); } +void GraphController::handleMessage(MessageGraphNodeBundleSplit* message) +{ + for (size_t i = 0; i < m_dummyNodes.size(); i++) + { + DummyNode& node = m_dummyNodes[i]; + if (node.isBundleNode() && node.tokenId == message->bundleId) + { + m_dummyNodes.insert(m_dummyNodes.end(), node.bundledNodes.begin(), node.bundledNodes.end()); + m_dummyNodes.erase(m_dummyNodes.begin() + i); + break; + } + } + + for (size_t i = 0; i < m_dummyEdges.size(); i++) + { + DummyEdge& edge = m_dummyEdges[i]; + if (!edge.data && edge.targetId == message->bundleId) + { + m_dummyEdges.erase(m_dummyEdges.begin() + i); + break; + } + } + + setActiveAndVisibility(m_activeTokenIds); + + layoutNesting(); + GraphLayouter::layoutSpectralPrototype(m_dummyNodes, m_dummyEdges); + GraphPostprocessor::doPostprocessing(m_dummyNodes); + + getView()->rebuildGraph(nullptr, m_dummyNodes, m_dummyEdges); +} + void GraphController::handleMessage(MessageGraphNodeExpand* message) { DummyNode* node = findDummyNodeRecursive(m_dummyNodes, message->tokenId); @@ -132,6 +158,8 @@ void GraphController::createDummyGraphForTokenIds(const std::vector& tokenId autoExpandActiveNode(tokenIds); setActiveAndVisibility(tokenIds); + bundleNodes(); + layoutNesting(); GraphLayouter::layoutSpectralPrototype(m_dummyNodes, m_dummyEdges); GraphPostprocessor::doPostprocessing(m_dummyNodes); @@ -160,7 +188,7 @@ DummyNode GraphController::createDummyNodeTopDown(Node* node) DummyNode* oldNode = findDummyNodeRecursive(m_dummyNodes, node->getId()); if (oldNode) { - result.expanded = oldNode->expanded; + result.expanded = oldNode->isExpanded(); } node->forEachChildNode( @@ -212,14 +240,10 @@ DummyNode GraphController::createDummyNodeTopDown(Node* node) } ); - node->forEachEdge( + node->forEachEdgeOfType( + ~Edge::EDGE_MEMBER, [&result, node, this](Edge* edge) { - if (edge->isType(Edge::EDGE_MEMBER)) - { - return; - } - for (const DummyEdge& dummy : m_dummyEdges) { if (dummy.data->getId() == edge->getId()) @@ -258,7 +282,7 @@ void GraphController::setActiveAndVisibility(const std::vector& activeTokenI for (DummyEdge& edge : m_dummyEdges) { - if (edge.data->isType(Edge::EDGE_AGGREGATION)) + if (!edge.data || edge.data->isType(Edge::EDGE_AGGREGATION)) { continue; } @@ -282,7 +306,7 @@ void GraphController::setActiveAndVisibility(const std::vector& activeTokenI for (DummyEdge& edge : m_dummyEdges) { - if (!edge.data->isType(Edge::EDGE_AGGREGATION)) + if (!edge.data || !edge.data->isType(Edge::EDGE_AGGREGATION)) { continue; } @@ -298,7 +322,7 @@ void GraphController::setActiveAndVisibility(const std::vector& activeTokenI { for (DummyEdge& e : m_dummyEdges) { - if (e.visible && e.data->getId() == id) + if (e.visible && e.data && e.data->getId() == id) { component->removeAggregationId(id); } @@ -345,6 +369,11 @@ bool GraphController::setNodeVisibilityRecursiveBottomUp(DummyNode& node, bool a node.visible = true; return false; } + else if (node.isBundleNode()) + { + node.visible = true; + return true; + } for (DummyNode& subNode : node.subNodes) { @@ -379,6 +408,215 @@ void GraphController::setNodeVisibilityRecursiveTopDown(DummyNode& node, bool pa } } +void GraphController::bundleNodes() +{ + bundleNodesMatching( + [&](const DummyNode& node) + { + return isTypeNodeWithSingleAggregation(node, TokenComponentAggregation::DIRECTION_BACKWARD); + }, + 3, + "Used Types" + ); + + bundleNodesMatching( + [&](const DummyNode& node) + { + return isTypeNodeWithSingleAggregation(node, TokenComponentAggregation::DIRECTION_FORWARD); + }, + 3, + "Using Types" + ); + + bundleNodesMatching( + [&](const DummyNode& node) + { + return isTypeNodeWithSingleInheritance(node, true); + }, + 3, + "Base Types" + ); + + bundleNodesMatching( + [&](const DummyNode& node) + { + return isTypeNodeWithSingleInheritance(node, false); + }, + 3, + "Derived Types" + ); + + bundleNodesMatching( + [](const DummyNode& node) + { + const Node::NodeTypeMask undefinedMask = + Node::NODE_UNDEFINED | Node::NODE_UNDEFINED_TYPE | + Node::NODE_UNDEFINED_VARIABLE | Node::NODE_UNDEFINED_FUNCTION; + + if (node.visible && node.isGraphNode() && !node.hasActiveSubNode() && node.data->isType(undefinedMask)) + { + return true; + } + + return false; + }, + 3, + "Undefined Nodes" + ); +} + +void GraphController::bundleNodesMatching(std::function matcher, size_t count, const std::string& name) +{ + size_t matchCount = 0; + std::vector nodeIndices; + + for (size_t i = 0; i < m_dummyNodes.size(); i++) + { + const DummyNode& node = m_dummyNodes[i]; + + if (matcher(node)) + { + matchCount++; + nodeIndices.push_back(i); + } + } + + if (matchCount < count) + { + return; + } + + DummyNode bundleNode; + bundleNode.name = name; + bundleNode.visible = true; + + for (int i = nodeIndices.size() - 1; i >= 0; i--) + { + DummyNode& node = m_dummyNodes[nodeIndices[i]]; + node.visible = false; + + bundleNode.bundledNodes.push_back(node); + if (!bundleNode.tokenId) + { + bundleNode.tokenId = node.data->getId(); + } + + m_dummyNodes.erase(m_dummyNodes.begin() + nodeIndices[i]); + } + + std::vector bundleEdges; + for (DummyNode& node : bundleNode.bundledNodes) + { + for (DummyEdge& edge : m_dummyEdges) + { + bool owner = (edge.ownerId == node.data->getId()); + bool target = (edge.targetId == node.data->getId()); + + if (!owner && !target) + { + continue; + } + + DummyEdge* bundleEdgePtr = nullptr; + for (DummyEdge& bundleEdge : bundleEdges) + { + if ((owner && bundleEdge.ownerId == edge.targetId) || + (target && bundleEdge.ownerId == edge.ownerId)) + { + bundleEdgePtr = &bundleEdge; + break; + } + } + + if (!bundleEdgePtr) + { + DummyEdge bundleEdge; + bundleEdge.visible = true; + bundleEdge.ownerId = (owner ? edge.targetId : edge.ownerId); + bundleEdge.targetId = bundleNode.bundledNodes.front().data->getId(); + bundleEdges.push_back(bundleEdge); + bundleEdgePtr = &bundleEdges.back(); + } + + bundleEdgePtr->weight += edge.getWeight(); + bundleEdgePtr->updateDirection(edge.getDirection(), owner); + edge.visible = false; + } + } + + m_dummyEdges.insert(m_dummyEdges.end(), bundleEdges.begin(), bundleEdges.end()); + m_dummyNodes.push_back(bundleNode); +} + +bool GraphController::isTypeNodeWithSingleAggregation( + const DummyNode& node, TokenComponentAggregation::Direction direction +) const { + const Node::NodeTypeMask typeMask = Node::NODE_STRUCT | Node::NODE_CLASS; + + if (!node.visible || !node.isGraphNode() || node.hasVisibleSubNode() || !node.data->isType(typeMask)) + { + return false; + } + + bool matches = false; + int count = 0; + Id tokenId = node.data->getId(); + + node.data->forEachEdgeOfType( + ~Edge::EDGE_MEMBER, + [direction, tokenId, &matches, &count](Edge* edge) + { + count++; + + if (edge->isType(Edge::EDGE_AGGREGATION)) + { + TokenComponentAggregation::Direction dir = + edge->getComponent()->getDirection(); + + if ((edge->getFrom()->getId() == tokenId && dir == direction) || + (edge->getTo()->getId() == tokenId && dir == TokenComponentAggregation::opposite(direction))) + { + matches = true; + } + } + } + ); + + if (count > 1) + { + return false; + } + + return matches; +} + +bool GraphController::isTypeNodeWithSingleInheritance(const DummyNode& node, bool isBase) const +{ + const Node::NodeTypeMask typeMask = Node::NODE_STRUCT | Node::NODE_CLASS; + + if (!node.visible || !node.isGraphNode() || node.hasVisibleSubNode() || !node.data->isType(typeMask)) + { + return false; + } + + bool matches = false; + Id tokenId = node.data->getId(); + + node.data->forEachEdgeOfType( + Edge::EDGE_INHERITANCE, + [isBase, tokenId, &matches](Edge* edge) + { + if ((!isBase && edge->getFrom()->getId() == tokenId) || + (isBase && edge->getTo()->getId() == tokenId)) + { + matches = true; + } + } + ); + + return matches; +} + void GraphController::layoutNesting() { for (DummyNode& node : m_dummyNodes) @@ -394,6 +632,11 @@ void GraphController::layoutNesting() void GraphController::layoutNestingRecursive(DummyNode& node) const { + if (!node.visible) + { + return; + } + GraphViewStyle::NodeMargins margins; if (node.isGraphNode()) @@ -408,6 +651,10 @@ void GraphController::layoutNestingRecursive(DummyNode& node) const { margins = GraphViewStyle::getMarginsOfExpandToggleNode(); } + else if (node.isBundleNode()) + { + margins = GraphViewStyle::getMarginsOfBundleNode(); + } int y = 0; int x = 0; @@ -423,6 +670,10 @@ void GraphController::layoutNestingRecursive(DummyNode& node) const addExpandToggleNode(node); } } + else if (node.isBundleNode()) + { + width = margins.charWidth * (node.name.size() + 1 + utility::digits(node.bundledNodes.size())); + } // Horizontal layouting is currently not used, but left in place for experimentation. bool layoutHorizontal = false; @@ -541,9 +792,8 @@ void GraphController::addExpandToggleNode(DummyNode& node) const void GraphController::layoutToGrid(DummyNode& node) const { - if (!node.isGraphNode()) + if (!node.visible) { - LOG_ERROR("Only GraphNodes can be layouted to the grid"); return; } @@ -556,6 +806,11 @@ void GraphController::layoutToGrid(DummyNode& node) const node.size.x = width; node.size.y = height; + if (!node.isGraphNode()) + { + return; + } + DummyNode* lastAccessNode = nullptr; for (DummyNode& subNode : node.subNodes) { @@ -581,7 +836,7 @@ void GraphController::layoutToGrid(DummyNode& node) const } } -DummyNode* GraphController::findDummyNodeRecursive(std::vector& nodes, Id tokenId) +DummyNode* GraphController::findDummyNodeRecursive(std::vector& nodes, Id tokenId) const { for (DummyNode& node : nodes) { @@ -602,7 +857,7 @@ DummyNode* GraphController::findDummyNodeRecursive(std::vector& nodes DummyNode* GraphController::findDummyNodeAccessRecursive( std::vector& nodes, Id parentId, TokenComponentAccess::AccessType type -){ +) const { DummyNode* node = findDummyNodeRecursive(nodes, parentId); if (node) { diff --git a/src/lib/component/controller/GraphController.h b/src/lib/component/controller/GraphController.h index 45f97902..c3278625 100644 --- a/src/lib/component/controller/GraphController.h +++ b/src/lib/component/controller/GraphController.h @@ -8,6 +8,7 @@ #include "utility/messaging/type/MessageFinishedParsing.h" #include "utility/messaging/type/MessageFocusIn.h" #include "utility/messaging/type/MessageFocusOut.h" +#include "utility/messaging/type/MessageGraphNodeBundleSplit.h" #include "utility/messaging/type/MessageGraphNodeExpand.h" #include "utility/messaging/type/MessageGraphNodeMove.h" @@ -15,6 +16,7 @@ #include "component/controller/GraphLayouter.h" #include "component/view/GraphView.h" #include "data/graph/token_component/TokenComponentAccess.h" +#include "data/graph/token_component/TokenComponentAggregation.h" struct DummyNode; struct DummyEdge; @@ -28,6 +30,7 @@ class GraphController , public MessageListener , public MessageListener , public MessageListener + , public MessageListener , public MessageListener , public MessageListener { @@ -40,6 +43,7 @@ private: virtual void handleMessage(MessageFinishedParsing* message); virtual void handleMessage(MessageFocusIn* message); virtual void handleMessage(MessageFocusOut* message); + virtual void handleMessage(MessageGraphNodeBundleSplit* message); virtual void handleMessage(MessageGraphNodeExpand* message); virtual void handleMessage(MessageGraphNodeMove* message); @@ -55,13 +59,18 @@ private: bool setNodeVisibilityRecursiveBottomUp(DummyNode& node, bool aggregated) const; void setNodeVisibilityRecursiveTopDown(DummyNode& node, bool parentExpanded) const; + void bundleNodes(); + void bundleNodesMatching(std::function matcher, size_t count, const std::string& name); + bool isTypeNodeWithSingleAggregation(const DummyNode& node, TokenComponentAggregation::Direction direction) const; + bool isTypeNodeWithSingleInheritance(const DummyNode& node, bool isBase) const; + void layoutNesting(); void layoutNestingRecursive(DummyNode& node) const; void addExpandToggleNode(DummyNode& node) const; void layoutToGrid(DummyNode& node) const; - DummyNode* findDummyNodeRecursive(std::vector& nodes, Id tokenId); - DummyNode* findDummyNodeAccessRecursive(std::vector& nodes, Id parentId, TokenComponentAccess::AccessType type); + DummyNode* findDummyNodeRecursive(std::vector& nodes, Id tokenId) const; + DummyNode* findDummyNodeAccessRecursive(std::vector& nodes, Id parentId, TokenComponentAccess::AccessType type) const; StorageAccess* m_storageAccess; diff --git a/src/lib/component/controller/helper/DummyEdge.h b/src/lib/component/controller/helper/DummyEdge.h index d64eecd0..90f0a67d 100644 --- a/src/lib/component/controller/helper/DummyEdge.h +++ b/src/lib/component/controller/helper/DummyEdge.h @@ -11,18 +11,35 @@ class Edge; // temporary data structure for (visual) graph creation process struct DummyEdge { + DummyEdge() + : ownerId(0) + , targetId(0) + , data(nullptr) + , visible(false) + , active(false) + , weight(0) + , direction(TokenComponentAggregation::DIRECTION_INVALID) + { + } + DummyEdge(const Id ownerId, const Id targetId, const Edge* data) : ownerId(ownerId) , targetId(targetId) , data(data) , visible(false) , active(false) + , weight(0) + , direction(TokenComponentAggregation::DIRECTION_INVALID) { } int getWeight() const { - if (data->isType(Edge::EDGE_AGGREGATION)) + if (!data) + { + return weight; + } + else if (data->isType(Edge::EDGE_AGGREGATION)) { return data->getComponent()->getAggregationCount(); } @@ -30,6 +47,37 @@ struct DummyEdge return 1; } + void updateDirection(TokenComponentAggregation::Direction dir, bool invert) + { + if (invert) + { + dir = TokenComponentAggregation::opposite(dir); + } + + if (direction == TokenComponentAggregation::DIRECTION_INVALID) + { + direction = dir; + } + else if (direction != dir) + { + direction = TokenComponentAggregation::DIRECTION_NONE; + } + } + + TokenComponentAggregation::Direction getDirection() const + { + if (!data) + { + return direction; + } + else if (data->isType(Edge::EDGE_AGGREGATION)) + { + return data->getComponent()->getDirection(); + } + + return TokenComponentAggregation::DIRECTION_FORWARD; + } + Id ownerId; Id targetId; @@ -37,6 +85,10 @@ struct DummyEdge bool visible; bool active; + + // BundleEdge + int weight; + TokenComponentAggregation::Direction direction; }; #endif // DUMMY_EDGE_H diff --git a/src/lib/component/controller/helper/DummyNode.h b/src/lib/component/controller/helper/DummyNode.h index 1683bd04..b5a9cd01 100644 --- a/src/lib/component/controller/helper/DummyNode.h +++ b/src/lib/component/controller/helper/DummyNode.h @@ -40,7 +40,12 @@ public: bool isExpandToggleNode() const { - return !data && !isAccessNode(); + return !isGraphNode() && !isAccessNode() && !isBundleNode(); + } + + bool isBundleNode() const + { + return bundledNodes.size() > 0; } bool isExpanded() const @@ -48,6 +53,55 @@ public: return expanded || autoExpanded; } + bool hasVisibleSubNode() const + { + for (const DummyNode& node : subNodes) + { + if (node.visible) + { + return true; + } + } + + return false; + } + + bool hasActiveSubNode() const + { + if (active) + { + return true; + } + + for (const DummyNode& node : subNodes) + { + if (node.hasActiveSubNode()) + { + return true; + } + } + + return false; + } + + bool hasConnectedSubNode() const + { + if (connected) + { + return true; + } + + for (const DummyNode& node : subNodes) + { + if (node.hasConnectedSubNode()) + { + return true; + } + } + + return false; + } + Vec2i position; Vec2i size; @@ -72,6 +126,10 @@ public: // ExpandToggleNode size_t invisibleSubNodeCount; + + // BundleNode + std::vector bundledNodes; + std::string name; }; #endif // DUMMY_NODE_H diff --git a/src/lib/component/view/GraphViewStyle.cpp b/src/lib/component/view/GraphViewStyle.cpp index 995bc4dc..1357f093 100644 --- a/src/lib/component/view/GraphViewStyle.cpp +++ b/src/lib/component/view/GraphViewStyle.cpp @@ -217,6 +217,11 @@ GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfExpandToggleNode() return margins; } +GraphViewStyle::NodeMargins GraphViewStyle::getMarginsOfBundleNode() +{ + return getMarginsForNodeType(Node::NODE_CLASS, false); +} + GraphViewStyle::NodeStyle GraphViewStyle::getStyleForNodeType( Node::NodeType type, bool isActive, bool isFocused, bool hasChildren ){ @@ -367,12 +372,23 @@ GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfExpandToggleNode() return style; } +GraphViewStyle::NodeStyle GraphViewStyle::getStyleOfBundleNode(bool isFocused) +{ + NodeStyle style = getStyleForNodeType(Node::NODE_CLASS, false, isFocused, false); + + style.shadowColor = ""; + style.borderColor = "#3c3c3c"; + style.borderWidth = isFocused ? 2 : 1; + + return style; +} + GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused) { EdgeStyle style; style.width = isActive ? 3 : 1; - style.zValue = isActive ? 5 : 1; + style.zValue = isActive ? 5 : 2; style.arrowLength = 5; style.arrowWidth = 8; @@ -392,7 +408,7 @@ GraphViewStyle::EdgeStyle GraphViewStyle::getStyleForEdgeType(Edge::EdgeType typ style.isStraight = true; style.color = isActive ? "#DDD" : "#EEE"; style.width = 1; - style.zValue = isActive ? -1 : -5; + style.zValue = isActive ? -2 : -5; break; case Edge::EDGE_CALL: diff --git a/src/lib/component/view/GraphViewStyle.h b/src/lib/component/view/GraphViewStyle.h index 9f3f0541..d661151e 100644 --- a/src/lib/component/view/GraphViewStyle.h +++ b/src/lib/component/view/GraphViewStyle.h @@ -94,10 +94,12 @@ public: static NodeMargins getMarginsForNodeType(Node::NodeType type, bool hasChildren); static NodeMargins getMarginsOfAccessNode(TokenComponentAccess::AccessType type); static NodeMargins getMarginsOfExpandToggleNode(); + static NodeMargins getMarginsOfBundleNode(); static NodeStyle getStyleForNodeType(Node::NodeType type, bool isActive, bool isFocused, bool hasChildren); static NodeStyle getStyleOfAccessNode(); static NodeStyle getStyleOfExpandToggleNode(); + static NodeStyle getStyleOfBundleNode(bool isFocused); static EdgeStyle getStyleForEdgeType(Edge::EdgeType type, bool isActive, bool isFocused); diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 66cac021..ef331d99 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -811,10 +811,15 @@ std::shared_ptr Storage::getGraphForActiveTokenIds(const std::vector& Node::NODE_UNDEFINED_FUNCTION | Node::NODE_FUNCTION | Node::NODE_METHOD | Node::NODE_UNDEFINED_VARIABLE | Node::NODE_GLOBAL_VARIABLE | Node::NODE_FIELD; - if (!node->isType(varFuncMask) || !edge->isType(Edge::EDGE_AGGREGATION)) + const Node::NodeTypeMask typeMask = Node::NODE_STRUCT | Node::NODE_CLASS; + + if ((node->isType(varFuncMask) && edge->isType(Edge::EDGE_AGGREGATION)) || + (node->isType(typeMask) && edge->isType(Edge::EDGE_TYPE_USAGE | Edge::EDGE_TYPE_OF))) { - graph->addEdgeAndAllChildrenAsPlainCopy(edge); + return; } + + graph->addEdgeAndAllChildrenAsPlainCopy(edge); } ); } diff --git a/src/lib/data/graph/Node.cpp b/src/lib/data/graph/Node.cpp index 048fb294..2d89c96a 100644 --- a/src/lib/data/graph/Node.cpp +++ b/src/lib/data/graph/Node.cpp @@ -128,17 +128,17 @@ Edge* Node::findEdge(std::function func) const return nullptr; } -Edge* Node::findEdgeOfType(Edge::EdgeType type) const +Edge* Node::findEdgeOfType(Edge::EdgeTypeMask mask) const { - return findEdgeOfType(type, [](Edge* e){ return true; }); + return findEdgeOfType(mask, [](Edge* e){ return true; }); } -Edge* Node::findEdgeOfType(Edge::EdgeType type, std::function func) const +Edge* Node::findEdgeOfType(Edge::EdgeTypeMask mask, std::function func) const { std::vector::const_iterator it = find_if(m_edges.begin(), m_edges.end(), - [type, func](Edge* e) + [mask, func](Edge* e) { - if (e->getType() == type) + if (e->isType(mask)) { return func(e); } @@ -180,12 +180,12 @@ void Node::forEachEdge(std::function func) const for_each(m_edges.begin(), m_edges.end(), func); } -void Node::forEachEdgeOfType(Edge::EdgeType type, std::function func) const +void Node::forEachEdgeOfType(Edge::EdgeTypeMask mask, std::function func) const { for_each(m_edges.begin(), m_edges.end(), - [type, func](Edge* e) + [mask, func](Edge* e) { - if (e->getType() == type) + if (e->isType(mask)) { func(e); } diff --git a/src/lib/data/graph/Node.h b/src/lib/data/graph/Node.h index 64f2e63e..9c92b899 100644 --- a/src/lib/data/graph/Node.h +++ b/src/lib/data/graph/Node.h @@ -67,12 +67,12 @@ public: Edge* getMemberEdge() const; Edge* findEdge(std::function func) const; - Edge* findEdgeOfType(Edge::EdgeType type) const; - Edge* findEdgeOfType(Edge::EdgeType type, std::function func) const; + Edge* findEdgeOfType(Edge::EdgeTypeMask mask) const; + Edge* findEdgeOfType(Edge::EdgeTypeMask mask, std::function func) const; Node* findChildNode(std::function func) const; void forEachEdge(std::function func) const; - void forEachEdgeOfType(Edge::EdgeType type, std::function func) const; + void forEachEdgeOfType(Edge::EdgeTypeMask mask, std::function func) const; void forEachChildNode(std::function func) const; bool hasReferences() const; diff --git a/src/lib/data/graph/StorageGraph.cpp b/src/lib/data/graph/StorageGraph.cpp index 6d01fe71..a12074f6 100644 --- a/src/lib/data/graph/StorageGraph.cpp +++ b/src/lib/data/graph/StorageGraph.cpp @@ -197,7 +197,8 @@ void StorageGraph::updateAggregationEdges(Node* from, Node* to, Id addEdgeId, Id if (addEdgeId) { - edge->getComponent()->addAggregationId(addEdgeId); + bool forward = (edge->getFrom() == from); + edge->getComponent()->addAggregationId(addEdgeId, forward); } if (edge && removeEdgeId) diff --git a/src/lib/data/graph/token_component/TokenComponentAggregation.cpp b/src/lib/data/graph/token_component/TokenComponentAggregation.cpp index 42147371..69b7c2b5 100644 --- a/src/lib/data/graph/token_component/TokenComponentAggregation.cpp +++ b/src/lib/data/graph/token_component/TokenComponentAggregation.cpp @@ -1,6 +1,21 @@ #include "data/graph/token_component/TokenComponentAggregation.h" +TokenComponentAggregation::Direction TokenComponentAggregation::opposite(Direction direction) +{ + if (direction == DIRECTION_FORWARD) + { + return DIRECTION_BACKWARD; + } + else if (direction == DIRECTION_BACKWARD) + { + return DIRECTION_FORWARD; + } + + return direction; +} + TokenComponentAggregation::TokenComponentAggregation() + : m_direction(DIRECTION_INVALID) { } @@ -18,17 +33,53 @@ int TokenComponentAggregation::getAggregationCount() const return m_ids.size(); } -const std::set& TokenComponentAggregation::getAggregationIds() const +std::set TokenComponentAggregation::getAggregationIds() const { - return m_ids; + std::set ids; + + for (const std::pair& p : m_ids) + { + ids.insert(p.first); + } + + return ids; } -void TokenComponentAggregation::addAggregationId(Id id) +void TokenComponentAggregation::addAggregationId(Id id, bool forward) { - m_ids.insert(id); + m_ids.emplace(id, forward ? DIRECTION_FORWARD : DIRECTION_BACKWARD); + + m_direction = DIRECTION_INVALID; } void TokenComponentAggregation::removeAggregationId(Id id) { m_ids.erase(id); + + m_direction = DIRECTION_INVALID; +} + +TokenComponentAggregation::Direction TokenComponentAggregation::getDirection() +{ + if (m_direction != DIRECTION_INVALID) + { + return m_direction; + } + + m_direction = DIRECTION_NONE; + + for (const std::pair& p : m_ids) + { + if (m_direction == DIRECTION_NONE) + { + m_direction = p.second; + } + else if (m_direction != p.second) + { + m_direction = DIRECTION_NONE; + break; + } + } + + return m_direction; } diff --git a/src/lib/data/graph/token_component/TokenComponentAggregation.h b/src/lib/data/graph/token_component/TokenComponentAggregation.h index d5a8be14..464cf269 100644 --- a/src/lib/data/graph/token_component/TokenComponentAggregation.h +++ b/src/lib/data/graph/token_component/TokenComponentAggregation.h @@ -1,6 +1,7 @@ #ifndef TOKEN_COMPONENT_AGGREGATION_H #define TOKEN_COMPONENT_AGGREGATION_H +#include #include #include "utility/types.h" @@ -11,19 +12,32 @@ class TokenComponentAggregation : public TokenComponent { public: + enum Direction + { + DIRECTION_NONE, + DIRECTION_FORWARD, + DIRECTION_BACKWARD, + DIRECTION_INVALID + }; + + static Direction opposite(Direction direction); + TokenComponentAggregation(); virtual ~TokenComponentAggregation(); virtual std::shared_ptr copy() const; int getAggregationCount() const; - const std::set& getAggregationIds() const; + std::set getAggregationIds() const; - void addAggregationId(Id id); + void addAggregationId(Id id, bool forward); void removeAggregationId(Id id); + Direction getDirection(); + private: - std::set m_ids; + std::map m_ids; + Direction m_direction; }; #endif // TOKEN_COMPONENT_AGGREGATION_H diff --git a/src/lib/utility/Property.h b/src/lib/utility/Property.h index ba05b838..2967dbfb 100644 --- a/src/lib/utility/Property.h +++ b/src/lib/utility/Property.h @@ -5,11 +5,11 @@ template class Property { public: - Property(T* valuePointer); + explicit Property(T* valuePointer); ~Property(); T& operator=(const T& value); - T& operator=(const Property& property); + Property& operator=(const Property& property); operator const T&() const; @@ -36,10 +36,9 @@ T& Property::operator=(const T& value) } template -T& Property::operator=(const Property& property) +Property& Property::operator=(const Property& property) { - *m_valuePointer = *property.m_valuePointer; - return *m_valuePointer; + return *this; } template diff --git a/src/lib/utility/math/VectorBase.h b/src/lib/utility/math/VectorBase.h index 47077045..418a5a37 100644 --- a/src/lib/utility/math/VectorBase.h +++ b/src/lib/utility/math/VectorBase.h @@ -59,7 +59,7 @@ public: T operator[](const unsigned int index); template - void operator=(const VectorBase& other); + VectorBase& operator=(const VectorBase& other); template VectorBase operator+(const VectorBase& other) const; @@ -339,9 +339,10 @@ T VectorBase::operator[](const unsigned int index) template template -void VectorBase::operator=(const VectorBase& other) +VectorBase& VectorBase::operator=(const VectorBase& other) { assign(other); + return *this; } template diff --git a/src/lib/utility/messaging/type/MessageActivateTokens.h b/src/lib/utility/messaging/type/MessageActivateTokens.h index 923669a0..70b41f81 100644 --- a/src/lib/utility/messaging/type/MessageActivateTokens.h +++ b/src/lib/utility/messaging/type/MessageActivateTokens.h @@ -10,7 +10,6 @@ public: MessageActivateTokens(const std::vector& tokenIds) : tokenIds(tokenIds) , isEdge(false) - , isAggregation(false) , isFromSystem(false) { } @@ -18,7 +17,6 @@ public: MessageActivateTokens(Id tokenId) : tokenIds(1, tokenId) , isEdge(false) - , isAggregation(false) , isFromSystem(false) { } @@ -31,7 +29,7 @@ public: const std::vector tokenIds; bool isEdge; - bool isAggregation; + bool isBundle; bool isFromSystem; }; diff --git a/src/lib/utility/messaging/type/MessageGraphNodeBundleSplit.h b/src/lib/utility/messaging/type/MessageGraphNodeBundleSplit.h new file mode 100644 index 00000000..4475d0df --- /dev/null +++ b/src/lib/utility/messaging/type/MessageGraphNodeBundleSplit.h @@ -0,0 +1,24 @@ +#ifndef MESSAGE_GRAPH_NODE_BUNDLE_SPLIT_H +#define MESSAGE_GRAPH_NODE_BUNDLE_SPLIT_H + +#include "utility/messaging/Message.h" +#include "utility/types.h" + +class MessageGraphNodeBundleSplit + : public Message +{ +public: + MessageGraphNodeBundleSplit(Id bundleId) + : bundleId(bundleId) + { + } + + static const std::string getStaticType() + { + return "MessageGraphNodeBundleSplit"; + } + + Id bundleId; +}; + +#endif // MESSAGE_GRAPH_NODE_BUNDLE_SPLIT_H diff --git a/src/lib/utility/utility.cpp b/src/lib/utility/utility.cpp index 3078f296..061f5726 100644 --- a/src/lib/utility/utility.cpp +++ b/src/lib/utility/utility.cpp @@ -43,3 +43,16 @@ bool utility::intersectionPoint(Vec2f a1, Vec2f b1, Vec2f a2, Vec2f b2, Vec2f* i return false; } + +size_t utility::digits(size_t n) +{ + int digits = 1; + + while (n >= 10) + { + n /= 10; + digits++; + } + + return digits; +} diff --git a/src/lib/utility/utility.h b/src/lib/utility/utility.h index 263d16ce..4fc7facf 100644 --- a/src/lib/utility/utility.h +++ b/src/lib/utility/utility.h @@ -24,6 +24,8 @@ namespace utility void append(std::set& a, const std::set& b); bool intersectionPoint(Vec2f a1, Vec2f b1, Vec2f a2, Vec2f b2, Vec2f* i); + + size_t digits(size_t n); } template