CustusX  15.8
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->setToolTip("Set the color part of a transfer function");
62  this->setFocusPolicy(Qt::StrongFocus);
63  mActiveImageProxy = ActiveImageProxy::New(patientModelService);
64  connect(mActiveImageProxy.get(), SIGNAL(transferFunctionsChanged()), this, SLOT(activeImageTransferFunctionsChangedSlot()));
65 
67 
68  // Create actions
69  mCustomColorAction = new QAction(tr("Custom color..."), this);
70  mRemoveColorAction = new QAction(tr("Remove point"), this);
71 
72  connect(mCustomColorAction, SIGNAL(triggered(bool)), this, SLOT(setColorSlot()));
73  connect(mRemoveColorAction, SIGNAL(triggered()), this, SLOT(removeColorSlot()));
74 }
75 
77 {}
78 
80 {
81  this->setMouseTracking(true);
82 }
83 
85 {
86  this->setMouseTracking(false);
87 }
88 
89 
91 {
92  if (( mImage == image )&&( mImageTF==tfData ))
93  return;
94 
95  mImage = image;
96  mImageTF = tfData;
97  this->update();
98 }
99 
101 {
102  this->update();
103 }
104 
106 {
107  QWidget::mousePressEvent(event);
108  mCurrentClickPos = event->pos();
109 
110  if(event->button() == Qt::LeftButton)
111  {
113  this->update();
114  }
115 }
116 
118 {
119  QWidget::mouseReleaseEvent(event);
120  //we no longer need these values
121  //mSelectedPoint.reset();
122 }
123 
125 {
126  QWidget::mouseMoveEvent(event);
127 
128  if(event->buttons() == Qt::LeftButton)
129  {
130  // Update current screen point for use in moveCurrentAlphaPoint
131  mCurrentClickPos = event->pos();
132 // this->moveCurrentPoint();
134  }
135 
136  this->updateTooltip(event->pos());
137 }
138 
140 {
141  ColorPoint selected = this->selectPoint(pos);
142  if (!selected.isValid())
143  selected = this->getCurrentColorPoint(pos.x());
144  this->updateTooltip(selected);
145  this->update();
146 }
147 
149 {
150  QString tip = QString("color(%1)=(%2)").arg(point.intensity).arg(color2string(point.value));
151 // std::cout << "updated to " << tip << std::endl;
152  this->setToolTip(tip);
153  reporter()->sendVolatile(tip);
154 // this->setStatusTip(tip);
155 }
156 
157 
159 {
160  if (mSelectedPoint.isValid())
161  {
162  int shift = 0;
163  if (event->key()==Qt::Key_Left)
164  shift = -1;
165  if (event->key()==Qt::Key_Right)
166  shift = +1;
167 
168  if (shift!=0)
169  {
170  ColorPoint newPoint = mSelectedPoint;
171  newPoint.intensity += shift;
172  this->moveSelectedPointTo(newPoint);
174  return;
175  }
176  }
177 
178  QWidget::keyPressEvent(event);
179 }
180 
182 {
183  // Get the screen (plot) position of this point
184  int retval =
185  static_cast<int>(mPlotArea.left() + mPlotArea.width() *
186  (intensity - mImage->getMin()) /
187  static_cast<double>(mImage->getRange()));
188  return retval;
189 }
190 
192 {
193 // int retval =
194  double i = mImage->getMin() + mImage->getRange() * double(screenX - mPlotArea.left()) /(mPlotArea.width()-1);
195  int retval = int(i+0.5);
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  point.intensity = screenX2imageIntensity(clickX);
338  point.intensity = constrainValue(point.intensity, mImage->getMin(), mImage->getMax());
339 
340  vtkColorTransferFunctionPtr trFunc = mImageTF->generateColorTF();
341 
342  double* rgb = trFunc->GetColor(point.intensity);
343  point.value = QColor::fromRgbF(rgb[0], rgb[1], rgb[2]);
344  return point;
345 }
346 
347 //void TransferFunctionColorWidget::moveCurrentPoint()
348 //{
349 // if(!mSelectedPoint.isValid())
350 // return;
351 
352 // std::pair<int,int> range = this->findAllowedMoveRangeAroundColorPoint(mSelectedPoint.intensity);
353 
354 // ColorPoint newColorPoint = this->getCurrentColorPoint();
355 // newColorPoint.value = mSelectedPoint.value;
356 // newColorPoint.intensity = constrainValue(newColorPoint.intensity, range.first, range.second);
357 
358 // mImageTF->moveColorPoint(mSelectedPoint.intensity, newColorPoint.intensity, newColorPoint.value);
359 // mSelectedPoint = newColorPoint;
360 // this->update();
361 //}
362 //void TransferFunctionColorWidget::moveCurrentPoint()
363 //{
364 // this->moveCurrentPoint(this->getCurrentColorPoint());
365 //}
366 
368 {
369  if(!mSelectedPoint.isValid())
370  return;
371 
372  std::pair<int,int> range = this->findAllowedMoveRangeAroundColorPoint(mSelectedPoint.intensity);
373 
374 // ColorPoint newColorPoint = this->getCurrentColorPoint();
375  newPoint.value = mSelectedPoint.value;
376  newPoint.intensity = constrainValue(newPoint.intensity, range.first, range.second);
377 
378  mImageTF->moveColorPoint(mSelectedPoint.intensity, newPoint.intensity, newPoint.value);
379  mSelectedPoint = newPoint;
380  this->update();
381 }
382 
383 std::pair<int,int> TransferFunctionColorWidget::findAllowedMoveRangeAroundColorPoint(int selectedPointIntensity)
384 {
385  // constrain new point intensity between the two neigbours
386  ColorMap colorMap = mImageTF->getColorMap();
387  ColorMap::iterator pointIterator = colorMap.find(selectedPointIntensity);
388 
389  std::pair<int,int> range(mImage->getMin(), mImage->getMax());
390  if (pointIterator!=colorMap.begin())
391  {
392  ColorMap::iterator prevPointIterator = pointIterator;
393  --prevPointIterator;
394  range.first = std::max(range.first, prevPointIterator->first + 1);
395  }
396 
397  ColorMap::iterator nextPointIterator = pointIterator;
398  ++nextPointIterator;
399  if (nextPointIterator!=colorMap.end())
400  {
401  range.second = std::min(range.second, nextPointIterator->first - 1);
402  }
403 
404  return range;
405 }
406 
408 {
409 // setColorSlotDelayed(); // crashed sporadically
410  QTimer::singleShot(1, this, SLOT(setColorSlotDelayed()));
411 }
412 
414 {
415  ColorPoint newPoint = mSelectedPoint;
416  if (!newPoint.isValid())
417  newPoint = getCurrentColorPoint();
418 
419  QColor result = QColorDialog::getColor( newPoint.value, this);
420 
421  if (result.isValid() && (result!=newPoint.value))
422  {
423  mImageTF->addColorPoint(newPoint.intensity, result);
424  }
425  this->update();
426 }
427 
429 {
430  if(!this->mSelectedPoint.isValid())
431  return;
432 
433  mImageTF->removeColorPoint(this->mSelectedPoint.intensity);
434 
435  this->update();
436 }
437 
438 }//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.
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:57
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.
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.