GraphView.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. #include "pch.h"
  2. #include "GraphView.h"
  3. #include <QDebug>
  4. #include <QPushButton>
  5. #include <QBrush>
  6. #include <random>
  7. #include <algorithm>
  8. #include <chrono>
  9. #include "util.h"
  10. #define X_AXIS_GAP_AMOUNT 5
  11. #define Y_AXIS_GAP_AMOUNT 10
  12. #define X_AXIS_NUMBER_LINE_LENGTH 5
  13. #define Y_AXIS_NUMBER_LINE_LENGTH 5
  14. #define X_AXIS_NUMBER_GAP_LENGTH 10
  15. #define Y_AXIS_NUMBER_GAP_LENGTH 30
  16. #define MARGIN_TOP 10
  17. #define MARGIN_BOTTOM 20
  18. #define MARGIN_LEFT 50
  19. #define MARGIN_RIGHT 10
  20. GraphView::GraphView(QWidget* parent, Bound fixedBound):QWidget(parent), fixedBound(fixedBound), linePen(Qt::blue), rectPen(QColor(255,255,255,255)), axisPen(Qt::black)
  21. {
  22. linePen.setJoinStyle(Qt::PenJoinStyle::RoundJoin);
  23. rectPen.setWidth(0);
  24. ////generate Random number for starting color
  25. //std::random_device rd; //Will be used to obtain a seed for the random number engine
  26. //std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
  27. //std::uniform_real_distribution<double> doubleDistr(0.0, 1.0);
  28. //hueoffset = doubleDistr(gen);
  29. hueoffset = 0.5;
  30. setFocusPolicy(Qt::ClickFocus);
  31. }
  32. GraphView::~GraphView()
  33. {
  34. }
  35. void GraphView::paintEvent(QPaintEvent* event)
  36. {
  37. std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
  38. QPainter painter(this);
  39. painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing);
  40. if (graphSeriesVec.empty() && yDoubleDoubleValuesAmount == 0) {
  41. painter.setPen(axisPen);
  42. painter.setFont(QFont("Arial", 12, QFont::Bold));
  43. painter.drawText(rect(), Qt::AlignCenter, "No Data connected");
  44. return;
  45. }
  46. //Calculate LineRect
  47. QRect graphDisplayRect(rect());
  48. graphDisplayRect.setBottom(graphDisplayRect.bottom() - MARGIN_BOTTOM);
  49. graphDisplayRect.setLeft(graphDisplayRect.left() + MARGIN_LEFT);
  50. graphDisplayRect.setTop(graphDisplayRect.top() + MARGIN_TOP);
  51. graphDisplayRect.setRight(graphDisplayRect.right() - MARGIN_RIGHT);
  52. graphDisplayRect.setWidth(graphDisplayRect.width() - (graphDisplayRect.width() % X_AXIS_GAP_AMOUNT) + 1);
  53. graphDisplayRect.setHeight(graphDisplayRect.height() - (graphDisplayRect.height() % Y_AXIS_GAP_AMOUNT) + 1);
  54. painter.setBrush(rectPen.color());
  55. painter.drawRect(graphDisplayRect);
  56. painter.setBrush(Qt::BrushStyle::NoBrush);
  57. double stregth_factorX = graphDisplayRect.width() / rangeGraphX;
  58. double stregth_factorY = graphDisplayRect.height() / rangeGraphY;
  59. QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
  60. for (const GraphSeries& graphSeries : graphSeriesVec) {
  61. if (!graphSeries.data) {
  62. qDebug() << "Pointer to nothing pls help";
  63. }
  64. if (graphSeries.data->empty() || graphSeries.hide) continue;
  65. linePen.setColor(graphSeries.color);
  66. if (graphSeries.type == GraphSeries::SeriesType::Line || graphSeries.type == GraphSeries::SeriesType::LineDot) {
  67. linePen.setWidth(graphSeries.lineWidth + ((&graphSeries == highlightSeries) ? 5 : 0));
  68. painter.setPen(linePen);
  69. QPainterPath painterPath;
  70. QPointF oldpoint = graphSeries.data->at(0).toQPointF();
  71. painterPath.moveTo(transformGraphToView(oldpoint, stregth_factorX, stregth_factorY, translation));
  72. for (int i = 1; i < graphSeries.data->size(); i++) {
  73. QPointF newpoint = graphSeries.data->at(i).toQPointF();
  74. if (!inBoundX(oldpoint) && !inBoundX(newpoint)) {
  75. }
  76. else if (!inBoundX(oldpoint) && inBoundX(newpoint)) {
  77. painterPath.moveTo(transformGraphToView(oldpoint, stregth_factorX, stregth_factorY, translation));
  78. painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation));
  79. }
  80. else if (inBoundX(oldpoint) && inBoundX(newpoint)) {
  81. painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation));
  82. }
  83. else if (inBoundX(oldpoint) && !inBoundX(newpoint)) {
  84. painterPath.lineTo(transformGraphToView(newpoint, stregth_factorX, stregth_factorY, translation));
  85. break;
  86. }
  87. oldpoint = newpoint;
  88. }
  89. //painterPath.translate(translation);
  90. painter.drawPath(painterPath);
  91. }
  92. if (graphSeries.type == GraphSeries::SeriesType::Dot || graphSeries.type == GraphSeries::SeriesType::LineDot) {
  93. linePen.setWidth(2);
  94. painter.setPen(linePen);
  95. painter.setBrush(graphSeries.color);
  96. for (auto data: *graphSeries.data) {
  97. if (useInterval) {
  98. if (data.orginalPoint->iteration < minIter) {
  99. continue;
  100. }
  101. if (data.orginalPoint->iteration > maxIter) {
  102. break;
  103. }
  104. }
  105. if (graphSeries.useDataPointColor) {
  106. linePen.setColor(data.color);
  107. painter.setPen(linePen);
  108. painter.setBrush(data.color);
  109. }
  110. if (inBound(data.toQPointF())) {
  111. painter.drawEllipse(transformGraphToView(data.toQPointF(), stregth_factorX, stregth_factorY, translation), graphSeries.circleRadius, graphSeries.circleRadius);
  112. }
  113. }
  114. painter.setBrush(Qt::BrushStyle::NoBrush);
  115. }
  116. }
  117. if (yDoubleDoubleValuesAmount > 0) {
  118. linePen.setWidth(2);
  119. linePen.setColor(QColor(0,100,200, 0));
  120. painter.setPen(linePen);
  121. painter.setBrush(QColor(0, 100, 200, 0));
  122. for (int i = 0; i < yDoubleDoubleValuesAmount; i++) {
  123. painter.setBrush(pointColors[i]);
  124. painter.drawEllipse(transformGraphToView(QPointF(yArray[2 * i], yArray[2 * i + 1]), stregth_factorX, stregth_factorY, translation), 3, 3);
  125. }
  126. painter.setBrush(Qt::BrushStyle::NoBrush);
  127. }
  128. if (drawTotalBound) {
  129. painter.setPen(axisPen);
  130. QRectF bounds(transformGraphToView(QPointF(totalBound.minX, totalBound.maxY), stregth_factorX, stregth_factorY, translation), transformGraphToView(QPointF(totalBound.maxX, totalBound.minY), stregth_factorX, stregth_factorY, translation));
  131. painter.drawRect(bounds);
  132. }
  133. //Draw White Rect for axis
  134. painter.setPen(rectPen);
  135. painter.setBrush(rectPen.color());
  136. QRect leftRect(rect().topLeft(), QPoint(graphDisplayRect.left() - 1, rect().bottom()));
  137. QRect topRect(rect().topLeft(), QPoint(rect().right(), graphDisplayRect.top() - 1));
  138. QRect bottomRect(QPoint(rect().left(), graphDisplayRect.bottom()), rect().bottomRight());
  139. QRect rightRect(QPoint(graphDisplayRect.right(), rect().top()), rect().bottomRight());
  140. painter.drawRect(leftRect);
  141. painter.drawRect(topRect);
  142. painter.drawRect(bottomRect);
  143. painter.drawRect(rightRect);
  144. painter.setBrush(Qt::BrushStyle::NoBrush);
  145. //Font for Axis;
  146. painter.setFont(QFont("Arial", 8));
  147. //draw X-Axis
  148. painter.setPen(axisPen);
  149. QRect xAxisRect(QPoint(graphDisplayRect.left(), graphDisplayRect.bottom()), QPoint(graphDisplayRect.right(), rect().bottom()));
  150. QPainterPath xAxisPath;
  151. xAxisPath.moveTo(xAxisRect.left(), xAxisRect.top());
  152. xAxisPath.lineTo(xAxisRect.right(), xAxisRect.top());
  153. int xGap = xAxisRect.width() / X_AXIS_GAP_AMOUNT;
  154. QRect textRect(0, 0, 40, 9);
  155. for (int i = 0; i < 11; i++) {
  156. xAxisPath.moveTo(xAxisRect.left() + i * xGap, xAxisRect.top() + X_AXIS_NUMBER_LINE_LENGTH);
  157. xAxisPath.lineTo(xAxisRect.left() + i * xGap, xAxisRect.top());
  158. textRect.moveCenter(QPoint(xAxisRect.left() + i * xGap, xAxisRect.top() + X_AXIS_NUMBER_GAP_LENGTH));
  159. painter.drawText(textRect, Qt::AlignCenter, xAxisNumbers[i]);
  160. }
  161. painter.drawPath(xAxisPath);
  162. //draw Y-Axis
  163. QRect yAxisRect(QPoint(rect().left(), graphDisplayRect.top()), QPoint(graphDisplayRect.left(), graphDisplayRect.bottom()));
  164. QPainterPath yAxisPath;
  165. yAxisPath.moveTo(yAxisRect.right(), yAxisRect.bottom());
  166. yAxisPath.lineTo(yAxisRect.right(), yAxisRect.top());
  167. int yGap = yAxisRect.height() / Y_AXIS_GAP_AMOUNT;
  168. for (int i = 0; i < 11; i++) {
  169. yAxisPath.moveTo(yAxisRect.right() - Y_AXIS_NUMBER_LINE_LENGTH, yAxisRect.bottom() - i * yGap);
  170. yAxisPath.lineTo(yAxisRect.right(), yAxisRect.bottom() - i * yGap);
  171. textRect.moveCenter(QPoint(yAxisRect.right() - Y_AXIS_NUMBER_GAP_LENGTH, yAxisRect.bottom() - i * yGap));
  172. painter.drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, yAxisNumbers[i]);
  173. }
  174. painter.drawPath(yAxisPath);
  175. std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
  176. std::chrono::milliseconds time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
  177. //Mouse:
  178. if (mousePressed) {
  179. QColor blue(0, 122, 204);
  180. QColor gray(120, 120, 120);
  181. //QPointF value = transformViewToGraph(oldPositionForMouseEvent);
  182. QPointF viewPoint = transformGraphToView(mousePressedValue, stregth_factorX, stregth_factorY, translation);
  183. if (graphDisplayRect.top() <= viewPoint.y() && viewPoint.y() <= graphDisplayRect.bottom()) {
  184. painter.setPen(gray);
  185. painter.drawLine(QPointF(graphDisplayRect.left() - Y_AXIS_NUMBER_LINE_LENGTH, viewPoint.y()), QPointF(graphDisplayRect.right(), viewPoint.y()));
  186. textRect.moveCenter(QPoint(graphDisplayRect.left() - Y_AXIS_NUMBER_GAP_LENGTH, viewPoint.y()));
  187. painter.fillRect(textRect, rectPen.color());
  188. painter.setPen(blue);
  189. painter.drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, QString::number(mousePressedValue.y(), 'f', 1));
  190. }
  191. if (graphDisplayRect.left() <= viewPoint.x() && viewPoint.x() <= graphDisplayRect.right()) {
  192. painter.setPen(gray);
  193. painter.drawLine(QPointF(viewPoint.x(), graphDisplayRect.top()), QPointF(viewPoint.x(), graphDisplayRect.bottom() + X_AXIS_NUMBER_LINE_LENGTH));
  194. textRect.moveCenter(QPoint(viewPoint.x(), graphDisplayRect.bottom() + X_AXIS_NUMBER_GAP_LENGTH));
  195. painter.fillRect(textRect, rectPen.color());
  196. painter.setPen(blue);
  197. painter.drawText(textRect, Qt::AlignCenter, QString::number(mousePressedValue.x(), 'f', 1));
  198. }
  199. }
  200. qDebug() << "PaintTime: " << time.count() << "ms";
  201. /*for (GraphSeries& series : graphSeriesVec) {
  202. qDebug() << "BEGIN ---------";
  203. for (GraphDataPoint point : *series.data) {
  204. qDebug() << "QPoint(" << point.x << "," << point.y << ")";
  205. }
  206. qDebug() << "END ---------";
  207. }*/
  208. }
  209. QPointF GraphView::transformGraphToView(QPointF& point, double stregth_factorX, double stregth_factorY, QPointF& translation) const
  210. {
  211. return QPointF((point.x() - actualBound.minX) * stregth_factorX, (actualBound.maxY - point.y()) * stregth_factorY) + translation;
  212. }
  213. QPointF GraphView::transformViewToGraph(QPointF& point) const
  214. {
  215. QRect graphDisplayRect(rect());
  216. graphDisplayRect.setBottom(graphDisplayRect.bottom() - MARGIN_BOTTOM);
  217. graphDisplayRect.setLeft(graphDisplayRect.left() + MARGIN_LEFT);
  218. graphDisplayRect.setTop(graphDisplayRect.top() + MARGIN_TOP);
  219. graphDisplayRect.setRight(graphDisplayRect.right() - MARGIN_RIGHT);
  220. graphDisplayRect.setWidth(graphDisplayRect.width() - (graphDisplayRect.width() % X_AXIS_GAP_AMOUNT) + 1);
  221. graphDisplayRect.setHeight(graphDisplayRect.height() - (graphDisplayRect.height() % Y_AXIS_GAP_AMOUNT) + 1);
  222. QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
  223. double stregth_factorX = graphDisplayRect.width() / rangeGraphX;
  224. double stregth_factorY = graphDisplayRect.height() / rangeGraphY;
  225. return QPointF(((point.x()-translation.x())/stregth_factorX)+ actualBound.minX, actualBound.maxY - ((point.y()-translation.y())/stregth_factorY));
  226. }
  227. GraphDataPoint GraphView::findNearestGraphDataPointFromGraphCoordinate(QPointF& graphpoint)
  228. {
  229. if (graphSeriesVec.empty() || graphSeriesVec[0].data->empty()) {
  230. return GraphDataPoint();
  231. }
  232. GraphDataPoint min = graphSeriesVec[0].data->at(0);
  233. double minSqaredDistance = min.squaredDistance(graphpoint);
  234. for (GraphSeries& series : graphSeriesVec) {
  235. for (const GraphDataPoint point : *series.data) {
  236. double distance = point.squaredDistance(graphpoint);
  237. if (distance < minSqaredDistance) {
  238. minSqaredDistance = distance;
  239. min = point;
  240. highlightSeries = &series;
  241. }
  242. }
  243. }
  244. return min;
  245. }
  246. //Help Function:
  247. double sqaredDistance(const QPointF& a, const QPointF& b)
  248. {
  249. return std::pow(a.x() - b.x(), 2) + std::pow(a.y() - b.y(), 2);
  250. }
  251. GraphDataPoint GraphView::findNearestGraphDataPointFromViewCordinate(QPointF& viewpoint)
  252. {
  253. if (graphSeriesVec.empty()) {
  254. return GraphDataPoint();
  255. }
  256. QRect graphDisplayRect(rect());
  257. graphDisplayRect.setBottom(graphDisplayRect.bottom() - MARGIN_BOTTOM);
  258. graphDisplayRect.setLeft(graphDisplayRect.left() + MARGIN_LEFT);
  259. graphDisplayRect.setTop(graphDisplayRect.top() + MARGIN_TOP);
  260. graphDisplayRect.setRight(graphDisplayRect.right() - MARGIN_RIGHT);
  261. graphDisplayRect.setWidth(graphDisplayRect.width() - (graphDisplayRect.width() % X_AXIS_GAP_AMOUNT) + 1);
  262. graphDisplayRect.setHeight(graphDisplayRect.height() - (graphDisplayRect.height() % Y_AXIS_GAP_AMOUNT) + 1);
  263. QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
  264. double stregth_factorX = graphDisplayRect.width() / rangeGraphX;
  265. double stregth_factorY = graphDisplayRect.height() / rangeGraphY;
  266. GraphDataPoint min = graphSeriesVec[0].data->at(0);
  267. double minSqaredDistance = sqaredDistance(min.toQPointF(), viewpoint);
  268. std::vector<GraphSeries>::iterator actualBestSeries = graphSeriesVec.begin();
  269. for (auto seriesIter = graphSeriesVec.begin(); seriesIter != graphSeriesVec.end(); seriesIter++) {
  270. for (const GraphDataPoint point : *seriesIter->data) {
  271. double distance = sqaredDistance(transformGraphToView(point.toQPointF(), stregth_factorX, stregth_factorY, translation), viewpoint);
  272. if (distance < minSqaredDistance) {
  273. minSqaredDistance = distance;
  274. min = point;
  275. //highlightSeries = &*seriesIter;
  276. actualBestSeries = seriesIter;
  277. }
  278. }
  279. }
  280. std::rotate(graphSeriesVec.begin(), actualBestSeries + 1, graphSeriesVec.end());
  281. highlightSeries = &graphSeriesVec.back();
  282. return min;
  283. }
  284. bool GraphView::inBoundX(QPointF& point)
  285. {
  286. return point.x() >= actualBound.minX && point.x() <= actualBound.maxX;
  287. }
  288. bool GraphView::inBoundY(QPointF& point)
  289. {
  290. return point.y() >= actualBound.minY && point.y() <= actualBound.maxY;;
  291. }
  292. bool GraphView::inBound(QPointF& point)
  293. {
  294. return inBoundX(point) && inBoundY(point);
  295. }
  296. void GraphView::keyPressEvent(QKeyEvent* event)
  297. {
  298. switch (event->key()) {
  299. case Qt::Key_Up:
  300. fixedBound.move(this, Bound::Change::MoveUp);
  301. break;
  302. case Qt::Key_Right:
  303. fixedBound.move(this, Bound::Change::MoveRight);
  304. break;
  305. case Qt::Key_Left:
  306. fixedBound.move(this, Bound::Change::MoveLeft);
  307. break;
  308. case Qt::Key_Down:
  309. fixedBound.move(this, Bound::Change::MoveDown);
  310. break;
  311. case Qt::Key_PageUp:
  312. case Qt::Key_Plus:
  313. if (event->modifiers() & Qt::ShiftModifier) {
  314. fixedBound.move(this, Bound::Change::ZoomInY);
  315. }
  316. else if (event->modifiers() & Qt::ControlModifier) {
  317. fixedBound.move(this, Bound::Change::ZoomInX);
  318. }
  319. else {
  320. fixedBound.move(this, Bound::Change::ZoomIn);
  321. }
  322. break;
  323. case Qt::Key_PageDown:
  324. case Qt::Key_Minus:
  325. if (event->modifiers() & Qt::ShiftModifier) {
  326. fixedBound.move(this, Bound::Change::ZoomOutY);
  327. }
  328. else if (event->modifiers() & Qt::ControlModifier) {
  329. fixedBound.move(this, Bound::Change::ZoomOutX);
  330. }
  331. else {
  332. fixedBound.move(this, Bound::Change::ZoomOut);
  333. }
  334. break;
  335. case Qt::Key_R:
  336. resetBound();
  337. update();
  338. default:
  339. QWidget::keyPressEvent(event);
  340. }
  341. //If not reacted forward:
  342. QWidget::keyPressEvent(event);
  343. }
  344. void GraphView::resetBound()
  345. {
  346. calculateTotalMatrixBound();
  347. fixedBound = totalBound;
  348. calculateRangeXY();
  349. generateAxisNumberStrings();
  350. }
  351. void GraphView::wheelEvent(QWheelEvent* event)
  352. {
  353. QPoint numPixels = event->pixelDelta();
  354. QPoint numDegrees = event->angleDelta() / 8;
  355. if (!numPixels.isNull()) {
  356. //scrollWithPixels(numPixels);
  357. switch (numPixels.y()) {
  358. case -1:
  359. fixedBound.move(this, Bound::Change::ZoomIn);
  360. break;
  361. case 1:
  362. fixedBound.move(this, Bound::Change::ZoomOut);
  363. default:
  364. break;
  365. }
  366. }
  367. else if (!numDegrees.isNull()) {
  368. QPoint numSteps = numDegrees / 15;
  369. switch (numSteps.y()) {
  370. case 1:
  371. if (event->modifiers() & Qt::ShiftModifier) {
  372. fixedBound.move(this, Bound::Change::ZoomInY);
  373. }
  374. else if (event->modifiers() & Qt::ControlModifier) {
  375. fixedBound.move(this, Bound::Change::ZoomInX);
  376. }
  377. else {
  378. fixedBound.move(this, Bound::Change::ZoomIn);
  379. }
  380. break;
  381. case -1:
  382. if (event->modifiers() & Qt::ShiftModifier) {
  383. fixedBound.move(this, Bound::Change::ZoomOutY);
  384. }
  385. else if (event->modifiers() & Qt::ControlModifier) {
  386. fixedBound.move(this, Bound::Change::ZoomOutX);
  387. }
  388. else {
  389. fixedBound.move(this, Bound::Change::ZoomOut);
  390. }
  391. break;
  392. default:
  393. break;
  394. }
  395. }
  396. event->accept();
  397. }
  398. void GraphView::mouseMoveEvent(QMouseEvent* event)
  399. {
  400. //QToolTip::showText(event->globalPos(), "Hello", this);
  401. QPointF delta = event->pos() - oldPositionForMouseEvent;
  402. oldPositionForMouseEvent = event->pos();
  403. double rangeX = std::abs(actualBound.maxX - actualBound.minX);
  404. int width = (rect().right() - 10) - (rect().left() + 50);
  405. width -= (width % X_AXIS_GAP_AMOUNT) + 1;
  406. double pixelWidthX = rangeX / (double)width ;
  407. double rangeY = std::abs(actualBound.maxY - actualBound.minY);
  408. int height = (rect().bottom() - 20) - (rect().top() + 20);
  409. height -= (height % Y_AXIS_GAP_AMOUNT) + 1;
  410. double pixelWidthY = rangeY / (double)height;
  411. fixedBound.move(this, delta, pixelWidthX, pixelWidthY);
  412. }
  413. void GraphView::mousePressEvent(QMouseEvent* event)
  414. {
  415. mousePressed = true;
  416. //To calculate the delta for mouse move event
  417. oldPositionForMouseEvent = event->pos();
  418. mousePressedValue = transformViewToGraph(oldPositionForMouseEvent);
  419. GraphDataPoint nearestPoint = findNearestGraphDataPointFromViewCordinate(oldPositionForMouseEvent);
  420. QString metaFileName;
  421. if (highlightSeries && highlightSeries->run)
  422. {
  423. metaFileName = " " + QString::fromStdString(highlightSeries->run->name);
  424. }
  425. QString string =metaFileName;
  426. QToolTip::showText(event->globalPos() - QPoint(0, 35), string);
  427. }
  428. void GraphView::mouseReleaseEvent(QMouseEvent* event)
  429. {
  430. highlightSeries = nullptr;
  431. mousePressed = false;
  432. update();
  433. }
  434. void GraphView::update()
  435. {
  436. calculateRangeXY();
  437. generateAxisNumberStrings();
  438. QWidget::update();
  439. }
  440. void GraphView::setDrawBound(bool value)
  441. {
  442. drawTotalBound = value;
  443. }
  444. /**
  445. * Delete all series.
  446. *
  447. */
  448. void GraphView::reset()
  449. {
  450. graphSeriesVec.clear();
  451. }
  452. void GraphView::calculateTotalMatrixBound()
  453. {
  454. if (yDoubleDoubleValuesAmount > 0) {
  455. /*double maxX, maxY;
  456. double minX = maxX = yArray[0], minY = maxY = yArray[1];
  457. for (int i = 1; i < yDoubleDoubleValuesAmount; i++) {
  458. if (yArray[2 * i] < minX) {
  459. minX = yArray[2 * i];
  460. }else if (yArray[2 * i] > maxX) {
  461. maxX = yArray[2 * i];
  462. }
  463. if (yArray[2 * i + 1] < minY) {
  464. minY = yArray[2 * i + 1];
  465. }
  466. else if (yArray[2 * i + 1] > maxY) {
  467. maxY = yArray[2 * i + 1];
  468. }
  469. }
  470. totalBound.minX = minX;
  471. totalBound.minY = minY;
  472. totalBound.maxX = maxX;
  473. totalBound.maxY = maxY;*/
  474. // Calculate AveragePoint of Matrix
  475. }
  476. }
  477. void GraphView::calculateTotalGraphBound()
  478. {
  479. totalBound.minX = std::min_element(std::begin(graphSeriesVec), std::end(graphSeriesVec), [](const GraphSeries& a, const GraphSeries& b) -> bool {return a.minX < b.minX; })->minX;
  480. totalBound.maxX = std::max_element(std::begin(graphSeriesVec), std::end(graphSeriesVec), [](const GraphSeries& a, const GraphSeries& b) -> bool {return a.maxX < b.maxX; })->maxX;
  481. totalBound.minY = std::min_element(std::begin(graphSeriesVec), std::end(graphSeriesVec), [](const GraphSeries& a, const GraphSeries& b) -> bool {return a.minY < b.minY; })->minY;
  482. totalBound.maxY = std::max_element(std::begin(graphSeriesVec), std::end(graphSeriesVec), [](const GraphSeries& a, const GraphSeries& b) -> bool {return a.maxY < b.maxY; })->maxY;
  483. }
  484. void GraphView::calculateRangeXY()
  485. {
  486. actualBound = fixedBound;
  487. rangeGraphX = std::abs(actualBound.maxX - actualBound.minX);
  488. rangeGraphY = std::abs(actualBound.maxY - actualBound.minY);
  489. if (std::abs(rangeGraphX) < 0.0001) {
  490. actualBound.minX -= 1.0;
  491. actualBound.maxX += 1.0;
  492. rangeGraphX = 2;
  493. }
  494. if (std::abs(rangeGraphY) < 0.0001) {
  495. actualBound.minY -= 1.0;
  496. actualBound.maxY += 1.0;
  497. rangeGraphY = 2;
  498. }
  499. }
  500. //TODO: graphview
  501. void GraphView::calculateMinMaxXY(GraphSeries& lgs)
  502. {
  503. auto pairX = std::minmax_element(lgs.data->begin(), lgs.data->end(), [](const GraphDataPoint& a, const GraphDataPoint& b) -> bool {return a.x < b.x; });
  504. lgs.minX = pairX.first->x;
  505. lgs.maxX = pairX.second->x;
  506. auto pairY = std::minmax_element(lgs.data->begin(), lgs.data->end(), [](const GraphDataPoint& a, const GraphDataPoint& b) -> bool {return a.y < b.y; });
  507. lgs.minY = pairY.first->y;
  508. lgs.maxY = pairY.second->y;
  509. }
  510. void GraphView::addSeries(std::vector<GraphDataPoint>* line, RunData* run, QColor color, GraphSeries::SeriesType type)
  511. {
  512. if (line->empty()) {
  513. qDebug() << "Series is empty!";
  514. return;
  515. }
  516. GraphSeries lgs;
  517. lgs.run = run;
  518. lgs.data = line;
  519. lgs.type = type;
  520. calculateMinMaxXY(lgs);
  521. lgs.color = color;
  522. graphSeriesVec.push_back(lgs);
  523. calculateTotalGraphBound();
  524. if (!useFixedBound) calculateRangeXY();
  525. generateAxisNumberStrings();
  526. }
  527. QColor GraphView::generateNextColorForGraph()
  528. {
  529. /* http://devmag.org.za/2012/07/29/how-to-choose-colours-procedurally-algorithms/
  530. use golden ratio 0.618033988749895f
  531. */
  532. hueoffset = std::fmod(hueoffset + 0.618033988749895f, 1.0);
  533. return QColor::fromHsvF(hueoffset, 0.83, 1.0);
  534. }
  535. QColor interpolate(QColor& first, QColor& second, double alpha)
  536. {
  537. double firstH, firstS, firstL;
  538. double secondH, secondS, secondL;
  539. first.getHslF(&firstH, &firstS, &firstL);
  540. second.getHslF(&secondH, &secondS, &secondL);
  541. const double h = util::linearInterpolate(firstH, secondH, alpha);
  542. const double s = util::linearInterpolate(firstS, secondS, alpha);
  543. const double l = util::linearInterpolate(firstL, secondL, alpha);
  544. return QColor::fromHslF(h, s, l);
  545. }
  546. void GraphView::addYMatrix(double* yArray, std::vector<SolutionPointData>& solVec)
  547. {
  548. this->yArray = yArray; yDoubleDoubleValuesAmount = solVec.size();
  549. this->solVec = &solVec;
  550. if (pointColors != nullptr) delete pointColors;
  551. pointColors = new QColor[yDoubleDoubleValuesAmount];
  552. }
  553. void GraphView::autoZoomOut()
  554. {
  555. if (yDoubleDoubleValuesAmount == 0) return;
  556. QPointF sumDistance(0, 0);
  557. for (int i = 0; i < yDoubleDoubleValuesAmount; i++) {
  558. sumDistance += QPointF(std::abs(yArray[2 * i]), std::abs(yArray[2 * i + 1]));
  559. }
  560. QPointF avgDistance(sumDistance.x() / yDoubleDoubleValuesAmount,
  561. sumDistance.y() / yDoubleDoubleValuesAmount);
  562. double scalar = 3;
  563. double addScalar = 1;
  564. if (fixedBound.maxX < avgDistance.x() * scalar) {
  565. fixedBound.minX = -avgDistance.x() * scalar * addScalar;
  566. fixedBound.maxX = avgDistance.x() * scalar * addScalar;
  567. fixedBound.minY = -avgDistance.y() * scalar * addScalar;
  568. fixedBound.maxY = avgDistance.y() * scalar * addScalar;
  569. }
  570. }
  571. void GraphView::updateGraphColors(ColorGradient& gradient)
  572. {
  573. if (yDoubleDoubleValuesAmount == 0) return;
  574. double bestValue = 0;
  575. double worstValue = 100;
  576. for (int i = 0; i < solVec->size(); i++) {
  577. pointColors[i] = gradient.getColorFromAlpha( std::clamp(util::inverseLinearInterpolation(bestValue, worstValue, solVec->at(i).objectiveFunction), 0.0, 1.0));
  578. }
  579. }
  580. void GraphView::generateAxisNumberStrings()
  581. {
  582. for (int i = 0; i < 11; i++) {
  583. xAxisNumbers[i] = QString::number(actualBound.minX + i * (rangeGraphX / (double)X_AXIS_GAP_AMOUNT), 'f', 1);
  584. yAxisNumbers[i] = QString::number(actualBound.minY + i * (rangeGraphY / (double)Y_AXIS_GAP_AMOUNT), 'f', 1);
  585. }
  586. }
  587. void GraphView::setUseFixedBound(bool value)
  588. {
  589. //if value == other update else do nothing
  590. if (useFixedBound != value) {
  591. useFixedBound = value;
  592. calculateRangeXY();
  593. actualBound = fixedBound;
  594. generateAxisNumberStrings();
  595. }
  596. }
  597. void GraphView::generateAndAddRandomLine()
  598. {
  599. std::random_device rd; //Will be used to obtain a seed for the random number engine
  600. std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
  601. std::uniform_real_distribution<double> realDistr(-30, 30);
  602. std::uniform_int_distribution<int> intScaleDistr(1, 3);
  603. for (int randomSeries = 0; randomSeries < 1; randomSeries++) {
  604. std::vector<GraphDataPoint> randomPointVec(101);
  605. int scale = intScaleDistr(gen);
  606. for (int i = 0; i < randomPointVec.size(); i++) {
  607. randomPointVec[i] = GraphDataPoint(i + 500, i * scale + realDistr(gen));
  608. }
  609. addLine(&randomPointVec, nullptr);
  610. }
  611. }
  612. void GraphView::addLine(std::vector<GraphDataPoint>* line, RunData* run)
  613. {
  614. addSeries(line, run,generateNextColorForGraph(), GraphSeries::SeriesType::Line);
  615. }
  616. void GraphView::addLine(std::vector<GraphDataPoint>* line, RunData* run, QColor color)
  617. {
  618. addSeries(line, run, color, GraphSeries::SeriesType::Line);
  619. }
  620. void GraphView::addDots(std::vector<GraphDataPoint>* dots, RunData* run)
  621. {
  622. addSeries(dots, run, generateNextColorForGraph(), GraphSeries::SeriesType::Dot);
  623. }
  624. void GraphView::addDots(std::vector<GraphDataPoint>* dots, RunData* run, QColor color)
  625. {
  626. addSeries(dots, run, color, GraphSeries::SeriesType::Dot);
  627. }
  628. void GraphView::removeAllSeriesWithRundata(RunData* run)
  629. {
  630. //TODO not implemented jet
  631. //std::for_each(graphSeriesVec);
  632. }
  633. void GraphView::removeAllSeries()
  634. {
  635. }
  636. void GraphView::setMinIter(int value)
  637. {
  638. minIter = value;
  639. update();
  640. }
  641. void GraphView::setMaxIter(int value)
  642. {
  643. maxIter = value;
  644. update();
  645. }
  646. void Bound::move(GraphView* widget,const Change& type, const double changePercentage)
  647. {
  648. double changeX = std::abs(minX - maxX) * changePercentage;
  649. double changeY = std::abs(minY - maxY) * changePercentage;
  650. switch (type) {
  651. case Change::MoveLeft:
  652. minX -= changeX;
  653. maxX -= changeX;
  654. break;
  655. case Change::MoveRight:
  656. minX += changeX;
  657. maxX += changeX;
  658. break;
  659. case Change::MoveUp:
  660. minY += changeY;
  661. maxY += changeY;
  662. break;
  663. case Change::MoveDown:
  664. minY -= changeY;
  665. maxY -= changeY;
  666. break;
  667. case Change::ZoomOut:
  668. minY -= changeY;
  669. maxY += changeY;
  670. minX -= changeX;
  671. maxX += changeX;
  672. break;
  673. case Change::ZoomIn:
  674. minY += changeY;
  675. maxY -= changeY;
  676. minX += changeX;
  677. maxX -= changeX;
  678. break;
  679. case Change::ZoomInX:
  680. minX += changeX;
  681. maxX -= changeX;
  682. break;
  683. case Change::ZoomInY:
  684. minY += changeY;
  685. maxY -= changeY;
  686. break;
  687. case Change::ZoomOutX:
  688. minX -= changeX;
  689. maxX += changeX;
  690. break;
  691. case Change::ZoomOutY:
  692. minY -= changeY;
  693. maxY += changeY;
  694. break;
  695. default:
  696. break;
  697. }
  698. //TODO: Move out of bound in calculate widget
  699. widget->update();
  700. }
  701. void Bound::move(GraphView* widget, QPointF& delta, double realPixelWidthX, double realPixelWidthY)
  702. {
  703. minX += -delta.x() * realPixelWidthX;
  704. maxX += -delta.x() * realPixelWidthX;
  705. minY += delta.y() * realPixelWidthY;
  706. maxY += delta.y() * realPixelWidthY;
  707. widget->update();
  708. }
  709. void Bound::move(GraphView* widget, QPointF& targetPoint)
  710. {
  711. //Calculate MiddlePoint
  712. QPointF actualMiddlePoint((maxX - minX) / 2.0, (maxY - minY) / 2.0);
  713. QPointF dif = targetPoint - actualMiddlePoint;
  714. minX += dif.x();
  715. maxX += dif.x();
  716. minY += dif.y();
  717. maxY += dif.y();
  718. }