CustusX  16.5
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
34 
35 #include <vtkColorTransferFunction.h>
36 
37 #include <limits.h>
38 #include <QPainter>
39 #include <QPen>
40 #include <QColor>
41 #include <QMouseEvent>
42 #include <QColorDialog>
43 #include <QMenu>
44 #include <QTimer>
45 #include "cxImageTF3D.h"
46 #include "cxImageTFData.h"
47 #include "cxUtilHelpers.h"
48 #include "cxImageLUT2D.h"
49 #include "cxLogger.h"
50 #include "cxTypeConversions.h"
51 #include "cxReporter.h"
52 #include "cxMathUtils.h"
53 
54 
55 namespace cx
56 {
58  BaseWidget(parent, "TransferFunctionColorWidget", "Color Transfer Function"),
59  mCurrentClickPos(INT_MIN,INT_MIN),
60  mBorder(5)
61 {
62  this->setToolTip("Set the color part of a transfer function");
63  this->setFocusPolicy(Qt::StrongFocus);
65  connect(mActiveImageProxy.get(), SIGNAL(transferFunctionsChanged()), this, SLOT(activeImageTransferFunctionsChangedSlot()));
66 
68 
69  // Create actions
70  mCustomColorAction = new QAction(tr("Custom color..."), this);
71  mRemoveColorAction = new QAction(tr("Remove point"), this);
72 
73  connect(mCustomColorAction, SIGNAL(triggered(bool)), this, SLOT(setColorSlot()));
74  connect(mRemoveColorAction, SIGNAL(triggered()), this, SLOT(removeColorSlot()));
75 }
76 
78 {}
79 
81 {
82  this->setMouseTracking(true);
83 }
84 
86 {
87  this->setMouseTracking(false);
88 }
89 
90 
92 {
93  if (( mImage == image )&&( mImageTF==tfData ))
94  return;
95 
96  mImage = image;
97  mImageTF = tfData;
98  this->update();
99 }
100 
102 {
103  this->update();
104 }
105 
107 {
108  QWidget::mousePressEvent(event);
109  mCurrentClickPos = event->pos();
110 
111  if(event->button() == Qt::LeftButton)
112  {
114  this->update();
115  }
116 }
117 
119 {
120  QWidget::mouseReleaseEvent(event);
121  //we no longer need these values
122  //mSelectedPoint.reset();
123 }
124 
126 {
127  QWidget::mouseMoveEvent(event);
128 
129  if(event->buttons() == Qt::LeftButton)
130  {
131  // Update current screen point for use in moveCurrentAlphaPoint
132  mCurrentClickPos = event->pos();
133 // this->moveCurrentPoint();
135  }
136 
137  this->updateTooltip(event->pos());
138 }
139 
141 {
142  ColorPoint selected = this->selectPoint(pos);
143  if (!selected.isValid())
144  selected = this->getCurrentColorPoint(pos.x());
145  this->updateTooltip(selected);
146  this->update();
147 }
148 
150 {
151  QString tip = QString("color(%1)=(%2)").arg(point.intensity).arg(color2string(point.value));
152 // std::cout << "updated to " << tip << std::endl;
153  this->setToolTip(tip);
154  reporter()->sendVolatile(tip);
155 // this->setStatusTip(tip);
156 }
157 
158 
160 {
161  if (mSelectedPoint.isValid())
162  {
163  int shift = 0;
164  if (event->key()==Qt::Key_Left)
165  shift = -1;
166  if (event->key()==Qt::Key_Right)
167  shift = +1;
168 
169  if (shift!=0)
170  {
171  ColorPoint newPoint = mSelectedPoint;
172  newPoint.intensity += shift;
173  this->moveSelectedPointTo(newPoint);
175  return;
176  }
177  }
178 
179  QWidget::keyPressEvent(event);
180 }
181 
183 {
184  // Get the screen (plot) position of this point
185  int retval =
186  static_cast<int>(mPlotArea.left() + mPlotArea.width() *
187  (intensity - mImage->getMin()) /
188  static_cast<double>(mImage->getRange()));
189  return retval;
190 }
191 
193 {
194  double i = mImage->getMin() + mImage->getRange() * double(screenX - mPlotArea.left()) /(mPlotArea.width()-1);
195  int retval = roundAwayFromZero(i);
196  return retval;
197 }
198 
200 {
201  // Don't do anything before we have an image
202  if (!mImage)
203  return;
204  QWidget::paintEvent(event);
205 
206  QPainter painter(this);
207 
208  // Fill with white background color and grey plot area background color
209  painter.fillRect(this->mFullArea, QColor(170, 170, 170));
210  painter.fillRect(this->mPlotArea, QColor(200, 200, 200));
211 
212  this->paintColorBar(painter);
213  this->paintColorPointsAndGenerateCache(painter);
214 }
215 
217 {
218  // Draw color-background
219 
220  // Use vtkColorTransferFunction for interpolation
221  vtkColorTransferFunctionPtr trFunc = mImageTF->generateColorTF();
222  int imin = mImageTF->getLevel() - mImageTF->getWindow()/2;
223  int imax = mImageTF->getLevel() + mImageTF->getWindow()/2;
224 
225  for (int x = this->mPlotArea.left(); x <= this->mPlotArea.right(); ++x)
226  {
227  int intensity = screenX2imageIntensity(x);
228  double* rgb = trFunc->GetColor(intensity);
229  painter.setPen(QColor::fromRgbF(rgb[0], rgb[1], rgb[2]));
230 
231  if (( imin <= intensity )&&( intensity <= imax ))
232  {
233  painter.drawLine(x, mPlotArea.top(), x, mPlotArea.bottom());
234  }
235  else
236  {
237  int areaHeight = mPlotArea.bottom() - mPlotArea.top();
238  int halfAreaTop = mPlotArea.top() + areaHeight / 4;
239  int halfAreaBottom = mPlotArea.bottom() - areaHeight / 4;
240  painter.drawLine(x, halfAreaTop, x, halfAreaBottom);
241  }
242  }
243 }
244 
246 {
247  // Go through each point and draw squares
248  ColorMap colorMap = mImageTF->getColorMap();
249  this->mPointRects.clear();
250  for (ColorMap::iterator colorPoint = colorMap.begin(); colorPoint != colorMap.end(); ++colorPoint)
251  {
252  // Get the screen (plot) position of this point
253  int screenX = this->imageIntensity2screenX(colorPoint->first);
254  // Draw the rectangle
255  QRect pointRect(screenX - mBorder, mFullArea.top(),
256  mBorder*2, mFullArea.height());
257 
258  if (colorPoint->first == mSelectedPoint.intensity)
259  {
260  QPen pen;
261  pen.setWidth(2);
262  painter.setPen(pen);
263  }
264  else
265  {
266  QPen pen;
267  painter.setPen(pen);
268  }
269 
270  painter.drawRect(pointRect);
271  this->mPointRects[colorPoint->first] = pointRect;
272  }
273 }
274 
276 {
277  QWidget::resizeEvent(evt);
278 
279  // Calculate areas
280  this->mFullArea = QRect(0, 0, width(), height());
281  this->mPlotArea = QRect(mBorder, mBorder,
282  width() - mBorder*2, height() - mBorder*2);
283 }
284 
286 {
287  std::map<int, QRect>::const_iterator it = mPointRects.begin();
288  for(;it != mPointRects.end(); ++it)
289  {
290  if (it->second.contains(pos))
291  {
292  ColorPoint retval;
293  retval.intensity = it->first;
294  ColorMap colorMap = mImageTF->getColorMap();
295  if (colorMap.find(retval.intensity) != colorMap.end())
296  retval.value = colorMap.find(retval.intensity)->second;
297  return retval;
298  }
299  }
300  return ColorPoint();
301 }
302 
304 {
305  if (mPointRects.begin()->first == intensity)
306  return true;
307  if (mPointRects.rbegin()->first == intensity)
308  return true;
309  return false;
310 }
311 
312 void TransferFunctionColorWidget::contextMenuEvent(QContextMenuEvent *event)
313 {
314  QMenu menu;
315  menu.addAction(mCustomColorAction);
316 
318 
320  {
321  menu.addSeparator();
322  menu.addAction(mRemoveColorAction);
323  }
324  this->update();
325 
326  menu.exec(event->globalPos());
327 }
328 
330 {
331  return this->getCurrentColorPoint(mCurrentClickPos.x());
332 }
333 
335 {
336  ColorPoint point;
337  if(!mImage)
338  return point;
339  point.intensity = screenX2imageIntensity(clickX);
340  point.intensity = constrainValue(point.intensity, mImage->getMin(), mImage->getMax());
341 
342  vtkColorTransferFunctionPtr trFunc = mImageTF->generateColorTF();
343 
344  double* rgb = trFunc->GetColor(point.intensity);
345  point.value = QColor::fromRgbF(rgb[0], rgb[1], rgb[2]);
346  return point;
347 }
348 
349 //void TransferFunctionColorWidget::moveCurrentPoint()
350 //{
351 // if(!mSelectedPoint.isValid())
352 // return;
353 
354 // std::pair<int,int> range = this->findAllowedMoveRangeAroundColorPoint(mSelectedPoint.intensity);
355 
356 // ColorPoint newColorPoint = this->getCurrentColorPoint();
357 // newColorPoint.value = mSelectedPoint.value;
358 // newColorPoint.intensity = constrainValue(newColorPoint.intensity, range.first, range.second);
359 
360 // mImageTF->moveColorPoint(mSelectedPoint.intensity, newColorPoint.intensity, newColorPoint.value);
361 // mSelectedPoint = newColorPoint;
362 // this->update();
363 //}
364 //void TransferFunctionColorWidget::moveCurrentPoint()
365 //{
366 // this->moveCurrentPoint(this->getCurrentColorPoint());
367 //}
368 
370 {
371  if(!mSelectedPoint.isValid())
372  return;
373 
374  std::pair<int,int> range = this->findAllowedMoveRangeAroundColorPoint(mSelectedPoint.intensity);
375 
376 // ColorPoint newColorPoint = this->getCurrentColorPoint();
377  newPoint.value = mSelectedPoint.value;
378  newPoint.intensity = constrainValue(newPoint.intensity, range.first, range.second);
379 
380  mImageTF->moveColorPoint(mSelectedPoint.intensity, newPoint.intensity, newPoint.value);
381  mSelectedPoint = newPoint;
382  this->update();
383 }
384 
385 std::pair<int,int> TransferFunctionColorWidget::findAllowedMoveRangeAroundColorPoint(int selectedPointIntensity)
386 {
387  // constrain new point intensity between the two neigbours
388  ColorMap colorMap = mImageTF->getColorMap();
389  ColorMap::iterator pointIterator = colorMap.find(selectedPointIntensity);
390 
391  std::pair<int,int> range(mImage->getMin(), mImage->getMax());
392  if (pointIterator!=colorMap.begin())
393  {
394  ColorMap::iterator prevPointIterator = pointIterator;
395  --prevPointIterator;
396  range.first = std::max(range.first, prevPointIterator->first + 1);
397  }
398 
399  ColorMap::iterator nextPointIterator = pointIterator;
400  ++nextPointIterator;
401  if (nextPointIterator!=colorMap.end())
402  {
403  range.second = std::min(range.second, nextPointIterator->first - 1);
404  }
405 
406  return range;
407 }
408 
410 {
411 // setColorSlotDelayed(); // crashed sporadically
412  QTimer::singleShot(1, this, SLOT(setColorSlotDelayed()));
413 }
414 
416 {
417  ColorPoint newPoint = mSelectedPoint;
418  if (!newPoint.isValid())
419  newPoint = getCurrentColorPoint();
420 
421  QColor result = QColorDialog::getColor( newPoint.value, this);
422 
423  if (result.isValid() && (result!=newPoint.value))
424  {
425  mImageTF->addColorPoint(newPoint.intensity, result);
426  }
427  this->update();
428 }
429 
431 {
432  if(!this->mSelectedPoint.isValid())
433  return;
434 
435  mImageTF->removeColorPoint(this->mSelectedPoint.intensity);
436 
437  this->update();
438 }
439 
440 }//namespace cx
virtual void mousePressEvent(QMouseEvent *event)
Reimplemented from superclass.
ReporterPtr reporter()
Definition: cxReporter.cpp:59
QString color2string(QColor color)
void paintColorPointsAndGenerateCache(QPainter &painter)
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
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:42
QAction * mRemoveColorAction
Action for removing a color.
virtual void keyPressEvent(QKeyEvent *event)
double roundAwayFromZero(double val)
Definition: cxMathUtils.cpp:35
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:57
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:108
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's transfer function is changed.