ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvQCustomPlot.h
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #pragma once
9 
10 // QCustomPlot
11 #include <qcustomplot.h>
12 
13 // System
14 #include <assert.h>
15 
16 /*********************************/
17 /*** Custom QCustomPlot wigets ***/
18 /*********************************/
19 
21 class QCPBarsWithText : public QCPBars {
22  Q_OBJECT
23 
24 public:
25  QCPBarsWithText(QCPAxis *keyAxis, QCPAxis *valueAxis)
26  : QCPBars(keyAxis, valueAxis), m_textOnTheLeft(false) {}
27 
28  void setText(QString text) { m_text = QStringList(text); }
29  void appendText(QString text) { m_text.append(text); }
30  void setTextAlignment(bool left) { m_textOnTheLeft = left; }
31 
32 protected:
33  QStringList m_text;
35 
36  // reimplemented virtual draw method
37  virtual void draw(QCPPainter *painter) {
38  if (!mKeyAxis || !mValueAxis) {
39  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
40  return;
41  }
42 
43  // switch to standard display
44  QCPBars::draw(painter);
45 
46  int fontHeight = painter->fontMetrics().height();
47 
48  if (!data()->isEmpty()) {
49  double &key = data()->begin()->key;
50  double &value = data()->begin()->value;
51  QPointF P = coordsToPixels(key, value);
52  // apply a small shift
53  int margin = 5; // in pixels
54  if (m_textOnTheLeft) margin = -margin;
55  P.setX(P.x() + margin);
56  // we draw at the 'base' line
57  P.setY(P.y() + fontHeight);
58 
59  for (int i = 0; i < m_text.size(); ++i) {
60  QPointF Pstart = P;
61  if (m_textOnTheLeft)
62  Pstart.setX(P.x() -
63  painter->fontMetrics().horizontalAdvance(
64  m_text[i]));
65  painter->drawText(Pstart, m_text[i]);
66  P.setY(P.y() + fontHeight);
67  }
68  }
69  }
70 };
71 
73 class QCPColoredBars : public QCPBars {
74  Q_OBJECT
75 
76 public:
77  class QCPColoredBarData : public QCPBarsData {
78  public:
80 
81  QColor color;
82  };
83  typedef QMultiMap<double, QCPColoredBarData> QCPColoredBarDataMap;
84 
85  QCPColoredBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
86  : QCPBars(keyAxis, valueAxis) {}
87 
88  void setData(const QVector<double> &key, const QVector<double> &value) {
89  // no colors? we switch to the standard QCPBars object
90  m_coloredData.clear();
91  QCPBars::setData(key, value);
92  }
93 
94  void setData(const QVector<double> &key,
95  const QVector<double> &value,
96  const QVector<QColor> &colors) {
97  Q_ASSERT(colors.size() == key.size());
98 
99  data()->clear(); // we duplicate the structures so that other stuff in
100  // QCPBarData works!
101 
102  int n = qMin(key.size(), value.size());
103 
104  for (int i = 0; i < n; ++i) {
105  QCPColoredBarData newData;
106  newData.key = key[i];
107  newData.value = value[i];
108  if (colors.size() > i) newData.color = colors[i];
109  m_coloredData.insert(newData.key, newData);
110  QCPBars::addData(newData.key, newData.value);
111  }
112  }
113 
114  inline QRect rect() const { return clipRect(); }
115 
116  // reimplemented virtual methods:
117  virtual void clearData() {
118  QCPBars::data().clear();
119  m_coloredData.clear();
120  }
121 
122 protected:
123  // reimplemented virtual draw method
124  virtual void draw(QCPPainter *painter) {
125  // no colors?
126  if (m_coloredData.empty()) {
127  // switch to standard display
128  QCPBars::draw(painter);
129  }
130 
131  if (!mKeyAxis || !mValueAxis) {
132  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
133  return;
134  }
135 
136  QCPColoredBarDataMap::const_iterator it;
137  for (it = m_coloredData.constBegin(); it != m_coloredData.constEnd();
138  ++it) {
139  // skip bar if not visible in key axis range:
140  if (it.key() + mWidth * 0.5 < mKeyAxis.data()->range().lower ||
141  it.key() - mWidth * 0.5 > mKeyAxis.data()->range().upper)
142  continue;
143 
144  QRectF barRect = getBarRect(it.key(), it.value().value);
145  // draw bar fill:
146  if (brush().style() != Qt::NoBrush &&
147  brush().color().alpha() != 0) {
148  QBrush theBrush = brush();
149  theBrush.setColor(it.value().color);
150 
151  applyFillAntialiasingHint(painter);
152  painter->setPen(Qt::NoPen);
153  painter->setBrush(theBrush);
154  painter->drawRect(barRect);
155  }
156  // draw bar line:
157  if (pen().style() != Qt::NoPen && pen().color().alpha() != 0) {
158  QPen thePen = pen();
159  thePen.setColor(it.value().color);
160 
161  applyDefaultAntialiasingHint(painter);
162  painter->setPen(thePen);
163  painter->setBrush(Qt::NoBrush);
164  painter->drawPolyline(barRect);
165  }
166  }
167  }
168 
170 };
171 
173 class QCPSelectableCursor : public QCPAbstractPlottable {
174  Q_OBJECT
175 
176 public:
178  explicit QCPSelectableCursor(QCPAxis *keyAxis, QCPAxis *valueAxis)
179  : QCPAbstractPlottable(keyAxis, valueAxis),
180  mCurrentVal(0),
181  mMinVal(0),
182  mMaxVal(0),
183  mLastPos(-1, -1),
184  mLastRadius(0) {}
185 
188  inline virtual bool isSelectable(QPoint click) const {
189  if (mLastPos.x() < 0 || mLastPos.y() < 0) return false;
190  QPoint d = mLastPos - click;
191  return (d.x() * d.x() + d.y() * d.y() <= mLastRadius * mLastRadius);
192  }
193 
194  // getters
195  inline double currentVal() const { return mCurrentVal; }
196  inline double minVal() const { return mMinVal; }
197  inline double maxVal() const { return mMaxVal; }
198  inline void range(double &minVal, double &maxVal) const {
199  minVal = mMinVal;
200  maxVal = mMaxVal;
201  }
202 
203  // setters
204  inline void setCurrentVal(double val) {
206  }
207  inline void setRange(double minVal, double maxVal) {
208  mMinVal = minVal;
209  mMaxVal = maxVal;
210  }
211 
213  inline double pixelToKey(int pixX) const {
214  return keyAxis() ? keyAxis()->pixelToCoord(pixX) : 0;
215  }
217  inline double pixelToValue(int pixY) const {
218  return valueAxis() ? valueAxis()->pixelToCoord(pixY) : 0;
219  }
220 
221  // reimplemented virtual methods:
222  virtual void clearData() {}
223  double selectTest(const QPointF &pos,
224  bool onlySelectable,
225  QVariant *details = 0) const override {
226  return -1;
227  } // we don't use the QCP internal selection mechanism!
228 
229 protected:
230  // reimplemented virtual methods:
231  void drawLegendIcon(QCPPainter *painter,
232  const QRectF &rect) const override {}
233  QCPRange getKeyRange(
234  bool &foundRange,
235  QCP::SignDomain inSignDomain = QCP::sdBoth) const override {
236  foundRange = false;
237  return QCPRange();
238  }
239  QCPRange getValueRange(
240  bool &foundRange,
241  QCP::SignDomain inSignDomain = QCP::sdBoth,
242  const QCPRange &inKeyRange = QCPRange()) const override {
243  foundRange = false;
244  return QCPRange();
245  }
246 
247  // property members:
248  double mCurrentVal;
249  double mMinVal, mMaxVal;
250  QPoint mLastPos;
252 };
253 
256  Q_OBJECT
257 
258 public:
259  explicit QCPHiddenArea(bool leftSide, QCPAxis *keyAxis, QCPAxis *valueAxis)
260  : QCPSelectableCursor(keyAxis, valueAxis), mLeftSide(leftSide) {
261  mPen = QPen(QColor(80, 80, 80), Qt::SolidLine); // dark grey
262  mPen.setWidth(2);
263  setPen(mPen);
264 
265  mBrush = QBrush(Qt::white, Qt::SolidPattern); // white
266  setBrush(mBrush);
267  }
268 
269 protected:
270  // reimplemented virtual methods:
271  virtual void draw(QCPPainter *painter) {
272  if (!keyAxis()) return;
273 
274  QRect rect = clipRect();
275 
276  double currentPosd = keyAxis()->coordToPixel(mCurrentVal);
277  if (mLeftSide) {
278  int x2 = static_cast<int>(ceil(currentPosd));
279  // assert(x2 >= rect.x());
280  if (x2 < rect.x()) return;
281  rect.setWidth(x2 - rect.x());
282  } else {
283  int x1 = static_cast<int>(floor(currentPosd));
284  // assert(x1 >= rect.x());
285  if (x1 < rect.x()) return;
286  int newWidth = rect.width() - (x1 - rect.x());
287  rect.setX(x1);
288  rect.setWidth(newWidth);
289  }
290 
291  // draw greyed rect
292  if ((mLeftSide && mCurrentVal > mMinVal) ||
293  (!mLeftSide && mCurrentVal < mMaxVal)) {
294  applyFillAntialiasingHint(painter);
295  painter->setPen(Qt::NoPen);
296  painter->setBrush(
297  QBrush(QColor(128, 128, 128, 128),
298  Qt::SolidPattern)); // semi-transparent grey
299  painter->drawPolygon(rect);
300  }
301 
302  // draw circle (handle)
303  if (pen().style() != Qt::NoPen && pen().color().alpha() != 0) {
304  // circle
305  QPoint C(mLeftSide ? rect.x() + rect.width() : rect.x(),
306  rect.y() + rect.height() / 2);
307  int r = rect.height() / 10;
308 
309  painter->setPen(pen());
310  painter->setBrush(brush());
311  painter->drawEllipse(C, r, r);
312 
313  painter->setPen(QPen(QColor(128, 128, 128, 128),
314  Qt::SolidLine)); // semi-transparent grey
315  painter->drawLine(C + QPoint(0, r), C - QPoint(0, r));
316 
317  // save last circle position
318  mLastPos = C;
319  mLastRadius = r;
320  } else {
321  // no circle
322  mLastPos = QPoint(-1, -1);
323  mLastRadius = 0;
324  }
325  }
326 
328  bool mLeftSide;
329 };
330 
333  Q_OBJECT
334 
335 public:
336  explicit QCPArrow(QCPAxis *keyAxis, QCPAxis *valueAxis)
337  : QCPSelectableCursor(keyAxis, valueAxis) {
338  mPen.setColor(QColor(128, 128, 0)); // dark yellow
339  mPen.setStyle(Qt::SolidLine);
340  mPen.setWidth(2);
341  setPen(mPen);
342 
343  mBrush.setColor(QColor(255, 255, 0, 196)); // semi-transparent yellow
344  mBrush.setStyle(Qt::SolidPattern);
345  setBrush(mBrush);
346  }
347 
349  void setColor(int r, int g, int b) {
350  mBrush.setColor(QColor(r, g, b, 196)); // semi-transparent color
351  setBrush(mBrush);
352  }
353 
354 protected:
355  // reimplemented virtual methods:
356  virtual void draw(QCPPainter *painter) {
357  if (!keyAxis()) return;
358 
359  QRect rect = clipRect();
360 
361  int currentPos = static_cast<int>(keyAxis()->coordToPixel(mCurrentVal));
362 
363  int r = rect.height() / 10;
364 
365  // draw dashed line
366  {
367  QPen pen(QColor(128, 128, 128, 128), Qt::DashLine);
368  pen.setWidth(1);
369  painter->setPen(pen); // semi-transparent grey
370  painter->drawLine(QPoint(currentPos, rect.y() + 2 * r),
371  QPoint(currentPos, rect.y() + rect.height()));
372  }
373 
374  // draw triangle(handle)
375  if (pen().style() != Qt::NoPen && pen().color().alpha() != 0) {
376  // QPoint O(currentPos,rect.y() + rect.height() - r);
377  // QPoint T[3] = { O - QPoint(0,r), O + QPoint(r,r), O +
378  // QPoint(-r,r) };
379 
380  QPoint O(currentPos, rect.y() + r);
381  QPoint T[3] = {O + QPoint(0, r), O - QPoint(r, r),
382  O - QPoint(-r, r)};
383 
384  painter->setPen(pen());
385  painter->setBrush(brush());
386  painter->drawPolygon(T, 3);
387 
388  // save last circle position
389  mLastPos = O;
390  mLastRadius = r;
391  } else {
392  // no circle
393  mLastPos = QPoint(-1, -1);
394  mLastRadius = 0;
395  }
396  }
397 };
math::float4 color
QCustomPlot: small arrows at the bottom.
virtual void draw(QCPPainter *painter)
QCPArrow(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setColor(int r, int g, int b)
Sets triangle 'inside' color.
QCustomPlot: vertical bar with text along side.
QCPBarsWithText(QCPAxis *keyAxis, QCPAxis *valueAxis)
QStringList m_text
void setText(QString text)
virtual void draw(QCPPainter *painter)
void setTextAlignment(bool left)
void appendText(QString text)
QCustomPlot: colored histogram.
QRect rect() const
QCPColoredBarDataMap m_coloredData
virtual void draw(QCPPainter *painter)
QCPColoredBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
QMultiMap< double, QCPColoredBarData > QCPColoredBarDataMap
void setData(const QVector< double > &key, const QVector< double > &value)
virtual void clearData()
void setData(const QVector< double > &key, const QVector< double > &value, const QVector< QColor > &colors)
QCustomPlot: greyed areas.
QCPHiddenArea(bool leftSide, QCPAxis *keyAxis, QCPAxis *valueAxis)
virtual void draw(QCPPainter *painter)
bool mLeftSide
Whether the cursor is displayed on the left side or not.
QCustomPlot: selectable cursor interface.
double pixelToValue(int pixY) const
Converts a pixel value (Y) to the equivalent value.
void range(double &minVal, double &maxVal) const
double pixelToKey(int pixX) const
Converts a pixel value (X) to the equivalent key.
double maxVal() const
void setRange(double minVal, double maxVal)
virtual bool isSelectable(QPoint click) const
double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const override
QCPSelectableCursor(QCPAxis *keyAxis, QCPAxis *valueAxis)
Default constructor.
QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const override
QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const override
void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const override
void setCurrentVal(double val)
double minVal() const
virtual void clearData()
double currentVal() const
double colors[3]
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
constexpr Rgb white(MAX, MAX, MAX)
constexpr Rgb blue(0, 0, MAX)
Definition: lsd.c:1170
double x
Definition: lsd.c:1173
double width
Definition: lsd.c:1172
double y
Definition: lsd.c:1173