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:
Manuel
2015-01-29 15:47:02 +01:00
parent 016d692dda
commit a7eaeae432
23 changed files with 2003 additions and 11 deletions
+2
View File
@@ -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)
+199 -1
View File
@@ -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
+5
View File
@@ -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;
};
+25
View File
@@ -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;
}
+525
View File
@@ -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
+134
View File
@@ -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
+17 -3
View File
@@ -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>