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