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