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