diff --git a/metavis/BitInspector.cpp b/metavis/BitInspector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b04d49506662c7c765ce7d757a8e40bcc8aed46 --- /dev/null +++ b/metavis/BitInspector.cpp @@ -0,0 +1,99 @@ +#include "pch.h" +#include "BitInspector.h" + +#define ItemWidth 20 +#define FieldStart 170 +#define EmptySpaceRight 5 +#define EmptySpaceBottom 40 +#define HeaderSpace 0 + +BitInspector::BitInspector(QWidget* parent) + : QWidget(parent) +{ + //DummyData + //std::vector< SolutionPointData>* dummyVec = new std::vector< SolutionPointData>(); + //SolutionPointData data; + //data.iteration = 20; + //data.bitVec.push_back(true); + //data.objectiveFunction = 230.234; + //data.particleNumber = 120; + //dummyVec->push_back(data); + //begin = dummyVec->begin(); + //end = dummyVec->end(); +} + +BitInspector::~BitInspector() +{ + +} + +void BitInspector::updateData(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end) +{ + this->begin = begin; + this->end = end; + setMinimumSize(FieldStart + begin->bitVec.size() * 1 + EmptySpaceRight, std::distance(begin, end) * ItemWidth + EmptySpaceBottom + HeaderSpace); + update(); +} + +void BitInspector::clear() +{ + begin = std::vector<SolutionPointData>::iterator(); + end = std::vector<SolutionPointData>::iterator(); + update(); +} + + +void BitInspector::keyPressEvent(QKeyEvent* event) +{ + +} + +void BitInspector::paintEvent(QPaintEvent* event) +{ + if (begin == end) { + return; + } + int value = (parentWidget()->size().width() - FieldStart - EmptySpaceRight) / begin->bitVec.size(); + + if (value > 1) { + sizePerBit = value; + } + else { + sizePerBit = 1; + } + //PaintList + QPainter painter(this); + painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing); + painter.setFont(QFont("Arial", 8)); + painter.setPen(QColor(0, 0, 0)); + QRect rect(event->rect()); + rect.moveTop(rect.top() - parentWidget()->size().height()); + int minimumY = rect.top() - parentWidget()->size().height(); + int maximumY = rect.bottom() + parentWidget()->size().height(); + + QPen blackPen(QColor(0, 0, 0)); + QPen whitePen(QColor(255, 255, 255)); + int iteration = 0; + int drawSize = (sizePerBit > 1)? sizePerBit - 1: 1; + if (begin != end) { + painter.fillRect(QRect(FieldStart -1 , 0, begin->bitVec.size() * sizePerBit, std::distance(begin, end) * ItemWidth), Qt::lightGray); + int width = FieldStart + begin->bitVec.size() * sizePerBit; + for (auto iter = begin; iter != end; iter++) { + int startY = (iteration++) * ItemWidth + HeaderSpace; + if (startY <= minimumY) continue; + if (startY >= maximumY) break; + QRect textRect(0, startY, 40, ItemWidth); + painter.drawText(textRect, Qt::AlignCenter, QString::number(iter->iteration)); + textRect.moveLeft(40); + painter.drawText(textRect, Qt::AlignCenter, QString::number(iter->particleNumber)); + textRect.moveLeft(80); + textRect.setWidth(90); + painter.drawText(textRect, Qt::AlignCenter, QString::number(iter->objectiveFunction, 'f', 1)); + //painter.setPen(blackPen); + for (int j = 0; j < iter->bitVec.size(); j++) { + painter.fillRect(FieldStart + j * sizePerBit, startY, drawSize, ItemWidth, (iter->bitVec[j]) ? blackPen.color() : whitePen.color()); + } + painter.fillRect(FieldStart, startY, width - FieldStart, 1 ,QColor(140,140,140)); + } + } +} diff --git a/metavis/BitInspector.h b/metavis/BitInspector.h new file mode 100644 index 0000000000000000000000000000000000000000..c4f993711c22a2bb8278a9c1e3daea25298585a9 --- /dev/null +++ b/metavis/BitInspector.h @@ -0,0 +1,27 @@ +#pragma once + +#include <QWidget> +#include "RunData.h" +#include <QString> +#include <vector> + + + +class BitInspector : public QWidget +{ + Q_OBJECT + +public: + std::vector<SolutionPointData>::iterator begin; + std::vector<SolutionPointData>::iterator end; + int sizePerBit = 3; + + QPixmap map; + BitInspector(QWidget *parent); + ~BitInspector(); + void updateData(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end); + void clear(); +private: + void keyPressEvent(QKeyEvent* event) override; + void paintEvent(QPaintEvent* event) override; +}; diff --git a/metavis/BitInspectorPanel.cpp b/metavis/BitInspectorPanel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cec2144576f853780fd1ce331b20517d80a30ebd --- /dev/null +++ b/metavis/BitInspectorPanel.cpp @@ -0,0 +1,78 @@ +#include "pch.h" +#include "BitInspectorPanel.h" +#include <QScrollArea> +#include <QPushButton> +#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QSettings> +#include <QApplication> +#include <QDesktopWidget> + +BitInspectorPanel::BitInspectorPanel(QWidget *parent) + : QWidget(parent), inspector(new BitInspector(this)) +{ + QScrollArea* scrollArea = new QScrollArea(this); + scrollArea->setWidget(inspector); + scrollArea->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + scrollArea->setWidgetResizable(true); + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + topPanel = new QHBoxLayout(); + topPanel->setContentsMargins(0, 0, 0, 0); + topPanel->setSpacing(0); + topPanel->addWidget(new QLabel("Iteration")); + topPanel->addSpacing(2); + topPanel->addWidget(new QLabel("Number")); + topPanel->addSpacing(2); + topPanel->addWidget(new QLabel("ObjectiveFunction")); + topPanel->addSpacing(2); + topPanel->addWidget(new QLabel("Binary")); + topPanel->addStretch(); + runNameLabel = new QLabel(""); + topPanel->addWidget(runNameLabel); + + layout->addLayout(topPanel); + layout->addWidget(scrollArea); + popupWidget->setInformation("<h3><u>Inspector</u></h3> Shows all solution of a round. Each solution gets displayed in a row.<br><br><b>Iteration:</b> Show the iteration the solution is generated.<br><b>Number:</b> Show which individual in the popullation is represent.<br><b>Objective Function:</b> Describes how well the solution is, it depends on the problem if a low or high value is desired.<br><b>Binary:</b> Shows the bitstring of the solution. White rectangle represents the bit is <i>false<i> and black <i>true<i>."); + informationButton->setMinimumSize(20, 20); + informationButton->setMaximumSize(20, 20); + informationButton->setIcon(QIcon(":/metavis/Resources/information_icon.svg")); + informationButton->setHoveredIcon(QIcon(":/metavis/Resources/information_icon_hovered.svg")); + informationButton->setAttribute(Qt::WA_TranslucentBackground); + informationButton->setStyleSheet(informationButton->styleSheet() + "border: none;"); + informationButton->setToolTip("Information"); + connect(informationButton, &QPushButton::released, this, &BitInspectorPanel::showInformation); + topPanel->addWidget(informationButton); +} + +BitInspectorPanel::~BitInspectorPanel() +{ +} + +void BitInspectorPanel::setRunName(std::string& string) +{ + runNameLabel->setText("\"" + QString::fromStdString(string) + "\""); +} + +void BitInspectorPanel::removeRun() +{ + inspector->clear(); + runNameLabel->setText(""); +} + +void BitInspectorPanel::showInformation() +{ + QPoint position = QCursor::pos(); + popupWidget->width(); + QRect screen = QApplication::desktop()->screenGeometry(); + if (position.x() + popupWidget->width() > screen.width()) { + popupWidget->move(screen.width() - popupWidget->width(), position.y()); + } + else { + popupWidget->move(position.x(), position.y()); + + } + popupWidget->show(); + +} \ No newline at end of file diff --git a/metavis/BitInspectorPanel.h b/metavis/BitInspectorPanel.h new file mode 100644 index 0000000000000000000000000000000000000000..d311e303968b592d56db682a166fd5d8b468b4dd --- /dev/null +++ b/metavis/BitInspectorPanel.h @@ -0,0 +1,24 @@ +#pragma once + +#include <QWidget> +#include "BitInspector.h" +#include <string> +#include "HoverButton.h" +#include "InformationPopUp.h" +class BitInspectorPanel : public QWidget +{ + Q_OBJECT + +public: + BitInspectorPanel(QWidget *parent); + ~BitInspectorPanel(); + BitInspector* inspector; + QLabel* runNameLabel; + void setRunName(std::string& string); + void removeRun(); + void showInformation(); +protected: + HoverButton* informationButton = new HoverButton(); + InformationPopUp* popupWidget = new InformationPopUp(nullptr); + QHBoxLayout* topPanel; +}; diff --git a/metavis/Bitfield.cpp b/metavis/Bitfield.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e152e08962e062f5824870ad6485524dd8308b0c --- /dev/null +++ b/metavis/Bitfield.cpp @@ -0,0 +1,143 @@ +#include "pch.h" +#include "Bitfield.h" +#include <QSlider> +#include <QTimer> +#include <QCursor> + + +Bitfield::Bitfield(QWidget *parent) + : SearchSpacePlott(parent) +{ + setAxisLegend("Position", "#SetBits"); +} + +Bitfield::~Bitfield() +{ + +} + +void Bitfield::frameGraphInView() +{ + setVisibleWindow(0, 1, 0, maxAmountOfSetBits); + window.ZoomOut(0.05); + update(); +} + + + +void Bitfield::drawData(QPainter& painter) +{ + painter.setPen(Qt::lightGray); + painter.setBrush(Qt::Dense4Pattern); + painter.drawRect(this->getDisplayRect()); + painter.setPen(Qt::black); + painter.setBrush(Qt::NoBrush); + painter.fillRect(QRectF(transformGraphToView(QPointF(0, maxAmountOfSetBits)), transformGraphToView(QPointF(1, 0))), Qt::white); + painter.drawRect(QRectF(transformGraphToView(QPointF(0, maxAmountOfSetBits)), transformGraphToView(QPointF(1, 0)))); + + //Draw Dots + QPen linePen; + QRect graphDisplayRect = getDisplayRect(); + QPointF translation(graphDisplayRect.left(), graphDisplayRect.top()); + double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin); + double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin); + + for (const GraphPlottSeries& graphSeries : seriesVec) { + if (!graphSeries.data) { + qDebug() << "Pointer to nothing pls help"; + continue; + } + if (graphSeries.data->empty() || graphSeries.hide) continue; + linePen.setWidth(2); + linePen.setColor(Qt::transparent); + painter.setPen(linePen); + for (auto data : *graphSeries.data) { + if (data.orginalPoint->iteration < minIter) { + continue; + } + if (data.orginalPoint->iteration > maxIter) { + break; + } + QColor color = data.color; + color.setAlphaF(this->transparentAlphaValue); + linePen.setColor(Qt::transparent); + painter.setPen(linePen); + painter.setBrush(color); + if (window.inBound(data.toQPointF())) { + painter.drawEllipse(transformGraphToView(data.toQPointF(), stregth_factorX, stregth_factorY, translation), circleSize, circleSize); + } + } + painter.setBrush(Qt::BrushStyle::NoBrush); + } +} + + + + + +void Bitfield::searchForPointUnderCursor() +{ + //check if mouse stayed still + QPoint globalPosition = QCursor::pos(); + QPointF pos = this->mapFromGlobal(globalPosition); + + if (pos != lastPosition){ + return; + } + if (seriesVec.empty() || seriesVec[0].data->empty()) { + return; + } + QRect graphDisplayRect = getDisplayRect(); + QPointF translation(graphDisplayRect.left(), graphDisplayRect.top()); + double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin); + double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin); + + const GraphDataPoint* min = &seriesVec[0].data->at(0); + double minSqaredDistance = sqaredDistance(transformGraphToView(min->toQPointF(), stregth_factorX, stregth_factorY, translation), pos); + std::vector<GraphPlottSeries>::iterator actualBestSeries = seriesVec.begin(); + for (auto seriesIter = seriesVec.begin(); seriesIter != seriesVec.end(); seriesIter++) { + for (const GraphDataPoint& point : *seriesIter->data) { + if (point.orginalPoint->iteration < minIter) { + continue; + } + if (point.orginalPoint->iteration > maxIter) { + break; + } + double distance = sqaredDistance(transformGraphToView(point.toQPointF(), stregth_factorX, stregth_factorY, translation), pos); + if (distance < minSqaredDistance) { + minSqaredDistance = distance; + min = &point; + actualBestSeries = seriesIter; + } + } + } + //if curser is radius + 3pixel away + if (minSqaredDistance <= circleSize * circleSize + 9) { + QPointF pointInWidget = transformGraphToView(min->toQPointF(), stregth_factorX, stregth_factorY, translation); + QToolTip::showText(this->mapToGlobal(QPoint(pointInWidget.x(), pointInWidget.y())) - QPoint(0, 35), QString::fromStdString(min->orginalPoint->bitstringToStdString())); + } +} + +void Bitfield::addPointsInWindowToScratchPad(VisibleWindow& window) +{ + if (!pad) { + qDebug() << "NoPad"; + return; + } + for (auto seriesIter = seriesVec.begin(); seriesIter != seriesVec.end(); seriesIter++) { + if (seriesIter->hide) continue; + for (const GraphDataPoint& point : *seriesIter->data) { + if (point.orginalPoint->iteration < minIter) { + continue; + } + if (point.orginalPoint->iteration > maxIter) { + break; + } + if (window.inBound(point.toQPointF()) && point.existLink()) { + pad->addPoint(*point.orginalPoint); + } + } + } +} + + diff --git a/metavis/Bitfield.h b/metavis/Bitfield.h new file mode 100644 index 0000000000000000000000000000000000000000..b2c957f25f04bd64fd0de9788fb5df3bf0f73f0a --- /dev/null +++ b/metavis/Bitfield.h @@ -0,0 +1,20 @@ +#pragma once +#include "SearchSpacePlott.h" + +class Bitfield : public SearchSpacePlott +{ + Q_OBJECT + +public: + unsigned int maxAmountOfSetBits = 5; + Bitfield(QWidget *parent); + ~Bitfield(); + void frameGraphInView() override; +protected: + void drawData(QPainter& painter) override; + void searchForPointUnderCursor() override; + void addPointsInWindowToScratchPad(VisibleWindow& window) override; +private: + + +}; diff --git a/metavis/BitfieldControlPanel.cpp b/metavis/BitfieldControlPanel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b704d5543c59c50a56f7ed566397a718341c048c --- /dev/null +++ b/metavis/BitfieldControlPanel.cpp @@ -0,0 +1,88 @@ +#include "pch.h" +#include "BitfieldControlPanel.h" +#include <QSettings> + +BitfieldControlPanel::BitfieldControlPanel(QWidget *parent) + : QWidget(parent) +{ + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + + layout->addWidget(field); + field->setAxisLegend("Position", "#SetBits"); + showOptionsButton = new QPushButton("Hide Options"); + layout->addWidget(showOptionsButton); + connect(showOptionsButton, &QPushButton::pressed, this, &BitfieldControlPanel::toggleOptions); + layout->addWidget(slider); + slider->setTitle("Iteration:"); + + //Connect slider with field + connect(slider, &RangeSlider::maxChanged, field, &Bitfield::setMaximumIterationToDispaly); + connect(slider, &RangeSlider::minChanged, field, &Bitfield::setMinimumIterationToDispaly); + connect(gradient, &ColorGradient::gradientChanged, this, &BitfieldControlPanel::updateDotColorsFromDisplayedRun); + + layout->addWidget(gradient); + field->frameGraphInView(); + + //Settings + QSettings settings("settings.ini", QSettings::IniFormat, this); + settings.beginGroup("BitField"); + if (settings.value("hideOptions", true).toBool()) { + toggleOptions(); + } + settings.endGroup(); +} + +BitfieldControlPanel::~BitfieldControlPanel() +{ + //Settings + QSettings settings("settings.ini", QSettings::IniFormat, this); + settings.beginGroup("BitField"); + settings.setValue("hideOptions",this->isOptionHidden); + settings.endGroup(); +} + +void BitfieldControlPanel::updateDotColorsFromDisplayedRun() +{ + if (!displayedRun || displayedRun->dotsForBitField.empty() || !displayedRun->dotsForBitField.back().existLink()) { + return; + } + for (GraphDataPoint& dot : displayedRun->dotsForBitField) { + dot.color = gradient->getColorFromValue(dot.orginalPoint->objectiveFunction); + } + field->update(); +} + +void BitfieldControlPanel::displaySingleRun(SingleRun* run) +{ + displayedRun = run; + updateDotColorsFromDisplayedRun(); + field->removeAll(); + field->addSeries(&run->dotsForBitField, run->name, "", QColor(Qt::black), GraphPlottSeries::SeriesType::Dot); + if (!run->dotsForBitField.empty() && run->dotsForBitField.back().existLink()) { + field->maxAmountOfSetBits = run->dotsForBitField.back().orginalPoint->bitVec.size(); + if (run->dotsForBitField.front().orginalPoint->iteration < slider->getMinRange()) { + slider->setMinRange(run->dotsForBitField.front().orginalPoint->iteration); + } + if (run->dotsForBitField.back().orginalPoint->iteration > slider->getMaxRange()) { + slider->setMaxRange(run->dotsForBitField.back().orginalPoint->iteration); + } + + } + + field->frameGraphInView(); +} + +void BitfieldControlPanel::clearRun() +{ + displayedRun = nullptr; + field->removeAll(); +} + +void BitfieldControlPanel::toggleOptions() +{ + this->isOptionHidden = !this->isOptionHidden; + showOptionsButton->setText(this->isOptionHidden ? "More Options" : "Hide Options"); + this->slider->setHidden(this->isOptionHidden); + this->gradient->setHidden(this->isOptionHidden); +} diff --git a/metavis/BitfieldControlPanel.h b/metavis/BitfieldControlPanel.h new file mode 100644 index 0000000000000000000000000000000000000000..cdc8bf5b355cb3c158eb3f187feb80b3c49b6816 --- /dev/null +++ b/metavis/BitfieldControlPanel.h @@ -0,0 +1,29 @@ +#pragma once +#include <QWidget> +#include <QPushButton> +#include "Bitfield.h" +#include "RangeSlider.h" +#include "ColorGradient.h" +#include "RunData.h" + + +class BitfieldControlPanel : public QWidget +{ + Q_OBJECT + +public: + BitfieldControlPanel(QWidget *parent); + ~BitfieldControlPanel(); + void updateDotColorsFromDisplayedRun(); + void displaySingleRun(SingleRun* run); + void clearRun(); + Bitfield* field = new Bitfield(this); +protected: + RangeSlider* slider = new RangeSlider(this); + ColorGradient* gradient = new ColorGradient(this); + bool isOptionHidden = false; + void toggleOptions(); +private: + QPushButton* showOptionsButton; + SingleRun* displayedRun = nullptr; +}; diff --git a/metavis/ColorButton.cpp b/metavis/ColorButton.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f80d2ccdf4ada2441035a587dead76a89891eb7 --- /dev/null +++ b/metavis/ColorButton.cpp @@ -0,0 +1,57 @@ +#include "pch.h" +#include "ColorButton.h" +#include <QColorDialog> +#include <QPixmap> + +ColorButton::ColorButton(QWidget *parent, QColor color) + : QPushButton(parent), color(color) +{ + //setText("Set Color"); + + //setContentsMargins(0, 0, 0, 0); + //this->setStyleSheet(this->styleSheet() + "border: 1px solid white; background-color: red"); + updateVisual(); + setToolTip("Set Color"); + connect(this, &QPushButton::pressed, this, &ColorButton::openColorMenu); +} + +ColorButton::~ColorButton() +{ +} + +QColor ColorButton::getColor() +{ + return color; +} + +void ColorButton::updateVisual() +{ + QPixmap pixmap(100, 14); + pixmap.fill(color); + QIcon redIcon(pixmap); + setIcon(QIcon(redIcon)); + setIconSize(QSize(100, 14)); +} + +void ColorButton::resizeEvent(QResizeEvent* event) +{ +} + +void ColorButton::openColorMenu() +{ + QColor color = QColorDialog::getColor(this->color, this, "Set Color", QColorDialog::ShowAlphaChannel); + if (!color.isValid()) return; + this->color = color; + updateVisual(); + emit colorChanged(this->color); +} + +void ColorButton::setColor(QColor color) +{ + this->color = color; + updateVisual(); +} + + + + diff --git a/metavis/ColorButton.h b/metavis/ColorButton.h new file mode 100644 index 0000000000000000000000000000000000000000..f618019dda50a1631034f2252f91b7b9c270601d --- /dev/null +++ b/metavis/ColorButton.h @@ -0,0 +1,23 @@ +#pragma once + +#include <QPushButton> +#include <QColor> +class ColorButton : public QPushButton +{ + Q_OBJECT + + +public: + ColorButton(QWidget *parent, QColor color); + ~ColorButton(); + QColor getColor(); +private: + QColor color; + void updateVisual(); + void resizeEvent(QResizeEvent* event) override; + void openColorMenu(); +public slots: + void setColor(QColor color); +signals: + void colorChanged(QColor color); +}; diff --git a/metavis/ColorGradient.cpp b/metavis/ColorGradient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d7dd6acc8953b6dc1b266f5110ea8093a293a4b --- /dev/null +++ b/metavis/ColorGradient.cpp @@ -0,0 +1,424 @@ +#include "pch.h" +#include <QLayout> +#include <QDoubleValidator> +#include "ColorGradient.h" +#include "util.h" +#define LeftRightGap 10 +#define ColorExampleStart 30 +#define ColorExmapleHeight 80 +#define SliderBeginHeight 83 +#define SliderBody 14 +#define SliderPeak 6 +#define SelectedPreview 20 +#define SelectedHeight 115 +ColorGradient::ColorGradient(QWidget *parent) + : QWidget(parent) +{ + interpolationCombobox = new QComboBox(this); + interpolationCombobox->addItem("RGB"); + interpolationCombobox->addItem("HLS"); + connect(interpolationCombobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + this, [this](int index) {this->setInterpolationMode((ColorGradient::ColorInterpolationMode)index); }); + modeCombobox = new QComboBox(this); + modeCombobox->addItem("Interpolate"); + modeCombobox->addItem("Discrete"); + connect(modeCombobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + this, [this](int index) {this->setMode((ColorGradient::ColorMode)index);}); + colorButton = new ColorButton(this, QColor(0,0,0)); + + connect(colorButton, &ColorButton::colorChanged, this, [this](QColor color) { + if (selected != nullptr) setColorPointColor(*selected, color); + }); + QVBoxLayout* vBoxLayout = new QVBoxLayout(this); + vBoxLayout->setContentsMargins(0,0,0,0); + QGridLayout* layoutTop = new QGridLayout(); + vBoxLayout->addLayout(layoutTop); + layoutTop->setContentsMargins(0, 0, 0, 0); + layoutTop->setAlignment(Qt::AlignTop); + spacerH = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + layoutTop->addWidget(new QLabel("ObjectiveFunction-Gradient:", this), 0, 0); + layoutTop->addItem(spacerH, 0, 1); + + layoutTop->addWidget(modeCombobox, 0, 6); + layoutTop->addWidget(interpolationCombobox, 0, 7); + QGridLayout* layoutBottom = new QGridLayout(); + vBoxLayout->addLayout(layoutBottom); + layoutBottom->setContentsMargins(0, 0, 0, 0); + layoutBottom->setAlignment(Qt::AlignBottom); + layoutBottom->addItem(new QSpacerItem(10, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 0); + colorLabel = new QLabel("Color:"); + layoutBottom->addWidget(colorLabel, 0, 1); + layoutBottom->addWidget(colorButton, 0, 2); + alphaLabel = new QLabel("Alpha:"); + layoutBottom->addWidget(alphaLabel, 0, 3); + + + + layoutTop->addWidget(new QLabel("ValueRange:"), 0, 2); + minEdit = new QLineEdit(); + minEdit->setMaximumWidth(80); + layoutTop->addWidget(minEdit, 0, 3); + layoutTop->addWidget(new QLabel(":"), 0, 4); + maxEdit = new QLineEdit(); + maxEdit->setMaximumWidth(80); + layoutTop->addWidget(maxEdit, 0, 5); + valueLabel = new QLabel("Value:"); + layoutBottom->addWidget(valueLabel, 0,5); + + + QRegExp rxMinMax("[+-]?[0-9]*(\\.[0-9]{0,6})?"); + QRegExpValidator* validatorMinMax = new QRegExpValidator(rxMinMax, 0); + minEdit->setValidator(validatorMinMax); + maxEdit->setValidator(validatorMinMax); + minEdit->setText(QString::number(minValue)); + maxEdit->setText(QString::number(maxValue)); + connect(minEdit, &QLineEdit::editingFinished, this, &ColorGradient::minEditChangeEvent); + connect(maxEdit, &QLineEdit::editingFinished, this, &ColorGradient::maxEditChangeEvent); + + alphaLineEdit = new QLineEdit(this); + alphaLineEdit->setMaximumWidth(80); + layoutBottom->addWidget(alphaLineEdit, 0, 4); + + valueEdit = new QLineEdit(); + valueEdit->setMaximumWidth(80); + valueEdit->setValidator(validatorMinMax); + connect(valueEdit, &QLineEdit::editingFinished, this, &ColorGradient::setValueOfSelected); + layoutBottom->addWidget(valueEdit, 0, 6); + + QRegExp rx("0(\\.[0-9]{0,6})?|1(\\.0*)?"); + QRegExpValidator* v = new QRegExpValidator(rx, 0); + alphaLineEdit->setValidator(v); + connect(alphaLineEdit, &QLineEdit::editingFinished, this, &ColorGradient::setAlphaOfSelected); + + + layoutBottom->addItem(new QSpacerItem(10, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 7); + + //this->setMinimumHeight(150); + this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + this->setMinimumHeight(150); + //Dummy Data + //TODO(ColorGradient #1) get gradient from last time or save project + pointList.push_back(ColorPoint(0.0, QColor(0, 100, 200))); + pointList.push_back(ColorPoint(0.5, QColor(0, 100, 50))); + pointList.push_back(ColorPoint(1.0, QColor(200, 10, 10))); + //select first + selectColorPoint(&pointList.front()); +} + +ColorGradient::~ColorGradient() +{ + +} + +QColor ColorGradient::getColorFromAlpha(double alpha) +{ + alpha = std::clamp(alpha, 0.0, 1.0); + switch(mode) { + case ColorMode::Discrete: { + QColor result(255,255,255); + for (ColorPoint& point : pointList) { + if (point.alpha >= alpha) { + result = point.color; + break; + } + } + return result; + } + case ColorMode::Interpolate: { + ColorPoint before(0.0, QColor(255, 255, 255)); + ColorPoint last(1.0, QColor(255, 255, 255)); + ColorPoint* first = &before; + ColorPoint* second = &last; + for (ColorPoint& point : pointList) { + if (point.alpha >= alpha) { + second = &point; + break; + } + first = &point; + } + double alphaBetween = util::inverseLinearInterpolation(first->alpha, second->alpha, alpha); + switch (interpolationMode) { + case ColorInterpolationMode::HSL: + return util::interpolateHSL(first->color, second->color, alphaBetween); + case ColorInterpolationMode::RGB: + default: + return util::interpolateRGB(first->color, second->color, alphaBetween); + } + + } + default: { + return QColor(0, 0, 0); + } + } + +} + +QColor ColorGradient::getColorFromValue(double value) +{ + double alpha = util::inverseLinearInterpolation(minValue, maxValue, value); + return getColorFromAlpha(alpha); +} + +void ColorGradient::addColorPoint(double alpha) +{ + auto iter = pointList.begin(); + for (; iter != pointList.end(); iter++) { + if (iter->alpha >= alpha) { + break; + } + } + selectColorPoint(&*pointList.insert(iter, ColorPoint(alpha, getColorFromAlpha(alpha)))); + emit gradientChanged(); +} + +void ColorGradient::removeColorPoint(ColorPoint& pointToRemove) +{ + pointList.erase(std::remove_if(pointList.begin(), pointList.end(), + [&pointToRemove](ColorPoint& point) {return &point == &pointToRemove; })); + //colorButton->setVisible(false); + selected = nullptr; + hideSelectionEdit(true); + emit gradientChanged(); +} + +void ColorGradient::setColorPointAlpha(ColorPoint& point, double alpha) +{ + alpha = std::clamp(alpha, 0.0, 1.0); + if (alpha == point.alpha) return; + point.alpha = alpha; + pointList.sort([](ColorPoint& a, ColorPoint& b) { + return a.alpha < b.alpha; + }); + if (&point == selected) { + alphaLineEdit->setText(QString::number(alpha)); + valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, alpha))); + } + emit gradientChanged(); + update(); +} + +void ColorGradient::setColorPointColor(ColorPoint& point, QColor newColor) +{ + if (newColor == point.color) return; + point.color = newColor; + emit gradientChanged(); + update(); +} + +void ColorGradient::setMode(ColorMode mode) +{ + this->mode = mode; + update(); + emit gradientChanged(); + interpolationCombobox->setEnabled(mode != ColorGradient::ColorMode::Discrete); +} + +void ColorGradient::setInterpolationMode(ColorInterpolationMode mode) +{ + this->interpolationMode = mode; + update(); + emit gradientChanged(); +} + +void ColorGradient::setMinValue(double value) +{ + if (value > maxValue) { + minValue = maxValue; + maxValue = value; + minEdit->setText(QString::number(minValue)); + maxEdit->setText(QString::number(maxValue)); + } + else { + minValue = value; + minEdit->setText(QString::number(minValue)); + } + if (selected != nullptr) { + valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, selected->alpha))); + } + emit gradientChanged(); +} + +void ColorGradient::setMaxValue(double value) +{ + if (value < minValue) { + maxValue = minValue; + minValue = value; + minEdit->setText(QString::number(minValue)); + maxEdit->setText(QString::number(maxValue)); + } + else { + maxValue = value; + maxEdit->setText(QString::number(maxValue)); + } + if (selected != nullptr) { + valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, selected->alpha))); + } + emit gradientChanged(); +} + +void ColorGradient::hideSelectionEdit(bool value) +{ + colorButton->setHidden(value); + alphaLineEdit->setHidden(value); + alphaLabel->setHidden(value); + colorLabel->setHidden(value); + valueEdit->setHidden(value); + valueLabel->setHidden(value); +} + +void ColorGradient::setAlphaOfSelected() +{ + if (selected == nullptr) return; + bool success = false; + double alpha = alphaLineEdit->text().toDouble(&success); + if (success) { + setColorPointAlpha(*selected, alpha); + } + +} + +void ColorGradient::setValueOfSelected() +{ + if (selected == nullptr) return; + bool success = false; + double value = valueEdit->text().toDouble(&success); + + if (success) { + if (value < minValue) { + value = minValue; + } + else if (value > maxValue) { + value = maxValue; + } + setColorPointAlpha(*selected, util::inverseLinearInterpolation(minValue, maxValue, value)); + } +} + +void ColorGradient::selectColorPoint(ColorPoint* point) +{ + selected = point; + colorButton->setColor(selected->color); + alphaLineEdit->setText(QString::number(selected->alpha)); + valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, selected->alpha))); + hideSelectionEdit(false); +} + +void ColorGradient::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing); + QRect colorExampleRect(QPoint(rect().left() + LeftRightGap, ColorExampleStart), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight)); + painter.drawRect(colorExampleRect); + //drawSliders + QPen sliderPen; + sliderPen.setWidth(2); + painter.setPen(Qt::NoPen); + + + for (ColorPoint& point : pointList) { + QPainterPath slider; + if (&point == selected) { + sliderPen.setColor(QColor(0, 0, 0)); + painter.setPen(sliderPen); + } + painter.setBrush(point.color); + int middlePosition = util::linearInterpolate(colorExampleRect.left(), colorExampleRect.right(), point.alpha); + slider.moveTo(middlePosition, SliderBeginHeight); + slider.lineTo(middlePosition - SliderPeak, SliderBeginHeight + SliderPeak); + slider.lineTo(middlePosition - SliderPeak, SliderBeginHeight + SliderPeak + SliderBody); + slider.lineTo(middlePosition + SliderPeak, SliderBeginHeight + SliderPeak + SliderBody); + slider.lineTo(middlePosition + SliderPeak, SliderBeginHeight + SliderPeak); + slider.closeSubpath(); + painter.drawPath(slider); + if (&point == selected) { + painter.setPen(Qt::NoPen); + } + } + for (int pixelX = colorExampleRect.left() + 1; pixelX < colorExampleRect.right()+1; pixelX++) { + double alpha = util::inverseLinearInterpolation(colorExampleRect.left(), colorExampleRect.right()+1, pixelX); + QColor color = getColorFromAlpha(alpha); + painter.setPen(color); + painter.drawLine(pixelX, colorExampleRect.top() + 1, pixelX, colorExampleRect.bottom()); + } + + + + + +} + +void ColorGradient::mousePressEvent(QMouseEvent* event) +{ + if (childAt(event->pos()) != nullptr) { + return; + } + QPoint mousePos = event->pos(); + QRect area(QPoint(rect().left() + LeftRightGap, ColorExmapleHeight), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight+SliderBody+SliderPeak)); + QRect responisveArea(QPoint(area.left() - SliderPeak * 2, area.top()), QPoint(area.right() + SliderPeak * 2, area.bottom())); + if (responisveArea.contains(mousePos)) { + + // Check for existing color point + // Select with left mouse Button + // Remove Point with right mouse Button + for (ColorPoint& point : pointList) { + double posX = util::linearInterpolate(area.left(), area.right() + 1, point.alpha); + if (posX - SliderPeak * 2 < mousePos.x() && mousePos.x() < posX + SliderPeak * 2) { + if (event->buttons() & Qt::LeftButton) { + selectColorPoint(&point); + } + else if(event->buttons() & Qt::RightButton){ + removeColorPoint(point); + + } + update(); + return; + } + } + + + // Insert new Color Point + if (event->buttons() & Qt::LeftButton) { + double alpha = util::inverseLinearInterpolation(area.left(), area.right() + 1, mousePos.x()); + addColorPoint(alpha); + + update(); + } + } +} + +void ColorGradient::mouseMoveEvent(QMouseEvent* event) +{ + if (selected == nullptr) return; + if (childAt(event->pos()) != nullptr) { + return; + } + //TODO(Bug #1) when mouse press button mouse move gets emitted and move the selected + //currently a workarround to not move the selected colorpoint when selecting a button + //if (event->pos().y() < ColorExmapleHeight) return; + + QPoint mousePos = event->pos(); + QRect area(QPoint(rect().left() + LeftRightGap, ColorExmapleHeight), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight + SliderBody + SliderPeak)); + double alpha = util::inverseLinearInterpolation(area.left(), area.right() + 1, mousePos.x()); + this->setColorPointAlpha(*selected, alpha); + + +} + +void ColorGradient::minEditChangeEvent() +{ + bool success = false; + double value = minEdit->text().toDouble(&success); + if (success) { + this->setMinValue(value); + } +} + +void ColorGradient::maxEditChangeEvent() +{ + bool success = false; + double value = maxEdit->text().toDouble(&success); + if (success) { + this->setMaxValue(value); + } +} diff --git a/metavis/ColorGradient.h b/metavis/ColorGradient.h new file mode 100644 index 0000000000000000000000000000000000000000..599fa1431a9cc36b526abde2fbc4107e1eaade70 --- /dev/null +++ b/metavis/ColorGradient.h @@ -0,0 +1,68 @@ +#pragma once + +#include <QWidget> +#include <QColor> +#include <QComboBox> +#include <list> +#include <QLineEdit> +#include "ColorButton.h" + +struct ColorPoint { + double alpha; + QColor color; + ColorPoint(double alpha, QColor color) : alpha(std::clamp(alpha, 0.0, 1.0)), color(color) {} +}; + + +class ColorGradient : public QWidget +{ + Q_OBJECT + +public: + enum ColorInterpolationMode{ RGB, HSL}; + enum ColorMode{ Interpolate, Discrete}; + ColorGradient(QWidget *parent); + ~ColorGradient(); + QColor getColorFromAlpha(double alpha); + QColor getColorFromValue(double value); + void addColorPoint(double alpha); + void removeColorPoint(ColorPoint& point); + void setColorPointAlpha(ColorPoint& point, double newAlpha); + void setColorPointColor(ColorPoint& point, QColor newColor); + void setMode(ColorMode mode); + void setInterpolationMode(ColorInterpolationMode mode); + void setMinValue(double value); + void setMaxValue(double value); +private: + double minValue = 0, maxValue = 100; + ColorMode mode = ColorMode::Interpolate; + ColorInterpolationMode interpolationMode = ColorInterpolationMode::RGB; + std::list<ColorPoint> pointList; + //UI + QLineEdit* minEdit; + QLineEdit* maxEdit; + ColorPoint* selected = nullptr; + QComboBox* interpolationCombobox = nullptr; + QComboBox* modeCombobox = nullptr; + QSpacerItem* spacerH = nullptr; + QLineEdit* alphaLineEdit = nullptr; + ColorButton* colorButton = nullptr; + QLineEdit* valueEdit; + QLabel* colorLabel; + QLabel* alphaLabel; + QLabel* valueLabel; + void hideSelectionEdit(bool value); + void setAlphaOfSelected(); + void setValueOfSelected(); + void selectColorPoint(ColorPoint* point); + void paintEvent(QPaintEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + + void minEditChangeEvent(); + void maxEditChangeEvent(); + + +signals: + void gradientChanged(); +}; diff --git a/metavis/Concurrent.cpp b/metavis/Concurrent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..15d10f9021502020261caa5bac63e0505211424e --- /dev/null +++ b/metavis/Concurrent.cpp @@ -0,0 +1,65 @@ +#include "pch.h" +#include "Concurrent.h" +#include <QDebug> + +Concurrent::Concurrent() +{ +} + +Concurrent::~Concurrent() +{ + reset(); +} + +void Concurrent::start() +{ + qDebug() << "Hello Start"; + if (thread != nullptr) reset(); + qDebug() << "Start after reset"; + thread = new std::thread(&Concurrent::run, this); + qDebug() << "Start end"; +} + +void Concurrent::pause() +{ + paused = !paused; + cv.notify_one(); +} + +void Concurrent::reset() +{ + qDebug() << "Hello Reset"; + cancel = true; + paused = false; + cv.notify_one(); + if (thread != nullptr) { + thread->join(); + delete thread; + thread = nullptr; + } + cancel = false; +} + + + +void Concurrent::run() +{ + for (;false;) { + checkPaused(); + if (checkCancel()) { break; } + } +} + +bool Concurrent::checkCancel() +{ + return cancel; +} + +void Concurrent::checkPaused() +{ + std::unique_lock<std::mutex> lck(mutex); + while (paused) { + qDebug() << "while"; + cv.wait(lck); + } +} diff --git a/metavis/Concurrent.h b/metavis/Concurrent.h new file mode 100644 index 0000000000000000000000000000000000000000..c57c00ddb244010e2fea7e4625dc3f0989b06f65 --- /dev/null +++ b/metavis/Concurrent.h @@ -0,0 +1,28 @@ +#pragma once +#include <thread> +#include <mutex> +#include <condition_variable> + +class Concurrent +{ +public: + Concurrent(); + ~Concurrent(); + void start(); + void pause(); + void reset(); + +protected: + bool checkCancel(); + void checkPaused(); + + +private: + bool paused = false; + bool cancel = false; + std::thread* thread = nullptr; + std::mutex mutex; + std::condition_variable cv; + void virtual run(); +}; + diff --git a/metavis/DockableGraphView.cpp b/metavis/DockableGraphView.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b65ba9943d8d804bf0ded9a50eb4096f0d01c94 --- /dev/null +++ b/metavis/DockableGraphView.cpp @@ -0,0 +1,39 @@ +#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() +{ +} diff --git a/metavis/DockableGraphView.h b/metavis/DockableGraphView.h new file mode 100644 index 0000000000000000000000000000000000000000..7bcad2adc477de93df6b2c5c7ce04fc22d3bc635 --- /dev/null +++ b/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: +}; diff --git a/metavis/GraphPlott.cpp b/metavis/GraphPlott.cpp new file mode 100644 index 0000000000000000000000000000000000000000..affe879a0894770c506e8b5819f96d710ecaff74 --- /dev/null +++ b/metavis/GraphPlott.cpp @@ -0,0 +1,358 @@ +#include "pch.h" +#include "GraphPlott.h" +#include <QDebug> +#include <QPen> +#include <QPushButton> +#include <QLayout> +#include <QHBoxLayout> +#include <QVBoxLayout> +#include "HoverButton.h" +#include <QApplication> +#include <QDesktopWidget> + + +GraphPlott::GraphPlott(QWidget* parent, bool showSettingsButton, bool showInformationButton, bool showGridButton) + : Plott(parent) +{ + QVBoxLayout* layoutOuter = new QVBoxLayout(this); + buttonPanel = new QHBoxLayout(); + dialog = new GraphViewSettingDialog(this, "Settings"); + this->setMouseTracking(true); + + + buttonPanel->insertStretch(0); + displayLabel = new QLabel(""); + buttonPanel->addWidget(displayLabel); + + HoverButton* frameButton = new HoverButton(); + frameButton->setMinimumSize(20, 20); + frameButton->setMaximumSize(20, 20); + frameButton->setIcon(QIcon(":/metavis/Resources/frame.svg")); + frameButton->setHoveredIcon(QIcon(":/metavis/Resources/frame_hovered.svg")); + frameButton->setAttribute(Qt::WA_TranslucentBackground); + frameButton->setStyleSheet(frameButton->styleSheet() + "border: none;"); + frameButton->setToolTip("Frame"); + buttonPanel->addWidget(frameButton); + connect(frameButton, &QPushButton::released, this, &GraphPlott::frameGraphInView); + + + if (showGridButton) { + HoverButton* gridButton = new HoverButton(); + gridButton->setMinimumSize(20, 20); + gridButton->setMaximumSize(20, 20); + gridButton->setIcon(QIcon(":/metavis/Resources/gridIcon.svg")); + gridButton->setHoveredIcon(QIcon(":/metavis/Resources/gridIcon_hovered.svg")); + gridButton->setAttribute(Qt::WA_TranslucentBackground); + gridButton->setStyleSheet(gridButton->styleSheet() + "border: none;"); + gridButton->setToolTip("Toggle Grid"); + buttonPanel->addWidget(gridButton); + connect(gridButton, &QPushButton::released, this, [this]() {this->enableGrid = !this->enableGrid; update(); }); + } + if (showInformationButton) { + + informationButton->setMinimumSize(20, 20); + informationButton->setMaximumSize(20, 20); + informationButton->setIcon(QIcon(":/metavis/Resources/information_icon.svg")); + informationButton->setHoveredIcon(QIcon(":/metavis/Resources/information_icon_hovered.svg")); + informationButton->setAttribute(Qt::WA_TranslucentBackground); + informationButton->setStyleSheet(informationButton->styleSheet() + "border: none;"); + informationButton->setToolTip("Information"); + connect(informationButton, &QPushButton::released, this, &GraphPlott::showInformation); + buttonPanel->addWidget(informationButton); + } + + if (showSettingsButton) { + HoverButton* settingsButton = new HoverButton(); + settingsButton->setMinimumSize(20, 20); + settingsButton->setMaximumSize(20, 20); + settingsButton->setIcon(QIcon(":/metavis/Resources/settingIcon.svg")); + settingsButton->setHoveredIcon(QIcon(":/metavis/Resources/settingIcon_hovered.svg")); + settingsButton->setAttribute(Qt::WA_TranslucentBackground); + settingsButton->setStyleSheet(settingsButton->styleSheet() + "border: none;"); + settingsButton->setToolTip("Settings"); + buttonPanel->addWidget(settingsButton); + connect(settingsButton, &QPushButton::released, dialog, &GraphViewSettingDialog::openDialog); + } + + + + + buttonPanel->setSpacing(0); + buttonPanel->setContentsMargins(0, 0, 0, 0); + layoutOuter->addLayout(buttonPanel); + layoutOuter->insertStretch(1); + layoutOuter->setContentsMargins(0, 0, 0, 0); + + //QMenu + selectMenu->addAction(displayAreaAction); + selectMenu->addAction(scratchpadAction); + displayAreaAction->setShortcut(QKeySequence(Qt::CTRL)); + scratchpadAction->setShortcut(QKeySequence(Qt::ALT)); + scratchpadAction->setEnabled(false); + //setUpTimer + timer->setSingleShot(true); + connect(timer, &QTimer::timeout, this, &GraphPlott::searchForPointUnderCursor); + +} + +GraphPlott::~GraphPlott() +{ +} + +void GraphPlott::addSeries(std::vector<GraphDataPoint>* line, std::string runName, QString description, QColor color, GraphPlottSeries::SeriesType type) +{ + if (line->empty()) { + qDebug() << "Series is empty!"; + return; + } + GraphPlottSeries lgs; + lgs.runName = runName; + lgs.description = description; + lgs.data = line; + lgs.type = type; + lgs.color = color; + //set min max x y + auto pairX = std::minmax_element(lgs.data->begin(), lgs.data->end(), [](const GraphDataPoint& a, const GraphDataPoint& b) -> bool {return a.x < b.x; }); + lgs.xMin = pairX.first->x; + lgs.xMax = pairX.second->x; + auto pairY = std::minmax_element(lgs.data->begin(), lgs.data->end(), [](const GraphDataPoint& a, const GraphDataPoint& b) -> bool {return a.y < b.y; }); + lgs.yMin = pairY.first->y; + lgs.yMax = pairY.second->y; + seriesVec.push_back(lgs); +} + + + + +void GraphPlott::removeRunData(RunData* run) +{ + //Remove all series with this rundata + auto iter = std::remove_if(seriesVec.begin(), seriesVec.end(), [run](GraphPlottSeries& series) {return (run->name == series.runName); }); + seriesVec.erase(iter, seriesVec.end()); + displayLabel->setText(""); +} + +void GraphPlott::removeAll() +{ + seriesVec.clear(); +} + +void GraphPlott::setInformation(QString information) +{ + this->informationButtonString = information; + popupWidget->setInformation(information); +} + +std::vector<GraphPlottSeries>& GraphPlott::getSeriesVector() +{ + return seriesVec; +} + +void GraphPlott::resetToDefaultWindow() +{ + frameGraphInView(); +} + +void GraphPlott::frameGraphInView() +{ + if(seriesVec.empty()) return; + window.xMin = std::min_element(std::begin(seriesVec), std::end(seriesVec), [](const GraphPlottSeries& a, const GraphPlottSeries& b) -> bool {return a.xMin < b.xMin; })->xMin; + window.xMax = std::max_element(std::begin(seriesVec), std::end(seriesVec), [](const GraphPlottSeries& a, const GraphPlottSeries& b) -> bool {return a.xMax < b.xMax; })->xMax; + window.yMin = std::min_element(std::begin(seriesVec), std::end(seriesVec), [](const GraphPlottSeries& a, const GraphPlottSeries& b) -> bool {return a.yMin < b.yMin; })->yMin; + window.yMax = std::max_element(std::begin(seriesVec), std::end(seriesVec), [](const GraphPlottSeries& a, const GraphPlottSeries& b) -> bool {return a.yMax < b.yMax; })->yMax; + window.ZoomOut(0.05); + update(); +} + +void GraphPlott::setDisplayLabel(QString file) +{ + displayLabel->setText("\"" + file + "\" "); +} + +void GraphPlott::setScratchpad(Scratchpad* pad) +{ + this->pad = pad; + scratchpadAction->setEnabled(true); +} + + +void GraphPlott::showInformation() +{ + QPoint position = QCursor::pos(); + popupWidget->setInformation(informationButtonString + graphplottInformation); + popupWidget->width(); + QRect screen = QApplication::desktop()->screenGeometry(); + if (position.x() + popupWidget->width() > screen.width()) { + popupWidget->move(screen.width() - popupWidget->width(), position.y()); + } + else { + popupWidget->move(position.x(), position.y()); + + } + popupWidget->show(); + +} + +void GraphPlott::drawData(QPainter& painter) +{ + QPen linePen; + QRect graphDisplayRect = getDisplayRect(); + QPointF translation(graphDisplayRect.left(), graphDisplayRect.top()); + double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin); + double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin); + + for (const GraphPlottSeries& graphSeries : seriesVec) { + if (!graphSeries.data) { + qDebug() << "Pointer to nothing pls help"; + } + if (graphSeries.data->empty() || graphSeries.hide) continue; + linePen.setColor(graphSeries.color); + + if (graphSeries.type == GraphPlottSeries::SeriesType::Line || graphSeries.type == GraphPlottSeries::SeriesType::LineDot) { + linePen.setWidth(graphSeries.lineWidth/* + ((&graphSeries == highlightSeries) ? 5 : 0)*/); + painter.setPen(linePen); + QPainterPath painterPath; + + QPointF oldpoint = graphSeries.data->at(0).toQPointF(); + 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(); + bool inBoundOldPoint = window.inBoundX(oldpoint); + bool inBoundNewPoint = window.inBoundX(newpoint); + if (!inBoundOldPoint && !inBoundNewPoint) { + + } + else if (!inBoundOldPoint && inBoundNewPoint) { + painterPath.moveTo(transformGraphToView(oldpoint, stregth_factorX, stregth_factorY, translation)); + painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation)); + } + else if (inBoundOldPoint && inBoundNewPoint) { + painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation)); + } + else if (inBoundOldPoint && !inBoundNewPoint) { + painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation)); + break; + } + oldpoint = newpoint; + } + + //painterPath.translate(translation); + painter.drawPath(painterPath); + } + if (graphSeries.type == GraphPlottSeries::SeriesType::Dot || graphSeries.type == GraphPlottSeries::SeriesType::LineDot) { + linePen.setWidth(2); + linePen.setColor(Qt::transparent); + painter.setPen(linePen); + painter.setBrush(graphSeries.color); + const bool useColor = graphSeries.useDataPointColor; + for (auto data : *graphSeries.data) { + if (useColor){ + linePen.setColor(data.color); + painter.setPen(linePen); + painter.setBrush(data.color); + } + if (window.inBound(data.toQPointF())) { + painter.drawEllipse(transformGraphToView(data.toQPointF(), stregth_factorX, stregth_factorY, translation), graphSeries.circleRadius, graphSeries.circleRadius); + } + } + painter.setBrush(Qt::BrushStyle::NoBrush); + } + } +} + +void GraphPlott::mouseMoveEvent(QMouseEvent* event) +{ + Plott::mouseMoveEvent(event); + lastPosition = event->pos(); + timer->start(stayStillTimeMS); + QToolTip::hideText(); +} + +void GraphPlott::handleSelectedWindow(VisibleWindow& window, QMouseEvent* event) +{ + if (event->modifiers() & Qt::ControlModifier) { + setWindow(window); + } + else if (event->modifiers() & Qt::AltModifier) { + addPointsInWindowToScratchPad(window); + } + else { + QAction* used = selectMenu->exec(this->mapToGlobal(event->pos())); + if(used == displayAreaAction){ + setWindow(window); + } + else if (used == scratchpadAction) { + addPointsInWindowToScratchPad(window); + } + } + +} + +void GraphPlott::searchForPointUnderCursor() +{ + //check if mouse stayed still + QPoint globalPosition = QCursor::pos(); + QPointF pos = this->mapFromGlobal(globalPosition); + if (pos != lastPosition) { + return; + } + if (seriesVec.empty() || seriesVec[0].data->empty()) { + return; + } + + QRect graphDisplayRect = getDisplayRect(); + QPointF translation(graphDisplayRect.left(), graphDisplayRect.top()); + double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin); + double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin); + + const GraphDataPoint* min = &seriesVec[0].data->at(0); + double minSqaredDistance = sqaredDistance(transformGraphToView(min->toQPointF(), stregth_factorX, stregth_factorY, translation), pos); + std::vector<GraphPlottSeries>::iterator actualBestSeries = seriesVec.begin(); + for (auto seriesIter = seriesVec.begin(); seriesIter != seriesVec.end(); seriesIter++) { + for (const GraphDataPoint& point : *seriesIter->data) { + double distance = sqaredDistance(transformGraphToView(point.toQPointF(), stregth_factorX, stregth_factorY, translation), pos); + if (distance <= minSqaredDistance) { + minSqaredDistance = distance; + min = &point; + actualBestSeries = seriesIter; + } + } + } + if (minSqaredDistance <= 20) { + QPointF pointInWidget = transformGraphToView(min->toQPointF(), stregth_factorX, stregth_factorY, translation); + QToolTip::showText(this->mapToGlobal(QPoint(pointInWidget.x(), pointInWidget.y())) - QPoint(0, 35), actualBestSeries->description); + } +} + +double GraphPlott::sqaredDistance(const QPointF& a, const QPointF& b) +{ + return std::pow(a.x() - b.x(), 2) + std::pow(a.y() - b.y(), 2); +} + +void GraphPlott::addPointsInWindowToScratchPad(VisibleWindow& window) +{ + if (!pad) { + qDebug() << "NoPad"; + return; + } + for (auto seriesIter = seriesVec.begin(); seriesIter != seriesVec.end(); seriesIter++) { + if (seriesIter->hide) continue; + for (const GraphDataPoint& point : *seriesIter->data) { + if (window.inBound(point.toQPointF()) && point.existLink()) { + pad->addPoint(*point.orginalPoint); + } + } + } +} + +void GraphPlott::keyPressEvent(QKeyEvent* event) +{ + Plott::keyPressEvent(event); + switch (event->key()) { + case Qt::Key_T: + frameGraphInView(); + break; + default: + break; + } +} diff --git a/metavis/GraphPlott.h b/metavis/GraphPlott.h new file mode 100644 index 0000000000000000000000000000000000000000..4e7b9c040b6b4518b7d043d1119a6f7b26e5a566 --- /dev/null +++ b/metavis/GraphPlott.h @@ -0,0 +1,79 @@ +#pragma once +#include <vector> + +#include "Plott.h" +#include "RunData.h" +#include "GraphViewSettingDialog.h" +#include <QString> +#include <QLabel> +#include <string> +#include "Scratchpad.h" +#include "HoverButton.h" +#include "InformationPopUp.h" +class GraphViewSettingDialog; + +struct GraphPlottSeries { + std::vector<GraphDataPoint>* data; + double xMin, xMax; + double yMin, yMax; + std::string runName; + + + + //Settings for visual + QString description; + QColor color; + int lineWidth = 2; + double circleRadius = 2.0; + enum class SeriesType { Line, Dot, LineDot }; + bool useDataPointColor = false; + SeriesType type; + bool hide = false; +}; + +class GraphPlott : public Plott +{ + Q_OBJECT + +public: + + GraphPlott(QWidget *parent,bool settingsButton = true, bool informationButton = true, bool gridButton = true); + ~GraphPlott(); + void addSeries(std::vector<GraphDataPoint>* line, std::string runName, QString description, QColor color, GraphPlottSeries::SeriesType type); + void removeRunData(RunData* run); + void removeAll(); + void setInformation(QString information); + std::vector<GraphPlottSeries>& getSeriesVector(); + void virtual resetToDefaultWindow() override; + void virtual frameGraphInView(); + void setDisplayLabel(QString file); + void setScratchpad(Scratchpad* pad); +protected: + QHBoxLayout* buttonPanel; + void showInformation(); + void drawData(QPainter& painter) override; + void mouseMoveEvent(QMouseEvent* event) override; + std::vector<GraphPlottSeries> seriesVec; + virtual void handleSelectedWindow(VisibleWindow& window, QMouseEvent* event) override; + + int stayStillTimeMS = 300; + virtual void searchForPointUnderCursor(); + virtual void addPointsInWindowToScratchPad(VisibleWindow& window); + //Help Function: + double sqaredDistance(const QPointF& a, const QPointF& b); + QPoint lastPosition; + QTimer* timer = new QTimer(this); + Scratchpad* pad = nullptr; + HoverButton* informationButton = new HoverButton(); + InformationPopUp* popupWidget = new InformationPopUp(nullptr); +private: + QMenu* selectMenu = new QMenu(this); + QAction* displayAreaAction = new QAction("Display this area", this); + QAction* scratchpadAction = new QAction("Add points to Scratchpad", this); + GraphViewSettingDialog* dialog; + QString displayNameOfDataThatIsDisplayed = ""; + QString informationButtonString = "No information."; + QString graphplottInformation = "<h3><u>Control:</u></h3><b>Scrolling:</b> Zoom in and out.<br><b>Scrolling + CTRL:</b> Zoom only the X Axis in and out.<br><b>Scrolling + SHIFT:</b> Zoom only the Y Axis in and out.<br><b>Drag & Drop LMB:</b> Moves the visible area.<br><b>Drag & Drop RMB:</b> Select area to display or adds solutions to Scratchpad.<br><b>Drag & Drop RMB + CTRL:</b> Select area to display.<br><b>Drag & Drop RMB + ALT:</b> Adds solutions to Scratchpad.<br><b>Arrow Keys:</b> Moves the visible area<br><b>Page Up:</b> Zoom in.<br><b>Page Down:</b> Zoom out.<br><b>CTRL + R:</b> Resetts the visible area.<br>"; + QLabel* displayLabel; + virtual void keyPressEvent(QKeyEvent* event) override; +}; diff --git a/metavis/GraphView.cpp b/metavis/GraphView.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a0bc623d069f03c50fbe5855d5316a8db9a7518 --- /dev/null +++ b/metavis/GraphView.cpp @@ -0,0 +1,827 @@ +#include "pch.h" +#include "GraphView.h" + +#include <QDebug> +#include <QPushButton> +#include <QBrush> +#include <random> +#include <algorithm> +#include <chrono> +#include "util.h" + +#define X_AXIS_GAP_AMOUNT 5 +#define Y_AXIS_GAP_AMOUNT 10 +#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 30 + +#define MARGIN_TOP 10 +#define MARGIN_BOTTOM 20 +#define MARGIN_LEFT 50 +#define MARGIN_RIGHT 10 + + + +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); + + ////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); +} + +GraphView::~GraphView() +{ +} + +void GraphView::paintEvent(QPaintEvent* event) +{ + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + QPainter painter(this); + painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing); + if (graphSeriesVec.empty() && yDoubleDoubleValuesAmount == 0) { + painter.setPen(axisPen); + painter.setFont(QFont("Arial", 12, QFont::Bold)); + painter.drawText(rect(), Qt::AlignCenter, "No Data connected"); + return; + } + //Calculate LineRect + 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); + + painter.setBrush(rectPen.color()); + painter.drawRect(graphDisplayRect); + painter.setBrush(Qt::BrushStyle::NoBrush); + + double stregth_factorX = graphDisplayRect.width() / rangeGraphX; + double stregth_factorY = graphDisplayRect.height() / rangeGraphY; + QPointF translation(graphDisplayRect.left(), graphDisplayRect.top()); + for (const GraphSeries& graphSeries : graphSeriesVec) { + if (!graphSeries.data) { + qDebug() << "Pointer to nothing pls help"; + } + if (graphSeries.data->empty() || graphSeries.hide) continue; + linePen.setColor(graphSeries.color); + + if (graphSeries.type == GraphSeries::SeriesType::Line || graphSeries.type == GraphSeries::SeriesType::LineDot) { + linePen.setWidth(graphSeries.lineWidth + ((&graphSeries == highlightSeries) ? 5 : 0)); + painter.setPen(linePen); + QPainterPath painterPath; + + QPointF oldpoint = graphSeries.data->at(0).toQPointF(); + 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(transformGraphToView(oldpoint, stregth_factorX, stregth_factorY, translation)); + painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation)); + } + else if (inBoundX(oldpoint) && inBoundX(newpoint)) { + painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation)); + } + else if (inBoundX(oldpoint) && !inBoundX(newpoint)) { + painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation)); + break; + } + oldpoint = newpoint; + } + + //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(transformGraphToView(data.toQPointF(), stregth_factorX, stregth_factorY, translation), graphSeries.circleRadius, graphSeries.circleRadius); + } + } + painter.setBrush(Qt::BrushStyle::NoBrush); + } + } + + if (yDoubleDoubleValuesAmount > 0) { + linePen.setWidth(2); + linePen.setColor(QColor(0,100,200, 0)); + painter.setPen(linePen); + painter.setBrush(QColor(0, 100, 200, 0)); + for (int i = 0; i < yDoubleDoubleValuesAmount; i++) { + painter.setBrush(pointColors[i]); + painter.drawEllipse(transformGraphToView(QPointF(yArray[2 * i], yArray[2 * i + 1]), stregth_factorX, stregth_factorY, translation), 3, 3); + } + painter.setBrush(Qt::BrushStyle::NoBrush); + } + + + + + if (drawTotalBound) { + painter.setPen(axisPen); + 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); + } + + + + + //Draw White Rect for axis + painter.setPen(rectPen); + + painter.setBrush(rectPen.color()); + QRect leftRect(rect().topLeft(), QPoint(graphDisplayRect.left() - 1, rect().bottom())); + QRect topRect(rect().topLeft(), QPoint(rect().right(), graphDisplayRect.top() - 1)); + QRect bottomRect(QPoint(rect().left(), graphDisplayRect.bottom()), rect().bottomRight()); + QRect rightRect(QPoint(graphDisplayRect.right(), rect().top()), rect().bottomRight()); + painter.drawRect(leftRect); + painter.drawRect(topRect); + painter.drawRect(bottomRect); + painter.drawRect(rightRect); + painter.setBrush(Qt::BrushStyle::NoBrush); + + + + + + //Font for Axis; + painter.setFont(QFont("Arial", 8)); + //draw X-Axis + painter.setPen(axisPen); + QRect xAxisRect(QPoint(graphDisplayRect.left(), graphDisplayRect.bottom()), QPoint(graphDisplayRect.right(), rect().bottom())); + 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); + + for (int i = 0; i < 11; i++) { + xAxisPath.moveTo(xAxisRect.left() + i * xGap, xAxisRect.top() + X_AXIS_NUMBER_LINE_LENGTH); + xAxisPath.lineTo(xAxisRect.left() + i * xGap, xAxisRect.top()); + textRect.moveCenter(QPoint(xAxisRect.left() + i * xGap, xAxisRect.top() + X_AXIS_NUMBER_GAP_LENGTH)); + painter.drawText(textRect, Qt::AlignCenter, xAxisNumbers[i]); + } + painter.drawPath(xAxisPath); + + //draw Y-Axis + QRect yAxisRect(QPoint(rect().left(), graphDisplayRect.top()), QPoint(graphDisplayRect.left(), graphDisplayRect.bottom())); + QPainterPath yAxisPath; + yAxisPath.moveTo(yAxisRect.right(), yAxisRect.bottom()); + yAxisPath.lineTo(yAxisRect.right(), yAxisRect.top()); + int yGap = yAxisRect.height() / Y_AXIS_GAP_AMOUNT; + for (int i = 0; i < 11; i++) { + 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::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; +} + +//Help Function: +double sqaredDistance(const QPointF& a, const QPointF& b) +{ + 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) +{ + + return point.x() >= actualBound.minX && point.x() <= actualBound.maxX; +} + +bool GraphView::inBoundY(QPointF& point) +{ + return point.y() >= actualBound.minY && point.y() <= actualBound.maxY;; +} + +bool GraphView::inBound(QPointF& point) +{ + return inBoundX(point) && inBoundY(point); +} + +void GraphView::keyPressEvent(QKeyEvent* event) +{ + switch (event->key()) { + case Qt::Key_Up: + fixedBound.move(this, Bound::Change::MoveUp); + break; + case Qt::Key_Right: + fixedBound.move(this, Bound::Change::MoveRight); + break; + case Qt::Key_Left: + fixedBound.move(this, Bound::Change::MoveLeft); + break; + case Qt::Key_Down: + fixedBound.move(this, Bound::Change::MoveDown); + break; + case Qt::Key_PageUp: + case Qt::Key_Plus: + 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: + 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: + resetBound(); + update(); + default: + QWidget::keyPressEvent(event); + } + //If not reacted forward: + QWidget::keyPressEvent(event); +} + +void GraphView::resetBound() +{ + calculateTotalMatrixBound(); + fixedBound = totalBound; + calculateRangeXY(); + generateAxisNumberStrings(); +} + +void GraphView::wheelEvent(QWheelEvent* event) +{ + QPoint numPixels = event->pixelDelta(); + QPoint numDegrees = event->angleDelta() / 8; + + if (!numPixels.isNull()) { + //scrollWithPixels(numPixels); + switch (numPixels.y()) { + case -1: + fixedBound.move(this, Bound::Change::ZoomIn); + break; + case 1: + fixedBound.move(this, Bound::Change::ZoomOut); + default: + break; + } + } + else if (!numDegrees.isNull()) { + QPoint numSteps = numDegrees / 15; + switch (numSteps.y()) { + case 1: + 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: + 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; + } + } + + event->accept(); +} + +void GraphView::mouseMoveEvent(QMouseEvent* event) +{ + //QToolTip::showText(event->globalPos(), "Hello", this); + 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; + double pixelWidthX = rangeX / (double)width ; + double rangeY = std::abs(actualBound.maxY - actualBound.minY); + int height = (rect().bottom() - 20) - (rect().top() + 20); + height -= (height % Y_AXIS_GAP_AMOUNT) + 1; + double pixelWidthY = rangeY / (double)height; + + + + fixedBound.move(this, delta, pixelWidthX, pixelWidthY); + +} + +void GraphView::mousePressEvent(QMouseEvent* event) +{ + 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); +} + +void GraphView::mouseReleaseEvent(QMouseEvent* event) +{ + highlightSeries = nullptr; + mousePressed = false; + update(); +} + +void GraphView::update() +{ + calculateRangeXY(); + generateAxisNumberStrings(); + QWidget::update(); +} + +void GraphView::setDrawBound(bool value) +{ + drawTotalBound = value; +} +/** + * Delete all series. + * + */ +void GraphView::reset() +{ + graphSeriesVec.clear(); +} + + +void GraphView::calculateTotalMatrixBound() +{ + if (yDoubleDoubleValuesAmount > 0) { + /*double maxX, maxY; + double minX = maxX = yArray[0], minY = maxY = yArray[1]; + for (int i = 1; i < yDoubleDoubleValuesAmount; i++) { + + if (yArray[2 * i] < minX) { + minX = yArray[2 * i]; + }else if (yArray[2 * i] > maxX) { + maxX = yArray[2 * i]; + } + if (yArray[2 * i + 1] < minY) { + minY = yArray[2 * i + 1]; + } + else if (yArray[2 * i + 1] > maxY) { + maxY = yArray[2 * i + 1]; + } + } + totalBound.minX = minX; + totalBound.minY = minY; + totalBound.maxX = maxX; + totalBound.maxY = maxY;*/ + + // Calculate AveragePoint of Matrix + + + + + } +} + +void GraphView::calculateTotalGraphBound() +{ + totalBound.minX = std::min_element(std::begin(graphSeriesVec), std::end(graphSeriesVec), [](const GraphSeries& a, const GraphSeries& b) -> bool {return a.minX < b.minX; })->minX; + totalBound.maxX = std::max_element(std::begin(graphSeriesVec), std::end(graphSeriesVec), [](const GraphSeries& a, const GraphSeries& b) -> bool {return a.maxX < b.maxX; })->maxX; + totalBound.minY = std::min_element(std::begin(graphSeriesVec), std::end(graphSeriesVec), [](const GraphSeries& a, const GraphSeries& b) -> bool {return a.minY < b.minY; })->minY; + totalBound.maxY = std::max_element(std::begin(graphSeriesVec), std::end(graphSeriesVec), [](const GraphSeries& a, const GraphSeries& b) -> bool {return a.maxY < b.maxY; })->maxY; + +} + + + +void GraphView::calculateRangeXY() +{ + actualBound = fixedBound; + rangeGraphX = std::abs(actualBound.maxX - actualBound.minX); + rangeGraphY = std::abs(actualBound.maxY - actualBound.minY); + if (std::abs(rangeGraphX) < 0.0001) { + actualBound.minX -= 1.0; + actualBound.maxX += 1.0; + rangeGraphX = 2; + } + if (std::abs(rangeGraphY) < 0.0001) { + actualBound.minY -= 1.0; + actualBound.maxY += 1.0; + rangeGraphY = 2; + } +} + +//TODO: graphview +void GraphView::calculateMinMaxXY(GraphSeries& lgs) +{ + auto pairX = std::minmax_element(lgs.data->begin(), lgs.data->end(), [](const GraphDataPoint& a, const GraphDataPoint& b) -> bool {return a.x < b.x; }); + lgs.minX = pairX.first->x; + lgs.maxX = pairX.second->x; + auto pairY = std::minmax_element(lgs.data->begin(), lgs.data->end(), [](const GraphDataPoint& a, const GraphDataPoint& b) -> bool {return a.y < b.y; }); + lgs.minY = pairY.first->y; + lgs.maxY = pairY.second->y; +} + +void GraphView::addSeries(std::vector<GraphDataPoint>* line, RunData* run, QColor color, GraphSeries::SeriesType type) +{ + if (line->empty()) { + qDebug() << "Series is empty!"; + return; + } + GraphSeries lgs; + lgs.run = run; + lgs.data = line; + lgs.type = type; + calculateMinMaxXY(lgs); + lgs.color = color; + graphSeriesVec.push_back(lgs); + calculateTotalGraphBound(); + if (!useFixedBound) calculateRangeXY(); + generateAxisNumberStrings(); +} + +QColor GraphView::generateNextColorForGraph() +{ + /* http://devmag.org.za/2012/07/29/how-to-choose-colours-procedurally-algorithms/ + use golden ratio 0.618033988749895f + */ + hueoffset = std::fmod(hueoffset + 0.618033988749895f, 1.0); + return QColor::fromHsvF(hueoffset, 0.83, 1.0); +} + + +QColor 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 GraphView::addYMatrix(double* yArray, std::vector<SolutionPointData>& solVec) +{ + this->yArray = yArray; yDoubleDoubleValuesAmount = solVec.size(); + this->solVec = &solVec; + if (pointColors != nullptr) delete pointColors; + pointColors = new QColor[yDoubleDoubleValuesAmount]; +} +void GraphView::autoZoomOut() +{ + if (yDoubleDoubleValuesAmount == 0) return; + QPointF sumDistance(0, 0); + for (int i = 0; i < yDoubleDoubleValuesAmount; i++) { + sumDistance += QPointF(std::abs(yArray[2 * i]), std::abs(yArray[2 * i + 1])); + } + QPointF avgDistance(sumDistance.x() / yDoubleDoubleValuesAmount, + sumDistance.y() / yDoubleDoubleValuesAmount); + double scalar = 3; + double addScalar = 1; + if (fixedBound.maxX < avgDistance.x() * scalar) { + fixedBound.minX = -avgDistance.x() * scalar * addScalar; + fixedBound.maxX = avgDistance.x() * scalar * addScalar; + fixedBound.minY = -avgDistance.y() * scalar * addScalar; + fixedBound.maxY = avgDistance.y() * scalar * addScalar; + } +} + +void GraphView::updateGraphColors(ColorGradient& gradient) +{ + if (yDoubleDoubleValuesAmount == 0) return; + double bestValue = 0; + double worstValue = 100; + for (int i = 0; i < solVec->size(); i++) { + pointColors[i] = gradient.getColorFromAlpha( std::clamp(util::inverseLinearInterpolation(bestValue, worstValue, solVec->at(i).objectiveFunction), 0.0, 1.0)); + } +} + +void GraphView::generateAxisNumberStrings() +{ + for (int i = 0; i < 11; i++) { + xAxisNumbers[i] = QString::number(actualBound.minX + i * (rangeGraphX / (double)X_AXIS_GAP_AMOUNT), 'f', 1); + yAxisNumbers[i] = QString::number(actualBound.minY + i * (rangeGraphY / (double)Y_AXIS_GAP_AMOUNT), 'f', 1); + } +} + +void GraphView::setUseFixedBound(bool value) +{ + //if value == other update else do nothing + if (useFixedBound != value) { + useFixedBound = value; + calculateRangeXY(); + actualBound = fixedBound; + generateAxisNumberStrings(); + } +} + +void GraphView::generateAndAddRandomLine() +{ + 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> realDistr(-30, 30); + std::uniform_int_distribution<int> intScaleDistr(1, 3); + + for (int randomSeries = 0; randomSeries < 1; randomSeries++) { + std::vector<GraphDataPoint> randomPointVec(101); + int scale = intScaleDistr(gen); + for (int i = 0; i < randomPointVec.size(); i++) { + randomPointVec[i] = GraphDataPoint(i + 500, i * scale + realDistr(gen)); + } + addLine(&randomPointVec, nullptr); + } +} + +void GraphView::addLine(std::vector<GraphDataPoint>* line, RunData* run) +{ + addSeries(line, run,generateNextColorForGraph(), GraphSeries::SeriesType::Line); +} + +void GraphView::addLine(std::vector<GraphDataPoint>* line, RunData* run, QColor color) +{ + addSeries(line, run, color, GraphSeries::SeriesType::Line); +} + +void GraphView::addDots(std::vector<GraphDataPoint>* dots, RunData* run) +{ + addSeries(dots, run, generateNextColorForGraph(), GraphSeries::SeriesType::Dot); +} + +void GraphView::addDots(std::vector<GraphDataPoint>* dots, RunData* run, QColor color) +{ + addSeries(dots, run, color, GraphSeries::SeriesType::Dot); +} + +void GraphView::removeAllSeriesWithRundata(RunData* run) +{ + //TODO not implemented jet + //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) +{ + double changeX = std::abs(minX - maxX) * changePercentage; + double changeY = std::abs(minY - maxY) * changePercentage; + switch (type) { + case Change::MoveLeft: + minX -= changeX; + maxX -= changeX; + break; + case Change::MoveRight: + minX += changeX; + maxX += changeX; + break; + case Change::MoveUp: + minY += changeY; + maxY += changeY; + break; + case Change::MoveDown: + minY -= changeY; + maxY -= changeY; + break; + case Change::ZoomOut: + minY -= changeY; + maxY += changeY; + minX -= changeX; + maxX += changeX; + break; + case Change::ZoomIn: + minY += changeY; + maxY -= changeY; + 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; + } + //TODO: Move out of bound in calculate widget + widget->update(); +} + +void Bound::move(GraphView* widget, QPointF& delta, double realPixelWidthX, double realPixelWidthY) +{ + minX += -delta.x() * realPixelWidthX; + maxX += -delta.x() * realPixelWidthX; + minY += delta.y() * realPixelWidthY; + maxY += delta.y() * realPixelWidthY; + widget->update(); +} + +void Bound::move(GraphView* widget, QPointF& targetPoint) +{ + //Calculate MiddlePoint + QPointF actualMiddlePoint((maxX - minX) / 2.0, (maxY - minY) / 2.0); + QPointF dif = targetPoint - actualMiddlePoint; + minX += dif.x(); + maxX += dif.x(); + minY += dif.y(); + maxY += dif.y(); +} diff --git a/metavis/GraphView.h b/metavis/GraphView.h new file mode 100644 index 0000000000000000000000000000000000000000..0fa77296efa715f879e155b8f151b6f4475aabf4 --- /dev/null +++ b/metavis/GraphView.h @@ -0,0 +1,139 @@ +#pragma once +#include <QWidget> +#include <QPaintEvent> +#include <QAbstractListModel> +#include <QPen> +#include <QPainter> +#include <vector> +#include "RunData.h" +#include "ColorGradient.h" + +class GraphView; +class RunData; +struct SolutionPointData; + + + + +struct GraphSeries { + std::vector<GraphDataPoint>* data; + double minX, maxX; + double minY, maxY; + RunData* run; + + + + //Settings for visual + QString name; + QColor color; + int lineWidth = 2; + double circleRadius = 2.0; + 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, 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); + void move(GraphView* widget, QPointF& delta, double realPixelWidthX, double realPixelWidthY); + void move(GraphView* widget, QPointF& targetPoint); +}; + +class GraphView :public QWidget +{ + Q_OBJECT + +public: + 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; + //Data + Bound totalBound; + Bound actualBound = totalBound; + bool drawTotalBound = false; + double rangeGraphX = 0, rangeGraphY = 0; + + double* yArray = nullptr; + QColor* pointColors = nullptr; + std::vector<SolutionPointData>* solVec = nullptr; + int yDoubleDoubleValuesAmount = 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, 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(); + void resetBound(); + void addYMatrix(double* yArray, std::vector<SolutionPointData>& solVec); + void autoZoomOut(); + void updateGraphColors(ColorGradient& gradient); + + + + + +private: + void calculateTotalMatrixBound(); + void calculateTotalGraphBound(); + void GraphView::calculateMinMaxXY(GraphSeries& lgs); + 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; + void paintEvent(QPaintEvent* event) override; +public slots: + void setMaxIter(int value); + void setMinIter(int value); +}; + diff --git a/metavis/GraphViewSettingDialog.cpp b/metavis/GraphViewSettingDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1418b0611bfb7efe679e09253b99c9e993029bb6 --- /dev/null +++ b/metavis/GraphViewSettingDialog.cpp @@ -0,0 +1,55 @@ +#include "pch.h" +#include "GraphViewSettingDialog.h" +#include <QListWidgetItem> +#include <QPushButton> +#include "GraphViewSettingItem.h" +#include <QScrollArea> + + + +GraphViewSettingDialog::GraphViewSettingDialog(GraphPlott* view, QString title) + : QDialog(view, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowType::WindowCloseButtonHint), view(view) +{ + ui.setupUi(this); + setWindowTitle(title); + 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 + //qDebug() << "vecsize " << vecsize << " view->getSeriesVector().size() " << view->getSeriesVector().size(); + if (vecsize == view->getSeriesVector().size()) { + this->activateWindow(); + this->setFocus(); + this->show(); + return; + } + else { + vecsize = view->getSeriesVector().size(); + QLayoutItem* item; + while ((item = layout->takeAt(0))) + delete item; + } + + for (auto iter = view->getSeriesVector().begin(); iter != view->getSeriesVector().end(); iter++) { + GraphViewSettingItem* handle = new GraphViewSettingItem(view, &*iter, this); + layout->addWidget(handle); + } + this->show(); +} + diff --git a/metavis/GraphViewSettingDialog.h b/metavis/GraphViewSettingDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..7ef08f66ae75fe1e009f777bb38f9a912c6254a7 --- /dev/null +++ b/metavis/GraphViewSettingDialog.h @@ -0,0 +1,23 @@ +#pragma once + +#include <QDialog> +#include "GraphPlott.h" +#include "ui_GraphViewSettingDialog.h" +class GraphPlott; + + +class GraphViewSettingDialog : public QDialog +{ + Q_OBJECT + +public: + GraphPlott* view; + GraphViewSettingDialog(GraphPlott* view, QString title); + ~GraphViewSettingDialog(); +public slots: + void openDialog(); +private: + int vecsize = 0; + Ui::GraphViewSettingDialog ui; + QVBoxLayout* layout; +}; diff --git a/metavis/GraphViewSettingDialog.ui b/metavis/GraphViewSettingDialog.ui new file mode 100644 index 0000000000000000000000000000000000000000..7d5d58c9d03b9d1226c3e562bfdb0f873a7dc327 --- /dev/null +++ b/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> diff --git a/metavis/GraphViewSettingItem.cpp b/metavis/GraphViewSettingItem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..729bcc299e8d8cce15f03d61adb48b9133ce0fab --- /dev/null +++ b/metavis/GraphViewSettingItem.cpp @@ -0,0 +1,91 @@ +#include "pch.h" +#include "GraphViewSettingItem.h" +#include <QColorDialog> + +GraphViewSettingItem::GraphViewSettingItem(GraphPlott* view, GraphPlottSeries* series, QWidget *parent) : QWidget(parent), series(series), view(view), colorButton(new ColorButton(this, series->color)) +{ + ui.setupUi(this); + // Init + switch (series->type) { + case GraphPlottSeries::SeriesType::Dot: + ui.SytleComboBox->setCurrentIndex(1); + break; + case GraphPlottSeries::SeriesType::LineDot: + ui.SytleComboBox->setCurrentIndex(2); + break; + case GraphPlottSeries::SeriesType::Line: + default: + ui.SytleComboBox->setCurrentIndex(0); + break; + } + ui.LineSlider->setValue(series->lineWidth); + ui.CircleSlider->setValue(series->circleRadius); + dissableSlidersOnType(); + updateGroupBoxTitle(); + ui.RightFormLayout->setWidget(1, QFormLayout::FieldRole, colorButton); + + + //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(colorButton, &ColorButton::colorChanged, this, &GraphViewSettingItem::setColor); + connect(ui.NameEdit, &QLineEdit::textChanged, [=](const QString& text) { series->description = text; updateGroupBoxTitle(); }); +} + +GraphViewSettingItem::~GraphViewSettingItem() +{ +} + +void GraphViewSettingItem::dissableSlidersOnType() +{ + switch (series->type) { + case GraphPlottSeries::SeriesType::Line: + ui.LineSlider->setEnabled(true); + ui.CircleSlider->setEnabled(false); + break; + case GraphPlottSeries::SeriesType::Dot: + ui.LineSlider->setEnabled(false); + ui.CircleSlider->setEnabled(true); + break; + case GraphPlottSeries::SeriesType::LineDot: + ui.LineSlider->setEnabled(true); + ui.CircleSlider->setEnabled(true); + break; + default: + break; + } +} + +void GraphViewSettingItem::setType(int type) +{ + switch (type) { + case 0: + series->type = GraphPlottSeries::SeriesType::Line; + break; + case 1: + series->type = GraphPlottSeries::SeriesType::Dot; + break; + case 2: + series->type = GraphPlottSeries::SeriesType::LineDot; + break; + default: + break; + } +} + +void GraphViewSettingItem::setColor(QColor color) +{ + series->color = color; + updateGroupBoxTitle(); + view->update(); +} + +void GraphViewSettingItem::updateGroupBoxTitle() +{ + ui.GroupBox->setTitle("Series: " + series->description); + QString style = "QGroupBox { color: rgb(%1, %2, %3); };"; + ui.GroupBox->setStyleSheet(style.arg(series->color.red()).arg(series->color.green()).arg(series->color.blue())); +} diff --git a/metavis/GraphViewSettingItem.h b/metavis/GraphViewSettingItem.h new file mode 100644 index 0000000000000000000000000000000000000000..8f48f0220de007dfcab3def17e23ef00c1d53d9b --- /dev/null +++ b/metavis/GraphViewSettingItem.h @@ -0,0 +1,26 @@ +#pragma once + +#include <QWidget> +#include "ui_GraphViewSettingItem.h" +#include "GraphPlott.h" +#include "ColorButton.h" +class GraphPlott; + +class GraphViewSettingItem : public QWidget +{ + Q_OBJECT + +public: + GraphViewSettingItem(GraphPlott* view, GraphPlottSeries* series, QWidget *parent = Q_NULLPTR); + ~GraphViewSettingItem(); + +private: + Ui::GraphViewSettingItem ui; + GraphPlottSeries* series; + GraphPlott* view; + ColorButton* colorButton; + void dissableSlidersOnType(); + void setType(int type); + void setColor(QColor color); + void updateGroupBoxTitle(); +}; diff --git a/metavis/GraphViewSettingItem.ui b/metavis/GraphViewSettingItem.ui new file mode 100644 index 0000000000000000000000000000000000000000..4a075220b08f7815b1a3bdb0f61aa6cac9d069ca --- /dev/null +++ b/metavis/GraphViewSettingItem.ui @@ -0,0 +1,188 @@ +<?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&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="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> diff --git a/metavis/HoverButton.cpp b/metavis/HoverButton.cpp new file mode 100644 index 0000000000000000000000000000000000000000..313ebc34be18be42346b679f4bfe8ebc49d96d87 --- /dev/null +++ b/metavis/HoverButton.cpp @@ -0,0 +1,27 @@ +#include "pch.h" +#include "HoverButton.h" + +HoverButton::HoverButton(QWidget *parent) + : QPushButton(parent) +{ +} + +HoverButton::~HoverButton() +{ +} + +void HoverButton::setHoveredIcon(const QIcon& icon) +{ + hoveredIcon = icon; +} + +void HoverButton::enterEvent(QEvent*) +{ + normalIcon = this->icon(); + this->setIcon(hoveredIcon); +} + +void HoverButton::leaveEvent(QEvent*) +{ + this->setIcon(normalIcon); +} diff --git a/metavis/HoverButton.h b/metavis/HoverButton.h new file mode 100644 index 0000000000000000000000000000000000000000..d0c4c721c3b35a1d10187bff1971228437c12899 --- /dev/null +++ b/metavis/HoverButton.h @@ -0,0 +1,24 @@ +#pragma once + +#include <QPushButton> +#include <QPalette> +#include <QColor> +#include <QDebug> +#include <QIcon> + +class HoverButton : public QPushButton +{ + Q_OBJECT + +public: + HoverButton(QWidget *parent = nullptr); + ~HoverButton(); + void setHoveredIcon(const QIcon& icon); + +protected: + virtual void enterEvent(QEvent*); + virtual void leaveEvent(QEvent*); +private: + QIcon normalIcon; + QIcon hoveredIcon; +}; diff --git a/metavis/InformationPopUp.cpp b/metavis/InformationPopUp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a4d3e8cf7c89b1d8848d389c773e578de3a6a2d --- /dev/null +++ b/metavis/InformationPopUp.cpp @@ -0,0 +1,27 @@ +#include "pch.h" +#include "InformationPopUp.h" + +InformationPopUp::InformationPopUp(QWidget *parent) + : QWidget(parent) +{ + this->setWindowFlag(Qt::Popup); + QVBoxLayout* layout = new QVBoxLayout(this); + setContentsMargins(3, 3, 3, 3); + informationLabel->setTextFormat(Qt::RichText); + layout->addWidget(informationLabel); +} + +InformationPopUp::~InformationPopUp() +{ +} + +void InformationPopUp::setInformation(QString information) +{ + informationLabel->setText(information); + update(); +} + +QString InformationPopUp::getInformation() const +{ + return informationLabel->text(); +} diff --git a/metavis/InformationPopUp.h b/metavis/InformationPopUp.h new file mode 100644 index 0000000000000000000000000000000000000000..916f4de3f6d216d758a97a34f3799c56bb41f0f5 --- /dev/null +++ b/metavis/InformationPopUp.h @@ -0,0 +1,17 @@ +#pragma once + +#include <QWidget> +#include <QString> +#include <QLabel> +class InformationPopUp : public QWidget +{ + Q_OBJECT + +public: + InformationPopUp(QWidget *parent); + ~InformationPopUp(); + void setInformation(QString information); + QString getInformation() const; +private: + QLabel* informationLabel = new QLabel(); +}; diff --git a/metavis/MetalogManagerItem.cpp b/metavis/MetalogManagerItem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71df02f00213a6abbbb8e4b8dff2782ccefc3c2f --- /dev/null +++ b/metavis/MetalogManagerItem.cpp @@ -0,0 +1,13 @@ +#include "pch.h" +#include "MetalogManagerItem.h" + +MetalogManagerItem::MetalogManagerItem(QWidget *parent) + : QWidget(parent) +{ + ui.setupUi(this); +} + +MetalogManagerItem::~MetalogManagerItem() +{ + +} diff --git a/metavis/MetalogManagerItem.h b/metavis/MetalogManagerItem.h new file mode 100644 index 0000000000000000000000000000000000000000..c3b4f9e27904ba1e61d3d06ecd14eeddf2e0a57a --- /dev/null +++ b/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; +}; diff --git a/metavis/MetalogManagerItem.ui b/metavis/MetalogManagerItem.ui new file mode 100644 index 0000000000000000000000000000000000000000..78cc98476a785af231c07900c45fefdfadd50e4a --- /dev/null +++ b/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> diff --git a/metavis/Plott.cpp b/metavis/Plott.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de4bbcb885d57e286f57c99fda73fdb9432ac670 --- /dev/null +++ b/metavis/Plott.cpp @@ -0,0 +1,520 @@ +#include "pch.h" +#include "Plott.h" +#include "util.h" + + + +#include <chrono> + +#define X_AXIS_GAP_AMOUNT 5 +#define Y_AXIS_GAP_AMOUNT 10 +#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 30 +#define X_AXIS_LEGEND_TOP_BEGIN 12 +#define Y_AXIS_LEGEND_LEFT_BEGIN 12 + +#define MARGIN_TOP 20 +#define MARGIN_BOTTOM 25 +#define MARGIN_LEFT 50 +#define MARGIN_RIGHT 10 + + + +Plott::Plott(QWidget *parent) + : QWidget(parent) +{ + setFocusPolicy(Qt::ClickFocus); +} + +Plott::~Plott() +{ +} +void Plott::setVisibleWindow(double xMin, double xMax, double yMin, double yMax) +{ + window.xMin = xMin; + window.xMax = xMax; + window.yMin = yMin; + window.yMax = yMax; +} +void Plott::setDefaultVisibleWindow(double xMin, double xMax, double yMin, double yMax) +{ + defaultWindow.xMin = xMin; + defaultWindow.xMax = xMax; + defaultWindow.yMin = yMin; + defaultWindow.yMax = yMax; +} +void Plott::resetToDefaultWindow() +{ + window.xMin = defaultWindow.xMin; + window.xMax = defaultWindow.xMax; + window.yMin = defaultWindow.yMin; + window.yMax = defaultWindow.yMax; +} +void Plott::setAxisLegend(QString xAxisLegend, QString yAxisLegend) +{ + this->xAxisLegend = xAxisLegend; + this->yAxisLegend = yAxisLegend; +} +QPointF Plott::transformGraphToView(QPointF graphpoint) const +{ + QRect graphDisplayRect = getDisplayRect(); + QPointF translation(graphDisplayRect.left(), graphDisplayRect.top()); + double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin); + double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin); + return QPointF((graphpoint.x() - window.xMin) * stregth_factorX, (window.yMax - graphpoint.y()) * stregth_factorY) + translation; +} +QPointF Plott::transformGraphToView(QPointF graphpoint, double stregth_factorX, double stregth_factorY, QPointF translation) const +{ + return QPointF((graphpoint.x() - window.xMin) * stregth_factorX, (window.yMax - graphpoint.y()) * stregth_factorY) + translation; +} +QPointF Plott::transformViewToGraph(QPointF viewpoint) const +{ + QRect graphDisplayRect = getDisplayRect(); + QPointF translation(graphDisplayRect.left(), graphDisplayRect.top()); + double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin); + double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin); + return QPointF(((viewpoint.x() - translation.x()) / stregth_factorX) + window.xMin, window.yMax - ((viewpoint.y() - translation.y()) / stregth_factorY)); +} + +QPointF Plott::transformViewToGraph(QPointF viewpoint, double stregth_factorX, double stregth_factorY, QPointF translation) const +{ + return QPointF(((viewpoint.x() - translation.x()) / stregth_factorX) + window.xMin, window.yMax - ((viewpoint.y() - translation.y()) / stregth_factorY)); +} + + + + +void Plott::drawData(QPainter& painter) +{ +} + +QRect Plott::getDisplayRect() 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); + return graphDisplayRect; +} + + +// Draws the axis +void Plott::paintEvent(QPaintEvent* event) +{ + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + QPainter painter(this); + painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing); + + QRect graphDisplayRect = getDisplayRect(); + int xGap = graphDisplayRect.width() / X_AXIS_GAP_AMOUNT; + int yGap = graphDisplayRect.height() / Y_AXIS_GAP_AMOUNT; + if(enableGrid){ + QPainterPath gridPath; + for (int i = 1; i < X_AXIS_GAP_AMOUNT; i++) { + gridPath.moveTo(graphDisplayRect.left() + i * xGap, graphDisplayRect.top()); + gridPath.lineTo(graphDisplayRect.left() + i * xGap, graphDisplayRect.bottom()); + } + + for (int i = 0; i < Y_AXIS_GAP_AMOUNT + 1; i++) { + gridPath.moveTo(graphDisplayRect.left(), graphDisplayRect.bottom() - i * yGap); + gridPath.lineTo(graphDisplayRect.right(), graphDisplayRect.bottom() - i * yGap); + } + QPen graypen(QColor(150, 150, 150)); + painter.setPen(graypen); + painter.drawPath(gridPath); + } + + drawData(painter); + + //Draw White Rect for axis + QPen blackpen(QColor(0, 0, 0)); + blackpen.setWidth(1); + QPen whitePen(QColor(255, 255, 255)); + whitePen.setWidth(1); + painter.setPen(whitePen); + painter.setBrush(whitePen.color()); + QRect leftRect(rect().topLeft(), QPoint(graphDisplayRect.left() - 1, rect().bottom())); + QRect topRect(rect().topLeft(), QPoint(rect().right(), graphDisplayRect.top() - 1)); + QRect bottomRect(QPoint(rect().left(), graphDisplayRect.bottom()), rect().bottomRight()); + QRect rightRect(QPoint(graphDisplayRect.right(), rect().top()), rect().bottomRight()); + painter.drawRect(leftRect); + painter.drawRect(topRect); + painter.drawRect(bottomRect); + painter.drawRect(rightRect); + painter.setBrush(Qt::BrushStyle::NoBrush); + + + + + //Font for Axis; + painter.setFont(QFont("Arial", 8)); + //draw X-Axis + painter.setPen(blackpen); + QRect xAxisRect(QPoint(graphDisplayRect.left(), graphDisplayRect.bottom()), QPoint(graphDisplayRect.right(), rect().bottom())); + QPainterPath xAxisPath; + xAxisPath.moveTo(xAxisRect.left(), xAxisRect.top()); + xAxisPath.lineTo(xAxisRect.right(), xAxisRect.top()); + + QRect textRect(0, 0, 40, 9); + + for (int i = 0; i < X_AXIS_GAP_AMOUNT + 1; i++) { + xAxisPath.moveTo(xAxisRect.left() + i * xGap, xAxisRect.top() + X_AXIS_NUMBER_LINE_LENGTH); + xAxisPath.lineTo(xAxisRect.left() + i * xGap, xAxisRect.top()); + textRect.moveCenter(QPoint(xAxisRect.left() + i * xGap, xAxisRect.top() + X_AXIS_NUMBER_GAP_LENGTH)); + painter.drawText(textRect, Qt::AlignCenter, QString::number(util::linearInterpolate(window.xMin, window.xMax, (1/ (double)X_AXIS_GAP_AMOUNT )*i), 'f', 1)); + } + + painter.drawPath(xAxisPath); + //Draw XAxisLegend + QRect xAxisLegendRect(QPoint(xAxisRect.left(), rect().bottom() - X_AXIS_LEGEND_TOP_BEGIN), rect().bottomRight()); + painter.drawText(xAxisLegendRect, Qt::AlignCenter, xAxisLegend); + + //draw Y-Axis + QRect yAxisRect(QPoint(rect().left(), graphDisplayRect.top()), QPoint(graphDisplayRect.left(), graphDisplayRect.bottom())); + QPainterPath yAxisPath; + yAxisPath.moveTo(yAxisRect.right(), yAxisRect.bottom()); + yAxisPath.lineTo(yAxisRect.right(), yAxisRect.top()); + + for (int i = 0; i < Y_AXIS_GAP_AMOUNT + 1; i++) { + 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::AlignRight | Qt::AlignVCenter, QString::number(util::linearInterpolate(window.yMin, window.yMax, (1 / (double)Y_AXIS_GAP_AMOUNT) * i), 'f', 1)); + } + painter.drawPath(yAxisPath); + //Draw YAxisLegend + //to draw vertical the painter have to translated and rotated (order is crucial) + QRect yAxisLegendRect(0, 0, graphDisplayRect.bottom(), Y_AXIS_LEGEND_LEFT_BEGIN); + painter.translate(0,graphDisplayRect.bottom()); + painter.rotate(-90.0); + painter.drawText(yAxisLegendRect, Qt::AlignCenter, yAxisLegend); + painter.rotate(+90.0); + painter.translate(0, -graphDisplayRect.bottom()); + + + + if (isLeftMousePressed) { + QColor blue(0, 122, 204); + QColor gray(120, 120, 120); + //QPointF value = transformViewToGraph(oldPositionForMouseEvent); + QPointF viewPoint = transformGraphToView(mousePressedValue); + 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, whitePen.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, whitePen.color()); + painter.setPen(blue); + painter.drawText(textRect, Qt::AlignCenter, QString::number(mousePressedValue.x(), 'f', 1)); + } + } + if (isRightMousePressed) { + QColor blue(0, 122, 204); + painter.setPen(blue); + painter.drawRect(QRectF(transformGraphToView(this->mousePressedValue), oldPositionForMouseEvent)); + } + std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now(); + std::chrono::milliseconds timeElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); + +} + +void Plott::keyPressEvent(QKeyEvent* event) +{ + double percent = 0.1; + switch (event->key()) { + case Qt::Key_Up: + window.moveUp(percent); + break; + case Qt::Key_Right: + window.moveRight(percent); + break; + case Qt::Key_Left: + window.moveLeft(percent); + break; + case Qt::Key_Down: + window.moveDown(percent); + break; + case Qt::Key_PageUp: + case Qt::Key_Plus: + if (event->modifiers() & Qt::ShiftModifier) { + //ZoomInY + window.ZoomInY(percent); + } + else if (event->modifiers() & Qt::ControlModifier) { + //ZoomInX + window.ZoomInX(percent); + } + else { + //Zoom + window.ZoomIn(percent); + } + break; + case Qt::Key_PageDown: + case Qt::Key_Minus: + if (event->modifiers() & Qt::ShiftModifier) { + //ZoomOutX + window.ZoomOutY(percent); + } + else if (event->modifiers() & Qt::ControlModifier) { + //ZoomOutX + window.ZoomOutX(percent); + } + else { + //ZoomOut + window.ZoomOut(percent); + } + break; + case Qt::Key_R: + resetToDefaultWindow(); + default: + QWidget::keyPressEvent(event); + } + update(); +} + +void Plott::wheelEvent(QWheelEvent* event) +{ + double percent = 0.1; + QPoint numDegrees = event->angleDelta() / 8; + if (!numDegrees.isNull()) { + QPoint numSteps = numDegrees / 15; + switch (numSteps.y()) { + case 1: + if (event->modifiers() & Qt::ShiftModifier) { + window.ZoomInY(percent); + } + else if (event->modifiers() & Qt::ControlModifier) { + window.ZoomInX(percent); + } + else { + window.ZoomIn(percent); + } + break; + case -1: + if (event->modifiers() & Qt::ShiftModifier) { + window.ZoomOutY(percent); + } + else if (event->modifiers() & Qt::ControlModifier) { + window.ZoomOutX(percent); + } + else { + window.ZoomOut(percent); + } + break; + default: + break; + } + update(); + } + + event->accept(); +} + +void Plott::mouseMoveEvent(QMouseEvent* event) +{ + if (isLeftMousePressed) { + //Move window by calculating the change in visibleWindow + QPointF delta = event->pos() - oldPositionForMouseEvent; + oldPositionForMouseEvent = event->pos(); + double rangeX = window.rangeX(); + int width = (rect().right() - 10) - (rect().left() + 50); + width -= (width % X_AXIS_GAP_AMOUNT) + 1; + double pixelWidthX = rangeX / (double)width; + double rangeY = window.rangeY(); + int height = (rect().bottom() - 20) - (rect().top() + 20); + height -= (height % Y_AXIS_GAP_AMOUNT) + 1; + double pixelWidthY = rangeY / (double)height; + + window.xMin += -delta.x() * pixelWidthX; + window.xMax += -delta.x() * pixelWidthX; + window.yMin += delta.y() * pixelWidthY; + window.yMax += delta.y() * pixelWidthY; + update(); + } + else if (isRightMousePressed) { + + oldPositionForMouseEvent = event->pos(); + update(); + } + +} + +void Plott::mousePressEvent(QMouseEvent* event) +{ + if (event->buttons() == Qt::LeftButton) { + isLeftMousePressed = true; + } + else if (event->buttons() == Qt::RightButton) { + isRightMousePressed = true; + } + //To calculate the delta for mouse move event + oldPositionForMouseEvent = event->pos(); + mousePressedValue = transformViewToGraph(oldPositionForMouseEvent); + update(); +} + +void Plott::mouseReleaseEvent(QMouseEvent* event) +{ + if (isRightMousePressed) { + QPointF mouseViewPostion = transformGraphToView(mousePressedValue); + if (std::abs(mouseViewPostion.x() - oldPositionForMouseEvent.x()) > 0 || std::abs(mouseViewPostion.y() - oldPositionForMouseEvent.y()) > 0) { + //set visible window to selection + VisibleWindow selectedWindow(0,0,0,0); + QPointF endPosition = transformViewToGraph(oldPositionForMouseEvent); + bool pressedIsLowerX = mousePressedValue.x() < endPosition.x(); + selectedWindow.xMin = pressedIsLowerX? mousePressedValue.x() : endPosition.x(); + selectedWindow.xMax = pressedIsLowerX ? endPosition.x() : mousePressedValue.x(); + + bool pressedIsLowerY = mousePressedValue.y() < endPosition.y(); + selectedWindow.yMin = pressedIsLowerY ? mousePressedValue.y() : endPosition.y(); + selectedWindow.yMax = pressedIsLowerY ? endPosition.y() : mousePressedValue.y(); + handleSelectedWindow(selectedWindow, event); + } + } + isLeftMousePressed = false; + isRightMousePressed = false; + + update(); +} + +void Plott::handleSelectedWindow(VisibleWindow& window, QMouseEvent* event) +{ + setWindow(window); +} + +void Plott::setWindow(VisibleWindow& window) +{ + this->window = window; + update(); +} + +void VisibleWindow::moveUp(double percent) +{ + double range = rangeY(); + yMin += percent * range; + yMax += percent * range; + +} + +void VisibleWindow::moveDown(double percent) +{ + double range = rangeY(); + yMin -= percent * range; + yMax -= percent * range; +} + +void VisibleWindow::moveLeft(double percent) +{ + double range = rangeX(); + xMin -= percent * range; + xMax -= percent * range; +} + +void VisibleWindow::moveRight(double percent) +{ + double range = rangeX(); + xMin += percent * range; + xMax += percent * range; +} + +void VisibleWindow::ZoomIn(double percent) +{ + //Both sides same percentage + percent /= 2; + double Xrange = rangeX(); + if (Xrange == 0) Xrange = 1.0; + xMin += percent * Xrange; + xMax -= percent * Xrange; + double Yrange = rangeY(); + if (Yrange == 0) Yrange = 1.0; + yMin += percent * Yrange; + yMax -= percent * Yrange; +} + +void VisibleWindow::ZoomInX(double percent) +{ + percent /= 2; + double Xrange = rangeX(); + if (Xrange == 0) Xrange = 1.0; + xMin += percent * Xrange; + xMax -= percent * Xrange; +} + +void VisibleWindow::ZoomInY(double percent) +{ + percent /= 2; + double Yrange = rangeY(); + if (Yrange == 0) Yrange = 1.0; + yMin += percent * Yrange; + yMax -= percent * Yrange; +} + +void VisibleWindow::ZoomOut(double percent) +{ + //Both sides same percentage + percent /= 2; + double Xrange = rangeX(); + if (Xrange == 0) Xrange = 1.0; + xMin -= percent * Xrange; + xMax += percent * Xrange; + double Yrange = rangeY(); + if (Yrange == 0) Yrange = 1.0; + yMin -= percent * Yrange; + yMax += percent * Yrange; +} + +void VisibleWindow::ZoomOutX(double percent) +{ + percent /= 2; + double Xrange = rangeX(); + if (Xrange == 0) Xrange = 1.0; + xMin -= percent * Xrange; + xMax += percent * Xrange; +} + +void VisibleWindow::ZoomOutY(double percent) +{ + percent /= 2; + double Yrange = rangeY(); + if (Yrange == 0) Yrange = 1.0; + yMin -= percent * Yrange; + yMax += percent * Yrange; +} + +double VisibleWindow::rangeX() const +{ + return xMax - xMin; +} + +double VisibleWindow::rangeY() const +{ + return yMax - yMin; +} + +bool VisibleWindow::inBoundX(QPointF point) const +{ + return point.x() >= xMin && point.x() <= xMax; +} + +bool VisibleWindow::inBoundY(QPointF point) const +{ + return point.y() >= yMin && point.y() <= yMax; +} + +bool VisibleWindow::inBound(QPointF point) const +{ + return inBoundX(point) && inBoundY(point); +} diff --git a/metavis/Plott.h b/metavis/Plott.h new file mode 100644 index 0000000000000000000000000000000000000000..f4d44f714eb148fb0a621f837c89a0082f78551d --- /dev/null +++ b/metavis/Plott.h @@ -0,0 +1,83 @@ +#pragma once + +#include <QWidget> +#include <QPointF> +#include <QPainter> +#include <QMenu> + + + +struct VisibleWindow { + double xMin, xMax, yMin, yMax; + VisibleWindow(double xMin, double xMax, double yMin, double yMax): + xMin(xMin), xMax(xMax), yMin(yMin), yMax(yMax){} + void moveUp(double percent); + void moveDown(double percent); + void moveLeft(double percent); + void moveRight(double percent); + void ZoomIn(double percent); + void ZoomInX(double percent); + void ZoomInY(double percent); + void ZoomOut(double percent); + void ZoomOutX(double percent); + void ZoomOutY(double percent); + double rangeX() const; + double rangeY() const; + bool inBoundX(QPointF point) const; + bool inBoundY(QPointF point) const; + bool inBound(QPointF point) const; +}; + +class Plott : public QWidget +{ + Q_OBJECT + +public: + //Grid + bool enableGrid = false; + + Plott(QWidget *parent); + ~Plott(); + //Setter + void setVisibleWindow(double xMin, double xMax, double yMin, double yMax); + void setDefaultVisibleWindow(double xMin, double xMax, double yMin, double yMax); + void setAxisLegend(QString xAxisLegend, QString yAxisLegend); + //sets the window to the default window + void virtual resetToDefaultWindow(); + + QPointF transformGraphToView(QPointF graphpoint) const; + QPointF transformGraphToView(QPointF graphpoint, double stregth_factorX, double stregth_factorY, QPointF translation) const; + QPointF transformViewToGraph(QPointF viewpoint) const; + QPointF transformViewToGraph(QPointF viewpoint, double stregth_factorX, double stregth_factorY, QPointF translation) const; +protected: + //Represent the visual area in graph space that is shown in view space + VisibleWindow window = VisibleWindow(0.0, 10, 0, 10); + VisibleWindow defaultWindow = VisibleWindow(0.0, 10, 0, 10); + + + + virtual void drawData(QPainter& painter); + //Returns the area in view space where + QRect getDisplayRect() const; + + //Display + virtual void paintEvent(QPaintEvent* event) override; + //Process user input events + virtual void keyPressEvent(QKeyEvent* event) override; + virtual void wheelEvent(QWheelEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; + virtual void mousePressEvent(QMouseEvent* event) override; + virtual void mouseReleaseEvent(QMouseEvent* event) override; + + virtual void handleSelectedWindow(VisibleWindow& window, QMouseEvent* event); + void setWindow(VisibleWindow& window); +private: + + //Axis Legends + QString xAxisLegend = "X Legende", yAxisLegend = "Y Legend"; + //MouseControl + bool isLeftMousePressed = false; + bool isRightMousePressed = false; + QPointF oldPositionForMouseEvent; + QPointF mousePressedValue; +}; diff --git a/metavis/Project.cpp b/metavis/Project.cpp new file mode 100644 index 0000000000000000000000000000000000000000..574e7b4322a60009f36cbae499e6fb30120ee9c5 --- /dev/null +++ b/metavis/Project.cpp @@ -0,0 +1,43 @@ +#include "pch.h" +#include "Project.h" + +void Project::addFile(std::string metaLogFile) +{ + //If file exist and can be open + runList.push_back(metaLogFile); +} + +void Project::removeRunData(RunData* data) +{ + //Remove all series with this rundata + auto iter = std::remove_if(runList.begin(), runList.end(), [data](RunData& run) {return (data == &run); }); + runList.erase(iter, runList.end()); +} + + + +void Project::removeLastData() +{ + if(!runList.empty()) runList.pop_back(); +} + +void Project::removeIndex(int index) +{ + //TODO +} + +void Project::removeName(std::string fileName) +{ + auto iter = std::remove_if(runList.begin(), runList.end(), [fileName](RunData& series) {return (fileName == series.name); }); + runList.erase(iter, runList.end()); +} + +void Project::clearDataList() +{ + runList.clear(); +} + +std::list<RunData>& Project::getRunList() +{ + return runList; +} diff --git a/metavis/Project.h b/metavis/Project.h new file mode 100644 index 0000000000000000000000000000000000000000..56f3d09d7b4123e609c65a29c4feac58e512d61d --- /dev/null +++ b/metavis/Project.h @@ -0,0 +1,20 @@ +#pragma once +#include <vector> +#include <string> +#include "RunData.h" + + +class Project +{ +public: + void addFile(std::string fileName); + void removeRunData(RunData* data); + void removeLastData(); + void removeIndex(int index); + void removeName(std::string fileName); + void clearDataList(); + std::list<RunData>& getRunList(); +private: + std::list<RunData> runList; +}; + diff --git a/metavis/ProjectManager.cpp b/metavis/ProjectManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..171db3771089c1e05119a3d64936300db4b15f32 --- /dev/null +++ b/metavis/ProjectManager.cpp @@ -0,0 +1,229 @@ +#include "pch.h" +#include <QTreeWidget> +#include <QAction> +#include <QMenu> +#include "ProjectManager.h" +#include <QtConcurrent> + +ProjectManager::ProjectManager(metavis* owner) + : QWidget(owner), 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); + connect(treeWidget, &QTreeWidget::itemActivated, this, &ProjectManager::itemClicked); + projectItem = new QTreeWidgetItem(treeWidget, QStringList("Project"), ProjectManager::Types::ProjectHeader); + projectItem->setExpanded(true); + gridlayout->addWidget(treeWidget); + this->setLayout(gridlayout); + + + //Create Menus + projectMenu = new QMenu(this); + QAction* metaLogAddAction = new QAction("Add .csv-File..."); + QIcon test(":/metavis/Resources/file.svg"); + metaLogAddAction->setIcon(test); + connect(metaLogAddAction, &QAction::triggered, this, &ProjectManager::openFileDialog); + //connect(metaLogAddAction, &QAction::triggered, this, [this]() { project.addFile(); }); + QAction* removeAllFilesAction = new QAction("Remove all .csv-Files"); + removeAllFilesAction->setIcon(QIcon(":/metavis/Resources/csv_remove.svg")); + connect(removeAllFilesAction, &QAction::triggered, this, [this]() { + for (RunData& data : project.getRunList()) { + this->owner->removeRunDataFromAllViews(&data); + } + project.clearDataList(); + createTreeWidget(); + }); + projectMenu->addAction(metaLogAddAction); + projectMenu->addAction(removeAllFilesAction); + + metalogFileMenu = new QMenu(this); + QIcon removeIcon(":/metavis/Resources/close_big_red.svg"); + removeMetalogFileAction = new QAction("Remove"); + removeMetalogFileAction->setIcon(removeIcon); + //connect(removeMetalogFileAction, &QAction::triggered, this, [this]() {project.clearDataList(); }); + selectAction = new QAction("Show"); + selectAction->setIcon(QIcon(":/metavis/Resources/arrow_right.svg")); + metalogFileMenu->addAction(selectAction); + metalogFileMenu->addAction(removeMetalogFileAction); + singleRunMenu = new QMenu(this); + singleRunMenu->addAction(selectAction); + connect(&watcher, &QFutureWatcher<void>::finished, this, &ProjectManager::displayNewFile); + + //Settings + QSettings settings("settings.ini", QSettings::IniFormat, this); + settings.beginGroup("ProjectManager"); + lastPath = settings.value("path", "").toString(); + settings.endGroup(); +} + +ProjectManager::~ProjectManager() +{ + //Settings + QSettings settings("settings.ini", QSettings::IniFormat, this); + settings.beginGroup("ProjectManager"); + settings.setValue("path", lastPath); + settings.endGroup(); +} + + + + +void ProjectManager::openFileDialog() +{ + qDebug() << "openFile"; + this->lastPath != ""; + QStringList pathList = QFileDialog::getOpenFileNames(this, "Open logFile",(this->lastPath != "")? lastPath :QStandardPaths::displayName(QStandardPaths::DesktopLocation), "Logfile (*.csv)"); + if (pathList.isEmpty()) { + qDebug() << "No file selected"; + return; + } + QDir d = QFileInfo(pathList.front()).absoluteDir(); + lastPath = d.absolutePath(); + owner->showStatusBarLoading(); + //this->loadingFuture = std::async(std::launch::async, static_cast<void(ProjectManager::*)(std::list<QString>)>(&ProjectManager::openFiles), this, stdList); + future = QtConcurrent::run(this, &ProjectManager::openFiles, pathList); + watcher.setFuture(future); +} + + +void ProjectManager::openFiles(QStringList pathList) +{ + int size = project.getRunList().size(); + for (QString& string: pathList) { + qDebug() << "file:" << string; + project.addFile(string.toStdString()); + } + for (std::list<RunData>::iterator iter = std::next(project.getRunList().begin(), size); iter != project.getRunList().end(); iter++) { + //owner->addRunDataToCompareViews(&*iter); + } +} + +void ProjectManager::displayNewFile() +{ + owner->selectRunData(&project.getRunList().back()); + owner->selectSingleRun(&project.getRunList().back().singleRunList.front()); + createTreeWidget(); + owner->clearStatusBar(); +} + + + + + +void ProjectManager::createTreeWidget() +{ + //remove all clidren + for (QTreeWidgetItem* item : projectItem->takeChildren()) { + delete item; + } + for (RunData& run : project.getRunList()) { + qDebug() << "RunList add" << QString::fromStdString(run.name); + QTreeWidgetItem* item = new QTreeWidgetItem(projectItem, QStringList(QString::fromStdString(run.name)), ProjectManager::Types::LogFile); + QVariant variant(QVariant::fromValue(static_cast<void*>(&run))); + item->setData(1, Qt::UserRole, variant); + item->setExpanded(true); + int count = 0; + for (SingleRun& srun : run.singleRunList) { + QTreeWidgetItem* runitem = new QTreeWidgetItem(item, QStringList(QString::fromStdString(run.name) + "[" + QString::number(count) + "]"), ProjectManager::Types::Run); + QVariant srunVariant(QVariant::fromValue(static_cast<void*>(&srun))); + runitem->setData(1, Qt::UserRole, srunVariant); + count++; + } + } + +} + +void ProjectManager::showItem(QTreeWidgetItem* item) +{ + switch (item->type()) { + case LogFile: + { + RunData* data = static_cast<RunData*>(item->data(1, Qt::UserRole).value<void*>()); + if (data != nullptr) { + owner->selectRunData(data); + } + break; + } + case Run: + { + SingleRun* data = static_cast<SingleRun*>(item->data(1, Qt::UserRole).value<void*>()); + if (data != nullptr) { + owner->selectSingleRun(data); + } + break; + } + default: + break; + } + +} + + + + + +void ProjectManager::itemClicked(QTreeWidgetItem* item, int column) +{ + switch (item->type()) { + case Types::Graph: + break; + case Types::GraphManger: + break; + case Types::LogFile: + case Types::Run: + showItem(item); + break; + case Types::LogManger: + break; + default: + break; + } +} + + +void ProjectManager::prepareMenu(const QPoint& pos) { + QTreeWidgetItem* item = treeWidget->itemAt(pos); + if (item == nullptr) return; + switch (item->type()) { + case Types::Graph: + //graphMenu->exec(treeWidget->mapToGlobal(pos)); + break; + case Types::GraphManger: + break; + case Types::Run: + { + QAction* used = singleRunMenu->exec(treeWidget->mapToGlobal(pos)); + if (used == selectAction) { + showItem(item); + } + break; + } + case Types::LogFile: + { + QAction* used = metalogFileMenu->exec(treeWidget->mapToGlobal(pos)); + if (used == removeMetalogFileAction) { + RunData* data = static_cast<RunData*>(item->data(1, Qt::UserRole).value<void*>()); + owner->removeRunDataFromAllViews(data); + project.removeRunData(data); + createTreeWidget(); + } + else if (used == selectAction) { + showItem(item); + } + break; + } + + case Types::ProjectHeader: + projectMenu->exec(treeWidget->mapToGlobal(pos)); + break; + default: + break; + } + +} + diff --git a/metavis/ProjectManager.h b/metavis/ProjectManager.h new file mode 100644 index 0000000000000000000000000000000000000000..54515ced0362bc6b5170b573aa50fc675490e237 --- /dev/null +++ b/metavis/ProjectManager.h @@ -0,0 +1,45 @@ +#pragma once +#include <QTreeWidget> +#include <QWidget> +#include <list> +#include "metavis.h" +#include <future> +#include <QStringList> +#include <QFuture> +#include <QFutureWatcher> +#include "Project.h" +class metavis; + +class ProjectManager : public QWidget +{ + Q_OBJECT + +public: + ProjectManager(metavis* owner); + ~ProjectManager(); + + + Project project; + void openFileDialog(); + void openFiles(QStringList pathlist); + QMenu* projectMenu; +private: + std::future<void> loadingFuture = std::async(std::launch::async, []() {}); + QFuture<void> future; + QFutureWatcher<void> watcher = QFutureWatcher<void>(this); + metavis* owner; + QTreeWidget* treeWidget; + QTreeWidgetItem* projectItem; + void displayNewFile(); + QMenu* metalogFileMenu; + QMenu* singleRunMenu; + QAction* removeMetalogFileAction; + QAction* selectAction; + enum Types{ LogManger = 1001, LogFile, Run, GraphManger, Graph, ProjectHeader}; + QString lastPath; + void createTreeWidget(); + void showItem(QTreeWidgetItem* item); +private slots: + void prepareMenu(const QPoint& pos); + void itemClicked(QTreeWidgetItem* item, int column); +}; diff --git a/metavis/RangeSlider.cpp b/metavis/RangeSlider.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5035ec76ed42712c8fdc503806d6d94de16b2515 --- /dev/null +++ b/metavis/RangeSlider.cpp @@ -0,0 +1,256 @@ +#include "pch.h" +#include "RangeSlider.h" +#include <QDebug> +#include <QComboBox> + +#include "util.h" + +RangeSlider::RangeSlider(QWidget *parent) + : QWidget(parent), sliderRangeColor(200,200,200), rangeColor(0,122,204) +{ + QVBoxLayout* layoutOuter = new QVBoxLayout(this); + layoutOuter->setContentsMargins(0, 0, 0, 0); + QHBoxLayout* buttonPanelLayout = new QHBoxLayout(); + buttonPanelLayout->setContentsMargins(0, 0, 0, 0); + layoutOuter->addLayout(buttonPanelLayout); + layoutOuter->insertStretch(1); + titleLabel = new QLabel("Title"); + buttonPanelLayout->addWidget(titleLabel); + buttonPanelLayout->insertStretch(1); + + QComboBox* combobox = new QComboBox(this); + combobox->addItem("Range"); + combobox->addItem("Iteration"); + connect(combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + this, [this](int index) {this->setMode((DisplayMode)index); }); + buttonPanelLayout->addWidget(combobox); + this->setMaximumHeight(70); + this->setMinimumHeight(70); +} + +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::setItertionSliderValue(int value) +{ + value = std::clamp(value, minRange, maxRange); + itertionSliderValue = value; + emit minChanged(itertionSliderValue); + emit maxChanged(itertionSliderValue); +} + +void RangeSlider::setRange(int min, int max) +{ + if (min > max) { + std::swap(min, max); + } + minRange = min; + maxRange = max; +} + +int RangeSlider::getMinRange() const +{ + return minRange; +} + +int RangeSlider::getMaxRange() const +{ + return maxRange; +} + +void RangeSlider::setMinRange(int value) +{ + if (value > maxRange) { + minRange = maxRange; + maxRange = value; + } + else { + minRange = value; + } + emit minChanged(minSliderValue); +} + +void RangeSlider::setMaxRange(int value) +{ + if (value < minRange) { + maxRange = minRange; + minRange = value; + } + else { + maxRange = value; + } + emit maxChanged(maxSliderValue); +} + +void RangeSlider::setMode(DisplayMode mode) +{ + this->mode = mode; + switch (mode) { + case Iteration: + emit minChanged(itertionSliderValue); + emit maxChanged(itertionSliderValue); + break; + default: + case Range: + emit minChanged(minSliderValue); + emit maxChanged(maxSliderValue); + break; + } + update(); +} + +void RangeSlider::setTitle(QString string) +{ + titleLabel->setText(string); +} + +void RangeSlider::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing); + painter.setPen(sliderRangeColor); + //painter.setBrush(sliderRangeColor); + //FindMiddle + int middle = 35; + painter.drawRect(QRect(QPoint(minPos(), middle + 2), QPoint(maxPos(), middle - 2))); + painter.setPen(Qt::black); + painter.setFont(QFont("Arial", 8)); + QRect textRect(0, 0, 30, 9); + textRect.moveCenter(QPoint(minPos(), middle + 20)); + painter.drawText(textRect, Qt::AlignCenter, QString::number(this->minRange, 'f', 0)); + textRect.moveCenter(QPoint(maxPos(), middle + 20)); + painter.drawText(textRect, Qt::AlignCenter, QString::number(this->maxRange, 'f', 0)); + + + + + //DrawSlider + QPen pen(rangeColor); + pen.setWidth(2); + painter.setPen(pen); + painter.setBrush(sliderRangeColor); + switch (mode) { + case Iteration: + { + double itertionSliderAlpha = util::inverseLinearInterpolation(minRange, maxRange, itertionSliderValue); + double itertionSliderPos = util::linearInterpolate(minPos(), maxPos(), itertionSliderAlpha); + + painter.drawEllipse(QPointF(itertionSliderPos, (double)middle + 1), 10, 10); + painter.setPen(Qt::black); + textRect.moveCenter(QPoint(itertionSliderPos, middle + 20)); + painter.drawText(textRect, Qt::AlignCenter, QString::number(this->itertionSliderValue, 'f', 0)); + break; + } + default: + case Range: + { + 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); + painter.drawEllipse(QPointF(minSliderPos, (double)middle + 1), 10, 10); + painter.drawEllipse(QPointF(maxSliderPos, (double)middle + 1), 10, 10); + painter.setPen(Qt::black); + textRect.moveCenter(QPoint(minSliderPos, middle + 20)); + painter.drawText(textRect, Qt::AlignCenter, QString::number(this->minSliderValue, 'f', 0)); + textRect.moveCenter(QPoint(maxSliderPos, middle + 20)); + painter.drawText(textRect, Qt::AlignCenter, QString::number(this->maxSliderValue, 'f', 0)); + break; + } + } +} + +void RangeSlider::mouseMoveEvent(QMouseEvent* event) +{ + if (childAt(event->pos()) != nullptr) { + return; + } + updateSliderPosition(event); +} + +void RangeSlider::mousePressEvent(QMouseEvent* event) +{ + if (childAt(event->pos()) != nullptr) { + return; + } + 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); + + + + double value = util::linearInterpolate(minRange, maxRange, alpha); + switch (mode) { + case Iteration: + setItertionSliderValue(value); + break; + default: + case Range: + double distanceToMin = std::abs(minSliderValue - value); + double distanceToMax = std::abs(maxSliderValue - value); + if (distanceToMin < distanceToMax) { + setMinSliderValue(value); + } + else { + setMaxSliderValue(value); + } + break; + } + + update(); +} + +int RangeSlider::maxPos() +{ + return rect().right() - 11; +} + +int RangeSlider::minPos() +{ + return rect().left() + 11; +} + diff --git a/metavis/RangeSlider.h b/metavis/RangeSlider.h new file mode 100644 index 0000000000000000000000000000000000000000..4860c17cd485a6d463e09a5423a0916c3fee692e --- /dev/null +++ b/metavis/RangeSlider.h @@ -0,0 +1,46 @@ +#pragma once +#include <QColor> +#include <QWidget> +#include <QString> +#include <QLabel> + + + +class RangeSlider : public QWidget +{ + Q_OBJECT + + +public: + enum DisplayMode{ Range, Iteration}; + RangeSlider(QWidget *parent); + ~RangeSlider(); + void setMinSliderValue(int value); + void setMaxSliderValue(int value); + void setItertionSliderValue(int value); + void setRange(int min, int max); + int getMinRange() const; + int getMaxRange() const; + void setMinRange(int value); + void setMaxRange(int value); + void setMode(DisplayMode mode); + void setTitle(QString string); + //Visual Settings: + void setRangeColor(QColor color); + void setSliderRangeColor(QColor color); +private: + int minRange = 0, maxRange = 100, minSliderValue = 0, maxSliderValue = 100, itertionSliderValue = 50; + QColor rangeColor, sliderRangeColor; + DisplayMode mode = DisplayMode::Range; + void paintEvent(QPaintEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void updateSliderPosition(QMouseEvent* event); + QLabel* titleLabel; + int maxPos(); + int minPos(); + +signals: + void maxChanged(int max); + void minChanged(int min); +}; diff --git a/metavis/Resources/3-up-triangle.svg b/metavis/Resources/3-up-triangle.svg new file mode 100644 index 0000000000000000000000000000000000000000..dac8cf75bf82f8fda999650cc77ab14af3700e0a --- /dev/null +++ b/metavis/Resources/3-up-triangle.svg @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--U+25B3--> +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="-40 0 640 180"> + <polygon points="20 153 90 38 160 153" fill="none" stroke="#000" stroke-width="20" /> + <polygon points="220 153 290 38 360 153" fill="none" stroke="#000" stroke-width="20" /> + <polygon points="420 153 490 38 560 153" fill="none" stroke="#000" stroke-width="20" /> +</svg> \ No newline at end of file diff --git a/metavis/Resources/arrow_right.svg b/metavis/Resources/arrow_right.svg new file mode 100644 index 0000000000000000000000000000000000000000..7df925e87c0ad7d66bf25108920cd0ffff9b3610 --- /dev/null +++ b/metavis/Resources/arrow_right.svg @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<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" + width="95.312px" height="95.312px" viewBox="0 0 95.312 95.312" style="enable-background:new 0 0 95.312 95.312;" + xml:space="preserve"> +<g> + <path d="M63.408,89.924V77.131c0-1.021-0.828-1.849-1.849-1.849H33.137v0.006h-1.333c-8.453,0-15.33-6.875-15.33-15.33 + c0-8.451,6.877-15.329,15.33-15.329l34.397-0.025L52.9,57.854c-0.688,0.686-0.688,1.901,0,2.588l8.267,8.266 + c0.343,0.344,0.81,0.537,1.294,0.537s0.951-0.193,1.294-0.537l31.021-31.022c0.717-0.715,0.717-1.873,0-2.589L63.755,4.076 + c-0.716-0.714-1.873-0.714-2.588,0L52.9,12.341c-0.344,0.343-0.537,0.809-0.537,1.294s0.193,0.951,0.537,1.295l13.301,13.202 + l-34.396,0.023C14.268,28.155,0,42.422,0,59.958c0,17.537,14.268,31.806,31.805,31.806l29.756,0.009 + C62.58,91.772,63.408,90.944,63.408,89.924z"/> +</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> diff --git a/metavis/Resources/assignButton.svg b/metavis/Resources/assignButton.svg new file mode 100644 index 0000000000000000000000000000000000000000..bf45e00ad277879130cad9dc7b02c0d78161f9d8 --- /dev/null +++ b/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> \ No newline at end of file diff --git a/metavis/Resources/binaryIcon.svg b/metavis/Resources/binaryIcon.svg new file mode 100644 index 0000000000000000000000000000000000000000..e73bcd5c3e4131a45c545fcf75cf33ed3ec8ce76 --- /dev/null +++ b/metavis/Resources/binaryIcon.svg @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<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 261.501 261.501" style="enable-background:new 0 0 261.501 261.501;" xml:space="preserve"> +<g> + <path d="M37.984,161.696c0,4.142,3.358,7.5,7.5,7.5s7.5-3.358,7.5-7.5v-58.627c0-3.034-1.827-5.768-4.63-6.929 + c-2.801-1.16-6.028-0.519-8.173,1.626L21.177,116.77c-2.929,2.929-2.929,7.678,0,10.606c2.929,2.929,7.678,2.929,10.606,0 + l6.201-6.201V161.696z"/> + <path d="M82.279,31.807l6.201-6.201v40.521c0,4.142,3.358,7.5,7.5,7.5s7.5-3.358,7.5-7.5V7.5c0-3.034-1.827-5.768-4.63-6.929 + c-2.801-1.16-6.028-0.519-8.173,1.626L71.673,21.201c-2.929,2.929-2.929,7.678,0,10.606C74.602,34.736,79.351,34.736,82.279,31.807 + z"/> + <path d="M167.815,96.14c-2.801-1.16-6.028-0.519-8.173,1.626l-19.004,19.004c-2.929,2.929-2.929,7.678,0,10.606 + c2.929,2.929,7.678,2.929,10.606,0l6.201-6.201v40.521c0,4.142,3.358,7.5,7.5,7.5s7.5-3.358,7.5-7.5v-58.627 + C172.445,100.036,170.618,97.301,167.815,96.14z"/> + <path d="M135.217,188.445c-2.801-1.16-6.028-0.519-8.173,1.626l-19.004,19.004c-2.929,2.929-2.929,7.678,0,10.606 + c2.929,2.929,7.678,2.929,10.606,0l6.201-6.201v22.197c0,4.142,3.358,7.5,7.5,7.5s7.5-3.358,7.5-7.5v-40.303 + C139.847,192.34,138.02,189.606,135.217,188.445z"/> + <path d="M122.504,143.42v-22.074c0-14.213-11.563-25.776-25.776-25.776h-0.369c-14.213,0-25.776,11.563-25.776,25.776v22.074 + c0,14.213,11.563,25.776,25.776,25.776h0.369C110.941,169.196,122.504,157.633,122.504,143.42z M107.504,143.42 + c0,5.942-4.834,10.776-10.776,10.776h-0.369c-5.942,0-10.776-4.834-10.776-10.776v-22.074c0-5.942,4.834-10.776,10.776-10.776 + h0.369c5.942,0,10.776,4.834,10.776,10.776V143.42z"/> + <path d="M216.745,95.569h-0.369c-14.213,0-25.776,11.563-25.776,25.776v22.074c0,14.213,11.563,25.776,25.776,25.776h0.369 + c14.213,0,25.776-11.563,25.776-25.776v-22.074C242.521,107.133,230.958,95.569,216.745,95.569z M227.521,143.42 + c0,5.942-4.834,10.776-10.776,10.776h-0.369c-5.942,0-10.776-4.834-10.776-10.776v-22.074c0-5.942,4.834-10.776,10.776-10.776 + h0.369c5.942,0,10.776,4.834,10.776,10.776V143.42z"/> + <path d="M147.224,0h-0.369c-14.213,0-25.776,11.563-25.776,25.776v22.074c0,14.213,11.563,25.776,25.776,25.776h0.369 + c4.142,0,7.5-3.358,7.5-7.5s-3.358-7.5-7.5-7.5h-0.369c-5.942,0-10.776-4.834-10.776-10.776V25.776 + c0-5.942,4.834-10.776,10.776-10.776h0.369c5.942,0,10.776,4.834,10.776,10.776c0,4.142,3.358,7.5,7.5,7.5s7.5-3.358,7.5-7.5 + C173.001,11.563,161.438,0,147.224,0z"/> + <path d="M216.745,0h-0.369c-14.213,0-25.776,11.563-25.776,25.776v22.074c0,14.213,11.563,25.776,25.776,25.776h0.369 + c14.213,0,25.776-11.563,25.776-25.776V25.776C242.521,11.563,230.958,0,216.745,0z M227.521,47.851 + c0,5.942-4.834,10.776-10.776,10.776h-0.369c-5.942,0-10.776-4.834-10.776-10.776V25.776c0-5.942,4.834-10.776,10.776-10.776h0.369 + c5.942,0,10.776,4.834,10.776,10.776V47.851z"/> + <path d="M61.682,187.874h-0.369c-14.213,0-25.776,11.563-25.776,25.776v22.074c0,14.213,11.563,25.776,25.776,25.776h0.369 + c14.213,0,25.776-11.563,25.776-25.776V213.65C87.459,199.438,75.896,187.874,61.682,187.874z M72.459,235.725 + c0,5.942-4.834,10.776-10.776,10.776h-0.369c-5.942,0-10.776-4.834-10.776-10.776V213.65c0-5.942,4.834-10.776,10.776-10.776h0.369 + c5.942,0,10.776,4.834,10.776,10.776V235.725z"/> +</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> diff --git a/metavis/Resources/close.svg b/metavis/Resources/close.svg new file mode 100644 index 0000000000000000000000000000000000000000..858c231cf359d2f8699b1bd2ff8433590c9628b5 --- /dev/null +++ b/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> \ No newline at end of file diff --git a/metavis/Resources/close_Big.svg b/metavis/Resources/close_Big.svg new file mode 100644 index 0000000000000000000000000000000000000000..9a22cc364f05f55360ad9e78b6dead1ceecfdbc6 --- /dev/null +++ b/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> \ No newline at end of file diff --git a/metavis/Resources/close_big_red.svg b/metavis/Resources/close_big_red.svg new file mode 100644 index 0000000000000000000000000000000000000000..583a25e785f39d1fd21fd1fc3dd6a73415bb1c57 --- /dev/null +++ b/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> \ No newline at end of file diff --git a/metavis/Resources/close_red.svg b/metavis/Resources/close_red.svg new file mode 100644 index 0000000000000000000000000000000000000000..346ba8dd9cbff3b6a4c7026b6db6c281a7b5d2ae --- /dev/null +++ b/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> \ No newline at end of file diff --git a/metavis/Resources/csv.svg b/metavis/Resources/csv.svg new file mode 100644 index 0000000000000000000000000000000000000000..0071098ba6abc86d33e4f22191e135e0b611009f --- /dev/null +++ b/metavis/Resources/csv.svg @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 318.188 318.188" style="enable-background:new 0 0 318.188 318.188;" xml:space="preserve"> +<g> + <g> + <g> + + <polygon points="112.09,123.663 112.09,123.662 118.286,113.621 124.548,123.662 134.588,123.662 123.647,107.909 133.82,91.54 + 123.911,91.54 118.33,101.472 112.53,91.54 102.906,91.54 112.925,107.228 102.269,123.663 "/> + <path d="M201.02,249.514c-0.339,1.27-0.73,3.015-1.174,5.236c-0.445,2.222-0.741,4.073-0.889,5.555 + c-0.127-2.053-0.847-5.691-2.158-10.918l-6.316-23.519h-14.092l15.139,46.401h14.759l15.202-46.401h-14.027L201.02,249.514z"/> + + <path d="M283.149,52.723L232.624,2.197C231.218,0.79,229.311,0,227.321,0H40.342c-4.142,0-7.5,3.358-7.5,7.5v303.188 + c0,4.142,3.358,7.5,7.5,7.5h237.504c4.142,0,7.5-3.358,7.5-7.5V58.025C285.346,56.036,284.556,54.129,283.149,52.723z + M234.821,25.606l24.918,24.919h-24.918V25.606z M47.842,15h171.979v10.263H47.842V15z M47.842,303.188V40.263h171.979v17.763 + c0,4.143,3.358,7.5,7.5,7.5h43.024v237.662H47.842z"/> + + <path d="M122.372,235.484c1.969,0,3.809,0.275,5.523,0.826c1.713,0.55,3.428,1.227,5.141,2.031l3.841-9.871 + c-4.57-2.18-9.362-3.27-14.378-3.27c-4.591,0-8.585,0.98-11.98,2.937c-3.396,1.957-5.999,4.755-7.808,8.395 + c-1.81,3.64-2.714,7.86-2.714,12.663c0,7.682,1.867,13.553,5.602,17.615c3.734,4.063,9.104,6.094,16.107,6.094 + c4.888,0,9.268-0.857,13.14-2.57v-10.602c-1.947,0.805-3.883,1.492-5.808,2.063c-1.926,0.571-3.915,0.857-5.967,0.857 + c-6.793,0-10.188-4.464-10.188-13.393c0-4.295,0.836-7.665,2.507-10.109C117.062,236.707,119.39,235.484,122.372,235.484z"/> + <path d="M163.57,244.594c-4.169-1.904-6.724-3.216-7.665-3.936c-0.942-0.719-1.412-1.533-1.412-2.443 + c-0.002-0.847,0.368-1.556,1.11-2.127c0.74-0.571,1.925-0.857,3.555-0.857c3.152,0,6.897,0.995,11.234,2.984l3.841-9.681 + c-4.994-2.222-9.892-3.333-14.694-3.333c-5.439,0-9.713,1.196-12.822,3.587c-3.111,2.392-4.666,5.724-4.666,9.997 + c0,2.285,0.365,4.264,1.095,5.936s1.851,3.152,3.364,4.443s3.782,2.624,6.809,3.999c3.343,1.503,5.4,2.497,6.173,2.983 + c0.771,0.486,1.333,0.968,1.682,1.444c0.35,0.476,0.524,1.031,0.524,1.666c0,1.016-0.435,1.847-1.302,2.491 + c-0.868,0.647-2.233,0.969-4.095,0.969c-2.158,0-4.527-0.344-7.109-1.032c-2.581-0.687-5.067-1.645-7.458-2.872v11.172 + c2.264,1.079,4.443,1.836,6.538,2.27c2.095,0.434,4.687,0.65,7.775,0.65c3.703,0,6.93-0.619,9.681-1.856 + c2.75-1.238,4.856-2.973,6.315-5.205c1.461-2.232,2.191-4.787,2.191-7.665c0-3.131-0.777-5.729-2.333-7.792 + C170.346,248.323,167.569,246.393,163.57,244.594z"/> + + </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> +<g> +</g> +</svg> diff --git a/metavis/Resources/csv_remove.svg b/metavis/Resources/csv_remove.svg new file mode 100644 index 0000000000000000000000000000000000000000..1148d35d047129f13d2434d03e1814c2a77f45ba --- /dev/null +++ b/metavis/Resources/csv_remove.svg @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 318.188 318.188" style="enable-background:new 0 0 318.188 318.188;" xml:space="preserve"> +<g> + <g> + <g> + <rect x="182.882" y="155.008" width="33.713" height="15"/> + <rect x="101.592" y="132.689" width="33.713" height="15"/> + <rect x="182.882" y="132.689" width="33.713" height="15"/> + <rect x="182.882" y="88.053" width="33.713" height="15"/> + <rect x="182.882" y="110.371" width="33.713" height="15"/> + <rect x="101.592" y="155.008" width="33.713" height="15"/> + <polygon points="112.09,123.663 112.09,123.662 118.286,113.621 124.548,123.662 134.588,123.662 123.647,107.909 133.82,91.54 + 123.911,91.54 118.33,101.472 112.53,91.54 102.906,91.54 112.925,107.228 102.269,123.663 "/> + <path d="M201.02,249.514c-0.339,1.27-0.73,3.015-1.174,5.236c-0.445,2.222-0.741,4.073-0.889,5.555 + c-0.127-2.053-0.847-5.691-2.158-10.918l-6.316-23.519h-14.092l15.139,46.401h14.759l15.202-46.401h-14.027L201.02,249.514z"/> + <rect x="142.457" y="110.371" width="33.713" height="15"/> + <rect x="142.457" y="88.053" width="33.713" height="15"/> + <path d="M283.149,52.723L232.624,2.197C231.218,0.79,229.311,0,227.321,0H40.342c-4.142,0-7.5,3.358-7.5,7.5v303.188 + c0,4.142,3.358,7.5,7.5,7.5h237.504c4.142,0,7.5-3.358,7.5-7.5V58.025C285.346,56.036,284.556,54.129,283.149,52.723z + M234.821,25.606l24.918,24.919h-24.918V25.606z M47.842,15h171.979v10.263H47.842V15z M47.842,303.188V40.263h171.979v17.763 + c0,4.143,3.358,7.5,7.5,7.5h43.024v237.662H47.842z"/> + <rect x="142.457" y="132.689" width="33.713" height="15"/> + <path d="M122.372,235.484c1.969,0,3.809,0.275,5.523,0.826c1.713,0.55,3.428,1.227,5.141,2.031l3.841-9.871 + c-4.57-2.18-9.362-3.27-14.378-3.27c-4.591,0-8.585,0.98-11.98,2.937c-3.396,1.957-5.999,4.755-7.808,8.395 + c-1.81,3.64-2.714,7.86-2.714,12.663c0,7.682,1.867,13.553,5.602,17.615c3.734,4.063,9.104,6.094,16.107,6.094 + c4.888,0,9.268-0.857,13.14-2.57v-10.602c-1.947,0.805-3.883,1.492-5.808,2.063c-1.926,0.571-3.915,0.857-5.967,0.857 + c-6.793,0-10.188-4.464-10.188-13.393c0-4.295,0.836-7.665,2.507-10.109C117.062,236.707,119.39,235.484,122.372,235.484z"/> + <path d="M163.57,244.594c-4.169-1.904-6.724-3.216-7.665-3.936c-0.942-0.719-1.412-1.533-1.412-2.443 + c-0.002-0.847,0.368-1.556,1.11-2.127c0.74-0.571,1.925-0.857,3.555-0.857c3.152,0,6.897,0.995,11.234,2.984l3.841-9.681 + c-4.994-2.222-9.892-3.333-14.694-3.333c-5.439,0-9.713,1.196-12.822,3.587c-3.111,2.392-4.666,5.724-4.666,9.997 + c0,2.285,0.365,4.264,1.095,5.936s1.851,3.152,3.364,4.443s3.782,2.624,6.809,3.999c3.343,1.503,5.4,2.497,6.173,2.983 + c0.771,0.486,1.333,0.968,1.682,1.444c0.35,0.476,0.524,1.031,0.524,1.666c0,1.016-0.435,1.847-1.302,2.491 + c-0.868,0.647-2.233,0.969-4.095,0.969c-2.158,0-4.527-0.344-7.109-1.032c-2.581-0.687-5.067-1.645-7.458-2.872v11.172 + c2.264,1.079,4.443,1.836,6.538,2.27c2.095,0.434,4.687,0.65,7.775,0.65c3.703,0,6.93-0.619,9.681-1.856 + c2.75-1.238,4.856-2.973,6.315-5.205c1.461-2.232,2.191-4.787,2.191-7.665c0-3.131-0.777-5.729-2.333-7.792 + C170.346,248.323,167.569,246.393,163.57,244.594z"/> + <rect x="142.457" y="155.008" width="33.713" height="15"/> + <polygon fill="#EF2D56" transform="translate(150.0 2.0), scale(0.3)" points="386.813,0 245,141.812 103.188,0 0,103.188 141.813,245 0,386.812 103.187,489.999 245,348.187 386.813,490 + 490,386.812 348.187,244.999 490,103.187 "/> + </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> +<g> +</g> +</svg> diff --git a/metavis/Resources/file.svg b/metavis/Resources/file.svg new file mode 100644 index 0000000000000000000000000000000000000000..2b4141f372d49d20f1b3f33556d9468863a073bf --- /dev/null +++ b/metavis/Resources/file.svg @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 450 450" style="enable-background:new 0 0 450 450;" xml:space="preserve"> +<g> + <path d="M281.213,0H60v450h330V108.787L281.213,0z M290,51.213L338.787,100H290V51.213z M90,420V30h170v100h100v290H90z"/> + +</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> diff --git a/metavis/Resources/frame.svg b/metavis/Resources/frame.svg new file mode 100644 index 0000000000000000000000000000000000000000..a3760ae3cd797867c81f1919abf83b6a3557ab07 --- /dev/null +++ b/metavis/Resources/frame.svg @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"> +<g> + <g> + <polygon points="426.056,79.367 466.119,79.367 406.387,19.664 346.669,79.367 386.728,79.367 386.728,287.698 346.669,287.698 + 406.387,347.401 466.119,287.698 426.056,287.698 "/> + </g> +</g> +<g> + <g> + <polygon points="314.626,452.282 254.922,392.55 254.922,432.613 112.139,432.613 112.139,392.55 52.436,452.282 112.139,512 + 112.139,471.941 254.922,471.941 254.922,512 "/> + </g> +</g> +<g> + <g> + <path d="M45.881,0v367.065H321.18V0H45.881z M281.852,327.737H85.21V39.328h196.642V327.737z"/> + </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> +</g> +</svg> diff --git a/metavis/Resources/frame_hovered.svg b/metavis/Resources/frame_hovered.svg new file mode 100644 index 0000000000000000000000000000000000000000..2a6206bd843384d4e03f7e667da1be0eab9049c1 --- /dev/null +++ b/metavis/Resources/frame_hovered.svg @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve"> +<g> + <g> + <polygon points="426.056,79.367 466.119,79.367 406.387,19.664 346.669,79.367 386.728,79.367 386.728,287.698 346.669,287.698 + 406.387,347.401 466.119,287.698 426.056,287.698 "/> + </g> +</g> +<g> + <g> + <polygon points="314.626,452.282 254.922,392.55 254.922,432.613 112.139,432.613 112.139,392.55 52.436,452.282 112.139,512 + 112.139,471.941 254.922,471.941 254.922,512 "/> + </g> +</g> +<g> + <g> + <path d="M45.881,0v367.065H321.18V0H45.881z M281.852,327.737H85.21V39.328h196.642V327.737z"/> + <path fill="#ff9f00" d="M281.852,327.737H85.21V39.328h196.642V327.737z"/> + </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> +</g> +</svg> diff --git a/metavis/Resources/gridIcon.svg b/metavis/Resources/gridIcon.svg new file mode 100644 index 0000000000000000000000000000000000000000..e47984f5637f29f98fc732db74ec116098d1bcd1 --- /dev/null +++ b/metavis/Resources/gridIcon.svg @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 460.002 460.002" style="enable-background:new 0 0 460.002 460.002;" xml:space="preserve"> +<g> + <g> + <path d="M427.137,0H32.865C14.743,0,0,14.743,0,32.865v394.272c0,18.122,14.743,32.865,32.865,32.865h394.272 + c18.122,0,32.865-14.743,32.865-32.865V32.865C460.002,14.743,445.259,0,427.137,0z M137.305,399.443c0,8.284-6.716,15-15,15 + H56.251c-8.284,0-15-6.716-15-15v-66.054c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15V399.443z M137.305,263.028 + c0,8.284-6.716,15-15,15H56.251c-8.284,0-15-6.716-15-15v-66.054c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15V263.028z + M137.305,126.613c0,8.284-6.716,15-15,15H56.251c-8.284-0.001-15-6.717-15-15.001V60.559c0-8.284,6.716-15,15-15h66.054 + c8.284,0,15,6.716,15,15V126.613z M277.071,399.443c0,8.284-6.716,15-15,15h-66.054c-8.284,0-15-6.716-15-15v-66.054 + c0-8.284,6.715-15,15-15h66.054c8.284,0,15,6.716,15,15V399.443z M277.071,263.028c0,8.284-6.716,15-15,15h-66.054 + c-8.284,0-15-6.716-15-15v-66.054c0-8.284,6.715-15,15-15h66.054c8.284,0,15,6.716,15,15V263.028z M277.071,126.613 + c0,8.284-6.716,15-15,15h-66.054c-8.285-0.001-15-6.717-15-15.001V60.559c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15 + V126.613z M418.751,399.443c0,8.284-6.716,15-15,15h-66.054c-8.284,0-15-6.716-15-15v-66.054c0-8.284,6.716-15,15-15h66.054 + c8.284,0,15,6.716,15,15V399.443z M418.751,263.028c0,8.284-6.716,15-15,15h-66.054c-8.284,0-15-6.716-15-15v-66.054 + c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15V263.028z M418.751,126.612c0,8.284-6.716,15-15,15h-66.054 + c-8.284,0-15-6.716-15-15V60.559c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15V126.612z"/> + </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> +</g> +</svg> diff --git a/metavis/Resources/gridIcon_hovered.svg b/metavis/Resources/gridIcon_hovered.svg new file mode 100644 index 0000000000000000000000000000000000000000..dc8e4190ca258706ba32fc8063e6adfa27ecdbc6 --- /dev/null +++ b/metavis/Resources/gridIcon_hovered.svg @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 460.002 460.002" style="enable-background:new 0 0 460.002 460.002;" xml:space="preserve"> +<g> + <g> + <path fill="#ff9f00" d="M427.137,0H32.865C14.743,0,0,14.743,0,32.865v394.272c0,18.122,14.743,32.865,32.865,32.865h394.272 + c18.122,0,32.865-14.743,32.865-32.865V32.865C460.002,14.743,445.259,0,427.137,0z"/> + <path d="M427.137,0H32.865C14.743,0,0,14.743,0,32.865v394.272c0,18.122,14.743,32.865,32.865,32.865h394.272 + c18.122,0,32.865-14.743,32.865-32.865V32.865C460.002,14.743,445.259,0,427.137,0z M137.305,399.443c0,8.284-6.716,15-15,15 + H56.251c-8.284,0-15-6.716-15-15v-66.054c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15V399.443z M137.305,263.028 + c0,8.284-6.716,15-15,15H56.251c-8.284,0-15-6.716-15-15v-66.054c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15V263.028z + M137.305,126.613c0,8.284-6.716,15-15,15H56.251c-8.284-0.001-15-6.717-15-15.001V60.559c0-8.284,6.716-15,15-15h66.054 + c8.284,0,15,6.716,15,15V126.613z M277.071,399.443c0,8.284-6.716,15-15,15h-66.054c-8.284,0-15-6.716-15-15v-66.054 + c0-8.284,6.715-15,15-15h66.054c8.284,0,15,6.716,15,15V399.443z M277.071,263.028c0,8.284-6.716,15-15,15h-66.054 + c-8.284,0-15-6.716-15-15v-66.054c0-8.284,6.715-15,15-15h66.054c8.284,0,15,6.716,15,15V263.028z M277.071,126.613 + c0,8.284-6.716,15-15,15h-66.054c-8.285-0.001-15-6.717-15-15.001V60.559c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15 + V126.613z M418.751,399.443c0,8.284-6.716,15-15,15h-66.054c-8.284,0-15-6.716-15-15v-66.054c0-8.284,6.716-15,15-15h66.054 + c8.284,0,15,6.716,15,15V399.443z M418.751,263.028c0,8.284-6.716,15-15,15h-66.054c-8.284,0-15-6.716-15-15v-66.054 + c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15V263.028z M418.751,126.612c0,8.284-6.716,15-15,15h-66.054 + c-8.284,0-15-6.716-15-15V60.559c0-8.284,6.716-15,15-15h66.054c8.284,0,15,6.716,15,15V126.612z"/> + </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> +</g> +</svg> diff --git a/metavis/Resources/information_icon.svg b/metavis/Resources/information_icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..febc28da980f1ef056ca0d6dda3e60d379e02e00 --- /dev/null +++ b/metavis/Resources/information_icon.svg @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<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 490.318 490.318" style="enable-background:new 0 0 490.318 490.318;" + xml:space="preserve"> +<g> + <g> + <g> + <path d="M245.148,0C109.967,0,0.009,109.98,0.009,245.162c0,135.182,109.958,245.156,245.139,245.156 + c135.186,0,245.162-109.978,245.162-245.156C490.31,109.98,380.333,0,245.148,0z M245.148,438.415 + c-106.555,0-193.234-86.698-193.234-193.253c0-106.555,86.68-193.258,193.234-193.258c106.559,0,193.258,86.703,193.258,193.258 + C438.406,351.717,351.706,438.415,245.148,438.415z"/> + <path d="M270.036,221.352h-49.771c-8.351,0-15.131,6.78-15.131,15.118v147.566c0,8.352,6.78,15.119,15.131,15.119h49.771 + c8.351,0,15.131-6.77,15.131-15.119V236.471C285.167,228.133,278.387,221.352,270.036,221.352z"/> + <path d="M245.148,91.168c-24.48,0-44.336,19.855-44.336,44.336c0,24.484,19.855,44.34,44.336,44.34 + c24.485,0,44.342-19.855,44.342-44.34C289.489,111.023,269.634,91.168,245.148,91.168z"/> + </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> +<g> +</g> +</svg> diff --git a/metavis/Resources/information_icon_hovered.svg b/metavis/Resources/information_icon_hovered.svg new file mode 100644 index 0000000000000000000000000000000000000000..54170bd12e3eb749eb080415afac264f8618abce --- /dev/null +++ b/metavis/Resources/information_icon_hovered.svg @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 16.0.0, 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 490.318 490.318" style="enable-background:new 0 0 490.318 490.318;" + xml:space="preserve"> +<g> + <g> + <g> + <path d="M245.148,0C109.967,0,0.009,109.98,0.009,245.162c0,135.182,109.958,245.156,245.139,245.156 + c135.186,0,245.162-109.978,245.162-245.156C490.31,109.98,380.333,0,245.148,0z M245.148,438.415 + c-106.555,0-193.234-86.698-193.234-193.253c0-106.555,86.68-193.258,193.234-193.258c106.559,0,193.258,86.703,193.258,193.258 + C438.406,351.717,351.706,438.415,245.148,438.415z"/> + <path fill="#ff9f00" d="M245.148,438.415 + c-106.555,0-193.234-86.698-193.234-193.253c0-106.555,86.68-193.258,193.234-193.258c106.559,0,193.258,86.703,193.258,193.258 + C438.406,351.717,351.706,438.415,245.148,438.415z"/> + <path d="M270.036,221.352h-49.771c-8.351,0-15.131,6.78-15.131,15.118v147.566c0,8.352,6.78,15.119,15.131,15.119h49.771 + c8.351,0,15.131-6.77,15.131-15.119V236.471C285.167,228.133,278.387,221.352,270.036,221.352z"/> + <path d="M245.148,91.168c-24.48,0-44.336,19.855-44.336,44.336c0,24.484,19.855,44.34,44.336,44.34 + c24.485,0,44.342-19.855,44.342-44.34C289.489,111.023,269.634,91.168,245.148,91.168z"/> + </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> +<g> +</g> +</svg> diff --git a/metavis/Resources/rubbish.svg b/metavis/Resources/rubbish.svg new file mode 100644 index 0000000000000000000000000000000000000000..8a847768242b67262d7a9fdf02a0c4e94f6ef591 --- /dev/null +++ b/metavis/Resources/rubbish.svg @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<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 372.693 372.693" style="enable-background:new 0 0 372.693 372.693;" + xml:space="preserve"> +<g> + <path d="M322.248,51.317h-67.296v-18.09C254.952,14.904,240.047,0,221.727,0h-70.755c-18.323,0-33.233,14.904-33.233,33.228v18.084 + H50.452c-18.324,0-33.237,14.906-33.237,33.218v35.38c0,9.535,7.755,17.285,17.297,17.285h23.476v202.274 + c0,18.321,14.907,33.225,33.225,33.225h194.472c18.32,0,33.236-14.915,33.236-33.225V137.201h19.264 + c9.535,0,17.294-7.75,17.294-17.285v-35.38C355.478,66.224,340.568,51.317,322.248,51.317z M152.308,34.576h68.067v16.741h-68.067 + V34.576z M153.353,306.316c0,9.188-7.485,16.67-16.681,16.67c-9.206,0-16.696-7.482-16.696-16.67v-139.59 + c0-9.196,7.491-16.693,16.696-16.693c9.196,0,16.681,7.497,16.681,16.693V306.316z M203.028,306.316 + c0,9.188-7.481,16.67-16.681,16.67c-9.197,0-16.681-7.482-16.681-16.67v-139.59c0-9.196,7.484-16.693,16.681-16.693 + c9.206,0,16.681,7.497,16.681,16.693V306.316z M252.723,306.316c0,9.188-7.493,16.67-16.692,16.67c-9.2,0-16.67-7.482-16.67-16.67 + v-139.59c0-9.196,7.47-16.693,16.67-16.693c9.199,0,16.692,7.497,16.692,16.693V306.316z"/> + +</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> diff --git a/metavis/Resources/rubbish_hovered.svg b/metavis/Resources/rubbish_hovered.svg new file mode 100644 index 0000000000000000000000000000000000000000..fa7360a2c63db2dc486061d04fd44450504c568b --- /dev/null +++ b/metavis/Resources/rubbish_hovered.svg @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<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 372.693 372.693" style="enable-background:new 0 0 372.693 372.693;" + xml:space="preserve"> +<g> + <path d="M322.248,51.317h-67.296v-18.09C254.952,14.904,240.047,0,221.727,0h-70.755c-18.323,0-33.233,14.904-33.233,33.228v18.084 + H50.452c-18.324,0-33.237,14.906-33.237,33.218v35.38c0,9.535,7.755,17.285,17.297,17.285h23.476v202.274 + c0,18.321,14.907,33.225,33.225,33.225h194.472c18.32,0,33.236-14.915,33.236-33.225V137.201h19.264 + c9.535,0,17.294-7.75,17.294-17.285v-35.38C355.478,66.224,340.568,51.317,322.248,51.317z M152.308,34.576h68.067v16.741h-68.067 + V34.576z M153.353,306.316c0,9.188-7.485,16.67-16.681,16.67c-9.206,0-16.696-7.482-16.696-16.67v-139.59 + c0-9.196,7.491-16.693,16.696-16.693c9.196,0,16.681,7.497,16.681,16.693V306.316z M203.028,306.316 + c0,9.188-7.481,16.67-16.681,16.67c-9.197,0-16.681-7.482-16.681-16.67v-139.59c0-9.196,7.484-16.693,16.681-16.693 + c9.206,0,16.681,7.497,16.681,16.693V306.316z M252.723,306.316c0,9.188-7.493,16.67-16.692,16.67c-9.2,0-16.67-7.482-16.67-16.67 + v-139.59c0-9.196,7.47-16.693,16.67-16.693c9.199,0,16.692,7.497,16.692,16.693V306.316z"/> + <path fill="#ff9f00" d="M153.353,306.316c0,9.188-7.485,16.67-16.681,16.67c-9.206,0-16.696-7.482-16.696-16.67v-139.59 + c0-9.196,7.491-16.693,16.696-16.693c9.196,0,16.681,7.497,16.681,16.693V306.316z M203.028,306.316 + c0,9.188-7.481,16.67-16.681,16.67c-9.197,0-16.681-7.482-16.681-16.67v-139.59c0-9.196,7.484-16.693,16.681-16.693 + c9.206,0,16.681,7.497,16.681,16.693V306.316z M252.723,306.316c0,9.188-7.493,16.67-16.692,16.67c-9.2,0-16.67-7.482-16.67-16.67 + v-139.59c0-9.196,7.47-16.693,16.67-16.693c9.199,0,16.692,7.497,16.692,16.693V306.316z"/> + <path fill="#ff9f00" d="M152.308,34.576h68.067v16.741h-68.067 V34.576z"/> +</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> diff --git a/metavis/Resources/settingIcon.svg b/metavis/Resources/settingIcon.svg new file mode 100644 index 0000000000000000000000000000000000000000..4ca5176f474d1d76578ff99fa46b8b6aceb618ec --- /dev/null +++ b/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> diff --git a/metavis/Resources/settingIcon_hovered.svg b/metavis/Resources/settingIcon_hovered.svg new file mode 100644 index 0000000000000000000000000000000000000000..c2613d674c44968114d27c2b38136af0a6a94bbf --- /dev/null +++ b/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> diff --git a/metavis/Resources/up-triangle.svg b/metavis/Resources/up-triangle.svg new file mode 100644 index 0000000000000000000000000000000000000000..90ec064f7ca48ff28e38025b05edd803d47a1461 --- /dev/null +++ b/metavis/Resources/up-triangle.svg @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!--U+25B3--> +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="-40 10 240 170"> + <polygon points="20 153 90 38 160 153" fill="none" stroke="#000" stroke-width="20" /> +</svg> \ No newline at end of file diff --git a/metavis/RunData.cpp b/metavis/RunData.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08c591d63a7566d648ca0cee62146d8e533b8c68 --- /dev/null +++ b/metavis/RunData.cpp @@ -0,0 +1,463 @@ +#include "pch.h" +#include "GraphView.h" +#include "RunData.h" +#include <QDebug> +#include <QString> +#include <regex> +#include <algorithm> + + +RunData::RunData() +{ +} + +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 + badFileFlag = true; + return; + } + std::string buffer; + getLine(buffer); + qDebug() << QString::fromStdString(buffer); + /*bool retflag; + metalogFile(retflag); + if (retflag) return;*/ + std::string integerRegexString = "([\\+\\-]?\\d+)"; + std::string doubleRegexString = "([\\+\\-]?\\d+(?:\\.\\d+(?:E[\\+\\-]\\d+)?)?)"; + std::string binaryRegexString = "([01]+)"; + std::string seperator = ","; + std::regex regexLine(integerRegexString + + seperator + integerRegexString + + seperator + integerRegexString + + seperator + doubleRegexString + + seperator + binaryRegexString); + while (fileStream.peek() != EOF) { + std::string buffer; + getLine(buffer); + SolutionPointData sol; + std::smatch match; + if (!std::regex_search(buffer, match, regexLine)) { + qDebug() << "Bad formatatted Line[" << actualLine << "]."; + qDebug() << "Failed to matched:"; + qDebug() << QString::fromStdString(buffer); + return; + } + sol.round = std::stoi(match[1]); + sol.iteration = std::stoi(match[2]); + sol.particleNumber = std::stoi(match[3]); + sol.objectiveFunction = std::stod(match[4]); + sol.bitVec.resize(match[5].length()); + std::string binaryString = match[5]; + for (std::string::size_type i = 0; i < binaryString.size(); ++i) { + sol.bitVec[i] = (binaryString[i] == '1'); + } + solutionVec.push_back(sol); + } + fileStream.close(); + qDebug() << "Import done"; + + + + //Set Runs apart + int count = 0; + std::vector<SolutionPointData>::iterator begin = solutionVec.begin(); + for (auto iter = solutionVec.begin(); iter != solutionVec.end(); iter++) { + if (iter->round != begin->round) { + std::string endString("["); + endString += std::to_string(count++); + endString += "]"; + singleRunList.push_back(SingleRun(name, begin, iter, name + endString)); + begin = iter; + } + } + std::string endString("["); + endString += std::to_string(count); + endString += "]"; + singleRunList.push_back(SingleRun(name, begin, solutionVec.end(), name + endString)); + + + + //Generate PascalTriangle + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + + int amountOfBits = begin->bitVec.size(); + int lines = amountOfBits + 1; + 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; + } + else { + pascalTriangleVec[binominalIndex(line, number)] = pascalTriangleVec[binominalIndex(line - 1, number)] + pascalTriangleVec[binominalIndex(line - 1, number - 1)]; + } + + } + } + std::chrono::high_resolution_clock::time_point endPoint = std::chrono::high_resolution_clock::now(); + std::chrono::milliseconds time = std::chrono::duration_cast<std::chrono::milliseconds>(endPoint - start); + qDebug() << "PascalTriangle: " << time.count() << "ms"; + //Calculate Additional Data + for (SingleRun& srun : singleRunList) { + srun.calculateAdditionalData(pascalTriangleVec, amountOfBits); + } + calculateAverageOverRuns(); + +} + +void RunData::metalogFile(bool& retflag) +{ + retflag = true; + /* 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)) { + qDebug() << "Bad formatatted Line[" << actualLine << "]."; + qDebug() << "Failed to matched:"; + qDebug() << QString::fromStdString(buffer); + return; + } + sol.iteration = std::stoi(match[1]); + if (!std::regex_search(buffer, match, regexObjectiveFunction)) { + qDebug() << "Bad formatatted Line[" << actualLine << "]."; + qDebug() << "Failed to matched:"; + qDebug() << QString::fromStdString(buffer); + return; + } + sol.objectiveFunction = std::stod(match[1]); + std::regex regexParticleNumber("pN:([\\+\\-]?\\d+)"); + if (!std::regex_search(buffer, match, regexParticleNumber)) { + qDebug() << "Bad formatatted Line[" << actualLine << "]."; + qDebug() << "Failed to matched:"; + qDebug() << QString::fromStdString(buffer); + return; + } + sol.particleNumber = std::stoi(match[1]); + std::regex regexBitVec("b:([01]+)"); + if (!std::regex_search(buffer, match, regexBitVec)) { + qDebug() << "Bad formatatted Line[" << actualLine << "]."; + qDebug() << "Failed to matched:"; + qDebug() << QString::fromStdString(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); + } + retflag = false; +} + +void RunData::getLine(std::string& bufferString) +{ + std::getline(fileStream, bufferString); + actualLine++; +} + +void RunData::calculateAverageOverRuns() +{ + qDebug() << "calculateAverageOverRuns"; + + { + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + int iter = singleRunList.begin()->bestMaxSolutionFoundPerIteration.size(); + double n = singleRunList.size(); + bestAverageMaxSolutionFoundPerIteration.resize(iter); + for (int i = 0; i < iter; i++) { + double average = std::accumulate(singleRunList.begin(), singleRunList.end(), 0.0, [i](double a, const SingleRun& b) -> double + {return a + b.bestMaxSolutionFoundPerIteration[i].y; }) / n; + bestAverageMaxSolutionFoundPerIteration[i] = GraphDataPoint((double)i, average); + } + std::chrono::high_resolution_clock::time_point endPoint = std::chrono::high_resolution_clock::now(); + std::chrono::milliseconds time = std::chrono::duration_cast<std::chrono::milliseconds>(endPoint - start); + qDebug() << "Time" << time.count() << "ms"; + } + +} + +SingleRun::SingleRun(std::string uniqueName, const std::vector<SolutionPointData>::iterator begin, const std::vector<SolutionPointData>::iterator end, std::string name = "") + :begin(begin), end(end), name(name), runDataName(uniqueName) +{ + +} + +void SingleRun::calculateAdditionalData(std::vector<boost::multiprecision::cpp_int>& pascalTriangleVec, int amountOfBits) +{ + calculateBestAndAverageIter(); + calculateParticleSolution(); + calculateDotsForDistribution(); + calculateBitFieldData(pascalTriangleVec, amountOfBits); + calculateMeanHammingDistance(); +} + +void SingleRun::calculateBestAndAverageIter() +{ + if (begin == end) { + return; + } + double minObjectiveFunctionInIter = begin->objectiveFunction; + double maxObjectiveFunctionInIter = begin->objectiveFunction; + double bestMinObjectiveFunctionFound = begin->objectiveFunction; + double bestMaxObjectiveFunctionFound = begin->objectiveFunction; + double actualIterObjectiveFunctionAggregate = begin->objectiveFunction; + int actualIter = begin->iteration; + int foundSolutionInIteration = 1; + for (auto iter = begin; iter != end; iter++) { + SolutionPointData& nextData = *iter; + if (nextData.iteration != actualIter) { + //save last + bestMinSolutionFoundPerIteration.push_back(GraphDataPoint((double)actualIter, bestMinObjectiveFunctionFound)); + bestMaxSolutionFoundPerIteration.push_back(GraphDataPoint((double)actualIter, bestMaxObjectiveFunctionFound)); + averageSolutionPerItertion.push_back(GraphDataPoint((double)actualIter, actualIterObjectiveFunctionAggregate / (double)foundSolutionInIteration)); + minSolutionPerItertion.push_back(GraphDataPoint((double)actualIter, minObjectiveFunctionInIter)); + maxSolutionPerItertion.push_back(GraphDataPoint((double)actualIter, maxObjectiveFunctionInIter)); + //init new iteration + actualIter = nextData.iteration; + foundSolutionInIteration = 1; + actualIterObjectiveFunctionAggregate = nextData.objectiveFunction; + minObjectiveFunctionInIter = nextData.objectiveFunction; + maxObjectiveFunctionInIter = nextData.objectiveFunction; + } + else { + //increae aggregate + foundSolutionInIteration++; + actualIterObjectiveFunctionAggregate += nextData.objectiveFunction; + } + //update best min and max if better + if (nextData.objectiveFunction < bestMinObjectiveFunctionFound) { + bestMinObjectiveFunctionFound = nextData.objectiveFunction; + } + if (nextData.objectiveFunction > bestMaxObjectiveFunctionFound) { + bestMaxObjectiveFunctionFound = nextData.objectiveFunction; + } + if (nextData.objectiveFunction < minObjectiveFunctionInIter) { + minObjectiveFunctionInIter = nextData.objectiveFunction; + } + if (nextData.objectiveFunction > maxObjectiveFunctionInIter) { + maxObjectiveFunctionInIter = nextData.objectiveFunction; + } + } + //save last iteration + bestMinSolutionFoundPerIteration.push_back(GraphDataPoint((double)actualIter, bestMinObjectiveFunctionFound)); + bestMaxSolutionFoundPerIteration.push_back(GraphDataPoint((double)actualIter, bestMaxObjectiveFunctionFound)); + averageSolutionPerItertion.push_back(GraphDataPoint((double)actualIter, actualIterObjectiveFunctionAggregate / (double)foundSolutionInIteration)); + minSolutionPerItertion.push_back(GraphDataPoint((double)actualIter, minObjectiveFunctionInIter)); + maxSolutionPerItertion.push_back(GraphDataPoint((double)actualIter, maxObjectiveFunctionInIter)); +} + +void SingleRun::calculateParticleSolution() +{ + for (auto iter = begin; iter != end; iter++) { + GraphDataPoint point(iter->iteration, iter->objectiveFunction, &*iter); + auto treeIter = particleMap.find(iter->particleNumber); + if (treeIter == particleMap.end()) { + //create new Entry + std::vector<GraphDataPoint> vec; + vec.push_back(point); + particleMap.insert({ iter->particleNumber, vec}); + } + else { + //append to vector in Entry + treeIter->second.push_back(point); + } + } +} + +void SingleRun::calculateDotsForDistribution() +{ + for (std::vector<SolutionPointData>::iterator it = begin; it != end; ++it) { + dotsForDistribution.push_back(GraphDataPoint((double)it->iteration, it->objectiveFunction, &*it)); + } +} + +boost::multiprecision::cpp_int positionInPermutation2(std::vector<boost::multiprecision::cpp_int>& pascalTriangleVec, std::vector<bool>::iterator begin, std::vector<bool>::iterator end, int amountOfUnsetBits) +{ + //recursion base + if (amountOfUnsetBits == 0) return 1; + int amountOfBits = end - begin; + std::vector<bool>::iterator indexIter = std::find(begin, end, false); + int index = indexIter - begin; + int amountOfBitsAfterIndex = (amountOfBits - 1) - index; + //recursion base + if (amountOfUnsetBits == 1) return (amountOfBits - 1) - index; + //Step 1 the amount of permutations with the rest amountOfBitsAfterIndex + boost::multiprecision::cpp_int before = (amountOfUnsetBits <= amountOfBitsAfterIndex) ? pascalTriangleVec[RunData::binominalIndex(amountOfBitsAfterIndex, amountOfUnsetBits)] : 0; + //Step 2 the actual position of the rest + boost::multiprecision::cpp_int after = positionInPermutation2(pascalTriangleVec, ++indexIter, end, amountOfUnsetBits - 1); + //Step 3 add Step 1 and Step 2 + return before + after; +} + +void SingleRun::calculateBitFieldData(std::vector<boost::multiprecision::cpp_int>& pascalTriangleVec, int amountOfBits) +{ + + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + + for (std::vector<SolutionPointData>::iterator it = begin; it != end; ++it) { + int amountOfSetBits = std::count(it->bitVec.begin(), it->bitVec.end(), true); + int amountOfUnsetBits = it->bitVec.size() - amountOfSetBits; + boost::multiprecision::cpp_dec_float_100 position(positionInPermutation2(pascalTriangleVec, it->bitVec.begin(), it->bitVec.end(), amountOfUnsetBits)); + boost::multiprecision::cpp_dec_float_100 maxAmountOfPermutaions (pascalTriangleVec[RunData::binominalIndex(amountOfBits, amountOfSetBits)] - 1); + + dotsForBitField.push_back(GraphDataPoint((position / maxAmountOfPermutaions).convert_to<double>(), amountOfSetBits, &*it)); + } + std::chrono::high_resolution_clock::time_point endPoint = std::chrono::high_resolution_clock::now(); + std::chrono::milliseconds time = std::chrono::duration_cast<std::chrono::milliseconds>(endPoint - start); + qDebug() << "BitFieldBerechnung: " << time.count() << "ms"; +} + +void SingleRun::calculateMeanHammingDistance() +{ + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + std::vector<SolutionPointData>::iterator iterBegin = begin; + for (std::vector<SolutionPointData>::iterator iter = begin; iter != end; iter++) { + if (iter->iteration != iterBegin->iteration) { + double mean = RunData::meanHammingDistance(iterBegin, iter); + meanHammingDistancePerIteration.push_back(GraphDataPoint(iterBegin->iteration, mean)); + iterBegin = iter; + } + } + std::chrono::high_resolution_clock::time_point endPoint = std::chrono::high_resolution_clock::now(); + std::chrono::milliseconds time = std::chrono::duration_cast<std::chrono::milliseconds>(endPoint - start); + qDebug() << "Mean: " << time.count() << "ms"; +} + + +int RunData::binominalIndex(const int n, int k) +{ + if (k > n / 2) k = n - k; + return ((n + 1) / 2) * (n / 2 + 1) + k; +} + +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) +{ + boost::multiprecision::cpp_int value(1); + if (k > n / 2) k = n - k; + for (int i = 0; i < k; i++) { + value *= boost::multiprecision::cpp_int(n - i); + value /= boost::multiprecision::cpp_int(i + 1); + } + return value; +} + +boost::multiprecision::cpp_int RunData::positionInPermutation(std::vector<bool>::iterator begin, std::vector<bool>::iterator end, int setBits) +{ + int amountOfBits = end - begin; + //recursion base + if (setBits == 0) return 1; + std::vector<bool>::iterator indexIter = std::find(begin, end, true);//TODO:false k�nnte andersrum sortieren! + int index = indexIter - begin; + int bitsAfterIndex = (amountOfBits - 1) - index; + //recursion base + if (setBits == 1) return (amountOfBits - 1) - index; + //Step 1 the amount of permutations with the rest amountOfBitsAfterIndex + boost::multiprecision::cpp_int before = binominal(bitsAfterIndex, setBits); + //Step 2 teh actual position of the rest + boost::multiprecision::cpp_int after = positionInPermutation(++indexIter, end, setBits - 1); + //setp 3 add Step 1 and Step 2 + return before + after; +} + +boost::multiprecision::cpp_int RunData::positionInPermutation_reversed(std::vector<bool>::iterator begin, std::vector<bool>::iterator end, int amountOfUnsetBits) +{ + qDebug() << "Method()"; + int amountOfBits = end - begin; + //recursion base + if (amountOfUnsetBits == 0) return 1; + std::vector<bool>::iterator indexIter = std::find(begin, end, false); + int index = indexIter - begin; + qDebug() << "index: " << index; + int bitsAfterIndex = (amountOfBits - 1) - index; + //recursion base + if (amountOfUnsetBits == 1) return (amountOfBits - 1) - index; + //Step 1 the amount of permutations with the rest amountOfBitsAfterIndex + boost::multiprecision::cpp_int before = (amountOfUnsetBits <= bitsAfterIndex)? binominal(bitsAfterIndex, amountOfUnsetBits):0; + qDebug() << "before: " << "binominal("<< bitsAfterIndex << "," << amountOfUnsetBits << ")" << before.convert_to<int>(); + //Step 2 teh actual position of the rest + boost::multiprecision::cpp_int after = positionInPermutation_reversed(++indexIter, end, amountOfUnsetBits - 1); + qDebug() << "after: " << after.convert_to<int>(); + qDebug() << "return:" << before.convert_to<int>() << "+" << after.convert_to<int>(); + //setp 3 add Step 1 and Step 2 + return before + after; +} + + +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() const +{ + 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); +} + +std::string SolutionPointData::bitstringToStdString() +{ + std::string str(bitVec.size(), 0); + std::transform(bitVec.begin(), bitVec.end(), str.begin(), + [](bool b) -> char { return b ? '1' : '0'; }); + return str; +} diff --git a/metavis/RunData.h b/metavis/RunData.h new file mode 100644 index 0000000000000000000000000000000000000000..8b8d3340ea89d72caa5d2f4b8d2b8fa8eb8714ed --- /dev/null +++ b/metavis/RunData.h @@ -0,0 +1,93 @@ +#pragma once +#include <string> +#include <vector> +#include <fstream> +#include <QPoint> +#include <map> +#include <boost/multiprecision/cpp_int.hpp> +#include <boost/multiprecision/cpp_dec_float.hpp> + +class RunData; + +struct SolutionPointData { + int round; + int iteration; + double objectiveFunction; + int particleNumber; + std::vector<bool> bitVec; + std::string bitstringToStdString(); +}; +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() const; + QPointF toQPointF() const; + double squaredDistance(QPointF& graphPoint) const; +}; + +class SingleRun { +public: + std::string name; + std::string runDataName; + const std::vector<SolutionPointData>::iterator begin; + const std::vector<SolutionPointData>::iterator end; + SingleRun(std::string uniqueName, const std::vector<SolutionPointData>::iterator begin, const std::vector<SolutionPointData>::iterator end, std::string name); + std::vector<GraphDataPoint> bestMinSolutionFoundPerIteration; + std::vector<GraphDataPoint> bestMaxSolutionFoundPerIteration; + std::vector<GraphDataPoint> averageSolutionPerItertion; + std::vector<GraphDataPoint> minSolutionPerItertion; + std::vector<GraphDataPoint> maxSolutionPerItertion; + std::vector<GraphDataPoint> dotsForDistribution; + std::vector<GraphDataPoint> dotsForBitField; + std::vector<GraphDataPoint> meanHammingDistancePerIteration; + std::map<int, std::vector<GraphDataPoint>> particleMap; + + void calculateAdditionalData(std::vector<boost::multiprecision::cpp_int>& pascalTriangleVec, int amountOfBits); +private: + void calculateBestAndAverageIter(); + void calculateParticleSolution(); + void calculateDotsForDistribution(); + void calculateBitFieldData(std::vector<boost::multiprecision::cpp_int>& pascalTriangleVec, int amountOfBits); + void calculateMeanHammingDistance(); +}; + + + +class RunData +{ +public: + std::string name; + std::string information; + std::string filePath; + std::vector<SolutionPointData> solutionVec; + std::list<SingleRun> singleRunList; + std::vector<GraphDataPoint> bestAverageMaxSolutionFoundPerIteration; + + + + RunData(); + RunData(std::string filePath); + + void metalogFile(bool& retflag); + +private: + bool badFileFlag = false; + std::fstream fileStream; + int actualLine = 0; + void getLine(std::string& bufferString); + void calculateAverageOverRuns(); + +public: + static int binominalIndex(const int n, int k); + int static hammingdistance(std::vector<bool>& bitVecA, std::vector<bool>& bitVecB); + double static meanHammingDistance(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end); + static boost::multiprecision::cpp_int binominal(const int n, const int k); + static boost::multiprecision::cpp_int positionInPermutation(std::vector<bool>::iterator begin, std::vector<bool>::iterator end, int amountOfSetBits); + static boost::multiprecision::cpp_int positionInPermutation_reversed(std::vector<bool>::iterator begin, std::vector<bool>::iterator end, int amountOfUnsetBits); +}; + diff --git a/metavis/Scratchpad.cpp b/metavis/Scratchpad.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6182668fc0368338bbad02a203b5c6cb1c0485ad --- /dev/null +++ b/metavis/Scratchpad.cpp @@ -0,0 +1,48 @@ +#include "pch.h" +#include "Scratchpad.h" +#include "HoverButton.h" +#include <QAction> + +Scratchpad::Scratchpad(QWidget *parent) + : BitInspectorPanel(parent) +{ + HoverButton* rubbishButton = new HoverButton(); + rubbishButton->setMinimumSize(20, 20); + rubbishButton->setMaximumSize(20, 20); + rubbishButton->setIcon(QIcon(":/metavis/Resources/rubbish.svg")); + rubbishButton->setHoveredIcon(QIcon(":/metavis/Resources/rubbish_hovered.svg")); + rubbishButton->setAttribute(Qt::WA_TranslucentBackground); + rubbishButton->setStyleSheet(rubbishButton->styleSheet() + "border: none;"); + rubbishButton->setToolTip("Clear scratchpad"); + qDebug() << topPanel->count(); + topPanel->insertWidget(topPanel->count()-1, rubbishButton); + connect(rubbishButton, &QPushButton::released, this, &Scratchpad::clear); + + QAction* clearAction = new QAction("Clear"); + this->contextMenu->addAction(clearAction); + connect(clearAction, &QAction::triggered, this, &Scratchpad::clear); + + popupWidget->setInformation("<h3><u>Scratchpad</u></h3> Shows selected solution from search space visualizations or metrics. Each solution gets displayed in a row. To add solutions go in a other visualization and press & drag the RMB(Right-Mouse-Button) to select the points to add to this scratchpad.<br><b>Clear button</b> removes all solutions from this pad.<br> Alternative is press RMB and \"Clear\" in the scratchpad. <br><b>Iteration:</b> Show the iteration the solution is generated.<br><b>Number:</b> Show which individual in the popullation is represent.<br><b>Objective Function:</b> Describes how well the solution is, it depends on the problem if a low or high value is desired.<br><b>Binary:</b> Shows the bitstring of the solution. White rectangle represents the bit is <i>false<i> and black <i>true<i>."); +} + +Scratchpad::~Scratchpad() +{ +} + +void Scratchpad::addPoint(const SolutionPointData& point) +{ + inspector->clear(); + points.push_back(point); + inspector->updateData(points.begin(), points.end()); +} + +void Scratchpad::clear() +{ + inspector->clear(); + points.clear(); +} + +void Scratchpad::contextMenuEvent(QContextMenuEvent* event) +{ + contextMenu->exec(event->globalPos()); +} diff --git a/metavis/Scratchpad.h b/metavis/Scratchpad.h new file mode 100644 index 0000000000000000000000000000000000000000..4b666ebcb6fbc710a2414a2fd00278b54fe19b8f --- /dev/null +++ b/metavis/Scratchpad.h @@ -0,0 +1,22 @@ +#pragma once + +#include "BitInspectorPanel.h" +#include "RunData.h" +#include <QMenu> + +class Scratchpad : public BitInspectorPanel +{ + Q_OBJECT + +public: + Scratchpad(QWidget *parent); + ~Scratchpad(); + void addPoint(const SolutionPointData& point); + void clear(); +protected: + QMenu* contextMenu = new QMenu(this); + void contextMenuEvent(QContextMenuEvent* event) override; +private: + //Points to display + std::vector<SolutionPointData> points; +}; diff --git a/metavis/SearchSpacePlott.cpp b/metavis/SearchSpacePlott.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85c9b9f9235bf6687d05ebaa12fc5d40cf456693 --- /dev/null +++ b/metavis/SearchSpacePlott.cpp @@ -0,0 +1,51 @@ +#include "pch.h" +#include "SearchSpacePlott.h" + +SearchSpacePlott::SearchSpacePlott(QWidget *parent) + : GraphPlott(parent, false, true, false) +{ + QSlider* circleSizeSlider = new QSlider(Qt::Horizontal); + circleSizeSlider->setMinimum(1); + circleSizeSlider->setMaximum(15); + circleSizeSlider->setValue(circleSize); + circleSizeSlider->setMaximumWidth(80); + circleSizeSlider->setMaximumHeight(16); + this->buttonPanel->insertSpacing(1, 5); + this->buttonPanel->insertWidget(1, circleSizeSlider); + this->buttonPanel->insertSpacing(1, 5); + this->buttonPanel->insertWidget(1, new QLabel("Size:")); + connect(circleSizeSlider, &QSlider::valueChanged, this, [this, circleSizeSlider]() {this->circleSize = circleSizeSlider->value(); this->update(); }); + QSlider* transparentSlider = new QSlider(Qt::Horizontal); + transparentSlider->setMinimum(5); + transparentSlider->setMaximum(50); + transparentSlider->setValue(50); + transparentSlider->setMaximumWidth(80); + transparentSlider->setMaximumHeight(16); + this->buttonPanel->insertSpacing(1, 5); + this->buttonPanel->insertWidget(1, transparentSlider); + this->buttonPanel->insertSpacing(1, 5); + this->buttonPanel->insertWidget(1, new QLabel("Transparency:")); + connect(transparentSlider, &QSlider::valueChanged, this, [this, transparentSlider]() {this->transparentAlphaValue = ((double)transparentSlider->value()) / 50.0; this->update(); }); + this->setMinimumSize(200, 50); +} + +SearchSpacePlott::~SearchSpacePlott() +{ +} + +void SearchSpacePlott::mouseMoveEvent(QMouseEvent* event) +{ + GraphPlott::mouseMoveEvent(event); +} + +void SearchSpacePlott::setMinimumIterationToDispaly(int min) +{ + minIter = min; + update(); +} + +void SearchSpacePlott::setMaximumIterationToDispaly(int max) +{ + maxIter = max; + update(); +} diff --git a/metavis/SearchSpacePlott.h b/metavis/SearchSpacePlott.h new file mode 100644 index 0000000000000000000000000000000000000000..14ed71fd61f16bf15c694206f4b1e7a78fb9c7cd --- /dev/null +++ b/metavis/SearchSpacePlott.h @@ -0,0 +1,25 @@ +#pragma once + +#include <QWidget> +#include <QMouseEvent> +#include "GraphPlott.h" + + +class SearchSpacePlott : public GraphPlott +{ + Q_OBJECT + +public: + SearchSpacePlott(QWidget *parent); + ~SearchSpacePlott(); +protected: + int minIter = 0; + int maxIter = 100; + + int circleSize = 2; + double transparentAlphaValue = 1; + virtual void mouseMoveEvent(QMouseEvent* event) override; +public slots: + void setMinimumIterationToDispaly(int min); + void setMaximumIterationToDispaly(int max); +}; diff --git a/metavis/SettingDialog.cpp b/metavis/SettingDialog.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c818ca11a0bf94cf6f528a1876088fa6272a7db2 --- /dev/null +++ b/metavis/SettingDialog.cpp @@ -0,0 +1,49 @@ +#include "pch.h" +#include "SettingDialog.h" +#include <QDebug> +SettingDialog::SettingDialog(QSettings* settings, QWidget* parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint), settings(settings) +{ + ui.setupUi(this); + //manuel connect all button with buttonHandler + connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(dialogButtonHandler(QAbstractButton*))); +} + +SettingDialog::~SettingDialog() +{ +} + + + +void SettingDialog::acceptButtonClicked() +{ + qDebug() << "Accept"; + apply(); + accept(); +} + +void SettingDialog::apply() +{ + qDebug() << "Apply"; +} + + +void SettingDialog::dialogButtonHandler(QAbstractButton* button) +{ + switch (ui.buttonBox->buttonRole(button)) { + case QDialogButtonBox::ButtonRole::AcceptRole: + acceptButtonClicked(); + break; + case QDialogButtonBox::ButtonRole::ApplyRole: + apply(); + break; + case QDialogButtonBox::ButtonRole::RejectRole: + default: + /* close the window and dont apply changes*/ + reject(); + break; + } +} + + + diff --git a/metavis/SettingDialog.h b/metavis/SettingDialog.h new file mode 100644 index 0000000000000000000000000000000000000000..ec1313c98c9c59f3ca5ca698f9474785ff6f3482 --- /dev/null +++ b/metavis/SettingDialog.h @@ -0,0 +1,38 @@ +#pragma once +#include <QDialog> +#include <QSettings> +#include "ui_SettingDialog.h" +#include <QAbstractButton> + +/** + * A dialog to change the Setting for the main program. + */ +class SettingDialog : public QDialog +{ + Q_OBJECT + +public: + SettingDialog(QSettings* settings, QWidget *parent = Q_NULLPTR); + ~SettingDialog(); + +private: + Ui::SettingDialog ui; + QSettings* settings; + /** + * Apply the settings and close the SettingDialog afterwords. + */ + void acceptButtonClicked(); + /** + * Save the changes from the dialog to the actual settings. + */ + void apply(); + + + +public slots: + /** + * Handle the button clicked in the SettingDialog. + * @param button the Button returned from the Signal. + */ + void dialogButtonHandler(QAbstractButton* button); +}; diff --git a/metavis/SettingDialog.ui b/metavis/SettingDialog.ui new file mode 100644 index 0000000000000000000000000000000000000000..33de0f14e78c0042197f23c2a4aa836d4954e435 --- /dev/null +++ b/metavis/SettingDialog.ui @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SettingDialog</class> + <widget class="QDialog" name="SettingDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>295</height> + </rect> + </property> + <property name="windowTitle"> + <string>SettingDialog</string> + </property> + <property name="locale"> + <locale language="English" country="UnitedKingdom"/> + </property> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="geometry"> + <rect> + <x>165</x> + <y>260</y> + <width>221</width> + <height>23</height> + </rect> + </property> + <property name="locale"> + <locale language="English" country="UnitedKingdom"/> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <layoutdefault spacing="6" margin="11"/> + <resources/> + <connections/> +</ui> diff --git a/metavis/TsneControlPanel.cpp b/metavis/TsneControlPanel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c8ece7fe73694341b0055baa2f04e0da7e3250ca --- /dev/null +++ b/metavis/TsneControlPanel.cpp @@ -0,0 +1,144 @@ +#include "pch.h" +#include "TsneControlPanel.h" +#include <QPushButton> +#include <QProgressBar> +#include <QGridLayout> +#include <QSpacerItem> +#include <QLineEdit> +#include <QSlider> + +TsneControlPanel::TsneControlPanel(QWidget *parent) + : QWidget(parent) +{ + plott->setVisibleWindow(-20, 20, -20, 20); + + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(plott); + layout->addLayout(buttonLayout); + + //ButtonPanel + showOptionsButton = new QPushButton("Hide Options"); + connect(showOptionsButton, &QPushButton::pressed, this, &TsneControlPanel::toggleOptions); + QPushButton* startButton = new QPushButton("Start"); + connect(startButton, &QPushButton::pressed, this, &TsneControlPanel::start); + QPushButton* pauseButton = new QPushButton("Pause"); + connect(pauseButton, &QPushButton::pressed, this, &TsneControlPanel::pause); + iterationLabel->setText("Iteration: 0"); + progressBar->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Preferred); + progressBar->setAlignment(Qt::AlignCenter); + progressBar->setValue(0); + progressBar->setMaximum(tsneSettings->maxIter - 1); + buttonLayout->addWidget(startButton); + buttonLayout->addWidget(pauseButton); + buttonLayout->addWidget(iterationLabel); + buttonLayout->addWidget(progressBar); + buttonLayout->addWidget(showOptionsButton); + + + layout->addWidget(tsneSettings); + + layout->addWidget(slider); + + //Slider + slider->setTitle("Iteration:"); + connect(slider, &RangeSlider::maxChanged, plott, &TsnePlott::setMaximumIterationToDispaly); + connect(slider, &RangeSlider::minChanged, plott, &TsnePlott::setMinimumIterationToDispaly); + + + //Gradient + layout->addWidget(gradient); + connect(gradient, &ColorGradient::gradientChanged, this, [this]() {this->plott->updateColors(*gradient); }); + + //Settings + QSettings settings("settings.ini", QSettings::IniFormat, this); + settings.beginGroup("Tsne"); + if (settings.value("hideOptions", true).toBool()) { + toggleOptions(); + } + settings.endGroup(); +} + +TsneControlPanel::~TsneControlPanel() +{ + //Settings + QSettings settings("settings.ini", QSettings::IniFormat, this); + settings.beginGroup("Tsne"); + settings.setValue("hideOptions", this->isOptionHidden); + settings.endGroup(); +} + +void TsneControlPanel::assignData(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end, QString runName) +{ + this->begin = begin; + this->end = end; + runName = runName; + plott->setDisplayLabel(runName); + resetPanel(); + plott->clear(); +} + +void TsneControlPanel::clear() +{ + begin = std::vector<SolutionPointData>::iterator(); + end = std::vector<SolutionPointData>::iterator(); + runName = ""; + resetPanel(); + plott->clear(); +} + + +void TsneControlPanel::toggleOptions() +{ + this->isOptionHidden = !this->isOptionHidden; + showOptionsButton->setText(this->isOptionHidden ? "More Options" : "Hide Options"); + this->slider->setHidden(this->isOptionHidden); + this->gradient->setHidden(this->isOptionHidden); + this->tsneSettings->setHidden(this->isOptionHidden); +} + +void TsneControlPanel::resetPanel() +{ + if (algo != nullptr) { + delete algo; + algo = nullptr; + } + iterationChanged(0); +} + + +void TsneControlPanel::pause() +{ + if (algo != nullptr) { + algo->pause(); + } +} + +void TsneControlPanel::iterationChanged(int iter) +{ + iterationLabel->setText("Iteration: " + QString::number(iter)); + plott->update(); + progressBar->setValue(iter); +} + +void TsneControlPanel::start() +{ + qDebug() << "start"; + if (begin == end) { + qDebug() << "NoData"; + return; + } + double* matrixY = nullptr; + double** matrixYPtr = &matrixY; + resetPanel(); + algo = new tSneAlgo(begin, end, matrixYPtr, tsneSettings->perplexity, tsneSettings->learnrate, tsneSettings->maxIter); + connect(algo, &tSneAlgo::changedIter, this, &TsneControlPanel::iterationChanged); + connect(algo, &tSneAlgo::algoDone, this, &TsneControlPanel::finished); + emit started(); + algo->start(); + qDebug() << "Y:" << matrixY; + plott->assignMatrix(begin, end, matrixY, std::distance(begin, end), *gradient); +} diff --git a/metavis/TsneControlPanel.h b/metavis/TsneControlPanel.h new file mode 100644 index 0000000000000000000000000000000000000000..abfb47d2a9bf834b51e59e6b50887e57f68a7e22 --- /dev/null +++ b/metavis/TsneControlPanel.h @@ -0,0 +1,48 @@ +#pragma once + +#include <QWidget> +#include "TsnePlott.h" +#include "RangeSlider.h" +#include "ColorGradient.h" +#include <QProgressBar> +#include "TsneSettings.h" +#include "tSneAlgo.h" +#include <string> +#include <vector> +#include "RunData.h" + +class TsneControlPanel : public QWidget +{ + Q_OBJECT + +public: + TsneControlPanel(QWidget *parent); + ~TsneControlPanel(); + TsnePlott* plott = new TsnePlott(this); + void assignData(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end, QString runName); + void clear(); + + +protected: + RangeSlider* slider = new RangeSlider(this); + ColorGradient* gradient = new ColorGradient(this); + QProgressBar* progressBar = new QProgressBar(); + TsneSettings* tsneSettings = new TsneSettings(this); + QLabel* iterationLabel = new QLabel(); + bool isOptionHidden = false; + void toggleOptions(); + void resetPanel(); + +private: + QString runName; + std::vector<SolutionPointData>::iterator begin, end; + tSneAlgo* algo = nullptr; + QPushButton* showOptionsButton; +public slots: + void start(); + void pause(); + void iterationChanged(int iter); +signals: + void started(); + void finished(); +}; diff --git a/metavis/TsnePlott.cpp b/metavis/TsnePlott.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c9250cd77d49ac345ac16f4f4c680015cef60c5d --- /dev/null +++ b/metavis/TsnePlott.cpp @@ -0,0 +1,172 @@ +#include "pch.h" +#include "TsnePlott.h" +#include <limits> +TsnePlott::TsnePlott(QWidget *parent) + : SearchSpacePlott(parent) +{ + setAxisLegend("X Axis", "Y Axis"); +} + +TsnePlott::~TsnePlott() +{ +} + +void TsnePlott::assignMatrix(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end, double* yMatrixFromTsneAlgo, int n, ColorGradient& gradient) +{ + this->begin = begin; + this->end = end; + this->yMatrixFromTsneAlgo = yMatrixFromTsneAlgo; + this->N = n; + updateColors(gradient); + +} + +void TsnePlott::clear() +{ + begin = end = std::vector<SolutionPointData>::iterator(); + yMatrixFromTsneAlgo = nullptr; + N = 0; + colorPointDataVec.clear(); + update(); +} + +void TsnePlott::updateColors(ColorGradient& gradient) +{ + colorPointDataVec.clear(); + for (auto iterator = begin; iterator != end; iterator++) { + colorPointDataVec.push_back(gradient.getColorFromValue(iterator->objectiveFunction)); + } + update(); +} + +void TsnePlott::frameGraphInView() +{ + if (begin == end || !yMatrixFromTsneAlgo) { + return; + } + VisibleWindow nextWindow(0,0,0,0); + QPointF first(yMatrixFromTsneAlgo[0], yMatrixFromTsneAlgo[1]); + nextWindow.xMin = nextWindow.xMax = first.x(); + nextWindow.yMin = nextWindow.yMax = first.y(); + + + for (int i = 1; i < this->N; i++) { + QPointF point(yMatrixFromTsneAlgo[2 * i], yMatrixFromTsneAlgo[2 * i + 1]); + if (point.x() < nextWindow.xMin) { + nextWindow.xMin = point.x(); + } + else if (point.x() > nextWindow.xMax) { + nextWindow.xMax = point.x(); + } + if (point.y() < nextWindow.yMin) { + nextWindow.yMin = point.y(); + } + else if (point.y() > nextWindow.yMax) { + nextWindow.yMax = point.y(); + } + } + nextWindow.ZoomOut(0.05); + if (std::abs(nextWindow.xMax - nextWindow.xMin) > std::numeric_limits<double>::min()) { + window.xMin = nextWindow.xMin; + window.xMax = nextWindow.xMax; + } + if (std::abs(nextWindow.yMax - nextWindow.yMin) > std::numeric_limits<double>::min()) { + window.yMin = nextWindow.yMin; + window.yMax = nextWindow.yMax; + } + update(); +} + +void TsnePlott::searchForPointUnderCursor() +{ + //check if mouse stayed still + QPoint globalPosition = QCursor::pos(); + QPointF pos = this->mapFromGlobal(globalPosition); + + if (pos != lastPosition) { + return; + } + if (begin == end || !yMatrixFromTsneAlgo) { + return; + } + QRect graphDisplayRect = getDisplayRect(); + QPointF translation(graphDisplayRect.left(), graphDisplayRect.top()); + double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin); + double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin); + + QPointF minPoint = transformGraphToView(QPointF(yMatrixFromTsneAlgo[0], yMatrixFromTsneAlgo[1])); + std::vector<SolutionPointData>::iterator minData = begin; + double minSqaredDistance = sqaredDistance(transformGraphToView(minPoint, stregth_factorX, stregth_factorY, translation), pos); + auto iter = begin; + for (int i = 1; i < this->N; i++, iter++) { + if (iter->iteration < minIter) { + continue; + } + if (iter->iteration > maxIter) { + break; + } + QPointF point(yMatrixFromTsneAlgo[2 * i], yMatrixFromTsneAlgo[2 * i + 1]); + double distance = sqaredDistance(transformGraphToView(point, stregth_factorX, stregth_factorY, translation), pos); + if (distance < minSqaredDistance) { + minSqaredDistance = distance; + minPoint = point; + minData = iter; + } + } + + //if curser is radius + 3pixel away + if (minSqaredDistance <= circleSize * circleSize + 9) { + QPointF pointInWidget = transformGraphToView(minPoint, stregth_factorX, stregth_factorY, translation); + QToolTip::showText(this->mapToGlobal(QPoint(pointInWidget.x(), pointInWidget.y())) - QPoint(0, 35), QString::fromStdString(minData->bitstringToStdString())); + } +} + +void TsnePlott::addPointsInWindowToScratchPad(VisibleWindow& window) +{ + if (!pad) { + qDebug() << "NoPad"; + return; + } + if (begin == end || !yMatrixFromTsneAlgo) { + return; + } + auto iter = begin; + for (int i = 1; i < this->N; i++, iter++) { + if (iter->iteration < minIter) { + continue; + } + if (iter->iteration > maxIter) { + break; + } + QPointF point(yMatrixFromTsneAlgo[2 * i], yMatrixFromTsneAlgo[2 * i + 1]); + if (window.inBound(point)) { + pad->addPoint(*iter); + } + } +} + +void TsnePlott::drawData(QPainter& painter) +{ + if (begin == end || !yMatrixFromTsneAlgo) { + return; + } + QRect graphDisplayRect = getDisplayRect(); + QPointF translation(graphDisplayRect.left(), graphDisplayRect.top()); + double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin); + double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin); + auto iter = begin; + painter.setPen(Qt::transparent); + for (int i = 0; i < this->N; i++, iter++) { + if (iter->iteration < minIter) { + continue; + } + if (iter->iteration > maxIter) { + break; + } + QColor color = colorPointDataVec[i]; + color.setAlphaF(this->transparentAlphaValue); + painter.setBrush(color); + painter.drawEllipse(transformGraphToView(QPointF(yMatrixFromTsneAlgo[2 * i], yMatrixFromTsneAlgo[2 * i + 1]), stregth_factorX, stregth_factorY, translation), circleSize, circleSize); + } + painter.setBrush(Qt::BrushStyle::NoBrush); +} diff --git a/metavis/TsnePlott.h b/metavis/TsnePlott.h new file mode 100644 index 0000000000000000000000000000000000000000..1f5a9a18471266b9ec32514a76973eb4d72b89cb --- /dev/null +++ b/metavis/TsnePlott.h @@ -0,0 +1,33 @@ +#pragma once + +#include <QWidget> +#include <SearchSpacePlott.h> +#include <vector> +#include "RunData.h" +#include "ColorGradient.h" + + +class TsnePlott : public SearchSpacePlott +{ + Q_OBJECT + + + +public: + TsnePlott(QWidget *parent); + ~TsnePlott(); + void assignMatrix(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end, double* yMatrixFromTsneAlgo, int n, ColorGradient& gradient); + void clear(); + void updateColors(ColorGradient& gradient); + void virtual frameGraphInView(); +protected: + virtual void drawData(QPainter& painter) override; + virtual void searchForPointUnderCursor() override; + virtual void addPointsInWindowToScratchPad(VisibleWindow& window) override; +private: + std::vector<SolutionPointData>::iterator begin, end; + std::vector<QColor> colorPointDataVec; + double* yMatrixFromTsneAlgo = nullptr; + int N; + +}; diff --git a/metavis/TsneSettings.cpp b/metavis/TsneSettings.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17f37f8758597023e91ff10925863ffbef4fb713 --- /dev/null +++ b/metavis/TsneSettings.cpp @@ -0,0 +1,125 @@ +#include "pch.h" +#include "TsneSettings.h" +#include <QLineEdit> +#include <QSlider> +#include <QRegExp> + + +TsneSettings::TsneSettings(QWidget *parent) + : QWidget(parent) +{ + readSettigns(); + //Layout + tsneSettings->setContentsMargins(0, 0, 0, 0); + tsneSettings->addWidget(new QLabel("T-SNE Settings:"), 0, 0); + tsneSettings->addWidget(new QLabel(" Perplexity"), 1, 0); + tsneSettings->addWidget(new QLabel(" Lernrate"), 2, 0); + + perplexityEdit->setMinimumWidth(80); + perplexityEdit->setMaximumWidth(80); + perplexityEdit->setText(QString::number(perplexity)); + tsneSettings->addWidget(perplexityEdit, 1, 1); + + perplexitySlider->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Preferred); + perplexitySlider->setValue(perplexity); + perplexitySlider->setMinimum(perplexityMin); + perplexitySlider->setMaximum(perplexityMax); + perplexitySlider->setTickInterval((perplexityMax - perplexityMin + 1) / 10); + perplexitySlider->setTickPosition(QSlider::TickPosition::TicksBelow); + tsneSettings->addWidget(perplexitySlider, 1, 2); + + learnrateEdit->setMinimumWidth(80); + learnrateEdit->setMaximumWidth(80); + learnrateEdit->setText(QString::number(learnrate)); + tsneSettings->addWidget(learnrateEdit, 2, 1); + + learnrateSlider->setValue(learnrate); + learnrateSlider->setMinimum(learnrateMin); + learnrateSlider->setMaximum(learnrateMax); + learnrateSlider->setTickInterval((learnrateMax - learnrateMin + 1) / 10); + learnrateSlider->setTickPosition(QSlider::TickPosition::TicksBelow); + learnrateSlider->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Preferred); + tsneSettings->addWidget(learnrateSlider, 2, 2); + + + QRegExp rxMinMax("[0-9]+"); + QRegExpValidator* validatorMinMax = new QRegExpValidator(rxMinMax, 0); + learnrateEdit->setValidator(validatorMinMax); + perplexityEdit->setValidator(validatorMinMax); + + connect(perplexitySlider, &QSlider::valueChanged, this, &TsneSettings::setPerplexity); + connect(learnrateSlider, &QSlider::valueChanged, this, &TsneSettings::setLearnrate); + connect(learnrateEdit, &QLineEdit::editingFinished, this, &TsneSettings::learnrateEditChangeEvent); + connect(perplexityEdit, &QLineEdit::editingFinished, this, &TsneSettings::perplexityEditChangeEvent); +} + +TsneSettings::~TsneSettings() +{ + writeSettings(); +} + +void TsneSettings::setPerplexity(int value) +{ + if (perplexity == value) return; + perplexity = std::clamp(value, perplexityMin, perplexityMax); + perplexitySlider->setValue(perplexity); + perplexityEdit->setText(QString::number(perplexity)); + emit perplexityChanged(perplexity); +} + +void TsneSettings::setLearnrate(int value) +{ + if (learnrate == value) return; + learnrate = std::clamp(value, learnrateMin, learnrateMax); + learnrateSlider->setValue(learnrate); + learnrateEdit->setText(QString::number(learnrate)); + emit learnrateChanged(learnrate); +} + +void TsneSettings::readSettigns() +{ + //Settings + QSettings settings("settings.ini", QSettings::IniFormat, this); + settings.beginGroup("Tsne"); + maxIter = settings.value("maxIter", 750).toInt(); + perplexity = settings.value("perplexity", 10).toInt(); + perplexityMin = settings.value("perplexity_MIN", 1).toInt(); + perplexityMax = settings.value("perplexity_MAX", 50).toInt(); + learnrate = settings.value("learnrate", 200).toInt(); + learnrateMin = settings.value("learnrate_MIN", 1).toInt(); + learnrateMax = settings.value("learnrate_MAX", 1000).toInt(); + settings.endGroup(); +} + +void TsneSettings::writeSettings() +{ + //Settings + QSettings settings("settings.ini", QSettings::IniFormat, this); + settings.beginGroup("Tsne"); + settings.setValue("maxIter", maxIter); + settings.setValue("perplexity", perplexity); + settings.setValue("perplexity_MIN", perplexityMin); + settings.setValue("perplexity_MAX", perplexityMax); + settings.setValue("learnrate", learnrate); + settings.setValue("learnrate_MIN", learnrateMin); + settings.setValue("learnrate_MAX", learnrateMax); + settings.endGroup(); +} + +void TsneSettings::learnrateEditChangeEvent() +{ + bool success = false; + int value = learnrateEdit->text().toInt(&success); + if (success) { + setLearnrate(value); + } +} + +void TsneSettings::perplexityEditChangeEvent() +{ + bool success = false; + int value = perplexityEdit->text().toInt(&success); + if (success) { + setPerplexity(value); + } +} diff --git a/metavis/TsneSettings.h b/metavis/TsneSettings.h new file mode 100644 index 0000000000000000000000000000000000000000..2720b8dfb768266d391b8f191738928a6f88c0bc --- /dev/null +++ b/metavis/TsneSettings.h @@ -0,0 +1,34 @@ +#pragma once + +#include <QWidget> +#include <QLineEdit> +#include <QSlider> +#include <QGridLayout> + +class TsneSettings : public QWidget +{ + Q_OBJECT + +public: + TsneSettings(QWidget *parent); + ~TsneSettings(); + int maxIter; + int perplexity, perplexityMax, perplexityMin; + int learnrate, learnrateMax, learnrateMin; + void setPerplexity(int value); + void setLearnrate(int value); +protected: + QGridLayout* tsneSettings = new QGridLayout(this); + QSlider* perplexitySlider = new QSlider(Qt::Orientation::Horizontal); + QSlider* learnrateSlider = new QSlider(Qt::Orientation::Horizontal); + QLineEdit* perplexityEdit = new QLineEdit(); + QLineEdit* learnrateEdit = new QLineEdit(); +private: + void readSettigns(); + void writeSettings(); + void learnrateEditChangeEvent(); + void perplexityEditChangeEvent(); +signals: + void perplexityChanged(int perplexity); + void learnrateChanged(int learnrate); +}; diff --git a/metavis/Worker.cpp b/metavis/Worker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..83bc909517edb0161ed8b84c6ce30c8ab7255408 --- /dev/null +++ b/metavis/Worker.cpp @@ -0,0 +1,11 @@ +#include "pch.h" +#include "Worker.h" + +Worker::Worker(std::thread t) : t(std::move(t)) +{ +} + +Worker::~Worker() +{ + t.join(); +} diff --git a/metavis/Worker.h b/metavis/Worker.h new file mode 100644 index 0000000000000000000000000000000000000000..516bf2ac3a93043000085e9a8e4564a1aca80e9e --- /dev/null +++ b/metavis/Worker.h @@ -0,0 +1,10 @@ +#pragma once +#include <thread> +class Worker +{ + std::thread t; +public: + Worker(std::thread t); + ~Worker(); +}; + diff --git a/metavis/main.cpp b/metavis/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..770c974e9b1d6ebc22a93173c60552263caf7891 --- /dev/null +++ b/metavis/main.cpp @@ -0,0 +1,16 @@ +#include "pch.h" +#include "metavis.h" +#include <QtWidgets/QApplication> +#include <QtGui> +#include <QDebug> +#include <Vector> +#include <boost/multiprecision/cpp_int.hpp> +#include "RunData.h" +/* entrance of the program */ +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + metavis w; + w.show(); + return a.exec(); +} diff --git a/metavis/metavis.cpp b/metavis/metavis.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d7e9384b245c6baae35d406cc94de48ff21c6641 --- /dev/null +++ b/metavis/metavis.cpp @@ -0,0 +1,406 @@ +#include "pch.h" +#include "metavis.h" +#include "SettingDialog.h" +#include <QStandardPaths> +#include <QDockwidget> +#include <QLabel> +#include <QLayout> +#include <QDebug> +#include <QStyleFactory> +#include <QFileDialog> +#include <QDir> +#include <map> +#include <boost/multiprecision/cpp_int.hpp> +#include <QDesktopWidget> +#include <QSlider> +#include <QSizePolicy> +#include <QScrollArea> +#include <QMainWindow> +#include "DockableGraphView.h" +#include <QShortcut> +#include "RangeSlider.h" +#include "tsneIteractive.h" +#include "util.h" + + +metavis::metavis(QWidget* parent) + : QMainWindow(parent) +{ + ui.setupUi(this); + /* create settings object*/ + //settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, "TK", "metavis", this); + settings = new QSettings("settings.ini", QSettings::IniFormat, this); + //settings = new QSettings(QCoreApplication::applicationDirPath()+ "hatschi.ini", QSettings::IniFormat); + setStyleSheet(styleSheet() + "QMainWindow::separator {background: rgb(200, 200, 200);width: 1px;height: 1px;}"); + setStyleSheet(styleSheet() + "QTabBar::tab:selected {color: rgb(0, 122, 204);}"); + setStyleSheet(styleSheet() + "QTabWidget::pane {border-top: 0px solid #C2C7CB;margin: -9px -9px -13px -9px;}"); + this->setDockNestingEnabled(true); + option = dockOption::right; + createBitInSpector(); + QDockWidget* saveForTabPad = lastDocked; + option = dockOption::splitTop; + createProjectManager(); + option = dockOption::splitLeft; + createBitFieldV2(); + QDockWidget* saveForTabTsne = lastDocked; + initPlotter(); + createScratchpad(saveForTabPad); + createTSNE(saveForTabTsne); + + + readMainWindowSettings(); + ui.actionOpen->setShortcut(QKeySequence(Qt::CTRL + Qt::Key::Key_O)); + QMenu* viewMenu = createPopupMenu(); + viewMenu->setTitle("View"); + ui.menuBar->insertMenu(manager->projectMenu->menuAction(), viewMenu); + this->setCentralWidget(nullptr); +} + + + + +void metavis::openSetting() { + SettingDialog settingDialog(settings, this); + /* Blocking operation */ + settingDialog.exec(); +} + +metavis::~metavis() +{ + writeActualMainWindowSettings(); +} + +void metavis::selectRunData(RunData* data) +{ + selectedBestGraph->removeAll(); + selectedBestGraph->setDisplayLabel(QString::fromStdString(data->name)); + for (SingleRun& run : data->singleRunList) { + selectedBestGraph->addSeries(&run.bestMaxSolutionFoundPerIteration, data->name, QString::fromStdString(run.name), QColor(12, 116, 137, 200), GraphPlottSeries::SeriesType::Line); + } + selectedBestGraph->addSeries(&data->bestAverageMaxSolutionFoundPerIteration, data->name, "average best from " + QString::fromStdString(data->name), QColor(255, 0, 0), GraphPlottSeries::SeriesType::Line); + selectedBestGraph->getSeriesVector().back().lineWidth = 3; + selectedBestGraph->frameGraphInView(); +} + +void metavis::selectSingleRun(SingleRun* run) +{ + selectedBestAverageGraph->removeAll(); + selectedBestAverageGraph->setDisplayLabel(QString::fromStdString(run->name)); + selectedBestAverageGraph->addSeries(&run->bestMaxSolutionFoundPerIteration, run->runDataName, "best", QColor(255, 0, 0), GraphPlottSeries::SeriesType::Line); + selectedBestAverageGraph->addSeries(&run->averageSolutionPerItertion, run->runDataName, "average", QColor(0, 0, 255), GraphPlottSeries::SeriesType::Line); + selectedBestAverageGraph->frameGraphInView(); + + selectedMinMaxGraph->removeAll(); + selectedMinMaxGraph->setDisplayLabel(QString::fromStdString(run->name)); + selectedMinMaxGraph->addSeries(&run->dotsForDistribution, run->runDataName,"distribution", QColor(255, 165, 0, 100), GraphPlottSeries::SeriesType::Dot); + selectedMinMaxGraph->addSeries(&run->minSolutionPerItertion, run->runDataName,"min", QColor(255, 0, 0), GraphPlottSeries::SeriesType::Line); + selectedMinMaxGraph->addSeries(&run->maxSolutionPerItertion, run->runDataName,"max" , QColor(0, 0, 255), GraphPlottSeries::SeriesType::Line); + selectedMinMaxGraph->frameGraphInView(); + + selectedMeanHammingDistanceGraph->removeAll(); + selectedMeanHammingDistanceGraph->setDisplayLabel(QString::fromStdString(run->name)); + selectedMeanHammingDistanceGraph->addSeries(&run->meanHammingDistancePerIteration, run->runDataName, "mean Hamming Distance", QColor(255, 0, 0), GraphPlottSeries::SeriesType::Line); + selectedMeanHammingDistanceGraph->frameGraphInView(); + + bitfieldPanel->field->setDisplayLabel(QString::fromStdString(run->name)); + bitfieldPanel->displaySingleRun(run); + bitfieldPanel->update(); + + inspectorPanel->inspector->updateData(run->begin, run->end); + inspectorPanel->setRunName(run->name); + + tsnePanel->assignData(run->begin, run->end, QString::fromStdString(run->name)); +} + + + + +void metavis::removeRunDataFromAllViews(RunData* data) +{ + selectedBestAverageGraph->removeRunData(data); + selectedBestAverageGraph->update(); + + selectedMinMaxGraph->removeRunData(data); + selectedMinMaxGraph->update(); + + selectedMeanHammingDistanceGraph->removeRunData(data); + selectedMeanHammingDistanceGraph->update(); + + selectedBestGraph->removeRunData(data); + selectedBestGraph->update(); + + bitfieldPanel->clearRun(); + bitfieldPanel->update(); + + inspectorPanel->removeRun(); + tsnePanel->clear(); +} + + + +void metavis::dockWidget(QDockWidget* dock) +{ + switch (option) { + case left: + addDockWidget(Qt::LeftDockWidgetArea, dock); + break; + case right: + addDockWidget(Qt::RightDockWidgetArea, dock); + break; + case top: + addDockWidget(Qt::TopDockWidgetArea, dock); + break; + case bottom: + addDockWidget(Qt::BottomDockWidgetArea, dock); + break; + case splitLeft: + this->splitDockWidget(lastDocked, dock, Qt::Orientation::Horizontal); + this->splitDockWidget(dock, lastDocked, Qt::Orientation::Horizontal); + break; + case splitRight: + this->splitDockWidget(lastDocked, dock, Qt::Orientation::Horizontal); + break; + case splitBottom: + this->splitDockWidget(lastDocked, dock, Qt::Orientation::Vertical); + break; + case splitTop: + this->splitDockWidget(lastDocked, dock, Qt::Orientation::Vertical); + this->splitDockWidget(dock, lastDocked, Qt::Orientation::Vertical); + break; + case tab: + tabifyDockWidget(lastDocked, dock); + break; + default: + addDockWidget(Qt::LeftDockWidgetArea, dock); + break; + } + lastDocked = dock; +} + +GraphView* metavis::createCustomWidget(QString titleString, bool tabToLast) +{ + DockableGraphView* dock = new DockableGraphView(this, titleString); + qDebug() << titleString; + dockWidget(dock); + return dock->view; +} + + +void metavis::createBitFieldV2() +{ + QDockWidget* dock = new QDockWidget(this); + bitfieldPanel = new BitfieldControlPanel(this); + bitfieldPanel->field->setScratchpad(pad); + bitfieldPanel->field->setInformation("<h3><u>Bitfield</u></h3>This search space visualization display all found solution of a single run.<br>Hover over a dot two see its bitstring.<br>The color represents the objective function.<br><b><font color=\"gray\">Gray</font> patternd area:</b> represents the area where no solution can be generated.<br><b>Y Axis:</b> Represents the amount of set bits of a sollution.<br><b>X Axis:</b> Represents a position when all sollutions with this ammount of set bits are side by side<br> ordered with their corresponding value is decreasing.<br>Example with a four long bitstring and two set bits the possible solutions are:<br>1100 1010 1001 0110 0101 0011<br>The solution 0101 is the 5th possible sollution and gets the position value \'0.8\'.<br><br><b>Tipp</b> select a interesting area and add the points to the scratchpad for fast comparison.<h3><u>Options</u></h3>Transparency slider handles the transparency of the dots.<br>Size slider handles the radius of the dots.<br><b>Tipp</b> make the dots big and the transparency low the see clustering.<br>Under \'More Options\' the color for the objective function and the displayed itertion can be controlled."); + dock->setWindowTitle("Bitfield"); + dock->setObjectName("Bitfield"); + dock->setWidget(bitfieldPanel); + dockWidget(dock); +} + +void metavis::createProjectManager() +{ + QDockWidget* dock= new QDockWidget(this); + manager = new ProjectManager(this); + dock->setWindowTitle("Project Manager"); + dock->setObjectName("Project Manager"); + dock->setWidget(manager); + connect(ui.actionOpen, &QAction::triggered, manager, &ProjectManager::openFileDialog); + dockWidget(dock); + manager->projectMenu->setTitle("Project"); + ui.menuBar->addMenu(manager->projectMenu); +} + +void metavis::createTSNE(QDockWidget* dockto) +{ + + + QDockWidget* dock = new QDockWidget(this); + tsnePanel = new TsneControlPanel(this); + connect(tsnePanel, &TsneControlPanel::started, this, [this]() {this->showStatusBarMessage("T-SNE calculating...");}); + connect(tsnePanel, &TsneControlPanel::finished, this, &metavis::clearStatusBar); + tsnePanel->plott->setScratchpad(pad); + tsnePanel->plott->setInformation("<h3><u>T-SNE</u></h3>This search space evaluation uses a gradient descent method and have to be calculated first via the \"Start\" button.<br>This search space visualization display all found solution of a single run.<br>Hover over a dot two see its bitstring.<br>The color represents the objective function.<br> The axis have no special meaning beside to indicating the distance between solutions.<br><b>Start Button:</b> starts the tsne methods this may take a while.<br><b>Pause Button:</b> pause the tsne methods.<br><br><br><b>Tipp</b> select a interesting area and add the points to the scratchpad for fast comparison.<h3><u>Options</u></h3>Transparency slider handles the transparency of the dots.<br>Size slider handles the radius of the dots.<br><b>Tipp</b> make the dots big and the transparency low the see clustering.<br>Under \'More Options\' the color for the objective function and the displayed itertion can be controlled.<br> Also two important parameter for T-SNE can be controlled via slider or textfield.<br><b>Perplexity</b> is a parameter that should indicate how many neighbors a solution have.<br><b>Learnrate:</b> is a parameter that determine how fast the gradient descent methods make updates."); + dock->setWindowTitle("T-SNE"); + dock->setObjectName("T-SNE"); + dock->setWidget(tsnePanel); + this->tabifyDockWidget(dockto, dock); + dockto->raise(); +} + +GraphPlott* metavis::initGraphPlott(QString title, QString YAxisLegend, QString XAxisLegend) +{ + QDockWidget* dock = new QDockWidget(this); + GraphPlott* graphplott = new GraphPlott(this); + graphplott->setDefaultVisibleWindow(0, 100, 30, 110); + dock->setWidget(graphplott); + dock->setWindowTitle(title); + dock->setObjectName(title); + graphplott->setAxisLegend(XAxisLegend, YAxisLegend); + dockWidget(dock); + return graphplott; +} + +void metavis::initPlotter() +{ + option = splitLeft; + selectedBestGraph = initGraphPlott("Best Overview"); + selectedBestGraph->setScratchpad(pad); + selectedBestGraph->setInformation("<h3><u>Best Overview</u></h3>Shows all rounds best graph in <font color=\"gray\">gray</font> for comparison.<br>The <font color=\"red\">red</font> line shows the average best over all rounds.<br>By hovering over a line the round name is shown.<br><br><b>Objective Function:</b> Describes how well the solution is, it depends on the problem if a low or high value is desired."); + option = splitTop; + selectedBestAverageGraph = initGraphPlott("Best Average"); + selectedBestAverageGraph->setScratchpad(pad); + selectedBestAverageGraph->setInformation("<h3><u>Best Average</u></h3>The <font color=\"red\">red</font> line shows the best graph that presents the best objective function value found for each iteration of a single round.<br>The <font color=\"blue\">blue</font> line shows the average graph that presents the average objective function value in the popullation for each iteration of a single round.<br><br><b>Objective Function:</b> Describes how well the solution is, it depends on the problem if a low or high value is desired."); + //GraphPlott* selectedParticleGraph = initGraphPlott("Best Vs Average"); + option = tab; + selectedMinMaxGraph = initGraphPlott("Min Max Distribution"); + selectedMinMaxGraph->setInformation("<h3><u>Min Max Distribution</u></h3>The <font color=\"blue\">blue</font> line shows the max graph that presents the maximum objective function value in the popullation for each iteration of a single round.<br>The <font color=\"red\">red</font> line shows the minimum objective function value in the popullation for each iteration of a single round.<br>The <font color=\"orange\">orange</font> dots shows the all objective function values in the popullation for each iteration of a single round.<br><br><b>Objective Function:</b> Describes how well the solution is, it depends on the problem if the a or high value is desired."); + selectedMinMaxGraph->setScratchpad(pad); + option = tab; + selectedMeanHammingDistanceGraph = initGraphPlott("Mean Hamming Distance", "Mean Hamming Distance"); + selectedMeanHammingDistanceGraph->setInformation("<h3><u>Mean Hamming Distance</u></h3>The <font color=\"red\">red</font> line shows the mean hamming distance for each solution to each solution from the popullation for each iteration of a single round.<br><br><b>Hamming Distance:</b> Describes the distance between two bitstrings by calculating the amount of different bits, e.g. 00110 and 11100 have a hamming distance of 3."); + multiBestGraph; + multiAvgGraph; + multiMinGraph; + multiMaxGraph; + multiMeanHammingDistanceGraph; +} + + +void metavis::createBitInSpector() +{ + QDockWidget* dock = new QDockWidget(this); + inspectorPanel = new BitInspectorPanel(this); + dock->setWidget(inspectorPanel); + dock->setWindowTitle("Inspector"); + dock->setObjectName("Inspector"); + dockWidget(dock); +} + +void metavis::createScratchpad(QDockWidget* dockto) +{ + QDockWidget* dock = new QDockWidget(this); + dock->setWidget(pad); + dock->setWindowTitle("Scratchpad"); + dock->setObjectName("Scratchpad"); + this->tabifyDockWidget(dockto, dock); + dockto->raise(); +} + +void metavis::writeActualMainWindowSettings() +{ + settings->beginGroup("MainWindow"); + settings->setValue("maximized", isMaximized()); + if (!isMaximized()) { + /* position and size of the window if not maximized */ + settings->setValue("pos", pos()); + settings->setValue("size", size()); + settings->setValue("screenCount", QApplication::desktop()->screenCount()); + } + settings->setValue("geometry", saveGeometry()); + settings->setValue("windowState", saveState()); + settings->endGroup(); +} + +void metavis::readMainWindowSettings() +{ + settings->beginGroup("MainWindow"); + if (QApplication::desktop()->screenCount() == settings->value("screenCount", 1)) { + //Only when same screeenCount move the window; + move(settings->value("pos", QPoint(360, 200)).toPoint()); + } + resize(settings->value("size", QSize(1200, 675)).toSize()); + if (settings->value("maximized", false).toBool()) { + showMaximized(); + } + this->restoreGeometry(settings->value("geometry").toByteArray()); + this->restoreState(settings->value("windowState").toByteArray()); + settings->endGroup(); +} + + + + + + + +void metavis::openFile() +{ + + //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->bestMinSolutionFoundPerIteration, &*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); + }*/ + + + /* + 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 + /*RunData* rundata = &runList.back(); + + bitField->addDots(&rundata->dotsForBitField, rundata, QColor(255, 165, 0, 100)); + updateBitFieldColors(); + bitField->graphSeriesVec.back().useDataPointColor = true; + tsneWidget->assignRunData(&runList.back()); + inspector->addData(&runList.back().solutionVec); + plottTest->addSeries(&rundata->bestMaxSolutionFoundPerIteration, rundata, QColor(255, 165, 0, 100), GraphPlottSeries::SeriesType::Line);*/ + //actualMeanHmmingDistanceGraph->addLine(runVec[0].meanHammingDistancePerIteration, QColor(255, 0, 0)); + + +} + +void metavis::showStatusBarLoading() +{ + ui.statusBar->showMessage("Loading..."); + ui.statusBar->setStyleSheet("background-color: rgb(247, 197, 72);"); +} + +void metavis::showStatusBarMessage(QString message) +{ + ui.statusBar->showMessage(message); + ui.statusBar->setStyleSheet("background-color: rgb(247, 197, 72);"); +} + +void metavis::clearStatusBar() +{ + ui.statusBar->clearMessage(); + ui.statusBar->setStyleSheet("background-color: rgb(240, 240, 240);"); +} diff --git a/metavis/metavis.h b/metavis/metavis.h new file mode 100644 index 0000000000000000000000000000000000000000..7b9952c0929a64493fbdf74bd17443e454ac1c0e --- /dev/null +++ b/metavis/metavis.h @@ -0,0 +1,102 @@ +#pragma once +#include <QtWidgets/QMainWindow> +#include <QSettings> +#include <vector> +#include "ui_metavis.h" +#include "RunData.h" +#include "GraphView.h" + +#include "BitfieldControlPanel.h" +#include "TsneControlPanel.h" + +#include "GraphPlott.h" +#include "BitInspectorPanel.h" +#include "Scratchpad.h" +#include "tsneIteractive.h" +#include "ProjectManager.h" +class ProjectManager; + + +/** + * Main class of the GUI. + */ +class metavis : public QMainWindow +{ + Q_OBJECT + +public: + metavis(QWidget *parent = Q_NULLPTR); + ~metavis(); + GraphPlott* selectedBestAverageGraph; + GraphPlott* selectedParticleGraph; + GraphPlott* selectedMinMaxGraph; + GraphPlott* selectedMeanHammingDistanceGraph; + GraphPlott* selectedBestGraph; + + + + GraphPlott* multiBestGraph; + GraphPlott* multiAvgGraph; + GraphPlott* multiMinGraph; + GraphPlott* multiMaxGraph; + GraphPlott* multiMeanHammingDistanceGraph; + + GraphPlott* plottTest; + + BitfieldControlPanel* bitfieldPanel; + BitInspectorPanel* inspectorPanel; + Scratchpad* pad = new Scratchpad(this); + tsneIteractive* tsneWidget; + TsneControlPanel* tsnePanel; + GraphView* bitField; + + enum dockOption{left, right, bottom, top, splitLeft, splitRight, splitBottom, splitTop, tab}; + + void selectRunData(RunData* data); + void selectSingleRun(SingleRun* run); + void removeRunDataFromAllViews(RunData* data); + + + + + +private: + Ui::metavisClass ui; + QSettings* settings; + ProjectManager* manager; + + //To Tab all + QDockWidget* lastDocked; + dockOption option; + void dockWidget(QDockWidget* dock); + +private: + /* Widget functions */ + GraphView* createCustomWidget(QString titleString, bool tabToLast = false); + void createBitFieldV2(); + void createProjectManager(); + void createTSNE(QDockWidget* dockto); + GraphPlott* initGraphPlott(QString title, QString YAxisLegend = "ObjectiveFunction", QString XAxisLegend = "Iteration"); + void initPlotter(); + void createBitInSpector(); + void createScratchpad(QDockWidget* dockto); + /* Setting functions*/ + void writeActualMainWindowSettings(); + void readMainWindowSettings(); + + + + +public slots: + /** + * Opens the settingWindows Dialog. + */ + void openSetting(); + /** + * Open a logFile. + */ + void openFile(); + void showStatusBarLoading(); + void showStatusBarMessage(QString message); + void clearStatusBar(); +}; diff --git a/metavis/metavis.ico b/metavis/metavis.ico new file mode 100644 index 0000000000000000000000000000000000000000..6396b1c0ca3d24c3ed0e9fea8cbc648b116fb373 Binary files /dev/null and b/metavis/metavis.ico differ diff --git a/metavis/metavis.qrc b/metavis/metavis.qrc new file mode 100644 index 0000000000000000000000000000000000000000..65fbfad7decc2d18f682e8cd12141f4d3576279c --- /dev/null +++ b/metavis/metavis.qrc @@ -0,0 +1,21 @@ +<RCC> + <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> + <file>Resources/information_icon.svg</file> + <file>Resources/information_icon_hovered.svg</file> + <file>Resources/gridIcon.svg</file> + <file>Resources/gridIcon_hovered.svg</file> + <file>Resources/up-triangle.svg</file> + <file>Resources/frame.svg</file> + <file>Resources/frame_hovered.svg</file> + <file>Resources/binaryIcon.svg</file> + <file>Resources/rubbish.svg</file> + <file>Resources/rubbish_hovered.svg</file> + <file>Resources/arrow_right.svg</file> + <file>Resources/file.svg</file> + </qresource> +</RCC> diff --git a/metavis/metavis.rc b/metavis/metavis.rc new file mode 100644 index 0000000000000000000000000000000000000000..d9089ef151a0c1ea54bc41d2faab4cbfa02be966 --- /dev/null +++ b/metavis/metavis.rc @@ -0,0 +1,2 @@ +IDI_ICON1 ICON DISCARDABLE "metavis.ico" + diff --git a/metavis/metavis.ui b/metavis/metavis.ui new file mode 100644 index 0000000000000000000000000000000000000000..88595b8fc44d57baf30aef8aab27ec0f2c8645f6 --- /dev/null +++ b/metavis/metavis.ui @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>metavisClass</class> + <widget class="QMainWindow" name="metavisClass"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1200</width> + <height>675</height> + </rect> + </property> + <property name="windowTitle"> + <string>metavis</string> + </property> + <widget class="QWidget" name="centralWidget"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>0</number> + </property> + <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> + </layout> + </widget> + <widget class="QMenuBar" name="menuBar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1200</width> + <height>21</height> + </rect> + </property> + <property name="nativeMenuBar"> + <bool>true</bool> + </property> + <widget class="QMenu" name="menuFile"> + <property name="title"> + <string>File</string> + </property> + <addaction name="actionOpen"/> + </widget> + <addaction name="menuFile"/> + </widget> + <widget class="QStatusBar" name="statusBar"/> + <action name="actionOpen"> + <property name="text"> + <string>Open..</string> + </property> + <property name="shortcut"> + <string>Ctrl+O</string> + </property> + </action> + <action name="actionSettings"> + <property name="text"> + <string>Settings</string> + </property> + <property name="shortcut"> + <string>Ctrl+P</string> + </property> + </action> + <action name="ActionAddMetalogFile"> + <property name="text"> + <string>Add .csv-File..</string> + </property> + </action> + <action name="ActionRemoveFiles"> + <property name="text"> + <string>Remove all files.</string> + </property> + </action> + </widget> + <layoutdefault spacing="6" margin="11"/> + <resources> + <include location="metavis.qrc"/> + </resources> + <connections/> + <slots> + <slot>openSetting()</slot> + <slot>openFile()</slot> + </slots> +</ui> diff --git a/metavis/metavis.vcxproj b/metavis/metavis.vcxproj new file mode 100644 index 0000000000000000000000000000000000000000..28d3eb5ce25fd09416576a692a9b133ab7cbde32 --- /dev/null +++ b/metavis/metavis.vcxproj @@ -0,0 +1,202 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{B12702AD-ABFB-343A-A199-8E24837244A3}</ProjectGuid> + <Keyword>QtVS_v301</Keyword> + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <PlatformToolset>v142</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup Condition="'$(QtMsBuild)'=='' or !Exists('$(QtMsBuild)\qt.targets')"> + <QtMsBuild>$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <OutDir>$(SolutionDir)Build\$(Platform)\$(Configuration)\</OutDir> + <IntDir>$(SolutionDir)Intermediate\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <OutDir>$(SolutionDir)Build\$(Platform)\$(Configuration)\</OutDir> + <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." /> + </Target> + <ImportGroup Label="ExtensionSettings" /> + <ImportGroup Label="Shared" /> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"> + <Import Project="$(QtMsBuild)\qt_defaults.props" /> + </ImportGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <QtInstall>msvc2017_64</QtInstall> + <QtModules>charts;core;gui;widgets;;concurrent</QtModules> + </PropertyGroup> + <PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <QtInstall>msvc2017_64</QtInstall> + <QtModules>charts;core;gui;widgets;;concurrent</QtModules> + </PropertyGroup> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"> + <Import Project="$(QtMsBuild)\qt.props" /> + </ImportGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <Optimization>Disabled</Optimization> + <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> + <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <AdditionalIncludeDirectories>$(SolutionDir)\metaviscon\;C:\Program Files (x86)\boost\boost_1_72_0;.\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <LanguageStandard>stdcpp17</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\$(ProjectName).exe</OutputFile> + <GenerateDebugInformation>true</GenerateDebugInformation> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>$(Qt_LIBS_);%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <MultiProcessorCompilation>true</MultiProcessorCompilation> + <DebugInformationFormat /> + <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> + <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <AdditionalIncludeDirectories>$(SolutionDir)\metaviscon\;C:\Program Files (x86)\boost\boost_1_72_0;.\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <LanguageStandard>stdcpp17</LanguageStandard> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <OutputFile>$(OutDir)\$(ProjectName).exe</OutputFile> + <GenerateDebugInformation>false</GenerateDebugInformation> + <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>$(Qt_LIBS_);%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\metaviscon\src\t_sne\sptree.cpp" /> + <ClCompile Include="..\metaviscon\src\t_sne\tsne.cpp" /> + <ClCompile Include="Bitfield.cpp" /> + <ClCompile Include="BitfieldControlPanel.cpp" /> + <ClCompile Include="BitInspector.cpp" /> + <ClCompile Include="BitInspectorPanel.cpp" /> + <ClCompile Include="ColorButton.cpp" /> + <ClCompile Include="ColorGradient.cpp" /> + <ClCompile Include="Concurrent.cpp" /> + <ClCompile Include="DockableGraphView.cpp" /> + <ClCompile Include="GraphPlott.cpp" /> + <ClCompile Include="GraphView.cpp" /> + <ClCompile Include="GraphViewSettingDialog.cpp" /> + <ClCompile Include="GraphViewSettingItem.cpp" /> + <ClCompile Include="HoverButton.cpp" /> + <ClCompile Include="InformationPopUp.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="Plott.cpp" /> + <ClCompile Include="Project.cpp" /> + <ClCompile Include="ProjectManager.cpp" /> + <ClCompile Include="RangeSlider.cpp" /> + <ClCompile Include="RunData.cpp" /> + <ClCompile Include="Scratchpad.cpp" /> + <ClCompile Include="SearchSpacePlott.cpp" /> + <ClCompile Include="SettingDialog.cpp" /> + <ClCompile Include="tSneAlgo.cpp" /> + <ClCompile Include="TsneControlPanel.cpp" /> + <ClCompile Include="tsneIteractive.cpp" /> + <ClCompile Include="TsnePlott.cpp" /> + <ClCompile Include="TsneSettings.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" /> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="metavis.rc" /> + </ItemGroup> + <ItemGroup> + <QtMoc Include="SettingDialog.h" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\metaviscon\src\t_sne\sptree.h" /> + <ClInclude Include="..\metaviscon\src\t_sne\tsne.h" /> + <ClInclude Include="..\metaviscon\src\t_sne\vptree.h" /> + <QtMoc Include="ColorGradient.h" /> + <QtMoc Include="ColorButton.h" /> + <QtMoc Include="BitInspector.h" /> + <QtMoc Include="Bitfield.h" /> + <QtMoc Include="BitfieldControlPanel.h" /> + <QtMoc Include="BitInspectorPanel.h" /> + <ClInclude Include="Concurrent.h" /> + <QtMoc Include="Plott.h" /> + <QtMoc Include="GraphPlott.h" /> + <QtMoc Include="HoverButton.h" /> + <QtMoc Include="InformationPopUp.h" /> + <ClInclude Include="Project.h" /> + <QtMoc Include="tSneAlgo.h" /> + <QtMoc Include="RangeSlider.h" /> + <QtMoc Include="tsneIteractive.h" /> + <QtMoc Include="Scratchpad.h" /> + <QtMoc Include="SearchSpacePlott.h" /> + <QtMoc Include="TsneControlPanel.h" /> + <QtMoc Include="TsnePlott.h" /> + <QtMoc Include="TsneSettings.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> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"> + <Import Project="$(QtMsBuild)\qt.targets" /> + </ImportGroup> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> \ No newline at end of file diff --git a/metavis/metavis.vcxproj.filters b/metavis/metavis.vcxproj.filters new file mode 100644 index 0000000000000000000000000000000000000000..0fabe9aeb9dfecfa0621a95ffb55534cd2c968d0 --- /dev/null +++ b/metavis/metavis.vcxproj.filters @@ -0,0 +1,313 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}</UniqueIdentifier> + <Extensions>qrc;*</Extensions> + <ParseFiles>false</ParseFiles> + </Filter> + <Filter Include="Form Files"> + <UniqueIdentifier>{99349809-55BA-4b9d-BF79-8FDBB0286EB3}</UniqueIdentifier> + <Extensions>ui</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}</UniqueIdentifier> + <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> + <Filter Include="Source Files\Algorithm"> + <UniqueIdentifier>{08564a5b-8aa0-4f33-bf76-2f50ff559168}</UniqueIdentifier> + </Filter> + <Filter Include="Source Files\Algorithm\tSNE"> + <UniqueIdentifier>{94e95fb3-1251-48f0-9e2f-ef92e78f6777}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\Data\algorithm"> + <UniqueIdentifier>{c236e5d9-90d7-4b48-beb3-ed38213acbbc}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\Data\algorithm\tSNE"> + <UniqueIdentifier>{492da30c-1983-45f2-a819-091e43e6d706}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files\MultiThreading"> + <UniqueIdentifier>{c80d6242-cd8a-4453-92f3-102a7a2a8474}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <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\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\UserInterface\MainMenu</Filter> + </ClCompile> + <ClCompile Include="pch.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <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> + <ClCompile Include="..\metaviscon\src\t_sne\tsne.cpp"> + <Filter>Source Files\Algorithm\tSNE</Filter> + </ClCompile> + <ClCompile Include="..\metaviscon\src\t_sne\sptree.cpp"> + <Filter>Source Files\Algorithm\tSNE</Filter> + </ClCompile> + <ClCompile Include="ColorGradient.cpp"> + <Filter>Source Files\UserInterface\Widget</Filter> + </ClCompile> + <ClCompile Include="ColorButton.cpp"> + <Filter>Source Files\UserInterface\Widget</Filter> + </ClCompile> + <ClCompile Include="tSneAlgo.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Concurrent.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Plott.cpp"> + <Filter>Source Files\UserInterface\GraphView</Filter> + </ClCompile> + <ClCompile Include="BitInspector.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="HoverButton.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="GraphPlott.cpp"> + <Filter>Source Files\UserInterface\GraphView</Filter> + </ClCompile> + <ClCompile Include="Bitfield.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="BitfieldControlPanel.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="BitInspectorPanel.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Scratchpad.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="SearchSpacePlott.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TsneControlPanel.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TsnePlott.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="TsneSettings.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="InformationPopUp.cpp"> + <Filter>Source Files</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\UserInterface\MainMenu</Filter> + </QtMoc> + <QtMoc Include="ProjectManager.h"> + <Filter>Header Files\UserInterface\ProjectManager</Filter> + </QtMoc> + <QtMoc Include="SettingDialog.h"> + <Filter>Header Files\UserInterface\MainMenu</Filter> + </QtMoc> + <QtMoc Include="RangeSlider.h"> + <Filter>Header Files\UserInterface\Widget</Filter> + </QtMoc> + <QtMoc Include="ColorGradient.h"> + <Filter>Header Files\UserInterface\Widget</Filter> + </QtMoc> + <QtMoc Include="tsneIteractive.h"> + <Filter>Header Files\UserInterface\MainMenu</Filter> + </QtMoc> + <QtMoc Include="ColorButton.h"> + <Filter>Header Files\UserInterface\Widget</Filter> + </QtMoc> + <QtMoc Include="tSneAlgo.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="Plott.h"> + <Filter>Header Files\UserInterface\GraphView</Filter> + </QtMoc> + <QtMoc Include="BitInspector.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="Bitfield.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="BitfieldControlPanel.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="GraphPlott.h"> + <Filter>Header Files\UserInterface\GraphView</Filter> + </QtMoc> + <QtMoc Include="HoverButton.h"> + <Filter>Header Files\UserInterface\Widget</Filter> + </QtMoc> + <QtMoc Include="BitInspectorPanel.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="Scratchpad.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="SearchSpacePlott.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="TsneControlPanel.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="TsnePlott.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="TsneSettings.h"> + <Filter>Header Files</Filter> + </QtMoc> + <QtMoc Include="InformationPopUp.h"> + <Filter>Header Files</Filter> + </QtMoc> + </ItemGroup> + <ItemGroup> + <QtUic Include="metavis.ui"> + <Filter>Form Files</Filter> + </QtUic> + <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"> + <Filter>Resource Files</Filter> + </QtRcc> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="metavis.rc" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="pch.h"> + <Filter>Header Files</Filter> + </ClInclude> + <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> + <ClInclude Include="..\metaviscon\src\t_sne\vptree.h"> + <Filter>Header Files\Data\algorithm\tSNE</Filter> + </ClInclude> + <ClInclude Include="..\metaviscon\src\t_sne\sptree.h"> + <Filter>Header Files\Data\algorithm\tSNE</Filter> + </ClInclude> + <ClInclude Include="..\metaviscon\src\t_sne\tsne.h"> + <Filter>Header Files\Data\algorithm\tSNE</Filter> + </ClInclude> + <ClInclude Include="Concurrent.h"> + <Filter>Header Files\MultiThreading</Filter> + </ClInclude> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/metavis/pch.cpp b/metavis/pch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17305716aacd2a39a54423c3fb2000dc2266794a --- /dev/null +++ b/metavis/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" \ No newline at end of file diff --git a/metavis/pch.h b/metavis/pch.h new file mode 100644 index 0000000000000000000000000000000000000000..26791a56c157942a232d190b7fd3696c1b6730d7 --- /dev/null +++ b/metavis/pch.h @@ -0,0 +1,36 @@ +#pragma once +//QT +#include <QWidget> +#include <QPaintEvent> +#include <QPen> +#include <QPainter> +#include <QSettings> +#include <QPoint> +#include <QDialog> +#include <QAbstractButton> +#include <QDebug> +#include <QBrush> +#include <QtWidgets/QApplication> +#include <QtGui> +#include <QStandardPaths> +#include <QDockwidget> +#include <QLabel> +#include <QLayout> +#include <QStyleFactory> +#include <QFileDialog> +#include <QDir> +#include <QString> +#include <QToolTip> +#include <QTimer> +#include <QSlider> + +//Std +#include <list> +#include <vector> +#include <string> +#include <fstream> +#include <map> +#include <random> +#include <algorithm> +#include <sstream> +#include <regex> \ No newline at end of file diff --git a/metavis/tSneAlgo.cpp b/metavis/tSneAlgo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5b87f7e1efc3806c74f0920135e54934e0ff4feb --- /dev/null +++ b/metavis/tSneAlgo.cpp @@ -0,0 +1,691 @@ +#include "pch.h" +#include "tSneAlgo.h" +#include <cfloat> +#include <cmath> +#include <cstdlib> +#include <cstdio> +#include <cstring> +#include <ctime> + + +//This product includes software developed by the Delft University of Technology. + + +#include "src/t_sne/tsne.h" +#include "src/t_sne/vptree.h" +#include "src/t_sne/sptree.h" + +#pragma warning(disable:4996) + + +static double sign(double inputArrayX) { return (inputArrayX == .0 ? .0 : (inputArrayX < .0 ? -1.0 : 1.0)); } + +static void zeroMean(double* inputArrayX, int N, int D); +static void computeGaussianPerplexity(double* inputArrayX, int N, int D, double* P, double perplexity); +static void computeGaussianPerplexity(double* inputArrayX, 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* inputArrayX, int N, int D, double* DD); +static void symmetrizeMatrix(unsigned int** row_P, unsigned int** col_P, double** val_P, int N); + +tSneAlgo::tSneAlgo(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end, double** YnotInitialized, double perplexity, double learningRate, int maxIter) + :perplexity(perplexity), learningRate(learningRate), N(std::distance(begin, end)), D(begin->bitVec.size()), maxIter(maxIter) +{ + //N -> amount of dataPoints + //D -> Dimension of DataPoints + qDebug() << "N:" << N << " D:" << D; + + //Create Input Matrix + inputArrayX = new double[N * D]; + for (int n = 0; n < N; n++) { + const SolutionPointData& sol = *std::next(begin, n); + for (int d = 0; d < D; d++) { + inputArrayX[n * D + d] = sol.bitVec[d] ? 1.0 : 0.0; + } + } + + //Create Output Matrix + *YnotInitialized = outputArrayY = (double*)calloc(N * outputDimesion, sizeof(double)); +} + +tSneAlgo::~tSneAlgo() +{ + reset(); + delete inputArrayX; + delete outputArrayY; +} + +void tSneAlgo::run() +{ + //TSNE::run + //Init + // Set random seed + + if (useRandomSeed != true) { + if (randomSeet >= 0) { + printf("Using random seed: %d\n", randomSeet); + srand((unsigned int)randomSeet); + } + 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", outputDimesion, 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 * outputDimesion * sizeof(double)); + double* uY = (double*)malloc(N * outputDimesion * sizeof(double)); + double* gains = (double*)malloc(N * outputDimesion * sizeof(double)); + if (dY == NULL || uY == NULL || gains == NULL) { printf("Memory allocation failed!\n"); exit(1); } + for (int i = 0; i < N * outputDimesion; i++) uY[i] = .0; + for (int i = 0; i < N * outputDimesion; i++) gains[i] = 1.0; + + // Normalize input data (to prevent numerical problems) + printf("Computing input similarities...\n"); + start = clock(); + zeroMean(inputArrayX, N, D); + double max_X = 0.0; + for (int i = 0; i < N * D; i++) { + if (fabs(inputArrayX[i]) > max_X) max_X = fabs(inputArrayX[i]); + } + for (int i = 0; i < N * D; i++) inputArrayX[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(inputArrayX, 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(inputArrayX, 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 (skipRandomInit != true) { + for (int i = 0; i < N * outputDimesion; i++) outputArrayY[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 (actualIteration = 0; actualIteration < maxIter; actualIteration++) { + checkPaused(); + if (checkCancel()) break; + emit changedIter(actualIteration); + // Compute (approximate) gradient + if (exact) computeExactGradient(P, outputArrayY, N, outputDimesion, dY); + else computeGradient(row_P, col_P, val_P, outputArrayY, N, outputDimesion, dY, theta); + + // Update gains + for (int i = 0; i < N * outputDimesion; i++) gains[i] = (sign(dY[i]) != sign(uY[i])) ? (gains[i] + .2) : (gains[i] * .8); + for (int i = 0; i < N * outputDimesion; i++) if (gains[i] < .01) gains[i] = .01; + + // Perform gradient update (with momentum and gains) + for (int i = 0; i < N * outputDimesion; i++) uY[i] = momentum * uY[i] - learningRate * gains[i] * dY[i]; + for (int i = 0; i < N * outputDimesion; i++) outputArrayY[i] += uY[i]; + + // Make solution zero-mean + zeroMean(outputArrayY, N, outputDimesion); + + // Stop lying about the P-values after a while, and switch momentum + if (actualIteration == stopLyingIter) { + 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 (actualIteration == momentumSwitchIter) momentum = final_momentum; + + // Print out progress + if (actualIteration > 0 && (actualIteration % 50 == 0 || actualIteration == maxIter - 1)) { + end = clock(); + double C = .0; + if (exact) C = evaluateError(P, outputArrayY, N, outputDimesion); + else C = evaluateError(row_P, col_P, val_P, outputArrayY, N, outputDimesion, theta); // doing approximate computation here! + + if (actualIteration == 0) + printf("Iteration %d: error is %f\n", actualIteration + 1, C); + else { + total_time += (float)(end - start) / CLOCKS_PER_SEC; + printf("Iteration %d: error is %f (50 iterations in %4.2f seconds)\n", actualIteration, C, (float)(end - start) / CLOCKS_PER_SEC); + } + start = clock(); + 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); + emit algoDone(); +} + + +void tSneAlgo::setLearningRate(double epsillon) +{ + learningRate = epsillon; + +} + +void tSneAlgo::setPerplexity(double perplexity) +{ + this->perplexity = perplexity; +} + + + +// 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* inputArrayX, 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(inputArrayX, 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* inputArrayX, 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, inputArrayX)); + for (int n = 0; n < N; n++) obj_X[n] = DataPoint(D, n, inputArrayX + 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* inputArrayX, int N, int D, double* DD) { + const double* XnD = inputArrayX; + 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* inputArrayX, 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] += inputArrayX[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++) { + inputArrayX[nD + d] -= mean[d]; + } + nD += D; + } + free(mean); mean = NULL; +} + + +// Generates a Gaussian random number +static double randn() { + double inputArrayX, y, radius; + do { + inputArrayX = 2 * (rand() / ((double)RAND_MAX + 1)) - 1; + y = 2 * (rand() / ((double)RAND_MAX + 1)) - 1; + radius = (inputArrayX * inputArrayX) + (y * y); + } while ((radius >= 1.0) || (radius == 0.0)); + radius = sqrt(-2 * log(radius) / radius); + inputArrayX *= radius; + y *= radius; + return inputArrayX; +} \ No newline at end of file diff --git a/metavis/tSneAlgo.h b/metavis/tSneAlgo.h new file mode 100644 index 0000000000000000000000000000000000000000..1756ebb8ae6478615b3a86297168b413f487f21a --- /dev/null +++ b/metavis/tSneAlgo.h @@ -0,0 +1,37 @@ +#pragma once +#include <QObject> +#include "RunData.h" +#include "Concurrent.h" +#include <vector> + + +class tSneAlgo : public QObject, public Concurrent +{ + Q_OBJECT +public: + tSneAlgo(std::vector<SolutionPointData>::iterator begin, std::vector<SolutionPointData>::iterator end, double** YnotInitialized, double perplexity, double learningRate, int maxIter); + ~tSneAlgo(); + int actualIteration = 0; + double perplexity, learningRate; + // in between 0.0 and 1.0 + double theta = 0; + int maxIter = 750, stopLyingIter = 120, momentumSwitchIter = 20; +private: + int N, D, outputDimesion = 2; + bool useRandomSeed = true; + bool skipRandomInit = false; + int randomSeet = 182437123; + + //intermediate + double* inputArrayX; + double* outputArrayY; + void run() override; + +public: + void setLearningRate(double epsillon); + void setPerplexity(double perplexity); +signals: + void changedIter(int iter); + void algoDone(); +}; + diff --git a/metavis/tsneIteractive.cpp b/metavis/tsneIteractive.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d7ad836f6adae843dbf57ed62a5440592849305 --- /dev/null +++ b/metavis/tsneIteractive.cpp @@ -0,0 +1,120 @@ +#include "pch.h" +#include "tsneIteractive.h" +#include <QDebug> +#include <thread> +#include <future> +#include <chrono> + +#include "src/t_sne/tsne.h" +#include "tSneAlgo.h" + +tsneIteractive::tsneIteractive(QWidget *parent) + : QWidget(parent), view(new GraphView(this, Bound(-50,50,-50,50))), gradient(new ColorGradient(this)) +{ + ui.setupUi(this); + ui.verticalLayout->insertWidget(0, view); + view->setSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding); + ui.verticalLayout->addWidget(gradient); + //ui.gridLayout->addWidget(gradient, 4, 0); + //ui.gridLayout->addWidget(gradient, 4, 0, 1, 4); + view->setUseFixedBound(true); + connect(timer, &QTimer::timeout, this, static_cast<void (tsneIteractive::*)()>(&tsneIteractive::updateCanvasIfAlgoRuns)); + connect(ui.startButton, &QPushButton::pressed, + this, &tsneIteractive::startRun); + connect(ui.pauseButton, &QPushButton::pressed, + this, &tsneIteractive::pauseRun); + connect(gradient, &ColorGradient::gradientChanged, this, &tsneIteractive::updateViewGradient); + ui.perplexitySlider->setMinimum(1); + ui.perplexitySlider->setMaximum(50); + ui.perplexitySlider->setValue(20); + ui.perplexityLineEdit->setText(QString::number(20)); + ui.learnrateSlider->setMinimum(1); + ui.learnrateSlider->setMaximum(1000); + ui.learnrateSlider->setValue(200); + ui.learnrateLineEdit->setText(QString::number(200)); + connect(ui.learnrateSlider, &QSlider::valueChanged, this, [this](int value){ + ui.learnrateSlider->setToolTip(QString::number(value)); + ui.learnrateLineEdit->setText(QString::number(value)); + if (tsneConcurrent != nullptr) { + this->tsneConcurrent->setLearningRate(value); + } + }); + connect(ui.perplexitySlider, &QSlider::valueChanged, this, [this](int value) { + ui.perplexitySlider->setToolTip(QString::number(value)); + ui.perplexityLineEdit->setText(QString::number(value)); + if (tsneConcurrent != nullptr) { + this->tsneConcurrent->setPerplexity(value); + } + }); + ui.progressBar->setMinimum(0); + ui.progressBar->setMaximum(750); + ui.progressBar->setValue(0); + +} + +tsneIteractive::~tsneIteractive() +{ + if (tsneConcurrent != nullptr) { + delete tsneConcurrent; + } +} + +void tsneIteractive::assignRunData(RunData* data) +{ + this->data = data; +} + +tSneAlgo* tsneIteractive::getTsneConcurrent() +{ + return tsneConcurrent; +} + +void tsneIteractive::updateCanvasIfAlgoRuns() +{ + if (tsneConcurrent->actualIteration >= 750) { + timer->stop(); + } + ui.progressBar->setValue(tsneConcurrent->actualIteration); + //view->autoZoomOut(); + view->update(); + ui.iterLabel->setText(QString("Iteration: ") + QString::number(tsneConcurrent->actualIteration)); +} + +void tsneIteractive::updateViewGradient() +{ + view->updateGraphColors(*gradient); + update(); +} + +void tsneIteractive::pauseRun() +{ + qDebug() << "Pause"; + tsneConcurrent->pause(); + if (timer->isActive()) { + timer->stop(); + } + else { + timer->start(); + } +} + +void tsneIteractive::startRun() +{ + if (data == nullptr) { + qDebug() << "NoData!"; + return; + } + double* Y = nullptr; + double** X = &Y; + if (tsneConcurrent != nullptr) { + delete tsneConcurrent; + } + tsneConcurrent = new tSneAlgo(data->solutionVec.begin(), data->solutionVec.end(), X, 20, 200, 750); + tsneConcurrent->start(); + qDebug() << "Y:" << Y; + view->addYMatrix(Y, data->solutionVec); + view->updateGraphColors(*gradient); + view->update(); + timer->start(34); + +} diff --git a/metavis/tsneIteractive.h b/metavis/tsneIteractive.h new file mode 100644 index 0000000000000000000000000000000000000000..b4fc4c8315eab5c1efebae09edc283f8b44dd501 --- /dev/null +++ b/metavis/tsneIteractive.h @@ -0,0 +1,35 @@ +#pragma once + +#include <QWidget> +#include "ui_tsneIteractive.h" +#include "GraphView.h" +#include "RunData.h" +#include "ColorGradient.h" +#include "tSneAlgo.h" + +class tsneIteractive : public QWidget +{ + Q_OBJECT +public: + tsneIteractive(QWidget *parent = Q_NULLPTR); + ~tsneIteractive(); + + GraphView* view; + ColorGradient* gradient; + RunData* data = nullptr; + void assignRunData(RunData* data); + tSneAlgo* tsneConcurrent = nullptr; + tSneAlgo* getTsneConcurrent(); +private: + Ui::tsneIteractive ui; + + //Concurrent + QTimer* timer = new QTimer(this); + void updateCanvasIfAlgoRuns(); + void updateViewGradient(); + + +public slots: + void startRun(); + void pauseRun(); +}; diff --git a/metavis/tsneIteractive.ui b/metavis/tsneIteractive.ui new file mode 100644 index 0000000000000000000000000000000000000000..3b527f5fe62da3324d901b8569c948ed33a33181 --- /dev/null +++ b/metavis/tsneIteractive.ui @@ -0,0 +1,174 @@ +<?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="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="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="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="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="2"> + <widget class="QLineEdit" name="perplexityLineEdit"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>70</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="iterLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>20</height> + </size> + </property> + <property name="text"> + <string>Iteration: 0</string> + </property> + </widget> + </item> + <item row="1" column="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="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="2" column="2"> + <widget class="QLineEdit" name="learnrateLineEdit"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>70</width> + <height>16777215</height> + </size> + </property> + </widget> + </item> + <item row="2" column="3" colspan="2"> + <widget class="QSlider" name="learnrateSlider"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="tickPosition"> + <enum>QSlider::TicksBelow</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11"/> + <resources/> + <connections/> +</ui> diff --git a/metavis/util.h b/metavis/util.h new file mode 100644 index 0000000000000000000000000000000000000000..7de822e6e70ca528f6d938bad56a9dd36c3cf865 --- /dev/null +++ b/metavis/util.h @@ -0,0 +1,36 @@ +#pragma once +#include <QColor> +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); + } + inline QColor interpolateHSL(QColor& first, QColor& second, double alpha) + { + double firstH, firstS, firstL; + double secondH, secondS, secondL; + + first.getHslF(&firstH, &firstS, &firstL); + second.getHslF(&secondH, &secondS, &secondL); + if (firstH == -1) { + firstH = (secondH != -1) ? secondH : 0; + } + if (secondH == -1) { + secondH = (firstH != -1) ? firstH : 0; + } + 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); + } + inline QColor interpolateRGB(QColor& first, QColor& second, double alpha) + { + const double r = util::linearInterpolate(first.redF(), second.redF(), alpha); + const double g = util::linearInterpolate(first.greenF(), second.greenF(), alpha); + const double b = util::linearInterpolate(first.blueF(), second.blueF(), alpha); + return QColor::fromRgbF(r, g, b); + } +}