Fraxinus  18.10
An IGT application
cxTransferFunctionAlphaWidget.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 <limits.h>
15 #include <vtkImageData.h>
16 #include <vtkPointData.h>
17 #include <vtkImageAccumulate.h>
18 #include <QPainter>
19 #include <QPen>
20 #include <QColor>
21 #include <QBrush>
22 #include <QMouseEvent>
23 #include "cxImageTF3D.h"
24 #include "cxImageTFData.h"
25 #include "cxLogger.h"
26 #include "cxUtilHelpers.h"
27 #include "cxTypeConversions.h"
28 #include "vtkDataArray.h"
29 #include "cxReporter.h"
30 #include "cxMathUtils.h"
31 
32 
33 namespace cx
34 {
36  BaseWidget(parent, "transfer_function_alpha_widget", "Alpha Transfer Function"),
37  mBorder(5),
38  mReadOnly(false)
39 {
40  this->setToolTip("Set the alpha part of a transfer function");
41  this->setFocusPolicy(Qt::StrongFocus);
42 
44  connect(mActiveImageProxy.get(), SIGNAL(transferFunctionsChanged()), this, SLOT(activeImageTransferFunctionsChangedSlot()));
45 
47 }
49 {}
50 
52 {
53  if (( mImage == image )&&( mImageTF==tfData ))
54  return;
55 
56  mImage = image;
57  mImageTF = tfData;
58  this->update();
59 }
60 
62 {
63  mReadOnly = readOnly;
64 }
65 
67 {
68  this->update();
69 }
70 
72 {
73  this->setMouseTracking(true);
74 }
75 
77 {
78  this->setMouseTracking(false);
79 }
80 
82 {
83  if(mReadOnly)
84  return;
85  QWidget::mousePressEvent(event);
86 
87  if(event->button() == Qt::LeftButton)
88  {
89  mSelectedAlphaPoint = this->selectPoint(event->pos());
90  }
91  else if(event->button() == Qt::RightButton)
92  {
93  this->toggleSelectedPoint(event->pos());
94  }
95 
96  this->update();
97 }
98 
100 {
101  if(mReadOnly)
102  return;
103  QWidget::mouseReleaseEvent(event);
104 // mCurrentAlphaPoint.reset();
105 }
106 
108 {
109  if (!mImage)
110  return;
111 
112  this->updateTooltip(event->pos());
113  if(mReadOnly) //Only show tool tip if readOnly
114  return;
115 
116  QWidget::mouseMoveEvent(event);
117 
118  if(event->buttons() == Qt::LeftButton)
119  {
120  this->moveCurrentAlphaPoint(this->getCurrentAlphaPoint(event->pos()));
121  this->update();
122  }
123 }
124 
126 {
128  {
129  int shift = 0;
130  int alphaShift = 0;
131  if (event->key()==Qt::Key_Left)
132  shift = -1;
133  if (event->key()==Qt::Key_Right)
134  shift = +1;
135  if (event->key()==Qt::Key_Down)
136  alphaShift = -1;
137  if (event->key()==Qt::Key_Up)
138  alphaShift = +1;
139 
140  if ((shift!=0) || (alphaShift!=0))
141  {
142  AlphaPoint newPoint = mSelectedAlphaPoint;
143  newPoint.position += shift;
144  newPoint.value += alphaShift;
145  this->moveCurrentAlphaPoint(newPoint);
147  this->update();
148  return;
149  }
150  }
151 
152  QWidget::keyPressEvent(event);
153 }
154 
156 {
157  AlphaPoint selected = this->selectPoint(pos);
158  if (!selected.isValid())
159  selected = this->getCurrentAlphaPoint(pos);
160  this->updateTooltip(selected);
161  this->update();
162 }
163 
165 {
166  QString tip = QString("alpha(%1)=%2").arg(point.position).arg(double(point.value)/255, 0, 'f', 2);
167  this->setToolTip(tip);
168  reporter()->sendVolatile(tip);
169 }
170 
171 
173 {
174  QWidget::paintEvent(event);
175  QPainter painter(this);
176  this->clearBackground(painter);
177 
178  if (!mImage)
179  return;
180 
181  this->paintHistogram(painter);
182  this->paintOpacityGraph(painter);
183 }
184 
185 void TransferFunctionAlphaWidget::clearBackground(QPainter& painter)
186 {
187  // Fill with white global background color and grey plot area background color
188  const QBrush frameBrush = QBrush(QColor(170, 170, 170));
189  const QBrush backgroundBrush = QBrush(QColor(200, 200, 200));
190  painter.fillRect(this->mFullArea, frameBrush);
191  painter.fillRect(this->mPlotArea, backgroundBrush);
192 
193 }
194 
196 {
197  QPen pointPen, pointLinePen;
198  pointPen.setColor(QColor(0, 0, 150));
199  pointLinePen.setColor(QColor(150, 100, 100));
200 
201  // Go through each point and draw squares and lines
202  IntIntMap opacityMap = mImageTF->getOpacityMap();
203 
204  QPoint lastScreenPoint;
205  this->mPointRects.clear();
206  for (IntIntMap::iterator opPoint = opacityMap.begin();
207  opPoint != opacityMap.end();
208  ++opPoint)
209  {
210  // Get the screen (plot) position of this point
211  AlphaPoint pt(opPoint->first, opPoint->second);
212  QPoint screenPoint = this->alpha2screen(pt);
213 
214  // draw line from left edge to first point:
215  if (opPoint==opacityMap.begin())
216  {
217  lastScreenPoint = QPoint(mPlotArea.left(), screenPoint.y());
218  }
219 
220  // Draw line from previous point
221  painter.setPen(pointLinePen);
222  painter.drawLine(lastScreenPoint, screenPoint);
223 
224  // Draw the rectangle
225  QRect pointRect(screenPoint.x() - mBorder, screenPoint.y() - mBorder,
226  mBorder*2, mBorder*2);
227  if (opPoint->first==mSelectedAlphaPoint.position)
228  {
229  pointPen.setWidth(2);
230  painter.setPen(pointPen);
231  }
232  else
233  {
234  pointPen.setWidth(1);
235  painter.setPen(pointPen);
236  }
237  painter.drawRect(pointRect);
238  this->mPointRects[opPoint->first] = pointRect;
239 
240  // Store the point
241  lastScreenPoint = screenPoint;
242  }
243 
244  // draw a line from the last point to the right end
245  QPoint screenPoint(mPlotArea.right(), lastScreenPoint.y());
246  painter.setPen(pointLinePen);
247  painter.drawLine(lastScreenPoint, screenPoint);
248 
249 }
250 
252 {
253  QPoint screenPoint = QPoint(
254  static_cast<int>(mPlotArea.left() + mPlotArea.width() *
255  (pt.position - mImage->getMin()) /
256  static_cast<double>(mImage->getRange())),
257  static_cast<int>(mPlotArea.bottom() - mPlotArea.height() *
258  pt.value /
259  static_cast<double>(mImage->getMaxAlphaValue())) );
260  return screenPoint;
261 }
262 
264 {
265  // Draw histogram
266  // with log compression
267 
268  vtkImageAccumulatePtr histogram = mImage->getHistogram();
269  int histogramSize = histogram->GetComponentExtent()[1] -
270  histogram->GetComponentExtent()[0];
271 
272  painter.setPen(QColor(140, 140, 210));
273 
274  double numElementsInBinWithMostElements = log(histogram->GetOutput()->GetPointData()->GetScalars()->GetRange()[1]+1);
275  double barHeightMult = (this->height() - mBorder*2) / numElementsInBinWithMostElements;
276 
277  double posMult = (this->width() - mBorder*2) / double(histogramSize);
278  for (int i = mImage->getMin(); i <= mImage->getMax(); i++)
279  {
280  int x = ((i- mImage->getMin()) * posMult); //Offset with min value
281  int y = log(double(static_cast<int*>(histogram->GetOutput()->GetScalarPointer(i - mImage->getMin(), 0, 0))[0]+1)) * barHeightMult;
282  if (y > 0)
283  {
284  painter.drawLine(x + mBorder, height() - mBorder,
285  x + mBorder, height() - mBorder - y);
286  }
287  }
288 }
289 
290 
292 {
293  QWidget::resizeEvent(evt);
294 
295  // Calculate areas
296  this->mFullArea = QRect(0, 0, width(), height());
297  this->mPlotArea = QRect(mBorder, mBorder,
298  width() - mBorder*2, height() - mBorder*2);
299 }
300 
302 {
303  std::map<int, QRect>::iterator it = mPointRects.begin();
304  for(;it != mPointRects.end(); ++it)
305  {
306  if (it->second.contains(pos))
307  {
308  AlphaPoint retval;
309  retval.position = it->first;
310  IntIntMap opactiyMap = mImageTF->getOpacityMap();
311  if (opactiyMap.find(retval.position) != opactiyMap.end())
312  retval.value = opactiyMap.find(retval.position)->second;
313  return retval;
314  }
315  }
316 
317  return AlphaPoint();
318 }
319 
321 {
322  if (mPointRects.begin()->first == intensity)
323  return true;
324  if (mPointRects.rbegin()->first == intensity)
325  return true;
326  return false;
327 }
328 
330 {
331  AlphaPoint point;
332 
333  double dposition = mImage->getMin() + mImage->getRange() * double(pos.x() - mPlotArea.left()) / mPlotArea.width();
334  double dvalue = mImage->getMaxAlphaValue() * double(mPlotArea.bottom() - pos.y())/mPlotArea.height();
335  point.position = roundAwayFromZero(dposition);
336  point.value = roundAwayFromZero(dvalue);
337 
338  point.position = constrainValue(point.position, mImage->getMin(), mImage->getMax());
339  point.value = constrainValue(point.value, 0, mImage->getMaxAlphaValue());
340 
341  return point;
342 }
343 
345 {
346  if(!mImage)
347  return;
348  mSelectedAlphaPoint = this->selectPoint(pos);
350  {
351  // Outside any of the rectangles
352  AlphaPoint point = getCurrentAlphaPoint(pos);
353  mImageTF->addAlphaPoint(point.position,point.value);
354  mSelectedAlphaPoint = this->selectPoint(pos);
355  }
356  else if(!this->isEndpoint(mSelectedAlphaPoint.position))
357  {
358  // Inside one of the rectangles
360  mImageTF->removeAlphaPoint(mSelectedAlphaPoint.position);
362  }
363 
364  this->update();
365 }
366 
368 {
370  return;
371 
372  newAlphaPoint.value = constrainValue(newAlphaPoint.value, 0, 255);
373 
374  std::pair<int,int> range = this->findAllowedMoveRangeAroundAlphaPoint(mSelectedAlphaPoint.position);
375  newAlphaPoint.position = constrainValue(newAlphaPoint.position, range.first, range.second);
376 
377  mImageTF->moveAlphaPoint(mSelectedAlphaPoint.position, newAlphaPoint.position, newAlphaPoint.value);
378 
379  mSelectedAlphaPoint = newAlphaPoint;
380  this->update();
381 }
382 
383 std::pair<int,int> TransferFunctionAlphaWidget::findAllowedMoveRangeAroundAlphaPoint(int selectedPointIntensity)
384 {
385  // constrain new point intensity between the two neigbours
386  IntIntMap opacityMap = mImageTF->getOpacityMap();
387  IntIntMap::iterator pointIterator = opacityMap.find(selectedPointIntensity);
388 
389  std::pair<int,int> range(mImage->getMin(), mImage->getMax());
390  if (pointIterator!=opacityMap.begin())
391  {
392  IntIntMap::iterator prevPointIterator = pointIterator;
393  --prevPointIterator;
394  range.first = std::max(range.first, prevPointIterator->first + 1);
395  }
396 
397  IntIntMap::iterator nextPointIterator = pointIterator;
398  ++nextPointIterator;
399  if (nextPointIterator!=opacityMap.end())
400  {
401  range.second = std::min(range.second, nextPointIterator->first - 1);
402  }
403 
404  return range;
405 }
406 
407 
408 }//namespace cx
virtual void keyPressEvent(QKeyEvent *event)
ReporterPtr reporter()
Definition: cxReporter.cpp:38
virtual void mouseReleaseEvent(QMouseEvent *event)
Reimplemented from superclass.
vtkSmartPointer< class vtkImageAccumulate > vtkImageAccumulatePtr
void moveCurrentAlphaPoint(AlphaPoint newAlphaPoint)
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:27
void setData(ImagePtr image, ImageTFDataPtr tfData)
boost::shared_ptr< class ActiveData > ActiveDataPtr
Definition: cxColorWidget.h:21
virtual void leaveEvent(QEvent *event)
Reimplemented from superclass.
double roundAwayFromZero(double val)
Definition: cxMathUtils.cpp:21
AlphaPoint getCurrentAlphaPoint(QPoint pos)
Get aplha point based on mCurrentClickX and mCurrentClickY.
TransferFunctionAlphaWidget(ActiveDataPtr activeData, QWidget *parent)
virtual void mousePressEvent(QMouseEvent *event)
Reimplemented from superclass.
static ActiveImageProxyPtr New(ActiveDataPtr activeData)
double constrainValue(double val, double min, double max)
void toggleSelectedPoint(QPoint pos)
Turn a transfer function point on or off (depending on it is on or not)
bool mReadOnly
Is class readOnly? Eg no mouse interaction possible.
AlphaPoint mSelectedAlphaPoint
The current alpha point.
void setReadOnly(bool readOnly)
Set class readonly: Disable mouse interaction.
virtual void resizeEvent(QResizeEvent *evt)
Reimplemented from superclass.
virtual void mouseMoveEvent(QMouseEvent *event)
Reimplemented from superclass.
Interface for QWidget which handles widgets uniformly for the system.
Definition: cxBaseWidget.h:88
std::map< int, int > IntIntMap
Definition: cxImage.h:35
void activeImageTransferFunctionsChangedSlot()
Acts when the image&#39;s transfer function is changed.
int mBorder
The size of the border around the transferfunction. The size of the rectangles are mBorder * 2...
std::map< int, QRect > mPointRects
Cache with all point rectangles.
virtual void paintEvent(QPaintEvent *event)
Reimplemented from superclass. Paints the transferfunction GUI.
boost::shared_ptr< class ImageTFData > ImageTFDataPtr
Internal placeholder for a function point.
virtual void enterEvent(QEvent *event)
Reimplemented from superclass.
std::pair< int, int > findAllowedMoveRangeAroundAlphaPoint(int selectedPointIntensity)
Namespace for all CustusX production code.