#include "pch.h" #include "GraphView.h" #include #include #include #include #include #include #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 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(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::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* 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& 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 realDistr(-30, 30); std::uniform_int_distribution intScaleDistr(1, 3); for (int randomSeries = 0; randomSeries < 1; randomSeries++) { std::vector 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* line, RunData* run) { addSeries(line, run,generateNextColorForGraph(), GraphSeries::SeriesType::Line); } void GraphView::addLine(std::vector* line, RunData* run, QColor color) { addSeries(line, run, color, GraphSeries::SeriesType::Line); } void GraphView::addDots(std::vector* dots, RunData* run) { addSeries(dots, run, generateNextColorForGraph(), GraphSeries::SeriesType::Dot); } void GraphView::addDots(std::vector* 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(); }