CustusX  2023.01.05-dev+develop.0da12
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 
31 namespace cx
32 {
34  BaseWidget(parent, "transfer_function_alpha_widget", "Alpha Transfer Function"),
35  mBorder(5),
36  mReadOnly(false)
37 {
38  this->setToolTip("Set the alpha part of a transfer function");
39  this->setFocusPolicy(Qt::StrongFocus);
40 
42  connect(mActiveImageProxy.get(), SIGNAL(transferFunctionsChanged()), this, SLOT(activeImageTransferFunctionsChangedSlot()));
43 
45 }
47 {}
48 
50 {
51  if (( mImage == image )&&( mImageTF==tfData ))
52  return;
53 
54  mImage = image;
55  mImageTF = tfData;
56  this->update();
57 }
58 
60 {
61  mReadOnly = readOnly;
62 }
63 
65 {
66  this->update();
67 }
68 
70 {
71  this->setMouseTracking(true);
72 }
73 
75 {
76  this->setMouseTracking(false);
77 }
78 
80 {
81  if(mReadOnly)
82  return;
83  QWidget::mousePressEvent(event);
84 
85  if(event->button() == Qt::LeftButton)
86  {
87  mSelectedAlphaPoint = this->selectPoint(event->pos());
88  }
89  else if(event->button() == Qt::RightButton)
90  {
91  this->toggleSelectedPoint(event->pos());
92  }
93 
94  this->update();
95 }
96 
98 {
99  if(mReadOnly)
100  return;
101  QWidget::mouseReleaseEvent(event);
102 // mCurrentAlphaPoint.reset();
103 }
104 
106 {
107  if (!mImage)
108  return;
109 
110  this->updateTooltip(event->pos());
111  if(mReadOnly) //Only show tool tip if readOnly
112  return;
113 
114  QWidget::mouseMoveEvent(event);
115 
116  if(event->buttons() == Qt::LeftButton)
117  {
118  this->moveCurrentAlphaPoint(this->getCurrentAlphaPoint(event->pos()));
119  this->update();
120  }
121 }
122 
124 {
126  {
127  int shift = 0;
128  int alphaShift = 0;
129  if (event->key()==Qt::Key_Left)
130  shift = -1;
131  if (event->key()==Qt::Key_Right)
132  shift = +1;
133  if (event->key()==Qt::Key_Down)
134  alphaShift = -1;
135  if (event->key()==Qt::Key_Up)
136  alphaShift = +1;
137 
138  if ((shift!=0) || (alphaShift!=0))
139  {
140  AlphaPoint newPoint = mSelectedAlphaPoint;
141  newPoint.position += shift;
142  newPoint.value += alphaShift;
143  this->moveCurrentAlphaPoint(newPoint);
145  this->update();
146  return;
147  }
148  }
149 
150  QWidget::keyPressEvent(event);
151 }
152 
154 {
155  AlphaPoint selected = this->selectPoint(pos);
156  if (!selected.isValid())
157  selected = this->getCurrentAlphaPoint(pos);
158  this->updateTooltip(selected);
159  this->update();
160 }
161 
163 {
164  QString tip = QString("alpha(%1)=%2").arg(point.position).arg(double(point.value)/255, 0, 'f', 2);
165  this->setToolTip(tip);
166  reporter()->sendVolatile(tip);
167 }
168 
169 
171 {
172  QWidget::paintEvent(event);
173  QPainter painter(this);
174  this->clearBackground(painter);
175 
176  if (!mImage)
177  return;
178 
179  this->paintHistogram(painter);
180  this->paintOpacityGraph(painter);
181 }
182 
183 void TransferFunctionAlphaWidget::clearBackground(QPainter& painter)
184 {
185  // Fill with white global background color and grey plot area background color
186  const QBrush frameBrush = QBrush(QColor(170, 170, 170));
187  const QBrush backgroundBrush = QBrush(QColor(200, 200, 200));
188  painter.fillRect(this->mFullArea, frameBrush);
189  painter.fillRect(this->mPlotArea, backgroundBrush);
190 
191 }
192 
194 {
195  QPen pointPen, pointLinePen;
196  pointPen.setColor(QColor(0, 0, 150));
197  pointLinePen.setColor(QColor(150, 100, 100));
198 
199  // Go through each point and draw squares and lines
200  IntIntMap opacityMap = mImageTF->getOpacityMap();
201 
202  QPoint lastScreenPoint;
203  this->mPointRects.clear();
204  for (IntIntMap::iterator opPoint = opacityMap.begin();
205  opPoint != opacityMap.end();
206  ++opPoint)
207  {
208  // Get the screen (plot) position of this point
209  AlphaPoint pt(opPoint->first, opPoint->second);
210  QPoint screenPoint = this->alpha2screen(pt);
211 
212  // draw line from left edge to first point:
213  if (opPoint==opacityMap.begin())
214  {
215  lastScreenPoint = QPoint(mPlotArea.left(), screenPoint.y());
216  }
217 
218  // Draw line from previous point
219  painter.setPen(pointLinePen);
220  painter.drawLine(lastScreenPoint, screenPoint);
221 
222  // Draw the rectangle
223  QRect pointRect(screenPoint.x() - mBorder, screenPoint.y() - mBorder,
224  mBorder*2, mBorder*2);
225  if (opPoint->first==mSelectedAlphaPoint.position)
226  {
227  pointPen.setWidth(2);
228  painter.setPen(pointPen);
229  }
230  else
231  {
232  pointPen.setWidth(1);
233  painter.setPen(pointPen);
234  }
235  painter.drawRect(pointRect);
236  this->mPointRects[opPoint->first] = pointRect;
237 
238  // Store the point
239  lastScreenPoint = screenPoint;
240  }
241 
242  // draw a line from the last point to the right end
243  QPoint screenPoint(mPlotArea.right(), lastScreenPoint.y());
244  painter.setPen(pointLinePen);
245  painter.drawLine(lastScreenPoint, screenPoint);
246 
247 }
248 
250 {
251  QPoint screenPoint = QPoint(
252  static_cast<int>(mPlotArea.left() + mPlotArea.width() *
253  (pt.position - mImage->getMin()) /
254  static_cast<double>(mImage->getRange())),
255  static_cast<int>(mPlotArea.bottom() - mPlotArea.height() *
256  pt.value /
257  static_cast<double>(mImage->getMaxAlphaValue())) );
258  return screenPoint;
259 }
260 
262 {
263  // Draw histogram
264  // with log compression
265 
266  vtkImageAccumulatePtr histogram = mImage->getHistogram();
267  int histogramSize = histogram->GetComponentExtent()[1] -
268  histogram->GetComponentExtent()[0];
269 
270  painter.setPen(QColor(140, 140, 210));
271 
272  double numElementsInBinWithMostElements = log(histogram->GetOutput()->GetPointData()->GetScalars()->GetRange()[1]+1);
273  double barHeightMult = (this->height() - mBorder*2) / numElementsInBinWithMostElements;
274 
275  double posMult = (this->width() - mBorder*2) / double(histogramSize);
276  for (int i = mImage->getMin(); i <= mImage->getMax(); i++)
277  {
278  int x = int(std::lround(((i- mImage->getMin()) * posMult))); //Offset with min value
279  int y = int(std::lround(log(double(static_cast<int*>(histogram->GetOutput()->GetScalarPointer(i - mImage->getMin(), 0, 0))[0]+1)) * barHeightMult));
280  if (y > 0)
281  {
282  painter.drawLine(x + mBorder, height() - mBorder,
283  x + mBorder, height() - mBorder - y);
284  }
285  }
286 }
287 
288 
290 {
291  QWidget::resizeEvent(evt);
292 
293  // Calculate areas
294  this->mFullArea = QRect(0, 0, width(), height());
295  this->mPlotArea = QRect(mBorder, mBorder,
296  width() - mBorder*2, height() - mBorder*2);
297 }
298 
300 {
301  std::map<int, QRect>::iterator it = mPointRects.begin();
302  for(;it != mPointRects.end(); ++it)
303  {
304  if (it->second.contains(pos))
305  {
306  AlphaPoint retval;
307  retval.position = it->first;
308  IntIntMap opactiyMap = mImageTF->getOpacityMap();
309  if (opactiyMap.find(retval.position) != opactiyMap.end())
310  retval.value = opactiyMap.find(retval.position)->second;
311  return retval;
312  }
313  }
314 
315  return AlphaPoint();
316 }
317 
319 {
320  if (mPointRects.begin()->first == intensity)
321  return true;
322  if (mPointRects.rbegin()->first == intensity)
323  return true;
324  return false;
325 }
326 
328 {
329  AlphaPoint point;
330 
331  double dposition = mImage->getMin() + mImage->getRange() * double(pos.x() - mPlotArea.left()) / mPlotArea.width();
332  double dvalue = mImage->getMaxAlphaValue() * double(mPlotArea.bottom() - pos.y())/mPlotArea.height();
333  point.position = int(std::lround(dposition));
334  point.value = int(std::lround(dvalue));
335 
336  point.position = constrainValue(point.position, mImage->getMin(), mImage->getMax());
337  point.value = constrainValue(point.value, 0, mImage->getMaxAlphaValue());
338 
339  return point;
340 }
341 
343 {
344  if(!mImage)
345  return;
346  mSelectedAlphaPoint = this->selectPoint(pos);
348  {
349  // Outside any of the rectangles
350  AlphaPoint point = getCurrentAlphaPoint(pos);
351  mImageTF->addAlphaPoint(point.position,point.value);
352  mSelectedAlphaPoint = this->selectPoint(pos);
353  }
354  else if(!this->isEndpoint(mSelectedAlphaPoint.position))
355  {
356  // Inside one of the rectangles
358  mImageTF->removeAlphaPoint(mSelectedAlphaPoint.position);
360  }
361 
362  this->update();
363 }
364 
366 {
368  return;
369 
370  newAlphaPoint.value = constrainValue(newAlphaPoint.value, 0, 255);
371 
372  std::pair<int,int> range = this->findAllowedMoveRangeAroundAlphaPoint(mSelectedAlphaPoint.position);
373  newAlphaPoint.position = constrainValue(newAlphaPoint.position, range.first, range.second);
374 
375  mImageTF->moveAlphaPoint(mSelectedAlphaPoint.position, newAlphaPoint.position, newAlphaPoint.value);
376 
377  mSelectedAlphaPoint = newAlphaPoint;
378  this->update();
379 }
380 
381 std::pair<int,int> TransferFunctionAlphaWidget::findAllowedMoveRangeAroundAlphaPoint(int selectedPointIntensity)
382 {
383  // constrain new point intensity between the two neigbours
384  IntIntMap opacityMap = mImageTF->getOpacityMap();
385  IntIntMap::iterator pointIterator = opacityMap.find(selectedPointIntensity);
386 
387  std::pair<int,int> range(mImage->getMin(), mImage->getMax());
388  if (pointIterator!=opacityMap.begin())
389  {
390  IntIntMap::iterator prevPointIterator = pointIterator;
391  --prevPointIterator;
392  range.first = std::max(range.first, prevPointIterator->first + 1);
393  }
394 
395  IntIntMap::iterator nextPointIterator = pointIterator;
396  ++nextPointIterator;
397  if (nextPointIterator!=opacityMap.end())
398  {
399  range.second = std::min(range.second, nextPointIterator->first - 1);
400  }
401 
402  return range;
403 }
404 
405 
406 }//namespace cx
virtual void keyPressEvent(QKeyEvent *event)
ReporterPtr reporter()
Definition: cxReporter.cpp:36
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.
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.