123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- #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);
- }
|