OpenWalnut  1.5.0dev
WMEEGView.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 <memory>
26 #include <string>
27 #include <vector>
28 
29 #include <osg/BlendFunc>
30 #include <osg/LightModel>
31 #include <osg/ShapeDrawable>
32 #include <osgSim/ColorRange>
33 
34 #include "WEEGEvent.h"
35 #include "WEEGSourceCalculator.h"
36 #include "WEEGViewHandler.h"
37 #include "WElectrodePositionCallback.h"
38 #include "WHeadSurfaceCallback.h"
39 #include "WLabelsTransformCallback.h"
40 #include "WLineStripCallback.h"
41 #include "WMEEGView.h"
42 #include "WMEEGView.xpm"
43 #include "WPanTransformCallback.h"
44 #include "WScaleTransformCallback.h"
45 #include "core/dataHandler/WDataSetDipoles.h"
46 #include "core/dataHandler/WEEG2.h"
47 #include "core/dataHandler/WEEG2Segment.h"
48 #include "core/dataHandler/WEEGChannelInfo.h"
49 #include "core/dataHandler/WEEGValueMatrix.h"
50 #include "core/graphicsEngine/WGEGeodeUtils.h"
51 #include "core/graphicsEngine/WGEGeometryUtils.h"
52 #include "core/graphicsEngine/WGEGroupNode.h"
53 #include "core/graphicsEngine/WGERequirement.h"
54 #include "core/graphicsEngine/WGEUtils.h"
55 #include "core/graphicsEngine/WROIBox.h"
56 #include "core/kernel/WKernel.h"
57 #include "core/kernel/WModuleInputData.h"
58 #include "core/kernel/WROIManager.h"
59 #include "core/ui/WUI.h"
60 #include "core/ui/WUIViewWidget.h"
61 
62 // This line is needed by the module loader to actually find your module.
63 W_LOADABLE_MODULE( WMEEGView )
64 
66  : WModule(),
67  m_dataChanged( new WCondition, true ),
68  m_wasActive( false ),
69  m_currentEventTime( -1.0 )
70 {
71 }
72 
74 {
75 }
76 
77 std::shared_ptr< WModule > WMEEGView::factory() const
78 {
79  return std::shared_ptr< WModule >( new WMEEGView() );
80 }
81 
82 const std::string WMEEGView::getName() const
83 {
84  return "EEG View";
85 }
86 
87 const std::string WMEEGView::getDescription() const
88 {
89  return "Displays EEG data.";
90 }
91 
92 const char** WMEEGView::getXPMIcon() const
93 {
94  return eeg_xpm;
95 }
96 
97 
99 {
100  // we need graphics to draw anything
101  m_requirements.push_back( new WGERequirement() );
102  m_requirements.push_back( new WUIRequirement() );
103 }
104 
106 {
107  m_eeg_input = std::shared_ptr< WModuleInputData< WEEG2 > >( new WModuleInputData< WEEG2 >(
108  shared_from_this(), "EEG recording", "Loaded EEG-dataset." ) );
110 
111  m_dipoles_input = std::shared_ptr< WModuleInputData< WDataSetDipoles > >( new WModuleInputData< WDataSetDipoles >(
112  shared_from_this(), "Dipoles", "The reconstructed dipoles for the EEG." ) );
114 
115  // call WModules initialization
117 }
118 
120 {
121  m_propCondition = std::shared_ptr< WCondition >( new WCondition() );
122  m_drawElectrodes = m_properties->addProperty( "Draw electrodes",
123  "Draw the 3D positions of the electrodes.",
124  true,
125  m_propCondition );
126  m_drawHeadSurface = m_properties->addProperty( "Draw head surface",
127  "Draw the head surface between the electrodes.",
128  true,
129  m_propCondition );
130  m_drawLabels = m_properties->addProperty( "Draw labels",
131  "Draw the labels of the electrodes at their 3D positions.",
132  true,
133  m_propCondition );
134  m_proofOfConcept = m_properties->addProperty( "Enable POC",
135  "Use proof of concept (POC) ROI positioning instead of real dipoles position.",
136  false,
137  m_propCondition );
138  m_snapToDipole = m_properties->addProperty( "Snap to dipole",
139  "If a time is selected, snap to the nearest time position with an active dipole.",
140  true,
141  m_propCondition );
142  m_butterfly = m_properties->addProperty( "Butterfly plot",
143  "Overlay all curves in one row.",
144  false,
145  m_propCondition );
146  m_ROIsize = m_properties->addProperty( "ROI size",
147  "The width ROI box.",
148  30.0,
149  m_propCondition );
150  m_ROIsize->setMin( 0.0 );
151  m_ROIsize->setMax( 50.0 );
152 
153 
154  m_appearanceGroup = m_properties->addPropertyGroup( "Appearance",
155  "Modification of the appearance of the EEG View widget" );
156  m_labelsWidth = m_appearanceGroup->addProperty( "Labels width",
157  "The width of the label display in pixel.",
158  24 );
159 
160  m_graphWidth = m_appearanceGroup->addProperty( "Graph width",
161  "The width of the graph in pixel.",
162  992 );
163  m_ySpacing = m_appearanceGroup->addProperty( "Spacing",
164  "The distance between two curves of the graph in pixel.",
165  23.0 );
166 
167  m_colorSensitivity = m_appearanceGroup->addProperty( "Color sensitivity",
168  "The sensitivity of the color map. It ranges from -Color Sensitivity to +Color Sensitivity in microvolt.",
169  23.0 );
170 
171 
172 
173  m_manualNavigationGroup = m_properties->addPropertyGroup( "Manual Navigation",
174  "Manually modify the parameters that are usually changes by mouse"
175  " actions in th EEG view." );
176  m_ySensitivity = m_manualNavigationGroup->addProperty( "Sensitivity",
177  "The sensitivity of the graph in microvolt per pixel.",
178  2.0 );
179  m_timePos = m_manualNavigationGroup->addProperty( "Time position",
180  "The time position in seconds where to start the graph at the left edge.",
181  0.0 );
182  m_timeRange = m_manualNavigationGroup->addProperty( "Time range",
183  "The width of the graph in seconds.",
184  4.0 );
185  m_yPos = m_manualNavigationGroup->addProperty( "Y position",
186  "The y position in pixel at the lower edge.",
187  -724.5 );
188 
189  m_labelsWidth->setMin( 0 );
190  m_labelsWidth->setMax( 64 );
191  m_timePos->setMin( 0.0 );
192  m_timePos->setMax( 86400.0 );
193  m_timeRange->setMin( 0.01 );
194  m_timeRange->setMax( 60.0 );
195  m_graphWidth->setMin( 1 );
196  m_graphWidth->setMax( 4096 );
197  m_yPos->setMin( -1048576.0 );
198  m_yPos->setMax( 0.0 );
199  m_ySpacing->setMin( 0.0 );
200  m_ySpacing->setMax( 1024.0 );
201  m_ySensitivity->setMin( 0.001 );
202  m_ySensitivity->setMax( 100.0 );
203  m_colorSensitivity->setMin( 0.01 );
204  m_colorSensitivity->setMax( 10000.0 );
205 
206 
208 }
209 
211  std::shared_ptr< WModuleConnector > /*here*/, std::shared_ptr< WModuleConnector > /*there*/ )
212 {
213  m_dataChanged.set( true );
214 }
215 
217  std::shared_ptr< WModuleConnector > /*input*/, std::shared_ptr< WModuleConnector > /*output*/ )
218 {
219  m_dataChanged.set( true );
220 }
221 
223 {
224  // do initialization
225  // add conditions to m_moduleState
226  m_moduleState.setResetable( true, true );
228  m_moduleState.add( m_active->getCondition() );
230 
231  // create WFlag for the event
232  m_event = std::shared_ptr< WFlag< std::shared_ptr< WEEGEvent > > >( new WFlag< std::shared_ptr< WEEGEvent > >(
233  m_propCondition, std::shared_ptr< WEEGEvent >() ) );
234 
235 
236  createColorMap();
237  m_ROIsize->get( true ); // reset changed state
238 
239  // signal ready
240  ready();
241 
242  while( !m_shutdownFlag() ) // loop until the module container requests the module to quit
243  {
244  // data changed?
245  if( m_dataChanged() || m_butterfly->changed() )
246  {
247  debugLog() << "Data changed";
248  m_dataChanged.set( false );
249  if( m_eeg_input.get() )
250  {
251  m_eeg = m_eeg_input->getData();
252  }
253  if( m_dipoles_input.get() )
254  {
255  m_dipoles = m_dipoles_input->getData();
256  }
257  redraw();
258  }
259 
260  // draw electrodes property changed?
261  if( m_drawElectrodes->changed() )
262  {
263  if( m_electrodesNode.valid() )
264  {
265  m_rootNode3d->remove( m_electrodesNode );
266  }
267 
268  if( m_drawElectrodes->get( true ) && m_eeg.get() )
269  {
271  m_rootNode3d->insert( m_electrodesNode );
272  }
273  else
274  {
275  m_electrodesNode = NULL;
276  }
277  }
278 
279  // draw head surface property changed?
280  if( m_drawHeadSurface->changed() )
281  {
282  if( m_headSurfaceNode.valid() )
283  {
284  m_rootNode3d->remove( m_headSurfaceNode );
285  }
286 
287  if( m_drawHeadSurface->get( true ) && m_eeg.get() )
288  {
290  m_rootNode3d->insert( m_headSurfaceNode );
291  }
292  else
293  {
294  m_headSurfaceNode = NULL;
295  }
296  }
297 
298  // draw labels property changed?
299  if( m_drawLabels->changed() )
300  {
301  if( m_labelsNode.valid() )
302  {
303  m_rootNode3d->remove( m_labelsNode );
304  }
305 
306  if( m_drawLabels->get( true ) && m_eeg.get() )
307  {
309  m_rootNode3d->insert( m_labelsNode );
310  }
311  else
312  {
313  m_labelsNode = NULL;
314  }
315  }
316 
317  // event position changed?
318  std::shared_ptr< WEEGEvent > event = m_event->get();
319  if( ( event && event->getTime() != m_currentEventTime )
320  || m_ROIsize->changed() )
321  {
322  debugLog() << "New event position: " << event->getTime();
323 
324  for( std::vector< osg::ref_ptr< WROIBox > >::iterator iter = m_rois.begin(); iter != m_rois.end(); ++iter )
325  {
326  WKernel::getRunningKernel()->getRoiManager()->removeRoi( *iter );
327  }
328  m_rois.clear();
329 
330  if( m_sourceCalculator )
331  {
332  if( m_proofOfConcept->get() )
333  {
334  WPosition position = m_sourceCalculator->calculate( event );
335  float halfWidth = m_ROIsize->get( true ) * 0.5;
336  m_rois.push_back( new WROIBox( position - WVector3d( halfWidth, halfWidth, halfWidth ),
337  position + WVector3d( halfWidth, halfWidth, halfWidth ) ) );
338  WKernel::getRunningKernel()->getRoiManager()->addRoi( m_rois.back() );
339  }
340  else if( m_dipoles.get() )
341  {
342  debugLog() << "Number of Dipoles: " << m_dipoles->getNumberOfDipoles();
343  for( size_t dipoleId = 0u; dipoleId < m_dipoles->getNumberOfDipoles(); ++dipoleId )
344  {
345  debugLog() << "Dipole[" << dipoleId << "]: " << m_dipoles->getMagnitude( event->getTime(), dipoleId );
346  if( m_dipoles->getMagnitude( event->getTime(), dipoleId ) != 0 )
347  {
348  float halfWidth = m_ROIsize->get( true ) * 0.5;
349  WPosition position = m_dipoles->getPosition( dipoleId );
350  m_rois.push_back( new WROIBox( position - WVector3d( halfWidth, halfWidth, halfWidth ),
351  position + WVector3d( halfWidth, halfWidth, halfWidth ) ) );
352  WKernel::getRunningKernel()->getRoiManager()->addRoi( m_rois.back() );
353  }
354  }
355  }
356  else
357  {
358  debugLog() << "No dipoles found and not in POC mode: placing NO ROI.";
359  }
360  }
361 
362  m_currentEventTime = event->getTime();
363  }
364 
365  // "active" property changed?
366  bool isActive = m_active->get();
367  if( isActive != m_wasActive )
368  {
369  if( isActive )
370  {
371  if( m_rootNode3d.valid() )
372  {
373  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_rootNode3d );
374  }
375 
376  if( !openCustomWidget() )
377  {
378  // Shut down module if widget could not be opened.
379  m_shutdownFlag.set( true );
380  }
381  }
382  else
383  {
385  if( m_rootNode3d.valid() )
386  {
387  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_rootNode3d );
388  }
389  }
390  m_wasActive = isActive;
391  }
392 
393  m_moduleState.wait(); // waits for firing of m_moduleState ( dataChanged, shutdown, etc. )
394  }
395 
396  // clean up
397  if( m_wasActive )
398  {
400  if( m_rootNode3d.valid() )
401  {
402  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_rootNode3d );
403  }
404  }
405 }
406 
408 {
409  // create color map
410  std::vector< osg::Vec4 > colors;
411  colors.reserve( 3 );
412  colors.push_back( osg::Vec4( 0.0, 0.0, 1.0, 1.0 ) ); // blue
413  colors.push_back( osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) ); // white
414  colors.push_back( osg::Vec4( 1.0, 0.0, 0.0, 1.0 ) ); // red
415  m_colorMap = new osgSim::ColorRange( -1.0, 1.0, colors );
416 
417  // create texture containing color map
418  const int size = 256;
419  osg::Image* image = new osg::Image;
420  // allocate the image data, size x 1 x 1 with 4 rgba floats - equivalent to a Vec4!
421  image->allocateImage( size, 1, 1, GL_RGBA, GL_FLOAT );
422  image->setInternalTextureFormat( GL_RGBA );
423 
424  osg::Vec4* data = reinterpret_cast< osg::Vec4* >( image->data() );
425  for( int i = 0; i < size; ++i )
426  {
427  data[i] = m_colorMap->getColor( ( 2 * i + 1 - size ) / static_cast< float >( size - 1 ) );
428  }
429 
430  m_colorMapTexture = new osg::Texture1D( image );
431  m_colorMapTexture->setWrap( osg::Texture1D::WRAP_S, osg::Texture1D::CLAMP_TO_EDGE );
432  m_colorMapTexture->setFilter( osg::Texture1D::MIN_FILTER, osg::Texture1D::LINEAR );
433 }
434 
436 {
437  debugLog() << "Try to open EEG View widget...";
438  m_widget = WKernel::getRunningKernel()->getUI()->getWidgetFactory()->createViewWidget( getName(),
439  WGECamera::TWO_D, m_shutdownFlag.getCondition() );
440  m_widget->show();
441  bool success = m_widget.get();
442  if( success )
443  {
444  debugLog() << "Successfully opened EEG View widget.";
445 
446  if( m_handler )
447  {
448  m_widget->getViewer()->getView()->addEventHandler( m_handler );
449  }
450 
451  if( m_rootNode2d.valid() )
452  {
453  debugLog() << "Adding rootNode to scene after opened EEG View widget";
454  m_widget->getScene()->insert( m_rootNode2d );
455  }
456  }
457  else
458  {
459  warnLog() << "Could not create EEG View widget.";
460  }
461  return success;
462 }
463 
465 {
466  if( m_rootNode2d.valid() )
467  {
468  m_widget->getScene()->remove( m_rootNode2d );
469  }
470 
471  if( m_handler )
472  {
473  m_widget->getViewer()->getView()->getEventHandlers().remove( m_handler );
474  }
475 
476  m_widget->close();
477  m_widget.reset(); // forces need call of destructor
478 }
479 
481 {
482  if( m_wasActive )
483  {
484  if( m_rootNode2d.valid() )
485  {
486  m_widget->getScene()->remove( m_rootNode2d );
487  }
488  if( m_rootNode3d.valid() )
489  {
490  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->remove( m_rootNode3d );
491  }
492  if( m_handler )
493  {
494  m_widget->getViewer()->getView()->getEventHandlers().remove( m_handler );
495  }
496  }
497 
498  // reset event position
499  if( !m_butterfly->changed( true ) )
500  {
501  m_event->set( std::shared_ptr< WEEGEvent >( new WEEGEvent ) );
502  }
503 
504  if( m_eeg.get() && m_eeg->getNumberOfSegments() > 0 )
505  {
506  const float text2dOffset = 2.0f;
507  const float text2dSize = 12.0f;
508  const osg::Vec4 text2dColor( 0.0, 0.0, 0.0, 1.0 );
509  const osg::Vec4 linesColor( 0.0, 0.0, 0.0, 1.0 );
510 
512  osg::StateSet* stateset = m_rootNode2d->getOrCreateStateSet();
513  stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
514 
516 
517  debugLog() << "Displaying EEG " << m_eeg->getFilename();
518  debugLog() << " Number of segments: " << m_eeg->getNumberOfSegments();
519  size_t nbChannels = m_eeg->getNumberOfChannels();
520  debugLog() << " Number of channels: " << nbChannels;
521  double rate = m_eeg->getSamplingRate();
522  debugLog() << " Sampling Rate: " << rate;
523  debugLog() << " Segment " << 0;
524  std::shared_ptr< WEEG2Segment > segment = m_eeg->getSegment( 0 );
525  size_t nbSamples = segment->getNumberOfSamples();
526  debugLog() << " Number of Samples: " << nbSamples;
527 
528  const float heightConstant = 736.0;
529  // reset and adjust properties to given dataset
530  if( m_butterfly->get() )
531  {
532  m_ySpacing->set( 0 );
533  m_yPos->set( -heightConstant / nbChannels );
534  }
535  else
536  {
537  m_ySpacing->set( heightConstant / nbChannels );
538  m_yPos->set( ( heightConstant * 0.5 ) / nbChannels - heightConstant );
539  }
540  m_timePos->set( 0.0 );
541  m_timePos->setMax( nbSamples / rate );
542 
543  // draw 2D plot
544  // create text geode for the channel labels
545  osg::Geode* textGeode = new osg::Geode;
546 
547  // create pan matrix transfom node
548  osg::MatrixTransform* panTransform = new osg::MatrixTransform;
549  panTransform->setDataVariance( osg::Object::DYNAMIC );
550  panTransform->setUpdateCallback( new WPanTransformCallback( m_labelsWidth, m_timePos, m_timeRange, m_graphWidth, m_yPos ) );
551 
552  for( size_t channelID = 0; channelID < nbChannels; ++channelID )
553  {
554  debugLog() << " Channel " << channelID;
555  std::shared_ptr< WEEGChannelInfo > channelInfo = m_eeg->getChannelInfo( channelID );
556  debugLog() << " Channel unit: " << channelInfo->getUnit();
557  debugLog() << " Channel label: " << channelInfo->getLabel();
558 
559  // create text for the channel label
560  osgText::Text* text = new osgText::Text;
561  text->setText( channelInfo->getLabel() );
562  text->setPosition( osg::Vec3( text2dOffset, -static_cast< float >( channelID ), 0.0f ) );
563  text->setAlignment( osgText::Text::LEFT_CENTER );
564  text->setAxisAlignment( osgText::Text::SCREEN );
565  text->setCharacterSize( text2dSize );
566  text->setCharacterSizeMode( osgText::Text::SCREEN_COORDS );
567  text->setColor( text2dColor );
568 
569  textGeode->addDrawable( text );
570 
571  // create geode to draw the actual data as line strip
572  osg::Geometry* geometry = new osg::Geometry;
573 
574  osg::Vec4Array* colors = new osg::Vec4Array;
575  colors->push_back( linesColor );
576  geometry->setColorArray( colors );
577  geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
578 
579  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINE_STRIP, 0, 0 ) );
580 
581  geometry->setDataVariance( osg::Object::DYNAMIC );
582  geometry->setUpdateCallback( new WLineStripCallback( channelID, m_timePos, m_timeRange, segment, rate ) );
583 
584  osg::Geode* linesGeode = new osg::Geode;
585  linesGeode->addDrawable( geometry );
586 
587  // create scaling matrix transform node
588  osg::MatrixTransform* scaleTransform = new osg::MatrixTransform;
589  scaleTransform->setDataVariance( osg::Object::DYNAMIC );
590  scaleTransform->setUpdateCallback( new WScaleTransformCallback( channelID, m_ySpacing, m_ySensitivity ) );
591 
592  // connect all creates nodes
593  scaleTransform->addChild( linesGeode );
594  panTransform->addChild( scaleTransform );
595  }
596 
597  // create matrix transform node for the labels
598  osg::MatrixTransform* labelsTransform = new osg::MatrixTransform;
599  labelsTransform->setDataVariance( osg::Object::DYNAMIC );
600  labelsTransform->setUpdateCallback( new WLabelsTransformCallback( m_yPos, m_ySpacing ) );
601  labelsTransform->addChild( textGeode );
602 
603  // create group node as parent for the events
604  WGEGroupNode* eventParentNode = new WGEGroupNode;
605  panTransform->addChild( eventParentNode );
606 
607  // create geode to draw the area were the dipole is active
608  if( m_dipoles.get() )
609  {
610  for( size_t dipoleId = 0u; dipoleId < m_dipoles->getNumberOfDipoles(); ++dipoleId )
611  {
612  debugLog() << "Dipole[" << dipoleId << "]: " << m_dipoles->getStartTime( dipoleId )
613  << " to " << m_dipoles->getEndTime( dipoleId );
614 
615  const osg::Vec4 color_min( 1.0f, 1.0f, 1.0f, 0.0f );
616  const osg::Vec4 color_max( 1.0f, 0.0f, 0.0f, 1.0f );
617 
618  const std::vector<float> times = m_dipoles->getTimes( dipoleId );
619  const std::vector<float> magnitudes = m_dipoles->getMagnitudes( dipoleId );
620 
621  osg::Geometry* geometry = new osg::Geometry;
622 
623  osg::Vec3Array* vertices = new osg::Vec3Array;
624  vertices->reserve( 2u * times.size() );
625  for( size_t id = 0u; id < times.size(); ++id )
626  {
627  vertices->push_back( osg::Vec3( times[id], -1048576.0f, -1.0f - id ) );
628  vertices->push_back( osg::Vec3( times[id], 1024.0f, -1.0f - id ) );
629  }
630  geometry->setVertexArray( vertices );
631 
632  osg::Vec4Array* colors = new osg::Vec4Array;
633  colors->reserve( 2u * magnitudes.size() );
634  for( size_t id = 0u; id < magnitudes.size(); ++id )
635  {
636  const float scale = magnitudes[id] / m_dipoles->getMaxMagnitude();
637  const osg::Vec4 color = color_min * ( 1.0f - scale ) + color_max * scale;
638  colors->push_back( color );
639  colors->push_back( color );
640  }
641  geometry->setColorArray( colors );
642  geometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
643 
644  geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::QUAD_STRIP, 0, 2u * times.size() ) );
645 
646  osg::StateSet* state = geometry->getOrCreateStateSet();
647  state->setMode( GL_BLEND, osg::StateAttribute::ON );
648  state->setMode( GL_DEPTH_TEST, osg::StateAttribute::ON );
649  state->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
650  state->setAttributeAndModes( new osg::BlendFunc( osg::BlendFunc::SRC_ALPHA,
651  osg::BlendFunc::ONE_MINUS_SRC_ALPHA ) );
652 
653  osg::Geode* geode = new osg::Geode;
654  geode->addDrawable( geometry );
655 
656  panTransform->addChild( geode );
657  }
658  }
659 
660  // add labels and graph to the root node
661  m_rootNode2d->addChild( labelsTransform );
662  m_rootNode2d->addChild( panTransform );
663 
664  // create UI event handler
666  m_timePos,
667  m_timeRange,
668  m_graphWidth,
669  m_yPos,
670  m_ySpacing,
673  m_event,
674  eventParentNode,
675  m_eeg,
676  0,
679  m_dipoles );
680 
681  // draw the electrode positions in 3D
682  if( m_drawElectrodes->get( true ) )
683  {
685  m_rootNode3d->addChild( m_electrodesNode );
686  }
687  else
688  {
689  m_electrodesNode = NULL;
690  }
691 
692  // draw the head surface in 3D
693  if( m_drawHeadSurface->get( true ) )
694  {
696  m_rootNode3d->addChild( m_headSurfaceNode );
697  }
698  else
699  {
700  m_headSurfaceNode = NULL;
701  }
702 
703  // draw the electrode labels in 3D
704  if( m_drawLabels->get( true ) )
705  {
707  m_rootNode3d->addChild( m_labelsNode );
708  }
709  else
710  {
711  m_labelsNode = NULL;
712  }
713 
714  // add rootNode to scene
715  if( m_wasActive )
716  {
717  debugLog() << "Adding rootNode to scene after redraw";
718  m_widget->getScene()->insert( m_rootNode2d );
719 
720  WKernel::getRunningKernel()->getGraphicsEngine()->getScene()->insert( m_rootNode3d );
721 
722  m_widget->getViewer()->getView()->addEventHandler( m_handler );
723  }
724 
725  // create new source calculator
726  m_sourceCalculator = std::shared_ptr< WEEGSourceCalculator >( new WEEGSourceCalculator( m_eeg ) );
727  }
728  else
729  {
730  m_rootNode2d = NULL;
731  m_rootNode3d = NULL;
732  m_handler = NULL;
733 
734  m_electrodesNode = NULL;
735  m_headSurfaceNode = NULL;
736  m_labelsNode = NULL;
737 
738  m_sourceCalculator.reset();
739  }
740 }
741 
742 osg::ref_ptr< osg::Node > WMEEGView::drawElectrodes()
743 {
744  // draw 3d positions of electrodes
745  const float sphereSize = 4.0f;
746 
747  osg::ref_ptr< osg::Group > electrodes( new osg::Group );
748 
749  for( size_t channelID = 0; channelID < m_eeg->getNumberOfChannels(); ++channelID )
750  {
751  std::shared_ptr< WEEGChannelInfo > channelInfo = m_eeg->getChannelInfo( channelID );
752  try
753  {
754  osg::Vec3 pos = channelInfo->getPosition();
755 
756  // create sphere geode on electrode position
757  osg::ShapeDrawable* shape = new osg::ShapeDrawable( new osg::Sphere( pos, sphereSize ) );
758  shape->setDataVariance( osg::Object::DYNAMIC );
759  shape->setUpdateCallback( new WElectrodePositionCallback( channelID, m_colorSensitivity, m_event, m_colorMap ) );
760 
761  osg::Geode* sphereGeode = new osg::Geode;
762  sphereGeode->addDrawable( shape );
763  electrodes->addChild( sphereGeode );
764  }
765  catch( const WDHException& )
766  {
767  warnLog() << "The position of the electrode " << channelInfo->getLabel() << " is unknown.";
768  }
769  }
770 
771  return electrodes;
772 }
773 
774 osg::ref_ptr< osg::Node > WMEEGView::drawHeadSurface()
775 {
776  // draw head surface
777  const size_t nbChannels = m_eeg->getNumberOfChannels();
778 
779  std::vector< WPosition > positions;
780  positions.reserve( nbChannels );
781  std::vector< std::size_t > channelIDs;
782  channelIDs.reserve( nbChannels );
783  for( size_t channelID = 0; channelID < nbChannels; ++channelID )
784  {
785  std::shared_ptr< WEEGChannelInfo > channelInfo = m_eeg->getChannelInfo( channelID );
786  try
787  {
788  WPosition position = channelInfo->getPosition();
789  positions.push_back( position );
790  channelIDs.push_back( channelID );
791  }
792  catch( const WDHException& )
793  {
794  warnLog() << "The position of the electrode " << channelInfo->getLabel() << " is unknown.";
795  }
796  }
797 
798  const std::size_t nbPositions = positions.size();
799 
800  osg::ref_ptr< osg::Geometry > geometry = wge::convertToOsgGeometry( wge::triangulate( positions, -0.005 ), WColor( 1.0, 1.0, 1.0, 1.0 ), true );
801 
802  osg::Vec4Array* colors = new osg::Vec4Array;
803  colors->push_back( osg::Vec4( 1.0f, 1.0f, 1.0f, 1.0f ) );
804  geometry->setColorArray( colors );
805  geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
806 
807  osg::LightModel* lightModel = new osg::LightModel;
808  lightModel->setTwoSided( true );
809  osg::StateSet* state = geometry->getOrCreateStateSet();
810  state->setAttributeAndModes( lightModel );
811 
812  state->setTextureAttributeAndModes( 0, m_colorMapTexture );
813  osg::FloatArray* texCoords = new osg::FloatArray;
814  texCoords->assign( nbPositions, 0.5f );
815  geometry->setTexCoordArray( 0, texCoords );
816 
817  geometry->setDataVariance( osg::Object::DYNAMIC );
818  geometry->setUpdateCallback( new WHeadSurfaceCallback( channelIDs, m_colorSensitivity, m_event ) );
819 
820  osg::ref_ptr< osg::Geode > surface( new osg::Geode );
821  surface->addDrawable( geometry );
822  return surface;
823 }
824 
825 osg::ref_ptr< osg::Node > WMEEGView::drawLabels()
826 {
827  // draw electrode labels in 3d
828  const float sphereSize = 4.0f;
829  const osg::Vec3 text3dOffset( 0.0, 0.0, sphereSize );
830  const double text3dSize = 14.0;
831  const osg::Vec4 text3dColor( 0.0, 0.0, 0.0, 1.0 );
832 
833  osg::ref_ptr< osg::Group > labels( new osg::Group );
834 
835  for( size_t channelID = 0; channelID < m_eeg->getNumberOfChannels(); ++channelID )
836  {
837  std::shared_ptr< WEEGChannelInfo > channelInfo = m_eeg->getChannelInfo( channelID );
838  try
839  {
840  osg::Vec3 pos = channelInfo->getPosition();
841 
842  // create text geode for the channel label
843  osgText::Text* text = new osgText::Text;
844  text->setText( channelInfo->getLabel() );
845  text->setPosition( pos + text3dOffset );
846  text->setAlignment( osgText::Text::CENTER_BOTTOM );
847  text->setAxisAlignment( osgText::Text::SCREEN );
848  text->setCharacterSize( text3dSize );
849  text->setCharacterSizeMode( osgText::Text::SCREEN_COORDS );
850  text->setColor( text3dColor );
851 
852  osg::Geode* textGeode = new osg::Geode;
853  textGeode->addDrawable( text );
854  labels->addChild( textGeode );
855  }
856  catch( const WDHException& )
857  {
858  warnLog() << "The position of the electrode " << channelInfo->getLabel() << " is unknown.";
859  }
860  }
861 
862  return labels;
863 }
864 
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
General purpose exception and therefore base class for all DataHandler related exceptions.
Definition: WDHException.h:40
A special time position in an EEG recording with corresponding data.
Definition: WEEGEvent.h:45
Class which calculates a source position from an EEG recording at a given time position.
GUI event handler class used for the 2D EEG view.
OSG Update Callback to change the color of an electrode position when the event position changed.
Class to have a simple notification/condition system for simple flags.
Definition: WFlag.h:39
std::shared_ptr< WCondition > getCondition()
Returns the condition that is used by this flag.
Definition: WFlag.h:319
virtual bool set(const T &value, bool suppressNotification=false)
Sets the new value for this flag.
Definition: WFlag.h:291
Class to wrap around the osg Group node and providing a thread safe add/removal mechanism.
Definition: WGEGroupNode.h:48
This requirement ensures an up and running WGE.
OSG Update Callback to change the color of a head surface by changing its 1D texture coordinates when...
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
std::shared_ptr< WROIManager > getRoiManager()
get for roi manager
Definition: WKernel.cpp:209
std::shared_ptr< WGraphicsEngine > getGraphicsEngine() const
Returns pointer to currently running instance of graphics engine.
Definition: WKernel.cpp:122
OSG Node Callback to update the MatrixTransform Node of the labels used for panning and zooming.
OSG Update Callback to update the EEG graph of one channel by changing the vertex array of the line s...
Test module to open a new widget and display EEG data.
Definition: WMEEGView.h:52
void closeCustomWidget()
Disconnects the m_node from the custom widget and closes the widget.
Definition: WMEEGView.cpp:464
WPropDouble m_ySpacing
the distance between two curves of the graph in pixel as property
Definition: WMEEGView.h:220
osg::ref_ptr< osg::Node > m_headSurfaceNode
OSG node for the 3D display of the head surface.
Definition: WMEEGView.h:278
std::shared_ptr< WFlag< std::shared_ptr< WEEGEvent > > > m_event
event marking a special time position as WFlag
Definition: WMEEGView.h:236
bool m_wasActive
The current active-state.
Definition: WMEEGView.h:301
virtual void moduleMain()
Entry point after loading the module.
Definition: WMEEGView.cpp:222
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...
Definition: WMEEGView.cpp:77
WPropBool m_proofOfConcept
Property determining whether we only show the proof of concept or the real dipoles.
Definition: WMEEGView.h:174
double m_currentEventTime
The time of the current event.
Definition: WMEEGView.h:306
std::shared_ptr< WModuleInputData< WEEG2 > > m_eeg_input
Input connector for a EEG dataset.
Definition: WMEEGView.h:134
WPropDouble m_yPos
the y position in pixel at the lower edge as property
Definition: WMEEGView.h:215
std::shared_ptr< WEEGSourceCalculator > m_sourceCalculator
calculates a source position at a given time position
Definition: WMEEGView.h:323
virtual void notifyDataChange(std::shared_ptr< WModuleConnector >, std::shared_ptr< WModuleConnector >)
Gets called when the data on one input connector changed.
Definition: WMEEGView.cpp:216
osg::ref_ptr< osg::Node > drawLabels()
Draw the electrode labels in 3D.
Definition: WMEEGView.cpp:825
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
Definition: WMEEGView.cpp:92
WPropBool m_snapToDipole
Property determining whether the selected time position should be snapped to an active dipole.
Definition: WMEEGView.h:179
osg::ref_ptr< osg::Node > drawHeadSurface()
Draw the head surface in 3D.
Definition: WMEEGView.cpp:774
osg::ref_ptr< osgSim::ScalarsToColors > m_colorMap
A ScalarsToColors object mapping the potentials at the electrodes to colors.
Definition: WMEEGView.h:312
virtual const std::string getDescription() const
Gets the description for this module.
Definition: WMEEGView.cpp:87
virtual void connectors()
Initialize connectors in this function.
Definition: WMEEGView.cpp:105
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
Definition: WMEEGView.h:144
WPropDouble m_ROIsize
Size of the region of interest.
Definition: WMEEGView.h:189
std::shared_ptr< WUIViewWidget > m_widget
Custom widget which is used by this module to display its data.
Definition: WMEEGView.h:251
void redraw()
Removes all Nodes from m_rootNode2d and m_rootNode3d and adds new ones based on the current data stor...
Definition: WMEEGView.cpp:480
WPropBool m_drawLabels
Property determining whether electrode labels should be drawn.
Definition: WMEEGView.h:169
WPropInt m_graphWidth
the width of the graph in pixel as property
Definition: WMEEGView.h:210
WPropBool m_drawElectrodes
Property determining whether electode positions should be drawn.
Definition: WMEEGView.h:159
bool openCustomWidget()
Opens a custom widget and connects the m_node with it.
Definition: WMEEGView.cpp:435
virtual const std::string getName() const
Gets the name of this module.
Definition: WMEEGView.cpp:82
WPropGroup m_appearanceGroup
Group for parameters that adjust the appearance of the EEG widget.
Definition: WMEEGView.h:154
WPropDouble m_colorSensitivity
The sensitivity of the color map as property.
Definition: WMEEGView.h:231
osg::ref_ptr< osg::Node > drawElectrodes()
Draw the electrode positions in 3D.
Definition: WMEEGView.cpp:742
WBoolFlag m_dataChanged
Bool flag which gets set when the data was changed.
Definition: WMEEGView.h:296
void createColorMap()
Prepare textures for colormapping EEG signal.
Definition: WMEEGView.cpp:407
WPropBool m_butterfly
Property switching between standard and butterfly plot of curves (overlay of all curves in one row)
Definition: WMEEGView.h:184
virtual void notifyConnectionEstablished(std::shared_ptr< WModuleConnector >, std::shared_ptr< WModuleConnector >)
Gets called whenever a connector gets connected to the specified input.
Definition: WMEEGView.cpp:210
WPropDouble m_ySensitivity
the sensitivity of the graph in microvolt per pixel as property
Definition: WMEEGView.h:225
osg::ref_ptr< osg::Node > m_electrodesNode
OSG node for the 3D display of the electrode positions.
Definition: WMEEGView.h:273
virtual void requirements()
Initialize requirements for this module.
Definition: WMEEGView.cpp:98
std::vector< osg::ref_ptr< WROIBox > > m_rois
The ROIs around the source dipole positions at the time determined by m_event.
Definition: WMEEGView.h:290
osg::ref_ptr< osg::Node > m_labelsNode
OSG node for the 3D display of the electrode labels.
Definition: WMEEGView.h:283
osg::ref_ptr< WGEGroupNode > m_rootNode3d
OSG node for all 3D stuff of this module.
Definition: WMEEGView.h:268
osg::ref_ptr< WEEGViewHandler > m_handler
GUI event handler used for interactive changing of many properties.
Definition: WMEEGView.h:256
osg::ref_ptr< osg::Texture1D > m_colorMapTexture
A 1D texture containing the color map as image.
Definition: WMEEGView.h:318
std::shared_ptr< WModuleInputData< WDataSetDipoles > > m_dipoles_input
Input connector for dipoles of EEG data.
Definition: WMEEGView.h:139
virtual ~WMEEGView()
destructor
Definition: WMEEGView.cpp:73
std::shared_ptr< WDataSetDipoles > m_dipoles
Pointer to the loaded dipoles dataset.
Definition: WMEEGView.h:246
WPropBool m_drawHeadSurface
Property determining whether the head surface should be drawn.
Definition: WMEEGView.h:164
WPropGroup m_manualNavigationGroup
Group for parameters that are normally adjusted using mouse actions.
Definition: WMEEGView.h:149
virtual void properties()
Initialize properties in this function.
Definition: WMEEGView.cpp:119
WPropDouble m_timePos
the time position in seconds where to start the graph at the left edge as property
Definition: WMEEGView.h:200
WMEEGView()
default constructor
Definition: WMEEGView.cpp:65
osg::ref_ptr< WGEGroupNode > m_rootNode2d
OSG node for all 2D stuff of this module.
Definition: WMEEGView.h:262
std::shared_ptr< WEEG2 > m_eeg
Pointer to the loaded EEG dataset.
Definition: WMEEGView.h:241
WPropInt m_labelsWidth
the width of the label display in pixel as property
Definition: WMEEGView.h:194
WPropDouble m_timeRange
the width of the graph in seconds as property
Definition: WMEEGView.h:205
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
wlog::WStreamedLogger warnLog() const
Logger instance for comfortable warning- logs.
Definition: WModule.cpp:580
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
WPropBool m_active
True whenever the module should be active.
Definition: WModule.h:723
virtual void connectors()
Initialize connectors in this function.
Definition: WModule.cpp:208
OSG Node Callback to update the MatrixTransform Node used for the panning.
This only is a 3d double vector.
A box representing a region of interest.
Definition: WROIBox.h:49
OSG Node Callback to update the MatrixTransform Nodes of one channel used for the y-scaling.
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.
WTriangleMesh::SPtr triangulate(const std::vector< WPosition > &points, double transformationFactor=0.0)
Calculate the Delaunay Triangulation of the given points.
osg::ref_ptr< osg::Geometry > convertToOsgGeometry(WTriangleMesh::SPtr mesh, const WColor &defaultColor=WColor(1.0, 1.0, 1.0, 1.0), bool includeNormals=false, bool lighting=false, bool useMeshColor=true)
Extract the vertices and triangles from a WTriangleMesh and save them into an osg::Geometry.