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