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