|
@@ -5,32 +5,25 @@
|
|
|
#include <random>
|
|
|
#include <algorithm>
|
|
|
|
|
|
-CustomLineGraph::CustomLineGraph(QWidget* parent):QWidget(parent), linePen(Qt::blue), rectPen(Qt::red), axisPen(Qt::black)
|
|
|
+#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
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+CustomLineGraph::CustomLineGraph(QWidget* parent, QString title, int lineWidth, double circleRadius):QWidget(parent), title(title), lineWidth(lineWidth), circleRadius(circleRadius), linePen(Qt::blue), rectPen(Qt::red), axisPen(Qt::black)
|
|
|
{
|
|
|
- linePen.setWidth(3);
|
|
|
+ linePen.setWidth(lineWidth);
|
|
|
linePen.setJoinStyle(Qt::PenJoinStyle::RoundJoin);
|
|
|
- LineGraphSeries lgs;
|
|
|
+
|
|
|
|
|
|
|
|
|
std::random_device rd;
|
|
|
std::mt19937 gen(rd());
|
|
|
- std::uniform_int_distribution<> dis(-3, 3);
|
|
|
- for (int randomSeries = 0; randomSeries < 2; randomSeries++) {
|
|
|
- for (int i = 0; i < 21; i++) {
|
|
|
- QPoint point(i * 50 + 500,i * 1 + dis(gen) - 10000);
|
|
|
- lgs.data.push_back(point);
|
|
|
- }
|
|
|
-
|
|
|
- auto pairX = std::minmax_element(std::begin(lgs.data), std::end(lgs.data), [](const QPoint& a,const QPoint& b) -> bool {return a.x() < b.x(); });
|
|
|
- lgs.minX = pairX.first->x();
|
|
|
- lgs.maxX = pairX.second->x();
|
|
|
- auto pairY = std::minmax_element(std::begin(lgs.data), std::end(lgs.data), [](const QPoint& a, const QPoint& b) -> bool {return a.y() < b.y(); });
|
|
|
- lgs.minY = pairY.first->y();
|
|
|
- lgs.maxY = pairY.second->y();
|
|
|
- lgs.rangeX = std::abs(lgs.maxX - lgs.minX);
|
|
|
- lgs.rangeY = std::abs(lgs.maxY - lgs.minY);
|
|
|
- seriesVec.push_back(lgs);
|
|
|
- }
|
|
|
+ std::uniform_real_distribution<double> doubleDistr(0.0, 1.0);
|
|
|
+ hueoffset = doubleDistr(gen);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
CustomLineGraph::~CustomLineGraph()
|
|
@@ -40,45 +33,56 @@ void CustomLineGraph::paintEvent(QPaintEvent* event)
|
|
|
{
|
|
|
QPainter painter(this);
|
|
|
painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing);
|
|
|
-
|
|
|
+ if (seriesVec.empty()) {
|
|
|
+ painter.setPen(axisPen);
|
|
|
+ painter.setFont(QFont("Arial", 12, QFont::Bold));
|
|
|
+ painter.drawText(rect(), Qt::AlignCenter, "No Data connected");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
QRect lineRect(rect());
|
|
|
lineRect.setBottom(lineRect.bottom() - 20);
|
|
|
lineRect.setLeft(lineRect.left() + 40);
|
|
|
lineRect.setTop(lineRect.top() + 20);
|
|
|
lineRect.setRight(lineRect.right() - 10);
|
|
|
- lineRect.setWidth(lineRect.width() - (lineRect.width() % 10) + 1);
|
|
|
- lineRect.setHeight(lineRect.height() - (lineRect.height() % 10) + 1);
|
|
|
+ lineRect.setWidth(lineRect.width() - (lineRect.width() % X_AXIS_GAP_AMOUNT) + 1);
|
|
|
+ lineRect.setHeight(lineRect.height() - (lineRect.height() % Y_AXIS_GAP_AMOUNT) + 1);
|
|
|
|
|
|
|
|
|
|
|
|
painter.setPen(axisPen);
|
|
|
painter.setFont(QFont("Arial", 12));
|
|
|
- QRect titleRect(QPoint(lineRect.left(), rect().top()), QPoint(lineRect.right(), lineRect.top()));
|
|
|
- painter.drawText(titleRect, Qt::AlignCenter, "title");
|
|
|
-
|
|
|
+ QRect titleRect(QPoint(rect().left(), rect().top()), QPoint(lineRect.right(), lineRect.top()));
|
|
|
+ painter.drawText(titleRect, Qt::AlignCenter, title);
|
|
|
|
|
|
+ painter.setFont(QFont("Arial", 8));
|
|
|
|
|
|
QRect xAxisRect(QPoint(lineRect.left(), lineRect.bottom()), QPoint(lineRect.right(), rect().bottom()));
|
|
|
QPainterPath xAxisPath;
|
|
|
xAxisPath.moveTo(xAxisRect.left(), xAxisRect.top());
|
|
|
xAxisPath.lineTo(xAxisRect.right(), xAxisRect.top());
|
|
|
- int xGap = xAxisRect.width() / 10;
|
|
|
+ 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() + 5);
|
|
|
+ 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() + 10));
|
|
|
+ painter.drawText(textRect, Qt::AlignCenter, xAxisNumbers[i]);
|
|
|
}
|
|
|
painter.drawPath(xAxisPath);
|
|
|
|
|
|
|
|
|
QRect yAxisRect(QPoint(rect().left(), lineRect.top()), QPoint(lineRect.left(), lineRect.bottom()));
|
|
|
QPainterPath yAxisPath;
|
|
|
- yAxisPath.moveTo(yAxisRect.right(), yAxisRect.bottom() + 5);
|
|
|
+ yAxisPath.moveTo(yAxisRect.right(), yAxisRect.bottom());
|
|
|
yAxisPath.lineTo(yAxisRect.right(), yAxisRect.top());
|
|
|
- int yGap = yAxisRect.height() / 10;
|
|
|
+ int yGap = yAxisRect.height() / Y_AXIS_GAP_AMOUNT;
|
|
|
for (int i = 0; i < 11; i++) {
|
|
|
- yAxisPath.moveTo(yAxisRect.right() - 5, yAxisRect.top() + i * yGap);
|
|
|
- yAxisPath.lineTo(yAxisRect.right(), yAxisRect.top() + i * yGap);
|
|
|
+ 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_LINE_LENGTH - 20, yAxisRect.bottom() - i * yGap));
|
|
|
+ painter.drawText(textRect, Qt::AlignCenter, yAxisNumbers[i]);
|
|
|
}
|
|
|
painter.drawPath(yAxisPath);
|
|
|
|
|
@@ -88,28 +92,141 @@ void CustomLineGraph::paintEvent(QPaintEvent* event)
|
|
|
|
|
|
|
|
|
painter.setPen(linePen);
|
|
|
- for (LineGraphSeries series : seriesVec) {
|
|
|
-
|
|
|
- double stregth_factorX = lineRect.width() / series.rangeX;
|
|
|
- double stregth_factorY = lineRect.height() / series.rangeY;
|
|
|
+ double stregth_factorX = lineRect.width() / rangeGraphX;
|
|
|
+ double stregth_factorY = lineRect.height() / rangeGraphY;
|
|
|
+ for (LineGraphSeries lgs : seriesVec) {
|
|
|
+ linePen.setColor(lgs.color);
|
|
|
+ painter.setPen(linePen);
|
|
|
QPointF translation(lineRect.left(), lineRect.top());
|
|
|
- QPainterPath painterPath;
|
|
|
- painterPath.moveTo(transformPoint(series.data[0], series, stregth_factorX, stregth_factorY));
|
|
|
- for(int i = 1; i < seriesVec[0].data.size(); i++) {
|
|
|
- painterPath.lineTo(transformPoint(series.data[i], series, stregth_factorX, stregth_factorY));
|
|
|
+ if (!lgs.type == LineGraphSeries::Dot) {
|
|
|
+ QPainterPath painterPath;
|
|
|
+ painterPath.moveTo(transformPoint(lgs.data[0], stregth_factorX, stregth_factorY));
|
|
|
+ for(int i = 1; i < lgs.data.size(); i++) {
|
|
|
+ painterPath.lineTo(transformPoint(lgs.data[i], stregth_factorX, stregth_factorY));
|
|
|
+ }
|
|
|
+ painterPath.translate(translation);
|
|
|
+ painter.drawPath(painterPath);
|
|
|
}
|
|
|
- painterPath.translate(translation);
|
|
|
- painter.drawPath(painterPath);
|
|
|
- int radius;
|
|
|
- for (int i = 0; i < seriesVec[0].data.size(); i++) {
|
|
|
- painter.drawEllipse(transformPoint(series.data[i], series, stregth_factorX, stregth_factorY) + translation, 3, 3);
|
|
|
+
|
|
|
+ if (!(lineWidth > 4 * circleRadius || circleRadius == 0)) {
|
|
|
+ painter.setBrush(lgs.color);
|
|
|
+ for (int i = 0; i < lgs.data.size(); i++) {
|
|
|
+ painter.drawEllipse(transformPoint(lgs.data[i], stregth_factorX, stregth_factorY) + translation, circleRadius, circleRadius);
|
|
|
+ }
|
|
|
+ painter.setBrush(Qt::BrushStyle::NoBrush);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
-QPoint CustomLineGraph::transformPoint(QPoint& point, LineGraphSeries& lgs, double stregth_factorX, double stregth_factorY)
|
|
|
+QPointF CustomLineGraph::transformPoint(QPointF& point, double stregth_factorX, double stregth_factorY)
|
|
|
+{
|
|
|
+ return QPointF((point.x() - minGraphX)* stregth_factorX , (maxGraphY - point.y()) * stregth_factorY);
|
|
|
+}
|
|
|
+
|
|
|
+void CustomLineGraph::calculateMinMaxGraphXY()
|
|
|
+{
|
|
|
+ minGraphX = std::min_element(std::begin(seriesVec), std::end(seriesVec), [](const LineGraphSeries& a, const LineGraphSeries& b) -> bool {return a.minX < b.minX; })->minX;
|
|
|
+ maxGraphX = std::max_element(std::begin(seriesVec), std::end(seriesVec), [](const LineGraphSeries& a, const LineGraphSeries& b) -> bool {return a.maxX < b.maxX; })->maxX;
|
|
|
+ minGraphY = std::min_element(std::begin(seriesVec), std::end(seriesVec), [](const LineGraphSeries& a, const LineGraphSeries& b) -> bool {return a.minY < b.minY; })->minY;
|
|
|
+ maxGraphY = std::max_element(std::begin(seriesVec), std::end(seriesVec), [](const LineGraphSeries& a, const LineGraphSeries& b) -> bool {return a.maxY < b.maxY; })->maxY;
|
|
|
+ rangeGraphX = std::abs(maxGraphX - minGraphX);
|
|
|
+ rangeGraphY = std::abs(maxGraphY - minGraphY);
|
|
|
+ if (std::abs(rangeGraphX) < 0.01) {
|
|
|
+ minGraphX -= 1.0;
|
|
|
+ maxGraphX += 1.0;
|
|
|
+ rangeGraphX = 2;
|
|
|
+ }
|
|
|
+ if (std::abs(rangeGraphY) < 0.01) {
|
|
|
+ minGraphY -= 1.0;
|
|
|
+ maxGraphY += 1.0;
|
|
|
+ rangeGraphY = 2;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CustomLineGraph::popullateLineGraphSeries(std::vector<QPointF>& line, LineGraphSeries& lgs)
|
|
|
+{
|
|
|
+ lgs.data = line;
|
|
|
+ auto pairX = std::minmax_element(std::begin(lgs.data), std::end(lgs.data), [](const QPointF& a, const QPointF& b) -> bool {return a.x() < b.x(); });
|
|
|
+ lgs.minX = pairX.first->x();
|
|
|
+ lgs.maxX = pairX.second->x();
|
|
|
+ auto pairY = std::minmax_element(std::begin(lgs.data), std::end(lgs.data), [](const QPointF& a, const QPointF& b) -> bool {return a.y() < b.y(); });
|
|
|
+ lgs.minY = pairY.first->y();
|
|
|
+ lgs.maxY = pairY.second->y();
|
|
|
+}
|
|
|
+
|
|
|
+void CustomLineGraph::addSeries(std::vector<QPointF>& line, QColor color, LineGraphSeries::SeriesType type)
|
|
|
+{
|
|
|
+ if (line.empty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ LineGraphSeries lgs;
|
|
|
+ lgs.type = type;
|
|
|
+ popullateLineGraphSeries(line, lgs);
|
|
|
+ lgs.color = color;
|
|
|
+ seriesVec.push_back(lgs);
|
|
|
+ calculateMinMaxGraphXY();
|
|
|
+ generateAxisNumberStrings();
|
|
|
+}
|
|
|
+
|
|
|
+QColor CustomLineGraph::generateNextColorForGraph()
|
|
|
+{
|
|
|
+
|
|
|
+ use golden ratio 0.618033988749895f
|
|
|
+ */
|
|
|
+ hueoffset = std::fmod(hueoffset + 0.618033988749895f, 1.0);
|
|
|
+ return QColor::fromHsvF(hueoffset, 0.83, 1.0);
|
|
|
+}
|
|
|
+
|
|
|
+void CustomLineGraph::generateAxisNumberStrings()
|
|
|
+{
|
|
|
+ for (int i = 0; i < 11; i++) {
|
|
|
+ xAxisNumbers[i] = QString::number(minGraphX + i * (rangeGraphX / (double)X_AXIS_GAP_AMOUNT), 'f', 1);
|
|
|
+ yAxisNumbers[i] = QString::number(minGraphY + i * (rangeGraphY / (double)Y_AXIS_GAP_AMOUNT), 'f', 1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void CustomLineGraph::generateAndAddRandomLine()
|
|
|
{
|
|
|
- return QPoint((point.x() - lgs.minX)* stregth_factorX , (lgs.maxY - point.y()) * stregth_factorY);
|
|
|
+ std::random_device rd;
|
|
|
+ std::mt19937 gen(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<QPointF> randomPointVec(101);
|
|
|
+ int scale = intScaleDistr(gen);
|
|
|
+ for (int i = 0; i < randomPointVec.size(); i++) {
|
|
|
+ randomPointVec[i] = QPointF(i + 500, i * scale + realDistr(gen));
|
|
|
+ }
|
|
|
+ addLine(randomPointVec);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+void CustomLineGraph::addLine(std::vector<QPointF>& line)
|
|
|
+{
|
|
|
+ addLine(line, generateNextColorForGraph());
|
|
|
+}
|
|
|
+
|
|
|
+void CustomLineGraph::addLine(std::vector<QPointF>& line, QColor color)
|
|
|
+{
|
|
|
+ addSeries(line, color, LineGraphSeries::Line);
|
|
|
+}
|
|
|
+
|
|
|
+void CustomLineGraph::addDots(std::vector<QPointF>& dots)
|
|
|
+{
|
|
|
+ addSeries(dots, generateNextColorForGraph(), LineGraphSeries::Dot);
|
|
|
+}
|
|
|
+
|
|
|
+void CustomLineGraph::addDots(std::vector<QPointF>& dots, QColor color)
|
|
|
+{
|
|
|
+ addSeries(dots, color, LineGraphSeries::Dot);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|