29 #include <osg/BlendFunc>
30 #include <osg/LightModel>
31 #include <osg/ShapeDrawable>
32 #include <osgSim/ColorRange>
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"
69 m_currentEventTime( -1.0 )
79 return std::shared_ptr< WModule >(
new WMEEGView() );
89 return "Displays EEG data.";
108 shared_from_this(),
"EEG recording",
"Loaded EEG-dataset." ) );
112 shared_from_this(),
"Dipoles",
"The reconstructed dipoles for the EEG." ) );
123 "Draw the 3D positions of the electrodes.",
127 "Draw the head surface between the electrodes.",
131 "Draw the labels of the electrodes at their 3D positions.",
135 "Use proof of concept (POC) ROI positioning instead of real dipoles position.",
139 "If a time is selected, snap to the nearest time position with an active dipole.",
143 "Overlay all curves in one row.",
147 "The width ROI box.",
155 "Modification of the appearance of the EEG View widget" );
157 "The width of the label display in pixel.",
161 "The width of the graph in pixel.",
164 "The distance between two curves of the graph in pixel.",
168 "The sensitivity of the color map. It ranges from -Color Sensitivity to +Color Sensitivity in microvolt.",
174 "Manually modify the parameters that are usually changes by mouse"
175 " actions in th EEG view." );
177 "The sensitivity of the graph in microvolt per pixel.",
180 "The time position in seconds where to start the graph at the left edge.",
183 "The width of the graph in seconds.",
186 "The y position in pixel at the lower edge.",
197 m_yPos->setMin( -1048576.0 );
211 std::shared_ptr< WModuleConnector > , std::shared_ptr< WModuleConnector > )
217 std::shared_ptr< WModuleConnector > , std::shared_ptr< WModuleConnector > )
318 std::shared_ptr< WEEGEvent >
event =
m_event->get();
322 debugLog() <<
"New event position: " <<
event->getTime();
324 for( std::vector< osg::ref_ptr< WROIBox > >::iterator iter =
m_rois.begin(); iter !=
m_rois.end(); ++iter )
335 float halfWidth =
m_ROIsize->get(
true ) * 0.5;
337 position +
WVector3d( halfWidth, halfWidth, halfWidth ) ) );
343 for(
size_t dipoleId = 0u; dipoleId <
m_dipoles->getNumberOfDipoles(); ++dipoleId )
345 debugLog() <<
"Dipole[" << dipoleId <<
"]: " <<
m_dipoles->getMagnitude( event->getTime(), dipoleId );
346 if(
m_dipoles->getMagnitude( event->getTime(), dipoleId ) != 0 )
348 float halfWidth =
m_ROIsize->get(
true ) * 0.5;
351 position +
WVector3d( halfWidth, halfWidth, halfWidth ) ) );
358 debugLog() <<
"No dipoles found and not in POC mode: placing NO ROI.";
410 std::vector< osg::Vec4 > colors;
412 colors.push_back( osg::Vec4( 0.0, 0.0, 1.0, 1.0 ) );
413 colors.push_back( osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );
414 colors.push_back( osg::Vec4( 1.0, 0.0, 0.0, 1.0 ) );
415 m_colorMap =
new osgSim::ColorRange( -1.0, 1.0, colors );
418 const int size = 256;
419 osg::Image* image =
new osg::Image;
421 image->allocateImage( size, 1, 1, GL_RGBA, GL_FLOAT );
422 image->setInternalTextureFormat( GL_RGBA );
424 osg::Vec4* data =
reinterpret_cast< osg::Vec4*
>( image->data() );
425 for(
int i = 0; i < size; ++i )
427 data[i] =
m_colorMap->getColor( ( 2 * i + 1 - size ) /
static_cast< float >( size - 1 ) );
431 m_colorMapTexture->setWrap( osg::Texture1D::WRAP_S, osg::Texture1D::CLAMP_TO_EDGE );
432 m_colorMapTexture->setFilter( osg::Texture1D::MIN_FILTER, osg::Texture1D::LINEAR );
437 debugLog() <<
"Try to open EEG View widget...";
444 debugLog() <<
"Successfully opened EEG View widget.";
453 debugLog() <<
"Adding rootNode to scene after opened EEG View widget";
459 warnLog() <<
"Could not create EEG View widget.";
504 if(
m_eeg.get() &&
m_eeg->getNumberOfSegments() > 0 )
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 );
512 osg::StateSet* stateset =
m_rootNode2d->getOrCreateStateSet();
513 stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
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;
524 std::shared_ptr< WEEG2Segment > segment =
m_eeg->getSegment( 0 );
525 size_t nbSamples = segment->getNumberOfSamples();
526 debugLog() <<
" Number of Samples: " << nbSamples;
528 const float heightConstant = 736.0;
533 m_yPos->set( -heightConstant / nbChannels );
537 m_ySpacing->set( heightConstant / nbChannels );
538 m_yPos->set( ( heightConstant * 0.5 ) / nbChannels - heightConstant );
545 osg::Geode* textGeode =
new osg::Geode;
548 osg::MatrixTransform* panTransform =
new osg::MatrixTransform;
549 panTransform->setDataVariance( osg::Object::DYNAMIC );
552 for(
size_t channelID = 0; channelID < nbChannels; ++channelID )
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();
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 );
569 textGeode->addDrawable( text );
572 osg::Geometry* geometry =
new osg::Geometry;
574 osg::Vec4Array* colors =
new osg::Vec4Array;
575 colors->push_back( linesColor );
576 geometry->setColorArray( colors );
577 geometry->setColorBinding( osg::Geometry::BIND_OVERALL );
579 geometry->addPrimitiveSet(
new osg::DrawArrays( osg::PrimitiveSet::LINE_STRIP, 0, 0 ) );
581 geometry->setDataVariance( osg::Object::DYNAMIC );
584 osg::Geode* linesGeode =
new osg::Geode;
585 linesGeode->addDrawable( geometry );
588 osg::MatrixTransform* scaleTransform =
new osg::MatrixTransform;
589 scaleTransform->setDataVariance( osg::Object::DYNAMIC );
593 scaleTransform->addChild( linesGeode );
594 panTransform->addChild( scaleTransform );
598 osg::MatrixTransform* labelsTransform =
new osg::MatrixTransform;
599 labelsTransform->setDataVariance( osg::Object::DYNAMIC );
601 labelsTransform->addChild( textGeode );
605 panTransform->addChild( eventParentNode );
610 for(
size_t dipoleId = 0u; dipoleId <
m_dipoles->getNumberOfDipoles(); ++dipoleId )
612 debugLog() <<
"Dipole[" << dipoleId <<
"]: " <<
m_dipoles->getStartTime( dipoleId )
613 <<
" to " <<
m_dipoles->getEndTime( dipoleId );
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 );
618 const std::vector<float> times =
m_dipoles->getTimes( dipoleId );
619 const std::vector<float> magnitudes =
m_dipoles->getMagnitudes( dipoleId );
621 osg::Geometry* geometry =
new osg::Geometry;
623 osg::Vec3Array* vertices =
new osg::Vec3Array;
624 vertices->reserve( 2u * times.size() );
625 for(
size_t id = 0u;
id < times.size(); ++id )
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 ) );
630 geometry->setVertexArray( vertices );
632 osg::Vec4Array* colors =
new osg::Vec4Array;
633 colors->reserve( 2u * magnitudes.size() );
634 for(
size_t id = 0u;
id < magnitudes.size(); ++id )
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 );
641 geometry->setColorArray( colors );
642 geometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX );
644 geometry->addPrimitiveSet(
new osg::DrawArrays( osg::PrimitiveSet::QUAD_STRIP, 0, 2u * times.size() ) );
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 ) );
653 osg::Geode* geode =
new osg::Geode;
654 geode->addDrawable( geometry );
656 panTransform->addChild( geode );
717 debugLog() <<
"Adding rootNode to scene after redraw";
745 const float sphereSize = 4.0f;
747 osg::ref_ptr< osg::Group > electrodes(
new osg::Group );
749 for(
size_t channelID = 0; channelID <
m_eeg->getNumberOfChannels(); ++channelID )
751 std::shared_ptr< WEEGChannelInfo > channelInfo =
m_eeg->getChannelInfo( channelID );
754 osg::Vec3 pos = channelInfo->getPosition();
757 osg::ShapeDrawable* shape =
new osg::ShapeDrawable(
new osg::Sphere( pos, sphereSize ) );
758 shape->setDataVariance( osg::Object::DYNAMIC );
761 osg::Geode* sphereGeode =
new osg::Geode;
762 sphereGeode->addDrawable( shape );
763 electrodes->addChild( sphereGeode );
767 warnLog() <<
"The position of the electrode " << channelInfo->getLabel() <<
" is unknown.";
777 const size_t nbChannels =
m_eeg->getNumberOfChannels();
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 )
785 std::shared_ptr< WEEGChannelInfo > channelInfo =
m_eeg->getChannelInfo( channelID );
788 WPosition position = channelInfo->getPosition();
789 positions.push_back( position );
790 channelIDs.push_back( channelID );
794 warnLog() <<
"The position of the electrode " << channelInfo->getLabel() <<
" is unknown.";
798 const std::size_t nbPositions = positions.size();
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 );
807 osg::LightModel* lightModel =
new osg::LightModel;
808 lightModel->setTwoSided(
true );
809 osg::StateSet* state = geometry->getOrCreateStateSet();
810 state->setAttributeAndModes( lightModel );
813 osg::FloatArray* texCoords =
new osg::FloatArray;
814 texCoords->assign( nbPositions, 0.5f );
815 geometry->setTexCoordArray( 0, texCoords );
817 geometry->setDataVariance( osg::Object::DYNAMIC );
820 osg::ref_ptr< osg::Geode > surface(
new osg::Geode );
821 surface->addDrawable( geometry );
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 );
833 osg::ref_ptr< osg::Group > labels(
new osg::Group );
835 for(
size_t channelID = 0; channelID <
m_eeg->getNumberOfChannels(); ++channelID )
837 std::shared_ptr< WEEGChannelInfo > channelInfo =
m_eeg->getChannelInfo( channelID );
840 osg::Vec3 pos = channelInfo->getPosition();
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 );
852 osg::Geode* textGeode =
new osg::Geode;
853 textGeode->addDrawable( text );
854 labels->addChild( textGeode );
858 warnLog() <<
"The position of the electrode " << channelInfo->getLabel() <<
" is unknown.";
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.
General purpose exception and therefore base class for all DataHandler related exceptions.
A special time position in an EEG recording with corresponding data.
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.
std::shared_ptr< WCondition > getCondition()
Returns the condition that is used by this flag.
virtual bool set(const T &value, bool suppressNotification=false)
Sets the new value for this flag.
Class to wrap around the osg Group node and providing a thread safe add/removal mechanism.
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.
std::shared_ptr< WUI > getUI() const
Getter for the associated UI.
std::shared_ptr< WROIManager > getRoiManager()
get for roi manager
std::shared_ptr< WGraphicsEngine > getGraphicsEngine() const
Returns pointer to currently running instance of graphics engine.
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.
void closeCustomWidget()
Disconnects the m_node from the custom widget and closes the widget.
WPropDouble m_ySpacing
the distance between two curves of the graph in pixel as property
osg::ref_ptr< osg::Node > m_headSurfaceNode
OSG node for the 3D display of the head surface.
std::shared_ptr< WFlag< std::shared_ptr< WEEGEvent > > > m_event
event marking a special time position as WFlag
bool m_wasActive
The current active-state.
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...
WPropBool m_proofOfConcept
Property determining whether we only show the proof of concept or the real dipoles.
double m_currentEventTime
The time of the current event.
std::shared_ptr< WModuleInputData< WEEG2 > > m_eeg_input
Input connector for a EEG dataset.
WPropDouble m_yPos
the y position in pixel at the lower edge as property
std::shared_ptr< WEEGSourceCalculator > m_sourceCalculator
calculates a source position at a given time position
virtual void notifyDataChange(std::shared_ptr< WModuleConnector >, std::shared_ptr< WModuleConnector >)
Gets called when the data on one input connector changed.
osg::ref_ptr< osg::Node > drawLabels()
Draw the electrode labels in 3D.
virtual const char ** getXPMIcon() const
Get the icon for this module in XPM format.
WPropBool m_snapToDipole
Property determining whether the selected time position should be snapped to an active dipole.
osg::ref_ptr< osg::Node > drawHeadSurface()
Draw the head surface in 3D.
osg::ref_ptr< osgSim::ScalarsToColors > m_colorMap
A ScalarsToColors object mapping the potentials at the electrodes to colors.
virtual const std::string getDescription() const
Gets the description for this module.
virtual void connectors()
Initialize connectors in this function.
std::shared_ptr< WCondition > m_propCondition
A condition used to notify about changes in several properties.
WPropDouble m_ROIsize
Size of the region of interest.
std::shared_ptr< WUIViewWidget > m_widget
Custom widget which is used by this module to display its data.
void redraw()
Removes all Nodes from m_rootNode2d and m_rootNode3d and adds new ones based on the current data stor...
WPropBool m_drawLabels
Property determining whether electrode labels should be drawn.
WPropInt m_graphWidth
the width of the graph in pixel as property
WPropBool m_drawElectrodes
Property determining whether electode positions should be drawn.
bool openCustomWidget()
Opens a custom widget and connects the m_node with it.
virtual const std::string getName() const
Gets the name of this module.
WPropGroup m_appearanceGroup
Group for parameters that adjust the appearance of the EEG widget.
WPropDouble m_colorSensitivity
The sensitivity of the color map as property.
osg::ref_ptr< osg::Node > drawElectrodes()
Draw the electrode positions in 3D.
WBoolFlag m_dataChanged
Bool flag which gets set when the data was changed.
void createColorMap()
Prepare textures for colormapping EEG signal.
WPropBool m_butterfly
Property switching between standard and butterfly plot of curves (overlay of all curves in one row)
virtual void notifyConnectionEstablished(std::shared_ptr< WModuleConnector >, std::shared_ptr< WModuleConnector >)
Gets called whenever a connector gets connected to the specified input.
WPropDouble m_ySensitivity
the sensitivity of the graph in microvolt per pixel as property
osg::ref_ptr< osg::Node > m_electrodesNode
OSG node for the 3D display of the electrode positions.
virtual void requirements()
Initialize requirements for this module.
std::vector< osg::ref_ptr< WROIBox > > m_rois
The ROIs around the source dipole positions at the time determined by m_event.
osg::ref_ptr< osg::Node > m_labelsNode
OSG node for the 3D display of the electrode labels.
osg::ref_ptr< WGEGroupNode > m_rootNode3d
OSG node for all 3D stuff of this module.
osg::ref_ptr< WEEGViewHandler > m_handler
GUI event handler used for interactive changing of many properties.
osg::ref_ptr< osg::Texture1D > m_colorMapTexture
A 1D texture containing the color map as image.
std::shared_ptr< WModuleInputData< WDataSetDipoles > > m_dipoles_input
Input connector for dipoles of EEG data.
virtual ~WMEEGView()
destructor
std::shared_ptr< WDataSetDipoles > m_dipoles
Pointer to the loaded dipoles dataset.
WPropBool m_drawHeadSurface
Property determining whether the head surface should be drawn.
WPropGroup m_manualNavigationGroup
Group for parameters that are normally adjusted using mouse actions.
virtual void properties()
Initialize properties in this function.
WPropDouble m_timePos
the time position in seconds where to start the graph at the left edge as property
WMEEGView()
default constructor
osg::ref_ptr< WGEGroupNode > m_rootNode2d
OSG node for all 2D stuff of this module.
std::shared_ptr< WEEG2 > m_eeg
Pointer to the loaded EEG dataset.
WPropInt m_labelsWidth
the width of the label display in pixel as property
WPropDouble m_timeRange
the width of the graph in seconds as property
Class representing a single module of OpenWalnut.
Requirements m_requirements
The list of requirements.
virtual void properties()
Initialize properties in this function.
wlog::WStreamedLogger debugLog() const
Logger instance for comfortable debug logging.
void addConnector(std::shared_ptr< WModuleInputConnector > con)
Adds the specified connector to the list of inputs.
wlog::WStreamedLogger warnLog() const
Logger instance for comfortable warning- logs.
std::shared_ptr< WProperties > m_properties
The property object for the module.
void ready()
Call this whenever your module is ready and can react on property changes.
WConditionSet m_moduleState
The internal state of the module.
WPropBool m_active
True whenever the module should be active.
virtual void connectors()
Initialize connectors in this function.
This only is a 3d double vector.
A box representing a region of interest.
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.