OpenWalnut  1.5.0dev
WTransferFunctionWidget.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <cassert>
26 #include <iostream>
27 #include <vector>
28 
29 #include <QGraphicsSceneMouseEvent>
30 #include <QMouseEvent>
31 #include <QPaintEngine>
32 
33 #include "core/common/WTransferFunction.h"
34 #include "WTransferFunctionBackground.h"
35 #include "WTransferFunctionColorPoint.h"
36 #include "WTransferFunctionHistogram.h"
37 #include "WTransferFunctionLine.h"
38 #include "WTransferFunctionPoint.h"
39 #include "WTransferFunctionScene.h"
40 #include "WTransferFunctionWidget.h"
41 
43  BaseClass( qparent ),
44  parent( parent ),
45  scene( 0x0 ),
46  first( 0x0 ),
47  last( 0x0 ),
48  current( 0x0 ),
49  cfirst( 0x0 ),
50  clast( 0x0 ),
51  ccurrent( 0x0 ),
52  background( 0x0 ),
53  histogram( 0x0 ),
54  initialized( false )
55 {
56  // std::cout << "new widget" << std::endl;
57  const int xMin( 0 );
58  const int yMin( 0 );
59  const int xMax( 300 );
60  const int yMax( 100 );
61 
62  // set up the scene and the parameters that define how we paint things
63  setMinimumSize( xMax-xMin+20, yMax - yMin + 30 );
64  this->setViewportUpdateMode( QGraphicsView::FullViewportUpdate );
65 
66  scene = new WTransferFunctionScene( this );
67  scene->setItemIndexMethod( QGraphicsScene::NoIndex );
68  scene->setSceneRect( xMin, yMin, xMax, yMax );
69  this->setScene( scene );
70 
71 
72  this->setCacheMode( CacheNone );
73  this->setRenderHint( QPainter::Antialiasing );
74  //this->setTransformationAnchor( AnchorUnderMouse );
75  //this->setResizeAnchor( AnchorViewCenter );
76 
77  // insert background and histogram items
78  scene->addItem( background = new WTransferFunctionBackground( this ) );
79  scene->addItem( histogram = new WTransferFunctionHistogram( this ) );
80 
81  // // insert first and last alpha point
82  // first = new WTransferFunctionPoint( this );
83  // last = new WTransferFunctionPoint( this );
84  //
85  // first->setRight( last );
86  // last->setLeft( first );
87  //
88  // scene->addItem( first );
89  // scene->addItem( last );
90  //
91  // first->setPos( QPointF( xMin, yMax/3*2 ) );
92  // last->setPos( QPointF( xMax, yMax/8 ) );
93 
94  // // connect points by a line
95  // WTransferFunctionLine* line = new WTransferFunctionLine();
96  // line->setLeft( first );
97  // line->setRight( last );
98  // first->setLine( line );
99  //
100  // scene->addItem( line );
101  //
102  // // create the control points for the color points
103  // cfirst = new WTransferFunctionColorPoint( this );
104  // clast = new WTransferFunctionColorPoint( this );
105  //
106  // cfirst->setRight( clast );
107  // clast->setLeft( cfirst );
108  //
109  // cfirst->colorSelected( Qt::black );
110  // clast->colorSelected( Qt::white );
111  //
112  // cfirst->setPos( xMin, 0 );
113  // clast->setPos( xMax, 0 );
114  //
115  // scene->addItem( cfirst );
116  //scene->addItem( clast );
117 
118  initialized = true;
119  // initialize the color map (aka. background)
120  setMyBackground(); // trigger first paint of transfer function
121 }
122 
123 namespace
124 {
125  /**
126  * linear blending of two colors in RGB space. Alpha is ignored
127  */
128  QColor blend( const QColor&a, double ta, const QColor &b, double tb )
129  {
130  return QColor(
131  ta*a.red()+tb*b.red(),
132  ta*a.green()+tb*b.green(),
133  ta*a.blue()+tb*b.blue() );
134  }
135 }
136 
137 void WTransferFunctionWidget::sample1DTransferFunction( unsigned char*array, int width )
138 {
139  if( !first || !cfirst )
140  return;
141 
142  WTransferFunctionPoint *acurrent( first );
144 
145  for( int i = 0; i < width; ++i )
146  {
147  double normalized = ( double )i/( double )scene->width();
148  while( acurrent && acurrent->getRight() && normalized > acurrent->getRight()->pos().x() )
149  {
150  acurrent = acurrent->getRight();
151  }
152 
153  while( ccurrent && ccurrent->getRight() && normalized > ccurrent->getRight()->pos().x() )
154  {
156  }
157 
158  if( !acurrent || !acurrent->getRight() )
159  {
160  break;
161  }
162  if( !ccurrent || !ccurrent->getRight() )
163  {
164  break;
165  }
166 
167  double awidth = acurrent->getRight()->pos().x() - acurrent->pos().x();
168  double cwidth = ccurrent->getRight()->pos().x() - ccurrent->pos().x();
169 
170  double at = ( normalized - acurrent->pos().x() )/awidth;
171  double ct = ( normalized - ccurrent->pos().x() )/cwidth;
172 
173  double alpha = ( double )( acurrent->getRight()->pos().y() )/scene->height()*at + ( double )( acurrent->pos().y() )/scene->height()*( 1.-at );
174  QColor rgb = blend( ccurrent->getRight()->getColor(), ct, ccurrent->getColor(), ( 1.-ct ) );
175 
176  array[ i*4 + 0 ] = rgb.red();
177  array[ i*4 + 1 ] = rgb.green();
178  array[ i*4 + 2 ] = rgb.blue();
179  array[ i*4 + 3 ] = alpha;
180  }
181 }
182 
183 
184 //! inaternal representation needs ARGB, but we do not display alpha component, so set it to 255
186 {
187  double scenewidth = scene->width();
188  //WTransferFunctionPoint *acurrent( first );
190 
191  for( int i = 0; i < width; ++i )
192  {
193  double normalized = ( double )i/( double )width * scenewidth;
194 
195  while( ccurrent && ccurrent->getRight() && normalized > ccurrent->getRight()->pos().x() )
196  {
198  }
199 
200  if( ccurrent && ccurrent->getRight() )
201  {
202  double cwidth = ccurrent->getRight()->pos().x() - ccurrent->pos().x();
203 
204  double ct = ( normalized - ccurrent->pos().x() )/cwidth;
205 
206  QColor rgb = blend( ccurrent->getRight()->getColor(), ct, ccurrent->getColor(), ( 1.-ct ) );
207 
208  array[ i*4 + 2 ] = rgb.red();
209  array[ i*4 + 1 ] = rgb.green();
210  array[ i*4 + 0 ] = rgb.blue();
211  array[ i*4 + 3 ] = 255; //alpha; //< displaying alpha in the gui does not make sense for me
212  }
213  }
214 }
215 
216 
217 
219 {
220  // // loop for debuging only
221  // WTransferFunctionPoint *current( first );
222  // while ( current )
223  // {
224  // current = current->getRight();
225  // }
226  // hopefully, the QScene will delete all its items.
227 }
228 
230 {
231  const int transferFunctionSize = 100;
232  static unsigned char texturearray[ 4*transferFunctionSize ];
233 
234  if( background )
235  {
236  sample1DTransferFunctionForDisplay( texturearray, transferFunctionSize );
237 
238  QImage image( texturearray, transferFunctionSize, 1, QImage::Format_ARGB32 );
239  QPixmap pixmap( transferFunctionSize, 1 );
240 #if( QT_VERSION >= 0x040700 )
241  pixmap.convertFromImage( image );
242 #else
243  // older versions have convertFromImage in Qt3Support
244  // to avoid linking to that one, we use the slower version
245  // here, which creates a copy, first.
246  pixmap = QPixmap::fromImage( image );
247 #endif
248 
249  background->setMyPixmap( pixmap );
250  }
251 }
252 
253 void WTransferFunctionWidget::drawBackground( QPainter *painter, const QRectF &rect )
254 {
255  BaseClass::drawBackground( painter, rect );
256 
257  // paint the border
258  // painter->setBrush( Qt::NoBrush );
259  // painter->drawRect( rect );
260 }
261 
262 void WTransferFunctionWidget::setHistogram( const std::vector< double > &newHistogram )
263 {
264  histogram->getData() = newHistogram;
265  histogram->update();
266  forceRedraw();
267 }
268 
270 {
271  if( !initialized )
272  {
273  return;
274  }
275  this->updateTransferFunction();
276  this->setMyBackground();
277  forceRedraw();
278 }
279 
281 {
282  if( !initialized )
283  {
284  return;
285  }
286  QRectF viewport( scene->sceneRect() );
287  scene->invalidate( viewport );
288  this->update();
289 }
290 
292 {
293  while( cfirst )
294  {
296  delete cfirst;
297  cfirst = next;
298  }
299  clast = 0x0;
300 
301  while( first )
302  {
304  if( first->getLine() )
305  {
306  delete ( first->getLine() );
307  }
308  delete first;
309  first = next;
310  }
311  last = 0x0;
312  ccurrent = 0x0;
313  current = 0x0;
314 }
315 
317 {
318  if( event->key() == Qt::Key_Backspace
319  || event->key() == Qt::Key_Delete )
320  {
321  if( current )
322  {
323  if( current->getRight() && current->getLeft() )
324  {
326  delete current->getLine();
327 
328  WTransferFunctionPoint *next = 0;
329  if( current->getLeft() && current->getLeft()->getLeft( ) )
330  {
331  next = current->getLeft();
332  }
333  else if( current->getRight() && current->getRight()->getRight() )
334  {
335  next = current->getRight();
336  }
337 
340  delete current;
341  current = next;
342  this->dataChanged();
343  }
344  }
345  if( ccurrent )
346  {
347  if( ccurrent->getRight() && ccurrent->getLeft() )
348  {
349  WTransferFunctionColorPoint *next = 0;
350  if( ccurrent->getLeft() && ccurrent->getLeft()->getLeft( ) )
351  {
352  next = ccurrent->getLeft();
353  }
354  else if( ccurrent->getRight() && ccurrent->getRight()->getRight() )
355  {
356  next = ccurrent->getRight();
357  }
358 
361  delete ccurrent;
362  ccurrent = next;
363  this->dataChanged();
364  }
365  }
366  }
367 }
368 
369 void WTransferFunctionWidget::insertColorNormalized( const QPointF& pos, QColor const *const color )
370 {
371  insertColor( QPointF( pos.x()*scene->width(), 0 ), color );
372 }
373 
374 void WTransferFunctionWidget::insertColor( const QPointF& pos, QColor const *const color )
375 {
377  point->setPos( QPointF( pos.x(), 0 ) );
378  scene->addItem( point );
379 
380  WTransferFunctionColorPoint* left( this->findCPointOnLeft( pos ) );
381  if( left )
382  {
383  WTransferFunctionColorPoint* right( left->getRight() );
384 
385  left->setRight( point );
386  point->setLeft( left );
387  point->setRight( right );
388  if( right )
389  {
390  right->setLeft( point );
391  }
392  if( color )
393  {
394  point->colorSelected( *color );
395  }
396  else
397  {
398  QColor a = left->getColor();
399  if( right )
400  {
401  QColor b = right->getColor();
402  double p = ( point->pos().x() - left->pos().x() )/( right->pos().x() - left->pos().x() );
403  point->colorSelected( blend( a, ( 1.-p ), b, ( p ) ) );
404  }
405  else
406  {
407  point->colorSelected( a );
408  }
409  }
410  }
411  else
412  {
413  point->setRight( cfirst );
414  if( cfirst )
415  {
416  cfirst->setLeft( point );
417  }
418  cfirst = point;
419  if( !clast )
420  {
421  clast = cfirst;
422  }
423  if( color )
424  {
425  point->colorSelected( *color );
426  }
427  else
428  {
429  // this is not part of our logic, maybe find the point to the right and look there?
430  if( point->getRight() )
431  {
432  point->colorSelected( point->getRight()->getColor() );
433  }
434  }
435  }
436 
437  point->update();
438 }
439 
440 void WTransferFunctionWidget::insertPointNormalized( const QPointF& position_ )
441 {
442  insertPoint( QPointF( position_.x()*scene->width(), ( 1.-position_.y() )*scene->height() ) );
443 }
444 
445 void WTransferFunctionWidget::insertPoint( const QPointF& position )
446 {
447  WTransferFunctionLine *line( new WTransferFunctionLine( this ) );
448  WTransferFunctionPoint *point( new WTransferFunctionPoint( this ) );
449  scene->addItem( point );
450  scene->addItem( line );
451 
452  // insert into list
453  WTransferFunctionPoint* left( this->findPointOnLeft( position ) );
454  if( left )
455  {
456  WTransferFunctionPoint* right( left->getRight() );
457 
458  left->setRight( point );
459  point->setLeft( left );
460  point->setRight( right );
461 
462  if( right )
463  {
464  right->setLeft( point );
465  }
466 
467  // if we are the rightmost point
468  // add the line to the new point
469  // otherwise, add the line to the left point.
470  // because we are the last point in the list
471  if( left->getLine() )
472  {
473  assert( right );
474 
475  left->getLine()->setRight( point );
476 
477  point->setLine( line );
478  line->setLeft( point );
479  line->setRight( right );
480  }
481  else
482  {
483  left->setLine( line );
484  line->setLeft( left );
485  line->setRight( point );
486  }
487  }
488  else
489  {
490  // there is nothing left of su, so we are the leftmost element
491  // now, add pointers to the right and we are first.
492  point->setRight( first );
493  if( first )
494  {
495  // if there is already a point to our right, we have to add a line
496  first->setLeft( point );
497  line->setLeft( point );
498  line->setRight( first );
499  }
500  else
501  {
502  // otherwise, we do not need the line
503  delete line;
504  }
505  first = point;
506  if( !clast )
507  {
508  clast = cfirst;
509  }
510  }
511  point->setPos( position );
512  //left->update();
513  //point->update();
514 }
515 
517 {
518  if( event->button() == Qt::RightButton )
519  {
520  QPointF position( this->mapToScene( event->pos() ) );
521  if( position.y() < 0 )
522  {
523  insertColor( position, 0 );
524  }
525  else
526  {
527  insertPoint( position );
528  }
529  this->forceRedraw();
530  event->accept();
531  }
532  else
533  {
534  BaseClass::mousePressEvent( event );
535  }
536 }
537 
539 {
541  if( !current || current->pos().x() > position.x() )
542  return 0x0;
543  while( current && current->getRight() )
544  {
546  if( right->pos().x() > position.x() )
547  {
548  return current;
549  }
550  current = right;
551  }
552  // seems like we are larger than the largest point, so return the last point
553  return current;
554 }
555 
557 {
559  if( !current || current->pos().x() > position.x() )
560  {
561  return 0x0;
562  }
563 
564  while( current && current->getRight() )
565  {
567  if( right->pos().x() > position.x() )
568  {
569  return current;
570  }
571  current = right;
572  }
573  return current;
574 }
575 
576 namespace
577 {
578  WColor toWColor( const QColor& q )
579  {
580  return WColor( q.redF(), q.greenF(), q.blueF(), q.alphaF() );
581  }
582 }
583 
585 {
587  {
588  // this part does not trigger qt rendering updates
589  std::vector < double > hist( histogram->getData() ); //< copy data, this will be deleted
590  tf.setHistogram( hist ); // get the data back because we need this for comparison
591 
592  QRectF bb = scene->sceneRect();
593 
595  while( cp )
596  {
597  double iso = ( cp->pos().x() - bb.x() )/bb.width();
598  tf.addColor( iso, toWColor( cp->getColor() ) );
599  cp = cp->getRight();
600  }
601 
603  while( p )
604  {
605  double iso = ( p->pos().x() - bb.x() )/bb.width();
606  double alpha = 1.-( ( p->pos().y() - bb.y() )/bb.height() );
607  tf.addAlpha( iso, alpha );
608  p = p->getRight();
609  }
610  }
611 
612  if( parent )
613  {
614  parent->guiUpdate( tf );
615  }
616 }
617 
A QGraphicsItem that displays a pixmap in the background of the scene.
void setMyPixmap(const QPixmap &pixmap)
Set the background pixmap that will be displayed.
A control point for the color function.
void colorSelected(const QColor &color)
Called by the color dialog every time the user changes the color.
void setLeft(WTransferFunctionColorPoint *left)
Set the item left of us.
WTransferFunctionColorPoint * getRight() const
Returns the item right of this item.
void setRight(WTransferFunctionColorPoint *right)
Set the item right of us.
WTransferFunctionColorPoint * getLeft() const
Returns the item left of the this item.
Display a semi-transparent line graph as the histogram of the current data set.
const std::vector< double > & getData() const
Get histogram data.
Line object for the connection of alpha control points.
void setRight(WTransferFunctionPoint *right)
Sets point to the right.
void setLeft(WTransferFunctionPoint *left)
Sets point to the left.
A control point for the alpha function.
WTransferFunctionPoint * getLeft() const
Get point to the left.
WTransferFunctionLine * getLine() const
The current line if there is one.
WTransferFunctionPoint * getRight() const
Get point to the right.
virtual void setPos(QPointF point)
Overloaded form base class for debugging.
void setLeft(WTransferFunctionPoint *left)
Set point to the left.
void setRight(WTransferFunctionPoint *right)
Set point to the right.
void setLine(WTransferFunctionLine *line)
Set the line pointing to the right.
The scene for our visualization of the transfer function.
void insertPoint(const QPointF &position)
Insert a new alpha control point at scene position position.
virtual ~WTransferFunctionWidget()
Destructor.
WTransferFunctionGuiNotificationClass * parent
the class that receives our update notifications
WTransferFunctionPoint * findPointOnLeft(QPointF position)
Internal helper function: Find the point to the left of the given point.
void setMyBackground()
Internal helper function to update the QGraphicsPixmapItem that holds a representation of the current...
virtual void keyPressEvent(QKeyEvent *event)
Interactions implemented so far: right click: insert new object (Ctrl+left click or two-finger tap on...
WTransferFunctionColorPoint * findCPointOnLeft(QPointF position)
Internal helper function: Find the point to the left of the given color control point.
void insertColorNormalized(const QPointF &pos, QColor const *const color=0)
Same as insertColor but in normalized coordinates, i.e., [ 0...1 ] along x.
WTransferFunctionPoint * current
last element in list
WTransferFunctionHistogram * histogram
background that displays the color map
bool initialized
item responsible for displaying histogram data
void updateTransferFunction()
Updates the transfer function.
WTransferFunctionColorPoint * ccurrent
last element
void sample1DTransferFunctionForDisplay(unsigned char *array, int width)
Sample the transfer function into a 1D RGBA, ABGR, ARGB, ...
WTransferFunctionPoint * last
first element
QGraphicsScene * scene
our scene
virtual void drawBackground(QPainter *painter, const QRectF &rect)
Draws the background.
void insertColor(const QPointF &pos, QColor const *const color=0)
Insert a new color control point at scene position pos (only the x-value is relevant) If a color is p...
void forceRedraw()
Redraw the widget.
void setHistogram(const std::vector< double > &histogram)
Called from external to set histogram.
WTransferFunctionPoint * first
linked list of alpha items
virtual void mousePressEvent(QMouseEvent *event)
For a documentation of the implemented actions confer the keyPressEvent documentation.
WTransferFunctionColorPoint * clast
first element
void insertPointNormalized(const QPointF &position)
Same as insertPoint but in normalized coordinates and not in screen space.
void dataChanged()
Notification that the data changed, i.e., a control point has been moved or a color changed.
WTransferFunctionBackground * background
currently selected/active color element
void clearTransferFunction()
Remove all points from the transfer function widget to be able to insert new points.
WTransferFunctionColorPoint * cfirst
currently selected/active element
WTransferFunctionWidget(QWidget *qparent=NULL, WTransferFunctionGuiNotificationClass *parent=NULL)
Constructor.
void sample1DTransferFunction(unsigned char *array, int width)
Sample the transfer function into a 1D RGBA texture.
A class that stores a 1D transfer function which consists of a linear interpolation of alpha and colo...
void setHistogram(std::vector< double > &data)
Set the histogram going along with the transfer function.
void addColor(double iso, const WColor &color)
Insert a new color point.
void addAlpha(double iso, double alpha)
Insert a new alpha point.
The class managing the widget that wants to receive updates whenever a change occurrs.
virtual void guiUpdate(const WTransferFunction &tf)=0
update the gui