logic: Refactored graph node sorting

* Fixed sorting to always be alphabetical within each bucket
* Bundled nodes will expand in place
* Overview bundles are not sorted alphabetical
* Probably improved sorting performance by using std::set
This commit is contained in:
Eberhard Graether
2016-12-10 12:40:46 +01:00
parent 8bf02140b2
commit 05ad054421
5 changed files with 114 additions and 98 deletions
@@ -36,6 +36,7 @@ void GraphController::handleMessage(MessageActivateAll* message)
bundleNodesByType();
layoutNesting();
assignBundleIds();
layoutGraph();
buildGraph(message, false);
@@ -82,6 +83,7 @@ void GraphController::handleMessage(MessageActivateTokens* message)
layoutNesting();
layoutGraph(true);
assignBundleIds();
buildGraph(message, true);
}
@@ -133,7 +135,7 @@ void GraphController::handleMessage(MessageGraphNodeBundleSplit* message)
setActiveAndVisibility(tokenIds);
layoutNesting();
layoutGraph(tokenIds.size() > 0);
layoutGraph();
buildGraph(message, false);
}
@@ -785,13 +787,13 @@ void GraphController::bundleNodesAndEdgesMatching(
std::shared_ptr<DummyNode> node = m_dummyNodes[matchedNodeIndices[i]];
node->visible = false;
bundleNode->bundledNodes.push_back(node);
bundleNode->bundledNodes.insert(node);
bundleNode->bundledNodeCount += node->getBundledNodeCount();
m_dummyNodes.erase(m_dummyNodes.begin() + matchedNodeIndices[i]);
}
DummyNode* firstNode = bundleNode->bundledNodes[0].get();
DummyNode* firstNode = bundleNode->bundledNodes.begin()->get();
// Use token Id of first node and make first bit 1
bundleNode->tokenId = ~(~size_t(0) >> 1) + firstNode->data->getId();
@@ -875,12 +877,12 @@ std::shared_ptr<DummyNode> GraphController::bundleNodesMatching(
std::shared_ptr<DummyNode> node = *matchedNodes[i];
node->visible = false;
bundleNode->bundledNodes.push_back(node);
bundleNode->bundledNodes.insert(node);
nodes.erase(matchedNodes[i]);
}
// Use token Id of first node and make first bit 1
bundleNode->tokenId = ~(~size_t(0) >> 1) + bundleNode->bundledNodes[0]->data->getId();
bundleNode->tokenId = ~(~size_t(0) >> 1) + (*bundleNode->bundledNodes.begin())->data->getId();
return bundleNode;
}
@@ -973,19 +975,15 @@ void GraphController::bundleNodesByType()
for (std::shared_ptr<DummyNode> node : nodes)
{
bundleNode->bundledNodes.push_back(node);
bundleNode->bundledNodes.insert(node);
}
if (anonymousBundle)
{
anonymousBundle->sortBundleNode();
bundleNode->bundledNodeCount = bundleNode->getBundledNodeCount() + anonymousBundle->getBundledNodeCount();
bundleNode->bundledNodes.push_back(anonymousBundle);
bundleNode->bundledNodes.insert(anonymousBundle);
}
}
bundleNode->sortBundleNode();
}
}
@@ -1233,19 +1231,27 @@ void GraphController::layoutToGrid(DummyNode* node) const
}
}
void GraphController::layoutGraph(bool sort)
void GraphController::layoutGraph(bool getSortedNodes)
{
TRACE();
BucketGrid grid(getView()->getViewSize());
grid.createBuckets(m_dummyNodes, m_dummyEdges);
if (sort)
{
grid.sortBuckets();
}
grid.layoutBuckets();
if (getSortedNodes)
{
m_dummyNodes = grid.getSortedNodes();
}
}
void GraphController::assignBundleIds()
{
Id bundleId = 0;
for (size_t i = m_dummyNodes.size(); i > 0; i--)
{
bundleId = m_dummyNodes[i - 1]->setBundleIdRecursive(bundleId);
}
}
DummyNode* GraphController::getDummyGraphNodeById(Id tokenId) const
@@ -1505,7 +1511,7 @@ void GraphController::handleMessage(MessageColorSchemeTest* message)
focusedTokenIds.push_back(bundleNode->tokenId);
}
bundleNode->bundledNodes.push_back(std::make_shared<DummyNode>());
bundleNode->bundledNodes.insert(std::make_shared<DummyNode>());
bundleNode->bundledNodeCount = 123;
m_dummyNodes.push_back(bundleNode);
}
@@ -93,7 +93,8 @@ private:
void addExpandToggleNode(DummyNode* node) const;
void layoutToGrid(DummyNode* node) const;
void layoutGraph(bool sort = false);
void layoutGraph(bool getSortedNodes = false);
void assignBundleIds();
DummyNode* getDummyGraphNodeById(Id tokenId) const;
@@ -1,9 +1,6 @@
#include "component/controller/helper/BucketGrid.h"
#include "utility/utilityString.h"
#include "component/controller/helper/DummyEdge.h"
#include "component/controller/helper/DummyNode.h"
#include "component/view/GraphViewStyle.h"
Bucket::Bucket()
@@ -32,9 +29,9 @@ int Bucket::getHeight() const
return m_height;
}
bool Bucket::hasNode(DummyNode* node) const
bool Bucket::hasNode(std::shared_ptr<DummyNode> node) const
{
for (DummyNode* n : m_nodes)
for (std::shared_ptr<DummyNode> n : m_nodes)
{
if (node == n)
{
@@ -45,33 +42,17 @@ bool Bucket::hasNode(DummyNode* node) const
return false;
}
void Bucket::addNode(DummyNode* node)
void Bucket::addNode(std::shared_ptr<DummyNode> node)
{
m_nodes.push_back(node);
m_nodes.insert(node);
m_width = (node->size.x > m_width ? node->size.x : m_width);
m_height += node->size.y + GraphViewStyle::toGridGap(10);
}
void Bucket::sort()
const DummyNode::BundledNodesSet& Bucket::getNodes() const
{
std::sort(m_nodes.begin(), m_nodes.end(),
[](const DummyNode* a, const DummyNode* b) -> bool
{
if ((a->isGraphNode() && b->isGraphNode()) || (a->isBundleNode() && b->isBundleNode()))
{
return utility::toLowerCase(a->name) < utility::toLowerCase(b->name);
}
else if (a->isGraphNode())
{
return true;
}
else
{
return false;
}
}
);
return m_nodes;
}
void Bucket::preLayout(Vec2i viewSize)
@@ -85,7 +66,7 @@ void Bucket::preLayout(Vec2i viewSize)
m_height = 0;
for (DummyNode* node : m_nodes)
for (std::shared_ptr<DummyNode> node : m_nodes)
{
node->position.x = x;
node->position.y = y;
@@ -112,7 +93,7 @@ void Bucket::layout(int x, int y, int width, int height)
int cx = GraphViewStyle::toGridOffset(x + (width - m_width) / 2);
int cy = GraphViewStyle::toGridOffset(y + (height - m_height) / 2);
for (DummyNode* node : m_nodes)
for (std::shared_ptr<DummyNode> node : m_nodes)
{
node->position.x = node->position.x + cx;
node->position.y = node->position.y + cy;
@@ -143,7 +124,7 @@ void BucketGrid::createBuckets(
{
if (node->hasActiveSubNode() || !edges.size())
{
addNode(node.get());
addNode(node);
activeNodeAdded = true;
}
}
@@ -155,7 +136,7 @@ void BucketGrid::createBuckets(
if (!activeNodeAdded)
{
addNode(nodes[0].get());
addNode(nodes[0]);
}
std::vector<const DummyEdge*> remainingEdges;
@@ -169,8 +150,8 @@ void BucketGrid::createBuckets(
{
const DummyEdge* edge = remainingEdges[i];
DummyNode* owner = findTopMostDummyNodeRecursive(nodes, edge->ownerId);
DummyNode* target = findTopMostDummyNodeRecursive(nodes, edge->targetId);
std::shared_ptr<DummyNode> owner = findTopMostDummyNodeRecursive(nodes, edge->ownerId, nullptr);
std::shared_ptr<DummyNode> target = findTopMostDummyNodeRecursive(nodes, edge->targetId, nullptr);
bool removeEdge = false;
if (!owner || !target)
@@ -213,17 +194,6 @@ void BucketGrid::createBuckets(
}
}
void BucketGrid::sortBuckets()
{
for (int j = m_j1; j <= m_j2; j++)
{
for (int i = m_i1; i <= m_i2; i++)
{
m_buckets[j][i].sort();
}
}
}
void BucketGrid::layoutBuckets()
{
std::map<int, int> widths;
@@ -275,19 +245,35 @@ void BucketGrid::layoutBuckets()
}
}
DummyNode* BucketGrid::findTopMostDummyNodeRecursive(
std::vector<std::shared_ptr<DummyNode>>& nodes, Id tokenId, DummyNode* top
std::vector<std::shared_ptr<DummyNode>> BucketGrid::getSortedNodes()
{
std::vector<std::shared_ptr<DummyNode>> sortedNodes;
for (int j = m_j1; j <= m_j2; j++)
{
for (int i = m_i1; i <= m_i2; i++)
{
DummyNode::BundledNodesSet nodes = m_buckets[j][i].getNodes();
sortedNodes.insert(sortedNodes.end(), nodes.begin(), nodes.end());
}
}
return sortedNodes;
}
std::shared_ptr<DummyNode> BucketGrid::findTopMostDummyNodeRecursive(
std::vector<std::shared_ptr<DummyNode>>& nodes, Id tokenId, std::shared_ptr<DummyNode> top
){
for (std::shared_ptr<DummyNode> node : nodes)
{
DummyNode* t = (top ? top : node.get());
std::shared_ptr<DummyNode> t = (top ? top : node);
if (node->visible && node->tokenId == tokenId)
{
return t;
}
DummyNode* result = findTopMostDummyNodeRecursive(node->subNodes, tokenId, t);
std::shared_ptr<DummyNode> result = findTopMostDummyNodeRecursive(node->subNodes, tokenId, t);
if (result != nullptr)
{
return result;
@@ -297,13 +283,13 @@ DummyNode* BucketGrid::findTopMostDummyNodeRecursive(
return nullptr;
}
void BucketGrid::addNode(DummyNode* node)
void BucketGrid::addNode(std::shared_ptr<DummyNode> node)
{
Bucket* bucket = getBucket(node->layoutBucket.x, node->layoutBucket.y);
bucket->addNode(node);
}
bool BucketGrid::addNode(DummyNode* owner, DummyNode* target, bool horizontal)
bool BucketGrid::addNode(std::shared_ptr<DummyNode> owner, std::shared_ptr<DummyNode> target, bool horizontal)
{
Bucket* ownerBucket = getBucket(owner);
Bucket* targetBucket = getBucket(target);
@@ -389,7 +375,7 @@ Bucket* BucketGrid::getBucket(int i, int j)
return nullptr;
}
Bucket* BucketGrid::getBucket(DummyNode* node)
Bucket* BucketGrid::getBucket(std::shared_ptr<DummyNode> node)
{
for (int j = m_j1; j <= m_j2; j++)
{
@@ -6,10 +6,10 @@
#include "utility/math/Vector2.h"
#include "utility/types.h"
#include "component/controller/helper/DummyNode.h"
#include "data/graph/Edge.h"
struct DummyEdge;
struct DummyNode;
class Bucket
{
@@ -20,10 +20,9 @@ public:
int getWidth() const;
int getHeight() const;
bool hasNode(DummyNode* node) const;
void addNode(DummyNode* node);
void sort();
bool hasNode(std::shared_ptr<DummyNode> node) const;
void addNode(std::shared_ptr<DummyNode> node);
const DummyNode::BundledNodesSet& getNodes() const;
void preLayout(Vec2i viewSize);
void layout(int x, int y, int width, int height);
@@ -35,7 +34,7 @@ private:
int m_width;
int m_height;
std::vector<DummyNode*> m_nodes;
DummyNode::BundledNodesSet m_nodes;
};
@@ -46,18 +45,19 @@ public:
void createBuckets(
std::vector<std::shared_ptr<DummyNode>>& nodes,
const std::vector<std::shared_ptr<DummyEdge>>& edges);
void sortBuckets();
void layoutBuckets();
private:
DummyNode* findTopMostDummyNodeRecursive(
std::vector<std::shared_ptr<DummyNode>>& nodes, Id tokenId, DummyNode* top = nullptr);
std::vector<std::shared_ptr<DummyNode>> getSortedNodes();
void addNode(DummyNode* node);
bool addNode(DummyNode* owner, DummyNode* target, bool horizontal);
private:
std::shared_ptr<DummyNode> findTopMostDummyNodeRecursive(
std::vector<std::shared_ptr<DummyNode>>& nodes, Id tokenId, std::shared_ptr<DummyNode> top);
void addNode(std::shared_ptr<DummyNode> node);
bool addNode(std::shared_ptr<DummyNode> owner, std::shared_ptr<DummyNode> target, bool horizontal);
Bucket* getBucket(int i, int j);
Bucket* getBucket(DummyNode* node);
Bucket* getBucket(std::shared_ptr<DummyNode> node);
Vec2i m_viewSize;
std::map<int, std::map<int, Bucket>> m_buckets;
+39 -16
View File
@@ -15,6 +15,25 @@ class Node;
struct DummyNode
{
public:
struct DummyNodeComp
{
bool operator()(const std::shared_ptr<DummyNode> a, const std::shared_ptr<DummyNode> b) const
{
if (a->bundleId != b->bundleId)
{
return a->bundleId > b->bundleId;
}
else if (a->isBundleNode() != b->isBundleNode())
{
return a->isBundleNode();
}
return utility::toLowerCase(a->name) < utility::toLowerCase(b->name);
}
};
typedef std::set<std::shared_ptr<DummyNode>, DummyNodeComp> BundledNodesSet;
struct BundleInfo
{
BundleInfo()
@@ -46,6 +65,7 @@ public:
, hasQualifier(false)
, accessKind(ACCESS_NONE)
, invisibleSubNodeCount(0)
, bundleId(0)
, layoutBucket(0, 0)
, bundledNodeCount(0)
{
@@ -184,21 +204,6 @@ public:
return bundledNodes.size();
}
void sortBundleNode()
{
sort(bundledNodes.begin(), bundledNodes.end(),
[](const std::shared_ptr<DummyNode> a, const std::shared_ptr<DummyNode> b) -> bool
{
if (a->isBundleNode() != b->isBundleNode())
{
return a->isBundleNode();
}
return utility::toLowerCase(a->name) < utility::toLowerCase(b->name);
}
);
}
void forEachDummyNodeRecursive(std::function<void(DummyNode*)> func)
{
func(this);
@@ -209,6 +214,23 @@ public:
}
}
Id setBundleIdRecursive(Id bundleId)
{
if (isBundleNode())
{
bundleId++;
}
this->bundleId = bundleId;
for (std::shared_ptr<DummyNode> node : bundledNodes)
{
bundleId = node->setBundleIdRecursive(bundleId);
}
return bundleId;
}
Vec2i position;
Vec2i size;
@@ -239,12 +261,13 @@ public:
// Bundling
BundleInfo bundleInfo;
Id bundleId;
// Layout
Vec2i layoutBucket;
// BundleNode
std::vector<std::shared_ptr<DummyNode>> bundledNodes;
BundledNodesSet bundledNodes;
size_t bundledNodeCount;
// QualifierNode