UI: GraphLayouting
Implemented graph layouting, hybrid approach using spectral and force based layouting fortune cookie message = Giving credit where it's due ensures loyalty to you.
This commit is contained in:
@@ -47,6 +47,10 @@ set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
set(BOOST_LIBRARYDIR $ENV{BOOST_155_DIR})
|
||||
find_package(Boost 1.55 COMPONENTS system filesystem REQUIRED)
|
||||
|
||||
# Eigen -------------------------------------------------------------------------
|
||||
|
||||
set(EIGEN_ROOT $ENV{EIGEN_DIR})
|
||||
|
||||
# Lib --------------------------------------------------------------------------
|
||||
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/lib)
|
||||
@@ -78,6 +82,7 @@ set_property(
|
||||
${LLVM_INCLUDE_DIRS}
|
||||
${CLANG_INCLUDE_DIRS}
|
||||
${Boost_INCLUDE_DIRS}
|
||||
${EIGEN_ROOT}
|
||||
)
|
||||
|
||||
link_directories(${LLVM_LIBRARY_DIRS} ${CLANG_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS})
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* Valgrind 3.9.0(linux, macOS)
|
||||
* Clang & LLVM (installation guide http://clang.llvm.org/docs/LibASTMatchersTutorial.html)
|
||||
* Boost 1.55 ()
|
||||
* Eigen 3.2.3
|
||||
|
||||
##### Environment Variables
|
||||
|
||||
@@ -16,6 +17,7 @@
|
||||
* CXX_TEST_DIR - .../cxxtest-4.3/
|
||||
* CLANG_DIR - .../clang-llvm/
|
||||
* BOOST_155_DIR - .../boost_1_55_0
|
||||
* EIGEN_DIR - .../eigen
|
||||
|
||||
For Win32:
|
||||
* VLD_DIR - .../Visual Leak Detector
|
||||
|
||||
@@ -132,4 +132,141 @@ public:
|
||||
|
||||
private:
|
||||
int m_importantInt;
|
||||
};
|
||||
|
||||
class AnotherClass
|
||||
: public A
|
||||
{
|
||||
public:
|
||||
AnotherClass()
|
||||
: A('x')
|
||||
{
|
||||
}
|
||||
|
||||
int publicInt;
|
||||
protected:
|
||||
int protectedInt;
|
||||
private:
|
||||
int privateInt;
|
||||
};
|
||||
|
||||
|
||||
class circleA
|
||||
{
|
||||
public:
|
||||
circleA()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
//circleB m_b;
|
||||
};
|
||||
|
||||
class circleB
|
||||
{
|
||||
public:
|
||||
circleB()
|
||||
{
|
||||
m_a = circleA();
|
||||
}
|
||||
|
||||
circleA foo(){return circleA();}
|
||||
|
||||
private:
|
||||
circleA m_a;
|
||||
};
|
||||
|
||||
struct circleC
|
||||
{
|
||||
public:
|
||||
circleC()
|
||||
{
|
||||
m_b = circleB();
|
||||
}
|
||||
|
||||
circleA foo0(){return circleA();}
|
||||
circleB foo1(){return circleB();}
|
||||
|
||||
private:
|
||||
circleB m_b;
|
||||
};
|
||||
|
||||
class circleD
|
||||
{
|
||||
public:
|
||||
circleD()
|
||||
{
|
||||
m_c = circleC();
|
||||
}
|
||||
|
||||
circleC foo(){return circleC();}
|
||||
|
||||
circleA foo2(){return circleA();}
|
||||
circleB foo3(){return circleB();}
|
||||
|
||||
private:
|
||||
circleC m_c;
|
||||
};
|
||||
|
||||
class circleE
|
||||
{
|
||||
public:
|
||||
circleE()
|
||||
{
|
||||
m_d = circleD();
|
||||
}
|
||||
|
||||
circleC foo(){return circleC();}
|
||||
circleB foo1(){return circleB();}
|
||||
circleD foo2(){return circleD();}
|
||||
circleA foo3(){return circleA();}
|
||||
|
||||
private:
|
||||
circleD m_d;
|
||||
};
|
||||
|
||||
class circleF
|
||||
{
|
||||
public:
|
||||
circleF()
|
||||
{
|
||||
m_e = circleE();
|
||||
}
|
||||
|
||||
circleC foo(){return circleC();}
|
||||
circleB foo1(){return circleB();}
|
||||
circleD foo2(){return circleD();}
|
||||
circleA foo3(){return circleA();}
|
||||
circleE foo4(){return circleE();}
|
||||
|
||||
private:
|
||||
circleE m_e;
|
||||
};
|
||||
|
||||
class circleCenter
|
||||
{
|
||||
public:
|
||||
circleCenter()
|
||||
{
|
||||
m_a = circleA();
|
||||
m_b = circleB();
|
||||
m_c = circleC();
|
||||
m_d = circleD();
|
||||
}
|
||||
|
||||
circleA foo0(){return circleA();}
|
||||
circleB foo1(){return circleB();}
|
||||
circleC foo2(){return circleC();}
|
||||
circleD foo3(){return circleD();}
|
||||
circleE foo4(){return circleE();}
|
||||
circleF foo5(){return circleF();}
|
||||
|
||||
private:
|
||||
circleA m_a;
|
||||
circleB m_b;
|
||||
circleC m_c;
|
||||
circleD m_d;
|
||||
|
||||
//AnotherClass m_anotherClass;
|
||||
};
|
||||
@@ -26,6 +26,8 @@ add_files(
|
||||
qt/graphics/QtGraphicsRoundedRectItem.h
|
||||
|
||||
qt/utility/QtDeviceScaledPixmap.h
|
||||
qt/utility/QtGraphPostprocessor.cpp
|
||||
qt/utility/QtGraphPostprocessor.h
|
||||
qt/utility/QtHighlighter.cpp
|
||||
qt/utility/QtHighlighter.h
|
||||
qt/utility/QtThreadedFunctor.h
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
#include "qt/utility/utilityQt.h"
|
||||
#include "qt/view/QtViewFactory.h"
|
||||
#include "utility/logging/ConsoleLogger.h"
|
||||
#include "utility/logging/FileLogger.h"
|
||||
#include "utility/logging/LogManager.h"
|
||||
|
||||
void init()
|
||||
{
|
||||
LogManager::getInstance()->addLogger(std::make_shared<ConsoleLogger>());
|
||||
LogManager::getInstance()->addLogger(std::make_shared<FileLogger>());
|
||||
|
||||
utility::loadFontsFromDirectory("data/fonts", ".otf");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
#include "QtGraphPostprocessor.h"
|
||||
|
||||
void QtGraphPostprocessor::doPostprocessing(std::list<std::shared_ptr<QtGraphNode>>& nodes)
|
||||
{
|
||||
unsigned int atomarGridSize = 20;
|
||||
|
||||
resizeNodes(nodes, atomarGridSize);
|
||||
|
||||
if(nodes.size() < 2)
|
||||
{
|
||||
LOG_WARNING_STREAM(<< "Skipping postprocessing, need at least 2 nodes but got " << nodes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// determine center of mass (CoD) which is used to get outliers closer to the rest of the graph
|
||||
int divisor = 999999;
|
||||
int maxNodeSize = 0;
|
||||
Vec2i centerOfMass(0, 0);
|
||||
float totalMass = 0.0f;
|
||||
std::list<std::shared_ptr<QtGraphNode>>::iterator it = nodes.begin();
|
||||
for(it; it != nodes.end(); it++)
|
||||
{
|
||||
if((*it)->getSize().x < divisor)
|
||||
{
|
||||
divisor = (*it)->getSize().x;
|
||||
}
|
||||
|
||||
if((*it)->getSize().y < divisor)
|
||||
{
|
||||
divisor = (*it)->getSize().y;
|
||||
}
|
||||
|
||||
if((*it)->getSize().x > maxNodeSize)
|
||||
{
|
||||
maxNodeSize = (*it)->getSize().x;
|
||||
}
|
||||
else if((*it)->getSize().y > maxNodeSize)
|
||||
{
|
||||
maxNodeSize = (*it)->getSize().y;
|
||||
}
|
||||
|
||||
float nodeMass = (*it)->getSize().getLengthSquared();
|
||||
centerOfMass += (*it)->getPosition() * nodeMass;
|
||||
totalMass += nodeMass;
|
||||
}
|
||||
|
||||
centerOfMass /= totalMass;
|
||||
|
||||
divisor = std::min(divisor, (int)atomarGridSize);
|
||||
|
||||
resolveOutliers(nodes, centerOfMass);
|
||||
|
||||
MatrixDynamicBase<unsigned int> heatMap = buildHeatMap(nodes, divisor, maxNodeSize);
|
||||
resolveOverlap(nodes, heatMap, divisor);
|
||||
}
|
||||
|
||||
void QtGraphPostprocessor::resolveOutliers(std::list<std::shared_ptr<QtGraphNode>>& nodes, const Vec2i& centerPoint)
|
||||
{
|
||||
float maxDist = 0.0f;
|
||||
std::list<std::shared_ptr<QtGraphNode>>::iterator it = nodes.begin();
|
||||
for(it; it != nodes.end(); it++)
|
||||
{
|
||||
Vec2i pos = (*it)->getPosition();
|
||||
Vec2i toCenterOfMass = centerPoint - pos;
|
||||
if(toCenterOfMass.getLength() > maxDist)
|
||||
{
|
||||
maxDist = toCenterOfMass.getLength();
|
||||
}
|
||||
}
|
||||
|
||||
it = nodes.begin();
|
||||
for(it; it != nodes.end(); it++)
|
||||
{
|
||||
Vec2i pos = (*it)->getPosition();
|
||||
Vec2i toCenterOfMass = centerPoint - pos;
|
||||
float dist = toCenterOfMass.getLength();
|
||||
|
||||
float distFactor = std::sqrt(dist/maxDist); // causes far away nodes to be effected stronger than nodes that are already close to the center
|
||||
|
||||
(*it)->setPosition(pos + toCenterOfMass * distFactor);
|
||||
}
|
||||
}
|
||||
|
||||
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 heatMapHeight = heatMapWidth;
|
||||
|
||||
MatrixDynamicBase<unsigned int> heatMap(heatMapWidth, heatMapHeight);
|
||||
|
||||
std::list<std::shared_ptr<QtGraphNode>>::const_iterator it = nodes.cbegin();
|
||||
for(it; it != nodes.end(); it++)
|
||||
{
|
||||
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;
|
||||
|
||||
if(left + width > heatMapWidth || left < 0)
|
||||
continue;
|
||||
|
||||
if(up + height > heatMapHeight || up < 0)
|
||||
continue;
|
||||
|
||||
for(unsigned int i = 0; i < width; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < height; j++)
|
||||
{
|
||||
unsigned int x = left + i;
|
||||
unsigned int y = up + j;
|
||||
unsigned int value = heatMap.getValue(x, y);
|
||||
heatMap.setValue(x, y, value+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return heatMap;
|
||||
}
|
||||
|
||||
void QtGraphPostprocessor::resolveOverlap(std::list<std::shared_ptr<QtGraphNode>>& nodes, MatrixDynamicBase<unsigned int>& heatMap, const int divisor)
|
||||
{
|
||||
int heatMapWidth = heatMap.getColumnsCount();
|
||||
int heatMapHeight = heatMap.getRowsCount();
|
||||
|
||||
bool overlap = true;
|
||||
int iterationCount = 0;
|
||||
int maxIterations = 10;
|
||||
|
||||
while(overlap && iterationCount < maxIterations)
|
||||
{
|
||||
overlap = false;
|
||||
iterationCount++;
|
||||
|
||||
std::list<std::shared_ptr<QtGraphNode>>::iterator it = nodes.begin();
|
||||
for(it; 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);
|
||||
|
||||
if(nodePos.x + nodeSize.x > heatMapWidth || nodePos.x < 0)
|
||||
continue;
|
||||
|
||||
if(nodePos.y + nodeSize.y > heatMapHeight || nodePos.y < 0)
|
||||
continue;
|
||||
|
||||
Vec2f grad(0.0f, 0.0f);
|
||||
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)
|
||||
{
|
||||
int val = heatMap.getValue(nodePos.x, nodePos.y);
|
||||
if(val > 1)
|
||||
{
|
||||
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
|
||||
modifyHeatmapArea(heatMap, nodePos, nodeSize, -1);
|
||||
|
||||
// move node to new position
|
||||
int xOffset = grad.x * divisor;
|
||||
int yOffset = grad.y * divisor;
|
||||
|
||||
// prevent the graph from "exploding" again...
|
||||
if(xOffset > divisor)
|
||||
xOffset = divisor;
|
||||
else if(xOffset < -divisor)
|
||||
xOffset = -divisor;
|
||||
|
||||
if(yOffset > divisor)
|
||||
yOffset = divisor;
|
||||
else if(yOffset < -divisor)
|
||||
yOffset = -divisor;
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtGraphPostprocessor::modifyHeatmapArea(MatrixDynamicBase<unsigned int>& heatMap, const Vec2i& leftUpperCorner, const Vec2i& size, const int modifier)
|
||||
{
|
||||
bool wentOutOfRange = false;
|
||||
|
||||
for(unsigned int i = 0; i < size.x; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < size.y; j++)
|
||||
{
|
||||
int x = leftUpperCorner.x + i;
|
||||
int y = leftUpperCorner.y + j;
|
||||
|
||||
if(x < 0 || x > heatMap.getColumnsCount()-1)
|
||||
{
|
||||
wentOutOfRange = true;
|
||||
continue;
|
||||
}
|
||||
if(y < 0 || y > heatMap.getRowsCount()-1)
|
||||
{
|
||||
wentOutOfRange = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int value = heatMap.getValue(x, y);
|
||||
heatMap.setValue(x, y, value+modifier);
|
||||
|
||||
if(wentOutOfRange == true)
|
||||
{
|
||||
LOG_WARNING("Left matrix range while trying to modify values.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QtGraphPostprocessor::getHeatmapGradient(Vec2f& outGradient, const MatrixDynamicBase<unsigned int>& heatMap, const Vec2i& leftUpperCorner, const Vec2i& size)
|
||||
{
|
||||
bool overlap = false;
|
||||
|
||||
for(unsigned int i = 0; i < size.x; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < size.y; j++)
|
||||
{
|
||||
int x = leftUpperCorner.x + i;
|
||||
int y = leftUpperCorner.y + j;
|
||||
|
||||
// if x and y lie directly at the border not all 4 neighbours can be checked
|
||||
if(x < 1 || x > heatMap.getColumnsCount()-2)
|
||||
continue;
|
||||
if(y < 1 || y > heatMap.getRowsCount()-2)
|
||||
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);
|
||||
|
||||
xP1 = std::sqrtf(xP1);
|
||||
xM1 = std::sqrtf(xM1);
|
||||
yP1 = std::sqrtf(yP1);
|
||||
yM1 = std::sqrtf(yM1);
|
||||
|
||||
float xOffset = (xM1 - val) + (val - xP1);
|
||||
float yOffset = (yM1 - val) + (val - yP1);
|
||||
|
||||
outGradient += Vec2f(xOffset, yOffset);
|
||||
|
||||
if(val > 1)
|
||||
{
|
||||
overlap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return overlap;
|
||||
}
|
||||
|
||||
void QtGraphPostprocessor::resizeNodes(std::list<std::shared_ptr<QtGraphNode>>& nodes, const unsigned int atomarSize)
|
||||
{
|
||||
std::list<std::shared_ptr<QtGraphNode>>::iterator it = nodes.begin();
|
||||
for(it; it != nodes.end(); it++)
|
||||
{
|
||||
Vec2i size = (*it)->getSize();
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef QT_GRAPH_POSTPROCESSOR_H
|
||||
#define QT_GRAPH_POSTPROCESSOR_H
|
||||
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
#include "utility/math/MatrixDynamicBase.h"
|
||||
|
||||
#include "qt/view/graphElements/QtGraphNode.h"
|
||||
|
||||
class QtGraphPostprocessor
|
||||
{
|
||||
public:
|
||||
static void doPostprocessing(std::list<std::shared_ptr<QtGraphNode>>& nodes);
|
||||
|
||||
private:
|
||||
static MatrixDynamicBase<unsigned int> buildHeatMap(const std::list<std::shared_ptr<QtGraphNode>>& nodes, const int atomarNodeSize, const int maxNodeSize);
|
||||
static void resolveOutliers(std::list<std::shared_ptr<QtGraphNode>>& nodes, const Vec2i& centerPoint);
|
||||
static void resolveOverlap(std::list<std::shared_ptr<QtGraphNode>>& nodes, MatrixDynamicBase<unsigned int>& heatMap, const int divisor);
|
||||
static void modifyHeatmapArea(MatrixDynamicBase<unsigned int>& heatMap, const Vec2i& leftUpperCorner, const Vec2i& size, const int modifier);
|
||||
static bool getHeatmapGradient(Vec2f& outGradient, const MatrixDynamicBase<unsigned int>& heatMap, const Vec2i& leftUpperCorner, const Vec2i& size);
|
||||
static void resizeNodes(std::list<std::shared_ptr<QtGraphNode>>& nodes, const unsigned int atomarSize);
|
||||
};
|
||||
|
||||
#endif // QT_GRAPH_POSTPROCESSOR_H
|
||||
@@ -1,10 +1,13 @@
|
||||
#include "QtGraphView.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QFrame>
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsView>
|
||||
|
||||
#include "qt/utility/QtGraphPostprocessor.h"
|
||||
#include "qt/utility/utilityQt.h"
|
||||
|
||||
#include "qt/view/QtViewWidgetWrapper.h"
|
||||
@@ -136,6 +139,8 @@ void QtGraphView::doRebuildGraph(
|
||||
m_graph = graph;
|
||||
}
|
||||
|
||||
QtGraphPostprocessor::doPostprocessing(m_nodes);
|
||||
|
||||
// Manually hover the items below the mouse cursor.
|
||||
view->scene()->setSceneRect(view->scene()->itemsBoundingRect());
|
||||
QPointF point = view->mapToScene(view->mapFromGlobal(QCursor::pos()));
|
||||
@@ -148,6 +153,8 @@ void QtGraphView::doRebuildGraph(
|
||||
node->hoverEnter();
|
||||
}
|
||||
}
|
||||
|
||||
m_graph = graph;
|
||||
}
|
||||
|
||||
void QtGraphView::doClear()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define QT_GRAPH_VIEW_H
|
||||
|
||||
#include "qt/utility/QtThreadedFunctor.h"
|
||||
#include "utility/math/MatrixDynamicBase.h"
|
||||
#include "utility/math/Vector4.h"
|
||||
#include "utility/types.h"
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
add_files(
|
||||
EXTERNAL_FILES
|
||||
|
||||
|
||||
tinyxml/tinystr.cpp
|
||||
tinyxml/tinystr.h
|
||||
tinyxml/tinyxml.cpp
|
||||
|
||||
@@ -192,6 +192,8 @@ add_files(
|
||||
utility/logging/PlainFileLogger.h
|
||||
|
||||
utility/math/Color.h
|
||||
utility/math/MatrixBase.h
|
||||
utility/math/MatrixDynamicBase.h
|
||||
utility/math/Vector2.h
|
||||
utility/math/Vector4.h
|
||||
utility/math/VectorBase.h
|
||||
|
||||
@@ -105,8 +105,6 @@ void GraphController::setActiveTokenIds(const std::vector<Id>& activeTokenIds)
|
||||
|
||||
void GraphController::createDummyGraphForTokenIds(const std::vector<Id>& tokenIds)
|
||||
{
|
||||
const GraphLayouter::LayoutFunction layoutFunction = &GraphLayouter::layoutSimpleRaster;
|
||||
|
||||
GraphView* view = getView();
|
||||
if (!view)
|
||||
{
|
||||
@@ -142,7 +140,7 @@ void GraphController::createDummyGraphForTokenIds(const std::vector<Id>& tokenId
|
||||
setActiveAndVisibility(tokenIds);
|
||||
|
||||
layoutNesting();
|
||||
layoutFunction(m_dummyNodes);
|
||||
GraphLayouter::layoutSpectralPrototype(m_dummyNodes, m_dummyEdges);
|
||||
|
||||
view->rebuildGraph(graph, m_dummyNodes, m_dummyEdges);
|
||||
}
|
||||
@@ -150,6 +148,19 @@ void GraphController::createDummyGraphForTokenIds(const std::vector<Id>& tokenId
|
||||
DummyNode GraphController::createDummyNodeTopDown(Node* node)
|
||||
{
|
||||
DummyNode result(node);
|
||||
result.tokenId = node->getId();
|
||||
|
||||
// there is a global root node with id 0 afaik, so here we actually want the one node below this global root
|
||||
Node* parent = node;
|
||||
while(parent != NULL && parent->getParentNode() != NULL)
|
||||
{
|
||||
parent = parent->getParentNode();
|
||||
}
|
||||
|
||||
if(parent != NULL)
|
||||
{
|
||||
result.topLevelAncestorId = parent->getId();
|
||||
}
|
||||
|
||||
node->forEachChildNode(
|
||||
[node, &result, this](Node* child)
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
#include "component/controller/GraphLayouter.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
|
||||
#include "component/view/graphElements/GraphEdge.h"
|
||||
#include "component/view/graphElements/GraphNode.h"
|
||||
|
||||
#include "utility/math/MatrixDynamicBase.h"
|
||||
|
||||
// for prototyping, remove when done
|
||||
#include "Eigen/Dense"
|
||||
#include "Eigen/Eigenvalues"
|
||||
#include "unsupported/Eigen/MatrixFunctions"
|
||||
|
||||
bool compareEigenvaluePairs(const std::pair<int, double>& p0, const std::pair<int, double>& p1)
|
||||
{
|
||||
return p0.second > p1.second;
|
||||
}
|
||||
|
||||
void GraphLayouter::layoutSimpleRaster(std::vector<DummyNode>& nodes)
|
||||
{
|
||||
int x = 0;
|
||||
@@ -48,3 +65,184 @@ void GraphLayouter::layoutSimpleRing(std::vector<DummyNode>& nodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphLayouter::layoutSpectralPrototype(std::vector<DummyNode>& nodes, const std::vector<DummyEdge>& edges)
|
||||
{
|
||||
if(nodes.size() < 2)
|
||||
{
|
||||
LOG_WARNING("Not enough nodes for layouting");
|
||||
return;
|
||||
}
|
||||
|
||||
MatrixDynamicBase<int> laplacian = buildLaplacianMatrix(nodes, edges);
|
||||
|
||||
Eigen::MatrixXd degreeMatrix(laplacian.getColumnsCount(), laplacian.getRowsCount());
|
||||
Eigen::MatrixXd eigenMatrix(laplacian.getColumnsCount(), laplacian.getRowsCount());
|
||||
|
||||
for(unsigned int x = 0; x < laplacian.getColumnsCount(); x++)
|
||||
{
|
||||
for(unsigned int y = 0; y < laplacian.getRowsCount(); y++)
|
||||
{
|
||||
eigenMatrix(x, y) = laplacian.getValue(x, y);
|
||||
|
||||
if(x == y)
|
||||
{
|
||||
degreeMatrix(x, y) = laplacian.getValue(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
degreeMatrix = degreeMatrix.inverse();
|
||||
Eigen::MatrixPower<Eigen::MatrixXd> dPow(degreeMatrix);
|
||||
degreeMatrix = dPow(0.5);
|
||||
|
||||
eigenMatrix = degreeMatrix * eigenMatrix * degreeMatrix;
|
||||
|
||||
eigenMatrix.normalize();
|
||||
Eigen::EigenSolver<Eigen::MatrixXd> solver(eigenMatrix);
|
||||
|
||||
std::vector<std::vector<double>> eigenVectors;
|
||||
for(unsigned int i = 0; i < solver.eigenvectors().cols(); i++)
|
||||
{
|
||||
eigenVectors.push_back(std::vector<double>());
|
||||
|
||||
for(unsigned int j = 0; j < solver.eigenvectors().rows(); j++)
|
||||
{
|
||||
eigenVectors[i].push_back(solver.eigenvectors()(i*solver.eigenvectors().rows() + j).real());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, double>> eigenValues;
|
||||
for(unsigned int i = 0; i < solver.eigenvalues().size(); i++)
|
||||
{
|
||||
eigenValues.push_back(std::pair<int, double>(i, solver.eigenvalues()(i).real()));
|
||||
}
|
||||
|
||||
std::sort(eigenValues.begin(), eigenValues.end(), compareEigenvaluePairs);
|
||||
|
||||
if(eigenVectors.size() > 0 && eigenVectors[0].size() >= 3)
|
||||
{
|
||||
unsigned int xIdx = eigenValues[eigenValues.size()-2].first;
|
||||
unsigned int yIdx = eigenValues[eigenValues.size()-3].first;
|
||||
|
||||
/*double xEigenValue = std::sqrt(solver.eigenvalues()(xIdx).real());
|
||||
double yEigenValue = std::sqrt(solver.eigenvalues()(yIdx).real());*/
|
||||
|
||||
for(unsigned int i = 0; i < nodes.size(); i++)
|
||||
{
|
||||
float xPos = eigenVectors[xIdx][i];
|
||||
float yPos = eigenVectors[yIdx][i];
|
||||
|
||||
Vec2f newPos(xPos, yPos);
|
||||
|
||||
newPos.normalize();
|
||||
newPos *= 600.0f;
|
||||
|
||||
nodes[i].position.x = newPos.x;
|
||||
nodes[i].position.y = newPos.y;
|
||||
|
||||
//std::cout << newPos << std::endl;
|
||||
}
|
||||
//std::cout << "=================" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
MatrixDynamicBase<int> GraphLayouter::buildLaplacianMatrix(const std::vector<DummyNode>& nodes, const std::vector<DummyEdge>& edges)
|
||||
{
|
||||
MatrixDynamicBase<int> matrix(nodes.size(), nodes.size());
|
||||
|
||||
std::map<Id, DummyNode> nodesMap;
|
||||
std::queue<DummyNode> remainingNodes;
|
||||
for(unsigned int i = 0; i < nodes.size(); i++)
|
||||
{
|
||||
remainingNodes.push(nodes[i]);
|
||||
}
|
||||
|
||||
while(remainingNodes.size() > 0)
|
||||
{
|
||||
if(remainingNodes.front().subNodes.size() > 0)
|
||||
{
|
||||
for(unsigned int i = 0; i < remainingNodes.front().subNodes.size(); i++)
|
||||
{
|
||||
remainingNodes.push(remainingNodes.front().subNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
nodesMap[remainingNodes.front().tokenId] = remainingNodes.front();
|
||||
remainingNodes.pop();
|
||||
}
|
||||
|
||||
std::map<std::pair<Id, Id>, int> weightsMap;
|
||||
for(unsigned int i = 0; i < edges.size(); i++)
|
||||
{
|
||||
DummyNode ownerNode = nodesMap[edges[i].ownerId];
|
||||
DummyNode targetNode = nodesMap[edges[i].targetId];
|
||||
|
||||
if(ownerNode.topLevelAncestorId != targetNode.topLevelAncestorId)
|
||||
{
|
||||
int weightIncrement = 1;
|
||||
|
||||
Id ownerId = ownerNode.topLevelAncestorId;
|
||||
Id targetId = targetNode.topLevelAncestorId;
|
||||
std::pair<Id, Id> key(ownerId, targetId);
|
||||
std::pair<Id, Id> inverseKey(targetId, ownerId);
|
||||
std::pair<Id, Id> keyOwner(ownerId, ownerId);
|
||||
std::pair<Id, Id> keyTarget(targetId, targetId);
|
||||
|
||||
std::map<std::pair<Id, Id>, int>::iterator it = weightsMap.find(key);
|
||||
if(it == weightsMap.end())
|
||||
{
|
||||
weightsMap[key] = 0;
|
||||
}
|
||||
|
||||
weightsMap[key] += weightIncrement;
|
||||
|
||||
it = weightsMap.find(inverseKey);
|
||||
if(it == weightsMap.end())
|
||||
{
|
||||
weightsMap[inverseKey] = 0;
|
||||
}
|
||||
|
||||
weightsMap[inverseKey] += weightIncrement;
|
||||
|
||||
it = weightsMap.find(keyOwner);
|
||||
if(it == weightsMap.end())
|
||||
{
|
||||
weightsMap[keyOwner] = 0;
|
||||
}
|
||||
|
||||
weightsMap[keyOwner] += weightIncrement;
|
||||
|
||||
it = weightsMap.find(keyTarget);
|
||||
if(it == weightsMap.end())
|
||||
{
|
||||
weightsMap[keyTarget] = 0;
|
||||
}
|
||||
|
||||
weightsMap[keyTarget] += weightIncrement;
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int x = 0; x < nodes.size(); x++)
|
||||
{
|
||||
for(unsigned int y = x; y < nodes.size(); y++)
|
||||
{
|
||||
unsigned int xNodeId = nodes[x].tokenId;
|
||||
unsigned int yNodeId = nodes[y].tokenId;
|
||||
|
||||
std::pair<Id, Id> key(xNodeId, yNodeId);
|
||||
|
||||
if(x == y)
|
||||
{
|
||||
matrix.setValue(x, y, weightsMap[key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
matrix.setValue(x, y, -weightsMap[key]);
|
||||
matrix.setValue(y, x, -weightsMap[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
template<class T>
|
||||
class MatrixDynamicBase;
|
||||
|
||||
struct DummyEdge;
|
||||
struct DummyNode;
|
||||
|
||||
class GraphLayouter
|
||||
@@ -12,6 +16,11 @@ public:
|
||||
|
||||
static void layoutSimpleRaster(std::vector<DummyNode>& nodes);
|
||||
static void layoutSimpleRing(std::vector<DummyNode>& nodes);
|
||||
|
||||
static void layoutSpectralPrototype(std::vector<DummyNode>& nodes, const std::vector<DummyEdge>& edges);
|
||||
|
||||
private:
|
||||
static MatrixDynamicBase<int> buildLaplacianMatrix(const std::vector<DummyNode>& nodes, const std::vector<DummyEdge>& edges);
|
||||
};
|
||||
|
||||
#endif // GRAPH_LAYOUTER_H
|
||||
|
||||
@@ -15,6 +15,11 @@ CodeView::CodeSnippetParams::CodeSnippetParams()
|
||||
|
||||
bool CodeView::CodeSnippetParams::sort(const CodeSnippetParams& a, const CodeSnippetParams& b)
|
||||
{
|
||||
if(a.isActive && b.isActive)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// sort active snippet first
|
||||
if (a.isActive && !b.isActive)
|
||||
{
|
||||
|
||||
@@ -47,8 +47,9 @@ protected:
|
||||
|
||||
struct DummyNode
|
||||
{
|
||||
DummyNode(const Node* data)
|
||||
: data(data)
|
||||
public:
|
||||
DummyNode()
|
||||
: data(nullptr)
|
||||
, accessType(TokenComponentAccess::ACCESS_NONE)
|
||||
, active(false)
|
||||
, connected(false)
|
||||
@@ -60,6 +61,12 @@ struct DummyNode
|
||||
{
|
||||
}
|
||||
|
||||
DummyNode(const Node* data)
|
||||
: data(data)
|
||||
, accessType(TokenComponentAccess::ACCESS_NONE)
|
||||
{
|
||||
}
|
||||
|
||||
DummyNode(TokenComponentAccess::AccessType accessType)
|
||||
: data(nullptr)
|
||||
, accessType(accessType)
|
||||
@@ -78,6 +85,45 @@ struct DummyNode
|
||||
return expanded || autoExpanded;
|
||||
}
|
||||
|
||||
bool operator==(const DummyNode& other) const
|
||||
{
|
||||
if (data->getId() == other.data->getId()
|
||||
&& data->getName() == other.data->getName())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(const DummyNode& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const DummyNode& other) const
|
||||
{
|
||||
if (data->getId() < other.data->getId())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator>(const DummyNode& other) const
|
||||
{
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
DummyNode& operator=(const DummyNode& other)
|
||||
{
|
||||
data = other.data;
|
||||
subNodes = other.subNodes;
|
||||
position = other.position;
|
||||
topLevelAncestorId = other.topLevelAncestorId;
|
||||
tokenId = other.tokenId;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Node* data;
|
||||
TokenComponentAccess::AccessType accessType;
|
||||
|
||||
@@ -93,7 +139,10 @@ struct DummyNode
|
||||
size_t invisibleSubNodeCount;
|
||||
|
||||
bool visible;
|
||||
|
||||
|
||||
Id topLevelAncestorId;
|
||||
Id tokenId;
|
||||
|
||||
std::vector<DummyNode> subNodes;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "data/Storage.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "utility/logging/logging.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
@@ -553,6 +555,7 @@ std::shared_ptr<Graph> Storage::getGraphForActiveTokenIds(const std::vector<Id>&
|
||||
else if (tokenIds.size() == 1)
|
||||
{
|
||||
Token* token = m_graph.getTokenById(tokenIds[0]);
|
||||
|
||||
if (!token)
|
||||
{
|
||||
LOG_ERROR_STREAM(<< "Token with id " << tokenIds[0] << " was not found");
|
||||
@@ -585,6 +588,28 @@ std::shared_ptr<Graph> Storage::getGraphForActiveTokenIds(const std::vector<Id>&
|
||||
}
|
||||
}
|
||||
|
||||
for(const std::pair<Id, std::shared_ptr<Node>> nodePair : graph->getNodes())
|
||||
{
|
||||
Node* node = m_graph.getNodeById(nodePair.first);
|
||||
|
||||
node->forEachEdge(
|
||||
[graph](Edge* edge)
|
||||
{
|
||||
if(edge->getType() != Edge::EdgeType::EDGE_MEMBER)
|
||||
{
|
||||
Node* from = edge->getFrom();
|
||||
Node* to = edge->getTo();
|
||||
|
||||
if(graph->findNode([from](Node* node){return from->getId() == node->getId();}) != NULL
|
||||
&& graph->findNode([to](Node* node){return to->getId() == node->getId();}) != NULL)
|
||||
{
|
||||
graph->addEdge(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,525 @@
|
||||
#ifndef MATRIX_BASE_H
|
||||
#define MATRIX_BASE_H
|
||||
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "VectorBase.h"
|
||||
|
||||
#define MATRIX_CHECK_INDEX(nIdx, mIdx) \
|
||||
do \
|
||||
{ \
|
||||
unsigned int n((nIdx)); \
|
||||
unsigned int m((mIdx)); \
|
||||
checkIndexInRange(n, m, __FUNCTION__); \
|
||||
} \
|
||||
while (0) \
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
class MatrixBase
|
||||
{
|
||||
public:
|
||||
MatrixBase();
|
||||
MatrixBase(const T values[N][M]);
|
||||
template<class U>
|
||||
MatrixBase(const MatrixBase<U, N, M>& matrix);
|
||||
~MatrixBase();
|
||||
|
||||
T getValue(const unsigned int columnIndex, const unsigned int rowIndex) const;
|
||||
void setValue(const unsigned int columnIndex, const unsigned int rowIndex, const T& value);
|
||||
|
||||
unsigned int getColumnsCount() const;
|
||||
unsigned int getRowsCount() const;
|
||||
|
||||
MatrixBase<T, M, N> transposed() const;
|
||||
|
||||
template<class U>
|
||||
void assign(const MatrixBase<U, N, M>& other);
|
||||
|
||||
template<class U>
|
||||
MatrixBase<T, N, M> add(const MatrixBase<U, N, M>& other);
|
||||
template<class U>
|
||||
MatrixBase<T, N, M> subtract(const MatrixBase<U, N, M>& other);
|
||||
template<class U>
|
||||
MatrixBase<T, N, M> scalarMultiplication(const U& scalar);
|
||||
template<class U, unsigned int P>
|
||||
MatrixBase<T, P, M> matrixMultiplication(const MatrixBase<U, P, N>& other) const;
|
||||
|
||||
// Checks whether all values are the same.
|
||||
template<class U>
|
||||
bool isEqual(const MatrixBase<U, N, M>& other) const;
|
||||
// Checks whether it really is the same object (at one and the same memory address).
|
||||
template<class U>
|
||||
bool isSame(const MatrixBase<U, N, M>& other) const;
|
||||
|
||||
// jep, that's how you return an array...
|
||||
T (&operator[](const unsigned int index))[M];
|
||||
|
||||
template<class U>
|
||||
void operator=(const MatrixBase<U, N, M>& other);
|
||||
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> operator+(const MatrixBase<U, N, M>& other) const;
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> operator-(const MatrixBase<U, N, M>& other) const;
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> operator*(const U& scalar) const;
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> operator/(const U& scalar) const;
|
||||
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> operator+=(const MatrixBase<U, N, M>& other);
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> operator-=(const MatrixBase<U, N, M>& other);
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> operator*=(const U& scalar);
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> operator/=(const U& scalar);
|
||||
|
||||
// Checks whether all values are the same.
|
||||
template<class U>
|
||||
bool operator==(const MatrixBase<U, N, M>& other) const;
|
||||
// Checks whether at least one value is different.
|
||||
template<class U>
|
||||
bool operator!=(const MatrixBase<U, N, M>& other) const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
protected:
|
||||
T m_values[N][M];
|
||||
|
||||
private:
|
||||
inline void checkIndexInRange(unsigned int columnIndex, unsigned int rowIndex, const std::string& function) const
|
||||
{
|
||||
std::stringstream message;
|
||||
|
||||
if (columnIndex >= N)
|
||||
{
|
||||
message << function << ": columnIndex " << columnIndex << " is out of range, maximum is " << N - 1;
|
||||
}
|
||||
|
||||
if (rowIndex >= M)
|
||||
{
|
||||
if(message.str().length() > 0)
|
||||
{
|
||||
message << "\n";
|
||||
}
|
||||
|
||||
message << function << ": rowIndex " << rowIndex << " is out of range, maximum is " << M - 1;
|
||||
}
|
||||
|
||||
if(message.str().length() > 0)
|
||||
{
|
||||
throw std::range_error(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline void setValues(const U values[N][M])
|
||||
{
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < M; j++)
|
||||
{
|
||||
m_values[i][j] = (T)values[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class U>
|
||||
inline void setValues(const MatrixBase<U, N, M>& matrix)
|
||||
{
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < M; j++)
|
||||
{
|
||||
m_values[i][j] = (T)matrix.getValue(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
MatrixBase<T, N, M>::MatrixBase()
|
||||
{}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
MatrixBase<T, N, M>::MatrixBase(const T values[N][M])
|
||||
{
|
||||
setValues(values);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<T, N, M>::MatrixBase(const MatrixBase<U, N, M>& matrix)
|
||||
{
|
||||
setValues(matrix);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
MatrixBase<T, N, M>::~MatrixBase()
|
||||
{
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
T MatrixBase<T, N, M>::getValue(const unsigned int columnIndex, const unsigned int rowIndex) const
|
||||
{
|
||||
MATRIX_CHECK_INDEX(columnIndex, rowIndex);
|
||||
|
||||
return m_values[columnIndex][rowIndex];
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
void MatrixBase<T, N, M>::setValue(const unsigned int columnIndex, const unsigned int rowIndex, const T& value)
|
||||
{
|
||||
MATRIX_CHECK_INDEX(columnIndex, rowIndex);
|
||||
|
||||
m_values[columnIndex][rowIndex] = value;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
unsigned int MatrixBase<T, N, M>::getColumnsCount() const
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
unsigned int MatrixBase<T, N, M>::getRowsCount() const
|
||||
{
|
||||
return M;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
MatrixBase<T, M, N> MatrixBase<T, N, M>::transposed() const
|
||||
{
|
||||
T tmpValues[M][N];
|
||||
|
||||
for(unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < M; j++)
|
||||
{
|
||||
tmpValues[j][i] = m_values[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
return MatrixBase<T, M, N>(tmpValues);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
void MatrixBase<T, N, M>::assign(const MatrixBase<U, N, M>& other)
|
||||
{
|
||||
if(isSame(other))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(isEqual(other))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setValues(other.m_values);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<T, N, M> MatrixBase<T, N, M>::add(const MatrixBase<U, N, M>& other)
|
||||
{
|
||||
T tmpValues[N][M];
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < M; j++)
|
||||
{
|
||||
tmpValues[i][j] = m_values[i][j] + other.m_values[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// The values of *this won't be changed until they are all in a valid state.
|
||||
setValues(tmpValues);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<T, N, M> MatrixBase<T, N, M>::subtract(const MatrixBase<U, N, M>& other)
|
||||
{
|
||||
T tmpValues[N][M];
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < M; j++)
|
||||
{
|
||||
tmpValues[i][j] = m_values[i][j] - other.m_values[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// The values of *this won't be changed until they are all in a valid state.
|
||||
setValues(tmpValues);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<T, N, M> MatrixBase<T, N, M>::scalarMultiplication(const U& scalar)
|
||||
{
|
||||
T tmpValues[N][M];
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < M; j++)
|
||||
{
|
||||
tmpValues[i][j] = m_values[i][j] * scalar;
|
||||
}
|
||||
}
|
||||
|
||||
// The values of *this won't be changed until they are all in a valid state.
|
||||
setValues(tmpValues);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U, unsigned int P>
|
||||
MatrixBase<T, P, M> MatrixBase<T, N, M>::matrixMultiplication(const MatrixBase<U, P, N>& other) const
|
||||
{
|
||||
MatrixBase<T, P, M> result;
|
||||
|
||||
for(unsigned int m = 0; m < M; m++)
|
||||
{
|
||||
for(unsigned int p = 0; p < P; p++)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
for(unsigned int n = 0; n < N; n++)
|
||||
{
|
||||
val += m_values[n][m] * other.getValue(p, n);
|
||||
}
|
||||
|
||||
result.setValue(p, m, val);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
bool MatrixBase<T, N, M>::isEqual(const MatrixBase<U, N, M>& other) const
|
||||
{
|
||||
for(unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < M; j++)
|
||||
{
|
||||
if(m_values[i][j] != other.m_values[i][j])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
bool MatrixBase<T, N, M>::isSame(const MatrixBase<U, N, M>& other) const
|
||||
{
|
||||
return &other == this;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
T (&MatrixBase<T, N, M>::operator[](const unsigned int index))[M]
|
||||
{
|
||||
MATRIX_CHECK_INDEX(index, 0);
|
||||
|
||||
return m_values[index];
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
void MatrixBase<T, N, M>::operator=(const MatrixBase<U, N, M>& other)
|
||||
{
|
||||
assign(other);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> MatrixBase<T, N, M>::operator+(const MatrixBase<U, N, M>& other) const
|
||||
{
|
||||
MatrixBase<T, N, M> result(*this);
|
||||
return result.add(other);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> MatrixBase<T, N, M>::operator-(const MatrixBase<U, N, M>& other) const
|
||||
{
|
||||
MatrixBase<T, N, M> result(*this);
|
||||
return result.subtract(other);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> MatrixBase<T, N, M>::operator*(const U& scalar) const
|
||||
{
|
||||
MatrixBase<T, N, M> result(*this);
|
||||
return result.scalarMultiplication(scalar);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> MatrixBase<T, N, M>::operator/(const U& scalar) const
|
||||
{
|
||||
MatrixBase<T, N, M> result(*this);
|
||||
return result.scalarMultiplication(1.0f / scalar);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> MatrixBase<T, N, M>::operator+=(const MatrixBase<U, N, M>& other)
|
||||
{
|
||||
return add(other);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> MatrixBase<T, N, M>::operator-=(const MatrixBase<U, N, M>& other)
|
||||
{
|
||||
return subtract(other);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> MatrixBase<T, N, M>::operator*=(const U& scalar)
|
||||
{
|
||||
return scalarMultiplication(scalar);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
MatrixBase<U, N, M> MatrixBase<T, N, M>::operator/=(const U& scalar)
|
||||
{
|
||||
return scalarMultiplication(1.0f / scalar);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
bool MatrixBase<T, N, M>::operator==(const MatrixBase<U, N, M>& other) const
|
||||
{
|
||||
return isEqual(other);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
template<class U>
|
||||
bool MatrixBase<T, N, M>::operator!=(const MatrixBase<U, N, M>& other) const
|
||||
{
|
||||
return !isEqual(other);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
std::string MatrixBase<T, N, M>::toString() const
|
||||
{
|
||||
std::stringstream result;
|
||||
|
||||
result << "\n";
|
||||
|
||||
for(unsigned int j = 0; j < M; j++)
|
||||
{
|
||||
for(unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
if(i > 0)
|
||||
{
|
||||
result << ", ";
|
||||
}
|
||||
|
||||
result << m_values[i][j];
|
||||
}
|
||||
|
||||
result << "\n";
|
||||
}
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
|
||||
template<class T, unsigned int N, unsigned int M>
|
||||
std::ostream& operator<<(std::ostream& ostream, const MatrixBase<T, N, M>& matrix)
|
||||
{
|
||||
ostream << matrix.toString();
|
||||
|
||||
return ostream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @note Vector will be treated as column vector
|
||||
*/
|
||||
template<class T, class U, unsigned int N, unsigned int M>
|
||||
VectorBase<U, M> multiply(MatrixBase<T, N, M>& matrix, const VectorBase<U, N>& vector)
|
||||
{
|
||||
// vector will be stored in a matrix instance to make use of MatrixBase matrix multiplication
|
||||
MatrixBase<T, 1, N> vectorMatrix;
|
||||
for(unsigned int i = 0; i < vector.getDimensions(); i++)
|
||||
{
|
||||
vectorMatrix.setValue(0, i, vector.getValue(i));
|
||||
}
|
||||
|
||||
MatrixBase<T, 1, M> resultMatrix = matrix.matrixMultiplication(vectorMatrix);
|
||||
|
||||
VectorBase<U, M> result;
|
||||
for(unsigned int i = 0; i < result.getDimensions(); i++)
|
||||
{
|
||||
result.setValue(i, resultMatrix.getValue(0, i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @note Vector will be treated as row vector
|
||||
*/
|
||||
template<class T, class U, unsigned int N, unsigned int M>
|
||||
VectorBase<U, N> multiply(const VectorBase<U, M>& vector, const MatrixBase<T, N, M>& matrix)
|
||||
{
|
||||
// vector will be stored in a matrix instance to make use of MatrixBase matrix multiplication
|
||||
MatrixBase<T, M, 1> vectorMatrix;
|
||||
for(unsigned int i = 0; i < vector.getDimensions(); i++)
|
||||
{
|
||||
vectorMatrix.setValue(i, 0, vector.getValue(i));
|
||||
}
|
||||
|
||||
MatrixBase<T, N, 1> resultMatrix = vectorMatrix.matrixMultiplication(matrix);
|
||||
|
||||
VectorBase<U, N> result;
|
||||
for(unsigned int i = 0; i < result.getDimensions(); i++)
|
||||
{
|
||||
result.setValue(i, resultMatrix.getValue(i, 0));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @note Vector will be treated as row vector
|
||||
*/
|
||||
template<class T, class U, unsigned int N, unsigned int M>
|
||||
VectorBase<U, N> operator*(const VectorBase<U, M>& vector, const MatrixBase<T, N, M>& matrix)
|
||||
{
|
||||
// vector will be stored in a matrix instance to make use of MatrixBase matrix multiplication
|
||||
MatrixBase<T, N, 1> vectorMatrix;
|
||||
for(unsigned int i = 0; i < vector.getDimensions(); i++)
|
||||
{
|
||||
vectorMatrix.setValue(i, 1, vector.getValue(i));
|
||||
}
|
||||
|
||||
MatrixBase<T, N, 1> resultMatrix = vectorMatrix.matrixMultiplication(matrix);
|
||||
|
||||
VectorBase<U, N> result;
|
||||
for(unsigned int i = 0; i < vector.getDimensions(); i++)
|
||||
{
|
||||
result.setValue(i, resultMatrix.getValue(i, 1));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // MATRIX_BASE_H
|
||||
@@ -0,0 +1,134 @@
|
||||
#ifndef MATRIX_DYNAMIC_BASE_H
|
||||
#define MATRIX_DYNAMIC_BASE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief Matrix of variable size, needed for spectral graph layouting
|
||||
* @note Use MatrixBase whenever possible because it's more efficient (e.g. it doesn't use stl containers)
|
||||
*/
|
||||
template<class T>
|
||||
class MatrixDynamicBase
|
||||
{
|
||||
public:
|
||||
MatrixDynamicBase();
|
||||
MatrixDynamicBase(const unsigned int numColumns, const unsigned int numRows);
|
||||
MatrixDynamicBase(const std::vector<std::vector<T>>& values);
|
||||
~MatrixDynamicBase();
|
||||
|
||||
T getValue(const unsigned int columnIndex, const unsigned int rowIndex) const;
|
||||
void setValue(const unsigned int columnIndex, const unsigned int rowIndex, const T& value);
|
||||
|
||||
unsigned int getColumnsCount() const;
|
||||
unsigned int getRowsCount() const;
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
private:
|
||||
void initializeValues(const unsigned int numColumns, const unsigned int numRows);
|
||||
|
||||
std::vector<std::vector<T>> m_values;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
MatrixDynamicBase<T>::MatrixDynamicBase()
|
||||
{
|
||||
}
|
||||
|
||||
template<class T>
|
||||
MatrixDynamicBase<T>::MatrixDynamicBase(const unsigned int numColumns, const unsigned int numRows)
|
||||
{
|
||||
initializeValues(numColumns, numRows);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
MatrixDynamicBase<T>::MatrixDynamicBase(const std::vector<std::vector<T>>& values)
|
||||
: m_values(values)
|
||||
{
|
||||
}
|
||||
|
||||
template<class T>
|
||||
MatrixDynamicBase<T>::~MatrixDynamicBase()
|
||||
{
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T MatrixDynamicBase<T>::getValue(const unsigned int columnIndex, const unsigned int rowIndex) const
|
||||
{
|
||||
return m_values[columnIndex][rowIndex];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void MatrixDynamicBase<T>::setValue(const unsigned int columnIndex, const unsigned int rowIndex, const T& value)
|
||||
{
|
||||
m_values[columnIndex][rowIndex] = value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
unsigned int MatrixDynamicBase<T>::getColumnsCount() const
|
||||
{
|
||||
return m_values.size();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
unsigned int MatrixDynamicBase<T>::getRowsCount() const
|
||||
{
|
||||
if(m_values.size() > 0)
|
||||
{
|
||||
return m_values[0].size();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string MatrixDynamicBase<T>::toString() const
|
||||
{
|
||||
std::stringstream result;
|
||||
|
||||
result << "\n";
|
||||
|
||||
unsigned int rowCount = getRowsCount();
|
||||
unsigned int columnCount = getColumnsCount();
|
||||
|
||||
for(unsigned int j = 0; j < rowCount; j++)
|
||||
{
|
||||
for(unsigned int i = 0; i < columnCount; i++)
|
||||
{
|
||||
if(i > 0)
|
||||
{
|
||||
result << ", ";
|
||||
}
|
||||
|
||||
result << m_values[i][j];
|
||||
}
|
||||
|
||||
result << "\n";
|
||||
}
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void MatrixDynamicBase<T>::initializeValues(const unsigned int numColumns, const unsigned int numRows)
|
||||
{
|
||||
for(unsigned int x = 0; x < numColumns; x++)
|
||||
{
|
||||
std::vector<T> row;
|
||||
for(unsigned int y = 0; y < numRows; y++)
|
||||
{
|
||||
row.push_back(0);
|
||||
}
|
||||
m_values.push_back(row);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::ostream& operator<<(std::ostream& ostream, const MatrixDynamicBase<T>& matrix)
|
||||
{
|
||||
ostream << matrix.toString();
|
||||
|
||||
return ostream;
|
||||
}
|
||||
|
||||
#endif // MATRIX_DYNAMIC_BASE_H
|
||||
@@ -54,6 +54,8 @@ public:
|
||||
template<class U>
|
||||
bool isSame(const VectorBase<U, N>& other) const;
|
||||
|
||||
bool isSame(const VectorBase<T, N>& other) const;
|
||||
|
||||
T operator[](const unsigned int index);
|
||||
|
||||
template<class U>
|
||||
@@ -233,7 +235,13 @@ void VectorBase<T, N>::assign(const VectorBase<U, N>& other)
|
||||
return;
|
||||
}
|
||||
|
||||
setValues(other.m_values);
|
||||
T values[N];
|
||||
for(unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
values[i] = other.getValue(i);
|
||||
}
|
||||
|
||||
setValues(values);
|
||||
}
|
||||
|
||||
template<class T, unsigned int N>
|
||||
@@ -299,7 +307,7 @@ bool VectorBase<T, N>::isEqual(const VectorBase<U, N>& other) const
|
||||
{
|
||||
for (unsigned int i = 0; i < N; i++)
|
||||
{
|
||||
if (m_values[i] != other.m_values[i])
|
||||
if (m_values[i] != other.getValue(i))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -308,11 +316,17 @@ bool VectorBase<T, N>::isEqual(const VectorBase<U, N>& other) const
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N>
|
||||
bool VectorBase<T, N>::isSame(const VectorBase<T, N>& other) const
|
||||
{
|
||||
return &other == this;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N>
|
||||
template<class U>
|
||||
bool VectorBase<T, N>::isSame(const VectorBase<U, N>& other) const
|
||||
{
|
||||
return &other == this;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T, unsigned int N>
|
||||
|
||||
@@ -16,6 +16,7 @@ add_files(
|
||||
GraphFilterTestSuite.h
|
||||
GraphFilterConductorTestSuite.h
|
||||
LogManagerTestSuite.h
|
||||
MatrixBaseTestSuite.h
|
||||
MessageQueueTestSuite.h
|
||||
QueryTreeTestSuite.h
|
||||
SettingsTestSuite.h
|
||||
|
||||
@@ -0,0 +1,474 @@
|
||||
#include "cxxtest/TestSuite.h"
|
||||
|
||||
#include "utility/logging/logging.h"
|
||||
#include "utility/math/MatrixBase.h"
|
||||
#include "utility/math/VectorBase.h"
|
||||
|
||||
class MatrixBaseTestSuite : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_matrixBase_constructors()
|
||||
{
|
||||
MatrixBase<int, 4, 5> matrix0;
|
||||
|
||||
TS_ASSERT_EQUALS(4, matrix0.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(5, matrix0.getRowsCount());
|
||||
|
||||
Array3x5<int> testValues = getTestValues3x5();
|
||||
|
||||
MatrixBase<int, 3, 5> matrix1(testValues.array);
|
||||
|
||||
TS_ASSERT_EQUALS(3, matrix1.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(5, matrix1.getRowsCount());
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(6, matrix1.getValue(2, 4));
|
||||
|
||||
MatrixBase<int, 3, 5> matrix2(matrix1);
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix2.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(6, matrix2.getValue(2, 4));
|
||||
}
|
||||
|
||||
void test_matrixBase_getSetValue()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
|
||||
int value2_2 = matrix0.getValue(2, 2);
|
||||
matrix0.setValue(2, 2, value2_2*2);
|
||||
TS_ASSERT_EQUALS(value2_2*2, matrix0.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(3, matrix0.getValue(1, 2));
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
}
|
||||
|
||||
void test_matrixBase_getRowsColumnsCount()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
MatrixBase<int, 5, 3> matrix1 = getTestMatrix5x3();
|
||||
|
||||
TS_ASSERT_EQUALS(3, matrix0.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(5, matrix0.getRowsCount());
|
||||
|
||||
TS_ASSERT_EQUALS(5, matrix1.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(3, matrix1.getRowsCount());
|
||||
}
|
||||
|
||||
void test_matrixBase_transposed()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
TS_ASSERT_EQUALS(3, matrix0.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(5, matrix0.getRowsCount());
|
||||
|
||||
MatrixBase<int, 5, 3> matrix1 = matrix0.transposed();
|
||||
TS_ASSERT_EQUALS(5, matrix1.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(3, matrix1.getRowsCount());
|
||||
|
||||
TS_ASSERT_EQUALS(matrix0.getValue(0, 0), matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(matrix0.getValue(0, 1), matrix1.getValue(1, 0));
|
||||
TS_ASSERT_EQUALS(matrix0.getValue(0, 4), matrix1.getValue(4, 0));
|
||||
TS_ASSERT_EQUALS(matrix0.getValue(1, 4), matrix1.getValue(4, 1));
|
||||
TS_ASSERT_EQUALS(matrix0.getValue(2, 4), matrix1.getValue(4, 2));
|
||||
TS_ASSERT_EQUALS(matrix0.getValue(0, 3), matrix1.getValue(3, 0));
|
||||
TS_ASSERT_EQUALS(matrix0.getValue(1, 3), matrix1.getValue(3, 1));
|
||||
TS_ASSERT_EQUALS(matrix0.getValue(2, 3), matrix1.getValue(3, 2));
|
||||
}
|
||||
|
||||
void test_matrixBase_assign()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix1 = getTestMatrix3x5_b();
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(2, matrix0.getValue(1, 1));
|
||||
TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2));
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(-2, matrix1.getValue(1, 1));
|
||||
TS_ASSERT_EQUALS(-4, matrix1.getValue(2, 2));
|
||||
|
||||
matrix0.assign(matrix1);
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(-2, matrix0.getValue(1, 1));
|
||||
TS_ASSERT_EQUALS(-4, matrix0.getValue(2, 2));
|
||||
}
|
||||
|
||||
void test_matrixBase_add_subtract()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix1 = getTestMatrix3x5_b();
|
||||
|
||||
matrix0.add(matrix1);
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(1, 1));
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(2, 2));
|
||||
|
||||
matrix0.subtract(matrix1);
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(2, matrix0.getValue(1, 1));
|
||||
TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2));
|
||||
}
|
||||
|
||||
void test_matrixBase_multiplyDivideScalar()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
|
||||
matrix0.scalarMultiplication(2.0f); // float is on porpoise (so is porpoise, womp womp)
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(4, matrix0.getValue(1, 1));
|
||||
TS_ASSERT_EQUALS(8, matrix0.getValue(2, 2));
|
||||
|
||||
matrix0.scalarMultiplication(0.5f);
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(2, matrix0.getValue(1, 1));
|
||||
TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2));
|
||||
|
||||
matrix0.scalarMultiplication(0.5f);
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(1, matrix0.getValue(1, 1));
|
||||
TS_ASSERT_EQUALS(2, matrix0.getValue(2, 2));
|
||||
|
||||
matrix0.scalarMultiplication(0.5f);
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(1, 1));
|
||||
TS_ASSERT_EQUALS(1, matrix0.getValue(2, 2));
|
||||
}
|
||||
|
||||
void test_matrixBase_multiplyMatrix()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix1 = getTestMatrix3x5_b();
|
||||
MatrixBase<int, 5, 3> matrix1t = matrix1.transposed();
|
||||
|
||||
MatrixBase<int, 5, 5> matrix2 = matrix0.matrixMultiplication(matrix1t);
|
||||
MatrixBase<int, 3, 3> matrix3 = matrix1t.matrixMultiplication(matrix0);
|
||||
|
||||
// expected results
|
||||
// matrix0 * matrix1t
|
||||
/**
|
||||
* -5, -8, -11, -14, -17
|
||||
* -8, -14, -20, -26, -32
|
||||
* -11, -20, -29, -38, -47
|
||||
* -14, -26, -38, -50, -62
|
||||
* -17, -32, -47, -62, -77
|
||||
*/
|
||||
|
||||
// matrix1t * matrix0
|
||||
/**
|
||||
* -30, -40, -50
|
||||
* -40, -55, -70
|
||||
* -50, -70, -90
|
||||
*/
|
||||
|
||||
|
||||
TS_ASSERT_EQUALS(5, matrix2.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(5, matrix2.getRowsCount());
|
||||
|
||||
TS_ASSERT_EQUALS(3, matrix3.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(3, matrix3.getRowsCount());
|
||||
|
||||
TS_ASSERT_EQUALS(-5, matrix2.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(-77, matrix2.getValue(4, 4));
|
||||
TS_ASSERT_EQUALS(-29, matrix2.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(-11, matrix2.getValue(2, 0));
|
||||
TS_ASSERT_EQUALS(-11, matrix2.getValue(0, 2));
|
||||
TS_ASSERT_EQUALS(-38, matrix2.getValue(3, 2));
|
||||
|
||||
|
||||
TS_ASSERT_EQUALS(-30, matrix3.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(-90, matrix3.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(-50, matrix3.getValue(2, 0));
|
||||
TS_ASSERT_EQUALS(-50, matrix3.getValue(0, 2));
|
||||
TS_ASSERT_EQUALS(-55, matrix3.getValue(1, 1));
|
||||
}
|
||||
|
||||
void test_matrixBase_isEqual()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix0_b = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix1 = getTestMatrix3x5_b();
|
||||
|
||||
TS_ASSERT_EQUALS(true, matrix0.isEqual(matrix0_b));
|
||||
TS_ASSERT_EQUALS(false, matrix0.isEqual(matrix1));
|
||||
TS_ASSERT_EQUALS(true, matrix0.isEqual(matrix0));
|
||||
}
|
||||
|
||||
void test_matrixBase_isSame()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix0_b = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix1 = getTestMatrix3x5_b();
|
||||
|
||||
TS_ASSERT_EQUALS(false, matrix0.isSame(matrix0_b));
|
||||
TS_ASSERT_EQUALS(false, matrix0.isSame(matrix1));
|
||||
TS_ASSERT_EQUALS(true, matrix0.isSame(matrix0));
|
||||
}
|
||||
|
||||
void test_matrixBase_accessOperator()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0[0][0]);
|
||||
TS_ASSERT_EQUALS(4, matrix0[2][2]);
|
||||
TS_ASSERT_EQUALS(6, matrix0[2][4]);
|
||||
|
||||
matrix0[0][0] = 42;
|
||||
|
||||
TS_ASSERT_EQUALS(42, matrix0[0][0]);
|
||||
TS_ASSERT_EQUALS(4, matrix0[2][2]);
|
||||
TS_ASSERT_EQUALS(6, matrix0[2][4]);
|
||||
}
|
||||
|
||||
void test_matrixBase_operators()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix0_b = getTestMatrix3x5_b();
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(6, matrix0.getValue(2, 4));
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0_b.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(-4, matrix0_b.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(-6, matrix0_b.getValue(2, 4));
|
||||
|
||||
MatrixBase<int, 3, 5> matrix1 = matrix0 + matrix0_b;
|
||||
MatrixBase<int, 3, 5> matrix2 = matrix0 - matrix0_b;
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(2, 4));
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix2.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(8, matrix2.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(12, matrix2.getValue(2, 4));
|
||||
|
||||
MatrixBase<int, 3, 5> matrix3 = matrix0 * 3;
|
||||
MatrixBase<int, 3, 5> matrix4 = matrix0 / 2;
|
||||
MatrixBase<int, 3, 5> matrix5 = matrix0 * 3.3f; // float is on purpose
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix3.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(12, matrix3.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(18, matrix3.getValue(2, 4));
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix4.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(2, matrix4.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(3, matrix4.getValue(2, 4));
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix5.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(13, matrix5.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(19, matrix5.getValue(2, 4));
|
||||
}
|
||||
|
||||
void test_matrixBase_assignOperators()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix0_b = getTestMatrix3x5_b();
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(6, matrix0.getValue(2, 4));
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0_b.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(-4, matrix0_b.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(-6, matrix0_b.getValue(2, 4));
|
||||
|
||||
MatrixBase<int, 3, 5> matrix1 = getTestMatrix3x5();
|
||||
matrix1 += matrix0;
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(8, matrix1.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(12, matrix1.getValue(2, 4));
|
||||
|
||||
matrix1 += matrix0_b;
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(4, matrix1.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(6, matrix1.getValue(2, 4));
|
||||
|
||||
matrix1 -= matrix0_b;
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(8, matrix1.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(12, matrix1.getValue(2, 4));
|
||||
|
||||
matrix1 *= 3.3f;
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(26, matrix1.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(39, matrix1.getValue(2, 4));
|
||||
|
||||
matrix1 /= 3;
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(8, matrix1.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(13, matrix1.getValue(2, 4));
|
||||
}
|
||||
|
||||
void test_matrixBase_comparisonOperators()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
MatrixBase<int, 3, 5> matrix0_b = getTestMatrix3x5_b();
|
||||
MatrixBase<int, 3, 5> matrix1 = getTestMatrix3x5();
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(6, matrix0.getValue(2, 4));
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0_b.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(-4, matrix0_b.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(-6, matrix0_b.getValue(2, 4));
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix1.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(4, matrix1.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(6, matrix1.getValue(2, 4));
|
||||
|
||||
TS_ASSERT_EQUALS(true, matrix0 == matrix0);
|
||||
TS_ASSERT_EQUALS(true, matrix0 == matrix1);
|
||||
TS_ASSERT_EQUALS(true, matrix0 != matrix0_b);
|
||||
|
||||
TS_ASSERT_EQUALS(false, matrix0 != matrix0);
|
||||
TS_ASSERT_EQUALS(false, matrix0 != matrix1);
|
||||
TS_ASSERT_EQUALS(false, matrix0 == matrix0_b);
|
||||
}
|
||||
|
||||
void test_matrixBase_vectorMultiplication()
|
||||
{
|
||||
MatrixBase<int, 3, 5> matrix0 = getTestMatrix3x5();
|
||||
VectorBase<int, 3> vector0;
|
||||
|
||||
for(unsigned int i = 0; i < vector0.getDimensions(); i++)
|
||||
{
|
||||
vector0.setValue(i, i+1);
|
||||
}
|
||||
|
||||
VectorBase<int, 5> vector0_r = multiply(matrix0, vector0);
|
||||
|
||||
TS_ASSERT_EQUALS(8, vector0_r[0]);
|
||||
TS_ASSERT_EQUALS(14, vector0_r[1]);
|
||||
TS_ASSERT_EQUALS(20, vector0_r[2]);
|
||||
TS_ASSERT_EQUALS(26, vector0_r[3]);
|
||||
TS_ASSERT_EQUALS(32, vector0_r[4]);
|
||||
|
||||
MatrixBase<int, 5, 3> matrix1 = getTestMatrix5x3();
|
||||
VectorBase<int, 3> vector1;
|
||||
|
||||
for(unsigned int i = 0; i < vector1.getDimensions(); i++)
|
||||
{
|
||||
vector1.setValue(i, i+1);
|
||||
}
|
||||
|
||||
VectorBase<int, 5> vector1_r = multiply(vector1, matrix1);
|
||||
|
||||
TS_ASSERT_EQUALS(8, vector1_r[0]);
|
||||
TS_ASSERT_EQUALS(14, vector1_r[1]);
|
||||
TS_ASSERT_EQUALS(20, vector1_r[2]);
|
||||
TS_ASSERT_EQUALS(26, vector1_r[3]);
|
||||
TS_ASSERT_EQUALS(32, vector1_r[4]);
|
||||
|
||||
MatrixBase<int, 3, 5> matrix2 = getTestMatrix3x5();
|
||||
VectorBase<int, 5> vector2;
|
||||
|
||||
for(unsigned int i = 0; i < vector2.getDimensions(); i++)
|
||||
{
|
||||
vector2.setValue(i, i+1);
|
||||
}
|
||||
|
||||
VectorBase<int, 3> vector2_r = multiply(vector2, matrix2);
|
||||
|
||||
TS_ASSERT_EQUALS(40, vector2_r[0]);
|
||||
TS_ASSERT_EQUALS(55, vector2_r[1]);
|
||||
TS_ASSERT_EQUALS(70, vector2_r[2]);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* C++ functions can't return statically allocated arrays.
|
||||
* I don't want to use dynamically allocated arrays, so here's my work around for that...
|
||||
*
|
||||
* Update: acutally they can... see MatrixBase [] operator (in MatrixBase.cpp)
|
||||
*/
|
||||
template<class T>
|
||||
struct Array3x5
|
||||
{
|
||||
T array[3][5];
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct Array5x3
|
||||
{
|
||||
T array[5][3];
|
||||
};
|
||||
|
||||
Array3x5<int> getTestValues3x5()
|
||||
{
|
||||
Array3x5<int> result;
|
||||
|
||||
for(unsigned int i = 0; i < 3; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < 5; j++)
|
||||
{
|
||||
result.array[i][j] = i + j;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Array3x5<int> getTestValues3x5_b()
|
||||
{
|
||||
Array3x5<int> result;
|
||||
|
||||
for(unsigned int i = 0; i < 3; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < 5; j++)
|
||||
{
|
||||
result.array[i][j] = -i - j;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Array5x3<int> getTestValues5x3()
|
||||
{
|
||||
Array5x3<int> result;
|
||||
|
||||
for(unsigned int i = 0; i < 5; i++)
|
||||
{
|
||||
for(unsigned int j = 0; j < 3; j++)
|
||||
{
|
||||
result.array[i][j] = i + j;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MatrixBase<int, 3, 5> getTestMatrix3x5()
|
||||
{
|
||||
Array3x5<int> testValues = getTestValues3x5();
|
||||
|
||||
return MatrixBase<int, 3, 5>(testValues.array);
|
||||
}
|
||||
|
||||
MatrixBase<int, 3, 5> getTestMatrix3x5_b()
|
||||
{
|
||||
Array3x5<int> testValues = getTestValues3x5_b();
|
||||
|
||||
return MatrixBase<int, 3, 5>(testValues.array);
|
||||
}
|
||||
|
||||
MatrixBase<int, 5, 3> getTestMatrix5x3()
|
||||
{
|
||||
Array5x3<int> testValues = getTestValues5x3();
|
||||
|
||||
return MatrixBase<int, 5, 3>(testValues.array);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "cxxtest/TestSuite.h"
|
||||
|
||||
#include "utility/logging/logging.h"
|
||||
#include "utility/math/MatrixDynamicBase.h"
|
||||
|
||||
class MatrixDynamicBaseTestSuite : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_matrixDynamicBase_constructors()
|
||||
{
|
||||
MatrixDynamicBase<int> matrix0;
|
||||
MatrixDynamicBase<int> matrix1(3, 5);
|
||||
|
||||
std::vector<std::vector<int>> testValues = getTestValues(3, 5);
|
||||
MatrixDynamicBase<int> matrix2(testValues);
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(0, matrix0.getRowsCount());
|
||||
TS_ASSERT_EQUALS(3, matrix1.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(5, matrix1.getRowsCount());
|
||||
TS_ASSERT_EQUALS(3, matrix2.getColumnsCount());
|
||||
TS_ASSERT_EQUALS(5, matrix2.getRowsCount());
|
||||
}
|
||||
|
||||
void test_matrixDynamicBase_getValue_setValue()
|
||||
{
|
||||
std::vector<std::vector<int>> testValues = getTestValues(3, 5);
|
||||
MatrixDynamicBase<int> matrix0(testValues);
|
||||
|
||||
TS_ASSERT_EQUALS(0, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(4, matrix0.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(6, matrix0.getValue(2, 4));
|
||||
|
||||
matrix0.setValue(0, 0, 42);
|
||||
matrix0.setValue(2, 2, 84);
|
||||
matrix0.setValue(2, 4, 126);
|
||||
|
||||
TS_ASSERT_EQUALS(42, matrix0.getValue(0, 0));
|
||||
TS_ASSERT_EQUALS(84, matrix0.getValue(2, 2));
|
||||
TS_ASSERT_EQUALS(126, matrix0.getValue(2, 4));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::vector<int>> getTestValues(const unsigned int numColumns, const unsigned int numRows)
|
||||
{
|
||||
std::vector<std::vector<int>> testValues;
|
||||
|
||||
for(unsigned int x = 0; x < numColumns; x++)
|
||||
{
|
||||
std::vector<int> row;
|
||||
|
||||
for(unsigned int y = 0; y < numRows; y++)
|
||||
{
|
||||
row.push_back(x + y);
|
||||
}
|
||||
|
||||
testValues.push_back(row);
|
||||
}
|
||||
|
||||
return testValues;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user