graph.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #include "graph.h"
  2. #include <QDebug>
  3. PlotTimeGraph::PlotTimeGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) : QCPGraph(keyAxis, valueAxis) { }
  4. void PlotTimeGraph::addColoredSegment(double lowerValue, double upperValue, QPen pen, QBrush brush)
  5. {
  6. if (qFuzzyCompare(lowerValue, upperValue)) {
  7. qDebug() << "Invalid segment : identical values";
  8. return;
  9. }
  10. mLowerValues.append(lowerValue);
  11. mUpperValues.append(upperValue);
  12. mPens.append(pen);
  13. mBrushs.append(brush);
  14. }
  15. void PlotTimeGraph::clearColoredSegments()
  16. {
  17. mLowerValues.clear();
  18. mUpperValues.clear();
  19. mPens.clear();
  20. mBrushs.clear();
  21. }
  22. void PlotTimeGraph::draw(QCPPainter *painter)
  23. {
  24. if (!mKeyAxis || !mValueAxis) {
  25. qDebug() << Q_FUNC_INFO << "invalid key or value axis";
  26. return;
  27. }
  28. if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty())
  29. return;
  30. if (mLineStyle == lsNone && mScatterStyle.isNone())
  31. return;
  32. QVector<QPointF> lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while
  33. // iterating over segments
  34. QCPDataSelection thresholdsSelection;
  35. QList<GraphColoredSegment> allSegments;
  36. for (int index = 0; index < mLowerValues.count(); index++) {
  37. QCPGraphDataContainer::const_iterator begin = mDataContainer->constEnd();
  38. for (QCPGraphDataContainer::const_iterator it = mDataContainer->constBegin(); it != mDataContainer->constEnd();
  39. ++it) {
  40. if (it->value >= mLowerValues.at(index) && it->value < mUpperValues.at(index)) {
  41. if (begin == mDataContainer->constEnd())
  42. begin = it;
  43. } else {
  44. if (begin != mDataContainer->constEnd()) {
  45. thresholdsSelection.addDataRange(
  46. QCPDataRange(begin - mDataContainer->constBegin(), it - mDataContainer->constBegin()));
  47. allSegments.append(GraphColoredSegment(
  48. QCPDataRange(begin - mDataContainer->constBegin(), it - mDataContainer->constBegin()),
  49. mPens.at(index), mBrushs.at(index)));
  50. begin = mDataContainer->constEnd();
  51. }
  52. }
  53. }
  54. if (begin != mDataContainer->constEnd()) {
  55. thresholdsSelection.addDataRange(QCPDataRange(begin - mDataContainer->constBegin(), dataCount()));
  56. allSegments.append(GraphColoredSegment(QCPDataRange(begin - mDataContainer->constBegin(), dataCount()),
  57. mPens.at(index), mBrushs.at(index)));
  58. }
  59. }
  60. auto baseSegments = thresholdsSelection.inverse(QCPDataRange(0, dataCount())).dataRanges();
  61. for (int k = 0; k < baseSegments.count(); k++) {
  62. allSegments.append(GraphColoredSegment(baseSegments.at(k).adjusted(-1, 1), mPen, mBrush));
  63. }
  64. std::sort(allSegments.begin(), allSegments.end(), lessThanSegment);
  65. for (int i = 0; i < allSegments.size(); ++i) {
  66. QCPDataRange lineDataRange = allSegments.at(i).dataRange();
  67. getLines(&lines, lineDataRange);
  68. painter->setBrush(allSegments.at(i).brush());
  69. painter->setPen(Qt::NoPen);
  70. drawFill(painter, &lines);
  71. // draw line:
  72. if (mLineStyle != lsNone) {
  73. painter->setPen(allSegments.at(i).pen());
  74. painter->setBrush(Qt::NoBrush);
  75. if (mLineStyle == lsImpulse)
  76. drawImpulsePlot(painter, lines);
  77. else
  78. drawLinePlot(painter, lines); // also step plots can be drawn as a line plot
  79. }
  80. // draw scatters:
  81. QCPScatterStyle finalScatterStyle = mScatterStyle;
  82. if (!finalScatterStyle.isNone()) {
  83. finalScatterStyle.setPen(allSegments.at(i).pen());
  84. getScatters(&scatters, allSegments.at(i).dataRange());
  85. drawScatterPlot(painter, scatters, finalScatterStyle);
  86. }
  87. }
  88. }
  89. void PlotTimeGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const
  90. {
  91. if (mLineStyle == lsImpulse)
  92. return; // fill doesn't make sense for impulse plot
  93. if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0)
  94. return;
  95. applyFillAntialiasingHint(painter);
  96. QVector<QCPDataRange> segments = getNonNanSegments(lines, keyAxis()->orientation());
  97. if (!mChannelFillGraph) {
  98. // draw base fill under graph, fill goes all the way to the zero-value-line:
  99. for (int i = 0; i < segments.size(); ++i)
  100. painter->drawPolygon(getCustomFillPolygon(lines, segments.at(i)));
  101. } else {
  102. // draw fill between this graph and mChannelFillGraph:
  103. QVector<QPointF> otherLines;
  104. mChannelFillGraph->getLines(&otherLines, QCPDataRange(0, mChannelFillGraph->dataCount()));
  105. if (!otherLines.isEmpty()) {
  106. QVector<QCPDataRange> otherSegments =
  107. getNonNanSegments(&otherLines, mChannelFillGraph->keyAxis()->orientation());
  108. QVector<QPair<QCPDataRange, QCPDataRange>> segmentPairs =
  109. getOverlappingSegments(segments, lines, otherSegments, &otherLines);
  110. for (int i = 0; i < segmentPairs.size(); ++i)
  111. painter->drawPolygon(
  112. getChannelFillPolygon(lines, segmentPairs.at(i).first, &otherLines, segmentPairs.at(i).second));
  113. }
  114. }
  115. }
  116. const QPolygonF PlotTimeGraph::getCustomFillPolygon(const QVector<QPointF> *lineData, QCPDataRange segment) const
  117. {
  118. if (segment.size() < 2)
  119. return QPolygonF();
  120. QPolygonF result(segment.size() + 2);
  121. result[0] = getCustomFillBasePoint(lineData->at(segment.begin()));
  122. std::copy(lineData->constBegin() + segment.begin(), lineData->constBegin() + segment.end(), result.begin() + 1);
  123. result[result.size() - 1] = getCustomFillBasePoint(lineData->at(segment.end() - 1));
  124. return result;
  125. }
  126. QPointF PlotTimeGraph::getCustomFillBasePoint(QPointF matchingDataPoint) const
  127. {
  128. QCPAxis *keyAxis = mKeyAxis.data();
  129. QCPAxis *valueAxis = mValueAxis.data();
  130. if (!keyAxis || !valueAxis) {
  131. qDebug() << Q_FUNC_INFO << "invalid key or value axis";
  132. return QPointF();
  133. }
  134. double fillRefValue;
  135. if (mFillStyle == fsZero)
  136. fillRefValue = valueAxis->coordToPixel(0);
  137. else
  138. fillRefValue = valueAxis->axisRect()->bottom();
  139. QPointF result;
  140. if (valueAxis->scaleType() == QCPAxis::stLinear) {
  141. if (keyAxis->orientation() == Qt::Horizontal) {
  142. result.setX(matchingDataPoint.x());
  143. result.setY(fillRefValue);
  144. } else { // keyAxis->orientation() == Qt::Vertical
  145. result.setX(fillRefValue);
  146. result.setY(matchingDataPoint.y());
  147. }
  148. } else { // valueAxis->mScaleType == QCPAxis::stLogarithmic
  149. // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
  150. // to the axis which is in the direction towards 0
  151. if (keyAxis->orientation() == Qt::Vertical) {
  152. if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed())
  153. || (valueAxis->range().upper > 0
  154. && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
  155. result.setX(keyAxis->axisRect()->right());
  156. else
  157. result.setX(keyAxis->axisRect()->left());
  158. result.setY(matchingDataPoint.y());
  159. } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom) {
  160. result.setX(matchingDataPoint.x());
  161. if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed())
  162. || (valueAxis->range().upper > 0
  163. && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
  164. result.setY(keyAxis->axisRect()->top());
  165. else
  166. result.setY(keyAxis->axisRect()->bottom());
  167. }
  168. }
  169. return result;
  170. }
  171. PlotTimeGraph::FillStyle PlotTimeGraph::getFillStyle() const
  172. {
  173. return mFillStyle;
  174. }
  175. void PlotTimeGraph::setFillStyle(const FillStyle &fillStyle)
  176. {
  177. mFillStyle = fillStyle;
  178. }