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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user