ctkAxesWidget.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.commontk.org/LICENSE
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QDebug>
  16. #include <QBrush>
  17. #include <QGridLayout>
  18. #include <QLine>
  19. #include <QMouseEvent>
  20. #include <QPainter>
  21. // CTK includes
  22. #include "ctkAxesWidget.h"
  23. // STD includes
  24. #include <cmath>
  25. #include <math.h>
  26. static const double goldenRatio = 1.6180339887;
  27. static const double PI = 3.14159265358979323846;
  28. //ctkAxesWidgetPrivate
  29. //-----------------------------------------------------------------------------
  30. class ctkAxesWidgetPrivate
  31. {
  32. Q_DECLARE_PUBLIC(ctkAxesWidget);
  33. protected:
  34. ctkAxesWidget* const q_ptr;
  35. public:
  36. ctkAxesWidgetPrivate(ctkAxesWidget& object);
  37. QList<QPoint> extremities(QPoint center, int radius)const;
  38. QList<QRect> labelRects(const QList<QPoint>& extremities, QSize offset)const;
  39. ctkAxesWidget::Axis axisAtPos(QPoint pos)const;
  40. ctkAxesWidget::Axis CurrentAxis;
  41. ctkAxesWidget::Axis HighlightAxis;
  42. bool AutoReset;
  43. QStringList AxesLabels;
  44. QVector<double> AxesAngles;
  45. };
  46. //-----------------------------------------------------------------------------
  47. ctkAxesWidgetPrivate::ctkAxesWidgetPrivate(ctkAxesWidget& object)
  48. :q_ptr(&object)
  49. {
  50. qRegisterMetaType<ctkAxesWidget::Axis>("ctkAxesWidget::Axis");
  51. this->CurrentAxis = ctkAxesWidget::None;
  52. this->HighlightAxis = ctkAxesWidget::None;
  53. this->AutoReset = false;
  54. this->AxesLabels << "R" << "L" << "S" << "I" << "A" << "P";
  55. this->AxesAngles << 0 << 3.14159265 << 1.57079633 << 4.71238898 << 5.49778714 << 2.35619449;
  56. }
  57. //-----------------------------------------------------------------------------
  58. QList<QPoint> ctkAxesWidgetPrivate::extremities(QPoint center, int radius)const
  59. {
  60. QList<QPoint> pos;
  61. for (int i = 0; i < 6 ; ++i)
  62. {
  63. pos << center + QPoint(radius * cos(this->AxesAngles[i]),
  64. -radius * sin(this->AxesAngles[i]));
  65. }
  66. return pos;
  67. }
  68. //-----------------------------------------------------------------------------
  69. QList<QRect> ctkAxesWidgetPrivate::labelRects(const QList<QPoint>& extremities, QSize offset)const
  70. {
  71. Q_Q(const ctkAxesWidget);
  72. QFontMetrics fm = q->fontMetrics();
  73. QSize letterSize = fm.size(Qt::TextShowMnemonic, "X") + QSize(1,1);
  74. QSize halfLetterSize = letterSize / 2;
  75. QList<QRect> rects;
  76. for (int i = 0; i < 6; ++i)
  77. {
  78. rects << QRect(extremities[i]
  79. + QPoint(cos(this->AxesAngles[i]) * (offset.width()+halfLetterSize.width()),
  80. -sin(this->AxesAngles[i]) * (offset.height()+halfLetterSize.height()))
  81. - QPoint(halfLetterSize.width(), halfLetterSize.height()),
  82. letterSize);
  83. }
  84. return rects;
  85. }
  86. //-----------------------------------------------------------------------------
  87. ctkAxesWidget::Axis ctkAxesWidgetPrivate::axisAtPos(QPoint pos)const
  88. {
  89. Q_Q(const ctkAxesWidget);
  90. QPoint center = QPoint(q->width(), q->height()) / 2;
  91. int length = qMin(q->width(), q->height());
  92. int diameter = length / goldenRatio;
  93. int blankSize = (length - diameter) / 2;
  94. QSize sphereRadius((blankSize / 2) / 1.6180339887,
  95. (blankSize / 2) / 1.6180339887);
  96. QPointF mousePos = pos - center;
  97. double distance2 =
  98. mousePos.x() * mousePos.x() + mousePos.y() * mousePos.y();
  99. if (distance2 < sphereRadius.width()*sphereRadius.width())
  100. {
  101. return ctkAxesWidget::None;
  102. }
  103. double mouseAngle = atan2(-mousePos.y(), mousePos.x());
  104. // mouseAngle is in the interval [-pi,+pi] radians
  105. // change it to be in [-pi/8, 7/8 * pi]
  106. double PI_8 = 0.392699082;
  107. if (mouseAngle < -PI_8)
  108. {
  109. mouseAngle += 2. * PI;
  110. }
  111. for (int i = 0; i < 6; ++i)
  112. {
  113. if (mouseAngle >= (this->AxesAngles[i] - PI_8) &&
  114. mouseAngle <= (this->AxesAngles[i] + PI_8))
  115. {
  116. // the mouse is over the axis
  117. return static_cast<ctkAxesWidget::Axis>(i+1);
  118. }
  119. }
  120. return ctkAxesWidget::None;
  121. }
  122. //ctkAxesWidget
  123. //-----------------------------------------------------------------------------
  124. ctkAxesWidget::ctkAxesWidget(QWidget *newParent)
  125. : QWidget(newParent)
  126. , d_ptr(new ctkAxesWidgetPrivate(*this))
  127. {
  128. }
  129. //-----------------------------------------------------------------------------
  130. ctkAxesWidget::~ctkAxesWidget()
  131. {
  132. }
  133. // ----------------------------------------------------------------------------
  134. ctkAxesWidget::Axis ctkAxesWidget::currentAxis() const
  135. {
  136. Q_D(const ctkAxesWidget);
  137. return d->CurrentAxis;
  138. }
  139. //-----------------------------------------------------------------------------
  140. void ctkAxesWidget::setCurrentAxis(ctkAxesWidget::Axis newAxis)
  141. {
  142. Q_D(ctkAxesWidget);
  143. d->HighlightAxis = newAxis;
  144. if (newAxis == d->CurrentAxis)
  145. {
  146. return;
  147. }
  148. d->CurrentAxis = newAxis;
  149. this->repaint();
  150. emit currentAxisChanged(d->CurrentAxis);
  151. }
  152. //-----------------------------------------------------------------------------
  153. void ctkAxesWidget::setCurrentAxisToNone()
  154. {
  155. this->setCurrentAxis(ctkAxesWidget::None);
  156. }
  157. // ----------------------------------------------------------------------------
  158. bool ctkAxesWidget::autoReset() const
  159. {
  160. Q_D(const ctkAxesWidget);
  161. return d->AutoReset;
  162. }
  163. // ----------------------------------------------------------------------------
  164. void ctkAxesWidget::setAutoReset(bool newAutoReset)
  165. {
  166. Q_D(ctkAxesWidget);
  167. if (d->AutoReset == newAutoReset)
  168. {
  169. return;
  170. }
  171. d->AutoReset = newAutoReset;
  172. if (d->AutoReset)
  173. {
  174. connect(this, SIGNAL(currentAxisChanged(ctkAxesWidget::Axis)),
  175. this, SLOT(setCurrentAxisToNone()));
  176. setCurrentAxisToNone();
  177. }
  178. else
  179. {
  180. disconnect(this, SIGNAL(currentAxisChanged(ctkAxesWidget::Axis)),
  181. this, SLOT(setCurrentAxisToNone()));
  182. }
  183. }
  184. //-----------------------------------------------------------------------------
  185. void ctkAxesWidget::paintEvent(QPaintEvent *)
  186. {
  187. Q_D(ctkAxesWidget);
  188. // init
  189. QPainter painter(this);
  190. //painter.setRenderHint(QPainter::Antialiasing);
  191. QPoint center = QPoint(this->width(), this->height()) / 2;
  192. int length = qMin(this->width(), this->height());
  193. int diameter = length / goldenRatio;
  194. int radius = diameter / 2;
  195. QStringList axesLabels;
  196. QList<QPoint> positions = d->extremities(center, radius);
  197. QFontMetrics fm = this->fontMetrics();
  198. QSize letterSize = fm.size(Qt::TextShowMnemonic, "X") + QSize(1,1);
  199. QSize halfLetterSize = letterSize / 2;
  200. int blankSize = (length - diameter) / 2;
  201. QSize betweenLetterSpace = QSize(blankSize - letterSize.width(), blankSize - letterSize.height()) / 2;
  202. QList<QRect> labelRects = d->labelRects(positions, betweenLetterSpace);
  203. for (int i = 0; i < 6; ++i)
  204. {
  205. //QRect rect(positions[i] + QPoint(cos(d->AxesAngles[i]) * (betweenLetterSpace.width()+halfLetterSize.width()),
  206. // -sin(d->AxesAngles[i]) * (betweenLetterSpace.height()+halfLetterSize.height()))
  207. // - QPoint(halfLetterSize.width(), halfLetterSize.height()), letterSize);
  208. QRect rect = labelRects[i];
  209. //if (d->HighlightAxes)
  210. {
  211. QFont font = painter.font();
  212. font.setBold(d->HighlightAxis == (i + 1));
  213. painter.setFont(font);
  214. }
  215. painter.drawText(rect, Qt::AlignCenter, d->AxesLabels[i]);
  216. }
  217. // Drawing the lines
  218. for (int i = 0; i < 6; ++i)
  219. {
  220. //if (d->HighlightAxes)
  221. {
  222. QPen pen;
  223. if (d->HighlightAxis == (i + 1)) // axes start at 1
  224. {
  225. pen.setWidth(3);
  226. //pen.setColor(QColor(64, 64, 72)); // Payne's grey
  227. pen.setColor(this->palette().color(QPalette::Highlight));
  228. }
  229. painter.setPen(pen);
  230. }
  231. painter.drawLine(center, positions[i]);
  232. }
  233. QSize sphereRadius((blankSize / 2) / 1.6180339887,
  234. (blankSize / 2) / 1.6180339887);
  235. // Draw the center sphere
  236. QRadialGradient rg(QPointF(0.3333, 0.3333),0.7);
  237. rg.setCoordinateMode(QGradient::ObjectBoundingMode);
  238. if (//d->HighlightAxes &&
  239. d->HighlightAxis == ctkAxesWidget::None)
  240. {
  241. rg.setColorAt(0., this->palette().color(QPalette::Highlight));
  242. }
  243. else
  244. {
  245. rg.setColorAt(0., this->palette().color(QPalette::Light));
  246. }
  247. rg.setColorAt(1., QColor(64, 64, 72));
  248. painter.setBrush(QBrush(rg));
  249. painter.setPen(QPen(Qt::NoPen));
  250. painter.drawEllipse(QPointF(center), sphereRadius.width(), sphereRadius.height());
  251. }
  252. // ----------------------------------------------------------------------------------
  253. void ctkAxesWidget::mousePressEvent(QMouseEvent *mouseEvent)
  254. {
  255. Q_D(ctkAxesWidget);
  256. d->HighlightAxis = d->axisAtPos(mouseEvent->pos());
  257. this->update();
  258. }
  259. // ----------------------------------------------------------------------------------
  260. void ctkAxesWidget::mouseMoveEvent(QMouseEvent *mouseEvent)
  261. {
  262. Q_D(ctkAxesWidget);
  263. d->HighlightAxis = d->axisAtPos(mouseEvent->pos());
  264. this->update();
  265. }
  266. // ----------------------------------------------------------------------------------
  267. void ctkAxesWidget::mouseReleaseEvent(QMouseEvent *mouseEvent)
  268. {
  269. Q_D(ctkAxesWidget);
  270. this->setCurrentAxis(d->axisAtPos(mouseEvent->pos()));
  271. }
  272. // --------------------------------------------------------------------------
  273. QSize ctkAxesWidget::minimumSizeHint()const
  274. {
  275. // Pretty arbitrary size.
  276. return QSize(100, 100);
  277. }
  278. // --------------------------------------------------------------------------
  279. QSize ctkAxesWidget::sizeHint()const
  280. {
  281. // Pretty arbitrary size
  282. return QSize(100, 100);
  283. }
  284. //----------------------------------------------------------------------------
  285. bool ctkAxesWidget::hasHeightForWidth()const
  286. {
  287. return true;
  288. }
  289. //----------------------------------------------------------------------------
  290. int ctkAxesWidget::heightForWidth(int width)const
  291. {
  292. // Tends to be square
  293. return width;
  294. }