NorMIT-nav  18.04
An IGT application
cxTransferFunctionColorWidget.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
13 
14 #include <vtkColorTransferFunction.h>
15 
16 #include <limits.h>
17 #include <QPainter>
18 #include <QPen>
19 #include <QColor>
20 #include <QMouseEvent>
21 #include <QColorDialog>
22 #include <QMenu>
23 #include <QTimer>
24 #include "cxImageTF3D.h"
25 #include "cxImageTFData.h"
26 #include "cxUtilHelpers.h"
27 #include "cxImageLUT2D.h"
28 #include "cxLogger.h"
29 #include "cxTypeConversions.h"
30 #include "cxReporter.h"
31 #include "cxMathUtils.h"
32 
33 
34 namespace cx
35 {
37  BaseWidget(parent, "transfer_function_color_widget", "Color Transfer Function"),
38  mCurrentClickPos(INT_MIN,INT_MIN),
39  mBorder(5)
40 {
41  this->setToolTip("Set the color part of a transfer function");
42  this->setFocusPolicy(Qt::StrongFocus);
44  connect(mActiveImageProxy.get(), SIGNAL(transferFunctionsChanged()), this, SLOT(activeImageTransferFunctionsChangedSlot()));
45 
47 
48  // Create actions
49  mCustomColorAction = new QAction(tr("Custom color..."), this);
50  mRemoveColorAction = new QAction(tr("Remove point"), this);
51 
52  connect(mCustomColorAction, SIGNAL(triggered(bool)), this, SLOT(setColorSlot()));
53  connect(mRemoveColorAction, SIGNAL(triggered()), this, SLOT(removeColorSlot()));
54 }
55 
57 {}
58 
60 {
61  this->setMouseTracking(true);
62 }
63 
65 {
66  this->setMouseTracking(false);
67 }
68 
69 
71 {
72  if (( mImage == image )&&( mImageTF==tfData ))
73  return;
74 
75  mImage = image;
76  mImageTF = tfData;
77  this->update();
78 }
79 
81 {
82  this->update();
83 }
84 
86 {
87  QWidget::mousePressEvent(event);
88  mCurrentClickPos = event->pos();
89 
90  if(event->button() == Qt::LeftButton)
91  {
93  this->update();
94  }
95 }
96 
98 {
99  QWidget::mouseReleaseEvent(event);
100  //we no longer need these values
101  //mSelectedPoint.reset();
102 }
103 
105 {
106  QWidget::mouseMoveEvent(event);
107 
108  if(event->buttons() == Qt::LeftButton)
109  {
110  // Update current screen point for use in moveCurrentAlphaPoint
111  mCurrentClickPos = event->pos();
112 // this->moveCurrentPoint();
114  }
115 
116  this->updateTooltip(event->pos());
117 }
118 
120 {
121  ColorPoint selected = this->selectPoint(pos);
122  if (!selected.isValid())
123  selected = this->getCurrentColorPoint(pos.x());
124  this->updateTooltip(selected);
125  this->update();
126 }
127 
129 {
130  QString tip = QString("color(%1)=(%2)").arg(point.intensity).arg(color2string(point.value));
131 // std::cout << "updated to " << tip << std::endl;
132  this->setToolTip(tip);
133  reporter()->sendVolatile(tip);
134 // this->setStatusTip(tip);
135 }
136 
137 
139 {
140  if (mSelectedPoint.isValid())
141  {
142  int shift = 0;
143  if (event->key()==Qt::Key_Left)
144  shift = -1;
145  if (event->key()==Qt::Key_Right)
146  shift = +1;
147 
148  if (shift!=0)
149  {
150  ColorPoint newPoint = mSelectedPoint;
151  newPoint.intensity += shift;
152  this->moveSelectedPointTo(newPoint);
154  return;
155  }
156  }
157 
158  QWidget::keyPressEvent(event);
159 }
160 
162 {
163  // Get the screen (plot) position of this point
164  int retval =
165  static_cast<int>(mPlotArea.left() + mPlotArea.width() *
166  (intensity - mImage->getMin()) /
167  static_cast<double>(mImage->getRange()));
168  return retval;
169 }
170 
172 {
173  double i = mImage->getMin() + mImage->getRange() * double(screenX - mPlotArea.left()) /(mPlotArea.width()-1);
174  int retval = roundAwayFromZero(i);
175  return retval;
176 }
177 
179 {
180  // Don't do anything before we have an image
181  if (!mImage)
182  return;
183  QWidget::paintEvent(event);
184 
185  QPainter painter(this);
186 
187  // Fill with white background color and grey plot area background color
188  painter.fillRect(this->mFullArea, QColor(170, 170, 170));
189  painter.fillRect(this->mPlotArea, QColor(200, 200, 200));
190 
191  this->paintColorBar(painter);
192  this->paintColorPointsAndGenerateCache(painter);
193 }
194 
196 {
197  // Draw color-background
198 
199  // Use vtkColorTransferFunction for interpolation
200  vtkColorTransferFunctionPtr trFunc = mImageTF->generateColorTF();
201  int imin = mImageTF->getLevel() - mImageTF->getWindow()/2;
202  int imax = mImageTF->getLevel() + mImageTF->getWindow()/2;
203 
204  for (int x = this->mPlotArea.left(); x <= this->mPlotArea.right(); ++x)
205  {
206  int intensity = screenX2imageIntensity(x);
207  double* rgb = trFunc->GetColor(intensity);
208  painter.setPen(QColor::fromRgbF(rgb[0], rgb[1], rgb[2]));
209 
210  if (( imin <= intensity )&&( intensity <= imax ))
211  {
212  painter.drawLine(x, mPlotArea.top(), x, mPlotArea.bottom());
213  }
214  else
215  {
216  int areaHeight = mPlotArea.bottom() - mPlotArea.top();
217  int halfAreaTop = mPlotArea.top() + areaHeight / 4;
218  int halfAreaBottom = mPlotArea.bottom() - areaHeight / 4;
219  painter.drawLine(x, halfAreaTop, x, halfAreaBottom);
220  }
221  }
222 }
223 
225 {
226  // Go through each point and draw squares
227  ColorMap colorMap = mImageTF->getColorMap();
228  this->mPointRects.clear();
229  for (ColorMap::iterator colorPoint = colorMap.begin(); colorPoint != colorMap.end(); ++colorPoint)
230  {
231  // Get the screen (plot) position of this point
232  int screenX = this->imageIntensity2screenX(colorPoint->first);
233  // Draw the rectangle
234  QRect pointRect(screenX - mBorder, mFullArea.top(),
235  mBorder*2, mFullArea.height());
236 
237  if (colorPoint->first == mSelectedPoint.intensity)
238  {
239  QPen pen;
240  pen.setWidth(2);
241  painter.setPen(pen);
242  }
243  else
244  {
245  QPen pen;
246  painter.setPen(pen);
247  }
248 
249  painter.drawRect(pointRect);
250  this->mPointRects[colorPoint->first] = pointRect;
251  }
252 }
253 
255 {
256  QWidget::resizeEvent(evt);
257 
258  // Calculate areas
259  this->mFullArea = QRect(0, 0, width(), height());
260  this->mPlotArea = QRect(mBorder, mBorder,
261  width() - mBorder*2, height() - mBorder*2);
262 }
263 
265 {
266  std::map<int, QRect>::const_iterator it = mPointRects.begin();
267  for(;it != mPointRects.end(); ++it)
268  {
269  if (it->second.contains(pos))
270  {
271  ColorPoint retval;
272  retval.intensity = it->first;
273  ColorMap colorMap = mImageTF->getColorMap();
274  if (colorMap.find(retval.intensity) != colorMap.end())
275  retval.value = colorMap.find(retval.intensity)->second;
276  return retval;
277  }
278  }
279  return ColorPoint();
280 }
281 
283 {
284  if (mPointRects.begin()->first == intensity)
285  return true;
286  if (mPointRects.rbegin()->first == intensity)
287  return true;
288  return false;
289 }
290 
291 void TransferFunctionColorWidget::contextMenuEvent(QContextMenuEvent *event)
292 {
293  QMenu menu;
294  menu.addAction(mCustomColorAction);
295 
297 
299  {
300  menu.addSeparator();
301  menu.addAction(mRemoveColorAction);
302  }
303  this->update();
304 
305  menu.exec(event->globalPos());
306 }
307 
309 {
310  return this->getCurrentColorPoint(mCurrentClickPos.x());
311 }
312 
314 {
315  ColorPoint point;
316  if(!mImage)
317  return point;
318  point.intensity = screenX2imageIntensity(clickX);
319  point.intensity = constrainValue(point.intensity, mImage->getMin(), mImage->getMax());
320 
321  vtkColorTransferFunctionPtr trFunc = mImageTF->generateColorTF();
322 
323  double* rgb = trFunc->GetColor(point.intensity);
324  point.value = QColor::fromRgbF(rgb[0], rgb[1], rgb[2]);
325  return point;
326 }
327 
328 //void TransferFunctionColorWidget::moveCurrentPoint()
329 //{
330 // if(!mSelectedPoint.isValid())
331 // return;
332 
333 // std::pair<int,int> range = this->findAllowedMoveRangeAroundColorPoint(mSelectedPoint.intensity);
334 
335 // ColorPoint newColorPoint = this->getCurrentColorPoint();
336 // newColorPoint.value = mSelectedPoint.value;
337 // newColorPoint.intensity = constrainValue(newColorPoint.intensity, range.first, range.second);
338 
339 // mImageTF->moveColorPoint(mSelectedPoint.intensity, newColorPoint.intensity, newColorPoint.value);
340 // mSelectedPoint = newColorPoint;
341 // this->update();
342 //}
343 //void TransferFunctionColorWidget::moveCurrentPoint()
344 //{
345 // this->moveCurrentPoint(this->getCurrentColorPoint());
346 //}
347 
349 {
350  if(!mSelectedPoint.isValid())
351  return;
352 
353  std::pair<int,int> range = this->findAllowedMoveRangeAroundColorPoint(mSelectedPoint.intensity);
354 
355 // ColorPoint newColorPoint = this->getCurrentColorPoint();
356  newPoint.value = mSelectedPoint.value;
357  newPoint.intensity = constrainValue(newPoint.intensity, range.first, range.second);
358 
359  mImageTF->moveColorPoint(mSelectedPoint.intensity, newPoint.intensity, newPoint.value);
360  mSelectedPoint = newPoint;
361  this->update();
362 }
363 
364 std::pair<int,int> TransferFunctionColorWidget::findAllowedMoveRangeAroundColorPoint(int selectedPointIntensity)
365 {
366  // constrain new point intensity between the two neigbours
367  ColorMap colorMap = mImageTF->getColorMap();
368  ColorMap::iterator pointIterator = colorMap.find(selectedPointIntensity);
369 
370  std::pair<int,int> range(mImage->getMin(), mImage->getMax());
371  if (pointIterator!=colorMap.begin())
372  {
373  ColorMap::iterator prevPointIterator = pointIterator;
374  --prevPointIterator;
375  range.first = std::max(range.first, prevPointIterator->first + 1);
376  }
377 
378  ColorMap::iterator nextPointIterator = pointIterator;
379  ++nextPointIterator;
380  if (nextPointIterator!=colorMap.end())
381  {
382  range.second = std::min(range.second, nextPointIterator->first - 1);
383  }
384 
385  return range;
386 }
387 
389 {
390 // setColorSlotDelayed(); // crashed sporadically
391  QTimer::singleShot(1, this, SLOT(setColorSlotDelayed()));
392 }
393 
395 {
396  ColorPoint newPoint = mSelectedPoint;
397  if (!newPoint.isValid())
398  newPoint = getCurrentColorPoint();
399 
400  QColor result = QColorDialog::getColor( newPoint.value, this);
401 
402  if (result.isValid() && (result!=newPoint.value))
403  {
404  mImageTF->addColorPoint(newPoint.intensity, result);
405  }
406  this->update();
407 }
408 
410 {
411  if(!this->mSelectedPoint.isValid())
412  return;
413 
414  mImageTF->removeColorPoint(this->mSelectedPoint.intensity);
415 
416  this->update();
417 }
418 
419 }//namespace cx
virtual void mousePressEvent(QMouseEvent *event)
Reimplemented from superclass.
ReporterPtr reporter()
Definition: cxReporter.cpp:38
QString color2string(QColor color)
void paintColorPointsAndGenerateCache(QPainter &painter)
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:27
virtual void resizeEvent(QResizeEvent *evt)
Reimplemented from superclass.
ColorPoint getCurrentColorPoint()
Calculate the color point (position and color) based on clicked x coordinate.
std::map< int, QRect > mPointRects
Cache with all point rectangles.
boost::shared_ptr< class ActiveData > ActiveDataPtr
Definition: cxColorWidget.h:21
QAction * mRemoveColorAction
Action for removing a color.
virtual void keyPressEvent(QKeyEvent *event)
double roundAwayFromZero(double val)
Definition: cxMathUtils.cpp:14
virtual void paintEvent(QPaintEvent *event)
Reimplemented from superclass. Paints the transferfunction GUI.
int screenX2imageIntensity(int screenX)
Define a recommended size.
ColorPoint mSelectedPoint
The currently selected point.
vtkSmartPointer< class vtkColorTransferFunction > vtkColorTransferFunctionPtr
std::map< int, QColor > ColorMap
Definition: cxImage.h:36
std::pair< int, int > findAllowedMoveRangeAroundColorPoint(int selectedPointIntensity)
virtual void mouseReleaseEvent(QMouseEvent *event)
Reimplemented from superclass.
static ActiveImageProxyPtr New(ActiveDataPtr activeData)
double constrainValue(double val, double min, double max)
void setData(ImagePtr image, ImageTFDataPtr tfData)
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:88
Internal placeholder for a color point.
void contextMenuEvent(QContextMenuEvent *event)
Decides what happens when you rightclick in a view.
virtual void mouseMoveEvent(QMouseEvent *event)
Reimplemented from superclass.
boost::shared_ptr< class ImageTFData > ImageTFDataPtr
TransferFunctionColorWidget(ActiveDataPtr activeData, QWidget *parent)
QAction * mCustomColorAction
Action for choosing custom color.
int mBorder
The size of the border around the transferfunction. The size of the rectangles are mBorder * 2...
void activeImageTransferFunctionsChangedSlot()
Acts when the image&#39;s transfer function is changed.
Namespace for all CustusX production code.