ccQCustomPlot.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. //##########################################################################
  2. //# #
  3. //# CLOUDCOMPARE #
  4. //# #
  5. //# This program is free software; you can redistribute it and/or modify #
  6. //# it under the terms of the GNU General Public License as published by #
  7. //# the Free Software Foundation; version 2 or later of the License. #
  8. //# #
  9. //# This program is distributed in the hope that it will be useful, #
  10. //# but WITHOUT ANY WARRANTY; without even the implied warranty of #
  11. //# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
  12. //# GNU General Public License for more details. #
  13. //# #
  14. //# COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI) #
  15. //# #
  16. //##########################################################################
  17. #ifndef CC_QCUSTOMPLOT_HEADER
  18. #define CC_QCUSTOMPLOT_HEADER
  19. //QCustomPlot
  20. #ifdef _MSC_VER
  21. //To get rid of the really annoying warnings about unsafe methods
  22. #pragma warning( push )
  23. #pragma warning( disable : 4996 )
  24. #endif
  25. #include <qcustomplot.h>
  26. #ifdef _MSC_VER
  27. #pragma warning( pop )
  28. #endif
  29. //System
  30. #include <assert.h>
  31. /*********************************/
  32. /*** Custom QCustomPlot wigets ***/
  33. /*********************************/
  34. //! QCustomPlot: vertical bar with text along side
  35. class QCPBarsWithText : public QCPBars
  36. {
  37. Q_OBJECT
  38. public:
  39. QCPBarsWithText(QCPAxis* keyAxis, QCPAxis* valueAxis) : QCPBars(keyAxis,valueAxis), m_textOnTheLeft(false) {}
  40. void setText(QString text) { m_text = QStringList(text); }
  41. void appendText(QString text) { m_text.append(text); }
  42. void setTextAlignment(bool left) { m_textOnTheLeft = left; }
  43. protected:
  44. QStringList m_text;
  45. bool m_textOnTheLeft;
  46. // reimplemented virtual draw method
  47. virtual void draw(QCPPainter *painter)
  48. {
  49. if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
  50. //switch to standard display
  51. QCPBars::draw(painter);
  52. int fontHeight = painter->fontMetrics().height();
  53. if (!data()->isEmpty())
  54. {
  55. double& key = data()->begin()->key;
  56. double& value = data()->begin()->value;
  57. QPointF P = coordsToPixels(key, value);
  58. //apply a small shift
  59. int margin = 5; //in pixels
  60. if (m_textOnTheLeft)
  61. margin = -margin;
  62. P.setX(P.x() + margin);
  63. //we draw at the 'base' line
  64. P.setY(P.y() + fontHeight);
  65. for (int i=0; i<m_text.size(); ++i)
  66. {
  67. QPointF Pstart = P;
  68. if (m_textOnTheLeft)
  69. Pstart.setX(P.x() - painter->fontMetrics().width(m_text[i]));
  70. painter->drawText(Pstart,m_text[i]);
  71. P.setY(P.y() + fontHeight);
  72. }
  73. }
  74. }
  75. };
  76. //! QCustomPlot: colored histogram
  77. class QCPColoredBars : public QCPBars
  78. {
  79. Q_OBJECT
  80. public:
  81. class QCPColoredBarData : public QCPBarsData
  82. {
  83. public:
  84. QCPColoredBarData()
  85. : color(Qt::blue)
  86. {}
  87. QColor color;
  88. };
  89. typedef QMap<double, QCPColoredBarData> QCPColoredBarDataMap;
  90. QCPColoredBars(QCPAxis *keyAxis, QCPAxis *valueAxis) : QCPBars(keyAxis,valueAxis) {}
  91. void setData(const QVector<double> &key, const QVector<double> &value)
  92. {
  93. //no colors? we switch to the standard QCPBars object
  94. m_coloredData.clear();
  95. QCPBars::setData(key,value);
  96. }
  97. void setData(const QVector<double> &key, const QVector<double> &value, const QVector<QColor>& colors)
  98. {
  99. Q_ASSERT(colors.size() == key.size());
  100. data()->clear(); //we duplicate the structures so that other stuff in QCPBarData works!
  101. int n = qMin(key.size(), value.size());
  102. for (int i=0; i<n; ++i)
  103. {
  104. QCPColoredBarData newData;
  105. newData.key = key[i];
  106. newData.value = value[i];
  107. if (colors.size() > i)
  108. newData.color = colors[i];
  109. m_coloredData.insertMulti(newData.key, newData);
  110. QCPBars::addData(newData.key,newData.value);
  111. }
  112. }
  113. inline QRect rect() const { return clipRect(); }
  114. // reimplemented virtual methods:
  115. virtual void clearData() { QCPBars::data().clear(); m_coloredData.clear(); }
  116. protected:
  117. // reimplemented virtual draw method
  118. virtual void draw(QCPPainter *painter)
  119. {
  120. //no colors?
  121. if (m_coloredData.empty())
  122. {
  123. //switch to standard display
  124. QCPBars::draw(painter);
  125. }
  126. if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
  127. QCPColoredBarDataMap::const_iterator it;
  128. for (it = m_coloredData.constBegin(); it != m_coloredData.constEnd(); ++it)
  129. {
  130. // skip bar if not visible in key axis range:
  131. if (it.key()+mWidth*0.5 < mKeyAxis.data()->range().lower || it.key()-mWidth*0.5 > mKeyAxis.data()->range().upper)
  132. continue;
  133. QRectF barRect = getBarRect(it.key(), it.value().value);
  134. // draw bar fill:
  135. if (brush().style() != Qt::NoBrush && brush().color().alpha() != 0)
  136. {
  137. QBrush theBrush = brush();
  138. theBrush.setColor(it.value().color);
  139. applyFillAntialiasingHint(painter);
  140. painter->setPen(Qt::NoPen);
  141. painter->setBrush(theBrush);
  142. painter->drawRect(barRect);
  143. }
  144. // draw bar line:
  145. if (pen().style() != Qt::NoPen && pen().color().alpha() != 0)
  146. {
  147. QPen thePen = pen();
  148. thePen.setColor(it.value().color);
  149. applyDefaultAntialiasingHint(painter);
  150. painter->setPen(thePen);
  151. painter->setBrush(Qt::NoBrush);
  152. painter->drawPolyline(barRect);
  153. }
  154. }
  155. }
  156. QCPColoredBarDataMap m_coloredData;
  157. };
  158. //! QCustomPlot: selectable cursor interface
  159. class QCPSelectableCursor : public QCPAbstractPlottable
  160. {
  161. Q_OBJECT
  162. public:
  163. //! Default constructor
  164. explicit QCPSelectableCursor(QCPAxis *keyAxis, QCPAxis *valueAxis)
  165. : QCPAbstractPlottable(keyAxis, valueAxis)
  166. , mCurrentVal(0)
  167. , mMinVal(0)
  168. , mMaxVal(0)
  169. , mLastPos(-1,-1)
  170. , mLastRadius(0)
  171. {}
  172. //! Returns whether the item is "selectable" when the mouse is clicked at a given position
  173. inline virtual bool isSelectable(QPoint click) const
  174. {
  175. if (mLastPos.x() < 0 || mLastPos.y() < 0)
  176. return false;
  177. QPoint d = mLastPos - click;
  178. return (d.x()*d.x() + d.y()*d.y() <= mLastRadius*mLastRadius);
  179. }
  180. // getters
  181. inline double currentVal() const { return mCurrentVal; }
  182. inline double minVal() const { return mMinVal; }
  183. inline double maxVal() const { return mMaxVal; }
  184. inline void range(double& minVal, double& maxVal) const { minVal = mMinVal; maxVal = mMaxVal; }
  185. // setters
  186. inline void setCurrentVal(double val) { mCurrentVal = std::max(std::min(val,mMaxVal),mMinVal); }
  187. inline void setRange(double minVal, double maxVal) { mMinVal = minVal; mMaxVal = maxVal; }
  188. //! Converts a pixel value (X) to the equivalent key
  189. inline double pixelToKey(int pixX) const { return keyAxis() ? keyAxis()->pixelToCoord(pixX) : 0; }
  190. //! Converts a pixel value (Y) to the equivalent value
  191. inline double pixelToValue(int pixY) const { return valueAxis() ? valueAxis()->pixelToCoord(pixY) : 0; }
  192. // reimplemented virtual methods:
  193. virtual void clearData() {}
  194. double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const override { return -1; } //we don't use the QCP internal selection mechanism!
  195. protected:
  196. // reimplemented virtual methods:
  197. void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const override {}
  198. QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const override { foundRange = false; return QCPRange(); }
  199. QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const override { foundRange = false; return QCPRange(); }
  200. // property members:
  201. double mCurrentVal;
  202. double mMinVal,mMaxVal;
  203. QPoint mLastPos;
  204. int mLastRadius;
  205. };
  206. //! QCustomPlot: greyed areas
  207. class QCPHiddenArea : public QCPSelectableCursor
  208. {
  209. Q_OBJECT
  210. public:
  211. explicit QCPHiddenArea(bool leftSide, QCPAxis *keyAxis, QCPAxis *valueAxis)
  212. : QCPSelectableCursor(keyAxis, valueAxis)
  213. , mLeftSide(leftSide)
  214. {
  215. mPen = QPen(QColor(80, 80, 80),Qt::SolidLine); // dark grey
  216. mPen.setWidth(2);
  217. setPen( mPen );
  218. mBrush = QBrush(Qt::white,Qt::SolidPattern); // white
  219. setBrush( mBrush );
  220. }
  221. protected:
  222. // reimplemented virtual methods:
  223. virtual void draw(QCPPainter *painter)
  224. {
  225. if (!keyAxis())
  226. return;
  227. QRect rect = clipRect();
  228. double currentPosd = keyAxis()->coordToPixel(mCurrentVal);
  229. if (mLeftSide)
  230. {
  231. int x2 = static_cast<int>(ceil(currentPosd));
  232. //assert(x2 >= rect.x());
  233. if (x2 < rect.x())
  234. return;
  235. rect.setWidth(x2 - rect.x());
  236. }
  237. else
  238. {
  239. int x1 = static_cast<int>(floor(currentPosd));
  240. //assert(x1 >= rect.x());
  241. if (x1 < rect.x())
  242. return;
  243. int newWidth = rect.width() - (x1 - rect.x());
  244. rect.setX(x1);
  245. rect.setWidth(newWidth);
  246. }
  247. // draw greyed rect
  248. if ( (mLeftSide && mCurrentVal > mMinVal)
  249. || (!mLeftSide && mCurrentVal < mMaxVal) )
  250. {
  251. applyFillAntialiasingHint(painter);
  252. painter->setPen(Qt::NoPen);
  253. painter->setBrush(QBrush(QColor(128, 128, 128, 128),Qt::SolidPattern)); // semi-transparent grey
  254. painter->drawPolygon(rect);
  255. }
  256. //draw circle (handle)
  257. if (pen().style() != Qt::NoPen && pen().color().alpha() != 0)
  258. {
  259. //circle
  260. QPoint C(mLeftSide ? rect.x()+rect.width() : rect.x(), rect.y()+rect.height()/2);
  261. int r = rect.height() / 10;
  262. painter->setPen(pen());
  263. painter->setBrush(brush());
  264. painter->drawEllipse(C,r,r);
  265. painter->setPen(QPen(QColor(128, 128, 128, 128),Qt::SolidLine)); // semi-transparent grey
  266. painter->drawLine(C+QPoint(0,r),C-QPoint(0,r));
  267. //save last circle position
  268. mLastPos = C;
  269. mLastRadius = r;
  270. }
  271. else
  272. {
  273. //no circle
  274. mLastPos = QPoint(-1,-1);
  275. mLastRadius = 0;
  276. }
  277. }
  278. //! Whether the cursor is displayed on the left side or not
  279. bool mLeftSide;
  280. };
  281. //! QCustomPlot: small arrows at the bottom
  282. class QCPArrow : public QCPSelectableCursor
  283. {
  284. Q_OBJECT
  285. public:
  286. explicit QCPArrow(QCPAxis *keyAxis, QCPAxis *valueAxis)
  287. : QCPSelectableCursor(keyAxis, valueAxis)
  288. {
  289. mPen.setColor(QColor(128, 128, 0)); // dark yellow
  290. mPen.setStyle(Qt::SolidLine);
  291. mPen.setWidth(2);
  292. setPen( mPen );
  293. mBrush.setColor(QColor(255, 255, 0, 196)); // semi-transparent yellow
  294. mBrush.setStyle(Qt::SolidPattern);
  295. setBrush( mBrush );
  296. }
  297. //! Sets triangle 'inside' color
  298. void setColor(int r, int g, int b)
  299. {
  300. mBrush.setColor(QColor(r, g, b, 196)); // semi-transparent color
  301. setBrush( mBrush );
  302. }
  303. protected:
  304. // reimplemented virtual methods:
  305. virtual void draw(QCPPainter *painter)
  306. {
  307. if (!keyAxis())
  308. return;
  309. QRect rect = clipRect();
  310. int currentPos = static_cast<int>(keyAxis()->coordToPixel(mCurrentVal));
  311. int r = rect.height() / 10;
  312. // draw dashed line
  313. {
  314. QPen pen(QColor(128, 128, 128, 128),Qt::DashLine);
  315. pen.setWidth(1);
  316. painter->setPen(pen); // semi-transparent grey
  317. painter->drawLine(QPoint(currentPos,rect.y()+2*r), QPoint(currentPos,rect.y()+rect.height()));
  318. }
  319. //draw triangle(handle)
  320. if (pen().style() != Qt::NoPen && pen().color().alpha() != 0)
  321. {
  322. //QPoint O(currentPos,rect.y() + rect.height() - r);
  323. //QPoint T[3] = { O - QPoint(0,r), O + QPoint(r,r), O + QPoint(-r,r) };
  324. QPoint O(currentPos,rect.y() + r);
  325. QPoint T[3] = { O + QPoint(0,r), O - QPoint(r,r), O - QPoint(-r,r) };
  326. painter->setPen(pen());
  327. painter->setBrush(brush());
  328. painter->drawPolygon(T,3);
  329. //save last circle position
  330. mLastPos = O;
  331. mLastRadius = r;
  332. }
  333. else
  334. {
  335. //no circle
  336. mLastPos = QPoint(-1,-1);
  337. mLastRadius = 0;
  338. }
  339. }
  340. };
  341. #endif //CC_QCUSTOMPLOT_HEADER