diff --git a/src/lib/component/controller/GraphController.cpp b/src/lib/component/controller/GraphController.cpp index 3f03af3a..1588b19b 100644 --- a/src/lib/component/controller/GraphController.cpp +++ b/src/lib/component/controller/GraphController.cpp @@ -445,14 +445,14 @@ void GraphController::bundleNodes() "Referenced Types" ); - bundleNodesMatching( - [&](const DummyNode& node) - { - return isTypeNodeWithSingleAggregation(node, TokenComponentAggregation::DIRECTION_FORWARD); - }, - 3, - "Referencing Types" - ); + // bundleNodesMatching( + // [&](const DummyNode& node) + // { + // return isTypeNodeWithSingleAggregation(node, TokenComponentAggregation::DIRECTION_FORWARD); + // }, + // 3, + // "Referencing Types" + // ); bundleNodesMatching( [&](const DummyNode& node) @@ -473,18 +473,31 @@ void GraphController::bundleNodes() ); bundleNodesMatching( - [](const DummyNode& node) + [&](const DummyNode& node) { - if (node.visible && node.isGraphNode() && !node.hasActiveSubNode() && !node.data->isDefined()) - { - return true; - } - - return false; + return isUndefinedNode(node, false); }, 2, "Undefined Symbols" ); + + bundleNodesMatching( + [&](const DummyNode& node) + { + return isUndefinedNode(node, true); + }, + 2, + "Undefined Symbols" + ); + + bundleNodesMatching( + [&](const DummyNode& node) + { + return isTypeUserNode(node); + }, + 8, + "Referencing Symbols" + ); } void GraphController::bundleNodesMatching(std::function matcher, size_t count, const std::string& name) @@ -518,6 +531,8 @@ void GraphController::bundleNodesMatching(std::function node.visible = false; bundleNode.bundledNodes.push_back(node); + bundleNode.bundledNodeCount += node.getConnectedSubNodeCount(); + if (!bundleNode.tokenId) { bundleNode.tokenId = node.data->getId(); @@ -527,12 +542,13 @@ void GraphController::bundleNodesMatching(std::function } std::vector bundleEdges; - for (DummyNode& node : bundleNode.bundledNodes) + std::vector bundledNodes = bundleNode.getAllBundledNodes(); + for (const DummyNode* node : bundledNodes) { for (DummyEdge& edge : m_dummyEdges) { - bool owner = (edge.ownerId == node.data->getId()); - bool target = (edge.targetId == node.data->getId()); + bool owner = (edge.ownerId == node->data->getId()); + bool target = (edge.targetId == node->data->getId()); if (!owner && !target) { @@ -573,7 +589,7 @@ void GraphController::bundleNodesMatching(std::function bool GraphController::isTypeNodeWithSingleAggregation( const DummyNode& node, TokenComponentAggregation::Direction direction ) const { - const Node::NodeTypeMask typeMask = Node::NODE_STRUCT | Node::NODE_CLASS; + const Node::NodeTypeMask typeMask = Node::NODE_STRUCT | Node::NODE_CLASS | Node::NODE_TYPEDEF; if (!node.visible || !node.isGraphNode() || node.hasVisibleSubNode() || !node.data->isType(typeMask)) { @@ -639,6 +655,101 @@ bool GraphController::isTypeNodeWithSingleInheritance(const DummyNode& node, boo return matches; } +bool GraphController::isUndefinedNode(const DummyNode& node, bool isUsed) const +{ + if (!node.visible || node.active || !node.isGraphNode() || node.hasActiveSubNode() || node.data->isDefined()) + { + return false; + } + + bool matches = true; + Id tokenId = node.data->getId(); + + node.data->forEachEdge( + [isUsed, tokenId, &matches](Edge* edge) + { + if (edge->isType(Edge::EDGE_MEMBER)) + { + return; + } + + Id fromId = edge->getFrom()->getId(); + Id toId = edge->getTo()->getId(); + + if (edge->isType(Edge::EDGE_AGGREGATION)) + { + TokenComponentAggregation::Direction dir = edge->getComponent()->getDirection(); + + switch (dir) + { + case TokenComponentAggregation::DIRECTION_BACKWARD: + { + Id id = fromId; + fromId = toId; + toId = id; + } + break; + case TokenComponentAggregation::DIRECTION_NONE: + matches = false; + return; + default: + break; + } + } + + if ((!isUsed && toId == tokenId) || + (isUsed && fromId == tokenId)) + { + matches = false; + } + } + ); + + return matches; +} + +bool GraphController::isTypeUserNode(const DummyNode& node) const +{ + if (!node.visible || node.active || !node.isGraphNode() || node.hasActiveSubNode()) + { + return false; + } + + std::vector nodes; + nodes.push_back(node.data); + + node.data->forEachChildNodeRecursive( + [&nodes](Node* n) + { + nodes.push_back(n); + } + ); + + bool matches = true; + for (const Node* n : nodes) + { + Id tokenId = n->getId(); + + n->forEachEdge( + [tokenId, &matches](Edge* edge) + { + if (edge->isType(Edge::EDGE_MEMBER | Edge::EDGE_AGGREGATION)) + { + return; + } + + if (!edge->isType(Edge::EDGE_TYPE_USAGE | Edge::EDGE_TYPE_OF | Edge::EDGE_TEMPLATE_ARGUMENT | Edge::EDGE_TYPEDEF_OF) || + edge->getFrom()->getId() != tokenId) + { + matches = false; + } + } + ); + } + + return matches; +} + #define BUNDLE_BY_TYPE(__type__, __name__) \ bundleNodesMatching( \ [&](const DummyNode& node) \ diff --git a/src/lib/component/controller/GraphController.h b/src/lib/component/controller/GraphController.h index 20b9c7fd..f2613bd2 100644 --- a/src/lib/component/controller/GraphController.h +++ b/src/lib/component/controller/GraphController.h @@ -70,6 +70,8 @@ private: 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; + bool isUndefinedNode(const DummyNode& node, bool isUsed) const; + bool isTypeUserNode(const DummyNode& node) const; void bundleNodesByType(); void layoutNesting(); diff --git a/src/lib/component/controller/helper/BucketGrid.cpp b/src/lib/component/controller/helper/BucketGrid.cpp index 1ae4c17c..a606332e 100644 --- a/src/lib/component/controller/helper/BucketGrid.cpp +++ b/src/lib/component/controller/helper/BucketGrid.cpp @@ -224,8 +224,15 @@ void BucketGrid::layoutBuckets() { Bucket* bucket = &m_buckets[j][i]; - bucket->layout(x, y, widths[i], heights[j]); + int yOff = 0; + // move buckets over or below the middle one closer + if (i == 0 && (j == -1 || j == 1)) + { + Bucket* midBucket = &m_buckets[0][0]; + yOff = (heights[0] - midBucket->getHeight()) / 2 * -j; + } + bucket->layout(x, y + yOff, widths[i], heights[j]); x += widths[i] + GraphViewStyle::toGridGap(85); } diff --git a/src/lib/component/controller/helper/DummyNode.h b/src/lib/component/controller/helper/DummyNode.h index 3ec7df0c..54225122 100644 --- a/src/lib/component/controller/helper/DummyNode.h +++ b/src/lib/component/controller/helper/DummyNode.h @@ -3,6 +3,7 @@ #include "utility/math/Vector2.h" #include "utility/types.h" +#include "utility/utility.h" #include "data/graph/token_component/TokenComponentAccess.h" @@ -24,6 +25,7 @@ public: , hasParent(true) , accessType(TokenComponentAccess::ACCESS_NONE) , invisibleSubNodeCount(0) + , bundledNodeCount(0) { } @@ -101,6 +103,60 @@ public: return false; } + size_t getConnectedSubNodeCount() const + { + size_t count = 0; + + if (connected) + { + count += 1; + } + + for (const DummyNode& node : subNodes) + { + count += node.getConnectedSubNodeCount(); + } + + return count; + } + + std::vector getConnectedSubNodes() const + { + std::vector nodes; + + if (connected) + { + nodes.push_back(this); + } + + for (const DummyNode& node : subNodes) + { + utility::append(nodes, node.getConnectedSubNodes()); + } + + return nodes; + } + + std::vector getAllBundledNodes() const + { + std::vector nodes; + for (const DummyNode& node : bundledNodes) + { + utility::append(nodes, node.getConnectedSubNodes()); + } + return nodes; + } + + size_t getBundledNodeCount() const + { + if (bundledNodeCount > 0) + { + return bundledNodeCount; + } + + return bundledNodes.size(); + } + Vec2i position; Vec2i size; @@ -129,6 +185,7 @@ public: // BundleNode std::vector bundledNodes; + size_t bundledNodeCount; }; #endif // DUMMY_NODE_H diff --git a/src/lib/data/graph/Node.cpp b/src/lib/data/graph/Node.cpp index e05ca9cc..9746c6fc 100644 --- a/src/lib/data/graph/Node.cpp +++ b/src/lib/data/graph/Node.cpp @@ -287,6 +287,20 @@ void Node::forEachChildNode(std::function func) const ); } +void Node::forEachChildNodeRecursive(std::function func) const +{ + forEachEdgeOfType(Edge::EDGE_MEMBER, + [func, this](Edge* e) + { + if (this != e->getTo()) + { + func(e->getTo()); + e->getTo()->forEachChildNode(func); + } + } + ); +} + bool Node::hasReferences() const { if (getLocationIds().size() > 0) diff --git a/src/lib/data/graph/Node.h b/src/lib/data/graph/Node.h index dba291c0..f953ed69 100644 --- a/src/lib/data/graph/Node.h +++ b/src/lib/data/graph/Node.h @@ -80,6 +80,7 @@ public: void forEachEdge(std::function func) const; void forEachEdgeOfType(Edge::EdgeTypeMask mask, std::function func) const; void forEachChildNode(std::function func) const; + void forEachChildNodeRecursive(std::function func) const; bool hasReferences() const; diff --git a/src/lib_gui/qt/view/QtGraphView.cpp b/src/lib_gui/qt/view/QtGraphView.cpp index 07b6a687..f5843c5d 100644 --- a/src/lib_gui/qt/view/QtGraphView.cpp +++ b/src/lib_gui/qt/view/QtGraphView.cpp @@ -126,7 +126,9 @@ void QtGraphView::resizeView() Vec2i QtGraphView::getViewSize() const { QGraphicsView* view = getView(); - return Vec2i(view->width(), view->height()); + + float zoomFactor = GraphViewStyle::getZoomFactor(); + return Vec2i(view->width() / zoomFactor - 80, view->height() / zoomFactor - 80); } void QtGraphView::centerScrollBars() @@ -337,7 +339,7 @@ std::shared_ptr QtGraphView::createNodeRecursive( } else if (node.isBundleNode()) { - newNode = std::make_shared(node.tokenId, node.bundledNodes.size(), node.name); + newNode = std::make_shared(node.tokenId, node.getBundledNodeCount(), node.name); } newNode->setPosition(node.position);