OpenWalnut  1.5.0dev
WMHistogramView.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 <algorithm>
26 #include <limits>
27 #include <memory>
28 #include <string>
29 #include <utility>
30 #include <vector>
31 
32 #include <boost/bind/bind.hpp>
33 #include <boost/thread/locks.hpp>
34 #include <boost/thread/mutex.hpp>
35 #include <osg/Drawable>
36 #include <osg/Geode>
37 #include <osg/Geometry>
38 #include <osg/LineWidth>
39 #include <osgText/Text>
40 
41 #include "WMHistogramView.h"
42 #include "WMHistogramView.xpm"
43 #include "core/common/WPathHelper.h"
44 #include "core/common/WStringUtils.h"
45 #include "core/graphicsEngine/WGERequirement.h"
46 #include "core/kernel/WKernel.h"
47 #include "core/ui/WUI.h"
48 #include "core/ui/WUIViewEventHandler.h"
49 #include "core/ui/WUIViewWidget.h"
50 
52 
54  : WModule(),
55  m_mousePos(),
56  m_frameSize( 0.04 )
57 {
58 }
59 
61 {
62 }
63 
64 std::shared_ptr< WModule > WMHistogramView::factory() const
65 {
66  return std::shared_ptr< WModule >( new WMHistogramView() );
67 }
68 
69 const char** WMHistogramView::getXPMIcon() const
70 {
71  return WMHistogramView_xpm;
72 }
73 const std::string WMHistogramView::getName() const
74 {
75  return "HistogramView";
76 }
77 
78 const std::string WMHistogramView::getDescription() const
79 {
80  return "Draws a histogram.";
81 }
82 
84 {
85  m_input = std::shared_ptr< WModuleInputData< WDataSetHistogram1D > >(
87  shared_from_this(), "Histogram input",
88  "A histogram to show in the histogram viewer." ) );
90 
92 }
93 
95 {
96  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
97 
98  std::shared_ptr< WItemSelection > selections( new WItemSelection() );
99 
100  // add the possible histogram styles and
101  // corresponding geometry generation functions
102  selections->addItem( "Bars", "Draws transparent bars on top of each other." );
103  m_geometryFunctions.push_back( boost::bind( &WMHistogramView::createGeometryBars, this ) );
104 
105  selections->addItem( "Curves", "Draws curves." );
106  m_geometryFunctions.push_back( boost::bind( &WMHistogramView::createGeometryCurves, this ) );
107 
108  selections->addItem( "Stairs", "Draws 'stairs'." );
109  m_geometryFunctions.push_back( boost::bind( &WMHistogramView::createGeometryStairs, this ) );
110 
111  // add the actual selection property
112  m_styleSelection = m_properties->addProperty( "Histogram style", "How the histograms should be rendered",
113  selections->getSelectorFirst(), m_propCondition );
115 
116  m_color = m_properties->addProperty( "Color", "Choose a color for theinput histogram.", WColor( 1.0, 0.0, 0.0, 1.0 ), m_propCondition );
117 
119 }
120 
122 {
123  // we need graphics to draw anything
124  m_requirements.push_back( new WGERequirement() );
125  m_requirements.push_back( new WUIRequirement() );
126 }
127 
129 {
130  if( m_mainNode )
131  {
132  if( m_infoNode )
133  {
134  m_mainNode->remove( m_infoNode );
135  }
136  if( m_markerNode )
137  {
138  m_mainNode->remove( m_markerNode );
139  }
140 
141  if( m_histogram )
142  {
143  createInfo( pos );
144  }
145  }
146 }
147 
148 void WMHistogramView::handleResize( int /* x */, int /* y */, int width, int height )
149 {
150  if( m_mainNode )
151  {
152  m_windowWidth = width;
153  m_windowHeight = height;
154 
155  m_redrawMutex.lock();
156 
157  m_mainNode->clear();
158  if( m_windowHeight != 0 && m_windowWidth != 0 && m_histogram )
159  {
160  redraw();
161  }
162 
163  m_redrawMutex.unlock();
164  }
165 }
166 
168 {
169  m_moduleState.setResetable( true, true );
171  m_moduleState.add( m_input->getDataChangedCondition() );
172 
174 
175  ready();
176 
177  //! Holds the reference to the custom widget used for displaying the histogram
178  m_widget = WKernel::getRunningKernel()->getUI()->getWidgetFactory()->createViewWidget(
180  WGECamera::TWO_D, m_shutdownFlag.getValueChangeCondition() );
181  osg::ref_ptr< WUIViewEventHandler > eh = new WUIViewEventHandler( m_widget );
182  eh->subscribeMove( boost::bind( &WMHistogramView::handleMouseMove, this, boost::placeholders::_1 ) );
183  eh->subscribeResize( boost::bind( &WMHistogramView::handleResize,
184  this,
185  boost::placeholders::_1,
186  boost::placeholders::_2,
187  boost::placeholders::_3,
188  boost::placeholders::_4 ) );
189  m_widget->addEventHandler( eh );
190 
191  m_widget->show();
192 
193  if( m_widget )
194  {
195  // window width and height
196  m_windowWidth = m_widget->width();
197  m_windowHeight = m_widget->height();
198 
199  m_mainNode = m_widget->getScene();
200  if( !m_mainNode )
201  {
202  errorLog() << "Could not acquire scene node from widget.";
203  }
204  }
205  else
206  {
207  errorLog() << "Could not create widget for the histogram.";
208  }
209 
210  while( !m_shutdownFlag() )
211  {
212  debugLog() << "Waiting ...";
213 
215 
216  if( m_shutdownFlag() )
217  {
218  break;
219  }
220 
221  // if we do not have a main node, there is no point in doing anything
222  if( m_mainNode )
223  {
224  m_redrawMutex.lock();
225 
226  bool dataChanged = m_input->getData() && m_input->getData() != m_data;
227  bool hasData = m_input->getData() || m_data;
228 
229  if( !hasData )
230  {
231  continue;
232  }
233 
234  bool colorChanged = m_color->changed();
235 
236  if( dataChanged || colorChanged || m_styleSelection->changed() )
237  {
238  infoLog() << "Recalculating histogram.";
239 
240  m_data = m_input->getData();
241 
242  if( dataChanged )
243  {
245  }
246 
247  // remove all child nodes from main node
248  m_mainNode->clear();
249  redraw();
250  }
251 
252  m_redrawMutex.unlock();
253  }
254  }
255 
256  debugLog() << "Shutting down...";
257 
258  // clear main node, just in case
259  if( m_mainNode )
260  {
261  m_mainNode->clear();
262  }
263 
264  m_widget->close();
265 
266  debugLog() << "Finished. Good bye!";
267 }
268 
270 {
271  int sel = m_styleSelection->get( true ).getItemIndexOfSelected( 0 );
272  if( sel >= static_cast< int >( m_geometryFunctions.size() ) )
273  {
274  errorLog() << "BUG: There is no geometry generation function for this style!";
275  }
276  else
277  {
278  // call sel'th geometry generation function
279  // this adds the osg nodes that draw the histogram
280  // depending on which style was selected
281  m_geometryFunctions[ sel ]();
282 
283  // this creates the frame and labels
284  createFrame();
285  }
286 }
287 
289 {
290  m_histogram = m_data->getHistogram();
291 
292  // these are the lower left and upper right corners of the histogram (excluding frame and labels)
293  m_histogramLowerLeft[ 0 ] = m_histogram->getMinimum();
294  m_histogramLowerLeft[ 1 ] = 0.0;
295 
296  m_histogramUpperRight[ 0 ] = m_histogram->getMaximum();
297  // this sets m_histogramUpperRight[ 1 ]
299 }
300 
302 {
303  double max = std::numeric_limits< double >::min();
304 
305  // for all bins/buckets
306  for( std::size_t j = 0; j < m_histogram->size(); ++j )
307  {
308  double val = static_cast< double >( m_histogram->at( j ) );
309  if( val > max )
310  {
311  max = val;
312  }
313  }
314  m_histogramUpperRight[ 1 ] = max;
315 }
316 
318 {
319  // update the histogram size member
321 
322  // update the frame size
325 
326  // this is the geode for the histogram bars
327  osg::ref_ptr< osg::Geode > geode = new osg::Geode();
328  geode->setDataVariance( osg::Object::STATIC );
329 
330  osg::ref_ptr< osg::Vec2Array > quadVertices = new osg::Vec2Array;
331  osg::ref_ptr< osg::Vec2Array > quadTexCoords = new osg::Vec2Array;
332  osg::ref_ptr< osg::Vec4Array > quadColors = new osg::Vec4Array;
333 
334  osg::ref_ptr< osg::Vec2Array > lineVertices = new osg::Vec2Array;
335  osg::ref_ptr< osg::Vec4Array > lineColors = new osg::Vec4Array;
336 
337  // one color per dataset
338  WColor color = m_color->get( true );
339 
340  // add a quad for every bar/bucket/bin
341  for( std::size_t j = 0; j < m_histogram->size(); ++j )
342  {
343  // 'histogram' coords for bar j
344  std::pair< double, double > barPosHistoCoordsX = m_histogram->getIntervalForIndex( j );
345  WVector2d barLowerLeft( barPosHistoCoordsX.first, 0 );
346  WVector2d barUpperRight( barPosHistoCoordsX.second, m_histogram->at( j ) );
347 
348  // vertices
349  quadVertices->push_back( histogramSpaceToWindowSpace( barLowerLeft ) );
350  quadVertices->push_back( histogramSpaceToWindowSpace( WVector2d( barUpperRight[ 0 ], barLowerLeft[ 1 ] ) ) );
351  quadVertices->push_back( histogramSpaceToWindowSpace( barUpperRight ) );
352  quadVertices->push_back( histogramSpaceToWindowSpace( WVector2d( barLowerLeft[ 0 ], barUpperRight[ 1 ] ) ) );
353 
354  // tex coords
355  // these are not used yet,
356  // but they may be used to color the bars with the dataset's colormap
357  quadTexCoords->push_back( WVector2d( barPosHistoCoordsX.first, 0.0 ) );
358  quadTexCoords->push_back( WVector2d( barPosHistoCoordsX.second, 0.0 ) );
359  quadTexCoords->push_back( WVector2d( barPosHistoCoordsX.second, 0.0 ) );
360  quadTexCoords->push_back( WVector2d( barPosHistoCoordsX.first, 0.0 ) );
361 
362  // outline vertices
363  lineVertices->push_back( histogramSpaceToWindowSpace( barLowerLeft ) );
364  lineVertices->push_back( histogramSpaceToWindowSpace( WVector2d( barLowerLeft[ 0 ], barUpperRight[ 1 ] ) ) );
365  lineVertices->push_back( histogramSpaceToWindowSpace( WVector2d( barLowerLeft[ 0 ], barUpperRight[ 1 ] ) ) );
366  lineVertices->push_back( histogramSpaceToWindowSpace( barUpperRight ) );
367  lineVertices->push_back( histogramSpaceToWindowSpace( barUpperRight ) );
368  lineVertices->push_back( histogramSpaceToWindowSpace( WVector2d( barUpperRight[ 0 ], barLowerLeft[ 1 ] ) ) );
369 
370  if( m_data->hasColors() )
371  color = m_data->getColor( j );
372 
373  WColor lighterColor = WColor( color[ 0 ] * 1.1, color[ 1 ] * 1.1, color[ 2 ] * 1.1, 1.0 );
374  WColor darkerColor = WColor( color[ 0 ] * 0.9, color[ 1 ] * 0.9, color[ 2 ] * 0.9, 1.0 );
375  color[ 3 ] = lighterColor[ 3 ] = darkerColor[ 3 ] = 0.8;
376 
377  quadColors->push_back( lighterColor );
378  quadColors->push_back( lighterColor );
379  quadColors->push_back( lighterColor );
380  quadColors->push_back( lighterColor );
381 
382  // outline colors
383  lineColors->push_back( lighterColor );
384  lineColors->push_back( lighterColor );
385  lineColors->push_back( lighterColor );
386  lineColors->push_back( lighterColor );
387  lineColors->push_back( darkerColor );
388  lineColors->push_back( darkerColor );
389  }
390 
391  // create drawable for the quads
392  {
393  osg::ref_ptr< osg::Geometry > geometry = new osg::Geometry;
394 
395  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, 4 * m_histogram->size() ) );
396  geometry->setVertexArray( quadVertices );
397  geometry->setColorArray( quadColors );
398  geometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
399  geometry->setTexCoordArray( 0, quadTexCoords );
400 
401  // enable VBO
402  geometry->setUseDisplayList( false );
403  geometry->setUseVertexBufferObjects( true );
404 
405  geode->addDrawable( geometry );
406  }
407 
408  // create drawable for the outlines
409  {
410  osg::ref_ptr< osg::Geometry > geometry = new osg::Geometry;
411 
412  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINES, 0, 6 * m_histogram->size() ) );
413  geometry->setVertexArray( lineVertices );
414  geometry->setColorArray( lineColors );
415  geometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
416 
417  // enable VBO
418  geometry->setUseDisplayList( false );
419  geometry->setUseVertexBufferObjects( true );
420 
421  geode->addDrawable( geometry );
422  }
423 
424  // we do not want any lighting
425  osg::StateSet* state = geode->getOrCreateStateSet();
426  state->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
427 
428  // no depth test
429  state->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
430 
431  // enable blending if we draw bars on top of each other
432  state->setMode( GL_BLEND, osg::StateAttribute::ON );
433  state->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
434  state->setRenderBinDetails( 1001, "RenderBin" );
435 
436  m_mainNode->insert( geode );
437 }
438 
440 {
441  // update the histogram size member
443 
444  // update the frame size
447 
448  // this is the geode for the histogram curve
449  osg::ref_ptr< osg::Geode > geode = new osg::Geode();
450  geode->setDataVariance( osg::Object::STATIC );
451 
452  osg::ref_ptr< osg::Vec2Array > quadVertices = new osg::Vec2Array;
453  osg::ref_ptr< osg::Vec2Array > quadTexCoords = new osg::Vec2Array;
454  osg::ref_ptr< osg::Vec4Array > quadColors = new osg::Vec4Array;
455 
456  osg::ref_ptr< osg::Vec2Array > lineVertices = new osg::Vec2Array;
457  osg::ref_ptr< osg::Vec4Array > lineColors = new osg::Vec4Array;
458 
459  // one color per dataset
460  WColor color = m_color->get( true );
461 
462  // add a quad for every bar/bucket/bin
463  for( std::size_t j = 0; j < m_histogram->size() - 1; ++j )
464  {
465  // 'histogram' coords for bar j
466  double quadLeft = m_histogram->getIntervalForIndex( j ).first + m_histogram->getIntervalForIndex( j ).second;
467  quadLeft *= 0.5;
468  double quadRight = m_histogram->getIntervalForIndex( j + 1 ).first + m_histogram->getIntervalForIndex( j + 1 ).second;
469  quadRight *= 0.5;
470 
471  WVector2d quad[ 4 ];
472  quad[ 0 ] = WVector2d( quadLeft, 0.0 );
473  quad[ 1 ] = WVector2d( quadRight, 0.0 );
474  quad[ 2 ] = WVector2d( quadRight, m_histogram->at( j + 1 ) );
475  quad[ 3 ] = WVector2d( quadLeft, m_histogram->at( j ) );
476 
477  // colors
478  WColor c = color;
479  if( m_data->hasColors() )
480  c = m_data->getColor( j );
481  c[ 3 ] = 0.2;
482 
483  // transform to window coords
484  for( std::size_t i = 0; i < 4; ++i )
485  {
486  quad[ i ] = histogramSpaceToWindowSpace( quad[ i ] );
487  quadVertices->push_back( quad[ i ] );
488  }
489 
490  // tex coords
491  quadTexCoords->push_back( WVector2d( quadLeft, 0.0 ) );
492  quadTexCoords->push_back( WVector2d( quadRight, 0.0 ) );
493  quadTexCoords->push_back( WVector2d( quadRight, 0.0 ) );
494  quadTexCoords->push_back( WVector2d( quadLeft, 0.0 ) );
495 
496  quadColors->push_back( c );
497  quadColors->push_back( c );
498  quadColors->push_back( c );
499  quadColors->push_back( c );
500 
501  // line vertices
502  if( j == 0 )
503  {
504  lineVertices->push_back( quad[ 3 ] );
505  lineColors->push_back( color );
506  }
507  lineVertices->push_back( quad[ 2 ] );
508  lineColors->push_back( color );
509  }
510 
511  // create drawable for the quads
512  {
513  osg::ref_ptr< osg::Geometry > geometry = new osg::Geometry;
514 
515  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, 4 * m_histogram->size() - 4 ) );
516  geometry->setVertexArray( quadVertices );
517  geometry->setColorArray( quadColors );
518  geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
519  geometry->setTexCoordArray( 0, quadTexCoords );
520 
521  // enable VBO
522  geometry->setUseDisplayList( false );
523  geometry->setUseVertexBufferObjects( true );
524 
525  geode->addDrawable( geometry );
526  }
527 
528  // create drawable for the outlines
529  {
530  osg::ref_ptr< osg::Geometry > geometry = new osg::Geometry;
531 
532  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINE_STRIP, 0, m_histogram->size() ) );
533  geometry->setVertexArray( lineVertices );
534  geometry->setColorArray( lineColors );
535  geometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
536 
537  // enable VBO
538  geometry->setUseDisplayList( false );
539  geometry->setUseVertexBufferObjects( true );
540 
541  geode->addDrawable( geometry );
542  }
543 
544  // we do not want any lighting
545  osg::StateSet* state = geode->getOrCreateStateSet();
546  state->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
547 
548  // no depth test
549  state->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
550 
551  // enable blending if we draw stuff on top of each other
552  state->setMode( GL_BLEND, osg::StateAttribute::ON );
553  state->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
554 
555  state->setRenderBinDetails( 1001, "RenderBin" );
556 
557  m_mainNode->insert( geode );
558 }
559 
561 {
563 
564  // update the frame size
567 
568  // this is the geode for the histogram bars
569  osg::ref_ptr< osg::Geode > geode = new osg::Geode();
570  geode->setDataVariance( osg::Object::STATIC );
571 
572  osg::ref_ptr< osg::Vec2Array > lineVertices = new osg::Vec2Array;
573  osg::ref_ptr< osg::Vec4Array > lineColors = new osg::Vec4Array;
574 
575  // one color per dataset
576  WColor color = m_color->get( true );
577 
578  // add lines for every bar/bucket/bin
579  for( std::size_t j = 0; j < m_histogram->size(); ++j )
580  {
581  // 'histogram' coords for bar j
582  std::pair< double, double > barPosHistoCoordsX = m_histogram->getIntervalForIndex( j );
583  WVector2d barLowerLeft( barPosHistoCoordsX.first, 0.0 );
584  WVector2d barUpperRight( barPosHistoCoordsX.second, m_histogram->at( j ) );
585 
586  // transform to window coords
587  barLowerLeft = histogramSpaceToWindowSpace( barLowerLeft );
588  barUpperRight = histogramSpaceToWindowSpace( barUpperRight );
589 
590  if( m_data->hasColors() )
591  color = m_data->getColor( j );
592  color[ 3 ] = 1.0;
593 
594  // line vertices
595  if( j == 0 )
596  {
597  lineVertices->push_back( barLowerLeft );
598  }
599  lineVertices->push_back( WVector2d( barLowerLeft[ 0 ], barUpperRight[ 1 ] ) );
600  lineVertices->push_back( barUpperRight );
601  lineColors->push_back( color );
602  lineColors->push_back( color );
603  if( j == m_histogram->size() - 1 )
604  {
605  lineVertices->push_back( WVector2d( barUpperRight[ 0 ], barLowerLeft[ 1 ] ) );
606  lineColors->push_back( color );
607  }
608  }
609 
610  // create drawable for the lines
611  {
612  osg::ref_ptr< osg::Geometry > geometry = new osg::Geometry;
613 
614  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINE_STRIP, 0, 2 * m_histogram->size() + 2 ) );
615  geometry->setVertexArray( lineVertices );
616  geometry->setColorArray( lineColors );
617  geometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
618 
619  // enable VBO
620  geometry->setUseDisplayList( false );
621  geometry->setUseVertexBufferObjects( true );
622 
623  geode->addDrawable( geometry );
624  }
625 
626  // we do not want any lighting
627  osg::StateSet* state = geode->getOrCreateStateSet();
628  state->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
629 
630  // no depth test
631  state->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
632 
633  state->setRenderBinDetails( 1001, "RenderBin" );
634 
635  m_mainNode->insert( geode );
636 }
637 
638 double WMHistogramView::findOptimalSpacing( double intervalLength, double availableSpace, double textSize )
639 {
640  if( intervalLength < 0.0 )
641  {
642  throw WException( "Error in label spacing calculation!" );
643  }
644 
645  // minimum distance of two labels in window coordinates (pixels)
646  double const minDistance = 2 * textSize;
647 
648  if( availableSpace < minDistance )
649  {
650  return 0.0;
651  }
652 
653  // fact will be the power of 10 close to the interval length
654  double fact = 1.0;
655  double l = intervalLength;
656  while( l > 10.0 )
657  {
658  l *= 0.1;
659  fact *= 10.0;
660  }
661  while( l < 1.0 )
662  {
663  l *= 10.0;
664  fact *= 0.1;
665  }
666 
667  // try different spacings until the distance between labels
668  // for the chosen spacing f[ k ] * fact is larger than the minimum
669  // distance
670  double f[ 9 ] = { 0.1, 0.2, 0.25, 0.5, 1.0, 2.0, 2.5, 5.0, 10.0 };
671  double distance;
672  int k = 0;
673  do
674  {
675  distance = f[ k ] * fact * availableSpace / intervalLength;
676  ++k;
677  }
678  while( distance < minDistance && k < 9 );
679  return f[ k ] * fact;
680 }
681 
683 {
684  // find optimal label spacing for y direction
685  double l = m_histogramUpperRight[ 1 ];
686  double textSize = 12;
687  m_frameSpacing[ 1 ] = findOptimalSpacing( l, m_windowHeight * ( 1.0 - m_frameSize ) - m_framePosition[ 1 ], textSize );
688 
689  int factorYEnd = 1;
690  if( m_frameSpacing[ 1 ] != 0.0 )
691  {
692  // now round the maximum value up to the next multiple of spacing
693  factorYEnd = static_cast< int >( m_histogramUpperRight[ 1 ] / m_frameSpacing[ 1 ] );
694  if( factorYEnd * m_frameSpacing[ 1 ] < m_histogramUpperRight[ 1 ] )
695  {
696  factorYEnd++;
697  }
698  // minumum value is 0 in this case
699  }
700  else
701  {
703  }
704 
705  // find optimal spacing for x direction
707  textSize = 8 * static_cast< int >( std::abs( std::log10( m_frameSpacing[ 0 ] ) ) + 1 );
708  m_frameSpacing[ 0 ] = findOptimalSpacing( l, m_windowWidth * ( 1.0 - m_frameSize ) - m_framePosition[ 0 ], textSize );
709 
710  int factorXStart = 0;
711  int factorXEnd = 1;
712 
713  if( m_frameSpacing[ 0 ] != 0.0 )
714  {
715  // now round the minimum value down to the next multiple of spacing
716  factorXStart = static_cast< int >( m_histogramLowerLeft[ 0 ] / m_frameSpacing[ 0 ] );
717  if( m_histogramLowerLeft[ 0 ] >= 0.0 && factorXStart * m_frameSpacing[ 0 ] < m_histogramLowerLeft[ 0 ] )
718  {
719  factorXStart++;
720  }
721  if( m_histogramLowerLeft[ 0 ] < 0.0 && factorXStart * m_frameSpacing[ 0 ] > m_histogramLowerLeft[ 0 ] )
722  {
723  factorXStart--;
724  }
725 
726  // now round the maximum value up to the next multiple of spacing
727  factorXEnd = static_cast< int >( m_histogramUpperRight[ 0 ] / m_frameSpacing[ 0 ] );
728  if( m_histogramUpperRight[ 0 ] >= 0.0 && factorXEnd * m_frameSpacing[ 0 ] < m_histogramUpperRight[ 0 ] )
729  {
730  factorXEnd++;
731  }
732  if( m_histogramUpperRight[ 0 ] < 0.0 && factorXEnd * m_frameSpacing[ 0 ] > m_histogramUpperRight[ 0 ] )
733  {
734  factorXEnd--;
735  }
736  }
737  else
738  {
739  m_frameSpacing[ 0 ] = l;
740  }
741 
742  m_frameLowerLeft[ 0 ] = factorXStart * m_frameSpacing[ 0 ];
743  m_frameLowerLeft[ 1 ] = 0.0;
744  m_frameUpperRight[ 0 ] = factorXEnd * m_frameSpacing[ 0 ];
745  m_frameUpperRight[ 1 ] = factorYEnd * m_frameSpacing[ 1 ];
746 }
747 
749 {
750  // the funny equation estimates the number of characters needed to write a label times
751  // the estimated size of a character
752  m_framePosition[ 0 ] = 1.3 * 8.0 * static_cast< int >( std::abs( log10( m_frameSpacing[ 1 ] ) ) + 1 );
753  // as we write from left to right, text size is constant in y-direction
754  m_framePosition[ 1 ] = 2 * 1.000000 * 12;
755  // all these formulas are somewhat arbitrary
756 }
757 
759 {
760  WVector2d res = v;
761 
762  // move the histogram to 0,0
763  res -= m_histogramLowerLeft;
764 
765  // scale by the actual size of the part of the window where we want to draw to
766  // the size of that part is the window size minus some space over and to the right of the histogram
767  // and some space under and left to the histogram reserved for the frame and labels
768  //
769  // we also divide by the frame size to fit the framed histogram region to the part of the window
770  //
771  // we use the frame size instead of the histogram size (that we also know) because we want our frame size to be a
772  // multiple of the tick/label spacing
773  // by using the frame size for scaling, we ensure the frame fits into the window (except when the window is too
774  // small, but then you won't see anything anyway)
775  res[ 0 ] *= ( ( 1.0 - m_frameSize ) * m_windowWidth - m_framePosition[ 0 ] ) / ( m_frameUpperRight[ 0 ] - m_frameLowerLeft[ 0 ] );
776  res[ 1 ] *= ( ( 1.0 - m_frameSize ) * m_windowHeight - m_framePosition[ 1 ] ) / ( m_frameUpperRight[ 1 ] - m_frameLowerLeft[ 1 ] );
777 
778  // now translate by the frame position
779  res += m_framePosition;
780 
781  // note that when changing this function, you should change windowSpaceToHistogramSpace(...) accordingly
782  return res;
783 }
784 
786 {
787  // note that when changing this function, you should change histogramSpaceToWindowSpace(...) accordingly
788  WVector2d res = v;
789 
790  res -= m_framePosition;
791 
792  res[ 0 ] /= ( ( 1.0 - m_frameSize ) * m_windowWidth - m_framePosition[ 0 ] ) / ( m_frameUpperRight[ 0 ] - m_frameLowerLeft[ 0 ] );
793  res[ 1 ] /= ( ( 1.0 - m_frameSize ) * m_windowHeight - m_framePosition[ 1 ] ) / ( m_frameUpperRight[ 1 ] - m_frameLowerLeft[ 1 ] );
794 
795  res += m_histogramLowerLeft;
796 
797  return res;
798 }
799 
801 {
802  m_frameNode = new osg::Geode();
803  m_frameNode->setDataVariance( osg::Object::STATIC );
804 
805  osg::ref_ptr< osg::Vec2Array > lineVertices = new osg::Vec2Array;
806  osg::ref_ptr< osg::Vec4Array > lineColors = new osg::Vec4Array;
807 
808  // add color
809  WColor const frameColor( 0.2, 0.2, 0.2, 0.8 );
810  lineColors->push_back( frameColor );
811 
812  // horizontal frame
813  lineVertices->push_back( histogramSpaceToWindowSpace( m_frameLowerLeft ) );
814  lineVertices->push_back( histogramSpaceToWindowSpace( WVector2d( m_frameUpperRight[ 0 ], m_frameLowerLeft[ 1 ] ) ) );
815 
816  // vertical frame
817  lineVertices->push_back( histogramSpaceToWindowSpace( m_frameLowerLeft ) );
818  lineVertices->push_back( histogramSpaceToWindowSpace( WVector2d( m_frameLowerLeft[ 0 ], m_frameUpperRight[ 1 ] ) ) );
819 
820  WVector2d offsetX( 6, 0.0 );
821  WVector2d offsetY( 0.0, 6 );
822 
823  // label ticks
824  int numLabels = 0;
825  // x axis
826  for( double i = m_frameLowerLeft[ 0 ]; i <= m_frameUpperRight[ 0 ]; i += m_frameSpacing[ 0 ] )
827  {
828  lineVertices->push_back( histogramSpaceToWindowSpace( WVector2d( i, 0.0 ) ) );
829  lineVertices->push_back( histogramSpaceToWindowSpace( WVector2d( i, 0.0 ) ) - offsetY );
830  ++numLabels;
831 
832  // add label text
833  osgText::Text* text = new osgText::Text;
834  WVector2d textPos = histogramSpaceToWindowSpace( WVector2d( i, 0.0 ) ) - offsetY * 1.2;
835 
836  text->setFont( WPathHelper::getAllFonts().Default.string() );
837  text->setColor( frameColor );
838  text->setCharacterSize( 12 );
839  text->setAlignment( osgText::TextBase::CENTER_TOP );
840  text->setPosition( WVector3d( textPos[ 0 ], textPos[ 1 ], 0.0 ) );
841  text->setLayout( osgText::Text::LEFT_TO_RIGHT );
842  text->setText( string_utils::toString( i ) );
843 
844  m_frameNode->addDrawable( text );
845  }
846 
847  // y axis
848  for( double i = m_frameLowerLeft[ 1 ]; i <= m_frameUpperRight[ 1 ]; i += m_frameSpacing[ 1 ] )
849  {
850  lineVertices->push_back( histogramSpaceToWindowSpace( WVector2d( m_frameLowerLeft[ 0 ], i ) ) );
851  lineVertices->push_back( histogramSpaceToWindowSpace( WVector2d( m_frameLowerLeft[ 0 ], i ) ) - offsetX );
852  ++numLabels;
853 
854  // add label text
855  osgText::Text* text = new osgText::Text;
856  WVector2d textPos = histogramSpaceToWindowSpace( WVector2d( m_frameLowerLeft[ 0 ], i ) ) - offsetX * 1.2;
857 
858  text->setFont( WPathHelper::getAllFonts().Default.string() );
859  text->setColor( frameColor );
860  text->setCharacterSize( 12 );
861  text->setAlignment( osgText::TextBase::RIGHT_CENTER );
862  text->setPosition( WVector3d( textPos[ 0 ], textPos[ 1 ], 0.0 ) );
863  text->setLayout( osgText::Text::LEFT_TO_RIGHT );
864  text->setText( string_utils::toString( i ) );
865 
866  m_frameNode->addDrawable( text );
867  }
868 
869  // create drawable for the lines
870  {
871  osg::ref_ptr< osg::Geometry > geometry = new osg::Geometry;
872 
873  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINES, 0, 4 + 2 * numLabels ) );
874  geometry->setVertexArray( lineVertices );
875  geometry->setColorArray( lineColors );
876  geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
877 
878  // enable VBO
879  geometry->setUseDisplayList( false );
880  geometry->setUseVertexBufferObjects( true );
881 
882  m_frameNode->addDrawable( geometry );
883  }
884 
885  // we do not want any lighting
886  osg::StateSet* state = m_frameNode->getOrCreateStateSet();
887  state->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
888 
889  // no depth test
890  state->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
891 
892  state->setRenderBinDetails( 1002, "RenderBin" );
893  state->setMode( GL_BLEND, osg::StateAttribute::OFF );
894 
895  m_mainNode->insert( m_frameNode );
896 }
897 
899 {
900  m_createInfoMutex.lock();
901  // transform mouse position to histogram space
903 
904  if( m[ 0 ] >= m_histogramLowerLeft[ 0 ] && m[ 0 ] <= m_histogramUpperRight[ 0 ] )
905  {
906  m_infoNode = new osg::Geode;
907 
908  osg::StateSet* state = m_infoNode->getOrCreateStateSet();
909  state->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
910  state->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
911  state->setMode( GL_BLEND, osg::StateAttribute::OFF );
912  state->setRenderBinDetails( 1002, "RenderBin" );
913 
914  // this finds the bin selected by the mouse cursor
915  std::size_t bin;
916  for( bin = 0; m_histogram->getIntervalForIndex( bin ).second < m[ 0 ]; ++bin )
917  {
918  }
919 
920  // if the bin is in the histogram
921  if( bin < m_histogram->size() )
922  {
923  {
924  WVector3d textPos( m_windowWidth - 20.0, m_windowHeight - 16, 0.0 );
925 
926  osgText::Text* text = new osgText::Text;
927 
928  text->setFont( WPathHelper::getAllFonts().Default.string() );
929  text->setColor( m_color->get( false ) );
930  text->setCharacterSize( 12 );
931  text->setAlignment( osgText::TextBase::RIGHT_CENTER );
932  text->setPosition( textPos );
933  text->setLayout( osgText::Text::LEFT_TO_RIGHT );
934  text->setText( string_utils::toString( m_histogram->at( bin ) ) );
935 
936  m_infoNode->addDrawable( text );
937  }
938 
939  // add the bin minimum and maximum
940  WVector3d textPos( m_windowWidth - 20.0, m_windowHeight - 32, 0.0 );
941 
942  osgText::Text* text = new osgText::Text;
943  std::stringstream s;
944  s << "[" << m_histogram->getIntervalForIndex( bin ).first
945  << "," << m_histogram->getIntervalForIndex( bin ).second
946  << ")";
947 
948  text->setFont( WPathHelper::getAllFonts().Default.string() );
949  text->setColor( WColor( 0.0, 0.0, 0.0, 1.0 ) );
950  text->setCharacterSize( 12 );
951  text->setAlignment( osgText::TextBase::RIGHT_CENTER );
952  text->setPosition( textPos );
953  text->setLayout( osgText::Text::LEFT_TO_RIGHT );
954  text->setText( s.str() );
955 
956  m_infoNode->addDrawable( text );
957 
958  // mark the currently selected histogram bin by simple drawing a bar in the background
959  m_markerNode = new osg::Geode;
960 
961  osg::StateSet* markerState = m_markerNode->getOrCreateStateSet();
962  markerState->setMode( GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED );
963  markerState->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
964  markerState->setMode( GL_BLEND, osg::StateAttribute::OFF );
965  markerState->setRenderBinDetails( 1000, "RenderBin" );
966 
967  osg::ref_ptr< osg::Vec2Array > quadVertices = new osg::Vec2Array;
968  osg::ref_ptr< osg::Vec4Array > quadColors = new osg::Vec4Array;
969 
970  // the color should be adapted to the window background color
971  quadColors->push_back( WColor( 0.95, 0.95, 0.95, 1.0 ) );
972 
973  quadVertices->push_back( histogramSpaceToWindowSpace(
974  WVector2d( m_histogram->getIntervalForIndex( bin ).first, 0.0 ) ) );
975  quadVertices->push_back( histogramSpaceToWindowSpace(
976  WVector2d( m_histogram->getIntervalForIndex( bin ).second, 0.0 ) ) );
977  quadVertices->push_back( histogramSpaceToWindowSpace(
978  WVector2d( m_histogram->getIntervalForIndex( bin ).second, m_histogramUpperRight[ 1 ] ) ) );
979  quadVertices->push_back( histogramSpaceToWindowSpace(
980  WVector2d( m_histogram->getIntervalForIndex( bin ).first, m_histogramUpperRight[ 1 ] ) ) );
981 
982  osg::Geometry* geometry = new osg::Geometry;
983 
984  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUADS, 0, 4 ) );
985  geometry->setVertexArray( quadVertices );
986  geometry->setColorArray( quadColors );
987  geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
988 
989  // enable VBO
990  geometry->setUseDisplayList( false );
991  geometry->setUseVertexBufferObjects( true );
992 
993  m_markerNode->addDrawable( geometry );
994 
995  // TODO(reichenbach): only insert the info node if there is enough space to do so
996 
997  m_mainNode->insert( m_infoNode );
998  m_mainNode->insert( m_markerNode );
999  }
1000  }
1001  m_createInfoMutex.unlock();
1002 }
1003 
1005 {
1006  errorLog() << "This histogram style is not yet implemented.";
1007 }
virtual void wait() const
Wait for the condition.
void setResetable(bool resetable=true, bool autoReset=true)
Sets the resetable flag.
virtual void add(std::shared_ptr< WCondition > condition)
Adds another condition to the set of conditions to wait for.
Class to encapsulate boost::condition_variable_any.
Definition: WCondition.h:42
This is a simple but thread-safe counter.
Definition: WCounter.h:37
Basic exception handler.
Definition: WException.h:39
std::shared_ptr< WCondition > getValueChangeCondition()
Returns the condition denoting a value change.
Definition: WFlag.h:325
This requirement ensures an up and running WGE.
A class containing a list of named items.
static WKernel * getRunningKernel()
Returns pointer to the currently running kernel.
Definition: WKernel.cpp:117
std::shared_ptr< WUI > getUI() const
Getter for the associated UI.
Definition: WKernel.cpp:132
void updateHistogramMax()
This updates the maximum value of the histograms.
osg::ref_ptr< osg::Geode > m_infoNode
Draws histogram bin info to the top right corner of the window.
std::shared_ptr< WHistogramBasic const > m_histogram
A vector of histograms, one histogram per input.
WVector2d histogramSpaceToWindowSpace(WVector2d const &v)
This transforms histogram space coordinates to window coordinates.
boost::mutex m_createInfoMutex
Whenever a new info node is made this mutex should be used.
virtual void connectors()
Initialize the connectors this module is using.
static WCounter m_instanceCounter
The instance counter used to get the instance ID.
WMHistogramView()
Constuctor.
WVector2d m_framePosition
The space to the left and under the frame in window coordinates.
int m_windowWidth
The width of the window.
WVector2d m_frameSpacing
The spacing between labels at the histogram axis in histogram coordinates.
WVector2d m_histogramLowerLeft
The lower left corner of the histogram in histogram coordinates.
osg::ref_ptr< osg::Geode > m_markerNode
Draws a marker showing the currently selected histogram bin.
osg::ref_ptr< WGEGroupNode > m_mainNode
The scene node of the custom window. All geometry nodes are added as children of this node.
void handleResize(int x, int y, int width, int height)
Called on every resize event from the custom widget.
virtual void requirements()
Initialize requirements for this module.
virtual const std::string getName() const
Gives back the name of this module.
WUIViewWidget::SPtr m_widget
Holds the reference to the custom widget used for displaying the histogram.
std::shared_ptr< WCondition > m_propCondition
A condition for property updates.
void createFrame()
Creates the geometry for the frame and the ticks/labels.
double const m_frameSize
The distance between the histogram frame and the top resp. right side of the window in relative windo...
void handleMouseMove(WVector2f pos)
Called on every mouse move event from the custom widget.
void calculateFramePosition()
Finds a good position of the frame relative to the lower left corner of the window.
void calculateHistograms()
This simply calculates a histogram per dataset, where the bin sizes and positions are the same for ea...
virtual void properties()
Initialize the properties for this module.
void calculateFrameSize()
Finds a good size for the frame, depending on the chosen spacing for axis labels.
void createGeometryStairs()
Creates the geometry for stairs (i.e.
osg::ref_ptr< osg::Geode > m_frameNode
Draws the frame and ticks/labels.
WVector2d windowSpaceToHistogramSpace(WVector2d const &v)
This is the inverse of histogramSpaceToWindowSpace.
void createNothing()
This simply prints a NYI message to the errorLog.
WVector2d m_histogramUpperRight
The upper right corner of the histogram in histogram coordinates.
std::vector< boost::function< void(void) > > m_geometryFunctions
A vector containing functions to use for histogram geometry generation.
virtual void moduleMain()
Entry point after loading the module.
virtual std::shared_ptr< WModule > factory() const
Due to the prototype design pattern used to build modules, this method returns a new instance of this...
void createGeometryBars()
Creates the geometry for histogram bars.
boost::mutex m_redrawMutex
Whenever a redraw is made this mutex should be used.
WVector2d m_frameUpperRight
The upper right vertex of the frame box in histogram coordinates.
int m_instanceID
The number of this WMHistogram instance. Used to generate a unique window title for every instance of...
int m_windowHeight
The height of the window.
WPropSelection m_styleSelection
Allows to select which one of the geometry generation functions should be used.
std::shared_ptr< WModuleInputData< WDataSetHistogram1D > > m_input
The input connector.
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
WVector2d m_frameLowerLeft
The lower left vertex of the frame box in histogram coordinates.
void createGeometryCurves()
Creates the geometry for curves.
virtual ~WMHistogramView()
Destructor.
WPropColor m_color
The color properties for the dataset.
virtual const std::string getDescription() const
Gives back a description of this module.
void redraw()
Redraws the histogram and add it to the main geode.
void createInfo(WVector2f mousePos)
Writes the values of the currently selected histogram bin to the top right corner of the window.
double findOptimalSpacing(double intervalLength, double availableSpace, double textSize)
This finds a suitable spacing of ticks to use for an axis with a certain length and value interval.
std::shared_ptr< WDataSetHistogram1D > m_data
The histogram to show.
A fixed size matrix class.
Definition: WMatrixFixed.h:150
Class offering an instantiate-able data connection between modules.
Class representing a single module of OpenWalnut.
Definition: WModule.h:72
Requirements m_requirements
The list of requirements.
Definition: WModule.h:754
virtual void properties()
Initialize properties in this function.
Definition: WModule.cpp:212
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
Definition: WModule.cpp:575
void addConnector(std::shared_ptr< WModuleInputConnector > con)
Adds the specified connector to the list of inputs.
Definition: WModule.cpp:108
std::shared_ptr< WProperties > m_properties
The property object for the module.
Definition: WModule.h:640
void ready()
Call this whenever your module is ready and can react on property changes.
Definition: WModule.cpp:505
WConditionSet m_moduleState
The internal state of the module.
Definition: WModule.h:703
wlog::WStreamedLogger errorLog() const
Logger instance for comfortable error logging.
Definition: WModule.cpp:570
wlog::WStreamedLogger infoLog() const
Logger instance for comfortable info logging.
Definition: WModule.cpp:565
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
static Fonts getAllFonts()
The paths to all fonts supported.
WBoolFlag m_shutdownFlag
Condition getting fired whenever the thread should quit.
This requirement ensures an up and running UI which properly implements the WUI interface.
An event handler for a custom widget which eases interaction with GUIEvents within your module.
void addTo(WPropSelection prop)
Add the PC_SELECTONLYONE constraint to the property.
std::string toString(const T &value)
Convert a given value to a string.
Definition: WStringUtils.h:120