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