Browse Source

RangeSlider

Tom Troppmann 4 years ago
parent
commit
0e019bac10
54 changed files with 4338 additions and 200 deletions
  1. 38 0
      metavis/DockableGraphView.cpp
  2. 16 0
      metavis/DockableGraphView.h
  3. 277 67
      metavis/GraphView.cpp
  4. 43 25
      metavis/GraphView.h
  5. 53 0
      metavis/GraphViewSettingDialog.cpp
  6. 25 0
      metavis/GraphViewSettingDialog.h
  7. 20 0
      metavis/GraphViewSettingDialog.ui
  8. 92 0
      metavis/GraphViewSettingItem.cpp
  9. 23 0
      metavis/GraphViewSettingItem.h
  10. 195 0
      metavis/GraphViewSettingItem.ui
  11. 13 0
      metavis/MetalogManagerItem.cpp
  12. 16 0
      metavis/MetalogManagerItem.h
  13. 170 0
      metavis/MetalogManagerItem.ui
  14. 8 0
      metavis/Project.cpp
  15. 28 0
      metavis/Project.h
  16. 103 0
      metavis/ProjectManager.cpp
  17. 39 0
      metavis/ProjectManager.h
  18. 154 0
      metavis/RangeSlider.cpp
  19. 36 0
      metavis/RangeSlider.h
  20. 1 0
      metavis/Resources/assignButton.svg
  21. 1 0
      metavis/Resources/close.svg
  22. 1 0
      metavis/Resources/close_Big.svg
  23. 3 0
      metavis/Resources/close_big_red.svg
  24. 3 0
      metavis/Resources/close_red.svg
  25. 66 0
      metavis/Resources/settingIcon.svg
  26. 79 0
      metavis/Resources/settingIcon_hovered.svg
  27. 95 24
      metavis/RunData.cpp
  28. 19 5
      metavis/RunData.h
  29. 137 35
      metavis/metavis.cpp
  30. 25 7
      metavis/metavis.h
  31. 7 2
      metavis/metavis.qrc
  32. 16 1
      metavis/metavis.ui
  33. 27 4
      metavis/metavis.vcxproj
  34. 112 13
      metavis/metavis.vcxproj.filters
  35. 2 0
      metavis/pch.h
  36. 13 0
      metavis/tsneIteractive.cpp
  37. 18 0
      metavis/tsneIteractive.h
  38. 133 0
      metavis/tsneIteractive.ui
  39. 10 0
      metavis/util.h
  40. 75 0
      metaviscon/RunDataLite.cpp
  41. 29 0
      metaviscon/RunDataLite.h
  42. BIN
      metaviscon/data.dat
  43. 41 0
      metaviscon/generateMetalogFile.cpp
  44. 259 15
      metaviscon/metaviscon.cpp
  45. 13 2
      metaviscon/metaviscon.vcxproj
  46. 38 0
      metaviscon/metaviscon.vcxproj.filters
  47. 134 0
      metaviscon/myTsne.cpp
  48. 3 0
      metaviscon/myTsne.h
  49. 428 0
      metaviscon/src/t_sne/sptree.cpp
  50. 115 0
      metaviscon/src/t_sne/sptree.h
  51. 723 0
      metaviscon/src/t_sne/tsne.cpp
  52. 50 0
      metaviscon/src/t_sne/tsne.h
  53. 41 0
      metaviscon/src/t_sne/tsne_main.cpp
  54. 272 0
      metaviscon/src/t_sne/vptree.h

+ 38 - 0
metavis/DockableGraphView.cpp

@@ -0,0 +1,38 @@
+#include "pch.h"
+#include "DockableGraphView.h"
+#include <QIcon>
+#include <QStyle>
+#include <QPushButton>
+#include <QLayout>
+#include <QGridLayout>
+
+
+DockableGraphView::DockableGraphView(QWidget *parent, QString title)
+	: QDockWidget(parent)
+{
+	view = new GraphView(this, Bound(0, 100, -1.0, 101.0));
+	view->setUseFixedBound(true);
+	view->setMinimumSize(200, 200);
+	dialog = new GraphViewSettingDialog(view, title + " Settings", this);
+	QWidget* widget = new QWidget(this);
+	QGridLayout* layout = new QGridLayout;
+	QPushButton* button = new QPushButton();
+	button->setMinimumSize(20, 20);
+	button->setMaximumSize(20, 20);
+	button->setIcon(QIcon(":/metavis/Resources/settingIcon.svg"));
+	button->setAttribute(Qt::WA_TranslucentBackground);
+	button->setStyleSheet(button->styleSheet() + "border: none;");
+	button->setToolTip("Settings");
+	connect(button, &QPushButton::released, dialog, &GraphViewSettingDialog::openDialog);
+	layout->addWidget(view, 0, 0, 4, 4);
+	layout->addWidget(button,0, 3, 1, 1);
+	layout->setContentsMargins(0, 0, 0, 0);
+	widget->setLayout(layout);
+	this->setWindowTitle(title);
+	this->setAllowedAreas(Qt::AllDockWidgetAreas);
+	this->setWidget(widget);
+}
+
+DockableGraphView::~DockableGraphView()
+{
+}

+ 16 - 0
metavis/DockableGraphView.h

@@ -0,0 +1,16 @@
+#pragma once
+#include <QDockWidget>
+#include <QString>
+#include "GraphView.h"
+#include "GraphViewSettingDialog.h"
+class DockableGraphView : public QDockWidget
+{
+	Q_OBJECT
+public:
+	GraphView* view;
+	GraphViewSettingDialog* dialog = nullptr;
+public:
+	DockableGraphView(QWidget *parent, QString title);
+	~DockableGraphView();
+public:
+};

+ 277 - 67
metavis/GraphView.cpp

@@ -2,6 +2,7 @@
 #include "GraphView.h"
 
 #include <QDebug>
+#include <QPushButton>
 #include <QBrush>
 #include <random>
 #include <algorithm>
@@ -12,29 +13,34 @@
 #define X_AXIS_NUMBER_LINE_LENGTH 5
 #define Y_AXIS_NUMBER_LINE_LENGTH 5
 #define X_AXIS_NUMBER_GAP_LENGTH 10
-#define Y_AXIS_NUMBER_GAP_LENGTH 20
+#define Y_AXIS_NUMBER_GAP_LENGTH 30
 
+#define MARGIN_TOP 10
+#define MARGIN_BOTTOM 20
+#define MARGIN_LEFT 50
+#define MARGIN_RIGHT 10
 
-GraphView::GraphView(QWidget* parent, QString title, Bound fixedBound):QWidget(parent), title(title), fixedBound(fixedBound), linePen(Qt::blue), rectPen(QColor(255,255,255,255)), axisPen(Qt::black)
+
+
+GraphView::GraphView(QWidget* parent, Bound fixedBound):QWidget(parent), fixedBound(fixedBound), linePen(Qt::blue), rectPen(QColor(255,255,255,255)), axisPen(Qt::black)
 {
 	
 	linePen.setJoinStyle(Qt::PenJoinStyle::RoundJoin);
 	rectPen.setWidth(0);
-	//Populate data with DummyData
-	//Draw Points
-	std::random_device rd;  //Will be used to obtain a seed for the random number engine
-	std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
-	std::uniform_real_distribution<double> doubleDistr(0.0, 1.0);
-	hueoffset = doubleDistr(gen);
+
+	////generate Random number for starting color
+	//std::random_device rd;  //Will be used to obtain a seed for the random number engine
+	//std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
+	//std::uniform_real_distribution<double> doubleDistr(0.0, 1.0);
+	//hueoffset = doubleDistr(gen);
+	hueoffset = 0.5;
 	setFocusPolicy(Qt::ClickFocus);
-	/*QTimer* timer = new QTimer(this);
-	connect(timer, &QTimer::timeout, this, QOverload<>::of(&GraphView::update));
-	timer->start(1000);*/
 }
 
 GraphView::~GraphView()
 {
 }
+
 void GraphView::paintEvent(QPaintEvent* event)
 {
 	std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
@@ -48,10 +54,10 @@ void GraphView::paintEvent(QPaintEvent* event)
 	}
 	//Calculate LineRect
 	QRect graphDisplayRect(rect());
-	graphDisplayRect.setBottom(graphDisplayRect.bottom() - 20);
-	graphDisplayRect.setLeft(graphDisplayRect.left() + 50);
-	graphDisplayRect.setTop(graphDisplayRect.top() + 20);
-	graphDisplayRect.setRight(graphDisplayRect.right() - 10);
+	graphDisplayRect.setBottom(graphDisplayRect.bottom() - MARGIN_BOTTOM);
+	graphDisplayRect.setLeft(graphDisplayRect.left() + MARGIN_LEFT);
+	graphDisplayRect.setTop(graphDisplayRect.top() + MARGIN_TOP);
+	graphDisplayRect.setRight(graphDisplayRect.right() - MARGIN_RIGHT);
 	graphDisplayRect.setWidth(graphDisplayRect.width() - (graphDisplayRect.width() % X_AXIS_GAP_AMOUNT) + 1);
 	graphDisplayRect.setHeight(graphDisplayRect.height() - (graphDisplayRect.height() % Y_AXIS_GAP_AMOUNT) + 1);
 
@@ -63,44 +69,61 @@ void GraphView::paintEvent(QPaintEvent* event)
 	double stregth_factorY = graphDisplayRect.height() / rangeGraphY;
 	QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
 	for (const GraphSeries& graphSeries : graphSeriesVec) {
-		if (graphSeries.data->empty()) continue;
+		if (!graphSeries.data) {
+			qDebug() << "Pointer to nothing pls help";
+		}
+		if (graphSeries.data->empty() || graphSeries.hide) continue;
 		linePen.setColor(graphSeries.color);
-		painter.setPen(linePen);
 
 		if (graphSeries.type == GraphSeries::SeriesType::Line || graphSeries.type == GraphSeries::SeriesType::LineDot) {
-			linePen.setWidth(graphSeries.lineWidth);
+			linePen.setWidth(graphSeries.lineWidth + ((&graphSeries == highlightSeries) ? 5 : 0));
 			painter.setPen(linePen);
 			QPainterPath painterPath;
 
 			QPointF oldpoint = graphSeries.data->at(0).toQPointF();
-			painterPath.moveTo(transformPoint(oldpoint, stregth_factorX, stregth_factorY));
+			painterPath.moveTo(transformGraphToView(oldpoint, stregth_factorX, stregth_factorY, translation));
 			for (int i = 1; i < graphSeries.data->size(); i++) {
 				QPointF newpoint = graphSeries.data->at(i).toQPointF();
 				if (!inBoundX(oldpoint) && !inBoundX(newpoint)) {
 					
 				}
 				else if (!inBoundX(oldpoint) && inBoundX(newpoint)) {
-					painterPath.moveTo(transformPoint(oldpoint, stregth_factorX, stregth_factorY));
-					painterPath.lineTo(transformPoint(newpoint, stregth_factorX, stregth_factorY));
+					painterPath.moveTo(transformGraphToView(oldpoint, stregth_factorX, stregth_factorY, translation));
+					painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation));
 				}
 				else if (inBoundX(oldpoint) && inBoundX(newpoint)) {
-					painterPath.lineTo(transformPoint(newpoint, stregth_factorX, stregth_factorY));
+					painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation));
 				}
 				else if (inBoundX(oldpoint) && !inBoundX(newpoint)) {
-					painterPath.lineTo(transformPoint(newpoint, stregth_factorX, stregth_factorY));
+					painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation));
 					break;
 				}
 				oldpoint = newpoint;
 			}
 
-			painterPath.translate(translation);
+			//painterPath.translate(translation);
 			painter.drawPath(painterPath);
 		}
 		if (graphSeries.type == GraphSeries::SeriesType::Dot || graphSeries.type == GraphSeries::SeriesType::LineDot) {
+			linePen.setWidth(2);
+			painter.setPen(linePen);
 			painter.setBrush(graphSeries.color);
 			for (auto data: *graphSeries.data) {
+				if (useInterval) {
+					if (data.orginalPoint->iteration < minIter) {
+						continue;
+					}
+					if (data.orginalPoint->iteration > maxIter) {
+						break;
+					}
+				}
+				if (graphSeries.useDataPointColor) {
+					linePen.setColor(data.color);
+					painter.setPen(linePen);
+					painter.setBrush(data.color);
+				}
 				if (inBound(data.toQPointF())) {
-					painter.drawEllipse(transformPoint(data.toQPointF(), stregth_factorX, stregth_factorY) + translation, graphSeries.circleRadius, graphSeries.circleRadius);
+					painter.drawEllipse(transformGraphToView(data.toQPointF(), stregth_factorX, stregth_factorY, translation), graphSeries.circleRadius, graphSeries.circleRadius);
 				}
 			}
 			painter.setBrush(Qt::BrushStyle::NoBrush);
@@ -110,8 +133,7 @@ void GraphView::paintEvent(QPaintEvent* event)
 
 	if (drawTotalBound) {
 		painter.setPen(axisPen);
-		QRectF bounds(transformPoint(QPointF(totalBound.minX, totalBound.maxY), stregth_factorX, stregth_factorY), transformPoint(QPointF(totalBound.maxX, totalBound.minY), stregth_factorX, stregth_factorY));
-		bounds.translate(translation);
+		QRectF bounds(transformGraphToView(QPointF(totalBound.minX, totalBound.maxY), stregth_factorX, stregth_factorY, translation), transformGraphToView(QPointF(totalBound.maxX, totalBound.minY), stregth_factorX, stregth_factorY, translation));
 		painter.drawRect(bounds);
 	}
 
@@ -146,6 +168,7 @@ void GraphView::paintEvent(QPaintEvent* event)
 	QPainterPath xAxisPath;
 	xAxisPath.moveTo(xAxisRect.left(), xAxisRect.top());
 	xAxisPath.lineTo(xAxisRect.right(), xAxisRect.top());
+
 	int xGap = xAxisRect.width() / X_AXIS_GAP_AMOUNT;
 	QRect textRect(0, 0, 40, 9);
 
@@ -167,19 +190,136 @@ void GraphView::paintEvent(QPaintEvent* event)
 		yAxisPath.moveTo(yAxisRect.right() - Y_AXIS_NUMBER_LINE_LENGTH, yAxisRect.bottom() - i * yGap);
 		yAxisPath.lineTo(yAxisRect.right(), yAxisRect.bottom() - i * yGap);
 		textRect.moveCenter(QPoint(yAxisRect.right() - Y_AXIS_NUMBER_GAP_LENGTH, yAxisRect.bottom() - i * yGap));
-		painter.drawText(textRect, Qt::AlignCenter, yAxisNumbers[i]);
+		painter.drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, yAxisNumbers[i]);
 	}
 	painter.drawPath(yAxisPath);
 	std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
 	std::chrono::milliseconds time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
+
+
+	//Mouse:
+	if (mousePressed) {
+		QColor blue(0, 122, 204);
+		QColor gray(120, 120, 120);
+		//QPointF value = transformViewToGraph(oldPositionForMouseEvent);
+		QPointF viewPoint = transformGraphToView(mousePressedValue, stregth_factorX, stregth_factorY, translation);
+		if (graphDisplayRect.top() <= viewPoint.y() && viewPoint.y() <= graphDisplayRect.bottom()) {
+			painter.setPen(gray);
+			painter.drawLine(QPointF(graphDisplayRect.left() - Y_AXIS_NUMBER_LINE_LENGTH, viewPoint.y()), QPointF(graphDisplayRect.right(), viewPoint.y()));
+			textRect.moveCenter(QPoint(graphDisplayRect.left() - Y_AXIS_NUMBER_GAP_LENGTH, viewPoint.y()));
+			painter.fillRect(textRect, rectPen.color());
+			painter.setPen(blue);
+			painter.drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, QString::number(mousePressedValue.y(), 'f', 1));
+		}
+		if (graphDisplayRect.left() <= viewPoint.x() && viewPoint.x() <= graphDisplayRect.right()) {
+			painter.setPen(gray);
+			painter.drawLine(QPointF(viewPoint.x(), graphDisplayRect.top()), QPointF(viewPoint.x(), graphDisplayRect.bottom() + X_AXIS_NUMBER_LINE_LENGTH));
+			textRect.moveCenter(QPoint(viewPoint.x(), graphDisplayRect.bottom() + X_AXIS_NUMBER_GAP_LENGTH));
+			painter.fillRect(textRect, rectPen.color());
+			painter.setPen(blue);
+			painter.drawText(textRect, Qt::AlignCenter, QString::number(mousePressedValue.x(), 'f', 1));
+		}
+	}
+
+
 	qDebug() << "PaintTime: " << time.count() << "ms";
+	/*for (GraphSeries& series : graphSeriesVec) {
+		qDebug() << "BEGIN  ---------";
+		for (GraphDataPoint point : *series.data) {
+			qDebug() << "QPoint(" << point.x << "," << point.y << ")";
+		}
+		qDebug() << "END  ---------";
+	}*/
+}
+
+
+
+QPointF GraphView::transformGraphToView(QPointF& point, double stregth_factorX, double stregth_factorY, QPointF& translation) const
+{
+	return QPointF((point.x() - actualBound.minX) * stregth_factorX, (actualBound.maxY - point.y()) * stregth_factorY) + translation;
+}
+
+
+QPointF GraphView::transformViewToGraph(QPointF& point) const
+{
+	QRect graphDisplayRect(rect());
+	graphDisplayRect.setBottom(graphDisplayRect.bottom() - MARGIN_BOTTOM);
+	graphDisplayRect.setLeft(graphDisplayRect.left() + MARGIN_LEFT);
+	graphDisplayRect.setTop(graphDisplayRect.top() + MARGIN_TOP);
+	graphDisplayRect.setRight(graphDisplayRect.right() - MARGIN_RIGHT);
+	graphDisplayRect.setWidth(graphDisplayRect.width() - (graphDisplayRect.width() % X_AXIS_GAP_AMOUNT) + 1);
+	graphDisplayRect.setHeight(graphDisplayRect.height() - (graphDisplayRect.height() % Y_AXIS_GAP_AMOUNT) + 1);
+	QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
+	double stregth_factorX = graphDisplayRect.width() / rangeGraphX;
+	double stregth_factorY = graphDisplayRect.height() / rangeGraphY;
+	return QPointF(((point.x()-translation.x())/stregth_factorX)+ actualBound.minX, actualBound.maxY - ((point.y()-translation.y())/stregth_factorY));
+}
+
+GraphDataPoint GraphView::findNearestGraphDataPointFromGraphCoordinate(QPointF& graphpoint)
+{
+	if (graphSeriesVec.empty() || graphSeriesVec[0].data->empty()) {
+		return GraphDataPoint();
+	}
+	GraphDataPoint min = graphSeriesVec[0].data->at(0);
+	double minSqaredDistance = min.squaredDistance(graphpoint);
+	for (GraphSeries& series : graphSeriesVec) {
+		for (const GraphDataPoint point : *series.data) {
+			double distance = point.squaredDistance(graphpoint);
+			if (distance < minSqaredDistance) {
+				minSqaredDistance = distance;
+				min = point;
+				highlightSeries = &series;
+			}
+		}
+	}
+	return min;
 }
 
-QPointF GraphView::transformPoint(QPointF& point, double stregth_factorX, double stregth_factorY) const
+//Help Function:
+double sqaredDistance(const QPointF& a, const QPointF& b)
 {
-	return QPointF((point.x() - actualBound.minX) * stregth_factorX, (actualBound.maxY - point.y()) * stregth_factorY);
+	return std::pow(a.x() - b.x(), 2) + std::pow(a.y() - b.y(), 2);
 }
 
+
+GraphDataPoint GraphView::findNearestGraphDataPointFromViewCordinate(QPointF& viewpoint)
+{
+	if (graphSeriesVec.empty()) {
+		return GraphDataPoint();
+	}
+	QRect graphDisplayRect(rect());
+	graphDisplayRect.setBottom(graphDisplayRect.bottom() - MARGIN_BOTTOM);
+	graphDisplayRect.setLeft(graphDisplayRect.left() + MARGIN_LEFT);
+	graphDisplayRect.setTop(graphDisplayRect.top() + MARGIN_TOP);
+	graphDisplayRect.setRight(graphDisplayRect.right() - MARGIN_RIGHT);
+	graphDisplayRect.setWidth(graphDisplayRect.width() - (graphDisplayRect.width() % X_AXIS_GAP_AMOUNT) + 1);
+	graphDisplayRect.setHeight(graphDisplayRect.height() - (graphDisplayRect.height() % Y_AXIS_GAP_AMOUNT) + 1);
+	QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
+	double stregth_factorX = graphDisplayRect.width() / rangeGraphX;
+	double stregth_factorY = graphDisplayRect.height() / rangeGraphY;
+
+	GraphDataPoint min = graphSeriesVec[0].data->at(0);
+	double minSqaredDistance = sqaredDistance(min.toQPointF(), viewpoint);
+	std::vector<GraphSeries>::iterator actualBestSeries = graphSeriesVec.begin();
+	for (auto seriesIter = graphSeriesVec.begin(); seriesIter != graphSeriesVec.end(); seriesIter++) {
+		for (const GraphDataPoint point : *seriesIter->data) {
+			double distance = sqaredDistance(transformGraphToView(point.toQPointF(), stregth_factorX, stregth_factorY, translation), viewpoint);
+			if (distance < minSqaredDistance) {
+				minSqaredDistance = distance;
+				min = point;
+				//highlightSeries = &*seriesIter;
+				actualBestSeries = seriesIter;
+			}
+		}
+	}
+	std::rotate(graphSeriesVec.begin(), actualBestSeries + 1, graphSeriesVec.end());
+	highlightSeries = &graphSeriesVec.back();
+	return min;
+}
+
+
+
+
 bool GraphView::inBoundX(QPointF& point)
 {
 
@@ -213,11 +353,27 @@ void GraphView::keyPressEvent(QKeyEvent* event)
 		break;
 	case Qt::Key_PageUp:
 	case Qt::Key_Plus:
-		fixedBound.move(this, Bound::Change::ZoomIn);
+		if (event->modifiers() & Qt::ShiftModifier) {
+			fixedBound.move(this, Bound::Change::ZoomInY);
+		}
+		else if (event->modifiers() & Qt::ControlModifier) {
+			fixedBound.move(this, Bound::Change::ZoomInX);
+		}
+		else {
+			fixedBound.move(this, Bound::Change::ZoomIn);
+		}
 		break;
 	case Qt::Key_PageDown:
 	case Qt::Key_Minus:
-		fixedBound.move(this, Bound::Change::ZoomOut);
+		if (event->modifiers() & Qt::ShiftModifier) {
+			fixedBound.move(this, Bound::Change::ZoomOutY);
+		}
+		else if (event->modifiers() & Qt::ControlModifier) {
+			fixedBound.move(this, Bound::Change::ZoomOutX);
+		}
+		else {
+			fixedBound.move(this, Bound::Change::ZoomOut);
+		}
 		break;
 	case Qt::Key_R:
 		fixedBound = totalBound;
@@ -252,10 +408,27 @@ void GraphView::wheelEvent(QWheelEvent* event)
 		QPoint numSteps = numDegrees / 15;
 		switch (numSteps.y()) {
 		case 1:
-			fixedBound.move(this, Bound::Change::ZoomIn);
+			if (event->modifiers() & Qt::ShiftModifier) {
+				fixedBound.move(this, Bound::Change::ZoomInY);
+			}
+			else if (event->modifiers() & Qt::ControlModifier) {
+				fixedBound.move(this, Bound::Change::ZoomInX);
+			}
+			else {
+				fixedBound.move(this, Bound::Change::ZoomIn);
+			}
 			break;
 		case -1:
-			fixedBound.move(this, Bound::Change::ZoomOut);
+			if (event->modifiers() & Qt::ShiftModifier) {
+				fixedBound.move(this, Bound::Change::ZoomOutY);
+			}
+			else if (event->modifiers() & Qt::ControlModifier) {
+				fixedBound.move(this, Bound::Change::ZoomOutX);
+			}
+			else {
+				fixedBound.move(this, Bound::Change::ZoomOut);
+			}
+			break;
 		default:
 			break;
 		}
@@ -267,8 +440,8 @@ void GraphView::wheelEvent(QWheelEvent* event)
 void GraphView::mouseMoveEvent(QMouseEvent* event)
 {
 	//QToolTip::showText(event->globalPos(), "Hello", this);
-	QPointF delta = event->pos() - oldPos;
-	oldPos = event->pos();
+	QPointF delta = event->pos() - oldPositionForMouseEvent;
+	oldPositionForMouseEvent = event->pos();
 	double rangeX = std::abs(actualBound.maxX - actualBound.minX);
 	int width =  (rect().right() - 10) - (rect().left() + 50);
 	width -= (width % X_AXIS_GAP_AMOUNT) + 1;
@@ -286,15 +459,25 @@ void GraphView::mouseMoveEvent(QMouseEvent* event)
 
 void GraphView::mousePressEvent(QMouseEvent* event)
 {
-	QString string = "Pos (" + QString::number(event->pos().x()) + "," + QString::number(event->pos().y()) + ")";
-	//QString printable = QStringLiteral("Pos(%d,%d)").arg(event->pos().x(), event->pos().y());
+	mousePressed = true;
+	//To calculate the delta for mouse move  event
+	oldPositionForMouseEvent = event->pos();
+	mousePressedValue = transformViewToGraph(oldPositionForMouseEvent);
+	GraphDataPoint nearestPoint = findNearestGraphDataPointFromViewCordinate(oldPositionForMouseEvent);
+	QString metaFileName;
+	if (highlightSeries && highlightSeries->run)
+	{
+		metaFileName = " " + QString::fromStdString(highlightSeries->run->name);
+	}
+	QString string =metaFileName;
 	QToolTip::showText(event->globalPos() - QPoint(0, 35), string);
-	oldPos = event->pos();
 }
 
 void GraphView::mouseReleaseEvent(QMouseEvent* event)
 {
-
+	highlightSeries = nullptr;
+	mousePressed = false;
+	update();
 }
 
 void GraphView::update()
@@ -308,6 +491,14 @@ void GraphView::setDrawBound(bool value)
 {
 	drawTotalBound = value;
 }
+/**
+ *  Delete all series.
+ *
+ */
+void GraphView::reset()
+{
+	graphSeriesVec.clear();
+}
 
 
 void GraphView::calculateTotalGraphBound()
@@ -347,14 +538,15 @@ void GraphView::calculateMinMaxXY(GraphSeries& lgs)
 	lgs.maxY = pairY.second->y;
 }
 
-void GraphView::addSeries(std::vector<GraphDataPoint>& line, QColor color, GraphSeries::SeriesType type)
+void GraphView::addSeries(std::vector<GraphDataPoint>* line, RunData* run, QColor color, GraphSeries::SeriesType type)
 {
-	if (line.empty()) {
+	if (line->empty()) {
 		qDebug() << "Series is empty!";
 		return;
 	}
 	GraphSeries lgs;
-	lgs.data = &line;
+	lgs.run = run;
+	lgs.data = line;
 	lgs.type = type;
 	calculateMinMaxXY(lgs);
 	lgs.color = color;
@@ -407,31 +599,48 @@ void GraphView::generateAndAddRandomLine()
 		for (int i = 0; i < randomPointVec.size(); i++) {
 			randomPointVec[i] = GraphDataPoint(i + 500, i * scale + realDistr(gen));
 		}
-		addLine(randomPointVec);
+		addLine(&randomPointVec, nullptr);
 	}
 }
 
-void GraphView::addLine(std::vector<GraphDataPoint>& line)
+void GraphView::addLine(std::vector<GraphDataPoint>* line, RunData* run)
 {
-	addSeries(line, generateNextColorForGraph(), GraphSeries::SeriesType::Line);
+	addSeries(line, run,generateNextColorForGraph(), GraphSeries::SeriesType::Line);
 }
 
-void GraphView::addLine(std::vector<GraphDataPoint>& line, QColor color)
+void GraphView::addLine(std::vector<GraphDataPoint>* line, RunData* run, QColor color)
 {
-	addSeries(line, color, GraphSeries::SeriesType::Line);
+	addSeries(line, run, color, GraphSeries::SeriesType::Line);
 }
 
-void GraphView::addDots(std::vector<GraphDataPoint>& dots)
+void GraphView::addDots(std::vector<GraphDataPoint>* dots, RunData* run)
 {
-	addSeries(dots, generateNextColorForGraph(), GraphSeries::SeriesType::Dot);
+	addSeries(dots, run, generateNextColorForGraph(), GraphSeries::SeriesType::Dot);
 }
 
-void GraphView::addDots(std::vector<GraphDataPoint>& dots, QColor color)
+void GraphView::addDots(std::vector<GraphDataPoint>* dots, RunData* run, QColor color)
 {
-	addSeries(dots, color, GraphSeries::SeriesType::Dot);
+	addSeries(dots, run, color, GraphSeries::SeriesType::Dot);
 }
 
+void GraphView::removeAllSeriesWithRundata(RunData* run)
+{
+	//std::for_each(graphSeriesVec);
+}
 
+void GraphView::removeAllSeries()
+{
+}
+void GraphView::setMinIter(int value)
+{
+	minIter = value;
+	update();
+}
+void GraphView::setMaxIter(int value)
+{
+	maxIter = value;
+	update();
+}
 
 void Bound::move(GraphView* widget,const Change& type, const double changePercentage)
 {
@@ -466,6 +675,22 @@ void Bound::move(GraphView* widget,const Change& type, const double changePercen
 		minX += changeX;
 		maxX -= changeX;
 		break;
+	case Change::ZoomInX:
+		minX += changeX;
+		maxX -= changeX;
+		break;
+	case Change::ZoomInY:
+		minY += changeY;
+		maxY -= changeY;
+		break;
+	case Change::ZoomOutX:
+		minX -= changeX;
+		maxX += changeX;
+		break;
+	case Change::ZoomOutY:
+		minY -= changeY;
+		maxY += changeY;
+		break;
 	default:
 		break;
 	}
@@ -481,18 +706,3 @@ void Bound::move(GraphView* widget, QPointF& delta, double realPixelWidthX, doub
 	maxY += delta.y() * realPixelWidthY;
 	widget->update();
 }
-
-GraphDataPoint::GraphDataPoint(double x, double y, SolutionPointData* orginalPoint) : x(x), y(y), orginalPoint(orginalPoint)
-{
-
-}
-
-bool GraphDataPoint::existLink()
-{
-	return orginalPoint != nullptr;
-}
-
-QPointF GraphDataPoint::toQPointF()
-{
-	return QPointF(x, y);
-}

+ 43 - 25
metavis/GraphView.h

@@ -1,27 +1,27 @@
 #pragma once
 #include <QWidget>
 #include <QPaintEvent>
+#include <QAbstractListModel>
 #include <QPen>
 #include <QPainter>
 #include <vector>
 #include "RunData.h"
 
+
 class GraphView;
+class RunData;
 struct SolutionPointData;
-struct GraphDataPoint {
-	double x;
-	double y;
-	SolutionPointData* orginalPoint;
-	QColor color;
-	GraphDataPoint():x(0),y(0), orginalPoint(nullptr){}
-	GraphDataPoint(double x, double y, SolutionPointData* orginalPoint = nullptr);
-	bool existLink();
-	QPointF toQPointF();
-};
+
+
+
+
 struct GraphSeries {
 	std::vector<GraphDataPoint>* data;
 	double minX, maxX;
 	double minY, maxY;
+	RunData* run;
+
+
 
 	//Settings for visual
 	QString name;
@@ -31,11 +31,12 @@ struct GraphSeries {
 	enum class SeriesType{ Line, Dot, LineDot};
 	bool useDataPointColor = false;
 	SeriesType type;
+	bool hide = false;
 };
 
 struct Bound {
 	double minX = 0, minY = 0, maxX = 0, maxY = 0;
-	enum class Change{MoveLeft, MoveRight, MoveUp, MoveDown, ZoomOut, ZoomIn, Reset};
+	enum class Change{MoveLeft, MoveRight, MoveUp, MoveDown, ZoomOut, ZoomIn, ZoomInX, ZoomInY, ZoomOutX, ZoomOutY, Reset};
 	Bound() {}
 	Bound(double minX, double maxX, double minY, double maxY):minX(minX), maxX(maxX), minY(minY), maxY(maxY){}
 	void move(GraphView* widget,const Change& type, const double changePercentage = 0.1);
@@ -47,55 +48,72 @@ class GraphView :public QWidget
 	Q_OBJECT
 
 public:
-	GraphView(QWidget* parent, QString title, Bound fixedBound);
+	GraphView(QWidget* parent, Bound fixedBound = Bound());
 	~GraphView();
 	QString title;
 	Bound fixedBound;
+	//TODO: make fancy
+	bool useInterval = false;
+	int maxIter = 0;
+	int minIter = 0;
+	std::vector<GraphSeries> graphSeriesVec;
+
 private:
 	bool useFixedBound;
-	void paintEvent(QPaintEvent* event) override;
 	//Data
-	std::vector<GraphSeries> graphSeriesVec;
 	Bound totalBound;
 	Bound actualBound = totalBound;
 	bool drawTotalBound = false;
 	double rangeGraphX = 0, rangeGraphY = 0;
-	
-	
 	//Visualization
 	QString xAxisNumbers[11];
 	QString yAxisNumbers[11];
 	double hueoffset;
 	QPen linePen, rectPen, axisPen;
-
+	GraphSeries* highlightSeries = nullptr;
+	//Control
+	QPointF oldPositionForMouseEvent;
+	QPointF mousePressedValue;
+	bool mousePressed = false;
 
 
 
 public:
 	void setUseFixedBound(bool value);
 	void generateAndAddRandomLine();
-	void addLine(std::vector<GraphDataPoint>& line);
-	void addLine(std::vector<GraphDataPoint>& line, QColor color);
-	void addDots(std::vector<GraphDataPoint>& dots);
-	void addDots(std::vector<GraphDataPoint>& dots, QColor color);
+	void addLine(std::vector<GraphDataPoint>* line, RunData* run);
+	void addLine(std::vector<GraphDataPoint>* line, RunData* run, QColor color);
+	void addDots(std::vector<GraphDataPoint>* dots, RunData* run);
+	void addDots(std::vector<GraphDataPoint>* dots, RunData* run, QColor color);
+	void removeAllSeriesWithRundata(RunData* run);
+	void removeAllSeries();
+	void reset();
 	void calculateRangeXY();
 	void generateAxisNumberStrings();
 	void update();
 	void setDrawBound(bool value);
+	QColor generateNextColorForGraph();
 private:
 	void calculateTotalGraphBound();
 	void GraphView::calculateMinMaxXY(GraphSeries& lgs);
-	void addSeries(std::vector<GraphDataPoint>& line, QColor color, GraphSeries::SeriesType type);
-	QColor generateNextColorForGraph();
-	QPointF transformPoint(QPointF& point, double stregth_factorX, double stregth_factorY) const;
+	void addSeries(std::vector<GraphDataPoint>* line, RunData* run, QColor color, GraphSeries::SeriesType type);
+	QPointF transformGraphToView(QPointF& graphpoint, double stregth_factorX, double stregth_factorY, QPointF& translation) const;
+	QPointF transformViewToGraph(QPointF& viewpoint) const;
+	GraphDataPoint findNearestGraphDataPointFromGraphCoordinate(QPointF& graphpoint);
+	GraphDataPoint findNearestGraphDataPointFromViewCordinate(QPointF& viewpoint);
 	bool inBoundX(QPointF& point);
 	bool inBoundY(QPointF& point);
 	bool inBound(QPointF& point);
+
+	//overrides
 	void keyPressEvent(QKeyEvent* event) override;
 	void wheelEvent(QWheelEvent* event) override;
 	void mouseMoveEvent(QMouseEvent* event) override;
 	void mousePressEvent(QMouseEvent* event) override;
 	void mouseReleaseEvent(QMouseEvent* event) override;
-	QPointF oldPos;
+	void paintEvent(QPaintEvent* event) override;
+public slots:
+	void setMaxIter(int value);
+	void setMinIter(int value);
 };
 

+ 53 - 0
metavis/GraphViewSettingDialog.cpp

@@ -0,0 +1,53 @@
+#include "pch.h"
+#include "GraphViewSettingDialog.h"
+#include <QListWidgetItem>
+#include <QPushButton>
+#include "GraphViewSettingItem.h"
+#include <QScrollArea>
+
+
+
+GraphViewSettingDialog::GraphViewSettingDialog(GraphView* view, QString title ,QWidget *parent)
+	: QDialog(parent), view(view)
+{
+	ui.setupUi(this);
+	setWindowTitle(title);
+	this->graphVec = &view->graphSeriesVec;
+	vecsize = graphVec->size();
+	QWidget* toScroll = new QWidget(this);
+	layout = new QVBoxLayout(this);
+	layout->setContentsMargins(3, 0, 3, 0);
+	toScroll->setLayout(layout);
+	QVBoxLayout* myLayout = new QVBoxLayout(this);
+	myLayout->setContentsMargins(0, 0, 0, 0);
+	QScrollArea* scrollArea = new QScrollArea;
+	scrollArea->setWidgetResizable(true);
+	scrollArea->setWidget(toScroll);
+	myLayout->addWidget(scrollArea);
+	this->setLayout(myLayout);
+}
+
+GraphViewSettingDialog::~GraphViewSettingDialog()
+{
+}
+
+void GraphViewSettingDialog::openDialog()
+{
+	//Generate Buttons;
+
+	if (vecsize == graphVec->size()) {
+		this->activateWindow();
+		this->show();
+		return;
+	}
+	else {
+		vecsize = graphVec->size();
+	}
+
+	for (auto iter = graphVec->begin(); iter != graphVec->end(); iter++) {
+		GraphViewSettingItem* handle = new GraphViewSettingItem(view, &*iter, this);
+		layout->addWidget(handle);
+	}
+	this->show();
+}
+

+ 25 - 0
metavis/GraphViewSettingDialog.h

@@ -0,0 +1,25 @@
+#pragma once
+
+#include <QDialog>
+#include "GraphView.h"
+#include "ui_GraphViewSettingDialog.h"
+
+
+
+class GraphViewSettingDialog : public QDialog
+{
+	Q_OBJECT
+
+public:
+	GraphView* view;
+	std::vector<GraphSeries>* graphVec;
+public:
+	GraphViewSettingDialog(GraphView* view, QString title , QWidget *parent = Q_NULLPTR);
+	~GraphViewSettingDialog();
+public slots:
+	void openDialog();
+private:
+	int vecsize = 0;
+	Ui::GraphViewSettingDialog ui;
+	QVBoxLayout* layout;
+};

+ 20 - 0
metavis/GraphViewSettingDialog.ui

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GraphViewSettingDialog</class>
+ <widget class="QDialog" name="GraphViewSettingDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>695</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>GraphViewSettingDialog</string>
+  </property>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>

+ 92 - 0
metavis/GraphViewSettingItem.cpp

@@ -0,0 +1,92 @@
+#include "pch.h"
+#include "GraphViewSettingItem.h"
+#include <QColorDialog>
+
+GraphViewSettingItem::GraphViewSettingItem(GraphView* view, GraphSeries* series, QWidget *parent) : QWidget(parent), series(series), view(view)
+{
+	ui.setupUi(this);
+	// Init
+	switch (series->type) {
+	case GraphSeries::SeriesType::Dot:
+		ui.SytleComboBox->setCurrentIndex(1);
+		break;
+	case GraphSeries::SeriesType::LineDot:
+		ui.SytleComboBox->setCurrentIndex(2);
+		break;
+	case GraphSeries::SeriesType::Line:
+	default:
+		ui.SytleComboBox->setCurrentIndex(0);
+		break;
+	}
+	ui.LineSlider->setValue(series->lineWidth);
+	ui.CircleSlider->setValue(series->circleRadius);
+	dissableSlidersOnType();
+	updateGroupBoxTitle();
+
+
+	
+	//Connect
+	connect(ui.LineSlider, &QSlider::valueChanged, [=](const int& newValue) { series->lineWidth = newValue; view->update(); });
+	connect(ui.CircleSlider, &QSlider::valueChanged, [=](const int& newValue) { series->circleRadius = newValue; view->update(); });
+	connect(ui.SytleComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [=](const int& newValue) { setType(newValue);dissableSlidersOnType();view->update(); });
+	connect(ui.HideCheckbox, &QCheckBox::stateChanged, [=](const int& newValue) { series->hide = (newValue==2); view->update(); });
+	connect(ui.SetColorButton, &QPushButton::pressed, this, &GraphViewSettingItem::setColor);
+	connect(ui.NameEdit, &QLineEdit::textChanged, [=](const QString& text) { series->name = text; updateGroupBoxTitle(); });
+}
+
+GraphViewSettingItem::~GraphViewSettingItem()
+{
+}
+
+void GraphViewSettingItem::dissableSlidersOnType()
+{
+	switch (series->type) {
+	case GraphSeries::SeriesType::Line:
+		ui.LineSlider->setEnabled(true);
+		ui.CircleSlider->setEnabled(false);
+		break;
+	case GraphSeries::SeriesType::Dot:
+		ui.LineSlider->setEnabled(false);
+		ui.CircleSlider->setEnabled(true);
+		break;
+	case GraphSeries::SeriesType::LineDot:
+		ui.LineSlider->setEnabled(true);
+		ui.CircleSlider->setEnabled(true);
+		break;
+	default:
+		break;
+	}
+}
+
+void GraphViewSettingItem::setType(int type)
+{
+	switch (type) {
+	case 0:
+		series->type = GraphSeries::SeriesType::Line;
+		break;
+	case 1:
+		series->type = GraphSeries::SeriesType::Dot;
+		break;
+	case 2:
+		series->type = GraphSeries::SeriesType::LineDot;
+		break;
+	default:
+		break;
+	}
+}
+
+void GraphViewSettingItem::setColor()
+{
+	QColor color = QColorDialog::getColor(series->color, this, "Set Series Color", QColorDialog::ShowAlphaChannel);
+	if (!color.isValid()) return;
+	series->color = color;
+	updateGroupBoxTitle();
+	view->update();
+}
+
+void GraphViewSettingItem::updateGroupBoxTitle()
+{
+	ui.GroupBox->setTitle("Series: " + series->name);
+	QString style = "QGroupBox { color: rgb(%1, %2, %3); };";
+	ui.GroupBox->setStyleSheet(style.arg(series->color.red()).arg(series->color.green()).arg(series->color.blue()));
+}

+ 23 - 0
metavis/GraphViewSettingItem.h

@@ -0,0 +1,23 @@
+#pragma once
+
+#include <QWidget>
+#include "ui_GraphViewSettingItem.h"
+#include "GraphView.h"
+
+class GraphViewSettingItem : public QWidget
+{
+	Q_OBJECT
+
+public:
+	GraphViewSettingItem(GraphView* view, GraphSeries* series, QWidget *parent = Q_NULLPTR);
+	~GraphViewSettingItem();
+
+private:
+	Ui::GraphViewSettingItem ui;
+	GraphSeries* series;
+	GraphView* view;
+	void dissableSlidersOnType();
+	void setType(int type);
+	void setColor();
+	void updateGroupBoxTitle();
+};

+ 195 - 0
metavis/GraphViewSettingItem.ui

@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GraphViewSettingItem</class>
+ <widget class="QWidget" name="GraphViewSettingItem">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>645</width>
+    <height>520</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>GraphViewSettingItem</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout_2">
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item row="0" column="0">
+    <widget class="QGroupBox" name="GroupBox">
+     <property name="title">
+      <string>GroupBox</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_3">
+      <property name="leftMargin">
+       <number>9</number>
+      </property>
+      <item row="0" column="0">
+       <layout class="QFormLayout" name="LeftFormLayout">
+        <item row="0" column="0">
+         <widget class="QLabel" name="NameLabel">
+          <property name="text">
+           <string>Name</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <widget class="QLineEdit" name="NameEdit"/>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="LineLabel">
+          <property name="text">
+           <string>Line</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="0">
+         <widget class="QLabel" name="CircleLabel">
+          <property name="text">
+           <string>Circle</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="1">
+         <widget class="QSlider" name="CircleSlider">
+          <property name="minimum">
+           <number>1</number>
+          </property>
+          <property name="maximum">
+           <number>16</number>
+          </property>
+          <property name="value">
+           <number>1</number>
+          </property>
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="tickPosition">
+           <enum>QSlider::TicksBelow</enum>
+          </property>
+          <property name="tickInterval">
+           <number>1</number>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
+         <widget class="QSlider" name="LineSlider">
+          <property name="minimum">
+           <number>1</number>
+          </property>
+          <property name="maximum">
+           <number>16</number>
+          </property>
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="tickPosition">
+           <enum>QSlider::TicksBelow</enum>
+          </property>
+          <property name="tickInterval">
+           <number>1</number>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item row="0" column="1">
+       <spacer name="spacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Preferred</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>10</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="0" column="2">
+       <layout class="QFormLayout" name="RightFormLayout">
+        <item row="0" column="0">
+         <widget class="QLabel" name="StyleLabel">
+          <property name="text">
+           <string>Style</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <widget class="QComboBox" name="SytleComboBox">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <item>
+           <property name="text">
+            <string>Line</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>Dot</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>Line&amp;Dot</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="ColorLabel">
+          <property name="text">
+           <string>Color</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
+         <widget class="QPushButton" name="SetColorButton">
+          <property name="text">
+           <string>SetColor</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="1">
+         <widget class="QCheckBox" name="HideCheckbox">
+          <property name="text">
+           <string/>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="0">
+         <widget class="QLabel" name="HideLabel">
+          <property name="text">
+           <string>Hide</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>

+ 13 - 0
metavis/MetalogManagerItem.cpp

@@ -0,0 +1,13 @@
+#include "pch.h"
+#include "MetalogManagerItem.h"
+
+MetalogManagerItem::MetalogManagerItem(QWidget *parent)
+	: QWidget(parent)
+{
+	ui.setupUi(this);
+}
+
+MetalogManagerItem::~MetalogManagerItem()
+{
+	
+}

+ 16 - 0
metavis/MetalogManagerItem.h

@@ -0,0 +1,16 @@
+#pragma once
+
+#include <QWidget>
+#include "ui_MetalogManagerItem.h"
+
+class MetalogManagerItem : public QWidget
+{
+	Q_OBJECT
+
+public:
+	MetalogManagerItem(QWidget *parent = Q_NULLPTR);
+	~MetalogManagerItem();
+
+private:
+	Ui::MetalogManagerItem ui;
+};

+ 170 - 0
metavis/MetalogManagerItem.ui

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MetalogManagerItem</class>
+ <widget class="QWidget" name="MetalogManagerItem">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>475</width>
+    <height>78</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MetalogManagerItem</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <property name="leftMargin">
+    <number>3</number>
+   </property>
+   <property name="topMargin">
+    <number>3</number>
+   </property>
+   <property name="rightMargin">
+    <number>3</number>
+   </property>
+   <property name="bottomMargin">
+    <number>3</number>
+   </property>
+   <property name="horizontalSpacing">
+    <number>6</number>
+   </property>
+   <item row="2" column="0">
+    <widget class="QGroupBox" name="GroupBox">
+     <property name="title">
+      <string/>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0,0,0,0">
+      <property name="leftMargin">
+       <number>3</number>
+      </property>
+      <property name="topMargin">
+       <number>3</number>
+      </property>
+      <property name="rightMargin">
+       <number>3</number>
+      </property>
+      <property name="bottomMargin">
+       <number>3</number>
+      </property>
+      <property name="horizontalSpacing">
+       <number>0</number>
+      </property>
+      <property name="verticalSpacing">
+       <number>2</number>
+      </property>
+      <item row="0" column="2">
+       <widget class="QPushButton" name="AssigneButton">
+        <property name="text">
+         <string>Assign</string>
+        </property>
+        <property name="icon">
+         <iconset>
+          <normalon>:/metavis/Resources/assignButton.svg</normalon>
+         </iconset>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="3" column="3">
+       <spacer name="verticalSpacer">
+        <property name="orientation">
+         <enum>Qt::Vertical</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>20</width>
+          <height>40</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="0" column="0">
+       <widget class="QLabel" name="MetalogFileLabel">
+        <property name="text">
+         <string>Langer File Name Test Lorem Ipsum Lorem Ipsum</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="3">
+       <widget class="QPushButton" name="RemoveButton">
+        <property name="toolTip">
+         <string>Remove metalog file</string>
+        </property>
+        <property name="styleSheet">
+         <string notr="true"/>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset>
+          <normalon>:/metavis/Resources/close.svg</normalon>
+         </iconset>
+        </property>
+        <property name="autoDefault">
+         <bool>false</bool>
+        </property>
+        <property name="default">
+         <bool>false</bool>
+        </property>
+        <property name="flat">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0" colspan="4">
+       <widget class="QProgressBar" name="progressBar">
+        <property name="value">
+         <number>100</number>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+        </property>
+        <property name="textVisible">
+         <bool>true</bool>
+        </property>
+        <property name="invertedAppearance">
+         <bool>false</bool>
+        </property>
+        <property name="format">
+         <string>%p%</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="2" colspan="2">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Status: Ready</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources>
+  <include location="metavis.qrc"/>
+ </resources>
+ <connections/>
+</ui>

+ 8 - 0
metavis/Project.cpp

@@ -0,0 +1,8 @@
+#include "pch.h"
+#include "Project.h"
+
+void Project::addMetalogFile(std::string metaLogFile)
+{
+	//If file exist and can be open
+	runVec.push_back(Wrapper(metaLogFile));
+}

+ 28 - 0
metavis/Project.h

@@ -0,0 +1,28 @@
+#pragma once
+#include <vector>
+#include <string>
+
+#include "RunData.h"
+#include "GraphView.h"
+
+
+struct Wrapper {
+	RunData data;
+	Wrapper(std::string metaLogFile) : data(metaLogFile){}
+	std::vector<GraphView*> viewVec;
+};
+
+
+class Project
+{
+private:
+	std::vector<Wrapper> runVec;
+	std::vector<GraphView*> viewVec;
+public:	
+	void addMetalogFile(std::string metaLogFile);
+	void removeRunData(RunData* data);
+	void addGraphView(GraphView* view);
+	//TODO what assigned
+	void assignRunDataPointVecToView(RunData* data, std::vector<GraphDataPoint>* vec, GraphView* view);
+};
+

+ 103 - 0
metavis/ProjectManager.cpp

@@ -0,0 +1,103 @@
+#include "pch.h"
+#include <QTreeWidget>
+#include <QAction>
+#include <QMenu>
+#include "ProjectManager.h"
+
+ProjectManager::ProjectManager(QWidget *parent, metavis* owner)
+	: QWidget(parent), owner(owner)
+{
+	QGridLayout* gridlayout = new QGridLayout();
+	gridlayout->setContentsMargins(0, 0, 0, 0);
+	treeWidget = new QTreeWidget();
+	treeWidget->setColumnCount(1);
+	treeWidget->setHeaderHidden(true);
+	treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+	connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, &ProjectManager::prepareMenu);
+
+	QTreeWidgetItem* projectItem = new QTreeWidgetItem(treeWidget, QStringList("Project"), ProjectManager::Types::Project);
+	projectItem->setExpanded(true);
+	QTreeWidgetItem* filesItem = new QTreeWidgetItem(projectItem, QStringList("Metalog Files"), ProjectManager::Types::LogManger);
+	new QTreeWidgetItem(filesItem, QStringList("run.metalog"), ProjectManager::Types::LogFile);
+	new QTreeWidgetItem(filesItem, QStringList("run2.metalog"), ProjectManager::Types::LogFile);
+	new QTreeWidgetItem(filesItem, QStringList("run3.metalog"), ProjectManager::Types::LogFile);
+	new QTreeWidgetItem(filesItem, QStringList("run4.metalog"), ProjectManager::Types::LogFile);
+	new QTreeWidgetItem(filesItem, QStringList("run5.metalog"), ProjectManager::Types::LogFile);
+	filesItem->setExpanded(true);
+	QTreeWidgetItem* graphItem = new QTreeWidgetItem(projectItem, QStringList("Graph Views"), ProjectManager::Types::GraphManger);
+	new QTreeWidgetItem(graphItem, QStringList("Best vs Avg"), ProjectManager::Types::Graph);
+	new QTreeWidgetItem(graphItem, QStringList("Mean Hamming Distance"), ProjectManager::Types::Graph);
+	new QTreeWidgetItem(graphItem, QStringList("Min Max Average"), ProjectManager::Types::Graph);
+	new QTreeWidgetItem(graphItem, QStringList("Particle"), ProjectManager::Types::Graph);
+	graphItem->setExpanded(true);
+	gridlayout->addWidget(treeWidget);
+	this->setLayout(gridlayout);
+
+
+
+	//Create Menus
+	filesMenu = new QMenu(this);
+	QAction* metaLogAddAction = new QAction("Add new .metalog-File...");
+	connect(metaLogAddAction, &QAction::triggered, owner, &metavis::openFile);
+	QAction* removeAllFilesAction = new QAction("Remove all metalog-Files");
+	filesMenu->addAction(metaLogAddAction);
+	filesMenu->addAction(removeAllFilesAction);
+	
+	metalogFileMenu = new QMenu(this);
+	QIcon removeIcon(":/metavis/Resources/close_big_red.svg");
+	QAction* removeMetalogFileAction = new QAction("Remove");
+	removeMetalogFileAction->setIcon(removeIcon);
+	QAction* assignToGraphAction = new QAction("Assign...");
+	metalogFileMenu->addAction(removeMetalogFileAction);
+	metalogFileMenu->addAction(assignToGraphAction);
+
+
+	graphMenu = new QMenu(this);
+	QAction* assignMetalogFileAction = new QAction("Remove");
+	assignMetalogFileAction->setIcon(removeIcon);
+	graphMenu->addAction(assignMetalogFileAction);
+
+}
+
+ProjectManager::~ProjectManager()
+{
+}
+
+void ProjectManager::prepareMenu(const QPoint& pos) {
+	qDebug() << "TestMenu";
+	QTreeWidgetItem* item = treeWidget->itemAt(pos);
+	qDebug() << item->text(0) << "  " << item->type();
+	switch (item->type()) {
+	case Types::Graph:
+		graphMenu->exec(treeWidget->mapToGlobal(pos));
+		break;
+	case Types::GraphManger:
+		break;
+	case Types::LogFile:
+		metalogFileMenu->exec(treeWidget->mapToGlobal(pos));
+		break;
+	case Types::LogManger:
+		filesMenu->exec(treeWidget->mapToGlobal(pos));
+		break;
+	default:
+		break;
+	}
+
+}
+
+FileTreeItem::FileTreeItem(QTreeWidget* parent, const QStringList& strings, RunData* rundata, std::list<RunData>* list, int type) 
+	: QTreeWidgetItem(parent, strings, type), list(list), rundata(rundata)
+{
+
+}
+
+void FileTreeItem::remove()
+{
+	qDebug() << "Remove";
+	// Remove from graphs
+
+	//Remove from List
+	//list->remove(*rundata);
+	//Delete self
+	delete this;
+}

+ 39 - 0
metavis/ProjectManager.h

@@ -0,0 +1,39 @@
+#pragma once
+#include <QTreeWidget>
+#include <QWidget>
+#include <list>
+#include "metavis.h"
+#include "RunData.h"
+
+class FileTreeItem : public QTreeWidgetItem{
+
+private:
+	RunData* rundata = nullptr;
+	std::list<RunData>* list;
+
+public:
+	FileTreeItem(QTreeWidget* parent, const QStringList& strings, RunData* rundata, std::list<RunData>* list , int type = Type);
+	void remove();
+};
+
+class ProjectManager : public QWidget
+{
+	Q_OBJECT
+
+public:
+	ProjectManager(QWidget *parent, metavis* owner);
+	~ProjectManager();
+private:
+	metavis* owner;
+	QTreeWidget* treeWidget;
+	QMenu* filesMenu;
+	QMenu* metalogFileMenu;
+	QMenu* graphMenu;
+
+
+
+
+	enum Types{ LogManger = 1001, LogFile, GraphManger, Graph, Project};
+private slots:
+	void prepareMenu(const QPoint& pos);
+};

+ 154 - 0
metavis/RangeSlider.cpp

@@ -0,0 +1,154 @@
+#include "pch.h"
+#include "RangeSlider.h"
+#include <QDebug>
+#include "util.h"
+
+RangeSlider::RangeSlider(QWidget *parent)
+	: QWidget(parent), sliderRangeColor(200,200,200), rangeColor(0,122,204)
+{
+}
+
+RangeSlider::~RangeSlider()
+{
+}
+
+void RangeSlider::setMinSliderValue(int value)
+{
+	value = std::clamp(value, minRange, maxRange);
+	if (value > maxSliderValue) {
+		minSliderValue = maxSliderValue;
+		maxSliderValue = value;
+		emit minChanged(minSliderValue);
+		emit maxChanged(maxSliderValue);
+	}
+	else {
+		minSliderValue = value;
+		emit minChanged(minSliderValue);
+	}
+}
+
+void RangeSlider::setMaxSliderValue(int value)
+{
+	value = std::clamp(value, minRange, maxRange);
+	if (value < minSliderValue) {
+		maxSliderValue = minSliderValue;
+		minSliderValue = value;
+		emit minChanged(minSliderValue);
+		emit maxChanged(maxSliderValue);
+	}
+	else {
+		maxSliderValue = value;
+		emit maxChanged(maxSliderValue);
+	}
+}
+
+void RangeSlider::setRange(int min, int max)
+{
+	if (min > max) {
+		std::swap(min, max);
+	}
+	minRange = min;
+	maxRange = max;
+}
+
+void RangeSlider::setMinRange(int value)
+{
+	if (value > maxRange) {
+		minRange = maxRange;
+		maxRange = value;
+	}
+	else {
+		minRange = value;
+	}
+
+}
+
+void RangeSlider::setMaxRange(int value)
+{
+	if (value < minRange) {
+		maxRange = minRange;
+		minRange = value;
+	}
+	else {
+		maxRange = value;
+	}
+}
+
+void RangeSlider::paintEvent(QPaintEvent* event)
+{
+	QPainter painter(this);
+	painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing);
+	painter.setPen(sliderRangeColor);
+	//painter.setBrush(sliderRangeColor);
+	//FindMiddle
+	int middle = rect().center().y();
+	painter.drawRect(QRect(QPoint(minPos(), middle + 2), QPoint(maxPos(), middle - 2)));
+
+	double minSliderAlpha = util::inverseLinearInterpolation(minRange, maxRange, minSliderValue);
+	double minSliderPos = util::linearInterpolate(minPos(), maxPos(), minSliderAlpha);
+
+	double maxSliderAlpha = util::inverseLinearInterpolation(minRange, maxRange, maxSliderValue);
+	double maxSliderPos = util::linearInterpolate(minPos(), maxPos(), maxSliderAlpha);
+	
+	//DrawRange
+	painter.fillRect(QRectF(QPointF(minSliderPos, (double)middle - 2), QPointF(maxSliderPos, (double)middle + 2)), rangeColor);
+
+	
+	//DrawSlider
+	QPen pen(rangeColor);
+	pen.setWidth(2);
+	painter.setPen(pen);
+	painter.setBrush(sliderRangeColor);
+	painter.drawEllipse(QPointF(minSliderPos, (double)middle + 1), 10, 10);
+	painter.drawEllipse(QPointF(maxSliderPos, (double)middle + 1), 10, 10);
+}
+
+void RangeSlider::mouseMoveEvent(QMouseEvent* event)
+{
+	updateSliderPosition(event);
+}
+
+void RangeSlider::mousePressEvent(QMouseEvent* event)
+{
+	updateSliderPosition(event);
+}
+
+
+void RangeSlider::updateSliderPosition(QMouseEvent* event)
+{
+	
+	
+	
+	//pos to alpha
+	double alpha = util::inverseLinearInterpolation(minPos(), maxPos(), event->pos().x());
+	double minSliderAlpha = util::inverseLinearInterpolation(minRange, maxRange, minSliderValue);
+	double maxSliderAlpha = util::inverseLinearInterpolation(minRange, maxRange, maxSliderValue);
+
+	if (minSliderAlpha < alpha && alpha < maxSliderAlpha) {
+
+	}
+
+
+	double value = util::linearInterpolate(minRange, maxRange, alpha);
+	
+	double distanceToMin = std::abs(minSliderValue - value);
+	double distanceToMax = std::abs(maxSliderValue - value);
+	if (distanceToMin < distanceToMax) {
+		setMinSliderValue(value);
+	}
+	else {
+		setMaxSliderValue(value);
+	}
+	update();
+}
+
+int RangeSlider::maxPos()
+{
+	return rect().right() - 11;
+}
+
+int RangeSlider::minPos()
+{
+	return rect().left() + 11;
+}
+

+ 36 - 0
metavis/RangeSlider.h

@@ -0,0 +1,36 @@
+#pragma once
+#include <QColor>
+#include <QWidget>
+
+class RangeSlider : public QWidget
+{
+	Q_OBJECT
+
+	int minRange = 0, maxRange = 100, minSliderValue = 25, maxSliderValue = 75;
+	QColor rangeColor, sliderRangeColor;
+public:
+	RangeSlider(QWidget *parent);
+	~RangeSlider();
+	void setMinSliderValue(int value);
+	void setMaxSliderValue(int value);
+	void setRange(int min, int max);
+	void setMinRange(int value);
+	void setMaxRange(int value);
+
+	//Visual Settings:
+	void setRangeColor(QColor color);
+	void setSliderRangeColor(QColor color);
+private:
+	void paintEvent(QPaintEvent* event) override;
+	void mouseMoveEvent(QMouseEvent* event) override;
+	void mousePressEvent(QMouseEvent* event) override;
+	void updateSliderPosition(QMouseEvent* event);
+
+	int maxPos();
+	int minPos();
+
+
+signals:
+	void maxChanged(int max);
+	void minChanged(int min);
+};

+ 1 - 0
metavis/Resources/assignButton.svg

@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg data-name="Layer 1" id="Layer_1" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:#000000;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style></defs><title/><path class="cls-1" d="M52.74,27.78c1.95,1,2,2.73,0,3.77L15.53,51.45c-1.95,1-3.53.09-3.53-2.11V10c0-2.21,1.58-3.15,3.53-2.11Z"/></svg>

+ 1 - 0
metavis/Resources/close.svg

@@ -0,0 +1 @@
+<svg id="Capa_1" enable-background="new 0 0 413.348 413.348"  viewBox="0 0 413.348 413.348"  xmlns="http://www.w3.org/2000/svg"><path d="m413.348 24.354-24.354-24.354-182.32 182.32-182.32-182.32-24.354 24.354 182.32 182.32-182.32 182.32 24.354 24.354 182.32-182.32 182.32 182.32 24.354-24.354-182.32-182.32z"/></svg>

+ 1 - 0
metavis/Resources/close_Big.svg

@@ -0,0 +1 @@
+<svg id="Capa_1" enable-background="new 0 0 386.667 386.667" height="512" viewBox="0 0 386.667 386.667" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m386.667 45.564-45.564-45.564-147.77 147.769-147.769-147.769-45.564 45.564 147.769 147.769-147.769 147.77 45.564 45.564 147.769-147.769 147.769 147.769 45.564-45.564-147.768-147.77z"/></svg>

+ 3 - 0
metavis/Resources/close_big_red.svg

@@ -0,0 +1,3 @@
+<svg id="Capa_1" enable-background="new 0 0 386.667 386.667" viewBox="-100 -100 486.667 486.667" xmlns="http://www.w3.org/2000/svg">
+<path fill="#cc0000" d="m386.667 45.564-45.564-45.564-147.77 147.769-147.769-147.769-45.564 45.564 147.769 147.769-147.769 147.77 45.564 45.564 147.769-147.769 147.769 147.769 45.564-45.564-147.768-147.77z"/>
+</svg>

+ 3 - 0
metavis/Resources/close_red.svg

@@ -0,0 +1,3 @@
+<svg id="Capa_1" enable-background="new 0 0 1000 1000"  viewBox="0 0 413.348 413.348"  xmlns="http://www.w3.org/2000/svg">
+<path d="m413.348 24.354-24.354-24.354-182.32 182.32-182.32-182.32-24.354 24.354 182.32 182.32-182.32 182.32 24.354 24.354 182.32-182.32 182.32 182.32 24.354-24.354-182.32-182.32z"/>
+</svg>

+ 66 - 0
metavis/Resources/settingIcon.svg

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 174.248 174.248" style="enable-background:new 0 0 174.248 174.248;" xml:space="preserve">
+<g>
+	<path d="M173.145,73.91c-0.413-2.722-2.29-4.993-4.881-5.912l-13.727-4.881c-0.812-2.3-1.733-4.536-2.754-6.699l6.247-13.146
+		c1.179-2.479,0.899-5.411-0.729-7.628c-5.265-7.161-11.556-13.452-18.698-18.693c-2.219-1.629-5.141-1.906-7.625-0.724
+		l-13.138,6.242c-2.163-1.021-4.402-1.94-6.704-2.752l-4.883-13.729c-0.919-2.586-3.184-4.458-5.9-4.876
+		c-9.65-1.483-16.792-1.483-26.457,0c-2.713,0.418-4.981,2.29-5.9,4.876l-4.883,13.729c-2.302,0.812-4.541,1.731-6.702,2.752
+		l-13.143-6.242c-2.479-1.181-5.406-0.904-7.623,0.724c-7.142,5.241-13.433,11.532-18.698,18.693
+		c-1.629,2.217-1.908,5.148-0.729,7.628l6.247,13.146c-1.021,2.159-1.94,4.4-2.754,6.699L5.982,68.003
+		c-2.589,0.919-4.463,3.189-4.879,5.907c-0.749,4.92-1.099,9.115-1.099,13.219c0,4.098,0.35,8.299,1.099,13.219
+		c0.413,2.722,2.29,4.993,4.881,5.912l13.727,4.881c0.814,2.304,1.736,4.541,2.754,6.704l-6.247,13.141
+		c-1.179,2.479-0.899,5.411,0.727,7.623c5.258,7.156,11.549,13.447,18.7,18.698c2.217,1.629,5.144,1.911,7.625,0.724l13.138-6.242
+		c2.163,1.021,4.402,1.94,6.704,2.752l4.883,13.729c0.919,2.586,3.184,4.458,5.9,4.876c4.828,0.744,9.154,1.104,13.228,1.104
+		c4.074,0,8.401-0.36,13.228-1.104c2.715-0.418,4.981-2.29,5.9-4.876l4.883-13.729c2.302-0.812,4.541-1.731,6.704-2.752
+		l13.138,6.242c2.484,1.186,5.411,0.904,7.628-0.724c7.159-5.26,13.45-11.551,18.698-18.698c1.626-2.212,1.906-5.144,0.727-7.623
+		l-6.247-13.141c1.021-2.163,1.942-4.405,2.754-6.704l13.727-4.881c2.591-0.919,4.468-3.189,4.881-5.912
+		c0.749-4.92,1.099-9.12,1.099-13.219S173.894,78.829,173.145,73.91z M158.949,93.72l-12.878,4.58
+		c-2.251,0.797-3.982,2.625-4.66,4.92c-1.15,3.889-2.664,7.569-4.504,10.943c-1.142,2.1-1.213,4.619-0.187,6.777l5.841,12.285
+		c-2.822,3.389-5.943,6.515-9.337,9.334l-12.283-5.834c-2.161-1.036-4.672-0.953-6.775,0.185c-3.379,1.838-7.061,3.35-10.953,4.502
+		c-2.29,0.676-4.118,2.406-4.917,4.657l-4.582,12.883c-4.677,0.476-8.503,0.476-13.18,0l-4.582-12.883
+		c-0.8-2.246-2.628-3.982-4.917-4.657c-3.894-1.152-7.579-2.664-10.953-4.502c-2.103-1.147-4.619-1.22-6.775-0.185l-12.283,5.839
+		c-3.391-2.825-6.512-5.946-9.337-9.339l5.841-12.285c1.026-2.159,0.955-4.677-0.187-6.777c-1.835-3.364-3.35-7.049-4.504-10.948
+		c-0.678-2.29-2.411-4.118-4.66-4.915l-12.878-4.58c-0.243-2.343-0.36-4.502-0.36-6.592s0.117-4.244,0.36-6.587l12.881-4.584
+		c2.248-0.797,3.979-2.625,4.657-4.915c1.152-3.889,2.667-7.574,4.504-10.953c1.142-2.095,1.213-4.619,0.187-6.772l-5.841-12.285
+		c2.827-3.393,5.948-6.519,9.337-9.339l12.288,5.839c2.151,1.036,4.677,0.953,6.775-0.185c3.372-1.838,7.054-3.35,10.948-4.502
+		c2.29-0.676,4.118-2.411,4.917-4.657l4.582-12.883c4.633-0.481,8.466-0.481,13.18,0l4.582,12.883
+		c0.8,2.246,2.628,3.982,4.917,4.657c3.894,1.152,7.579,2.664,10.953,4.502c2.103,1.147,4.614,1.22,6.775,0.185l12.283-5.839
+		c3.389,2.82,6.51,5.946,9.337,9.339l-5.841,12.285c-1.026,2.154-0.955,4.677,0.187,6.772c1.843,3.389,3.357,7.069,4.504,10.948
+		c0.678,2.295,2.409,4.123,4.66,4.92l12.878,4.58c0.243,2.343,0.36,4.502,0.36,6.592S159.192,91.377,158.949,93.72z"/>
+	<path d="M87.124,50.802c-19.062,0-34.571,15.508-34.571,34.571s15.508,34.571,34.571,34.571s34.571-15.508,34.571-34.571
+		S106.186,50.802,87.124,50.802z M87.124,105.009c-10.827,0-19.636-8.809-19.636-19.636s8.809-19.636,19.636-19.636
+		s19.636,8.809,19.636,19.636S97.951,105.009,87.124,105.009z"/>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>

+ 79 - 0
metavis/Resources/settingIcon_hovered.svg

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 174.248 174.248" style="enable-background:new 0 0 174.248 174.248;" xml:space="preserve">
+<g>
+	<path d="M173.145,73.91c-0.413-2.722-2.29-4.993-4.881-5.912l-13.727-4.881c-0.812-2.3-1.733-4.536-2.754-6.699l6.247-13.146
+		c1.179-2.479,0.899-5.411-0.729-7.628c-5.265-7.161-11.556-13.452-18.698-18.693c-2.219-1.629-5.141-1.906-7.625-0.724
+		l-13.138,6.242c-2.163-1.021-4.402-1.94-6.704-2.752l-4.883-13.729c-0.919-2.586-3.184-4.458-5.9-4.876
+		c-9.65-1.483-16.792-1.483-26.457,0c-2.713,0.418-4.981,2.29-5.9,4.876l-4.883,13.729c-2.302,0.812-4.541,1.731-6.702,2.752
+		l-13.143-6.242c-2.479-1.181-5.406-0.904-7.623,0.724c-7.142,5.241-13.433,11.532-18.698,18.693
+		c-1.629,2.217-1.908,5.148-0.729,7.628l6.247,13.146c-1.021,2.159-1.94,4.4-2.754,6.699L5.982,68.003
+		c-2.589,0.919-4.463,3.189-4.879,5.907c-0.749,4.92-1.099,9.115-1.099,13.219c0,4.098,0.35,8.299,1.099,13.219
+		c0.413,2.722,2.29,4.993,4.881,5.912l13.727,4.881c0.814,2.304,1.736,4.541,2.754,6.704l-6.247,13.141
+		c-1.179,2.479-0.899,5.411,0.727,7.623c5.258,7.156,11.549,13.447,18.7,18.698c2.217,1.629,5.144,1.911,7.625,0.724l13.138-6.242
+		c2.163,1.021,4.402,1.94,6.704,2.752l4.883,13.729c0.919,2.586,3.184,4.458,5.9,4.876c4.828,0.744,9.154,1.104,13.228,1.104
+		c4.074,0,8.401-0.36,13.228-1.104c2.715-0.418,4.981-2.29,5.9-4.876l4.883-13.729c2.302-0.812,4.541-1.731,6.704-2.752
+		l13.138,6.242c2.484,1.186,5.411,0.904,7.628-0.724c7.159-5.26,13.45-11.551,18.698-18.698c1.626-2.212,1.906-5.144,0.727-7.623
+		l-6.247-13.141c1.021-2.163,1.942-4.405,2.754-6.704l13.727-4.881c2.591-0.919,4.468-3.189,4.881-5.912
+		c0.749-4.92,1.099-9.12,1.099-13.219S173.894,78.829,173.145,73.91z M158.949,93.72l-12.878,4.58
+		c-2.251,0.797-3.982,2.625-4.66,4.92c-1.15,3.889-2.664,7.569-4.504,10.943c-1.142,2.1-1.213,4.619-0.187,6.777l5.841,12.285
+		c-2.822,3.389-5.943,6.515-9.337,9.334l-12.283-5.834c-2.161-1.036-4.672-0.953-6.775,0.185c-3.379,1.838-7.061,3.35-10.953,4.502
+		c-2.29,0.676-4.118,2.406-4.917,4.657l-4.582,12.883c-4.677,0.476-8.503,0.476-13.18,0l-4.582-12.883
+		c-0.8-2.246-2.628-3.982-4.917-4.657c-3.894-1.152-7.579-2.664-10.953-4.502c-2.103-1.147-4.619-1.22-6.775-0.185l-12.283,5.839
+		c-3.391-2.825-6.512-5.946-9.337-9.339l5.841-12.285c1.026-2.159,0.955-4.677-0.187-6.777c-1.835-3.364-3.35-7.049-4.504-10.948
+		c-0.678-2.29-2.411-4.118-4.66-4.915l-12.878-4.58c-0.243-2.343-0.36-4.502-0.36-6.592s0.117-4.244,0.36-6.587l12.881-4.584
+		c2.248-0.797,3.979-2.625,4.657-4.915c1.152-3.889,2.667-7.574,4.504-10.953c1.142-2.095,1.213-4.619,0.187-6.772l-5.841-12.285
+		c2.827-3.393,5.948-6.519,9.337-9.339l12.288,5.839c2.151,1.036,4.677,0.953,6.775-0.185c3.372-1.838,7.054-3.35,10.948-4.502
+		c2.29-0.676,4.118-2.411,4.917-4.657l4.582-12.883c4.633-0.481,8.466-0.481,13.18,0l4.582,12.883
+		c0.8,2.246,2.628,3.982,4.917,4.657c3.894,1.152,7.579,2.664,10.953,4.502c2.103,1.147,4.614,1.22,6.775,0.185l12.283-5.839
+		c3.389,2.82,6.51,5.946,9.337,9.339l-5.841,12.285c-1.026,2.154-0.955,4.677,0.187,6.772c1.843,3.389,3.357,7.069,4.504,10.948
+		c0.678,2.295,2.409,4.123,4.66,4.92l12.878,4.58c0.243,2.343,0.36,4.502,0.36,6.592S159.192,91.377,158.949,93.72z"/>
+	<path fill="#ff9f00" d="M158.949,93.72l-12.878,4.58
+		c-2.251,0.797-3.982,2.625-4.66,4.92c-1.15,3.889-2.664,7.569-4.504,10.943c-1.142,2.1-1.213,4.619-0.187,6.777l5.841,12.285
+		c-2.822,3.389-5.943,6.515-9.337,9.334l-12.283-5.834c-2.161-1.036-4.672-0.953-6.775,0.185c-3.379,1.838-7.061,3.35-10.953,4.502
+		c-2.29,0.676-4.118,2.406-4.917,4.657l-4.582,12.883c-4.677,0.476-8.503,0.476-13.18,0l-4.582-12.883
+		c-0.8-2.246-2.628-3.982-4.917-4.657c-3.894-1.152-7.579-2.664-10.953-4.502c-2.103-1.147-4.619-1.22-6.775-0.185l-12.283,5.839
+		c-3.391-2.825-6.512-5.946-9.337-9.339l5.841-12.285c1.026-2.159,0.955-4.677-0.187-6.777c-1.835-3.364-3.35-7.049-4.504-10.948
+		c-0.678-2.29-2.411-4.118-4.66-4.915l-12.878-4.58c-0.243-2.343-0.36-4.502-0.36-6.592s0.117-4.244,0.36-6.587l12.881-4.584
+		c2.248-0.797,3.979-2.625,4.657-4.915c1.152-3.889,2.667-7.574,4.504-10.953c1.142-2.095,1.213-4.619,0.187-6.772l-5.841-12.285
+		c2.827-3.393,5.948-6.519,9.337-9.339l12.288,5.839c2.151,1.036,4.677,0.953,6.775-0.185c3.372-1.838,7.054-3.35,10.948-4.502
+		c2.29-0.676,4.118-2.411,4.917-4.657l4.582-12.883c4.633-0.481,8.466-0.481,13.18,0l4.582,12.883
+		c0.8,2.246,2.628,3.982,4.917,4.657c3.894,1.152,7.579,2.664,10.953,4.502c2.103,1.147,4.614,1.22,6.775,0.185l12.283-5.839
+		c3.389,2.82,6.51,5.946,9.337,9.339l-5.841,12.285c-1.026,2.154-0.955,4.677,0.187,6.772c1.843,3.389,3.357,7.069,4.504,10.948
+		c0.678,2.295,2.409,4.123,4.66,4.92l12.878,4.58c0.243,2.343,0.36,4.502,0.36,6.592S159.192,91.377,158.949,93.72z"/>
+	<path d="M87.124,50.802c-19.062,0-34.571,15.508-34.571,34.571s15.508,34.571,34.571,34.571s34.571-15.508,34.571-34.571
+		S106.186,50.802,87.124,50.802z M87.124,105.009c-10.827,0-19.636-8.809-19.636-19.636s8.809-19.636,19.636-19.636
+		s19.636,8.809,19.636,19.636S97.951,105.009,87.124,105.009z"/>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>

+ 95 - 24
metavis/RunData.cpp

@@ -1,4 +1,5 @@
 #include "pch.h"
+#include "GraphView.h"
 #include "RunData.h"
 #include <QDebug>
 #include <QString>
@@ -10,8 +11,11 @@ RunData::RunData()
 {
 }
 
-RunData::RunData(std::string filePath): fileStream(filePath)
+RunData::RunData(std::string filePath) : fileStream(filePath), filePath(filePath)
 {
+    
+    std::string file = filePath.substr(filePath.find_last_of("/\\")+1);
+    name = file.substr(0, file.rfind("."));
     if (!fileStream.is_open())
     {
         //Cant open file
@@ -68,7 +72,8 @@ RunData::RunData(std::string filePath): fileStream(filePath)
     calculateBestAndAverageIter();
     calculateParticleSolution();
     calculateDotsForDistribution();
-    generateRandomBitFieldData();
+    calculateBitFieldData();
+    calculateMeanHammingDistance();
 }
 
 void RunData::getLine(std::string& bufferString)
@@ -148,13 +153,14 @@ void RunData::calculateParticleSolution()
 
 void RunData::calculateDotsForDistribution()
 {
-    for (SolutionPointData sol : solutionVec) {
-        dotsForDistribution.push_back(GraphDataPoint((double)sol.iteration, sol.objectiveFunction, &sol));
+    for (std::vector<SolutionPointData>::iterator it = solutionVec.begin(); it != solutionVec.end(); ++it) {
+        dotsForDistribution.push_back(GraphDataPoint((double)it->iteration, it->objectiveFunction, &*it));
     }
 }
-int binominalIndex(const int n, const int k)
+int binominalIndex(const int n, int k)
 {
-    return n * (n + 1) / 2 + k;
+    if (k > n / 2) k = n - k;
+    return ((n+1)/2) * (n/2 + 1) + k;
 }
 boost::multiprecision::cpp_int positionInPermutation2(std::vector<boost::multiprecision::cpp_int>& pascalTriangleVec, std::vector<bool>::iterator begin, std::vector<bool>::iterator end, int amountOfSetBits)
 {
@@ -174,25 +180,16 @@ boost::multiprecision::cpp_int positionInPermutation2(std::vector<boost::multipr
     return before + after;
 }
 
-void RunData::generateRandomBitFieldData()
+void RunData::calculateBitFieldData()
 {
     std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
 
     int amountOfBits = solutionVec[0].bitVec.size();
-    //for (SolutionPointData sol : solutionVec) {
-    //    int amountOfSetBits = std::count(sol.bitVec.begin(), sol.bitVec.end(), true);
-    //    boost::multiprecision::cpp_dec_float_100 position(positionInPermutation(sol.bitVec.begin(), sol.bitVec.end(), amountOfSetBits) - 1);
-    //    boost::multiprecision::cpp_dec_float_100 maxAmountOfPermutaions (binominal(amountOfBits, amountOfSetBits) - 1);
-
-    //    testForBitField.push_back(GraphDataPoint((position / maxAmountOfPermutaions).convert_to<double>(), amountOfSetBits));
-    //}
-    //std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
-    //std::chrono::milliseconds time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
-    //qDebug() << "BitField: " << time.count() << "ms";
     int lines = amountOfBits + 1;
-    std::vector<boost::multiprecision::cpp_int> pascalTriangleVec((lines * (lines + 1)) / 2);
+    std::vector<boost::multiprecision::cpp_int> pascalTriangleVec(((lines+1)/2 * (lines/2 + 1)));
     for (int line = 0; line < lines; line++) {
         for (int number = 0; number < line + 1; number++) {
+            if (number > line / 2) break;
             if (number == 0 || number == line) {
                 pascalTriangleVec[binominalIndex(line, number)] = 1;
             }
@@ -206,17 +203,65 @@ void RunData::generateRandomBitFieldData()
     std::chrono::milliseconds time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
     qDebug() << "PascalTriangle: " << time.count() << "ms";
 
-    for (SolutionPointData sol : solutionVec) {
-            int amountOfSetBits = std::count(sol.bitVec.begin(), sol.bitVec.end(), true);
-            boost::multiprecision::cpp_dec_float_100 position(positionInPermutation2(pascalTriangleVec, sol.bitVec.begin(), sol.bitVec.end(), amountOfSetBits) - 1);
+    for (std::vector<SolutionPointData>::iterator it = solutionVec.begin(); it != solutionVec.end(); ++it) {
+            int amountOfSetBits = std::count(it->bitVec.begin(), it->bitVec.end(), true);
+            boost::multiprecision::cpp_dec_float_100 position(positionInPermutation2(pascalTriangleVec, it->bitVec.begin(), it->bitVec.end(), amountOfSetBits) - 1);
             boost::multiprecision::cpp_dec_float_100 maxAmountOfPermutaions (pascalTriangleVec[binominalIndex(amountOfBits, amountOfSetBits)] - 1);
 
-            testForBitField.push_back(GraphDataPoint((position / maxAmountOfPermutaions).convert_to<double>(), amountOfSetBits));
+            dotsForBitField.push_back(GraphDataPoint((position / maxAmountOfPermutaions).convert_to<double>(), amountOfSetBits, &*it));
     }
     std::chrono::high_resolution_clock::time_point end2 = std::chrono::high_resolution_clock::now();
     time = std::chrono::duration_cast<std::chrono::milliseconds>(end2 - end);
-    qDebug() << "PascalTriangle: " << time.count() << "ms";
+    qDebug() << "BitFieldBerechnung: " << time.count() << "ms";
+
+}
 
+void RunData::calculateMeanHammingDistance()
+{
+    std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
+    std::vector<SolutionPointData>::iterator iterBegin = solutionVec.begin();
+    for (std::vector<SolutionPointData>::iterator iter = solutionVec.begin(); iter != solutionVec.end(); iter++) {
+        if (iter->iteration != iterBegin->iteration) {
+            double mean = meanHammingDistance(iterBegin, iter);
+            meanHammingDistancePerIteration.push_back(GraphDataPoint(iterBegin->iteration, mean));
+            iterBegin = iter;
+        }
+    }
+    std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
+    std::chrono::milliseconds time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
+    qDebug() << "Mean: " << time.count() << "ms";
+}
+
+
+int RunData::hammingdistance(std::vector<bool>& bitVecA, std::vector<bool>& bitVecB)
+{
+    //assert((bitVecA.size() == bitVecB.size()));
+    int count = 0;
+    auto iterB = bitVecB.begin();
+    for (auto iterA = bitVecA.begin(); iterA != bitVecA.end(); iterA++) {
+        if (*iterA != *iterB) {
+            count++;
+        }
+        iterB++;
+    }
+    return count;
+}
+
+double RunData::meanHammingDistance(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end)
+{
+    if (std::distance(begin, end)  <= 1) {
+        return 0.0;
+    }
+    std::vector<SolutionPointData>::iterator startParticle(begin);
+    double hammingValuesAccumulated = 0.0;
+    int count = 0;
+    for (std::vector<SolutionPointData>::iterator iter = begin; iter != end; iter++) {
+        for (std::vector<SolutionPointData>::iterator iterParticle = iter + 1; iterParticle != end; iterParticle++) {
+            hammingValuesAccumulated += hammingdistance(iter->bitVec, iterParticle->bitVec);
+            count++;
+        }
+    }
+    return hammingValuesAccumulated / (double) count;
 }
 
 boost::multiprecision::cpp_int RunData::binominal(const int n,int k)
@@ -235,7 +280,7 @@ boost::multiprecision::cpp_int RunData::positionInPermutation(std::vector<bool>:
     int amountOfBits = end - begin;
     //recursion base
     if (amountOfSetBits == 0) return 1;
-    std::vector<bool>::iterator indexIter = std::find(begin, end, true);
+    std::vector<bool>::iterator indexIter = std::find(begin, end, true);//TODO:false könnte andersrum sortieren!
     int index = indexIter - begin;
     int amountOfBitsAfterIndex = amountOfBits - 1 - index;
     //recursion base
@@ -249,3 +294,29 @@ boost::multiprecision::cpp_int RunData::positionInPermutation(std::vector<bool>:
 }
 
 
+GraphDataPoint::GraphDataPoint(double x, double y, SolutionPointData* orginalPoint) : x(x), y(y), orginalPoint(orginalPoint)
+{
+
+}
+
+GraphDataPoint::GraphDataPoint(double x, double y, QColor color, SolutionPointData* orginalPoint)
+    : x(x), y(y), orginalPoint(orginalPoint), color(color)
+{
+}
+
+bool GraphDataPoint::existLink()
+{
+    return orginalPoint != nullptr;
+}
+
+QPointF GraphDataPoint::toQPointF() const
+{
+    return QPointF(x, y);
+}
+
+double GraphDataPoint::squaredDistance(QPointF& graphPoint) const
+{
+    return std::pow(graphPoint.x() - x, 2) + std::pow(graphPoint.y() - y, 2);
+}
+
+

+ 19 - 5
metavis/RunData.h

@@ -6,24 +6,34 @@
 #include <map>
 #include <boost/multiprecision/cpp_int.hpp>
 #include <boost/multiprecision/cpp_dec_float.hpp>
-#include "GraphView.h"
 
 
 
-
-struct GraphDataPoint;
 struct SolutionPointData {
 	int iteration;
 	double objectiveFunction;
 	int particleNumber;
 	std::vector<bool> bitVec;
 };
+struct GraphDataPoint {
+	double x;
+	double y;
+	SolutionPointData* orginalPoint;
+	QColor color;
+	GraphDataPoint() :x(0), y(0), orginalPoint(nullptr) {}
+	GraphDataPoint(double x, double y, SolutionPointData* orginalPoint = nullptr);
+	GraphDataPoint(double x, double y, QColor color, SolutionPointData* orginalPoint = nullptr);
+	bool existLink();
+	QPointF toQPointF() const;
+	double squaredDistance(QPointF& graphPoint) const;
+};
 
 class RunData
 {
 public:
 	std::string name;
 	std::string information;
+	std::string filePath;
 	std::vector<SolutionPointData> solutionVec;
 	RunData();
 	RunData(std::string filePath);
@@ -34,7 +44,8 @@ public:
 	std::vector<GraphDataPoint> minSolutionPerItertion;
 	std::vector<GraphDataPoint> maxSolutionPerItertion;
 	std::vector<GraphDataPoint> dotsForDistribution;
-	std::vector<GraphDataPoint> testForBitField;
+	std::vector<GraphDataPoint> dotsForBitField;
+	std::vector<GraphDataPoint> meanHammingDistancePerIteration;
 	std::map<int, std::vector<GraphDataPoint>> particleMap;
 private:
 	bool badFileFlag = false;
@@ -45,7 +56,10 @@ private:
 	void calculateBestAndAverageIter();
 	void calculateParticleSolution();
 	void calculateDotsForDistribution();
-	void generateRandomBitFieldData();
+	void calculateBitFieldData();
+	void calculateMeanHammingDistance();
+	int static hammingdistance(std::vector<bool>& bitVecA, std::vector<bool>& bitVecB);
+	double static meanHammingDistance(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end);
 
 	boost::multiprecision::cpp_int binominal(const int n, const int k);
 	boost::multiprecision::cpp_int positionInPermutation(std::vector<bool>::iterator begin, std::vector<bool>::iterator end, int amountOfSetBits);

+ 137 - 35
metavis/metavis.cpp

@@ -14,9 +14,19 @@
 #include <map>
 #include <boost/multiprecision/cpp_int.hpp>
 #include <QDesktopWidget>
+#include <QSlider>
+#include <QSizePolicy>
+#include "DockableGraphView.h"
+#include "ProjectManager.h"
+#include "RangeSlider.h"
+#include "tsneIteractive.h"
+#include "util.h"
+
+
 
 metavis::metavis(QWidget* parent)
-	: QMainWindow(parent), bestGraph(createCustomWidget("Best vs Avg")), particleGraph(createCustomWidget("Particle")), minMaxGraph(createCustomWidget("MinMaxGraph"))
+	: QMainWindow(parent), actualBestAverageGraph(createCustomWidget("Best vs Avg")), actualParticleGraph(createCustomWidget("Particle", true)), actualMinMaxGraph(createCustomWidget("MinMaxGraph", true)), actualMeanHmmingDistanceGraph(createCustomWidget("MeanHammingDistance", true)),
+	multiBestGraph(createCustomWidget("All Runs: Best")), multiAvgGraph(createCustomWidget("All Runs: Avg", true)), multiMinGraph(createCustomWidget("All Runs: Min", true)), multiMaxGraph(createCustomWidget("All Runs: Max", true)), multiMeanHammingDistanceGraph(createCustomWidget("All Runs: Mean Hamming Distance", true))
 {
 	ui.setupUi(this);
 	/* create settings object*/
@@ -26,12 +36,9 @@ metavis::metavis(QWidget* parent)
 	setStyleSheet(styleSheet() + "QTabWidget::pane {border-top: 0px solid #C2C7CB;margin: -9px -9px -13px -9px;}");
 
 	readMainWindowSettings();
-	
-
-
-	qDebug() << "Test Boost";
-	boost::multiprecision::cpp_int bla = 1;
 	createBitField();
+	createProjectManager();
+	createTSNE();
 } 
 
 
@@ -49,32 +56,60 @@ metavis::~metavis()
 }
 
 
-GraphView* metavis::createCustomWidget(QString titleString)
+GraphView* metavis::createCustomWidget(QString titleString, bool tabToLast)
 {
-	QDockWidget* dock = new QDockWidget(titleString, this);
-	dock->setObjectName(titleString);
-	dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
-	GraphView* graph = new GraphView(dock, titleString, Bound(10, 20, 40.0, 50.0));
-	graph->setUseFixedBound(true);
-	graph->setMinimumSize(200, 200);
-	graph->show();
-	graph->repaint();
-	dock->setWidget(graph);
-	addDockWidget(Qt::LeftDockWidgetArea, dock);
-	return graph;
+	DockableGraphView* dock = new DockableGraphView(this, titleString);
+	qDebug() << titleString;
+	if (tabToLast && lastDocked) {
+		tabifyDockWidget(lastDocked, dock);
+	}
+	else {
+		addDockWidget(Qt::LeftDockWidgetArea, dock);
+		lastDocked = dock;
+	}
+	return dock->view;
 }
 
 void metavis::createBitField()
 {
-	bitField = new GraphView(this, "BitField", Bound(0.0, 1.0, 0.0, 1600.0));
+	bitField = new GraphView(this, Bound(-0.1, 1.1, 0.0, 1601.0));
 	bitField->setUseFixedBound(true);
 	bitField->setMinimumSize(200, 200);
+	bitField->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+	bitField->maxIter = -2;
+	bitField->useInterval = true;
 	//this->setCentralWidget(bitField);
-	QHBoxLayout* layout = new QHBoxLayout;
+	QVBoxLayout* layout = new QVBoxLayout;
 	layout->addWidget(bitField);
+	RangeSlider* rangeSlider = new RangeSlider(this);
+	rangeSlider->setMinimumSize(100, 50);
+	rangeSlider->setMinRange(-2);
+	rangeSlider->setMaxRange(100);
+	connect(rangeSlider, &RangeSlider::maxChanged,
+		bitField, &GraphView::setMaxIter);
+	connect(rangeSlider, &RangeSlider::minChanged,
+		bitField, &GraphView::setMinIter);
+	rangeSlider->setMinSliderValue(-2);
+	rangeSlider->setMaxSliderValue(100);
+	layout->addWidget(rangeSlider);
 	ui.BitFieldWidget->setLayout(layout);
 }
 
+void metavis::createProjectManager()
+{
+	QDockWidget* dockWidget = new QDockWidget(this);
+	ProjectManager* manager = new ProjectManager(this, this);
+	dockWidget->setWindowTitle("Project Manager");
+	dockWidget->setWidget(manager);
+	addDockWidget(Qt::RightDockWidgetArea, dockWidget);
+}
+
+void metavis::createTSNE()
+{
+	tsneIteractive* window = new tsneIteractive(ui.tabWidget);
+	ui.tabWidget->addTab(window, "tSNE");
+}
+
 void metavis::writeActualMainWindowSettings()
 {
 	settings->beginGroup("MainWindow");
@@ -103,6 +138,34 @@ void metavis::readMainWindowSettings()
 }
 
 
+QColor metavis::interpolate(QColor& first, QColor& second, double alpha)
+{
+	double firstH, firstS, firstL;
+	double secondH, secondS, secondL;
+
+	first.getHslF(&firstH, &firstS, &firstL);
+	second.getHslF(&secondH, &secondS, &secondL);
+
+	const double h = util::linearInterpolate(firstH, secondH, alpha);
+	const double s = util::linearInterpolate(firstS, secondS, alpha);
+	const double l = util::linearInterpolate(firstL, secondL, alpha);
+
+	return QColor::fromHslF(h,s,l);
+}
+
+
+void metavis::updateBitFieldColors()
+{
+	QColor best(64, 186, 37);
+	QColor worst(255, 19, 71);
+	double bestValue = 32;
+	double worstValue = 46;
+	for (GraphDataPoint& data : runList.back().dotsForBitField) {
+		data.color = interpolate(best, worst, std::clamp(util::inverseLinearInterpolation(bestValue, worstValue, data.orginalPoint->objectiveFunction), 0.0, 1.0));
+	}
+}
+
+
 
 
 
@@ -110,23 +173,62 @@ void metavis::readMainWindowSettings()
 void metavis::openFile()
 {
 	qDebug() << "openFile";
-	QString fileName = QFileDialog::getOpenFileName(this, tr("Open LogFile"), QStandardPaths::displayName(QStandardPaths::DesktopLocation), tr("Metavis Logfile (*.metalog)"));
-	if (fileName == ("")) {
+	QStringList pathList = QFileDialog::getOpenFileNames(this, "Open LogFile", QStandardPaths::displayName(QStandardPaths::DesktopLocation), "Metavis Logfile (*.metalog)");
+	if (pathList.isEmpty()) {
 		qDebug() << "No file selected";
 		return;
 	}
-	qDebug() << "file:" << fileName;
-	runVec.push_back(RunData(fileName.toStdString()));
-	bestGraph->addLine(runVec[0].bestSolutionPerIteration, QColor(255, 0, 0));
-	bestGraph->addLine(runVec[0].averageSolutionPerItertion, QColor(0, 0, 255));
-
-	
-	minMaxGraph->addLine(runVec[0].minSolutionPerItertion, QColor(255, 0, 0));
-	minMaxGraph->addLine(runVec[0].maxSolutionPerItertion, QColor(0, 0, 255));
-	minMaxGraph->addDots(runVec[0].dotsForDistribution, QColor(255, 165, 0, 100));
-	for (auto iter = runVec[0].particleMap.begin(); iter != runVec[0].particleMap.end(); iter++) {
-		particleGraph->addLine(iter->second);
+	//int oldIndex = runVec.size();
+	int size = runList.size();
+	for (int i = 0; i < pathList.size(); i++) {
+		qDebug() << "file:" << pathList[i];
+		runList.push_back(RunData(pathList[i].toStdString()));
+	}
+	for (std::list<RunData>::iterator iter = std::next(runList.begin(), size); iter != runList.end(); iter++) {
+		QColor runColor = multiBestGraph->generateNextColorForGraph();
+		multiBestGraph->addLine(&iter->bestSolutionPerIteration, &*iter, runColor);
+		multiAvgGraph->addLine(&iter->averageSolutionPerItertion, &*iter, runColor);
+		multiMaxGraph->addLine(&iter->maxSolutionPerItertion, &*iter, runColor);
+		multiMinGraph->addLine(&iter->minSolutionPerItertion, &*iter, runColor);
+		multiMeanHammingDistanceGraph->addLine(&iter->meanHammingDistancePerIteration, &*iter, runColor);
 	}
-	//Test
-	bitField->addDots(runVec[0].testForBitField, QColor(255, 165, 0, 100));
+
+
+	/*
+	for (int i = 0; i < runList.size(); i++) {
+		QColor runColor = multiBestGraph->generateNextColorForGraph();
+		multiBestGraph->addLine(&runList[i].bestSolutionPerIteration, &runList[i], runColor);
+		multiAvgGraph->addLine(&runList[i].averageSolutionPerItertion, &runList[i], runColor);
+		multiMaxGraph->addLine(&runList[i].maxSolutionPerItertion, &runList[i], runColor);
+		multiMinGraph->addLine(&runList[i].minSolutionPerItertion, &runList[i], runColor);
+		//multiMeanHammingDistanceGraph->addLine(&runVec[i].meanHammingDistancePerIteration, &runVec[i], runColor);
+	}*/
+	/*std::vector<RunData>::iterator end = runVec.end();
+	for (int i = 0; i < pathList.size(); i++) {
+		qDebug() << "file:" << pathList[i];
+		runVec.push_back(RunData(pathList[i].toStdString()));
+	}
+	for (std::vector<RunData>::iterator iter = end; iter != runVec.end(); iter++) {
+		QColor runColor = multiBestGraph->generateNextColorForGraph();
+		multiBestGraph->addLine(&iter->bestSolutionPerIteration, &*iter, runColor);
+		multiAvgGraph->addLine(&iter->averageSolutionPerItertion, &*iter, runColor);
+		multiMaxGraph->addLine(&iter->maxSolutionPerItertion, &*iter, runColor);
+		multiMinGraph->addLine(&iter->minSolutionPerItertion, &*iter, runColor);
+		multiMeanHammingDistanceGraph->addLine(&iter->meanHammingDistancePerIteration, &*iter, runColor);
+	}*/
+	//actualBestAverageGraph->addLine(runVec[0].bestSolutionPerIteration, QColor(255, 0, 0));
+	//actualBestAverageGraph->addLine(runVec[0].averageSolutionPerItertion, QColor(0, 0, 255));
+	//actualMinMaxGraph->addLine(runVec[0].minSolutionPerItertion, QColor(255, 0, 0));
+	//actualMinMaxGraph->addLine(runVec[0].maxSolutionPerItertion, QColor(0, 0, 255));
+	//actualMinMaxGraph->addDots(runVec[0].dotsForDistribution, QColor(255, 165, 0, 100));
+	//for (auto iter = runVec[0].particleMap.begin(); iter != runVec[0].particleMap.end(); iter++) {
+	//	actualParticleGraph->addLine(iter->second);
+	//}
+	////Test
+	bitField->addDots(&runList.back().dotsForBitField, &runList.front(), QColor(255, 165, 0, 100));
+	updateBitFieldColors();
+	bitField->graphSeriesVec.back().useDataPointColor = true;
+	//actualMeanHmmingDistanceGraph->addLine(runVec[0].meanHammingDistancePerIteration, QColor(255, 0, 0));
+
+
 }

+ 25 - 7
metavis/metavis.h

@@ -16,26 +16,44 @@ class metavis : public QMainWindow
 public:
 	metavis(QWidget *parent = Q_NULLPTR);
 	~metavis();
-	GraphView* bestGraph;
-	GraphView* particleGraph;
-	GraphView* minMaxGraph;
+	GraphView* actualBestAverageGraph;
+	GraphView* actualParticleGraph;
+	GraphView* actualMinMaxGraph;
+	GraphView* actualMeanHmmingDistanceGraph;
+
+	GraphView* multiBestGraph;
+	GraphView* multiAvgGraph;
+	GraphView* multiMinGraph;
+	GraphView* multiMaxGraph;
+	GraphView* multiMeanHammingDistanceGraph;
+	
+
+
 	GraphView* bitField;
 private:
 	Ui::metavisClass ui;
 	QSettings* settings;
-	std::vector<RunData> runVec;
+	std::list<RunData> runList;
 
+	//To Tab all 
+	QDockWidget* lastDocked;
 
 private:
 	/* Widget functions */
-	GraphView* createCustomWidget(QString titleString);
+	GraphView* createCustomWidget(QString titleString, bool tabToLast = false);
 	void createBitField();
-
-
+	void createProjectManager();
+	void createTSNE();
 	/* Setting functions*/
 	void writeActualMainWindowSettings();
 	void readMainWindowSettings();
 
+	//Bitfield color interpolation
+	static QColor interpolate(QColor& first, QColor& second, double alpha);
+	void updateBitFieldColors();
+
+
+
 public slots:
 	/**
 	 * Opens the settingWindows Dialog.

+ 7 - 2
metavis/metavis.qrc

@@ -1,4 +1,9 @@
 <RCC>
-    <qresource prefix="metavis">
-    </qresource>
+  <qresource prefix="metavis">
+    <file>Resources/close_big_red.svg</file>
+    <file>Resources/settingIcon_hovered.svg</file>
+    <file>Resources/close.svg</file>
+    <file>Resources/settingIcon.svg</file>
+    <file>Resources/assignButton.svg</file>
+  </qresource>
 </RCC>

+ 16 - 1
metavis/metavis.ui

@@ -33,7 +33,10 @@
     <item>
      <widget class="QTabWidget" name="tabWidget">
       <property name="styleSheet">
-       <string notr="true"> margin: -13px -9px -13px -9px;</string>
+       <string notr="true"/>
+      </property>
+      <property name="currentIndex">
+       <number>0</number>
       </property>
       <widget class="QWidget" name="BitFieldWidget">
        <attribute name="title">
@@ -69,8 +72,15 @@
      <string>View</string>
     </property>
    </widget>
+   <widget class="QMenu" name="menuProject">
+    <property name="title">
+     <string>Project</string>
+    </property>
+    <addaction name="ActionAddMetalogFile"/>
+   </widget>
    <addaction name="menuFile"/>
    <addaction name="menuView"/>
+   <addaction name="menuProject"/>
   </widget>
   <widget class="QToolBar" name="mainToolBar">
    <attribute name="toolBarArea">
@@ -96,6 +106,11 @@
     <string>Ctrl+P</string>
    </property>
   </action>
+  <action name="ActionAddMetalogFile">
+   <property name="text">
+    <string>Add .metalog-File..</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <resources>

+ 27 - 4
metavis/metavis.vcxproj

@@ -30,11 +30,11 @@
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <OutDir>$(SolutionDir)Build\$(Platform)\$(Configuration)\</OutDir>
-    <IntDir>$(SolutionDir)Intermediate\$(Platform)\$(Configuration)\</IntDir>
+    <IntDir>$(SolutionDir)Intermediate\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <OutDir>$(SolutionDir)Build\$(Platform)\$(Configuration)\</OutDir>
-    <IntDir>$(SolutionDir)Intermediate\$(Platform)\$(Configuration)\</IntDir>
+    <IntDir>$(SolutionDir)Intermediate\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
   </PropertyGroup>
   <Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')">
     <Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." />
@@ -71,7 +71,8 @@
       <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
       <PrecompiledHeader>Use</PrecompiledHeader>
       <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
-      <AdditionalIncludeDirectories>C:\Program Files %28x86%29\boost\boost_1_72_0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>C:\Program Files (x86)\boost\boost_1_72_0;.\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <LanguageStandard>stdcpp17</LanguageStandard>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -89,7 +90,8 @@
       <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
       <PrecompiledHeader>Use</PrecompiledHeader>
       <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
-      <AdditionalIncludeDirectories>C:\Program Files %28x86%29\boost\boost_1_72_0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>C:\Program Files (x86)\boost\boost_1_72_0;.\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <LanguageStandard>stdcpp17</LanguageStandard>
     </ClCompile>
     <Link>
       <SubSystem>Windows</SubSystem>
@@ -100,22 +102,34 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="DockableGraphView.cpp" />
     <ClCompile Include="GraphView.cpp" />
+    <ClCompile Include="GraphViewSettingDialog.cpp" />
+    <ClCompile Include="GraphViewSettingItem.cpp" />
     <ClCompile Include="main.cpp" />
+    <ClCompile Include="MetalogManagerItem.cpp" />
     <ClCompile Include="metavis.cpp" />
     <ClCompile Include="pch.cpp">
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
       <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
     </ClCompile>
+    <ClCompile Include="Project.cpp" />
+    <ClCompile Include="ProjectManager.cpp" />
+    <ClCompile Include="RangeSlider.cpp" />
     <ClCompile Include="RunData.cpp" />
     <ClCompile Include="SettingDialog.cpp" />
+    <ClCompile Include="tsneIteractive.cpp" />
   </ItemGroup>
   <ItemGroup>
     <QtMoc Include="metavis.h" />
   </ItemGroup>
   <ItemGroup>
+    <QtUic Include="GraphViewSettingDialog.ui" />
+    <QtUic Include="GraphViewSettingItem.ui" />
+    <QtUic Include="MetalogManagerItem.ui" />
     <QtUic Include="metavis.ui" />
     <QtUic Include="SettingDialog.ui" />
+    <QtUic Include="tsneIteractive.ui" />
   </ItemGroup>
   <ItemGroup>
     <QtRcc Include="metavis.qrc" />
@@ -127,7 +141,16 @@
     <QtMoc Include="SettingDialog.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="Project.h" />
+    <QtMoc Include="RangeSlider.h" />
+    <QtMoc Include="tsneIteractive.h" />
+    <ClInclude Include="util.h" />
+    <QtMoc Include="DockableGraphView.h" />
+    <QtMoc Include="GraphViewSettingDialog.h" />
+    <QtMoc Include="GraphViewSettingItem.h" />
+    <QtMoc Include="MetalogManagerItem.h" />
     <ClInclude Include="pch.h" />
+    <QtMoc Include="ProjectManager.h" />
     <ClInclude Include="RunData.h" />
     <QtMoc Include="GraphView.h" />
   </ItemGroup>

+ 112 - 13
metavis/metavis.vcxproj.filters

@@ -23,35 +23,116 @@
       <Extensions>qrc;*</Extensions>
       <ParseFiles>false</ParseFiles>
     </Filter>
+    <Filter Include="Source Files\Data">
+      <UniqueIdentifier>{53513f5d-6839-4eae-849d-e19c5c42de66}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\UserInterface">
+      <UniqueIdentifier>{9ee1379c-2518-4abe-ba18-3acca2489089}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\UserInterface\GraphView">
+      <UniqueIdentifier>{690b435e-6eee-460a-b8b0-3b7041e1cd72}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\UserInterface\ProjectManager">
+      <UniqueIdentifier>{d1f02e10-3301-4d1d-95ff-8ce35a9eba2d}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\UserInterface\MainMenu">
+      <UniqueIdentifier>{6720c6d8-7b65-4f16-abd1-aade3b8799f8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\Data">
+      <UniqueIdentifier>{538f690b-294c-49b3-95d1-92948c60f6f8}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\UserInterface">
+      <UniqueIdentifier>{349cc4e0-be79-4c7a-8412-89a28d0ae77c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\UserInterface\ProjectManager">
+      <UniqueIdentifier>{e95bcea7-9a19-4b96-bb3c-8311955305d9}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\UserInterface\GraphView">
+      <UniqueIdentifier>{db20d7fd-184e-4d06-a522-5140379fd6d6}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\UserInterface\MainMenu">
+      <UniqueIdentifier>{85af9a93-1361-42e0-85cf-79b1d962850c}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Header Files\UserInterface\Widget">
+      <UniqueIdentifier>{c2f8a3bf-19b7-4cb4-9f0e-134dc4aaee70}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Source Files\UserInterface\Widget">
+      <UniqueIdentifier>{52a467e5-efc6-44c1-a9f6-ff9005d2b7dc}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="main.cpp">
-      <Filter>Source Files</Filter>
+    <ClCompile Include="GraphView.cpp">
+      <Filter>Source Files\UserInterface\GraphView</Filter>
+    </ClCompile>
+    <ClCompile Include="GraphViewSettingDialog.cpp">
+      <Filter>Source Files\UserInterface\GraphView</Filter>
+    </ClCompile>
+    <ClCompile Include="GraphViewSettingItem.cpp">
+      <Filter>Source Files\UserInterface\GraphView</Filter>
+    </ClCompile>
+    <ClCompile Include="DockableGraphView.cpp">
+      <Filter>Source Files\UserInterface\GraphView</Filter>
     </ClCompile>
     <ClCompile Include="metavis.cpp">
-      <Filter>Source Files</Filter>
+      <Filter>Source Files\UserInterface\MainMenu</Filter>
+    </ClCompile>
+    <ClCompile Include="RunData.cpp">
+      <Filter>Source Files\Data</Filter>
+    </ClCompile>
+    <ClCompile Include="MetalogManagerItem.cpp">
+      <Filter>Source Files\UserInterface\ProjectManager</Filter>
+    </ClCompile>
+    <ClCompile Include="ProjectManager.cpp">
+      <Filter>Source Files\UserInterface\ProjectManager</Filter>
     </ClCompile>
     <ClCompile Include="SettingDialog.cpp">
-      <Filter>Source Files</Filter>
+      <Filter>Source Files\UserInterface\MainMenu</Filter>
     </ClCompile>
-    <ClCompile Include="RunData.cpp">
+    <ClCompile Include="pch.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="GraphView.cpp">
+    <ClCompile Include="main.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
-    <ClCompile Include="pch.cpp">
-      <Filter>Source Files</Filter>
+    <ClCompile Include="Project.cpp">
+      <Filter>Source Files\Data</Filter>
+    </ClCompile>
+    <ClCompile Include="RangeSlider.cpp">
+      <Filter>Source Files\UserInterface\Widget</Filter>
+    </ClCompile>
+    <ClCompile Include="tsneIteractive.cpp">
+      <Filter>Source Files\UserInterface\MainMenu</Filter>
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
+    <QtMoc Include="DockableGraphView.h">
+      <Filter>Header Files\UserInterface\GraphView</Filter>
+    </QtMoc>
+    <QtMoc Include="GraphView.h">
+      <Filter>Header Files\UserInterface\GraphView</Filter>
+    </QtMoc>
+    <QtMoc Include="GraphViewSettingDialog.h">
+      <Filter>Header Files\UserInterface\GraphView</Filter>
+    </QtMoc>
+    <QtMoc Include="GraphViewSettingItem.h">
+      <Filter>Header Files\UserInterface\GraphView</Filter>
+    </QtMoc>
+    <QtMoc Include="MetalogManagerItem.h">
+      <Filter>Header Files\UserInterface\ProjectManager</Filter>
+    </QtMoc>
     <QtMoc Include="metavis.h">
-      <Filter>Header Files</Filter>
+      <Filter>Header Files\UserInterface\MainMenu</Filter>
+    </QtMoc>
+    <QtMoc Include="ProjectManager.h">
+      <Filter>Header Files\UserInterface\ProjectManager</Filter>
     </QtMoc>
     <QtMoc Include="SettingDialog.h">
-      <Filter>Header Files</Filter>
+      <Filter>Header Files\UserInterface\MainMenu</Filter>
     </QtMoc>
-    <QtMoc Include="GraphView.h">
+    <QtMoc Include="RangeSlider.h">
+      <Filter>Header Files\UserInterface\Widget</Filter>
+    </QtMoc>
+    <QtMoc Include="tsneIteractive.h">
       <Filter>Header Files</Filter>
     </QtMoc>
   </ItemGroup>
@@ -62,6 +143,18 @@
     <QtUic Include="SettingDialog.ui">
       <Filter>Form Files</Filter>
     </QtUic>
+    <QtUic Include="GraphViewSettingDialog.ui">
+      <Filter>Form Files</Filter>
+    </QtUic>
+    <QtUic Include="GraphViewSettingItem.ui">
+      <Filter>Form Files</Filter>
+    </QtUic>
+    <QtUic Include="MetalogManagerItem.ui">
+      <Filter>Form Files</Filter>
+    </QtUic>
+    <QtUic Include="tsneIteractive.ui">
+      <Filter>Form Files</Filter>
+    </QtUic>
   </ItemGroup>
   <ItemGroup>
     <QtRcc Include="metavis.qrc">
@@ -72,11 +165,17 @@
     <ResourceCompile Include="metavis.rc" />
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="RunData.h">
+    <ClInclude Include="pch.h">
       <Filter>Header Files</Filter>
     </ClInclude>
-    <ClInclude Include="pch.h">
+    <ClInclude Include="util.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="RunData.h">
+      <Filter>Header Files\Data</Filter>
+    </ClInclude>
+    <ClInclude Include="Project.h">
+      <Filter>Header Files\Data</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

+ 2 - 0
metavis/pch.h

@@ -22,8 +22,10 @@
 #include <QString>
 #include <QToolTip>
 #include <QTimer>
+#include <QSlider>
 
 //Std
+#include <list>
 #include <vector>
 #include <string>
 #include <fstream>

+ 13 - 0
metavis/tsneIteractive.cpp

@@ -0,0 +1,13 @@
+#include "pch.h"
+#include "tsneIteractive.h"
+
+tsneIteractive::tsneIteractive(QWidget *parent)
+	: QWidget(parent), view(new GraphView(this))
+{
+	ui.setupUi(this);
+	ui.verticalLayout->insertWidget(0, view);
+}
+
+tsneIteractive::~tsneIteractive()
+{
+}

+ 18 - 0
metavis/tsneIteractive.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include <QWidget>
+#include "ui_tsneIteractive.h"
+#include "GraphView.h"
+
+class tsneIteractive : public QWidget
+{
+	Q_OBJECT
+
+public:
+	tsneIteractive(QWidget *parent = Q_NULLPTR);
+	~tsneIteractive();
+	GraphView* view;
+
+private:
+	Ui::tsneIteractive ui;
+};

+ 133 - 0
metavis/tsneIteractive.ui

@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>tsneIteractive</class>
+ <widget class="QWidget" name="tsneIteractive">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>664</width>
+    <height>459</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>tsneIteractive</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <property name="sizeConstraint">
+      <enum>QLayout::SetFixedSize</enum>
+     </property>
+     <item row="2" column="2" colspan="3">
+      <widget class="QSlider" name="learnrateSlider">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="tickPosition">
+        <enum>QSlider::TicksBelow</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLabel" name="learnRateLabel">
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>20</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Learnrate</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLabel" name="perplexityLabel">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>20</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Perplexity</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="2" colspan="3">
+      <widget class="QSlider" name="perplexitySlider">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="tickPosition">
+        <enum>QSlider::TicksBelow</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="0">
+      <widget class="QPushButton" name="startButton">
+       <property name="minimumSize">
+        <size>
+         <width>35</width>
+         <height>30</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Start</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QPushButton" name="pauseButton">
+       <property name="minimumSize">
+        <size>
+         <width>35</width>
+         <height>30</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Pause</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="3">
+      <widget class="QProgressBar" name="progressBar">
+       <property name="value">
+        <number>24</number>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+       <property name="format">
+        <string>%p%</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="QLabel" name="iterLabel">
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>20</height>
+        </size>
+       </property>
+       <property name="text">
+        <string>Iteration: 0</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>

+ 10 - 0
metavis/util.h

@@ -0,0 +1,10 @@
+#pragma once
+namespace util {
+	inline double linearInterpolate(double first, double second, double alpha) {
+		return first * (1.0 - alpha) + second * alpha;
+	}
+	inline double inverseLinearInterpolation(double min, double max, double value) {
+		if (max - min == 0) return max;
+		else return (value - min) / (max - min);
+	}
+}

+ 75 - 0
metaviscon/RunDataLite.cpp

@@ -0,0 +1,75 @@
+#include "RunDataLite.h"
+#include <regex>
+#include <iostream>
+
+RunDataLite::RunDataLite(std::string filePath): fileStream(filePath), filePath(filePath)
+{
+    std::string file = filePath.substr(filePath.find_last_of("/\\") + 1);
+    name = file.substr(0, file.rfind("."));
+    if (!fileStream.is_open())
+    {
+        //Cant open file
+        badFileFlag = true;
+        std::cout << "Cannot open File.";
+        return;
+    }
+    /* Start extracting */
+    while (fileStream.peek() != EOF) {
+        std::string buffer;
+        getLine(buffer);
+        SolutionPointData sol;
+
+        std::regex regexIter("i:([\\+\\-]?\\d+)");
+        std::regex regexObjectiveFunction("of:([\\+\\-]?\\d+\\.\\d+(?:E[\\+\\-]\\d+)?)");
+        std::smatch match;
+        if (!std::regex_search(buffer, match, regexIter)) {
+           std::cout << "Bad formatatted Line[" << actualLine << "].";
+           std::cout << "Failed to matched:";
+           std::cout << buffer;
+            return;
+        }
+        sol.iteration = std::stoi(match[1]);
+        if (!std::regex_search(buffer, match, regexObjectiveFunction)) {
+           std::cout << "Bad formatatted Line[" << actualLine << "].";
+           std::cout << "Failed to matched:";
+           std::cout << buffer;
+            return;
+        }
+        sol.objectiveFunction = std::stod(match[1]);
+        std::regex regexParticleNumber("pN:([\\+\\-]?\\d+)");
+        if (!std::regex_search(buffer, match, regexParticleNumber)) {
+           std::cout << "Bad formatatted Line[" << actualLine << "].";
+           std::cout << "Failed to matched:";
+           std::cout << buffer;
+            return;
+        }
+        sol.particleNumber = std::stoi(match[1]);
+        std::regex regexBitVec("b:([01]+)");
+        if (!std::regex_search(buffer, match, regexBitVec)) {
+           std::cout << "Bad formatatted Line[" << actualLine << "].";
+           std::cout << "Failed to matched:";
+           std::cout << buffer;
+            return;
+        }
+        sol.bitVec.resize(match[1].length());
+        int count = 0;
+        std::string str = match[1];
+        for (std::string::size_type i = 0; i < str.size(); ++i) {
+            sol.bitVec[i] = (str[i] == '1');
+        }
+        solutionVec.push_back(sol);
+    }
+    fileStream.close();
+}
+
+bool RunDataLite::badFile()
+{
+    return badFileFlag;
+}
+
+void RunDataLite::getLine(std::string& bufferString)
+{
+    std::getline(fileStream, bufferString);
+    actualLine++;
+}
+

+ 29 - 0
metaviscon/RunDataLite.h

@@ -0,0 +1,29 @@
+#pragma once
+#include <vector>
+#include <string>
+#include <fstream>
+
+struct SolutionPointData {
+	int iteration;
+	double objectiveFunction;
+	int particleNumber;
+	std::vector<bool> bitVec;
+};
+
+
+class RunDataLite
+{
+public:
+	std::string name;
+	std::string information;
+	std::string filePath;
+	std::vector<SolutionPointData> solutionVec;
+	RunDataLite(std::string filePath);
+	bool badFile();
+private:
+	bool badFileFlag = false;
+	std::fstream fileStream;
+	int actualLine = 0;
+	void getLine(std::string& bufferString);
+};
+

BIN
metaviscon/data.dat


+ 41 - 0
metaviscon/generateMetalogFile.cpp

@@ -0,0 +1,41 @@
+#include <fstream>
+#include <string>
+#include <iostream>
+#include <random>
+
+
+
+
+
+int main18236() {
+	std::cout << "Hello World";
+	std::fstream writer("generated.metalog", std::ios::out);
+	const int numberOfParts = 5;
+	const int amountOfBits = 100;
+	const int pointsPerPart = 20;
+
+	//random
+	std::random_device rd;  //Will be used to obtain a seed for the random number engine
+	std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
+	std::uniform_real_distribution<double> doubleDistr(0.0, 1.0);
+
+
+	for (int i = 0; i < numberOfParts; i++) {
+		for (int pN = 0; pN < pointsPerPart; pN++) {
+			writer << "of:" << std::to_string(10.0 * (i + 1)) << " i:" << i << " pN:" << pN << " b:";
+			int part = amountOfBits / numberOfParts;
+			for (int index = 0; index < amountOfBits; index++) {
+				if (i * part < index && index <= (i+1)*part) {
+					writer << (doubleDistr(gen) < 0.95 ?"1":"0");
+				}
+				else {
+					writer << "0";
+				}
+			}
+			writer << std::endl;
+
+		}
+	}
+	writer.close();
+	return 0;
+}

+ 259 - 15
metaviscon/metaviscon.cpp

@@ -1,35 +1,279 @@
 
 
 #include <iostream>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <random>
 
-int binominalIndex(const int n, const int k);
 
 
-int main(int argc, char * argv[] )
+struct r2 {
+	double x = 0, y = 0;
+};
+
+
+
+
+std::string convertToStdString(const std::vector<bool>& vec);
+double euclidenDistance(const std::vector<bool>& vecA, const std::vector<bool>& vecB, int amountIfBits);
+double neighbourProbability(const std::vector<std::vector<bool>>& solutions, const int j, const int i, int amountOfBits, double omega);
+double euclidenDistance(const r2& y1, const r2& y2);
+double calculate_tDistributed_q(const std::vector<r2>& solutions, int j, int i);
+double calculate_thing(const r2& y1, r2 y2);
+double cost(const std::vector<double>& p, std::vector<double>& q, size_t amountOfSolutions);
+r2 computeGradient(int i, const std::vector<double>& q, const std::vector<double>& p, const std::vector<r2>& ySolutions, int amountOfSolutions);
+void updateQ(const size_t& amountOfSolutions, std::vector<double>& matrixQ, std::vector<r2>& ySoltuion);
+int main23(int argc, char * argv[] )
 {
-	if(argc > 1)
-    std::cout << "Hello World! " << argv[1] << std::endl;
-	std::cout << "PascalTriangle" << std::endl;
-	int memory[45];
-	for (int line = 0; line < 9; line++) {
-		for (int number = 0; number < line + 1; number++) {
-			if (number == 0 || number == line) {
-				memory[binominalIndex(line,number)] = 1;
+	if (argc > 1) {
+		std::cout << "Hello World! " << argv[1] << std::endl;
+	}
+	//Generate Solutions;
+	const int amountOfBits = 100;
+	size_t amountOfSolutions = 5;
+	const double omega = 5;
+	/*size_t amountOfSolutions = (size_t)std::pow(2, amountOfBits);
+	std::vector<std::vector<bool> > solutions(amountOfSolutions);
+	for (int i = 0; i < amountOfSolutions; i++) {
+		std::vector<bool> newVec(amountOfBits);
+		for (int k = 0; k < amountOfBits; k++) {
+			int maxHalf = (int)std::pow(2, k);
+			int max = maxHalf * 2;
+			newVec[k] = i % max < maxHalf;
+		}
+		solutions[i] = newVec;
+	}
+	auto rng = std::default_random_engine{};
+	std::shuffle(solutions.begin(), solutions.end(), rng);
+	solutions.erase(solutions.begin() + solutions.size() / 2);
+	amountOfSolutions /= 2;*/
+	
+	std::vector<std::vector<bool> > solutions(amountOfSolutions);
+	std::random_device rd;  //Will be used to obtain a seed for the random number engine
+	std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
+	std::uniform_real_distribution<double> doubleDistr(0.0, 1.0);
+	for (int i = 0; i < amountOfSolutions; i++) {
+		std::vector<bool> newVec(amountOfBits);
+		double rnd = doubleDistr(gen);
+		for (int k = 0; k < amountOfBits; k++) {
+			newVec[k] = doubleDistr(gen) < rnd;
+		}
+		solutions[i] = newVec;
+	}
+
+
+
+	for (int i = 0; i < amountOfSolutions; i++) {
+		std::cout << "[" << convertToStdString(solutions[i]) << "]" << std::endl;
+	}
+
+	//Normal
+	std::vector<double> matrixProbability(amountOfSolutions * amountOfSolutions);
+	for (size_t i = 0; i < amountOfSolutions; i++) {
+		for (size_t j = 0; j < amountOfSolutions; j++) {
+			if (j == i) {
+				matrixProbability[i * amountOfSolutions + j] = 0.0;
 			}
 			else {
-				memory[binominalIndex(line, number)] = memory[binominalIndex(line -1, number)] + memory[binominalIndex(line - 1, number - 1)];
+				matrixProbability[i * amountOfSolutions + j] = neighbourProbability(solutions, j, i, amountOfBits, omega);
 			}
-
 		}
 	}
-	for (int zeile = 0; zeile < 9; zeile++) {
-		for (int number = 0; number < zeile + 1; number++) {
-			std::cout << " " << memory[binominalIndex(zeile, number)];
+	std::vector<double> matrixP(amountOfSolutions  * amountOfSolutions);
+	double sum = 0.0;
+	//Symmettric
+	for (size_t x = 0; x < amountOfSolutions; x++) {
+		for (size_t y = 0; y < amountOfSolutions; y++) {
+			double value = (matrixProbability[x * amountOfSolutions + y] + matrixProbability[y * amountOfSolutions + x]) / 2.0;
+			matrixP[x * amountOfSolutions + y] = value;
+			sum += value;
+		}
+	}
+	//Normalized
+	for (size_t x = 0; x < amountOfSolutions; x++) {
+		for (size_t y = 0; y < amountOfSolutions; y++) {
+			matrixP[x * amountOfSolutions + y] = matrixP[x * amountOfSolutions + y] / sum;
+		}
+	}
 
+	//sample random solution
+	std::vector<r2> ySolutions(amountOfSolutions);
+	std::uniform_real_distribution<double> zeroDistr(-1.0, 1.0);
+	std::for_each(ySolutions.begin(), ySolutions.end(), [&gen, &zeroDistr](r2& r) {r.x = zeroDistr(gen);r.y = zeroDistr(gen);});
+	for (const r2& r : ySolutions) {
+		std::cout << r.x << ", " << r.y  << std::endl;
+	}
+
+	//generate q vec
+	std::vector<double> matrixQ(amountOfSolutions * amountOfSolutions);
+	updateQ(amountOfSolutions, matrixQ, ySolutions);
+	
+
+
+	/*std::cout << "MatrixP:" << std::endl;
+	for (size_t x = 0; x < amountOfSolutions; x++) {
+		for (size_t y = 0; y < amountOfSolutions; y++) {
+			std::cout << matrixP[x * amountOfSolutions + y] << " ";
+		}
+		std::cout << std::endl;
+	}
+	std::cout << "MatrixQ:" << std::endl;
+	for (size_t x = 0; x < amountOfSolutions; x++) {
+		for (size_t y = 0; y < amountOfSolutions; y++) {
+			std::cout << matrixQ[x * amountOfSolutions + y] << " ";
 		}
 		std::cout << std::endl;
 	}
+	std::cout << "Cost: " << cost(matrixP, matrixQ, amountOfSolutions) << std::endl;*/
+
+	//update
+	int iterations = 100;
+	double learningRate = 10;
+
+
+	std::vector<r2> update(ySolutions);
+
+
+	for (int iter = 0; iter < iterations; iter++) {
+		//updateQ
+		updateQ(amountOfSolutions, matrixQ, ySolutions);
+		for (size_t i = 0; i < amountOfSolutions; i++) {
+			//computeGradient
+			const r2 gradientFromI= computeGradient(i, matrixQ, matrixP, ySolutions, amountOfSolutions);
+			r2& yi = ySolutions[i];
+			//update Y
+			r2& ui = update[i];
+			ui.x = learningRate * gradientFromI.x + 0.5 * ui.x;
+			ui.y = learningRate * gradientFromI.y + 0.5 * ui.y;
+
+			yi.x = yi.x + ui.x;
+			yi.y = yi.y + ui.y;
+		}
+		//
+		
+		//std::cout << "Cost: " << cost(matrixP, matrixQ, amountOfSolutions) << std::endl;
+		std::cout << "Iter: " << iter << std::endl;
+		for (const r2& r : ySolutions) {
+			std::cout << r.x << ", " << r.y << std::endl;
+		}
+		getchar();
+	}
+
+
+
+
+
+
 	return 0;
 }
 
+void updateQ(const size_t& amountOfSolutions, std::vector<double>& matrixQ, std::vector<r2>& ySoltuion)
+{
+	double sum = 0.0;
+	for (size_t i = 0; i < amountOfSolutions; i++) {
+		for (size_t j = 0; j < amountOfSolutions; j++) {
+			if (j == i) {
+				matrixQ[i * amountOfSolutions + j] = 0.0;
+			}
+			else {
+				double value = calculate_tDistributed_q(ySoltuion, j, i);
+				matrixQ[i * amountOfSolutions + j] = value;
+				sum += value;
+			}
+		}
+	}
+	for (size_t i = 0; i < matrixQ.size(); i++) matrixQ[i] /= sum;
+
+}
+
+
+std::string convertToStdString(const std::vector<bool>& vec) {
+	std::string result("");
+	int count = 0;
+	for (const bool& boolean : vec) {
+		result += boolean ? "1" : "0";
+		if (boolean) count++;
+	}
+	return result + ":" + std::to_string(count);
+}
+
+double euclidenDistance(const std::vector<bool>& vecA, const std::vector<bool>& vecB, int amountOfBits) {
+	int count = 0;
+	for (int i = 0; i < amountOfBits; i++) {
+		if (vecA[i] ^ vecB[i]) count++;
+	}
+	return std::sqrt(count);
+}
+
+double neighbourProbability(const std::vector<std::vector<bool>>& solutions,const int j,const int i, int amountOfBits, double omega) {
+	double numerator = std::exp(-euclidenDistance(solutions[i], solutions[j], amountOfBits)/(2 * omega * omega));
+	
+	double denominator = 0.0;
+	for (int k = 0; k < solutions.size(); k++) {
+		if (k == i) continue;
+		denominator += std::exp(-euclidenDistance(solutions[i], solutions[k], amountOfBits) / (2 * omega * omega));
+	}
+	return numerator/ denominator;
+}
+
+double euclidenDistance(const r2& y1, const r2& y2) {
+	return std::sqrt(std::pow(y1.x - y2.x, 2) + std::pow(y1.y - y2.y, 2));
+}
+
+double calculate_tDistributed_q(const std::vector<r2>& solutions, int j, int i) {
+	double numerator = calculate_thing(solutions[i], solutions[j]);
+	double denominator = 0.0;
+	for (int k = 0; k < solutions.size(); k++) {
+		if (k == i) continue;
+		denominator += calculate_thing(solutions[i], solutions[k]);
+	}
+	return numerator / denominator;
+}
+double calculate_thing(const r2& y1, r2 y2) {
+	return 1 / (1 + euclidenDistance(y1, y2));
+}
+
 
+
+double cost(const std::vector<double>& p, std::vector<double>& q, size_t amountOfSolutions) {
+	double sum = 0.0;
+	for (size_t x = 0; x < amountOfSolutions; x++) {
+		for (size_t y = 0; y < amountOfSolutions; y++) {
+			sum += q[x * amountOfSolutions + y];
+		}
+	}
+	for (size_t x = 0; x < amountOfSolutions; x++) {
+		for (size_t y = 0; y < amountOfSolutions; y++) {
+			q[x * amountOfSolutions + y] /= sum;
+		}
+	}
+	
+	
+	double cost = 0.0;
+	for (size_t i = 0; i < amountOfSolutions; i++) {
+		for (size_t j = 0; j < amountOfSolutions; j++) {
+			if (i == j) continue;
+			double pji = p[j * amountOfSolutions + i];
+			double value = pji * std::log(pji / q[j * amountOfSolutions + i]);
+			cost += value;
+		}
+	}
+	return cost;
+}
+
+
+r2 computeGradient(int i, const std::vector<double>& q, const std::vector<double>& p, const std::vector<r2>& ySolutions, int amountOfSolutions) {
+	r2 value;
+	const r2& yi = ySolutions[i];
+	for (size_t j = 0; j < amountOfSolutions; j++) {
+		if (j == i) continue;
+		double scalar = p[j * amountOfSolutions + i] - q[j * amountOfSolutions + i];
+		const r2& yj = ySolutions[j];
+		value.x += scalar * (yi.x - yj.y);
+		value.y += scalar * (yi.y - yj.y);
+	}
+	value.x *= 4;
+	value.y *= 4;
+	return value;
+}

+ 13 - 2
metaviscon/metaviscon.vcxproj

@@ -73,7 +73,7 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <LinkIncremental>false</LinkIncremental>
     <OutDir>$(SolutionDir)Build\$(Platform)\$(Configuration)\</OutDir>
-    <IntDir>$(SolutionDir)Intermediate\$(Platform)\$(Configuration)\</IntDir>
+    <IntDir>$(SolutionDir)Intermediate\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <LinkIncremental>true</LinkIncremental>
@@ -81,7 +81,7 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <LinkIncremental>true</LinkIncremental>
     <OutDir>$(SolutionDir)Build\$(Platform)\$(Configuration)\</OutDir>
-    <IntDir>$(SolutionDir)Intermediate\$(Platform)\$(Configuration)\</IntDir>
+    <IntDir>$(SolutionDir)Intermediate\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <LinkIncremental>false</LinkIncremental>
@@ -157,13 +157,24 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="generateMetalogFile.cpp" />
     <ClCompile Include="metaviscon.cpp" />
+    <ClCompile Include="myTsne.cpp" />
+    <ClCompile Include="RunDataLite.cpp" />
+    <ClCompile Include="src\t_sne\sptree.cpp" />
+    <ClCompile Include="src\t_sne\tsne.cpp" />
+    <ClCompile Include="src\t_sne\tsne_main.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\metavis\metavis.vcxproj">
       <Project>{b12702ad-abfb-343a-a199-8e24837244a3}</Project>
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="src\t_sne\sptree.h" />
+    <ClInclude Include="src\t_sne\tsne.h" />
+    <ClInclude Include="src\t_sne\vptree.h" />
+  </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>

+ 38 - 0
metaviscon/metaviscon.vcxproj.filters

@@ -13,10 +13,48 @@
       <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
       <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
     </Filter>
+    <Filter Include="Quelldateien\t-SNE">
+      <UniqueIdentifier>{1cf0927b-b4a2-455c-9dee-dc59a6b357a0}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Headerdateien\t-SNE">
+      <UniqueIdentifier>{6b396d86-dbd9-435a-a305-a9b39597a3ef}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Quelldateien\metalog-file">
+      <UniqueIdentifier>{e25b6be8-40b4-41a3-94fa-1d026a5c05aa}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="metaviscon.cpp">
       <Filter>Quelldateien</Filter>
     </ClCompile>
+    <ClCompile Include="src\t_sne\tsne.cpp">
+      <Filter>Quelldateien\t-SNE</Filter>
+    </ClCompile>
+    <ClCompile Include="src\t_sne\tsne_main.cpp">
+      <Filter>Quelldateien\t-SNE</Filter>
+    </ClCompile>
+    <ClCompile Include="src\t_sne\sptree.cpp">
+      <Filter>Quelldateien\t-SNE</Filter>
+    </ClCompile>
+    <ClCompile Include="myTsne.cpp">
+      <Filter>Quelldateien\t-SNE</Filter>
+    </ClCompile>
+    <ClCompile Include="RunDataLite.cpp">
+      <Filter>Quelldateien</Filter>
+    </ClCompile>
+    <ClCompile Include="generateMetalogFile.cpp">
+      <Filter>Quelldateien\metalog-file</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="src\t_sne\tsne.h">
+      <Filter>Headerdateien\t-SNE</Filter>
+    </ClInclude>
+    <ClInclude Include="src\t_sne\vptree.h">
+      <Filter>Headerdateien\t-SNE</Filter>
+    </ClInclude>
+    <ClInclude Include="src\t_sne\sptree.h">
+      <Filter>Headerdateien\t-SNE</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

+ 134 - 0
metaviscon/myTsne.cpp

@@ -0,0 +1,134 @@
+
+#include <iostream>
+#include "src/t_sne/tsne.h"
+#include <random>
+#include "RunDataLite.h"
+
+
+
+void generateRandomData(const size_t& N, const size_t& D, double* X);
+double euclidenDistance(double* y1, double* y2, int D);
+int main(int argv, char* argc[]) {
+	int amountOfSolutions = 1000;
+	int amountOfBits = 1600;
+	bool log = false;
+	std::string filepath("generated.metalog");
+	if (argv > 1) {
+		std::cout << argc[1] << std::endl;
+		filepath = argc[1];
+		
+	}
+
+	RunDataLite* data = new RunDataLite(filepath);
+	if (data->badFile()) return -1;
+	data->solutionVec.erase(data->solutionVec.begin());
+
+	//N -> amount of dataPoints 
+	//D -> Dimension of DataPoints
+	size_t N = data->solutionVec.size(), D = data->solutionVec[0].bitVec.size();
+	std::cout << "N:" << N << " D:" << D << std::endl;
+	double* X = new double[N * D];
+	int nD = 0;
+	for (int n = 0; n < N; n++) {
+		const SolutionPointData& sol = data->solutionVec[n];
+		if(log)std::cout << "[";
+		for (int d = 0; d < D; d++) {
+			X[ nD + d] = sol.bitVec[d]?1.0:0.0;
+			if (log)std::cout << (sol.bitVec[d] ? "1" : "0");
+		}
+		if(log)std::cout << "]" << std::endl;
+		nD += D;
+	}
+	if (log) {
+		std::cout << std::endl << "Second:" << std::endl << std::endl;
+		nD = 0;
+		for (int n = 0; n < N; n++) {
+			std::cout << "[";
+			for (int d = 0; d < D; d++) {
+				std::cout << (X[nD + d]);
+			}
+			std::cout << "]" << std::endl;
+			nD += D;
+		}
+		std::cout << std::endl << "End:" << std::endl << std::endl;
+	}
+
+
+
+	//no_dims -> dimension of target 
+	size_t no_dims = 2;
+	double* Y = new double[N * no_dims];
+	//Perplexity and theta
+	double perplexity = 20;
+	double theta = 0;
+	//learnig rate epsillon;
+	double eta = 200;
+	//Random 
+	int rand_seed = 17235761;
+	bool skip_random_init = false;
+	//Iter changes
+	int max_iter = 750, stop_lying_iter = 250, mom_switch_iter = 100;
+	TSNE::run(X, N, D, Y, no_dims, perplexity, theta, eta, rand_seed, skip_random_init, max_iter, stop_lying_iter, mom_switch_iter);
+	std::cout << "Finished:" << std::endl;
+	for (size_t i = 0; i < N; i++) {
+		std::cout << Y[i * no_dims] << ", " << Y[i * no_dims + 1] << ", " << data->solutionVec[i].objectiveFunction << std::endl;
+	}
+
+	return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void generateRandomData(const size_t& N, const size_t& D, double* X)
+{
+	//init rnd
+	std::random_device rd;  //Will be used to obtain a seed for the random number engine
+	std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
+	std::uniform_real_distribution<double> doubleDistr(0.0, 1.0);
+	//GenerateData
+	for (size_t i = 0; i < N; i++) {
+		double rnd = doubleDistr(gen);
+		for (size_t j = 0; j < D; j++) {
+			X[i * D + j] = (doubleDistr(gen) < rnd ? 1.0 : 0.0);
+		}
+	}
+	if (N < 20) {
+		for (size_t i = 0; i < N; i++) {
+			std::cout << "[";
+			for (size_t j = 0; j < D; j++) {
+				std::cout << (X[i * D + j] != 0.0 ? "1" : "0");
+			}
+			std::cout << "]" << std::endl;
+		}
+	}
+}
+double euclidenDistance( double* y1, double* y2, int D) {
+	double sum = 0.0;
+	std::cout << "[";
+	for (size_t j = 0; j < D; j++) {
+		std::cout << (y1[j] != 0.0 ? "1" : "0");
+	}
+	std::cout << "]" << std::endl;
+	std::cout << "[";
+	for (size_t j = 0; j < D; j++) {
+		std::cout << (y2[j] != 0.0 ? "1" : "0");
+	}
+	std::cout << "]" << std::endl;
+	for (int i = 0; i < D; i++) {
+		sum += (y1[i] - y2[i]) * (y1[i] - y2[i]);
+	}
+	return std::sqrt(sum);
+}

+ 3 - 0
metaviscon/myTsne.h

@@ -0,0 +1,3 @@
+#pragma once
+
+void generateRandomData(const size_t& N, const size_t& D, double* X);

+ 428 - 0
metaviscon/src/t_sne/sptree.cpp

@@ -0,0 +1,428 @@
+/*
+ *
+ * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by the Delft University of Technology.
+ * 4. Neither the name of the Delft University of Technology nor the names of
+ *    its contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+#include <math.h>
+#include <float.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <cmath>
+#include "sptree.h"
+
+
+
+// Constructs cell
+Cell::Cell(unsigned int inp_dimension) {
+    dimension = inp_dimension;
+    corner = (double*) malloc(dimension * sizeof(double));
+    width  = (double*) malloc(dimension * sizeof(double));
+}
+
+Cell::Cell(unsigned int inp_dimension, double* inp_corner, double* inp_width) {
+    dimension = inp_dimension;
+    corner = (double*) malloc(dimension * sizeof(double));
+    width  = (double*) malloc(dimension * sizeof(double));
+    for(int d = 0; d < dimension; d++) setCorner(d, inp_corner[d]);
+    for(int d = 0; d < dimension; d++) setWidth( d,  inp_width[d]);
+}
+
+// Destructs cell
+Cell::~Cell() {
+    free(corner);
+    free(width);
+}
+
+double Cell::getCorner(unsigned int d) {
+    return corner[d];
+}
+
+double Cell::getWidth(unsigned int d) {
+    return width[d];
+}
+
+void Cell::setCorner(unsigned int d, double val) {
+    corner[d] = val;
+}
+
+void Cell::setWidth(unsigned int d, double val) {
+    width[d] = val;
+}
+
+// Checks whether a point lies in a cell
+bool Cell::containsPoint(double point[])
+{
+    for(int d = 0; d < dimension; d++) {
+        if(corner[d] - width[d] > point[d]) return false;
+        if(corner[d] + width[d] < point[d]) return false;
+    }
+    return true;
+}
+
+
+// Default constructor for SPTree -- build tree, too!
+SPTree::SPTree(unsigned int D, double* inp_data, unsigned int N)
+{
+    
+    // Compute mean, width, and height of current map (boundaries of SPTree)
+    int nD = 0;
+    double* mean_Y = (double*) calloc(D,  sizeof(double));
+    double*  min_Y = (double*) malloc(D * sizeof(double)); for(unsigned int d = 0; d < D; d++)  min_Y[d] =  DBL_MAX;
+    double*  max_Y = (double*) malloc(D * sizeof(double)); for(unsigned int d = 0; d < D; d++)  max_Y[d] = -DBL_MAX;
+    for(unsigned int n = 0; n < N; n++) {
+        for(unsigned int d = 0; d < D; d++) {
+            mean_Y[d] += inp_data[n * D + d];
+            if(inp_data[nD + d] < min_Y[d]) min_Y[d] = inp_data[nD + d];
+            if(inp_data[nD + d] > max_Y[d]) max_Y[d] = inp_data[nD + d];
+        }
+        nD += D;
+    }
+    for(int d = 0; d < D; d++) mean_Y[d] /= (double) N;
+    
+    // Construct SPTree
+    double* width = (double*) malloc(D * sizeof(double));
+    for(int d = 0; d < D; d++) width[d] = fmax(max_Y[d] - mean_Y[d], mean_Y[d] - min_Y[d]) + 1e-5;
+    init(NULL, D, inp_data, mean_Y, width);
+    fill(N);
+    
+    // Clean up memory
+    free(mean_Y);
+    free(max_Y);
+    free(min_Y);
+    free(width);
+}
+
+
+// Constructor for SPTree with particular size and parent -- build the tree, too!
+SPTree::SPTree(unsigned int D, double* inp_data, unsigned int N, double* inp_corner, double* inp_width)
+{
+    init(NULL, D, inp_data, inp_corner, inp_width);
+    fill(N);
+}
+
+
+// Constructor for SPTree with particular size (do not fill the tree)
+SPTree::SPTree(unsigned int D, double* inp_data, double* inp_corner, double* inp_width)
+{
+    init(NULL, D, inp_data, inp_corner, inp_width);
+}
+
+
+// Constructor for SPTree with particular size and parent (do not fill tree)
+SPTree::SPTree(SPTree* inp_parent, unsigned int D, double* inp_data, double* inp_corner, double* inp_width) {
+    init(inp_parent, D, inp_data, inp_corner, inp_width);
+}
+
+
+// Constructor for SPTree with particular size and parent -- build the tree, too!
+SPTree::SPTree(SPTree* inp_parent, unsigned int D, double* inp_data, unsigned int N, double* inp_corner, double* inp_width)
+{
+    init(inp_parent, D, inp_data, inp_corner, inp_width);
+    fill(N);
+}
+
+
+// Main initialization function
+void SPTree::init(SPTree* inp_parent, unsigned int D, double* inp_data, double* inp_corner, double* inp_width)
+{
+    parent = inp_parent;
+    dimension = D;
+    no_children = 2;
+    for(unsigned int d = 1; d < D; d++) no_children *= 2;
+    data = inp_data;
+    is_leaf = true;
+    size = 0;
+    cum_size = 0;
+    
+    boundary = new Cell(dimension);
+    for(unsigned int d = 0; d < D; d++) boundary->setCorner(d, inp_corner[d]);
+    for(unsigned int d = 0; d < D; d++) boundary->setWidth( d, inp_width[d]);
+    
+    children = (SPTree**) malloc(no_children * sizeof(SPTree*));
+    for(unsigned int i = 0; i < no_children; i++) children[i] = NULL;
+
+    center_of_mass = (double*) malloc(D * sizeof(double));
+    for(unsigned int d = 0; d < D; d++) center_of_mass[d] = .0;
+    
+    buff = (double*) malloc(D * sizeof(double));
+}
+
+
+// Destructor for SPTree
+SPTree::~SPTree()
+{
+    for(unsigned int i = 0; i < no_children; i++) {
+        if(children[i] != NULL) delete children[i];
+    }
+    free(children);
+    free(center_of_mass);
+    free(buff);
+    delete boundary;
+}
+
+
+// Update the data underlying this tree
+void SPTree::setData(double* inp_data)
+{
+    data = inp_data;
+}
+
+
+// Get the parent of the current tree
+SPTree* SPTree::getParent()
+{
+    return parent;
+}
+
+
+// Insert a point into the SPTree
+bool SPTree::insert(unsigned int new_index)
+{
+    // Ignore objects which do not belong in this quad tree
+    double* point = data + new_index * dimension;
+    if(!boundary->containsPoint(point))
+        return false;
+    
+    // Online update of cumulative size and center-of-mass
+    cum_size++;
+    double mult1 = (double) (cum_size - 1) / (double) cum_size;
+    double mult2 = 1.0 / (double) cum_size;
+    for(unsigned int d = 0; d < dimension; d++) center_of_mass[d] *= mult1;
+    for(unsigned int d = 0; d < dimension; d++) center_of_mass[d] += mult2 * point[d];
+    
+    // If there is space in this quad tree and it is a leaf, add the object here
+    if(is_leaf && size < QT_NODE_CAPACITY) {
+        index[size] = new_index;
+        size++;
+        return true;
+    }
+    
+    // Don't add duplicates for now (this is not very nice)
+    bool any_duplicate = false;
+    for(unsigned int n = 0; n < size; n++) {
+        bool duplicate = true;
+        for(unsigned int d = 0; d < dimension; d++) {
+            if(point[d] != data[index[n] * dimension + d]) { duplicate = false; break; }
+        }
+        any_duplicate = any_duplicate | duplicate;
+    }
+    if(any_duplicate) return true;
+    
+    // Otherwise, we need to subdivide the current cell
+    if(is_leaf) subdivide();
+    
+    // Find out where the point can be inserted
+    for(unsigned int i = 0; i < no_children; i++) {
+        if(children[i]->insert(new_index)) return true;
+    }
+    
+    // Otherwise, the point cannot be inserted (this should never happen)
+    return false;
+}
+
+    
+// Create four children which fully divide this cell into four quads of equal area
+void SPTree::subdivide() {
+    
+    // Create new children
+    double* new_corner = (double*) malloc(dimension * sizeof(double));
+    double* new_width  = (double*) malloc(dimension * sizeof(double));
+    for(unsigned int i = 0; i < no_children; i++) {
+        unsigned int div = 1;
+        for(unsigned int d = 0; d < dimension; d++) {
+            new_width[d] = .5 * boundary->getWidth(d);
+            if((i / div) % 2 == 1) new_corner[d] = boundary->getCorner(d) - .5 * boundary->getWidth(d);
+            else                   new_corner[d] = boundary->getCorner(d) + .5 * boundary->getWidth(d);
+            div *= 2;
+        }
+        children[i] = new SPTree(this, dimension, data, new_corner, new_width);
+    }
+    free(new_corner);
+    free(new_width);
+    
+    // Move existing points to correct children
+    for(unsigned int i = 0; i < size; i++) {
+        bool success = false;
+        for(unsigned int j = 0; j < no_children; j++) {
+            if(!success) success = children[j]->insert(index[i]);
+        }
+        index[i] = -1;
+    }
+    
+    // Empty parent node
+    size = 0;
+    is_leaf = false;
+}
+
+
+// Build SPTree on dataset
+void SPTree::fill(unsigned int N)
+{
+    for(unsigned int i = 0; i < N; i++) insert(i);
+}
+
+
+// Checks whether the specified tree is correct
+bool SPTree::isCorrect()
+{
+    for(unsigned int n = 0; n < size; n++) {
+        double* point = data + index[n] * dimension;
+        if(!boundary->containsPoint(point)) return false;
+    }
+    if(!is_leaf) {
+        bool correct = true;
+        for(int i = 0; i < no_children; i++) correct = correct && children[i]->isCorrect();
+        return correct;
+    }
+    else return true;
+}
+
+
+
+// Build a list of all indices in SPTree
+void SPTree::getAllIndices(unsigned int* indices)
+{
+    getAllIndices(indices, 0);
+}
+
+
+// Build a list of all indices in SPTree
+unsigned int SPTree::getAllIndices(unsigned int* indices, unsigned int loc)
+{
+    
+    // Gather indices in current quadrant
+    for(unsigned int i = 0; i < size; i++) indices[loc + i] = index[i];
+    loc += size;
+    
+    // Gather indices in children
+    if(!is_leaf) {
+        for(int i = 0; i < no_children; i++) loc = children[i]->getAllIndices(indices, loc);
+    }
+    return loc;
+}
+
+
+unsigned int SPTree::getDepth() {
+    if(is_leaf) return 1;
+    int depth = 0;
+    for(unsigned int i = 0; i < no_children; i++) depth = fmax(depth, children[i]->getDepth());
+    return 1 + depth;
+}
+
+
+// Compute non-edge forces using Barnes-Hut algorithm
+void SPTree::computeNonEdgeForces(unsigned int point_index, double theta, double neg_f[], double* sum_Q)
+{
+    
+    // Make sure that we spend no time on empty nodes or self-interactions
+    if(cum_size == 0 || (is_leaf && size == 1 && index[0] == point_index)) return;
+    
+    // Compute distance between point and center-of-mass
+    double D = .0;
+    unsigned int ind = point_index * dimension;
+    for(unsigned int d = 0; d < dimension; d++) buff[d] = data[ind + d] - center_of_mass[d];
+    for(unsigned int d = 0; d < dimension; d++) D += buff[d] * buff[d];
+    
+    // Check whether we can use this node as a "summary"
+    double max_width = 0.0;
+    double cur_width;
+    for(unsigned int d = 0; d < dimension; d++) {
+        cur_width = boundary->getWidth(d);
+        max_width = (max_width > cur_width) ? max_width : cur_width;
+    }
+    if(is_leaf || max_width / sqrt(D) < theta) {
+    
+        // Compute and add t-SNE force between point and current node
+        D = 1.0 / (1.0 + D);
+        double mult = cum_size * D;
+        *sum_Q += mult;
+        mult *= D;
+        for(unsigned int d = 0; d < dimension; d++) neg_f[d] += mult * buff[d];
+    }
+    else {
+
+        // Recursively apply Barnes-Hut to children
+        for(unsigned int i = 0; i < no_children; i++) children[i]->computeNonEdgeForces(point_index, theta, neg_f, sum_Q);
+    }
+}
+
+
+// Computes edge forces
+void SPTree::computeEdgeForces(unsigned int* row_P, unsigned int* col_P, double* val_P, int N, double* pos_f)
+{
+    
+    // Loop over all edges in the graph
+    unsigned int ind1 = 0;
+    unsigned int ind2 = 0;
+    double D;
+    for(unsigned int n = 0; n < N; n++) {
+        for(unsigned int i = row_P[n]; i < row_P[n + 1]; i++) {
+        
+            // Compute pairwise distance and Q-value
+            D = 1.0;
+            ind2 = col_P[i] * dimension;
+            for(unsigned int d = 0; d < dimension; d++) buff[d] = data[ind1 + d] - data[ind2 + d];
+            for(unsigned int d = 0; d < dimension; d++) D += buff[d] * buff[d];
+            D = val_P[i] / D;
+            
+            // Sum positive force
+            for(unsigned int d = 0; d < dimension; d++) pos_f[ind1 + d] += D * buff[d];
+        }
+        ind1 += dimension;
+    }
+}
+
+
+// Print out tree
+void SPTree::print() 
+{
+    if(cum_size == 0) {
+        printf("Empty node\n");
+        return;
+    }
+
+    if(is_leaf) {
+        printf("Leaf node; data = [");
+        for(int i = 0; i < size; i++) {
+            double* point = data + index[i] * dimension;
+            for(int d = 0; d < dimension; d++) printf("%f, ", point[d]);
+            printf(" (index = %d)", index[i]);
+            if(i < size - 1) printf("\n");
+            else printf("]\n");
+        }        
+    }
+    else {
+        printf("Intersection node with center-of-mass = [");
+        for(int d = 0; d < dimension; d++) printf("%f, ", center_of_mass[d]);
+        printf("]; children are:\n");
+        for(int i = 0; i < no_children; i++) children[i]->print();
+    }
+}
+

+ 115 - 0
metaviscon/src/t_sne/sptree.h

@@ -0,0 +1,115 @@
+/*
+ *
+ * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by the Delft University of Technology.
+ * 4. Neither the name of the Delft University of Technology nor the names of
+ *    its contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef SPTREE_H
+#define SPTREE_H
+
+using namespace std;
+
+
+class Cell {
+
+    unsigned int dimension;
+    double* corner;
+    double* width;
+    
+    
+public:
+    Cell(unsigned int inp_dimension);
+    Cell(unsigned int inp_dimension, double* inp_corner, double* inp_width);
+    ~Cell();
+    
+    double getCorner(unsigned int d);
+    double getWidth(unsigned int d);
+    void setCorner(unsigned int d, double val);
+    void setWidth(unsigned int d, double val);
+    bool containsPoint(double point[]);
+};
+
+
+class SPTree
+{
+    
+    // Fixed constants
+    static const unsigned int QT_NODE_CAPACITY = 1;
+
+    // A buffer we use when doing force computations
+    double* buff;
+    
+    // Properties of this node in the tree
+    SPTree* parent;
+    unsigned int dimension;
+    bool is_leaf;
+    unsigned int size;
+    unsigned int cum_size;
+        
+    // Axis-aligned bounding box stored as a center with half-dimensions to represent the boundaries of this quad tree
+    Cell* boundary;
+    
+    // Indices in this space-partitioning tree node, corresponding center-of-mass, and list of all children
+    double* data;
+    double* center_of_mass;
+    unsigned int index[QT_NODE_CAPACITY];
+    
+    // Children
+    SPTree** children;
+    unsigned int no_children;
+    
+public:
+    SPTree(unsigned int D, double* inp_data, unsigned int N);
+    SPTree(unsigned int D, double* inp_data, double* inp_corner, double* inp_width);
+    SPTree(unsigned int D, double* inp_data, unsigned int N, double* inp_corner, double* inp_width);
+    SPTree(SPTree* inp_parent, unsigned int D, double* inp_data, unsigned int N, double* inp_corner, double* inp_width);
+    SPTree(SPTree* inp_parent, unsigned int D, double* inp_data, double* inp_corner, double* inp_width);
+    ~SPTree();
+    void setData(double* inp_data);
+    SPTree* getParent();
+    void construct(Cell boundary);
+    bool insert(unsigned int new_index);
+    void subdivide();
+    bool isCorrect();
+    void rebuildTree();
+    void getAllIndices(unsigned int* indices);
+    unsigned int getDepth();
+    void computeNonEdgeForces(unsigned int point_index, double theta, double neg_f[], double* sum_Q);
+    void computeEdgeForces(unsigned int* row_P, unsigned int* col_P, double* val_P, int N, double* pos_f);
+    void print();
+    
+private:
+    void init(SPTree* inp_parent, unsigned int D, double* inp_data, double* inp_corner, double* inp_width);
+    void fill(unsigned int N);
+    unsigned int getAllIndices(unsigned int* indices, unsigned int loc);
+    bool isChild(unsigned int test_index, unsigned int start, unsigned int end);
+};
+
+#endif

+ 723 - 0
metaviscon/src/t_sne/tsne.cpp

@@ -0,0 +1,723 @@
+/*
+ *
+ * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by the Delft University of Technology.
+ * 4. Neither the name of the Delft University of Technology nor the names of
+ *    its contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+#include <cfloat>
+#include <cmath>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <ctime>
+
+#include "sptree.h"
+#include "tsne.h"
+#include "vptree.h"
+#pragma warning(disable:4996)
+
+using namespace std;
+
+static double sign(double x) { return (x == .0 ? .0 : (x < .0 ? -1.0 : 1.0)); }
+
+static void zeroMean(double* X, int N, int D);
+static void computeGaussianPerplexity(double* X, int N, int D, double* P, double perplexity);
+static void computeGaussianPerplexity(double* X, int N, int D, unsigned int** _row_P, unsigned int** _col_P, double** _val_P, double perplexity, int K);
+static double randn();
+static void computeExactGradient(double* P, double* Y, int N, int D, double* dC);
+static void computeGradient(unsigned int* inp_row_P, unsigned int* inp_col_P, double* inp_val_P, double* Y, int N, int D, double* dC, double theta);
+static double evaluateError(double* P, double* Y, int N, int D);
+static double evaluateError(unsigned int* row_P, unsigned int* col_P, double* val_P, double* Y, int N, int D, double theta);
+static void computeSquaredEuclideanDistance(double* X, int N, int D, double* DD);
+static void symmetrizeMatrix(unsigned int** row_P, unsigned int** col_P, double** val_P, int N);
+
+// Perform t-SNE
+void TSNE::run(double* X, int N, int D, double* Y, int no_dims, double perplexity, double theta, double eta, int rand_seed,
+               bool skip_random_init, int max_iter, int stop_lying_iter, int mom_switch_iter) {
+
+    // Set random seed
+    if (skip_random_init != true) {
+      if(rand_seed >= 0) {
+          printf("Using random seed: %d\n", rand_seed);
+          srand((unsigned int) rand_seed);
+      } else {
+          printf("Using current time as random seed...\n");
+          srand(time(NULL));
+      }
+    }
+
+    // Determine whether we are using an exact algorithm
+    if(N - 1 < 3 * perplexity) { printf("Perplexity too large for the number of data points!\n"); exit(1); }
+    printf("Using no_dims = %d, perplexity = %f, and theta = %f\n", no_dims, perplexity, theta);
+    bool exact = (theta == .0) ? true : false;
+
+    // Set learning parameters
+    float total_time = .0;
+    clock_t start, end;
+	double momentum = .5, final_momentum = .8;
+
+    // Allocate some memory
+    double* dY    = (double*) malloc(N * no_dims * sizeof(double));
+    double* uY    = (double*) malloc(N * no_dims * sizeof(double));
+    double* gains = (double*) malloc(N * no_dims * sizeof(double));
+    if(dY == NULL || uY == NULL || gains == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    for(int i = 0; i < N * no_dims; i++)    uY[i] =  .0;
+    for(int i = 0; i < N * no_dims; i++) gains[i] = 1.0;
+
+    // Normalize input data (to prevent numerical problems)
+    printf("Computing input similarities...\n");
+    start = clock();
+    zeroMean(X, N, D);
+    double max_X = .0;
+    for(int i = 0; i < N * D; i++) {
+        if(fabs(X[i]) > max_X) max_X = fabs(X[i]);
+    }
+    for(int i = 0; i < N * D; i++) X[i] /= max_X;
+
+    // Compute input similarities for exact t-SNE
+    double* P = nullptr; unsigned int* row_P = nullptr; unsigned int* col_P = nullptr; double* val_P = nullptr;
+    if(exact) {
+
+        // Compute similarities
+        printf("Exact?");
+        P = (double*) malloc(N * N * sizeof(double));
+        if(P == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+        computeGaussianPerplexity(X, N, D, P, perplexity);
+
+        // Symmetrize input similarities
+        printf("Symmetrizing...\n");
+        int nN = 0;
+        for(int n = 0; n < N; n++) {
+            int mN = (n + 1) * N;
+            for(int m = n + 1; m < N; m++) {
+                P[nN + m] += P[mN + n];
+                P[mN + n]  = P[nN + m];
+                mN += N;
+            }
+            nN += N;
+        }
+        double sum_P = .0;
+        for(int i = 0; i < N * N; i++) sum_P += P[i];
+        for(int i = 0; i < N * N; i++) P[i] /= sum_P;
+    }
+
+    // Compute input similarities for approximate t-SNE
+    else {
+
+        // Compute asymmetric pairwise input similarities
+        computeGaussianPerplexity(X, N, D, &row_P, &col_P, &val_P, perplexity, (int) (3 * perplexity));
+
+        // Symmetrize input similarities
+        symmetrizeMatrix(&row_P, &col_P, &val_P, N);
+        double sum_P = .0;
+        for(int i = 0; i < row_P[N]; i++) sum_P += val_P[i];
+        for(int i = 0; i < row_P[N]; i++) val_P[i] /= sum_P;
+    }
+    end = clock();
+
+    // Lie about the P-values
+    if(exact) { for(int i = 0; i < N * N; i++)        P[i] *= 12.0; }
+    else {      for(int i = 0; i < row_P[N]; i++) val_P[i] *= 12.0; }
+
+	// Initialize solution (randomly)
+  if (skip_random_init != true) {
+  	for(int i = 0; i < N * no_dims; i++) Y[i] = randn() * .0001;
+  }
+
+	// Perform main training loop
+    if(exact) printf("Input similarities computed in %4.2f seconds!\nLearning embedding...\n", (float) (end - start) / CLOCKS_PER_SEC);
+    else printf("Input similarities computed in %4.2f seconds (sparsity = %f)!\nLearning embedding...\n", (float) (end - start) / CLOCKS_PER_SEC, (double) row_P[N] / ((double) N * (double) N));
+    start = clock();
+    double last_C = -1;
+	for(int iter = 0; iter < max_iter; iter++) {
+
+        // Compute (approximate) gradient
+        if(exact) computeExactGradient(P, Y, N, no_dims, dY);
+        else computeGradient(row_P, col_P, val_P, Y, N, no_dims, dY, theta);
+
+        // Update gains
+        for(int i = 0; i < N * no_dims; i++) gains[i] = (sign(dY[i]) != sign(uY[i])) ? (gains[i] + .2) : (gains[i] * .8);
+        for(int i = 0; i < N * no_dims; i++) if(gains[i] < .01) gains[i] = .01;
+
+        // Perform gradient update (with momentum and gains)
+        for(int i = 0; i < N * no_dims; i++) uY[i] = momentum * uY[i] - eta * gains[i] * dY[i];
+		for(int i = 0; i < N * no_dims; i++)  Y[i] = Y[i] + uY[i];
+
+        // Make solution zero-mean
+		zeroMean(Y, N, no_dims);
+
+        // Stop lying about the P-values after a while, and switch momentum
+        if(iter == stop_lying_iter) {
+            if(exact) { for(int i = 0; i < N * N; i++)        P[i] /= 12.0; }
+            else      { for(int i = 0; i < row_P[N]; i++) val_P[i] /= 12.0; }
+        }
+        if(iter == mom_switch_iter) momentum = final_momentum;
+
+        // Print out progress
+        if (iter > 0 && (iter % 50 == 0 || iter == max_iter - 1)) {
+            end = clock();
+            double C = .0;
+            if(exact) C = evaluateError(P, Y, N, no_dims);
+            else      C = evaluateError(row_P, col_P, val_P, Y, N, no_dims, theta);  // doing approximate computation here!
+           
+            if(iter == 0)
+                printf("Iteration %d: error is %f\n", iter + 1, C);
+            else {
+                total_time += (float) (end - start) / CLOCKS_PER_SEC;
+                printf("Iteration %d: error is %f (50 iterations in %4.2f seconds)\n", iter, C, (float) (end - start) / CLOCKS_PER_SEC);
+            }
+			start = clock();
+            /*if (std::fabs(last_C - C) < 0.001) {
+                break;
+            }
+            last_C = C;*/
+        }
+    }
+    end = clock(); total_time += (float) (end - start) / CLOCKS_PER_SEC;
+
+    // Clean up memory
+    free(dY);
+    free(uY);
+    free(gains);
+    if(exact) free(P);
+    else {
+        free(row_P); row_P = NULL;
+        free(col_P); col_P = NULL;
+        free(val_P); val_P = NULL;
+    }
+    printf("Fitting performed in %4.2f seconds.\n", total_time);
+}
+
+
+// Compute gradient of the t-SNE cost function (using Barnes-Hut algorithm)
+static void computeGradient(unsigned int* inp_row_P, unsigned int* inp_col_P, double* inp_val_P, double* Y, int N, int D, double* dC, double theta)
+{
+
+    // Construct space-partitioning tree on current map
+    SPTree* tree = new SPTree(D, Y, N);
+
+    // Compute all terms required for t-SNE gradient
+    double sum_Q = .0;
+    double* pos_f = (double*) calloc(N * D, sizeof(double));
+    double* neg_f = (double*) calloc(N * D, sizeof(double));
+    if(pos_f == NULL || neg_f == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    tree->computeEdgeForces(inp_row_P, inp_col_P, inp_val_P, N, pos_f);
+    for(int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, neg_f + n * D, &sum_Q);
+
+    // Compute final t-SNE gradient
+    for(int i = 0; i < N * D; i++) {
+        dC[i] = pos_f[i] - (neg_f[i] / sum_Q);
+    }
+    free(pos_f);
+    free(neg_f);
+    delete tree;
+}
+
+// Compute gradient of the t-SNE cost function (exact)
+static void computeExactGradient(double* P, double* Y, int N, int D, double* dC) {
+
+	// Make sure the current gradient contains zeros
+	for(int i = 0; i < N * D; i++) dC[i] = 0.0;
+
+    // Compute the squared Euclidean distance matrix
+    double* DD = (double*) malloc(N * N * sizeof(double));
+    if(DD == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    computeSquaredEuclideanDistance(Y, N, D, DD);
+
+    // Compute Q-matrix and normalization sum
+    double* Q    = (double*) malloc(N * N * sizeof(double));
+    if(Q == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    double sum_Q = .0;
+    int nN = 0;
+    for(int n = 0; n < N; n++) {
+    	for(int m = 0; m < N; m++) {
+            if(n != m) {
+                Q[nN + m] = 1 / (1 + DD[nN + m]);
+                sum_Q += Q[nN + m];
+            }
+        }
+        nN += N;
+    }
+
+	// Perform the computation of the gradient
+    nN = 0;
+    int nD = 0;
+	for(int n = 0; n < N; n++) {
+        int mD = 0;
+    	for(int m = 0; m < N; m++) {
+            if(n != m) {
+                double mult = (P[nN + m] - (Q[nN + m] / sum_Q)) * Q[nN + m];
+                for(int d = 0; d < D; d++) {
+                    dC[nD + d] += (Y[nD + d] - Y[mD + d]) * mult;
+                }
+            }
+            mD += D;
+		}
+        nN += N;
+        nD += D;
+	}
+
+    // Free memory
+    free(DD); DD = NULL;
+    free(Q);  Q  = NULL;
+}
+
+
+// Evaluate t-SNE cost function (exactly)
+static double evaluateError(double* P, double* Y, int N, int D) {
+
+    // Compute the squared Euclidean distance matrix
+    double* DD = (double*) malloc(N * N * sizeof(double));
+    double* Q = (double*) malloc(N * N * sizeof(double));
+    if(DD == NULL || Q == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    computeSquaredEuclideanDistance(Y, N, D, DD);
+
+    // Compute Q-matrix and normalization sum
+    int nN = 0;
+    double sum_Q = DBL_MIN;
+    for(int n = 0; n < N; n++) {
+    	for(int m = 0; m < N; m++) {
+            if(n != m) {
+                Q[nN + m] = 1 / (1 + DD[nN + m]);
+                sum_Q += Q[nN + m];
+            }
+            else Q[nN + m] = DBL_MIN;
+        }
+        nN += N;
+    }
+    for(int i = 0; i < N * N; i++) Q[i] /= sum_Q;
+
+    // Sum t-SNE error
+    double C = .0;
+	for(int n = 0; n < N * N; n++) {
+        C += P[n] * log((P[n] + FLT_MIN) / (Q[n] + FLT_MIN));
+	}
+
+    // Clean up memory
+    free(DD);
+    free(Q);
+	return C;
+}
+
+// Evaluate t-SNE cost function (approximately)
+static double evaluateError(unsigned int* row_P, unsigned int* col_P, double* val_P, double* Y, int N, int D, double theta)
+{
+
+    // Get estimate of normalization term
+    SPTree* tree = new SPTree(D, Y, N);
+    double* buff = (double*) calloc(D, sizeof(double));
+    double sum_Q = .0;
+    for(int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, buff, &sum_Q);
+
+    // Loop over all edges to compute t-SNE error
+    int ind1, ind2;
+    double C = .0, Q;
+    for(int n = 0; n < N; n++) {
+        ind1 = n * D;
+        for(int i = row_P[n]; i < row_P[n + 1]; i++) {
+            Q = .0;
+            ind2 = col_P[i] * D;
+            for(int d = 0; d < D; d++) buff[d]  = Y[ind1 + d];
+            for(int d = 0; d < D; d++) buff[d] -= Y[ind2 + d];
+            for(int d = 0; d < D; d++) Q += buff[d] * buff[d];
+            Q = (1.0 / (1.0 + Q)) / sum_Q;
+            C += val_P[i] * log((val_P[i] + FLT_MIN) / (Q + FLT_MIN));
+        }
+    }
+
+    // Clean up memory
+    free(buff);
+    delete tree;
+    return C;
+}
+
+
+// Compute input similarities with a fixed perplexity
+static void computeGaussianPerplexity(double* X, int N, int D, double* P, double perplexity) {
+
+	// Compute the squared Euclidean distance matrix
+	double* DD = (double*) malloc(N * N * sizeof(double));
+    if(DD == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+	computeSquaredEuclideanDistance(X, N, D, DD);
+
+	// Compute the Gaussian kernel row by row
+    int nN = 0;
+	for(int n = 0; n < N; n++) {
+
+		// Initialize some variables
+		bool found = false;
+		double beta = 1.0;
+		double min_beta = -DBL_MAX;
+		double max_beta =  DBL_MAX;
+		double tol = 1e-5;
+        double sum_P;
+
+		// Iterate until we found a good perplexity
+		int iter = 0;
+		while(!found && iter < 200) {
+
+			// Compute Gaussian kernel row
+			for(int m = 0; m < N; m++) P[nN + m] = exp(-beta * DD[nN + m]);
+			P[nN + n] = DBL_MIN;
+
+			// Compute entropy of current row
+			sum_P = DBL_MIN;
+			for(int m = 0; m < N; m++) sum_P += P[nN + m];
+			double H = 0.0;
+			for(int m = 0; m < N; m++) H += beta * (DD[nN + m] * P[nN + m]);
+			H = (H / sum_P) + log(sum_P);
+
+			// Evaluate whether the entropy is within the tolerance level
+			double Hdiff = H - log(perplexity);
+			if(Hdiff < tol && -Hdiff < tol) {
+				found = true;
+			}
+			else {
+				if(Hdiff > 0) {
+					min_beta = beta;
+					if(max_beta == DBL_MAX || max_beta == -DBL_MAX)
+						beta *= 2.0;
+					else
+						beta = (beta + max_beta) / 2.0;
+				}
+				else {
+					max_beta = beta;
+					if(min_beta == -DBL_MAX || min_beta == DBL_MAX)
+						beta /= 2.0;
+					else
+						beta = (beta + min_beta) / 2.0;
+				}
+			}
+
+			// Update iteration counter
+			iter++;
+		}
+
+		// Row normalize P
+		for(int m = 0; m < N; m++) P[nN + m] /= sum_P;
+        nN += N;
+	}
+
+	// Clean up memory
+	free(DD); DD = NULL;
+}
+
+
+// Compute input similarities with a fixed perplexity using ball trees (this function allocates memory another function should free)
+static void computeGaussianPerplexity(double* X, int N, int D, unsigned int** _row_P, unsigned int** _col_P, double** _val_P, double perplexity, int K) {
+
+    if(perplexity > K) printf("Perplexity should be lower than K!\n");
+
+    // Allocate the memory we need
+    *_row_P = (unsigned int*)    malloc((N + 1) * sizeof(unsigned int));
+    *_col_P = (unsigned int*)    calloc(N * K, sizeof(unsigned int));
+    *_val_P = (double*) calloc(N * K, sizeof(double));
+    if(*_row_P == NULL || *_col_P == NULL || *_val_P == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    unsigned int* row_P = *_row_P;
+    unsigned int* col_P = *_col_P;
+    double* val_P = *_val_P;
+    double* cur_P = (double*) malloc((N - 1) * sizeof(double));
+    if(cur_P == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    row_P[0] = 0;
+    for(int n = 0; n < N; n++) row_P[n + 1] = row_P[n] + (unsigned int) K;
+
+    // Build ball tree on data set
+    VpTree<DataPoint, euclidean_distance>* tree = new VpTree<DataPoint, euclidean_distance>();
+    vector<DataPoint> obj_X(N, DataPoint(D, -1, X));
+    for(int n = 0; n < N; n++) obj_X[n] = DataPoint(D, n, X + n * D);
+    tree->create(obj_X);
+
+    // Loop over all points to find nearest neighbors
+    printf("Building tree...\n");
+    vector<DataPoint> indices;
+    vector<double> distances;
+    for(int n = 0; n < N; n++) {
+
+        if(n % 10000 == 0) printf(" - point %d of %d\n", n, N);
+
+        // Find nearest neighbors
+        indices.clear();
+        distances.clear();
+        tree->search(obj_X[n], K + 1, &indices, &distances);
+
+        // Initialize some variables for binary search
+        bool found = false;
+        double beta = 1.0;
+        double min_beta = -DBL_MAX;
+        double max_beta =  DBL_MAX;
+        double tol = 1e-5;
+
+        // Iterate until we found a good perplexity
+        int iter = 0; double sum_P;
+        while(!found && iter < 200) {
+
+            // Compute Gaussian kernel row
+            for(int m = 0; m < K; m++) cur_P[m] = exp(-beta * distances[m + 1] * distances[m + 1]);
+
+            // Compute entropy of current row
+            sum_P = DBL_MIN;
+            for(int m = 0; m < K; m++) sum_P += cur_P[m];
+            double H = .0;
+            for(int m = 0; m < K; m++) H += beta * (distances[m + 1] * distances[m + 1] * cur_P[m]);
+            H = (H / sum_P) + log(sum_P);
+
+            // Evaluate whether the entropy is within the tolerance level
+            double Hdiff = H - log(perplexity);
+            if(Hdiff < tol && -Hdiff < tol) {
+                found = true;
+            }
+            else {
+                if(Hdiff > 0) {
+                    min_beta = beta;
+                    if(max_beta == DBL_MAX || max_beta == -DBL_MAX)
+                        beta *= 2.0;
+                    else
+                        beta = (beta + max_beta) / 2.0;
+                }
+                else {
+                    max_beta = beta;
+                    if(min_beta == -DBL_MAX || min_beta == DBL_MAX)
+                        beta /= 2.0;
+                    else
+                        beta = (beta + min_beta) / 2.0;
+                }
+            }
+
+            // Update iteration counter
+            iter++;
+        }
+
+        // Row-normalize current row of P and store in matrix
+        for(unsigned int m = 0; m < K; m++) cur_P[m] /= sum_P;
+        for(unsigned int m = 0; m < K; m++) {
+            col_P[row_P[n] + m] = (unsigned int) indices[m + 1].index();
+            val_P[row_P[n] + m] = cur_P[m];
+        }
+    }
+
+    // Clean up memory
+    obj_X.clear();
+    free(cur_P);
+    delete tree;
+}
+
+
+// Symmetrizes a sparse matrix
+static void symmetrizeMatrix(unsigned int** _row_P, unsigned int** _col_P, double** _val_P, int N) {
+
+    // Get sparse matrix
+    unsigned int* row_P = *_row_P;
+    unsigned int* col_P = *_col_P;
+    double* val_P = *_val_P;
+
+    // Count number of elements and row counts of symmetric matrix
+    int* row_counts = (int*) calloc(N, sizeof(int));
+    if(row_counts == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    for(int n = 0; n < N; n++) {
+        for(int i = row_P[n]; i < row_P[n + 1]; i++) {
+
+            // Check whether element (col_P[i], n) is present
+            bool present = false;
+            for(int m = row_P[col_P[i]]; m < row_P[col_P[i] + 1]; m++) {
+                if(col_P[m] == n) present = true;
+            }
+            if(present) row_counts[n]++;
+            else {
+                row_counts[n]++;
+                row_counts[col_P[i]]++;
+            }
+        }
+    }
+    int no_elem = 0;
+    for(int n = 0; n < N; n++) no_elem += row_counts[n];
+
+    // Allocate memory for symmetrized matrix
+    unsigned int* sym_row_P = (unsigned int*) malloc((N + 1) * sizeof(unsigned int));
+    unsigned int* sym_col_P = (unsigned int*) malloc(no_elem * sizeof(unsigned int));
+    double* sym_val_P = (double*) malloc(no_elem * sizeof(double));
+    if(sym_row_P == NULL || sym_col_P == NULL || sym_val_P == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+
+    // Construct new row indices for symmetric matrix
+    sym_row_P[0] = 0;
+    for(int n = 0; n < N; n++) sym_row_P[n + 1] = sym_row_P[n] + (unsigned int) row_counts[n];
+
+    // Fill the result matrix
+    int* offset = (int*) calloc(N, sizeof(int));
+    if(offset == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    for(int n = 0; n < N; n++) {
+        for(unsigned int i = row_P[n]; i < row_P[n + 1]; i++) {                                  // considering element(n, col_P[i])
+
+            // Check whether element (col_P[i], n) is present
+            bool present = false;
+            for(unsigned int m = row_P[col_P[i]]; m < row_P[col_P[i] + 1]; m++) {
+                if(col_P[m] == n) {
+                    present = true;
+                    if(n <= col_P[i]) {                                                 // make sure we do not add elements twice
+                        sym_col_P[sym_row_P[n]        + offset[n]]        = col_P[i];
+                        sym_col_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = n;
+                        sym_val_P[sym_row_P[n]        + offset[n]]        = val_P[i] + val_P[m];
+                        sym_val_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = val_P[i] + val_P[m];
+                    }
+                }
+            }
+
+            // If (col_P[i], n) is not present, there is no addition involved
+            if(!present) {
+                sym_col_P[sym_row_P[n]        + offset[n]]        = col_P[i];
+                sym_col_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = n;
+                sym_val_P[sym_row_P[n]        + offset[n]]        = val_P[i];
+                sym_val_P[sym_row_P[col_P[i]] + offset[col_P[i]]] = val_P[i];
+            }
+
+            // Update offsets
+            if(!present || (present && n <= col_P[i])) {
+                offset[n]++;
+                if(col_P[i] != n) offset[col_P[i]]++;
+            }
+        }
+    }
+
+    // Divide the result by two
+    for(int i = 0; i < no_elem; i++) sym_val_P[i] /= 2.0;
+
+    // Return symmetrized matrices
+    free(*_row_P); *_row_P = sym_row_P;
+    free(*_col_P); *_col_P = sym_col_P;
+    free(*_val_P); *_val_P = sym_val_P;
+
+    // Free up some memery
+    free(offset); offset = NULL;
+    free(row_counts); row_counts  = NULL;
+}
+
+// Compute squared Euclidean distance matrix
+static void computeSquaredEuclideanDistance(double* X, int N, int D, double* DD) {
+    const double* XnD = X;
+    for(int n = 0; n < N; ++n, XnD += D) {
+        const double* XmD = XnD + D;
+        double* curr_elem = &DD[n*N + n];
+        *curr_elem = 0.0;
+        double* curr_elem_sym = curr_elem + N;
+        for(int m = n + 1; m < N; ++m, XmD+=D, curr_elem_sym+=N) {
+            *(++curr_elem) = 0.0;
+            for(int d = 0; d < D; ++d) {
+                *curr_elem += (XnD[d] - XmD[d]) * (XnD[d] - XmD[d]);
+            }
+            *curr_elem_sym = *curr_elem;
+        }
+    }
+}
+
+
+// Makes data zero-mean
+static void zeroMean(double* X, int N, int D) {
+
+	// Compute data mean
+	double* mean = (double*) calloc(D, sizeof(double));
+    if(mean == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    int nD = 0;
+	for(int n = 0; n < N; n++) {
+		for(int d = 0; d < D; d++) {
+			mean[d] += X[nD + d];
+		}
+        nD += D;
+	}
+	for(int d = 0; d < D; d++) {
+		mean[d] /= (double) N;
+	}
+
+	// Subtract data mean
+    nD = 0;
+	for(int n = 0; n < N; n++) {
+		for(int d = 0; d < D; d++) {
+			X[nD + d] -= mean[d];
+		}
+        nD += D;
+	}
+    free(mean); mean = NULL;
+}
+
+
+// Generates a Gaussian random number
+static double randn() {
+	double x, y, radius;
+	do {
+		x = 2 * (rand() / ((double) RAND_MAX + 1)) - 1;
+		y = 2 * (rand() / ((double) RAND_MAX + 1)) - 1;
+		radius = (x * x) + (y * y);
+	} while((radius >= 1.0) || (radius == 0.0));
+	radius = sqrt(-2 * log(radius) / radius);
+	x *= radius;
+	y *= radius;
+	return x;
+}
+
+// Function that loads data from a t-SNE file
+// Note: this function does a malloc that should be freed elsewhere
+bool TSNE::load_data(double** data, int* n, int* d, int* no_dims, double* theta, double* perplexity, int* rand_seed, int* max_iter) {
+
+	// Open file, read first 2 integers, allocate memory, and read the data
+    FILE *h;
+    
+	if((h = fopen("data.dat", "r+b")) == NULL) {
+		printf("Error: could not open data file.\n");
+		return false;
+	}
+	fread(n, sizeof(int), 1, h);											// number of datapoints
+	fread(d, sizeof(int), 1, h);											// original dimensionality
+    fread(theta, sizeof(double), 1, h);										// gradient accuracy
+	fread(perplexity, sizeof(double), 1, h);								// perplexity
+	fread(no_dims, sizeof(int), 1, h);                                      // output dimensionality
+    fread(max_iter, sizeof(int),1,h);                                       // maximum number of iterations
+	*data = (double*) malloc(*d * *n * sizeof(double));
+    if(*data == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+    fread(*data, sizeof(double), *n * *d, h);                               // the data
+    if(!feof(h)) fread(rand_seed, sizeof(int), 1, h);                       // random seed
+	fclose(h);
+	printf("Read the %i x %i data matrix successfully!\n", *n, *d);
+	return true;
+}
+
+// Function that saves map to a t-SNE file
+void TSNE::save_data(double* data, int* landmarks, double* costs, int n, int d) {
+
+	// Open file, write first 2 integers and then the data
+	FILE *h;
+	if((h = fopen("result.dat", "w+b")) == NULL) {
+		printf("Error: could not open data file.\n");
+		return;
+	}
+	fwrite(&n, sizeof(int), 1, h);
+	fwrite(&d, sizeof(int), 1, h);
+    fwrite(data, sizeof(double), n * d, h);
+	fwrite(landmarks, sizeof(int), n, h);
+    fwrite(costs, sizeof(double), n, h);
+    fclose(h);
+	printf("Wrote the %i x %i data matrix successfully!\n", n, d);
+}

+ 50 - 0
metaviscon/src/t_sne/tsne.h

@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by the Delft University of Technology.
+ * 4. Neither the name of the Delft University of Technology nor the names of
+ *    its contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ */
+
+
+#ifndef TSNE_H
+#define TSNE_H
+
+#ifdef __cplusplus
+extern "C" {
+namespace TSNE {
+#endif
+    void run(double* X, int N, int D, double* Y, int no_dims, double perplexity, double theta, double eta, int rand_seed,
+             bool skip_random_init, int max_iter, int stop_lying_iter, int mom_switch_iter);
+    bool load_data(double** data, int* n, int* d, int* no_dims, double* theta, double* perplexity, int* rand_seed, int* max_iter);
+    void save_data(double* data, int* landmarks, double* costs, int n, int d);
+#ifdef __cplusplus
+};
+}
+#endif
+
+#endif

+ 41 - 0
metaviscon/src/t_sne/tsne_main.cpp

@@ -0,0 +1,41 @@
+#include <cfloat>
+#include <cmath>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <ctime>
+#include "tsne.h"
+
+// Function that runs the Barnes-Hut implementation of t-SNE
+int main2() {
+
+    // Define some variables
+	int origN, N, D, no_dims, max_iter;
+	double perplexity, theta, *data;
+    int rand_seed = -1;
+
+    // Read the parameters and the dataset
+	if(TSNE::load_data(&data, &origN, &D, &no_dims, &theta, &perplexity, &rand_seed, &max_iter)) {
+
+		// Make dummy landmarks
+        N = origN;
+        int* landmarks = (int*) malloc(N * sizeof(int));
+        if(landmarks == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+        for(int n = 0; n < N; n++) landmarks[n] = n;
+
+		// Now fire up the SNE implementation
+		double* Y = (double*) malloc(N * no_dims * sizeof(double));
+		double* costs = (double*) calloc(N, sizeof(double));
+        if(Y == NULL || costs == NULL) { printf("Memory allocation failed!\n"); exit(1); }
+		TSNE::run(data, N, D, Y, no_dims, perplexity, theta, 200.0, rand_seed, false, max_iter, 250, 250);
+
+		// Save the results
+		TSNE::save_data(Y, landmarks, costs, N, no_dims);
+
+        // Clean up the memory
+		free(data); data = NULL;
+		free(Y); Y = NULL;
+		free(costs); costs = NULL;
+		free(landmarks); landmarks = NULL;
+    }
+}

+ 272 - 0
metaviscon/src/t_sne/vptree.h

@@ -0,0 +1,272 @@
+/*
+ *
+ * Copyright (c) 2014, Laurens van der Maaten (Delft University of Technology)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by the Delft University of Technology.
+ * 4. Neither the name of the Delft University of Technology nor the names of 
+ *    its contributors may be used to endorse or promote products derived from 
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY LAURENS VAN DER MAATEN ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 
+ * EVENT SHALL LAURENS VAN DER MAATEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE.
+ *
+ */
+
+
+/* This code was adopted with minor modifications from Steve Hanov's great tutorial at http://stevehanov.ca/blog/index.php?id=130 */
+
+#include <stdlib.h>
+#include <algorithm>
+#include <vector>
+#include <stdio.h>
+#include <queue>
+#include <limits>
+#include <cmath>
+
+
+#ifndef VPTREE_H
+#define VPTREE_H
+
+class DataPoint
+{
+    int _ind;
+
+public:
+    double* _x;
+    int _D;
+    DataPoint() {
+        _D = 1;
+        _ind = -1;
+        _x = NULL;
+    }
+    DataPoint(int D, int ind, double* x) {
+        _D = D;
+        _ind = ind;
+        _x = (double*) malloc(_D * sizeof(double));
+        for(int d = 0; d < _D; d++) _x[d] = x[d];
+    }
+    DataPoint(const DataPoint& other) {                     // this makes a deep copy -- should not free anything
+        if(this != &other) {
+            _D = other.dimensionality();
+            _ind = other.index();
+            _x = (double*) malloc(_D * sizeof(double));      
+            for(int d = 0; d < _D; d++) _x[d] = other.x(d);
+        }
+    }
+    ~DataPoint() { if(_x != NULL) free(_x); }
+    DataPoint& operator= (const DataPoint& other) {         // asignment should free old object
+        if(this != &other) {
+            if(_x != NULL) free(_x);
+            _D = other.dimensionality();
+            _ind = other.index();
+            _x = (double*) malloc(_D * sizeof(double));
+            for(int d = 0; d < _D; d++) _x[d] = other.x(d);
+        }
+        return *this;
+    }
+    int index() const { return _ind; }
+    int dimensionality() const { return _D; }
+    double x(int d) const { return _x[d]; }
+};
+
+double euclidean_distance(const DataPoint &t1, const DataPoint &t2) {
+    double dd = .0;
+    double* x1 = t1._x;
+    double* x2 = t2._x;
+    double diff;
+    for(int d = 0; d < t1._D; d++) {
+        diff = (x1[d] - x2[d]);
+        dd += diff * diff;
+    }
+    return sqrt(dd);
+}
+
+
+template<typename T, double (*distance)( const T&, const T& )>
+class VpTree
+{
+public:
+    
+    // Default constructor
+    VpTree() : _root(0) {}
+    
+    // Destructor
+    ~VpTree() {
+        delete _root;
+    }
+
+    // Function to create a new VpTree from data
+    void create(const std::vector<T>& items) {
+        delete _root;
+        _items = items;
+        _root = buildFromPoints(0, items.size());
+    }
+    
+    // Function that uses the tree to find the k nearest neighbors of target
+    void search(const T& target, int k, std::vector<T>* results, std::vector<double>* distances)
+    {
+        
+        // Use a priority queue to store intermediate results on
+        std::priority_queue<HeapItem> heap;
+        
+        // Variable that tracks the distance to the farthest point in our results
+        _tau = DBL_MAX;
+        
+        // Perform the search
+        search(_root, target, k, heap);
+        
+        // Gather final results
+        results->clear(); distances->clear();
+        while(!heap.empty()) {
+            results->push_back(_items[heap.top().index]);
+            distances->push_back(heap.top().dist);
+            heap.pop();
+        }
+        
+        // Results are in reverse order
+        std::reverse(results->begin(), results->end());
+        std::reverse(distances->begin(), distances->end());
+    }
+    
+private:
+    std::vector<T> _items;
+    double _tau;
+    
+    // Single node of a VP tree (has a point and radius; left children are closer to point than the radius)
+    struct Node
+    {
+        int index;              // index of point in node
+        double threshold;       // radius(?)
+        Node* left;             // points closer by than threshold
+        Node* right;            // points farther away than threshold
+        
+        Node() :
+        index(0), threshold(0.), left(0), right(0) {}
+        
+        ~Node() {               // destructor
+            delete left;
+            delete right;
+        }
+    }* _root;
+    
+    
+    // An item on the intermediate result queue
+    struct HeapItem {
+        HeapItem( int index, double dist) :
+        index(index), dist(dist) {}
+        int index;
+        double dist;
+        bool operator<(const HeapItem& o) const {
+            return dist < o.dist;
+        }
+    };
+    
+    // Distance comparator for use in std::nth_element
+    struct DistanceComparator
+    {
+        const T& item;
+        DistanceComparator(const T& item) : item(item) {}
+        bool operator()(const T& a, const T& b) {
+            return distance(item, a) < distance(item, b);
+        }
+    };
+    
+    // Function that (recursively) fills the tree
+    Node* buildFromPoints( int lower, int upper )
+    {
+        if (upper == lower) {     // indicates that we're done here!
+            return NULL;
+        }
+        
+        // Lower index is center of current node
+        Node* node = new Node();
+        node->index = lower;
+        
+        if (upper - lower > 1) {      // if we did not arrive at leaf yet
+            
+            // Choose an arbitrary point and move it to the start
+            int i = (int) ((double)rand() / RAND_MAX * (upper - lower - 1)) + lower;
+            std::swap(_items[lower], _items[i]);
+            
+            // Partition around the median distance
+            int median = (upper + lower) / 2;
+            std::nth_element(_items.begin() + lower + 1,
+                             _items.begin() + median,
+                             _items.begin() + upper,
+                             DistanceComparator(_items[lower]));
+            
+            // Threshold of the new node will be the distance to the median
+            node->threshold = distance(_items[lower], _items[median]);
+            
+            // Recursively build tree
+            node->index = lower;
+            node->left = buildFromPoints(lower + 1, median);
+            node->right = buildFromPoints(median, upper);
+        }
+        
+        // Return result
+        return node;
+    }
+    
+    // Helper function that searches the tree    
+    void search(Node* node, const T& target, int k, std::priority_queue<HeapItem>& heap)
+    {
+        if(node == NULL) return;     // indicates that we're done here
+        
+        // Compute distance between target and current node
+        double dist = distance(_items[node->index], target);
+
+        // If current node within radius tau
+        if(dist < _tau) {
+            if(heap.size() == k) heap.pop();                 // remove furthest node from result list (if we already have k results)
+            heap.push(HeapItem(node->index, dist));           // add current node to result list
+            if(heap.size() == k) _tau = heap.top().dist;     // update value of tau (farthest point in result list)
+        }
+        
+        // Return if we arrived at a leaf
+        if(node->left == NULL && node->right == NULL) {
+            return;
+        }
+        
+        // If the target lies within the radius of ball
+        if(dist < node->threshold) {
+            if(dist - _tau <= node->threshold) {         // if there can still be neighbors inside the ball, recursively search left child first
+                search(node->left, target, k, heap);
+            }
+            
+            if(dist + _tau >= node->threshold) {         // if there can still be neighbors outside the ball, recursively search right child
+                search(node->right, target, k, heap);
+            }
+        
+        // If the target lies outsize the radius of the ball
+        } else {
+            if(dist + _tau >= node->threshold) {         // if there can still be neighbors outside the ball, recursively search right child first
+                search(node->right, target, k, heap);
+            }
+            
+            if (dist - _tau <= node->threshold) {         // if there can still be neighbors inside the ball, recursively search left child
+                search(node->left, target, k, heap);
+            }
+        }
+    }
+};
+            
+#endif