UI: Overlaps resolve

Fixed several issues with the overlaps resolving algorithm.

Introducing the cell padding in the last graph update caused some issues that are now resolved.

Coincidently the average number of algorithm iterations needed could be reduced, therefore the thing is now faster.

fortune cookie message = All the answers you need are right there in front of you!
This commit is contained in:
Manuel
2015-03-04 23:07:34 -08:00
parent da1a6d69c4
commit 06aa7bdb58
5 changed files with 204 additions and 113 deletions
+173 -86
View File
@@ -1,5 +1,6 @@
#include "QtGraphPostprocessor.h"
// remark: maybe those two values could at some point be moved to an external config file (?)
unsigned int QtGraphPostprocessor::s_cellSize = 20;
unsigned int QtGraphPostprocessor::s_cellPadding = 10;
@@ -9,7 +10,7 @@ void QtGraphPostprocessor::doPostprocessing(std::list<std::shared_ptr<QtGraphNod
resizeNodes(nodes);
if(nodes.size() < 2)
if (nodes.size() < 2)
{
LOG_WARNING_STREAM(<< "Skipping postprocessing, need at least 2 nodes but got " << nodes.size());
return;
@@ -21,23 +22,23 @@ void QtGraphPostprocessor::doPostprocessing(std::list<std::shared_ptr<QtGraphNod
Vec2i centerOfMass(0, 0);
float totalMass = 0.0f;
std::list<std::shared_ptr<QtGraphNode>>::iterator it = nodes.begin();
for(; it != nodes.end(); it++)
for (; it != nodes.end(); it++)
{
if((*it)->getSize().x < divisor)
if ((*it)->getSize().x < divisor)
{
divisor = (*it)->getSize().x;
}
if((*it)->getSize().y < divisor)
if ((*it)->getSize().y < divisor)
{
divisor = (*it)->getSize().y;
}
if((*it)->getSize().x > maxNodeSize)
if ((*it)->getSize().x > maxNodeSize)
{
maxNodeSize = (*it)->getSize().x;
}
else if((*it)->getSize().y > maxNodeSize)
else if ((*it)->getSize().y > maxNodeSize)
{
maxNodeSize = (*it)->getSize().y;
}
@@ -49,14 +50,16 @@ void QtGraphPostprocessor::doPostprocessing(std::list<std::shared_ptr<QtGraphNod
centerOfMass /= totalMass;
divisor = std::min(divisor, (int)atomarGridSize);
divisor = (int)atomarGridSize + s_cellPadding; //std::min(divisor, (int)atomarGridSize);
resolveOutliers(nodes, centerOfMass);
// the nodes will be aligned everytime they move during post-processing
// align all nodes once here so that nodes that won't be moved again are aligned
it = nodes.begin();
for(; it != nodes.end(); it++)
for (; it != nodes.end(); it++)
{
allignNodeOnRaster((*it).get());
alignNodeOnRaster((*it).get());
}
MatrixDynamicBase<unsigned int> heatMap = buildHeatMap(nodes, divisor, maxNodeSize);
@@ -64,7 +67,7 @@ void QtGraphPostprocessor::doPostprocessing(std::list<std::shared_ptr<QtGraphNod
resolveOverlap(nodes, heatMap, divisor);
}
void QtGraphPostprocessor::allignNodeOnRaster(QtGraphNode* node)
void QtGraphPostprocessor::alignNodeOnRaster(QtGraphNode* node)
{
node->setPosition(alignOnRaster(node->getPosition()));
}
@@ -73,18 +76,18 @@ Vec2i QtGraphPostprocessor::alignOnRaster(Vec2i position)
{
int rasterPosDivisor = s_cellSize + s_cellPadding;
if(position.x % rasterPosDivisor != 0)
if (position.x % rasterPosDivisor != 0)
{
int t = position.x / rasterPosDivisor;
int r = position.x % rasterPosDivisor;
if(std::abs(r) > rasterPosDivisor/2)
if (std::abs(r) > rasterPosDivisor/2)
{
if(t != 0)
if (t != 0)
{
t += (t / std::abs(t));
}
else if(r != 0)
else if (r != 0)
{
t += (r / std::abs(r));
}
@@ -93,7 +96,7 @@ Vec2i QtGraphPostprocessor::alignOnRaster(Vec2i position)
position.x = t * rasterPosDivisor;
}
if(position.y % rasterPosDivisor != 0)
if (position.y % rasterPosDivisor != 0)
{
int t = position.y / rasterPosDivisor;
int r = position.y % rasterPosDivisor;
@@ -120,18 +123,18 @@ void QtGraphPostprocessor::resolveOutliers(std::list<std::shared_ptr<QtGraphNode
{
float maxDist = 0.0f;
std::list<std::shared_ptr<QtGraphNode>>::iterator it = nodes.begin();
for(; it != nodes.end(); it++)
for (; it != nodes.end(); it++)
{
Vec2i pos = (*it)->getPosition();
Vec2i toCenterOfMass = centerPoint - pos;
if(toCenterOfMass.getLength() > maxDist)
if (toCenterOfMass.getLength() > maxDist)
{
maxDist = toCenterOfMass.getLength();
}
}
it = nodes.begin();
for(; it != nodes.end(); it++)
for (; it != nodes.end(); it++)
{
Vec2i pos = (*it)->getPosition();
Vec2i toCenterOfMass = centerPoint - pos;
@@ -145,7 +148,7 @@ void QtGraphPostprocessor::resolveOutliers(std::list<std::shared_ptr<QtGraphNode
MatrixDynamicBase<unsigned int> QtGraphPostprocessor::buildHeatMap(const std::list<std::shared_ptr<QtGraphNode>>& nodes, const int atomarNodeSize, const int maxNodeSize)
{
int heatMapWidth = maxNodeSize * nodes.size() / atomarNodeSize;
int heatMapWidth = (maxNodeSize * nodes.size() / atomarNodeSize) * 5; // theoretically the nodes could horizontally or vertically far from the center, therefore '*5' (it's kinda arbitrary, generally *2 should suffice, I use *5 to prevent problems in extrem cases)
int heatMapHeight = heatMapWidth;
MatrixDynamicBase<unsigned int> heatMap(heatMapWidth, heatMapHeight);
@@ -155,18 +158,23 @@ MatrixDynamicBase<unsigned int> QtGraphPostprocessor::buildHeatMap(const std::li
{
int left = (*it)->getPosition().x / atomarNodeSize + heatMapWidth/2;
int up = (*it)->getPosition().y / atomarNodeSize + heatMapHeight/2;
int width = (*it)->getSize().x / atomarNodeSize;
int height = (*it)->getSize().y / atomarNodeSize;
Vec2i size = calculateRasterNodeSize(*it);
int width = size.x;
int height = size.y;
if(left + width > heatMapWidth || left < 0)
continue;
if(up + height > heatMapHeight || up < 0)
continue;
for(int i = 0; i < width; i++)
if (left + width > heatMapWidth || left < 0)
{
for(int j = 0; j < height; j++)
continue;
}
if (up + height > heatMapHeight || up < 0)
{
continue;
}
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
unsigned int x = left + i;
unsigned int y = up + j;
@@ -187,46 +195,48 @@ void QtGraphPostprocessor::resolveOverlap(std::list<std::shared_ptr<QtGraphNode>
bool overlap = true;
int iterationCount = 0;
int maxIterations = 10;
int maxIterations = 15;
while(overlap && iterationCount < maxIterations)
while (overlap && iterationCount < maxIterations)
{
LOG_WARNING_STREAM(<< iterationCount);
overlap = false;
iterationCount++;
std::list<std::shared_ptr<QtGraphNode>>::iterator it = nodes.begin();
for(; it != nodes.end(); it++)
for (; it != nodes.end(); it++)
{
Vec2i nodePos((*it)->getPosition().x / divisor + heatMapWidth/2,
(*it)->getPosition().y / divisor + heatMapHeight/2);
Vec2i nodeSize((*it)->getSize().x / divisor,
(*it)->getSize().y / divisor);
Vec2i nodePos(0, 0);
nodePos.x = (*it)->getPosition().x / divisor + heatMapWidth/2;
nodePos.y = (*it)->getPosition().y / divisor + heatMapHeight/2;
Vec2i nodeSize = calculateRasterNodeSize(*it);
if(nodePos.x + nodeSize.x > heatMapWidth || nodePos.x < 0)
if (nodePos.x + nodeSize.x > heatMapWidth || nodePos.x < 0)
{
LOG_WARNING("Leaving heatmap area in x");
continue;
}
if(nodePos.y + nodeSize.y > heatMapHeight || nodePos.y < 0)
if (nodePos.y + nodeSize.y > heatMapHeight || nodePos.y < 0)
{
LOG_WARNING("Leaving heatmap area in y");
continue;
}
Vec2f grad(0.0f, 0.0f);
if(getHeatmapGradient(grad, heatMap, nodePos, nodeSize))
if (getHeatmapGradient(grad, heatMap, nodePos, nodeSize))
{
overlap = true;
}
// handle overlap with no gradient
// e.g. when a node lies completely on top of another
// float gradLength = grad.getLength();
if(grad.getLengthSquared() <= 0.000001f)
if (grad.getLengthSquared() <= 0.000001f && overlap)
{
int val = heatMap.getValue(nodePos.x, nodePos.y);
if(val > 1)
{
grad = (*it)->getPosition();
grad.normalize();
grad *= -1.0f;
}
grad = (*it)->getPosition();
grad.normalize();
grad *= -1.0f;
}
// remove node temporarily from heat map, it will be re-added at the new position later on
@@ -239,29 +249,40 @@ void QtGraphPostprocessor::resolveOverlap(std::list<std::shared_ptr<QtGraphNode>
int maxOffset = 2*divisor;
// prevent the graph from "exploding" again...
if(xOffset > maxOffset)
if (xOffset > maxOffset)
{
xOffset = maxOffset;
else if(xOffset < -maxOffset)
}
else if (xOffset < -maxOffset)
{
xOffset = -maxOffset;
}
if(yOffset > maxOffset)
if (yOffset > maxOffset)
{
yOffset = maxOffset;
else if(yOffset < -maxOffset)
}
else if (yOffset < -maxOffset)
{
yOffset = -maxOffset;
}
Vec2i pos = (*it)->getPosition();
pos += Vec2i(xOffset, yOffset);
// grid allignment
// pos.x = (pos.x / divisor) * divisor;
// pos.y = (pos.y / divisor) * divisor;
(*it)->setPosition(pos);
allignNodeOnRaster((*it).get());
alignNodeOnRaster((*it).get());
// re-add node to heat map at new position
nodePos.x = (*it)->getPosition().x / divisor + heatMapWidth/2;
nodePos.y = (*it)->getPosition().y / divisor + heatMapHeight/2;
modifyHeatmapArea(heatMap, nodePos, nodeSize, 1);
if(getHeatmapGradient(grad, heatMap, nodePos, nodeSize))
{
overlap = true;
}
}
}
}
@@ -270,19 +291,19 @@ void QtGraphPostprocessor::modifyHeatmapArea(MatrixDynamicBase<unsigned int>& he
{
bool wentOutOfRange = false;
for(int i = 0; i < size.x; i++)
for (int i = 0; i < size.x; i++)
{
for(int j = 0; j < size.y; j++)
for (int j = 0; j < size.y; j++)
{
int x = leftUpperCorner.x + i;
int y = leftUpperCorner.y + j;
if(x < 0 || x > static_cast<int>(heatMap.getColumnsCount()-1))
if (x < 0 || x > static_cast<int>(heatMap.getColumnsCount()-1))
{
wentOutOfRange = true;
continue;
}
if(y < 0 || y > static_cast<int>(heatMap.getRowsCount()-1))
if (y < 0 || y > static_cast<int>(heatMap.getRowsCount()-1))
{
wentOutOfRange = true;
continue;
@@ -291,7 +312,7 @@ void QtGraphPostprocessor::modifyHeatmapArea(MatrixDynamicBase<unsigned int>& he
unsigned int value = heatMap.getValue(x, y);
heatMap.setValue(x, y, value+modifier);
if(wentOutOfRange == true)
if (wentOutOfRange == true)
{
LOG_WARNING("Left matrix range while trying to modify values.");
}
@@ -303,13 +324,17 @@ bool QtGraphPostprocessor::getHeatmapGradient(Vec2f& outGradient, const MatrixDy
{
bool overlap = false;
for(int i = 0; i < size.x; i++)
for (int i = 0; i < size.x; i++)
{
for(int j = 0; j < size.y; j++)
for (int j = 0; j < size.y; j++)
{
int x = leftUpperCorner.x + i;
int y = leftUpperCorner.y + j;
// weight factors that emphasize gradients near the nodes center
int hMagFactor = std::max(1, (int)(size.x*0.5 - std::abs(i+1 - size.x*0.5)));
int vMagFactor = std::max(1, (int)(size.y*0.5 - std::abs(j+1 - size.y*0.5)));
// if x and y lie directly at the border not all 4 neighbours can be checked
if(x < 1 || x > static_cast<int>(heatMap.getColumnsCount()-2))
continue;
@@ -317,10 +342,11 @@ bool QtGraphPostprocessor::getHeatmapGradient(Vec2f& outGradient, const MatrixDy
continue;
float val = heatMap.getValue(x, y);
float xP1 = heatMap.getValue(x+1, y);
float xM1 = heatMap.getValue(x-1, y);
float yP1 = heatMap.getValue(x, y+1);
float yM1 = heatMap.getValue(x, y-1);
float xP1 = heatMap.getValue(x+1, y) * hMagFactor;
float xM1 = heatMap.getValue(x-1, y) * hMagFactor;
float yP1 = heatMap.getValue(x, y+1) * vMagFactor;
float yM1 = heatMap.getValue(x, y-1) * vMagFactor;
xP1 = std::sqrt(xP1);
xM1 = std::sqrt(xM1);
@@ -332,7 +358,7 @@ bool QtGraphPostprocessor::getHeatmapGradient(Vec2f& outGradient, const MatrixDy
outGradient += Vec2f(xOffset, yOffset);
if(val > 1)
if (val > 1)
{
overlap = true;
}
@@ -342,10 +368,57 @@ bool QtGraphPostprocessor::getHeatmapGradient(Vec2f& outGradient, const MatrixDy
return overlap;
}
Vec2f QtGraphPostprocessor::heatMapRayCast(const MatrixDynamicBase<unsigned int>& heatMap, const Vec2f& startPosition, const Vec2f& direction, const int minValue)
{
float xOffset = 0.0f;
float yOffset = 0.0f;
if(std::abs(direction.x) > 0.0000000001f)
{
xOffset = direction.x / std::abs(direction.x);
}
if(std::abs(direction.y) > 0.0000000001f)
{
yOffset = direction.y / std::abs(direction.y);
}
if(startPosition.x < 1 || startPosition.x > static_cast<int>(heatMap.getColumnsCount()-2))
return Vec2f(0.0f, 0.0f);
if(startPosition.y < 1 || startPosition.y > static_cast<int>(heatMap.getRowsCount()-2))
return Vec2f(0.0f, 0.0f);
Vec2f length(0.0f, 0.0f);
bool hit = false;
float posX = startPosition.x + xOffset;
float posY = startPosition.y + yOffset;
do
{
if(heatMap.getValue(posX, posY) >= minValue)
{
hit = true;
length.x = length.x + xOffset;
length.y = length.y + yOffset;
posX += xOffset;
posY += yOffset;
}
else
{
hit = false;
}
}
while(hit);
return length;
}
void QtGraphPostprocessor::resizeNodes(std::list<std::shared_ptr<QtGraphNode>>& nodes)
{
std::list<std::shared_ptr<QtGraphNode>>::iterator it = nodes.begin();
for(; it != nodes.end(); it++)
for (; it != nodes.end(); it++)
{
Vec2i size = (*it)->getSize();
@@ -363,22 +436,36 @@ void QtGraphPostprocessor::resizeNodes(std::list<std::shared_ptr<QtGraphNode>>&
}
size.y = newHeight;
/*if(size.x % atomarSize != 0)
{
int multiplier = size.x / atomarSize;
++multiplier;
size.x = atomarSize * multiplier;
}
if(size.y % atomarSize != 0)
{
int multiplier = size.y / atomarSize;
++multiplier;
size.y = atomarSize * multiplier;
}*/
(*it)->setSize(size);
}
}
Vec2i QtGraphPostprocessor::calculateRasterNodeSize(const std::shared_ptr<QtGraphNode>& node)
{
Vec2i size = node->getSize();
Vec2i rasterSize(0, 0);
while(size.x > 0)
{
size.x = size.x - s_cellSize;
if(size.x > 0)
{
size.x = size.x - s_cellPadding;
}
rasterSize.x = rasterSize.x + 1;
}
while(size.y > 0)
{
size.y = size.y - s_cellSize;
if(size.y > 0)
{
size.y = size.y - s_cellPadding;
}
rasterSize.y = rasterSize.y + 1;
}
return rasterSize;
}