CustusX  15.4.0-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->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->updateTooltip(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  int x = 0;
296  int y = 0;
297  double barHeightMult = (this->height() - mBorder*2)
298  / log(histogram->GetOutput()->GetPointData()->GetScalars()->GetRange()[1]+1);
299 
300  double posMult = (this->width() - mBorder*2) / double(histogramSize);
301  for (int i = mImage->getMin(); i <= mImage->getMax(); i++)
302  {
303  x = ((i- mImage->getMin()) * posMult); //Offset with min value
304  y = log(double(static_cast<int*>(histogram->GetOutput()->GetScalarPointer())[i - mImage->getMin()]+1)) * barHeightMult;
305  if (y > 0)
306  {
307  painter.drawLine(x + mBorder, height() - mBorder,
308  x + mBorder, height() - mBorder - y);
309  }
310  }
311 }
312 
313 
315 {
316  QWidget::resizeEvent(evt);
317 
318  // Calculate areas
319  this->mFullArea = QRect(0, 0, width(), height());
320  this->mPlotArea = QRect(mBorder, mBorder,
321  width() - mBorder*2, height() - mBorder*2);
322 }
323 
325 {
326  std::map<int, QRect>::iterator it = mPointRects.begin();
327  for(;it != mPointRects.end(); ++it)
328  {
329  if (it->second.contains(pos))
330  {
331  AlphaPoint retval;
332  retval.position = it->first;
333  IntIntMap opactiyMap = mImageTF->getOpacityMap();
334  if (opactiyMap.find(retval.position) != opactiyMap.end())
335  retval.value = opactiyMap.find(retval.position)->second;
336  return retval;
337  }
338  }
339 
340  return AlphaPoint();
341 }
342 
344 {
345  if (mPointRects.begin()->first == intensity)
346  return true;
347  if (mPointRects.rbegin()->first == intensity)
348  return true;
349  return false;
350 }
351 
353 {
354  AlphaPoint point;
355 
356  double dposition = mImage->getMin() + mImage->getRange() * double(pos.x() - mPlotArea.left()) / mPlotArea.width();
357  double dvalue = mImage->getMaxAlphaValue() * double(mPlotArea.bottom() - pos.y())/mPlotArea.height();
358  point.position = int(dposition+0.5);
359  point.value = int(dvalue+0.5);
360 
361  point.position = constrainValue(point.position, mImage->getMin(), mImage->getMax());
362  point.value = constrainValue(point.value, 0, mImage->getMaxAlphaValue());
363 
364  return point;
365 }
366 
368 {
369  if(!mImage)
370  return;
371  mSelectedAlphaPoint = this->selectPoint(pos);
373  {
374  // Outside any of the rectangles
375  AlphaPoint point = getCurrentAlphaPoint(pos);
376  mImageTF->addAlphaPoint(point.position,point.value);
377  mSelectedAlphaPoint = this->selectPoint(pos);
378  }
379  else if(!this->isEndpoint(mSelectedAlphaPoint.position))
380  {
381  // Inside one of the rectangles
383  mImageTF->removeAlphaPoint(mSelectedAlphaPoint.position);
385  }
386 
387  this->update();
388 }
389 
391 {
393  return;
394 
395  newAlphaPoint.value = constrainValue(newAlphaPoint.value, 0, 255);
396 
397  std::pair<int,int> range = this->findAllowedMoveRangeAroundAlphaPoint(mSelectedAlphaPoint.position);
398  newAlphaPoint.position = constrainValue(newAlphaPoint.position, range.first, range.second);
399 
400  mImageTF->moveAlphaPoint(mSelectedAlphaPoint.position, newAlphaPoint.position, newAlphaPoint.value);
401 
402  mSelectedAlphaPoint = newAlphaPoint;
403  this->update();
404 }
405 
406 std::pair<int,int> TransferFunctionAlphaWidget::findAllowedMoveRangeAroundAlphaPoint(int selectedPointIntensity)
407 {
408  // constrain new point intensity between the two neigbours
409  IntIntMap opacityMap = mImageTF->getOpacityMap();
410  IntIntMap::iterator pointIterator = opacityMap.find(selectedPointIntensity);
411 
412  std::pair<int,int> range(mImage->getMin(), mImage->getMax());
413  if (pointIterator!=opacityMap.begin())
414  {
415  IntIntMap::iterator prevPointIterator = pointIterator;
416  --prevPointIterator;
417  range.first = std::max(range.first, prevPointIterator->first + 1);
418  }
419 
420  IntIntMap::iterator nextPointIterator = pointIterator;
421  ++nextPointIterator;
422  if (nextPointIterator!=opacityMap.end())
423  {
424  range.second = std::min(range.second, nextPointIterator->first - 1);
425  }
426 
427  return range;
428 }
429 
430 
431 }//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)