CustusX  2023.01.05-dev+develop.0da12
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 
32 namespace cx
33 {
35  BaseWidget(parent, "transfer_function_color_widget", "Color Transfer Function"),
36  mBorder(5),
37  mCurrentClickPos(INT_MIN,INT_MIN)
38 {
39  this->setToolTip("Set the color part of a transfer function");
40  this->setFocusPolicy(Qt::StrongFocus);
42  connect(mActiveImageProxy.get(), SIGNAL(transferFunctionsChanged()), this, SLOT(activeImageTransferFunctionsChangedSlot()));
43 
45 
46  // Create actions
47  mCustomColorAction = new QAction(tr("Custom color..."), this);
48  mRemoveColorAction = new QAction(tr("Remove point"), this);
49 
50  connect(mCustomColorAction, SIGNAL(triggered(bool)), this, SLOT(setColorSlot()));
51  connect(mRemoveColorAction, SIGNAL(triggered()), this, SLOT(removeColorSlot()));
52 }
53 
55 {}
56 
58 {
59  this->setMouseTracking(true);
60 }
61 
63 {
64  this->setMouseTracking(false);
65 }
66 
67 
69 {
70  if (( mImage == image )&&( mImageTF==tfData ))
71  return;
72 
73  mImage = image;
74  mImageTF = tfData;
75  this->update();
76 }
77 
79 {
80  this->update();
81 }
82 
84 {
85  QWidget::mousePressEvent(event);
86  mCurrentClickPos = event->pos();
87 
88  if(event->button() == Qt::LeftButton)
89  {
91  this->update();
92  }
93 }
94 
96 {
97  QWidget::mouseReleaseEvent(event);
98  //we no longer need these values
99  //mSelectedPoint.reset();
100 }
101 
103 {
104  QWidget::mouseMoveEvent(event);
105 
106  if(event->buttons() == Qt::LeftButton)
107  {
108  // Update current screen point for use in moveCurrentAlphaPoint
109  mCurrentClickPos = event->pos();
110 // this->moveCurrentPoint();
112  }
113 
114  this->updateTooltip(event->pos());
115 }
116 
118 {
119  ColorPoint selected = this->selectPoint(pos);
120  if (!selected.isValid())
121  selected = this->getCurrentColorPoint(pos.x());
122  this->updateTooltip(selected);
123  this->update();
124 }
125 
127 {
128  QString tip = QString("color(%1)=(%2)").arg(point.intensity).arg(color2string(point.value));
129 // std::cout << "updated to " << tip << std::endl;
130  this->setToolTip(tip);
131  reporter()->sendVolatile(tip);
132 // this->setStatusTip(tip);
133 }
134 
135 
137 {
138  if (mSelectedPoint.isValid())
139  {
140  int shift = 0;
141  if (event->key()==Qt::Key_Left)
142  shift = -1;
143  if (event->key()==Qt::Key_Right)
144  shift = +1;
145 
146  if (shift!=0)
147  {
148  ColorPoint newPoint = mSelectedPoint;
149  newPoint.intensity += shift;
150  this->moveSelectedPointTo(newPoint);
152  return;
153  }
154  }
155 
156  QWidget::keyPressEvent(event);
157 }
158 
160 {
161  // Get the screen (plot) position of this point
162  int retval =
163  static_cast<int>(mPlotArea.left() + mPlotArea.width() *
164  (intensity - mImage->getMin()) /
165  static_cast<double>(mImage->getRange()));
166  return retval;
167 }
168 
170 {
171  double i = mImage->getMin() + mImage->getRange() * double(screenX - mPlotArea.left()) /(mPlotArea.width()-1);
172  int retval = int(std::lround(i));
173  return retval;
174 }
175 
177 {
178  // Don't do anything before we have an image
179  if (!mImage)
180  return;
181  QWidget::paintEvent(event);
182 
183  QPainter painter(this);
184 
185  // Fill with white background color and grey plot area background color
186  painter.fillRect(this->mFullArea, QColor(170, 170, 170));
187  painter.fillRect(this->mPlotArea, QColor(200, 200, 200));
188 
189  this->paintColorBar(painter);
190  this->paintColorPointsAndGenerateCache(painter);
191 }
192 
194 {
195  // Draw color-background
196 
197  // Use vtkColorTransferFunction for interpolation
198  vtkColorTransferFunctionPtr trFunc = mImageTF->generateColorTF();
199  long imin = std::lround(mImageTF->getLevel() - mImageTF->getWindow()/2);
200  long imax = std::lround(mImageTF->getLevel() + mImageTF->getWindow()/2);
201 
202  for (int x = this->mPlotArea.left(); x <= this->mPlotArea.right(); ++x)
203  {
204  int intensity = screenX2imageIntensity(x);
205  double* rgb = trFunc->GetColor(intensity);
206  painter.setPen(QColor::fromRgbF(rgb[0], rgb[1], rgb[2]));
207 
208  if (( imin <= intensity )&&( intensity <= imax ))
209  {
210  painter.drawLine(x, mPlotArea.top(), x, mPlotArea.bottom());
211  }
212  else
213  {
214  int areaHeight = mPlotArea.bottom() - mPlotArea.top();
215  int halfAreaTop = mPlotArea.top() + areaHeight / 4;
216  int halfAreaBottom = mPlotArea.bottom() - areaHeight / 4;
217  painter.drawLine(x, halfAreaTop, x, halfAreaBottom);
218  }
219  }
220 }
221 
223 {
224  // Go through each point and draw squares
225  ColorMap colorMap = mImageTF->getColorMap();
226  this->mPointRects.clear();
227  for (ColorMap::iterator colorPoint = colorMap.begin(); colorPoint != colorMap.end(); ++colorPoint)
228  {
229  // Get the screen (plot) position of this point
230  int screenX = this->imageIntensity2screenX(colorPoint->first);
231  // Draw the rectangle
232  QRect pointRect(screenX - mBorder, mFullArea.top(),
233  mBorder*2, mFullArea.height());
234 
235  if (colorPoint->first == mSelectedPoint.intensity)
236  {
237  QPen pen;
238  pen.setWidth(2);
239  painter.setPen(pen);
240  }
241  else
242  {
243  QPen pen;
244  painter.setPen(pen);
245  }
246 
247  painter.drawRect(pointRect);
248  this->mPointRects[colorPoint->first] = pointRect;
249  }
250 }
251 
253 {
254  QWidget::resizeEvent(evt);
255 
256  // Calculate areas
257  this->mFullArea = QRect(0, 0, width(), height());
258  this->mPlotArea = QRect(mBorder, mBorder,
259  width() - mBorder*2, height() - mBorder*2);
260 }
261 
263 {
264  std::map<int, QRect>::const_iterator it = mPointRects.begin();
265  for(;it != mPointRects.end(); ++it)
266  {
267  if (it->second.contains(pos))
268  {
269  ColorPoint retval;
270  retval.intensity = it->first;
271  ColorMap colorMap = mImageTF->getColorMap();
272  if (colorMap.find(retval.intensity) != colorMap.end())
273  retval.value = colorMap.find(retval.intensity)->second;
274  return retval;
275  }
276  }
277  return ColorPoint();
278 }
279 
281 {
282  if (mPointRects.begin()->first == intensity)
283  return true;
284  if (mPointRects.rbegin()->first == intensity)
285  return true;
286  return false;
287 }
288 
289 void TransferFunctionColorWidget::contextMenuEvent(QContextMenuEvent *event)
290 {
291  QMenu menu;
292  menu.addAction(mCustomColorAction);
293 
295 
297  {
298  menu.addSeparator();
299  menu.addAction(mRemoveColorAction);
300  }
301  this->update();
302 
303  menu.exec(event->globalPos());
304 }
305 
307 {
308  return this->getCurrentColorPoint(mCurrentClickPos.x());
309 }
310 
312 {
313  ColorPoint point;
314  if(!mImage)
315  return point;
316  point.intensity = screenX2imageIntensity(clickX);
317  point.intensity = constrainValue(point.intensity, mImage->getMin(), mImage->getMax());
318 
319  vtkColorTransferFunctionPtr trFunc = mImageTF->generateColorTF();
320 
321  double* rgb = trFunc->GetColor(point.intensity);
322  point.value = QColor::fromRgbF(rgb[0], rgb[1], rgb[2]);
323  return point;
324 }
325 
326 //void TransferFunctionColorWidget::moveCurrentPoint()
327 //{
328 // if(!mSelectedPoint.isValid())
329 // return;
330 
331 // std::pair<int,int> range = this->findAllowedMoveRangeAroundColorPoint(mSelectedPoint.intensity);
332 
333 // ColorPoint newColorPoint = this->getCurrentColorPoint();
334 // newColorPoint.value = mSelectedPoint.value;
335 // newColorPoint.intensity = constrainValue(newColorPoint.intensity, range.first, range.second);
336 
337 // mImageTF->moveColorPoint(mSelectedPoint.intensity, newColorPoint.intensity, newColorPoint.value);
338 // mSelectedPoint = newColorPoint;
339 // this->update();
340 //}
341 //void TransferFunctionColorWidget::moveCurrentPoint()
342 //{
343 // this->moveCurrentPoint(this->getCurrentColorPoint());
344 //}
345 
347 {
348  if(!mSelectedPoint.isValid())
349  return;
350 
351  std::pair<int,int> range = this->findAllowedMoveRangeAroundColorPoint(mSelectedPoint.intensity);
352 
353 // ColorPoint newColorPoint = this->getCurrentColorPoint();
354  newPoint.value = mSelectedPoint.value;
355  newPoint.intensity = constrainValue(newPoint.intensity, range.first, range.second);
356 
357  mImageTF->moveColorPoint(mSelectedPoint.intensity, newPoint.intensity, newPoint.value);
358  mSelectedPoint = newPoint;
359  this->update();
360 }
361 
362 std::pair<int,int> TransferFunctionColorWidget::findAllowedMoveRangeAroundColorPoint(int selectedPointIntensity)
363 {
364  // constrain new point intensity between the two neigbours
365  ColorMap colorMap = mImageTF->getColorMap();
366  ColorMap::iterator pointIterator = colorMap.find(selectedPointIntensity);
367 
368  std::pair<int,int> range(mImage->getMin(), mImage->getMax());
369  if (pointIterator!=colorMap.begin())
370  {
371  ColorMap::iterator prevPointIterator = pointIterator;
372  --prevPointIterator;
373  range.first = std::max(range.first, prevPointIterator->first + 1);
374  }
375 
376  ColorMap::iterator nextPointIterator = pointIterator;
377  ++nextPointIterator;
378  if (nextPointIterator!=colorMap.end())
379  {
380  range.second = std::min(range.second, nextPointIterator->first - 1);
381  }
382 
383  return range;
384 }
385 
387 {
388 // setColorSlotDelayed(); // crashed sporadically
389  QTimer::singleShot(1, this, SLOT(setColorSlotDelayed()));
390 }
391 
393 {
394  ColorPoint newPoint = mSelectedPoint;
395  if (!newPoint.isValid())
396  newPoint = getCurrentColorPoint();
397 
398  QColor result = QColorDialog::getColor( newPoint.value, this);
399 
400  if (result.isValid() && (result!=newPoint.value))
401  {
402  mImageTF->addColorPoint(newPoint.intensity, result);
403  }
404  this->update();
405 }
406 
408 {
409  if(!this->mSelectedPoint.isValid())
410  return;
411 
412  mImageTF->removeColorPoint(this->mSelectedPoint.intensity);
413 
414  this->update();
415 }
416 
417 }//namespace cx
virtual void mousePressEvent(QMouseEvent *event)
Reimplemented from superclass.
ReporterPtr reporter()
Definition: cxReporter.cpp:36
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)
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.