Plott.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. #include "pch.h"
  2. #include "Plott.h"
  3. #include "util.h"
  4. #include <chrono>
  5. #define X_AXIS_GAP_AMOUNT 5
  6. #define Y_AXIS_GAP_AMOUNT 10
  7. #define X_AXIS_NUMBER_LINE_LENGTH 5
  8. #define Y_AXIS_NUMBER_LINE_LENGTH 5
  9. #define X_AXIS_NUMBER_GAP_LENGTH 10
  10. #define Y_AXIS_NUMBER_GAP_LENGTH 30
  11. #define X_AXIS_LEGEND_TOP_BEGIN 12
  12. #define Y_AXIS_LEGEND_LEFT_BEGIN 12
  13. #define MARGIN_TOP 20
  14. #define MARGIN_BOTTOM 25
  15. #define MARGIN_LEFT 50
  16. #define MARGIN_RIGHT 10
  17. Plott::Plott(QWidget *parent)
  18. : QWidget(parent)
  19. {
  20. setFocusPolicy(Qt::ClickFocus);
  21. }
  22. Plott::~Plott()
  23. {
  24. }
  25. void Plott::setVisibleWindow(double xMin, double xMax, double yMin, double yMax)
  26. {
  27. window.xMin = xMin;
  28. window.xMax = xMax;
  29. window.yMin = yMin;
  30. window.yMax = yMax;
  31. }
  32. void Plott::setDefaultVisibleWindow(double xMin, double xMax, double yMin, double yMax)
  33. {
  34. defaultWindow.xMin = xMin;
  35. defaultWindow.xMax = xMax;
  36. defaultWindow.yMin = yMin;
  37. defaultWindow.yMax = yMax;
  38. }
  39. void Plott::resetToDefaultWindow()
  40. {
  41. window.xMin = defaultWindow.xMin;
  42. window.xMax = defaultWindow.xMax;
  43. window.yMin = defaultWindow.yMin;
  44. window.yMax = defaultWindow.yMax;
  45. }
  46. void Plott::setAxisLegend(QString xAxisLegend, QString yAxisLegend)
  47. {
  48. this->xAxisLegend = xAxisLegend;
  49. this->yAxisLegend = yAxisLegend;
  50. }
  51. QPointF Plott::transformGraphToView(QPointF graphpoint) const
  52. {
  53. QRect graphDisplayRect = getDisplayRect();
  54. QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
  55. double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin);
  56. double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin);
  57. return QPointF((graphpoint.x() - window.xMin) * stregth_factorX, (window.yMax - graphpoint.y()) * stregth_factorY) + translation;
  58. }
  59. QPointF Plott::transformGraphToView(QPointF graphpoint, double stregth_factorX, double stregth_factorY, QPointF translation) const
  60. {
  61. return QPointF((graphpoint.x() - window.xMin) * stregth_factorX, (window.yMax - graphpoint.y()) * stregth_factorY) + translation;
  62. }
  63. QPointF Plott::transformViewToGraph(QPointF viewpoint) const
  64. {
  65. QRect graphDisplayRect = getDisplayRect();
  66. QPointF translation(graphDisplayRect.left(), graphDisplayRect.top());
  67. double stregth_factorX = graphDisplayRect.width() / std::abs(window.xMax - window.xMin);
  68. double stregth_factorY = graphDisplayRect.height() / std::abs(window.yMax - window.yMin);
  69. return QPointF(((viewpoint.x() - translation.x()) / stregth_factorX) + window.xMin, window.yMax - ((viewpoint.y() - translation.y()) / stregth_factorY));
  70. }
  71. QPointF Plott::transformViewToGraph(QPointF viewpoint, double stregth_factorX, double stregth_factorY, QPointF translation) const
  72. {
  73. return QPointF(((viewpoint.x() - translation.x()) / stregth_factorX) + window.xMin, window.yMax - ((viewpoint.y() - translation.y()) / stregth_factorY));
  74. }
  75. void Plott::drawData(QPainter& painter)
  76. {
  77. }
  78. QRect Plott::getDisplayRect() const
  79. {
  80. QRect graphDisplayRect(rect());
  81. graphDisplayRect.setBottom(graphDisplayRect.bottom() - MARGIN_BOTTOM);
  82. graphDisplayRect.setLeft(graphDisplayRect.left() + MARGIN_LEFT);
  83. graphDisplayRect.setTop(graphDisplayRect.top() + MARGIN_TOP);
  84. graphDisplayRect.setRight(graphDisplayRect.right() - MARGIN_RIGHT);
  85. graphDisplayRect.setWidth(graphDisplayRect.width() - (graphDisplayRect.width() % X_AXIS_GAP_AMOUNT) + 1);
  86. graphDisplayRect.setHeight(graphDisplayRect.height() - (graphDisplayRect.height() % Y_AXIS_GAP_AMOUNT) + 1);
  87. return graphDisplayRect;
  88. }
  89. // Draws the axis
  90. void Plott::paintEvent(QPaintEvent* event)
  91. {
  92. std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
  93. QPainter painter(this);
  94. painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing);
  95. QRect graphDisplayRect = getDisplayRect();
  96. int xGap = graphDisplayRect.width() / X_AXIS_GAP_AMOUNT;
  97. int yGap = graphDisplayRect.height() / Y_AXIS_GAP_AMOUNT;
  98. if(enableGrid){
  99. QPainterPath gridPath;
  100. for (int i = 1; i < X_AXIS_GAP_AMOUNT; i++) {
  101. gridPath.moveTo(graphDisplayRect.left() + i * xGap, graphDisplayRect.top());
  102. gridPath.lineTo(graphDisplayRect.left() + i * xGap, graphDisplayRect.bottom());
  103. }
  104. for (int i = 0; i < Y_AXIS_GAP_AMOUNT + 1; i++) {
  105. gridPath.moveTo(graphDisplayRect.left(), graphDisplayRect.bottom() - i * yGap);
  106. gridPath.lineTo(graphDisplayRect.right(), graphDisplayRect.bottom() - i * yGap);
  107. }
  108. QPen graypen(QColor(150, 150, 150));
  109. painter.setPen(graypen);
  110. painter.drawPath(gridPath);
  111. }
  112. drawData(painter);
  113. //Draw White Rect for axis
  114. QPen blackpen(QColor(0, 0, 0));
  115. blackpen.setWidth(1);
  116. QPen whitePen(QColor(255, 255, 255));
  117. whitePen.setWidth(1);
  118. painter.setPen(whitePen);
  119. painter.setBrush(whitePen.color());
  120. QRect leftRect(rect().topLeft(), QPoint(graphDisplayRect.left() - 1, rect().bottom()));
  121. QRect topRect(rect().topLeft(), QPoint(rect().right(), graphDisplayRect.top() - 1));
  122. QRect bottomRect(QPoint(rect().left(), graphDisplayRect.bottom()), rect().bottomRight());
  123. QRect rightRect(QPoint(graphDisplayRect.right(), rect().top()), rect().bottomRight());
  124. painter.drawRect(leftRect);
  125. painter.drawRect(topRect);
  126. painter.drawRect(bottomRect);
  127. painter.drawRect(rightRect);
  128. painter.setBrush(Qt::BrushStyle::NoBrush);
  129. //Font for Axis;
  130. painter.setFont(QFont("Arial", 8));
  131. //draw X-Axis
  132. painter.setPen(blackpen);
  133. QRect xAxisRect(QPoint(graphDisplayRect.left(), graphDisplayRect.bottom()), QPoint(graphDisplayRect.right(), rect().bottom()));
  134. QPainterPath xAxisPath;
  135. xAxisPath.moveTo(xAxisRect.left(), xAxisRect.top());
  136. xAxisPath.lineTo(xAxisRect.right(), xAxisRect.top());
  137. QRect textRect(0, 0, 40, 9);
  138. for (int i = 0; i < X_AXIS_GAP_AMOUNT + 1; i++) {
  139. xAxisPath.moveTo(xAxisRect.left() + i * xGap, xAxisRect.top() + X_AXIS_NUMBER_LINE_LENGTH);
  140. xAxisPath.lineTo(xAxisRect.left() + i * xGap, xAxisRect.top());
  141. textRect.moveCenter(QPoint(xAxisRect.left() + i * xGap, xAxisRect.top() + X_AXIS_NUMBER_GAP_LENGTH));
  142. painter.drawText(textRect, Qt::AlignCenter, QString::number(util::linearInterpolate(window.xMin, window.xMax, (1/ (double)X_AXIS_GAP_AMOUNT )*i), 'f', 1));
  143. }
  144. painter.drawPath(xAxisPath);
  145. //Draw XAxisLegend
  146. QRect xAxisLegendRect(QPoint(xAxisRect.left(), rect().bottom() - X_AXIS_LEGEND_TOP_BEGIN), rect().bottomRight());
  147. painter.drawText(xAxisLegendRect, Qt::AlignCenter, xAxisLegend);
  148. //draw Y-Axis
  149. QRect yAxisRect(QPoint(rect().left(), graphDisplayRect.top()), QPoint(graphDisplayRect.left(), graphDisplayRect.bottom()));
  150. QPainterPath yAxisPath;
  151. yAxisPath.moveTo(yAxisRect.right(), yAxisRect.bottom());
  152. yAxisPath.lineTo(yAxisRect.right(), yAxisRect.top());
  153. for (int i = 0; i < Y_AXIS_GAP_AMOUNT + 1; i++) {
  154. yAxisPath.moveTo(yAxisRect.right() - Y_AXIS_NUMBER_LINE_LENGTH, yAxisRect.bottom() - i * yGap);
  155. yAxisPath.lineTo(yAxisRect.right(), yAxisRect.bottom() - i * yGap);
  156. textRect.moveCenter(QPoint(yAxisRect.right() - Y_AXIS_NUMBER_GAP_LENGTH, yAxisRect.bottom() - i * yGap));
  157. painter.drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, QString::number(util::linearInterpolate(window.yMin, window.yMax, (1 / (double)Y_AXIS_GAP_AMOUNT) * i), 'f', 1));
  158. }
  159. painter.drawPath(yAxisPath);
  160. //Draw YAxisLegend
  161. //to draw vertical the painter have to translated and rotated (order is crucial)
  162. QRect yAxisLegendRect(0, 0, graphDisplayRect.bottom(), Y_AXIS_LEGEND_LEFT_BEGIN);
  163. painter.translate(0,graphDisplayRect.bottom());
  164. painter.rotate(-90.0);
  165. painter.drawText(yAxisLegendRect, Qt::AlignCenter, yAxisLegend);
  166. painter.rotate(+90.0);
  167. painter.translate(0, -graphDisplayRect.bottom());
  168. if (isLeftMousePressed) {
  169. QColor blue(0, 122, 204);
  170. QColor gray(120, 120, 120);
  171. //QPointF value = transformViewToGraph(oldPositionForMouseEvent);
  172. QPointF viewPoint = transformGraphToView(mousePressedValue);
  173. if (graphDisplayRect.top() <= viewPoint.y() && viewPoint.y() <= graphDisplayRect.bottom()) {
  174. painter.setPen(gray);
  175. painter.drawLine(QPointF(graphDisplayRect.left() - Y_AXIS_NUMBER_LINE_LENGTH, viewPoint.y()), QPointF(graphDisplayRect.right(), viewPoint.y()));
  176. textRect.moveCenter(QPoint(graphDisplayRect.left() - Y_AXIS_NUMBER_GAP_LENGTH, viewPoint.y()));
  177. painter.fillRect(textRect, whitePen.color());
  178. painter.setPen(blue);
  179. painter.drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, QString::number(mousePressedValue.y(), 'f', 1));
  180. }
  181. if (graphDisplayRect.left() <= viewPoint.x() && viewPoint.x() <= graphDisplayRect.right()) {
  182. painter.setPen(gray);
  183. painter.drawLine(QPointF(viewPoint.x(), graphDisplayRect.top()), QPointF(viewPoint.x(), graphDisplayRect.bottom() + X_AXIS_NUMBER_LINE_LENGTH));
  184. textRect.moveCenter(QPoint(viewPoint.x(), graphDisplayRect.bottom() + X_AXIS_NUMBER_GAP_LENGTH));
  185. painter.fillRect(textRect, whitePen.color());
  186. painter.setPen(blue);
  187. painter.drawText(textRect, Qt::AlignCenter, QString::number(mousePressedValue.x(), 'f', 1));
  188. }
  189. }
  190. if (isRightMousePressed) {
  191. QColor blue(0, 122, 204);
  192. painter.setPen(blue);
  193. painter.drawRect(QRectF(transformGraphToView(this->mousePressedValue), oldPositionForMouseEvent));
  194. }
  195. std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
  196. std::chrono::milliseconds timeElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
  197. }
  198. void Plott::keyPressEvent(QKeyEvent* event)
  199. {
  200. double percent = 0.1;
  201. switch (event->key()) {
  202. case Qt::Key_Up:
  203. window.moveUp(percent);
  204. break;
  205. case Qt::Key_Right:
  206. window.moveRight(percent);
  207. break;
  208. case Qt::Key_Left:
  209. window.moveLeft(percent);
  210. break;
  211. case Qt::Key_Down:
  212. window.moveDown(percent);
  213. break;
  214. case Qt::Key_PageUp:
  215. case Qt::Key_Plus:
  216. if (event->modifiers() & Qt::ShiftModifier) {
  217. //ZoomInY
  218. window.ZoomInY(percent);
  219. }
  220. else if (event->modifiers() & Qt::ControlModifier) {
  221. //ZoomInX
  222. window.ZoomInX(percent);
  223. }
  224. else {
  225. //Zoom
  226. window.ZoomIn(percent);
  227. }
  228. break;
  229. case Qt::Key_PageDown:
  230. case Qt::Key_Minus:
  231. if (event->modifiers() & Qt::ShiftModifier) {
  232. //ZoomOutX
  233. window.ZoomOutY(percent);
  234. }
  235. else if (event->modifiers() & Qt::ControlModifier) {
  236. //ZoomOutX
  237. window.ZoomOutX(percent);
  238. }
  239. else {
  240. //ZoomOut
  241. window.ZoomOut(percent);
  242. }
  243. break;
  244. case Qt::Key_R:
  245. resetToDefaultWindow();
  246. default:
  247. QWidget::keyPressEvent(event);
  248. }
  249. update();
  250. }
  251. void Plott::wheelEvent(QWheelEvent* event)
  252. {
  253. double percent = 0.1;
  254. QPoint numDegrees = event->angleDelta() / 8;
  255. if (!numDegrees.isNull()) {
  256. QPoint numSteps = numDegrees / 15;
  257. switch (numSteps.y()) {
  258. case 1:
  259. if (event->modifiers() & Qt::ShiftModifier) {
  260. window.ZoomInY(percent);
  261. }
  262. else if (event->modifiers() & Qt::ControlModifier) {
  263. window.ZoomInX(percent);
  264. }
  265. else {
  266. window.ZoomIn(percent);
  267. }
  268. break;
  269. case -1:
  270. if (event->modifiers() & Qt::ShiftModifier) {
  271. window.ZoomOutY(percent);
  272. }
  273. else if (event->modifiers() & Qt::ControlModifier) {
  274. window.ZoomOutX(percent);
  275. }
  276. else {
  277. window.ZoomOut(percent);
  278. }
  279. break;
  280. default:
  281. break;
  282. }
  283. update();
  284. }
  285. event->accept();
  286. }
  287. void Plott::mouseMoveEvent(QMouseEvent* event)
  288. {
  289. if (isLeftMousePressed) {
  290. //Move window by calculating the change in visibleWindow
  291. QPointF delta = event->pos() - oldPositionForMouseEvent;
  292. oldPositionForMouseEvent = event->pos();
  293. double rangeX = window.rangeX();
  294. int width = (rect().right() - 10) - (rect().left() + 50);
  295. width -= (width % X_AXIS_GAP_AMOUNT) + 1;
  296. double pixelWidthX = rangeX / (double)width;
  297. double rangeY = window.rangeY();
  298. int height = (rect().bottom() - 20) - (rect().top() + 20);
  299. height -= (height % Y_AXIS_GAP_AMOUNT) + 1;
  300. double pixelWidthY = rangeY / (double)height;
  301. window.xMin += -delta.x() * pixelWidthX;
  302. window.xMax += -delta.x() * pixelWidthX;
  303. window.yMin += delta.y() * pixelWidthY;
  304. window.yMax += delta.y() * pixelWidthY;
  305. update();
  306. }
  307. else if (isRightMousePressed) {
  308. oldPositionForMouseEvent = event->pos();
  309. update();
  310. }
  311. }
  312. void Plott::mousePressEvent(QMouseEvent* event)
  313. {
  314. if (event->buttons() == Qt::LeftButton) {
  315. isLeftMousePressed = true;
  316. }
  317. else if (event->buttons() == Qt::RightButton) {
  318. isRightMousePressed = true;
  319. }
  320. //To calculate the delta for mouse move event
  321. oldPositionForMouseEvent = event->pos();
  322. mousePressedValue = transformViewToGraph(oldPositionForMouseEvent);
  323. update();
  324. }
  325. void Plott::mouseReleaseEvent(QMouseEvent* event)
  326. {
  327. if (isRightMousePressed) {
  328. QPointF mouseViewPostion = transformGraphToView(mousePressedValue);
  329. if (std::abs(mouseViewPostion.x() - oldPositionForMouseEvent.x()) > 0 || std::abs(mouseViewPostion.y() - oldPositionForMouseEvent.y()) > 0) {
  330. //set visible window to selection
  331. VisibleWindow selectedWindow(0,0,0,0);
  332. QPointF endPosition = transformViewToGraph(oldPositionForMouseEvent);
  333. bool pressedIsLowerX = mousePressedValue.x() < endPosition.x();
  334. selectedWindow.xMin = pressedIsLowerX? mousePressedValue.x() : endPosition.x();
  335. selectedWindow.xMax = pressedIsLowerX ? endPosition.x() : mousePressedValue.x();
  336. bool pressedIsLowerY = mousePressedValue.y() < endPosition.y();
  337. selectedWindow.yMin = pressedIsLowerY ? mousePressedValue.y() : endPosition.y();
  338. selectedWindow.yMax = pressedIsLowerY ? endPosition.y() : mousePressedValue.y();
  339. handleSelectedWindow(selectedWindow, event);
  340. }
  341. }
  342. isLeftMousePressed = false;
  343. isRightMousePressed = false;
  344. update();
  345. }
  346. void Plott::handleSelectedWindow(VisibleWindow& window, QMouseEvent* event)
  347. {
  348. setWindow(window);
  349. }
  350. void Plott::setWindow(VisibleWindow& window)
  351. {
  352. this->window = window;
  353. update();
  354. }
  355. void VisibleWindow::moveUp(double percent)
  356. {
  357. double range = rangeY();
  358. yMin += percent * range;
  359. yMax += percent * range;
  360. }
  361. void VisibleWindow::moveDown(double percent)
  362. {
  363. double range = rangeY();
  364. yMin -= percent * range;
  365. yMax -= percent * range;
  366. }
  367. void VisibleWindow::moveLeft(double percent)
  368. {
  369. double range = rangeX();
  370. xMin -= percent * range;
  371. xMax -= percent * range;
  372. }
  373. void VisibleWindow::moveRight(double percent)
  374. {
  375. double range = rangeX();
  376. xMin += percent * range;
  377. xMax += percent * range;
  378. }
  379. void VisibleWindow::ZoomIn(double percent)
  380. {
  381. //Both sides same percentage
  382. percent /= 2;
  383. double Xrange = rangeX();
  384. if (Xrange == 0) Xrange = 1.0;
  385. xMin += percent * Xrange;
  386. xMax -= percent * Xrange;
  387. double Yrange = rangeY();
  388. if (Yrange == 0) Yrange = 1.0;
  389. yMin += percent * Yrange;
  390. yMax -= percent * Yrange;
  391. }
  392. void VisibleWindow::ZoomInX(double percent)
  393. {
  394. percent /= 2;
  395. double Xrange = rangeX();
  396. if (Xrange == 0) Xrange = 1.0;
  397. xMin += percent * Xrange;
  398. xMax -= percent * Xrange;
  399. }
  400. void VisibleWindow::ZoomInY(double percent)
  401. {
  402. percent /= 2;
  403. double Yrange = rangeY();
  404. if (Yrange == 0) Yrange = 1.0;
  405. yMin += percent * Yrange;
  406. yMax -= percent * Yrange;
  407. }
  408. void VisibleWindow::ZoomOut(double percent)
  409. {
  410. //Both sides same percentage
  411. percent /= 2;
  412. double Xrange = rangeX();
  413. if (Xrange == 0) Xrange = 1.0;
  414. xMin -= percent * Xrange;
  415. xMax += percent * Xrange;
  416. double Yrange = rangeY();
  417. if (Yrange == 0) Yrange = 1.0;
  418. yMin -= percent * Yrange;
  419. yMax += percent * Yrange;
  420. }
  421. void VisibleWindow::ZoomOutX(double percent)
  422. {
  423. percent /= 2;
  424. double Xrange = rangeX();
  425. if (Xrange == 0) Xrange = 1.0;
  426. xMin -= percent * Xrange;
  427. xMax += percent * Xrange;
  428. }
  429. void VisibleWindow::ZoomOutY(double percent)
  430. {
  431. percent /= 2;
  432. double Yrange = rangeY();
  433. if (Yrange == 0) Yrange = 1.0;
  434. yMin -= percent * Yrange;
  435. yMax += percent * Yrange;
  436. }
  437. double VisibleWindow::rangeX() const
  438. {
  439. return xMax - xMin;
  440. }
  441. double VisibleWindow::rangeY() const
  442. {
  443. return yMax - yMin;
  444. }
  445. bool VisibleWindow::inBoundX(QPointF point) const
  446. {
  447. return point.x() >= xMin && point.x() <= xMax;
  448. }
  449. bool VisibleWindow::inBoundY(QPointF point) const
  450. {
  451. return point.y() >= yMin && point.y() <= yMax;
  452. }
  453. bool VisibleWindow::inBound(QPointF point) const
  454. {
  455. return inBoundX(point) && inBoundY(point);
  456. }