init
@@ -0,0 +1,33 @@
|
||||
PROJECT(openvibe-plugins-designer-simple-visualization)
|
||||
MESSAGE(STATUS "Now building ${PROJECT_NAME} ${PROJECT_VERSION} (${OV_PROJECT_BRANCH}~${OV_PROJECT_COMMITHASH})" )
|
||||
|
||||
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl)
|
||||
INCLUDE("FindSourceRCProperties")
|
||||
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
|
||||
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
FOLDER ${PLUGINS_FOLDER}
|
||||
COMPILE_FLAGS "-DOVP_Exports -DOVP_Shared")
|
||||
|
||||
ADD_DEFINITIONS(-D_USE_MATH_DEFINES) # Definition for constant math as M_PI
|
||||
|
||||
INCLUDE("AddOpenViBESDKComponents")
|
||||
INCLUDE("FindOpenViBEVisualizationToolkit")
|
||||
INCLUDE("FindThirdPartyGTK")
|
||||
|
||||
# ---------------------------------
|
||||
# Target macros
|
||||
# Defines target operating system, architecture and compiler
|
||||
# ---------------------------------
|
||||
SET_BUILD_PLATFORM()
|
||||
|
||||
# -----------------------------
|
||||
# Install files
|
||||
# -----------------------------
|
||||
INSTALL(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION ${DIST_BINDIR}
|
||||
LIBRARY DESTINATION ${DIST_LIBDIR}
|
||||
ARCHIVE DESTINATION ${DIST_LIBDIR})
|
||||
|
||||
INSTALL(DIRECTORY share/ DESTINATION ${DIST_DATADIR}/openvibe/plugins/simple-visualization)
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_2DTopographicMap 2D topographic map
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_2DTopographicMap_Description|
|
||||
The 2D Topographic Map plugin is one possible way to combine signal measures and topographical information. It interpolates measured potentials
|
||||
and maps them over the scalp surface using a color scale, making it easy to locate areas where brain activity is the most intense at any given time.
|
||||
|
||||
Since this is a 2D plugin, one cannot freely move the camera around the head, as is the case with the 3D Topographic Map plugin
|
||||
(see \ref BoxAlgorithm_3DTopographicMap). However, the user may switch between several views which, when combined, cover the whole of the skull area
|
||||
over which potentials may be mapped.
|
||||
|
||||
This plugin uses a spherical spline algorithm to interpolate potentials measured at electrode locations over an area of interest (which varies depending on the active view).
|
||||
One can choose to interpolate potentials (which is done by interpolating along a spline whose control points lie at electrode locations) or current densities (which are
|
||||
computed using the spline laplacian).
|
||||
* |OVP_DocEnd_BoxAlgorithm_2DTopographicMap_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_2DTopographicMap_Inputs|
|
||||
Note: the channel names specified on the 'Signal' and 'Channel Localization' input streams should match. Mismatches may not be reported by the box. For example, if electrode localisation file reader provides the positions of the electrodes from some configuration file, the channel names in that file should correspend to the names of the signal channels delivered in the Signal stream.
|
||||
* |OVP_DocEnd_BoxAlgorithm_2DTopographicMap_Inputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_2DTopographicMap_Input1|
|
||||
* |OVP_DocEnd_BoxAlgorithm_2DTopographicMap_Input1|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_2DTopographicMap_Settings|
|
||||
Offline options include interpolation type (direct spline interpolation for potentials, or spline laplacian to map currents) and delay applied when mapping data (0 by default). The latter
|
||||
can prove useful in the case of e.g. neurofeedback experiments. Indeed, subjects may find it easier to observe their mental activity and the effect mental tasks can have on it
|
||||
when it is displayed with a small delay (on the order of a few hundreds of milliseconds).
|
||||
* |OVP_DocEnd_BoxAlgorithm_2DTopographicMap_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_2DTopographicMap_Setting1|
|
||||
Spline or laplacian interpolation.
|
||||
* |OVP_DocEnd_BoxAlgorithm_2DTopographicMap_Setting1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_2DTopographicMap_Setting2|
|
||||
Delay to apply to displayed data, in seconds
|
||||
* |OVP_DocEnd_BoxAlgorithm_2DTopographicMap_Setting2|
|
||||
__________________________________________________________________
|
||||
|
||||
Online visualisation settings
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_2DTopographicMap_OnlineVisualizationSettings|
|
||||
Online options include :
|
||||
|
||||
- Radial/Axial Projection : these settings control the projection mode. Radial projection displays a greater mapping area, but localisation is less intuitive than
|
||||
with axial projection.
|
||||
- Map Potentials/Currents : these settings control the nature of mapped values. While potentials are computed from spline values, currents computation uses the spline laplacian.
|
||||
- Toggle Electrodes : toggle electrodes on/off.
|
||||
- Top/Left/Right/Back View : these buttons enable view switching.
|
||||
- Delay : this cursor defines the delay to apply to the values that are drawn.
|
||||
|
||||
\image html topographicmap2ddisplay_toolbar.png "2D Topographic Map toolbar."
|
||||
* |OVP_DocEnd_BoxAlgorithm_2DTopographicMap_OnlineVisualizationSettings|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_2DTopographicMap_Examples|
|
||||
Practical example :
|
||||
|
||||
This example is based on the following scenario file :
|
||||
|
||||
\code
|
||||
box-tutorials/topographic_map.xml
|
||||
\endcode
|
||||
|
||||
To see this plugin in action, one should first locate a file corresponding to a prerecorded EEG or MEG session and play it back using e. g. a GDF file reader or some other suitable plugin.
|
||||
The GDF file used by default in this scenario is the following :
|
||||
|
||||
\code
|
||||
signals/real-hand-movements.gdf
|
||||
\endcode
|
||||
|
||||
Signal data read by this box can be forwarded to the 2D Topographic Map plugin, which has a single input connector. However, interpolating unprocessed data will not deliver very meaningful
|
||||
results. To make it easier to analyse, one can restrict signals to a frequency band of interest. Also, it can be desirable to average out signals using DSPs (a typical formula
|
||||
consists in visualising log(1+X*X) where X represents incoming signals) and epoch average boxes. This should result in smoothed out signals where peak activity areas will move gradually
|
||||
between frames, thus making them easily identifiable on the map.
|
||||
|
||||
Let's use a temporal filter box (found under 'Signal Processing > Filters') to restrict incoming signals to a frequency band of our choice, e.g. the Beta range, about 16-24 Hz.
|
||||
Appropriate settings could go like this : 'Butterworth' filter type, 'Band Pass' filtering, 16Hz low pass band edge, 24Hz high pass band edge).
|
||||
|
||||
Next, one can average out signals by applying the typical log(1+X*X) formula. A 'Simple DSP' (found under 'Signal Processing > Basic') can compute the square of the signal ('Equation'
|
||||
should read 'X*X'), its output is averaged using a Signal Average box (found under 'Signal Processing > Basic'), and the decimal log of its output is obtained using another
|
||||
Simple DSP box (formula : 'log(X)')).
|
||||
|
||||
Finally, once data has been restricted to a given frequency band and averaged out, it can be further smoothed out in time using an Epoch Average box ('Signal Processing > Averaging').
|
||||
This allows to average data over a number of epochs. re information.
|
||||
|
||||
Here is what an EEG recording may look like using the scenario that was just described :
|
||||
|
||||
\image html topographicmap2ddisplay_online.png "Top view mapping of potentials interpolated from 9 EEG electrodes."
|
||||
\image latex topographicmap2ddisplay_online.png "Top view mapping of potentials interpolated from 9 EEG electrodes." width=10cm
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_2DTopographicMap_Examples|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_2DTopographicMap_Miscellaneous|
|
||||
* |OVP_DocEnd_BoxAlgorithm_2DTopographicMap_Miscellaneous|
|
||||
*/
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* \page BoxAlgorithm_MatrixDisplay Matrix display
|
||||
__________________________________________________________________
|
||||
|
||||
Detailed description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Description|
|
||||
This box displays an input matrix in a table of gradient-colored squares.
|
||||
<b> WARNING : The color gradient is centered (50%) at the value 0. </b>
|
||||
The limits of colors match the minimum and maximum values, ever received or in real time if the corresponding setting is set.
|
||||
These values can evolve symmetrically, using the maximum absolute value.
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Description|
|
||||
__________________________________________________________________
|
||||
|
||||
Inputs description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Inputs|
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Inputs|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Input1|
|
||||
The matrix to display.
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Input1|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Settings description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Settings|
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Settings|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Setting1|
|
||||
The color gradient used.
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Setting1|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Setting2|
|
||||
The number of color steps used in the gradient. The more you have steps, the smoother the gradient will be .
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Setting2|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Setting3|
|
||||
This parameter determines if the minimum and maximum values, matching the 0% and 100% color in the gradient,
|
||||
should change symmetrically using the maximum absolute value and its opposite.
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Setting3|
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Setting4|
|
||||
This parameter determines if the minimum and maximum values, matching the 0% and 100% color in the gradient,
|
||||
should be recomputed for each matrix received.
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Setting4|
|
||||
__________________________________________________________________
|
||||
|
||||
Online visualisation settings
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_OnlineVisualizationSettings|
|
||||
Online settings :
|
||||
- Setting1 : Show/hide values
|
||||
- Setting2 : Show/hide colors
|
||||
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_OnlineVisualizationSettings|
|
||||
__________________________________________________________________
|
||||
|
||||
Examples description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Examples|
|
||||
Here is an example using a \ref BoxAlgorithm_ConfusionMatrix box that computes confusion matrix from classifier results.
|
||||
|
||||
\image html "matrix_display_online.png" "Confusion matrix displayed using the Matrix Display"
|
||||
\image latex "matrix_display_online.png" "Confusion matrix displayed using the Matrix Display" width=10cm
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Examples|
|
||||
|
||||
__________________________________________________________________
|
||||
|
||||
Miscellaneous description
|
||||
__________________________________________________________________
|
||||
|
||||
* |OVP_DocBegin_BoxAlgorithm_MatrixDisplay_Miscellaneous|
|
||||
* |OVP_DocEnd_BoxAlgorithm_MatrixDisplay_Miscellaneous|
|
||||
*/
|
||||
@@ -0,0 +1,129 @@
|
||||
.. _Doc_BoxAlgorithm_2DTopographicMap:
|
||||
|
||||
2D topographic map
|
||||
==================
|
||||
|
||||
.. container:: attribution
|
||||
|
||||
:Author:
|
||||
Vincent Delannoy
|
||||
:Company:
|
||||
INRIA/IRISA
|
||||
|
||||
.. image:: images/Doc_BoxAlgorithm_2DTopographicMap.png
|
||||
|
||||
The 2D Topographic Map plugin is one possible way to combine signal measures and topographical information. It interpolates measured potentials
|
||||
and maps them over the scalp surface using a color scale, making it easy to locate areas where brain activity is the most intense at any given time.
|
||||
|
||||
Since this is a 2D plugin, one cannot freely move the camera around the head. However, the user may switch between several views which,
|
||||
when combined, cover the whole of the skull area over which potentials may be mapped.
|
||||
|
||||
This plugin uses a spherical spline algorithm to interpolate potentials measured at electrode locations over an area of interest (which varies depending on the active view).
|
||||
One can choose to interpolate potentials (which is done by interpolating along a spline whose control points lie at electrode locations) or current densities (which are
|
||||
computed using the spline laplacian).
|
||||
|
||||
Inputs
|
||||
------
|
||||
|
||||
.. csv-table::
|
||||
:header: "Input Name", "Stream Type"
|
||||
|
||||
"Signal", "Streamed matrix"
|
||||
"Channel localization", "Channel localisation"
|
||||
|
||||
Note: the channel names specified on the 'Signal' and 'Channel Localization' input streams should match. Mismatches may not be reported by the box. For example, if electrode localisation file reader provides the positions of the electrodes from some configuration file, the channel names in that file should correspend to the names of the signal channels delivered in the Signal stream.
|
||||
|
||||
.. _Doc_BoxAlgorithm_2DTopographicMap_Settings:
|
||||
|
||||
Settings
|
||||
--------
|
||||
|
||||
.. csv-table::
|
||||
:header: "Setting Name", "Type", "Default Value"
|
||||
|
||||
"Interpolation type", "Spherical linear interpolation type", "1"
|
||||
"Delay (in s)", "Float", "0"
|
||||
|
||||
Offline options include interpolation type (direct spline interpolation for potentials, or spline laplacian to map currents) and delay applied when mapping data (0 by default). The latter
|
||||
can prove useful in the case of e.g. neurofeedback experiments. Indeed, subjects may find it easier to observe their mental activity and the effect mental tasks can have on it
|
||||
when it is displayed with a small delay (on the order of a few hundreds of milliseconds).
|
||||
|
||||
Interpolation type
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Spline or laplacian interpolation.
|
||||
|
||||
Delay (in s)
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Delay to apply to displayed data, in seconds
|
||||
|
||||
.. _Doc_BoxAlgorithm_2DTopographicMap_VizSettings:
|
||||
|
||||
Visualization Settings
|
||||
----------------------
|
||||
|
||||
Online options include :
|
||||
|
||||
|
||||
- Radial/Axial Projection : these settings control the projection mode. Radial projection displays a greater mapping area, but localisation is less intuitive than
|
||||
|
||||
with axial projection.
|
||||
|
||||
- Map Potentials/Currents : these settings control the nature of mapped values. While potentials are computed from spline values, currents computation uses the spline laplacian.
|
||||
- Toggle Electrodes : toggle electrodes on/off.
|
||||
- Top/Left/Right/Back View : these buttons enable view switching.
|
||||
- Delay : this cursor defines the delay to apply to the values that are drawn.
|
||||
|
||||
|
||||
.. figure:: images/topographicmap2ddisplay_toolbar.png
|
||||
:alt: 2D Topographic Map toolbar.
|
||||
:align: center
|
||||
|
||||
2D Topographic Map toolbar.
|
||||
|
||||
.. _Doc_BoxAlgorithm_2DTopographicMap_Examples:
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Practical example :
|
||||
|
||||
This example is based on the following scenario file :
|
||||
|
||||
|
||||
.. code::
|
||||
|
||||
box-tutorials/topographic_map.xml
|
||||
|
||||
To see this plugin in action, one should first locate a file corresponding to a prerecorded EEG or MEG session and play it back using e. g. a GDF file reader or some other suitable plugin.
|
||||
The GDF file used by default in this scenario is the following :
|
||||
|
||||
|
||||
.. code::
|
||||
|
||||
signals/real-hand-movements.gdf
|
||||
|
||||
Signal data read by this box can be forwarded to the 2D Topographic Map plugin, which has a single input connector. However, interpolating unprocessed data will not deliver very meaningful
|
||||
results. To make it easier to analyse, one can restrict signals to a frequency band of interest. Also, it can be desirable to average out signals using DSPs (a typical formula
|
||||
consists in visualising log(1+X\*X) where X represents incoming signals) and epoch average boxes. This should result in smoothed out signals where peak activity areas will move gradually
|
||||
between frames, thus making them easily identifiable on the map.
|
||||
|
||||
Let's use a temporal filter box (found under 'Signal Processing > Filters') to restrict incoming signals to a frequency band of our choice, e.g. the Beta range, about 16-24 Hz.
|
||||
Appropriate settings could go like this : 'Butterworth' filter type, 'Band Pass' filtering, 16Hz low pass band edge, 24Hz high pass band edge).
|
||||
|
||||
Next, one can average out signals by applying the typical log(1+X\*X) formula. A 'Simple DSP' (found under 'Signal Processing > Basic') can compute the square of the signal ('Equation'
|
||||
should read 'X\*X'), its output is averaged using a Signal Average box (found under 'Signal Processing > Basic'), and the decimal log of its output is obtained using another
|
||||
Simple DSP box (formula : 'log(X)')).
|
||||
|
||||
Finally, once data has been restricted to a given frequency band and averaged out, it can be further smoothed out in time using an Epoch Average box ('Signal Processing > Averaging').
|
||||
This allows to average data over a number of epochs. re information.
|
||||
|
||||
Here is what an EEG recording may look like using the scenario that was just described :
|
||||
|
||||
.. figure:: images/topographicmap2ddisplay_online.png
|
||||
:alt: Top view mapping of potentials interpolated from 9 EEG electrodes.
|
||||
:align: center
|
||||
|
||||
Top view mapping of potentials interpolated from 9 EEG electrodes.
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
.. _Doc_BoxAlgorithm_MatrixDisplay:
|
||||
|
||||
Matrix Display
|
||||
==============
|
||||
|
||||
.. container:: attribution
|
||||
|
||||
:Author:
|
||||
Laurent Bonnet
|
||||
:Company:
|
||||
INRIA/IRISA
|
||||
|
||||
.. image:: images/Doc_BoxAlgorithm_MatrixDisplay.png
|
||||
|
||||
The streamed matrix can be visualized using a table of values and/or a color gradient.
|
||||
|
||||
This box displays an input matrix in a table of gradient-colored squares.
|
||||
**WARNING : The color gradient is centered (50%) at the value 0.**
|
||||
The limits of colors match the minimum and maximum values, ever received or in real time if the corresponding setting is set.
|
||||
These values can evolve symmetrically, using the maximum absolute value.
|
||||
|
||||
Inputs
|
||||
------
|
||||
|
||||
.. csv-table::
|
||||
:header: "Input Name", "Stream Type"
|
||||
|
||||
"Matrix", "Streamed matrix"
|
||||
|
||||
Matrix
|
||||
~~~~~~
|
||||
|
||||
The matrix to display.
|
||||
|
||||
.. _Doc_BoxAlgorithm_MatrixDisplay_Settings:
|
||||
|
||||
Settings
|
||||
--------
|
||||
|
||||
.. csv-table::
|
||||
:header: "Setting Name", "Type", "Default Value"
|
||||
|
||||
"Color gradient", "Color Gradient", "0:2,36,58; 50:100,100,100; 100:83,17,20"
|
||||
"Steps", "Integer", "100"
|
||||
"Symetric min/max", "Boolean", "false"
|
||||
"Real time min/max", "Boolean", "false"
|
||||
|
||||
Color gradient
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The color gradient used.
|
||||
|
||||
Steps
|
||||
~~~~~
|
||||
|
||||
The number of color steps used in the gradient. The more you have steps, the smoother the gradient will be .
|
||||
|
||||
Symetric min/max
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
This parameter determines if the minimum and maximum values, matching the 0% and 100% color in the gradient,
|
||||
should change symmetrically using the maximum absolute value and its opposite.
|
||||
|
||||
Real time min/max
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
This parameter determines if the minimum and maximum values, matching the 0% and 100% color in the gradient,
|
||||
should be recomputed for each matrix received.
|
||||
|
||||
.. _Doc_BoxAlgorithm_MatrixDisplay_VizSettings:
|
||||
|
||||
Visualization Settings
|
||||
----------------------
|
||||
|
||||
Online settings :
|
||||
|
||||
- Setting1 : Show/hide values
|
||||
- Setting2 : Show/hide colors
|
||||
|
||||
|
||||
|
||||
.. _Doc_BoxAlgorithm_MatrixDisplay_Examples:
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Here is an example using a :ref:`Doc_BoxAlgorithm_ConfusionMatrix` box that computes confusion matrix from classifier results.
|
||||
|
||||
.. figure:: images/matrix_display_online.png
|
||||
:alt: Confusion matrix displayed using the Matrix Display
|
||||
:align: center
|
||||
|
||||
Confusion matrix displayed using the Matrix Display
|
||||
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 2.6 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkWindow" id="matrix-display">
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<child>
|
||||
<object class="GtkTable" id="matrix-display-table">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="border_width">8</property>
|
||||
<property name="column_spacing">8</property>
|
||||
<property name="row_spacing">8</property>
|
||||
<property name="homogeneous">True</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkWindow" id="matrix-display-toolbar">
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="title" translatable="yes">Matrix Display</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child>
|
||||
<object class="GtkToolbar" id="matrix-display-settings">
|
||||
<property name="visible">True</property>
|
||||
<property name="show_arrow">False</property>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton" id="show-values-toggle-button">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="label" translatable="yes">show values</property>
|
||||
<property name="stock_id">gtk-refresh</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="separator">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton" id="show-colors-toggle-button">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="label" translatable="yes">show colors</property>
|
||||
<property name="stock_id">gtk-select-color</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkWindow" id="dummy-window">
|
||||
<child>
|
||||
<object class="GtkEventBox" id="matrix-value-eventbox">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="matrix-value-label">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">X</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -0,0 +1,208 @@
|
||||
<?xml version="1.0"?>
|
||||
<interface>
|
||||
<!-- interface-requires gtk+ 2.12 -->
|
||||
<!-- interface-naming-policy toplevel-contextual -->
|
||||
<object class="GtkAdjustment" id="adjustment1">
|
||||
<property name="upper">2</property>
|
||||
<property name="step_increment">0.10000000000000001</property>
|
||||
<property name="page_increment">0.5</property>
|
||||
<property name="page_size">10</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="Toolbar">
|
||||
<property name="title" translatable="yes">2D Topographic Map Toolbar</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="window_position">mouse</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child>
|
||||
<object class="GtkToolbar" id="DisplayToolbar">
|
||||
<property name="visible">True</property>
|
||||
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
|
||||
<property name="toolbar_style">both</property>
|
||||
<property name="show_arrow">False</property>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="RadialProjection">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Radial Projection</property>
|
||||
<property name="stock_id">gtk-convert</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="AxialProjection">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Axial Projection</property>
|
||||
<property name="stock_id">gtk-convert</property>
|
||||
<property name="group">RadialProjection</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="separator">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="MapPotentials">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Map Potentials</property>
|
||||
<property name="stock_id">gtk-preferences</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="MapCurrents">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Map Currents</property>
|
||||
<property name="stock_id">gtk-preferences</property>
|
||||
<property name="group">MapPotentials</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="separator1">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleToolButton" id="ToggleElectrodes">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Toggle Electrodes</property>
|
||||
<property name="stock_id">gtk-info</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="separator2">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="TopView">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Top View</property>
|
||||
<property name="stock_id">gtk-orientation-portrait</property>
|
||||
<property name="active">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="LeftView">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Left View</property>
|
||||
<property name="stock_id">gtk-orientation-portrait</property>
|
||||
<property name="group">TopView</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="RightView">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Right View</property>
|
||||
<property name="stock_id">gtk-orientation-portrait</property>
|
||||
<property name="group">TopView</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioToolButton" id="BackView">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Back View</property>
|
||||
<property name="stock_id">gtk-orientation-portrait</property>
|
||||
<property name="group">TopView</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorToolItem" id="sep">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToolItem" id="toolbutton2">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkVBox" id="vbox1">
|
||||
<property name="visible">True</property>
|
||||
<child>
|
||||
<object class="GtkHScale" id="DelayScale">
|
||||
<property name="width_request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="adjustment">adjustment1</property>
|
||||
<property name="lower_stepper_sensitivity">on</property>
|
||||
<property name="upper_stepper_sensitivity">on</property>
|
||||
<property name="restrict_to_fill_level">False</property>
|
||||
<property name="fill_level">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Delay (s)</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkWindow" id="Window">
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="TopographicMap2DDrawingArea">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -0,0 +1,613 @@
|
||||
/*****************************************************************************/
|
||||
/** **/
|
||||
/** linpack : matrix computation **/
|
||||
/** **/
|
||||
/** from Linpack User's guide **/
|
||||
/** J.J. Dongarra, J.R. Brunch, C.B. Moler, G.W. Stewart **/
|
||||
/** SIAM Philadelphia 1979 **/
|
||||
/** **/
|
||||
/** Routines are translated to C from original FORTRAN code **/
|
||||
/** **/
|
||||
/*****************************************************************************/
|
||||
|
||||
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
|
||||
/*& Libraries to include &*/
|
||||
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
#define dmax(x,y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
void saxpy(const int* n, const double* sa, const double* sx, const int* incx, double* sy, const int* incy);
|
||||
double sdot(const int* n, const double* sx, const int* incx, const double* sy, const int* incy);
|
||||
void sswap(const int* n, double* sx, const int* incx, double* sy, const int* incy);
|
||||
int isamax(const int* n, const double* sx, const int* incx);
|
||||
|
||||
/************************************************************************/
|
||||
/* sspfa */
|
||||
/* */
|
||||
/* Factorization of a symmetric matrix A (n x n) */
|
||||
/* by elimination with symmetric pivoting */
|
||||
/* */
|
||||
/* to solve A * X = B, follow sspfa by sspl */
|
||||
/* */
|
||||
/* IN */
|
||||
/* ap : 1-D array (double) */
|
||||
/* the packed form of the symmetric matrix A */
|
||||
/* the columns of the upper triangle are stored sequentially */
|
||||
/* in a 1-dimensional array of length n*(n+1)/2 */
|
||||
/* n : order of matrix A (integer) */
|
||||
/* */
|
||||
/* OUT */
|
||||
/* ap : 1-D array (double) */
|
||||
/* a block diagonal matrix and the multipliers which were used */
|
||||
/* to obtain it stored in packed form. */
|
||||
/* the factorization can be written A = U * D * trans(U) */
|
||||
/* where U is a product of permutation and unit upper triangular*/
|
||||
/* matrices, trans(U) is the transpose of U, and D is block */
|
||||
/* diagonal with 1 by 1 and 2 by 2 blocks. */
|
||||
/* kpvt : 1-D array (integer) */
|
||||
/* vector of pivot indices */
|
||||
/* info : return code (integer) */
|
||||
/* = 0 normal value */
|
||||
/* = k if the k-th pivot block is singular */
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
void sspfa(double* ap, const int* n, int* kpvt, int* info)
|
||||
{
|
||||
double alpha, ak, bk, denom, t, tulk, tulkm1, absakk;
|
||||
double akm1, bkm1, colmax, rowmax;
|
||||
int ij, ik, ikm1, im = 0, imj, imk, imax, imaxp1, imim;
|
||||
int j, jj, jk, jkm1, jmax, jmim;
|
||||
int k, kk, km1, km2, km1K, km1Km1;
|
||||
int kstep, swap;
|
||||
int one, itmp;
|
||||
|
||||
one = 1;
|
||||
alpha = 0.6404;
|
||||
*info = 0;
|
||||
k = *n;
|
||||
ik = (*n * (*n - 1)) / 2;
|
||||
|
||||
begin_dspfa:;
|
||||
if (k == 0) { goto end_dspfa; }
|
||||
if (k <= 1)
|
||||
{
|
||||
*kpvt = 1;
|
||||
if (*ap == 0.0) { *info = 1; }
|
||||
goto end_dspfa;
|
||||
}
|
||||
km1 = k - 1;
|
||||
kk = ik + k;
|
||||
absakk = fabs(*(ap + kk - 1));
|
||||
itmp = k - 1;
|
||||
imax = isamax(&itmp, (ap + ik), &one);
|
||||
imk = ik + imax;
|
||||
colmax = fabs(*(ap + imk - 1));
|
||||
if (absakk >= (alpha * colmax))
|
||||
{
|
||||
kstep = 1;
|
||||
swap = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rowmax = 0.0;
|
||||
imaxp1 = imax + 1;
|
||||
im = (imax * (imax - 1)) / 2;
|
||||
imj = im + 2 * imax;
|
||||
for (j = imaxp1; j <= k; ++j)
|
||||
{
|
||||
rowmax = dmax(rowmax, fabs(*(ap + imj - 1)));
|
||||
imj += j;
|
||||
}
|
||||
if (imax != 1)
|
||||
{
|
||||
itmp = imax - 1;
|
||||
jmax = isamax(&itmp, (ap + im), &one);
|
||||
jmim = jmax + im;
|
||||
rowmax = dmax(rowmax, fabs(*(ap + jmim - 1)));
|
||||
}
|
||||
imim = imax + im;
|
||||
if (fabs(*(ap + imim - 1)) >= (alpha * rowmax))
|
||||
{
|
||||
kstep = 1;
|
||||
swap = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (absakk >= (alpha * colmax * (colmax / rowmax)))
|
||||
{
|
||||
kstep = 1;
|
||||
swap = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
kstep = 2;
|
||||
swap = (imax != km1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dmax(absakk, colmax) == 0.0)
|
||||
{
|
||||
*(kpvt + k - 1) = k;
|
||||
*info = k;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (kstep != 2)
|
||||
{
|
||||
if (swap)
|
||||
{
|
||||
sswap(&imax, (ap + im), &one, (ap + ik), &one);
|
||||
imj = ik + imax;
|
||||
for (jj = imax; jj <= k; ++jj)
|
||||
{
|
||||
j = k + imax - jj;
|
||||
jk = ik + j;
|
||||
t = *(ap + jk - 1);
|
||||
*(ap + jk - 1) = *(ap + imj - 1);
|
||||
*(ap + imj - 1) = t;
|
||||
imj -= j - 1;
|
||||
}
|
||||
}
|
||||
ij = ik - (k - 1);
|
||||
for (jj = 1; jj <= km1; ++jj)
|
||||
{
|
||||
j = k - jj;
|
||||
jk = ik + j;
|
||||
tulk = -(*(ap + jk - 1)) / (*(ap + kk - 1));
|
||||
t = tulk;
|
||||
saxpy(&j, &t, (ap + ik), &one, (ap + ij), &one);
|
||||
// FIXME is it necessary to keep next line uncomment ?
|
||||
//ijj = ij + j;
|
||||
*(ap + jk - 1) = tulk;
|
||||
ij -= j - 1;
|
||||
}
|
||||
*(kpvt + k - 1) = k;
|
||||
if (swap) { *(kpvt + k - 1) = imax; }
|
||||
}
|
||||
else
|
||||
{
|
||||
km1K = ik + k - 1;
|
||||
ikm1 = ik - (k - 1);
|
||||
if (swap)
|
||||
{
|
||||
sswap(&imax, (ap + im), &one, (ap + ikm1), &one);
|
||||
imj = ikm1 + imax;
|
||||
for (jj = imax; jj <= km1; ++jj)
|
||||
{
|
||||
j = km1 + imax - jj;
|
||||
jkm1 = ikm1 + j;
|
||||
t = *(ap + jkm1 - 1);
|
||||
*(ap + jkm1 - 1) = *(ap + imj - 1);
|
||||
*(ap + imj - 1) = t;
|
||||
imj -= j - 1;
|
||||
}
|
||||
t = *(ap + km1K - 1);
|
||||
*(ap + km1K - 1) = *(ap + imk - 1);
|
||||
*(ap + imk - 1) = t;
|
||||
}
|
||||
km2 = k - 2;
|
||||
if (km2 != 0)
|
||||
{
|
||||
ak = *(ap + kk - 1) / (*(ap + km1K - 1));
|
||||
km1Km1 = ikm1 + k - 1;
|
||||
akm1 = *(ap + km1Km1 - 1) / (*(ap + km1K - 1));
|
||||
denom = 1.0 - ak * akm1;
|
||||
ij = ik - (k - 1) - (k - 2);
|
||||
for (jj = 1; jj <= km2; ++jj)
|
||||
{
|
||||
j = km1 - jj;
|
||||
jk = ik + j;
|
||||
bk = *(ap + jk - 1) / (*(ap + km1K - 1));
|
||||
jkm1 = ikm1 + j;
|
||||
bkm1 = *(ap + jkm1 - 1) / (*(ap + km1K - 1));
|
||||
tulk = (akm1 * bk - bkm1) / denom;
|
||||
tulkm1 = (ak * bkm1 - bk) / denom;
|
||||
t = tulk;
|
||||
saxpy(&j, &t, (ap + ik), &one, (ap + ij), &one);
|
||||
t = tulkm1;
|
||||
saxpy(&j, &t, (ap + ikm1), &one, (ap + ij), &one);
|
||||
*(ap + jk - 1) = tulk;
|
||||
*(ap + jkm1 - 1) = tulkm1;
|
||||
// FIXME is it necessary to keep next line uncomment ?
|
||||
//ijj = ij + j;
|
||||
ij -= j - 1;
|
||||
}
|
||||
}
|
||||
*(kpvt + k - 1) = 1 - k;
|
||||
if (swap) { *(kpvt + k - 1) = -imax; }
|
||||
*(kpvt + k - 2) = *(kpvt + k - 1);
|
||||
}
|
||||
}
|
||||
ik -= k - 1;
|
||||
if (kstep == 2) { ik -= k - 2; }
|
||||
k -= kstep;
|
||||
goto begin_dspfa;
|
||||
end_dspfa:;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* sspsl */
|
||||
/* */
|
||||
/* Solving a symmetric system A * X = B using the factorization */
|
||||
/* computed by sspfa (with return code = 0) */
|
||||
/* */
|
||||
/* IN */
|
||||
/* ap : 1-D array (double) */
|
||||
/* the output array from sspfa */
|
||||
/* n : order of matrix A (integer) */
|
||||
/* kpvt : 1-D array (integer) */
|
||||
/* the pivot vector from sspfa */
|
||||
/* b : 1-D array (double) */
|
||||
/* the right hand side vector */
|
||||
/* */
|
||||
/* OUT */
|
||||
/* b : 1-D array (double) */
|
||||
/* the solution vector X */
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
void sspsl(double* ap, const int* n, const int* kpvt, double* b)
|
||||
{
|
||||
double temp;
|
||||
int kp;
|
||||
int one, oneb, itmp;
|
||||
|
||||
one = 1;
|
||||
oneb = 1;
|
||||
int k = *n;
|
||||
int ik = (*n * (*n - 1)) / 2;
|
||||
|
||||
while (k > 0)
|
||||
{
|
||||
int kk = ik + k;
|
||||
if (*(kpvt + k - 1) >= 0)
|
||||
{
|
||||
if (k != 1)
|
||||
{
|
||||
kp = *(kpvt + k - 1);
|
||||
if (kp != k)
|
||||
{
|
||||
temp = *(b + k - 1);
|
||||
*(b + k - 1) = *(b + kp - 1);
|
||||
*(b + kp - 1) = temp;
|
||||
}
|
||||
itmp = k - 1;
|
||||
saxpy(&itmp, (b + k - 1), (ap + ik), &one, b, &oneb);
|
||||
}
|
||||
*(b + k - 1) /= *(ap + kk - 1);
|
||||
k--;
|
||||
ik -= k;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int ikm1 = ik - (k - 1);
|
||||
if (ik != 2)
|
||||
{
|
||||
kp = abs(*(kpvt + k - 1));
|
||||
if (kp != (k - 1))
|
||||
{
|
||||
temp = *(b + k - 2);
|
||||
*(b + k - 2) = *(b + kp - 1);
|
||||
*(b + kp - 1) = temp;
|
||||
}
|
||||
itmp = k - 2;
|
||||
saxpy(&itmp, (b + k - 1), (ap + ik), &one, b, &oneb);
|
||||
saxpy(&itmp, (b + k - 2), (ap + ikm1), &one, b, &oneb);
|
||||
}
|
||||
const int km1k = ik + k - 1;
|
||||
kk = ik + k;
|
||||
const double ak = *(ap + kk - 1) / (*(ap + km1k - 1));
|
||||
const int km1km1 = ikm1 + k - 1;
|
||||
const double akm1 = *(ap + km1km1 - 1) / (*(ap + km1k - 1));
|
||||
const double bk = *(b + k - 1) / (*(ap + km1k - 1));
|
||||
const double bkm1 = *(b + k - 2) / (*(ap + km1k - 1));
|
||||
const double denom = ak * akm1 - 1.0;
|
||||
*(b + k - 1) = (akm1 * bk - bkm1) / denom;
|
||||
*(b + k - 2) = (ak * bkm1 - bk) / denom;
|
||||
k -= 2;
|
||||
ik -= (2 * k + 1);
|
||||
}
|
||||
}
|
||||
{
|
||||
k = 1;
|
||||
ik = 0;
|
||||
while (k <= *n)
|
||||
{
|
||||
if (*(kpvt + k - 1) >= 0)
|
||||
{
|
||||
if (k != 1)
|
||||
{
|
||||
itmp = k - 1;
|
||||
*(b + k - 1) += sdot(&itmp, (ap + ik), &one, b, &oneb);
|
||||
kp = *(kpvt + k - 1);
|
||||
if (kp != k)
|
||||
{
|
||||
temp = *(b + k - 1);
|
||||
*(b + k - 1) = *(b + kp - 1);
|
||||
*(b + kp - 1) = temp;
|
||||
}
|
||||
}
|
||||
ik += k;
|
||||
k++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (k != 1)
|
||||
{
|
||||
itmp = k - 1;
|
||||
*(b + k - 1) += sdot(&itmp, (ap + ik), &one, b, &oneb);
|
||||
const int ikp1 = ik + k;
|
||||
*(b + k) += sdot(&itmp, (ap + ikp1), &one, b, &oneb);
|
||||
kp = abs(*(kpvt + k - 1));
|
||||
if (kp != k)
|
||||
{
|
||||
temp = *(b + k - 1);
|
||||
*(b + k - 1) = *(b + kp - 1);
|
||||
*(b + kp - 1) = temp;
|
||||
}
|
||||
}
|
||||
ik += (2 * k + 1);
|
||||
k += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* saxpy */
|
||||
/* */
|
||||
/* Constant times a vector plus a vector */
|
||||
/* Y = A*X + Y */
|
||||
/* uses enrolled loops for increments equal to 1 */
|
||||
/* */
|
||||
/* IN */
|
||||
/* n : length of vector X and Y (integer) */
|
||||
/* sa : real constant A (double) */
|
||||
/* sx : vector X (double) */
|
||||
/* incx : increment for X (integer) */
|
||||
/* sy : vector Y (double) */
|
||||
/* incy : increment for Y (integer) */
|
||||
/* */
|
||||
/* OUT */
|
||||
/* sy : the result vector A*X + Y (double) */
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
void saxpy(const int* n, const double* sa, const double* sx, const int* incx, double* sy, const int* incy)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (*n > 0)
|
||||
{
|
||||
if (*sa != 0)
|
||||
{
|
||||
if ((*incx != 1) || (*incy != 1))
|
||||
{
|
||||
int ix = 1;
|
||||
int iy = 1;
|
||||
if (*incx < 0) { ix = (1 - *n) * (*incx) + 1; }
|
||||
if (*incy < 0) { iy = (1 - *n) * (*incy) + 1; }
|
||||
for (i = 1; i <= *n; ++i)
|
||||
{
|
||||
*(sy + iy - 1) += *sa * (*(sx + ix - 1));
|
||||
ix += *incx;
|
||||
iy += *incy;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int ok = 1;
|
||||
int m = int(fmod(double(*n), 4.0));
|
||||
if (m != 0)
|
||||
{
|
||||
for (i = 1; i <= m; ++i) { *(sy + i - 1) += *sa * (*(sx + i - 1)); }
|
||||
if (*n < 4) { ok = 0; }
|
||||
}
|
||||
if (ok == 1)
|
||||
{
|
||||
for (i = ++m; i <= *n; i += 4)
|
||||
{
|
||||
*(sy + i - 1) += *sa * (*(sx + i - 1));
|
||||
*(sy + i) += *sa * (*(sx + i));
|
||||
*(sy + i + 1) += *sa * (*(sx + i + 1));
|
||||
*(sy + i + 2) += *sa * (*(sx + i + 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* sdot */
|
||||
/* */
|
||||
/* Forms the dot product of 2 vectors */
|
||||
/* = X.Y */
|
||||
/* uses enrolled loops for increments equal to 1 */
|
||||
/* */
|
||||
/* IN */
|
||||
/* n : length of vector X and Y (integer) */
|
||||
/* sx : vector X (double) */
|
||||
/* incx : increment for X (integer) */
|
||||
/* sy : vector Y (double) */
|
||||
/* incy : increment for Y (integer) */
|
||||
/* */
|
||||
/* OUT */
|
||||
/* function value : the result value X.Y (double) */
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
double sdot(const int* n, const double* sx, const int* incx, const double* sy, const int* incy)
|
||||
{
|
||||
int i;
|
||||
double stemp = 0.0;
|
||||
|
||||
if (*n > 0)
|
||||
{
|
||||
if ((*incx != 1) || (*incy != 1))
|
||||
{
|
||||
int ix = 1;
|
||||
int iy = 1;
|
||||
if (*incx < 0) { ix = (1 - *n) * (*incx) + 1; }
|
||||
if (*incy < 0) { iy = (1 - *n) * (*incy) + 1; }
|
||||
for (i = 1; i <= *n; ++i)
|
||||
{
|
||||
stemp += *(sx + ix - 1) * (*(sy + iy - 1));
|
||||
ix += *incx;
|
||||
iy += *incy;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int ok = 1;
|
||||
int m = int(fmod(double(*n), 5.0));
|
||||
if (m != 0)
|
||||
{
|
||||
for (i = 1; i <= m; ++i) { stemp += *(sx + i - 1) * (*(sy + i - 1)); }
|
||||
if (*n < 5) { ok = 0; }
|
||||
}
|
||||
if (ok == 1)
|
||||
{
|
||||
for (i = ++m; i <= *n; i += 5)
|
||||
{
|
||||
stemp += *(sx + i - 1) * (*(sy + i - 1)) + *(sx + i) * (*(sy + i))
|
||||
+ *(sx + i + 1) * (*(sy + i + 1)) + *(sx + i + 2) * (*(sy + i + 2))
|
||||
+ *(sx + i + 3) * (*(sy + i + 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stemp;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* sswap */
|
||||
/* */
|
||||
/* Interchanges 2 vectors X and Y */
|
||||
/* uses enrolled loops for increments equal to 1 */
|
||||
/* */
|
||||
/* IN */
|
||||
/* n : length of vector X and Y (integer) */
|
||||
/* sx : vector X (double) */
|
||||
/* incx : increment for X (integer) */
|
||||
/* sy : vector Y (double) */
|
||||
/* incy : increment for Y (integer) */
|
||||
/* */
|
||||
/* OUT */
|
||||
/* sx : vector Y (double) */
|
||||
/* sy : vector X (double) */
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
void sswap(const int* n, double* sx, const int* incx, double* sy, const int* incy)
|
||||
{
|
||||
double stemp;
|
||||
int i;
|
||||
|
||||
if (*n > 0)
|
||||
{
|
||||
if ((*incx != 1) || (*incy != 1))
|
||||
{
|
||||
int ix = 1;
|
||||
int iy = 1;
|
||||
if (*incx < 0) { ix = (1 - *n) * (*incx) + 1; }
|
||||
if (*incy < 0) { iy = (1 - *n) * (*incy) + 1; }
|
||||
for (i = 1; i <= *n; ++i)
|
||||
{
|
||||
stemp = *(sx + ix - 1);
|
||||
*(sx + ix - 1) = *(sy + iy - 1);
|
||||
*(sy + iy - 1) = stemp;
|
||||
ix += *incx;
|
||||
iy += *incy;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int ok = 1;
|
||||
int m = int(fmod(double(*n), 3.0));
|
||||
if (m != 0)
|
||||
{
|
||||
for (i = 1; i <= m; ++i)
|
||||
{
|
||||
stemp = *(sx + i - 1);
|
||||
*(sx + i - 1) = *(sy + i - 1);
|
||||
*(sy + i - 1) = stemp;
|
||||
}
|
||||
if (*n < 3) { ok = 0; }
|
||||
}
|
||||
if (ok == 1)
|
||||
{
|
||||
for (i = ++m; i <= *n; i += 3)
|
||||
{
|
||||
stemp = *(sx + i - 1);
|
||||
*(sx + i - 1) = *(sy + i - 1);
|
||||
*(sy + i - 1) = stemp;
|
||||
stemp = *(sx + i);
|
||||
*(sx + i) = *(sy + i);
|
||||
*(sy + i) = stemp;
|
||||
stemp = *(sx + i + 1);
|
||||
*(sx + i + 1) = *(sy + i + 1);
|
||||
*(sy + i + 1) = stemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* isamax */
|
||||
/* */
|
||||
/* Finds the index of element having max absolute value in vector X */
|
||||
/* */
|
||||
/* IN */
|
||||
/* n : length of vector X (integer) */
|
||||
/* sx : vector X (double) */
|
||||
/* incx : increment for X (integer) */
|
||||
/* */
|
||||
/* OUT */
|
||||
/* function value : index of max absolute value of X (integer) */
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
int isamax(const int* n, const double* sx, const int* incx)
|
||||
{
|
||||
double smax;
|
||||
int i;
|
||||
|
||||
int ida = 0;
|
||||
if (*n < 1) { return ida; }
|
||||
ida = 1;
|
||||
if (*n == 1) { return ida; }
|
||||
if (*incx != 1)
|
||||
{
|
||||
int ix = 1;
|
||||
smax = fabs(*sx);
|
||||
ix += *incx;
|
||||
for (i = 2; i <= *n; ++i)
|
||||
{
|
||||
if (fabs(*(sx + ix - 1)) > smax)
|
||||
{
|
||||
ida = i;
|
||||
smax = fabs(*(sx + ix - 1));
|
||||
}
|
||||
ix += *incx;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smax = fabs(*sx);
|
||||
for (i = 2; i <= *n; ++i)
|
||||
{
|
||||
if (fabs(*(sx + i - 1)) > smax)
|
||||
{
|
||||
ida = i;
|
||||
smax = fabs(*(sx + i - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ida;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*************************************************************************************/
|
||||
/* linpack.h */
|
||||
/*************************************************************************************/
|
||||
|
||||
/*************************************************************************************/
|
||||
/* Linpack subroutines */
|
||||
/*************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
void sspfa(double* ap, const int* n, int* kpvt, int* info);
|
||||
void sspsl(double* ap, const int* n, const int* kpvt, double* b);
|
||||
@@ -0,0 +1,267 @@
|
||||
#include "ovpCAlgorithmSphericalSplineInterpolation.h"
|
||||
|
||||
//INSERM lib
|
||||
#include "spline_sph.h"
|
||||
|
||||
#include <cfloat> //DBL_MAX
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Test {
|
||||
|
||||
bool CAlgorithmSphericalSplineInterpolation::initialize()
|
||||
|
||||
{
|
||||
m_firstProcess = true;
|
||||
m_coords.clear();
|
||||
m_coordsPtr.clear();
|
||||
m_splineCoefs.clear();
|
||||
m_laplacianCoefs.clear();
|
||||
|
||||
ip_splineOrder.initialize(getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SplineOrder));
|
||||
ip_nControlPoints.initialize(getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCount));
|
||||
ip_controlPointsCoords.initialize(getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCoordinates));
|
||||
ip_controlPointsValues.initialize(getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsValues));
|
||||
ip_samplePointsCoords.initialize(getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SamplePointsCoordinates));
|
||||
|
||||
op_samplePointsValues.initialize(getOutputParameter(OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_SamplePointsValues));
|
||||
op_samplePointsValues->setDimensionCount(1);
|
||||
op_minSamplePointValue.initialize(getOutputParameter(OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_MinSamplePointValue));
|
||||
op_maxSamplePointValue.initialize(getOutputParameter(OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_MaxSamplePointValue));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmSphericalSplineInterpolation::uninitialize()
|
||||
|
||||
{
|
||||
ip_splineOrder.uninitialize();
|
||||
ip_nControlPoints.uninitialize();
|
||||
ip_controlPointsCoords.uninitialize();
|
||||
ip_controlPointsValues.uninitialize();
|
||||
ip_samplePointsCoords.uninitialize();
|
||||
|
||||
op_samplePointsValues.uninitialize();
|
||||
op_minSamplePointValue.uninitialize();
|
||||
op_maxSamplePointValue.uninitialize();
|
||||
|
||||
m_coords.clear();
|
||||
m_coordsPtr.clear();
|
||||
m_splineCoefs.clear();
|
||||
m_laplacianCoefs.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAlgorithmSphericalSplineInterpolation::process()
|
||||
|
||||
{
|
||||
if (m_firstProcess)
|
||||
{
|
||||
//store coords as doubles
|
||||
m_coords.resize(3 * size_t(ip_nControlPoints));
|
||||
//set up matrix of pointers to double coords matrix
|
||||
m_coordsPtr.resize(size_t(ip_nControlPoints));
|
||||
//fill both matrices
|
||||
for (size_t i = 0; i < size_t(ip_nControlPoints); ++i)
|
||||
{
|
||||
const size_t id = 3 * i;
|
||||
m_coords[id] = double((*ip_controlPointsCoords)[id]);
|
||||
m_coords[id + 1] = double((*ip_controlPointsCoords)[id + 1]);
|
||||
m_coords[id + 2] = double((*ip_controlPointsCoords)[id + 2]);
|
||||
m_coordsPtr[i] = id + m_coords.data();
|
||||
}
|
||||
m_firstProcess = false;
|
||||
}
|
||||
|
||||
//do we want to precompute tables?
|
||||
if (isInputTriggerActive(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_PrecomputeTables))
|
||||
{
|
||||
//compute cos/sin values used in spline polynomias
|
||||
const int result = SplineTables(int(ip_splineOrder), m_pot.data(), m_scd.data());
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << "Spline tables precomputation failed!\n";
|
||||
activateOutputTrigger(OVP_Algorithm_SphericalSplineInterpolation_OutputTriggerId_Error, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (isInputTriggerActive(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_ComputeSplineCoefs))
|
||||
{
|
||||
if (m_splineCoefs.empty() && size_t(ip_nControlPoints) != 0) { m_splineCoefs.resize(size_t(ip_nControlPoints) + 1); }
|
||||
|
||||
//compute spline ponderation coefficients using spline values
|
||||
//FIXME : have a working copy of control points values stored as doubles?
|
||||
const int result = SplineCoef(int(ip_nControlPoints), m_coordsPtr.data(), ip_controlPointsValues->getBuffer(), m_pot.data(), m_splineCoefs.data());
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << "Spline coefficients computation failed!\n";
|
||||
|
||||
const Kernel::ELogLevel level = Kernel::LogLevel_Debug;
|
||||
|
||||
getLogManager() << level << "CtrlPointsCount = " << int(ip_nControlPoints) << "\n";
|
||||
const auto size = size_t(ip_nControlPoints);
|
||||
std::stringstream ss;
|
||||
ss.fill('0');
|
||||
ss.precision(1);
|
||||
|
||||
ss << "CtrlPointsCoords = ";
|
||||
for (size_t i = 0; i < size; ++i) { ss << std::fixed << "[" << m_coordsPtr[i][0] << " " << m_coordsPtr[i][1] << " " << m_coordsPtr[i][2] << "] "; }
|
||||
ss << "\n";
|
||||
getLogManager() << level << ss.str();
|
||||
|
||||
ss.str("CtrlPointsValues = ");
|
||||
for (size_t i = 0; i < size; ++i) { ss << std::fixed << *(ip_controlPointsValues->getBuffer() + i) << " "; }
|
||||
ss << "\n";
|
||||
getLogManager() << level << ss.str();
|
||||
|
||||
ss.str("Spline Coeffs = ");
|
||||
for (size_t i = 0; i <= size; ++i) { ss << std::fixed << m_splineCoefs[i] << " "; }
|
||||
ss << "\n";
|
||||
getLogManager() << level << ss.str();
|
||||
|
||||
ss.str("PotTable coeffs = ");
|
||||
for (size_t i = 0; i < 10; ++i) { ss << std::fixed << m_pot[i] << " "; }
|
||||
ss << " ... ";
|
||||
for (size_t i = 2001; i < 2004; ++i) { ss << std::fixed << m_pot[i] << " "; }
|
||||
ss << "\n";
|
||||
getLogManager() << level << ss.str();
|
||||
|
||||
activateOutputTrigger(OVP_Algorithm_SphericalSplineInterpolation_OutputTriggerId_Error, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (isInputTriggerActive(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_ComputeLaplacianCoefs))
|
||||
{
|
||||
if (m_laplacianCoefs.empty() && size_t(ip_nControlPoints) != 0) { m_laplacianCoefs.resize(size_t(ip_nControlPoints) + 1); }
|
||||
|
||||
//compute spline ponderation coefficients using spline values
|
||||
//FIXME : have a working copy of control points values stored as doubles?
|
||||
const int result = SplineCoef(int(ip_nControlPoints), m_coordsPtr.data(), ip_controlPointsValues->getBuffer(), m_pot.data(),
|
||||
m_laplacianCoefs.data());
|
||||
m_laplacianCoefs[int(ip_nControlPoints)] = 0;
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << "Laplacian coefficients computation failed!\n";
|
||||
activateOutputTrigger(OVP_Algorithm_SphericalSplineInterpolation_OutputTriggerId_Error, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (isInputTriggerActive(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateSpline))
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
//ensure we got enough storage space for interpolated values
|
||||
if (op_samplePointsValues->getDimensionSize(0) != ip_samplePointsCoords->getDimensionSize(0))
|
||||
{
|
||||
op_samplePointsValues->setDimensionSize(0, ip_samplePointsCoords->getDimensionSize(0));
|
||||
}
|
||||
|
||||
//compute interpolated values using spline
|
||||
double* sampleValue = static_cast<double*>(op_samplePointsValues->getBuffer());
|
||||
|
||||
op_minSamplePointValue = +DBL_MAX;
|
||||
op_maxSamplePointValue = -DBL_MAX;
|
||||
|
||||
for (size_t i = 0; i < ip_samplePointsCoords->getDimensionSize(0); i++, sampleValue++)
|
||||
{
|
||||
#if defined TARGET_OS_Windows
|
||||
#ifndef NDEBUG
|
||||
if (_finite(*(ip_samplePointsCoords->getBuffer() + 3 * i)) == 0 ||
|
||||
_finite(*(ip_samplePointsCoords->getBuffer() + 3 * i + 1)) == 0 ||
|
||||
_finite(*(ip_samplePointsCoords->getBuffer() + 3 * i + 2)) == 0) //tests whether a double is infinite or a NaN
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << "Bad interpolation point coordinates !\n";
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << *(ip_samplePointsCoords->getBuffer() + 3 * i) << "\n";
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << *(ip_samplePointsCoords->getBuffer() + 3 * i + 1) << "\n";
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << *(ip_samplePointsCoords->getBuffer() + 3 * i + 2) << "\n";
|
||||
ok = false;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
*sampleValue = SplineInterp(int(ip_nControlPoints), //number of fixed values
|
||||
m_coordsPtr.data(), //coordinates of fixed values
|
||||
m_pot.data(), //sin/cos table for spline
|
||||
m_splineCoefs.data(), //spline coefficients
|
||||
*(ip_samplePointsCoords->getBuffer() + 3 * i),
|
||||
*(ip_samplePointsCoords->getBuffer() + 3 * i + 1),
|
||||
*(ip_samplePointsCoords->getBuffer() + 3 * i + 2) //coordinate where to interpolate
|
||||
);
|
||||
|
||||
#if defined TARGET_OS_Windows
|
||||
#ifndef NDEBUG
|
||||
if (_finite(*sampleValue) == 0) //tests whether a double is infinite or a NaN
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << "Interpolation fails !\n";
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << *(ip_samplePointsCoords->getBuffer() + 3 * i) << "\n";
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << *(ip_samplePointsCoords->getBuffer() + 3 * i + 1) << "\n";
|
||||
getLogManager() << Kernel::LogLevel_ImportantWarning << *(ip_samplePointsCoords->getBuffer() + 3 * i + 2) << "\n";
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (*sampleValue < op_minSamplePointValue) { op_minSamplePointValue = *sampleValue; }
|
||||
if (*sampleValue > op_maxSamplePointValue) { op_maxSamplePointValue = *sampleValue; }
|
||||
}
|
||||
|
||||
if (!ok) { activateOutputTrigger(OVP_Algorithm_SphericalSplineInterpolation_OutputTriggerId_Error, true); }
|
||||
}
|
||||
else if (isInputTriggerActive(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateLaplacian))
|
||||
{
|
||||
const bool ok = true;
|
||||
//ensure we got enough storage space for interpolated values
|
||||
if (op_samplePointsValues->getDimensionSize(0) != ip_samplePointsCoords->getDimensionSize(0))
|
||||
{
|
||||
op_samplePointsValues->setDimensionSize(0, ip_samplePointsCoords->getDimensionSize(0));
|
||||
}
|
||||
|
||||
//compute interpolated values using spline
|
||||
auto* sampleValue = static_cast<double*>(op_samplePointsValues->getBuffer());
|
||||
|
||||
op_minSamplePointValue = +DBL_MAX;
|
||||
op_maxSamplePointValue = -DBL_MAX;
|
||||
|
||||
for (size_t i = 0; i < ip_samplePointsCoords->getDimensionSize(0); i++, sampleValue++)
|
||||
{
|
||||
*sampleValue = SplineInterp(int(ip_nControlPoints), //number of fixed values
|
||||
m_coordsPtr.data(), //coordinates of fixed values
|
||||
m_scd.data(), //sin/cos table for laplacian
|
||||
m_laplacianCoefs.data(), //laplacian coefficients
|
||||
*(ip_samplePointsCoords->getBuffer() + 3 * i),
|
||||
*(ip_samplePointsCoords->getBuffer() + 3 * i + 1),
|
||||
*(ip_samplePointsCoords->getBuffer() + 3 * i + 2)); //coordinate where to interpolate
|
||||
|
||||
/***************************************************************************/
|
||||
/*** Units : potential values being very often expressed as micro-Volts ***/
|
||||
/*** SCD values should be multiplied by a scaling factor ***/
|
||||
/*** to get nano-Amperes/m3 ***/
|
||||
/*** Since the output of SplineInterp corresponds to the ***/
|
||||
/*** Laplacian operator only, SCD are obtained with a scaling ***/
|
||||
/*** factor equal to 10-3 * sigma / (R*R) ***/
|
||||
/*** with sigma = conductivity of the scalp = 0.45 Siemens/m ***/
|
||||
/*** and R = radius of the spherical head = 0.09 m ***/
|
||||
/***************************************************************************/
|
||||
*sampleValue = *sampleValue * (0.001 * 0.45 / 0.09 / 0.09);
|
||||
|
||||
if (*sampleValue < op_minSamplePointValue) { op_minSamplePointValue = *sampleValue; }
|
||||
if (*sampleValue > op_maxSamplePointValue) { op_maxSamplePointValue = *sampleValue; }
|
||||
}
|
||||
|
||||
if (!ok) { activateOutputTrigger(OVP_Algorithm_SphericalSplineInterpolation_OutputTriggerId_Error, true); }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Test
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <array>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace Test {
|
||||
class CAlgorithmSphericalSplineInterpolation final : public Toolkit::TAlgorithm<IAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TAlgorithm<IAlgorithm>, OVP_ClassId_Algorithm_SphericalSplineInterpolation)
|
||||
|
||||
protected:
|
||||
|
||||
//input parameters
|
||||
//----------------
|
||||
Kernel::TParameterHandler<int64_t> ip_splineOrder;
|
||||
Kernel::TParameterHandler<int64_t> ip_nControlPoints;
|
||||
Kernel::TParameterHandler<CMatrix*> ip_controlPointsCoords;
|
||||
Kernel::TParameterHandler<CMatrix*> ip_controlPointsValues;
|
||||
Kernel::TParameterHandler<CMatrix*> ip_samplePointsCoords;
|
||||
|
||||
//output parameters
|
||||
//-----------------
|
||||
Kernel::TParameterHandler<CMatrix*> op_samplePointsValues;
|
||||
Kernel::TParameterHandler<double> op_minSamplePointValue;
|
||||
Kernel::TParameterHandler<double> op_maxSamplePointValue;
|
||||
|
||||
//internal data
|
||||
//-------------
|
||||
bool m_firstProcess = true;
|
||||
std::vector<double> m_coords;
|
||||
std::vector<double*> m_coordsPtr;
|
||||
std::vector<double> m_splineCoefs;
|
||||
std::vector<double> m_laplacianCoefs;
|
||||
std::array<double, 2004> m_scd{};
|
||||
std::array<double, 2004> m_pot{};
|
||||
};
|
||||
|
||||
class CAlgorithmSphericalSplineInterpolationDesc final : public IAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Spherical spline interpolation"); }
|
||||
CString getAuthorName() const override { return CString("Vincent Delannoy"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA/IRISA"); }
|
||||
CString getShortDescription() const override { return CString("Interpolates potentials/laplacians using a spherical spline"); }
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Algorithm/Signal processing"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getSoftwareComponent() const override { return CString("openvibe-designer"); }
|
||||
CString getAddedSoftwareVersion() const override { return CString("0.0.0"); }
|
||||
CString getUpdatedSoftwareVersion() const override { return CString("0.0.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_Algorithm_SphericalSplineInterpolation; }
|
||||
IPluginObject* create() override { return new CAlgorithmSphericalSplineInterpolation(); }
|
||||
|
||||
bool getAlgorithmPrototype(Kernel::IAlgorithmProto& prototype) const override
|
||||
{
|
||||
//input parameters
|
||||
prototype.addInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SplineOrder, "Spline order", Kernel::ParameterType_Integer);
|
||||
prototype.addInputParameter(
|
||||
OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCount, "Number of values", Kernel::ParameterType_Integer);
|
||||
prototype.addInputParameter(
|
||||
OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCoordinates, "Values coordinates", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsValues, "Values", Kernel::ParameterType_Matrix);
|
||||
prototype.addInputParameter(
|
||||
OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SamplePointsCoordinates, "Coordinates where to interpolate values",
|
||||
Kernel::ParameterType_Matrix);
|
||||
//input triggers
|
||||
prototype.addInputTrigger(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_PrecomputeTables, CString("Precomputation"));
|
||||
prototype.addInputTrigger(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_ComputeSplineCoefs, CString("Spline coefficients computation"));
|
||||
prototype.addInputTrigger(
|
||||
OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_ComputeLaplacianCoefs, CString("Laplacian coefficients computation"));
|
||||
prototype.addInputTrigger(
|
||||
OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateSpline, CString("Interpolation using spline coefficients"));
|
||||
prototype.addInputTrigger(
|
||||
OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateLaplacian, CString("Interpolation using laplacian coefficients"));
|
||||
//output parameters
|
||||
prototype.addOutputParameter(
|
||||
OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_SamplePointsValues, "Interpolated values", Kernel::ParameterType_Matrix);
|
||||
prototype.addOutputParameter(
|
||||
OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_MinSamplePointValue, "Min interpolated value", Kernel::ParameterType_Float);
|
||||
prototype.addOutputParameter(
|
||||
OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_MaxSamplePointValue, "Max interpolated value", Kernel::ParameterType_Float);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IAlgorithmDesc, OVP_ClassId_Algorithm_SphericalSplineInterpolationDesc)
|
||||
};
|
||||
} // namespace Test
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,317 @@
|
||||
/*****************************************************************************/
|
||||
/*****************************************************************************/
|
||||
/** **/
|
||||
/** spline_sph : **/
|
||||
/** **/
|
||||
/** SplineTables **/
|
||||
/** SplineCoef **/
|
||||
/** SplineInterp **/
|
||||
/** **/
|
||||
/** note : SplineCoef calls linpack routines (linpack.c) **/
|
||||
/** **/
|
||||
/*****************************************************************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
|
||||
/*& Libraries to include &*/
|
||||
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "linpack.h"
|
||||
#include <iostream>
|
||||
|
||||
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
|
||||
/*& Define &*/
|
||||
/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
|
||||
|
||||
#define imin(x,y) (((x) < (y)) ? (x) : (y))
|
||||
|
||||
/**********************************************************************************/
|
||||
/* SplineTables */
|
||||
/* */
|
||||
/* Computes the tabulated functions km(cos(gamma)) and hm(cos(gamma)) */
|
||||
/* for cos(gamma) varying from -1 to 1 (see Patent, columns 2 and 6) */
|
||||
/* km and hm consist in series of Legendre polynomials */
|
||||
/* km and hm are used for potential and SCD interpolation respectively */
|
||||
/* m is the order of the spline interpolation function */
|
||||
/* */
|
||||
/* IN */
|
||||
/* order : spline order m (integer) */
|
||||
/* */
|
||||
/* OUT */
|
||||
/* pot_table : vector of tabulated km (array of 2004 double) */
|
||||
/* scd_table : vector of tabulated hm (array of 2004 double) */
|
||||
/* function value : return code (integer) */
|
||||
/* = 0 normal value */
|
||||
/* = -1 error */
|
||||
/* */
|
||||
/* Note : this function should be called once at the beginning of the program */
|
||||
/* */
|
||||
/**********************************************************************************/
|
||||
|
||||
int SplineTables(const int order, double* pot, double* scd)
|
||||
{
|
||||
if (order <= 2)
|
||||
{
|
||||
std::cout << "spline_table error : spline order <= 2\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
double cnpn, fn;
|
||||
int j, n;
|
||||
|
||||
/*===========================================================*/
|
||||
/* Estimate the number of terms for the Legendre series */
|
||||
/* to have an error lower than 1e-10 */
|
||||
/*===========================================================*/
|
||||
double dexp = 10. / float(2 * order - 2);
|
||||
const int kv = imin(400, int(pow(10.0, dexp) - 1.0));
|
||||
double fsv = 1.0;
|
||||
if (int(fmod(double(kv), 2.0)) == 1) { fsv = -1.0; }
|
||||
|
||||
dexp = 10. / float(2 * order - 4);
|
||||
const int kc = imin(400, int(pow(10.0, dexp) - 1.0));
|
||||
double fsc = 1.0;
|
||||
if (int(fmod(double(kc), 2.0)) == 1) { fsc = -1.0; }
|
||||
|
||||
double* c = static_cast<double*>(malloc(sizeof(double) * kc));
|
||||
double* p = static_cast<double*>(malloc(sizeof(double) * kc));
|
||||
|
||||
/*=========================*/
|
||||
/* Coefficient computation */
|
||||
/*=========================*/
|
||||
double cn = 1.0;
|
||||
for (j = 1; j < order; ++j) { cn /= 2.0; }
|
||||
c[0] = cn * 3.0;
|
||||
for (n = 2; n <= kc; ++n)
|
||||
{
|
||||
fn = double(n);
|
||||
const double cx = (fn - 1.0) / (fn + 1.0);
|
||||
for (j = 1; j < order; ++j) { cn *= cx; }
|
||||
c[n - 1] = (2.0 * fn + 1.0) * cn;
|
||||
}
|
||||
|
||||
/*========================*/
|
||||
/* Table generation */
|
||||
/*========================*/
|
||||
for (int ig = 0; ig <= 1000; ++ig)
|
||||
{
|
||||
/*-------------------------*/
|
||||
/* Pn polynomial */
|
||||
/*-------------------------*/
|
||||
double gamma = double(ig) / 1000.0;
|
||||
gamma = 1.0 - gamma;
|
||||
double p0 = 1.0;
|
||||
double p1 = gamma;
|
||||
p[0] = p1;
|
||||
for (n = 2; n <= kc; ++n)
|
||||
{
|
||||
fn = double(n);
|
||||
const double usfn = 1.0 / fn;
|
||||
const double pn = (2.0 - usfn) * gamma * p1 - (1.0 - usfn) * p0;
|
||||
p0 = p1;
|
||||
p1 = pn;
|
||||
p[n - 1] = pn;
|
||||
}
|
||||
|
||||
/*-----------------------*/
|
||||
/* pot_table computation */
|
||||
/*-----------------------*/
|
||||
double s1 = 0.0;
|
||||
double s2 = 0.0;
|
||||
double fs = fsv;
|
||||
for (n = kv; n >= 1; n--)
|
||||
{
|
||||
fn = double(n);
|
||||
cnpn = c[n - 1] * p[n - 1] / (fn * (fn + 1.0));
|
||||
s1 += cnpn;
|
||||
s2 += fs * cnpn;
|
||||
fs = -fs;
|
||||
}
|
||||
*(pot + 2001 - ig) = s1 * 1000.0;
|
||||
*(pot + 1 + ig) = s2 * 1000.0;
|
||||
|
||||
/*-----------------------*/
|
||||
/* scd_table computation */
|
||||
/*-----------------------*/
|
||||
s1 = 0.0;
|
||||
s2 = 0.0;
|
||||
fs = fsc;
|
||||
for (n = kc; n >= 1; n--)
|
||||
{
|
||||
cnpn = c[n - 1] * p[n - 1];
|
||||
s1 += cnpn;
|
||||
s2 += fs * cnpn;
|
||||
fs = -fs;
|
||||
}
|
||||
*(scd + 2001 - ig) = s1 * 1000.0;
|
||||
*(scd + 1 + ig) = s2 * 1000.0;
|
||||
}
|
||||
|
||||
*(pot + 2002) = *(pot + 2001);
|
||||
*(scd + 2002) = *(scd + 2001);
|
||||
*(pot + 2003) = *(pot + 2002);
|
||||
*(scd + 2003) = *(scd + 2002);
|
||||
*pot = *(pot + 1);
|
||||
*scd = *(scd + 1);
|
||||
|
||||
free(c);
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************************/
|
||||
/* SplineCoef */
|
||||
/* */
|
||||
/* Computes the interpolation coefficients P=(p1,p2,...,pn) and q */
|
||||
/* (see Patent, columns 2 and 6) */
|
||||
/* */
|
||||
/* IN */
|
||||
/* nb_value : number of electrodes and related potential values (integer) */
|
||||
/* xyz : array[nb_value] of array[3] of double */
|
||||
/* X, Y, Z electrode coordinates on a spherical surface */
|
||||
/* values : array[nb_value] of double */
|
||||
/* potential values at the electrode locations */
|
||||
/* table : array[2004] of double */
|
||||
/* tabulated function km (array pot_table computed by SplineTables */
|
||||
/* */
|
||||
/* OUT */
|
||||
/* coef : array[nb_value + 1] of double */
|
||||
/* spline coefficients P=(p1,p2,...,pn) and q */
|
||||
/* function value : return code (integer) */
|
||||
/* = 0 normal value */
|
||||
/* = -1 error */
|
||||
/* */
|
||||
/* Note : this function should be called once for a given set of electrodes */
|
||||
/* and a set of potential values */
|
||||
/* */
|
||||
/**********************************************************************************/
|
||||
|
||||
int SplineCoef(const int n, double** xyz, const double* values, const double* table, double* coef)
|
||||
{
|
||||
int i, info, itmp;
|
||||
|
||||
/*=========================================*/
|
||||
/* allocation of temporary arrays */
|
||||
/*=========================================*/
|
||||
double* p_mat_a = static_cast<double*>(malloc(sizeof(double) * ((n + 1) * (n + 2)) / 2));
|
||||
if (p_mat_a == nullptr)
|
||||
{
|
||||
std::cout << "SplineCoef error : allocation p_mat_a\n";
|
||||
return (-1);
|
||||
}
|
||||
int* p_iwork = static_cast<int*>(malloc(sizeof(int) * (n + 1)));
|
||||
if (p_iwork == nullptr)
|
||||
{
|
||||
std::cout << "SplineCoef error : allocation p_iwork\n";
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*================================*/
|
||||
/* Initialization of matrix A */
|
||||
/*================================*/
|
||||
const int l0 = ((n + 1) * (n)) / 2;
|
||||
for (i = l0; i < l0 + n; ++i) { *(p_mat_a + i) = 1.0; }
|
||||
*(p_mat_a + i) = 0.0;
|
||||
|
||||
/*=========================*/
|
||||
/* computation of matrix A */
|
||||
/*=========================*/
|
||||
int ih = 0;
|
||||
for (int j = 0; j < n; ++j)
|
||||
{
|
||||
const double xj = xyz[j][0];
|
||||
const double yj = xyz[j][1];
|
||||
const double zj = xyz[j][2];
|
||||
for (i = 0; i < j; ++i)
|
||||
{
|
||||
const double t1 = xyz[i][0] - xj;
|
||||
const double t2 = xyz[i][1] - yj;
|
||||
const double t3 = xyz[i][2] - zj;
|
||||
const double tp = (t1 * t1 + t2 * t2 + t3 * t3) / 2.0;
|
||||
double fgam = (1.0 - tp) * 1000.0 + 1002.0;
|
||||
const int igam = int(fgam);
|
||||
fgam -= float(igam);
|
||||
const double v1 = *(table + igam - 1);
|
||||
const double v2 = *(table + igam) - v1;
|
||||
*(p_mat_a + ih++) = v2 * fgam + v1;
|
||||
}
|
||||
*(p_mat_a + ih++) = *(table + 2001);
|
||||
}
|
||||
|
||||
/*=================================*/
|
||||
/* Triangularization of matrix A */
|
||||
/*=================================*/
|
||||
itmp = n + 1;
|
||||
sspfa(p_mat_a, &itmp, p_iwork, &info);
|
||||
if (info != 0)
|
||||
{
|
||||
printf("SplineCoef error : triangularization of matrix a (sspfa : %d) \n", info);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*=======================================================*/
|
||||
/* Coefficient computation (solving a triangular system) */
|
||||
/*=======================================================*/
|
||||
for (i = 0; i < n; ++i) { coef[i] = values[i]; }
|
||||
coef[n] = 0.0;
|
||||
sspsl(p_mat_a, &itmp, p_iwork, coef);
|
||||
free(p_mat_a);
|
||||
free(p_iwork);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************************/
|
||||
/* SplineInterp */
|
||||
/* */
|
||||
/* Computes the interpolated potential or SCD value at a location on the sphere */
|
||||
/* (see Patent, columns 2, 4, 7 and 9) */
|
||||
/* */
|
||||
/* IN */
|
||||
/* nb_value : number of electrodes and related potential values (integer) */
|
||||
/* xyz : array[nb_value] of array[3] of double */
|
||||
/* X, Y, Z electrode coordinates on a spherical surface (radius = 1) */
|
||||
/* table : array[2004] of double */
|
||||
/* tabulated function computed by SplineTables */
|
||||
/* use pot_table (km) for potential interpolation */
|
||||
/* use scd_table (hm) for SCD interpolation */
|
||||
/* coef : array[nb_value + 1] of double */
|
||||
/* spline coefficients P=(p1,p2,...,pn) and q */
|
||||
/* array coef computed by SplineCoef */
|
||||
/* IMPORTANT : coef[nb_value] should be set to 0.0 for computing */
|
||||
/* the interpolated SCD (this corresponds to q=0) */
|
||||
/* xx, yy, zz : double */
|
||||
/* X, Y, Z coordinates of a point on a spherical surface (radius = 1)*/
|
||||
/* where to compute the interpolated value */
|
||||
/* */
|
||||
/* OUT */
|
||||
/* function value : interpolated potential or SCD value (double) */
|
||||
/* */
|
||||
/* Note : this function should be called for every point of the spherical surface */
|
||||
/* where the potential or the SCD value should be estimated */
|
||||
/* */
|
||||
/**********************************************************************************/
|
||||
|
||||
double SplineInterp(const int n, double** xyz, const double* table, const double* coef, const double xx, const double yy, const double zz)
|
||||
{
|
||||
double ffn = coef[n];
|
||||
int k = 0;
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
const double t1 = xx - xyz[i][0];
|
||||
const double t2 = yy - xyz[i][1];
|
||||
const double t3 = zz - xyz[i][2];
|
||||
const double t123 = (t1 * t1 + t2 * t2 + t3 * t3) / 2.;
|
||||
double fgam = (1.0 - t123) * 1000. + 1002.;
|
||||
const int igam = int(fgam);
|
||||
fgam -= double(igam);
|
||||
const double v1 = table[igam - 1];
|
||||
const double v2 = table[igam] - v1;
|
||||
ffn += coef[k] * (v2 * fgam + v1);
|
||||
k++;
|
||||
}
|
||||
return ffn;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*************************************************************************************/
|
||||
/* spline_sph.h */
|
||||
/*************************************************************************************/
|
||||
|
||||
/*************************************************************************************/
|
||||
/* spline subroutines */
|
||||
/*************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
int SplineTables(int order, double* pot, double* scd);
|
||||
int SplineCoef(int n, double** xyz, const double* values, const double* table, double* coef);
|
||||
double SplineInterp(int n, double** xyz, const double* table, const double* coef, double xx, double yy, double zz);
|
||||
@@ -0,0 +1,364 @@
|
||||
#include "ovpCBoxAlgorithmMatrixDisplay.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <visualization-toolkit/ovvizColorGradient.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SimpleVisualization {
|
||||
|
||||
static void ShowValuesToggleButtonCB(GtkToggleToolButton* button, gpointer data)
|
||||
{
|
||||
auto* display = reinterpret_cast<CBoxAlgorithmMatrixDisplay*>(data);
|
||||
display->m_ShowValues = (gtk_toggle_tool_button_get_active(button) != 0);
|
||||
}
|
||||
|
||||
static void ShowColorsToggleButtonCB(GtkToggleToolButton* button, gpointer data)
|
||||
{
|
||||
auto* display = reinterpret_cast<CBoxAlgorithmMatrixDisplay*>(data);
|
||||
display->m_ShowColors = (gtk_toggle_tool_button_get_active(button) != 0);
|
||||
display->resetColors();
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixDisplay::resetColors()
|
||||
|
||||
{
|
||||
if (m_ShowColors)
|
||||
{
|
||||
//we take colors from cache and re-put it in the table
|
||||
for (auto it = m_eventBoxCache.begin(); it != m_eventBoxCache.end(); ++it) { gtk_widget_modify_bg((*it).first, GTK_STATE_NORMAL, &(*it).second); }
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto it = m_eventBoxCache.begin(); it != m_eventBoxCache.end(); ++it)
|
||||
{
|
||||
GdkColor white;
|
||||
white.red = 65535;
|
||||
white.green = 65535;
|
||||
white.blue = 65535;
|
||||
gtk_widget_modify_bg((*it).first, GTK_STATE_NORMAL, &white);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixDisplay::initialize()
|
||||
|
||||
{
|
||||
//targets decoder
|
||||
iMatrix = &this->getAlgorithmManager().getAlgorithm(this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_StreamedMatrixDecoder));
|
||||
iMatrix->initialize();
|
||||
|
||||
//IO for the targets MemoryBuffer -> StreamedMatrix
|
||||
ip_buffer.initialize(iMatrix->getInputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_InputParameterId_MemoryBufferToDecode));
|
||||
op_matrix.initialize(iMatrix->getOutputParameter(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputParameterId_Matrix));
|
||||
|
||||
//widgets
|
||||
m_mainWidgetInterface = gtk_builder_new();
|
||||
m_toolbarWidgetInterface = gtk_builder_new();
|
||||
// glade_xml_new(Directories::getDataDir() + "/plugins/simple-visualization/openvibe-simple-visualization-MatrixDisplay.ui", "matrix-display-table", nullptr);
|
||||
// glade_xml_new(Directories::getDataDir() + "/plugins/simple-visualization/openvibe-simple-visualization-MatrixDisplay.ui", "matrix-display-toolbar", nullptr);
|
||||
gtk_builder_add_from_file(m_mainWidgetInterface, Directories::getDataDir() + "/plugins/simple-visualization/openvibe-simple-visualization-MatrixDisplay.ui",
|
||||
nullptr);
|
||||
gtk_builder_add_from_file(m_toolbarWidgetInterface,
|
||||
Directories::getDataDir() + "/plugins/simple-visualization/openvibe-simple-visualization-MatrixDisplay.ui", nullptr);
|
||||
|
||||
gtk_builder_connect_signals(m_mainWidgetInterface, nullptr);
|
||||
gtk_builder_connect_signals(m_toolbarWidgetInterface, nullptr);
|
||||
|
||||
g_signal_connect(G_OBJECT(gtk_builder_get_object(m_toolbarWidgetInterface, "show-values-toggle-button")), "toggled",
|
||||
G_CALLBACK(ShowValuesToggleButtonCB), this);
|
||||
g_signal_connect(G_OBJECT(gtk_builder_get_object(m_toolbarWidgetInterface, "show-colors-toggle-button")), "toggled",
|
||||
G_CALLBACK(ShowColorsToggleButtonCB), this);
|
||||
g_signal_connect(G_OBJECT(gtk_builder_get_object(m_toolbarWidgetInterface, "matrix-display-toolbar")), "delete_event", G_CALLBACK(gtk_widget_hide),
|
||||
nullptr);
|
||||
|
||||
m_mainWidget = GTK_WIDGET(gtk_builder_get_object(m_mainWidgetInterface, "matrix-display-table"));
|
||||
m_toolbarWidget = GTK_WIDGET(gtk_builder_get_object(m_toolbarWidgetInterface, "matrix-display-toolbar"));
|
||||
|
||||
if (!this->canCreatePluginObject(OVP_ClassId_Plugin_VisualizationCtx))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Visualization framework is not loaded" << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_visualizationCtx = dynamic_cast<VisualizationToolkit::IVisualizationContext*>(this->createPluginObject(OVP_ClassId_Plugin_VisualizationCtx));
|
||||
m_visualizationCtx->setWidget(*this, m_mainWidget);
|
||||
m_visualizationCtx->setToolbar(*this, m_toolbarWidget);
|
||||
|
||||
m_ShowValues = (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(m_toolbarWidgetInterface, "show-values-toggle-button"))) !=
|
||||
0);
|
||||
m_ShowColors = (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(m_toolbarWidgetInterface, "show-colors-toggle-button"))) !=
|
||||
0);
|
||||
|
||||
CString gradientSetting;
|
||||
getBoxAlgorithmContext()->getStaticBoxContext()->getSettingValue(0, gradientSetting);
|
||||
VisualizationToolkit::ColorGradient::parse(m_colorGradient, gradientSetting);
|
||||
|
||||
CString gradientStepsSetting;
|
||||
getBoxAlgorithmContext()->getStaticBoxContext()->getSettingValue(1, gradientStepsSetting);
|
||||
m_gradientSteps = strtol(gradientStepsSetting, nullptr, 10);
|
||||
VisualizationToolkit::ColorGradient::interpolate(m_interpolatedColorGardient, m_colorGradient, m_gradientSteps);
|
||||
m_max = 0;
|
||||
m_min = 0;
|
||||
|
||||
CString symetricMinMaxSetting;
|
||||
getBoxAlgorithmContext()->getStaticBoxContext()->getSettingValue(2, symetricMinMaxSetting);
|
||||
m_symetricMinMax = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
|
||||
|
||||
CString realTimeMinMaxSetting;
|
||||
getBoxAlgorithmContext()->getStaticBoxContext()->getSettingValue(3, realTimeMinMaxSetting);
|
||||
m_realTimeMinMax = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixDisplay::uninitialize()
|
||||
|
||||
{
|
||||
op_matrix.uninitialize();
|
||||
ip_buffer.uninitialize();
|
||||
|
||||
//decoders
|
||||
iMatrix->uninitialize();
|
||||
this->getAlgorithmManager().releaseAlgorithm(*iMatrix);
|
||||
|
||||
//widgets
|
||||
g_object_unref(m_toolbarWidgetInterface);
|
||||
m_toolbarWidgetInterface = nullptr;
|
||||
|
||||
g_object_unref(m_mainWidgetInterface);
|
||||
m_mainWidgetInterface = nullptr;
|
||||
|
||||
this->releasePluginObject(m_visualizationCtx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixDisplay::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmMatrixDisplay::process()
|
||||
|
||||
{
|
||||
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
|
||||
|
||||
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
|
||||
{
|
||||
ip_buffer = boxContext.getInputChunk(0, i);
|
||||
iMatrix->process();
|
||||
|
||||
if (iMatrix->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedHeader))
|
||||
{
|
||||
//header received
|
||||
//adding the event to the window
|
||||
GtkTable* table = GTK_TABLE(gtk_builder_get_object(m_mainWidgetInterface, "matrix-display-table"));
|
||||
guint nRow, nCol;
|
||||
if (op_matrix->getDimensionCount() == 1)
|
||||
{
|
||||
//getLogManager() << Kernel::LogLevel_Warning<< "The streamed matrix received has 1 dimensions (found "<< op_matrix->getDimensionCount() <<" dimensions)\n";
|
||||
nRow = 1;
|
||||
nCol = guint(op_matrix->getDimensionSize(0));
|
||||
//return false;
|
||||
}
|
||||
else if (op_matrix->getDimensionCount() != 2)
|
||||
{
|
||||
getLogManager() << Kernel::LogLevel_Error << "The streamed matrix received has more than 2 dimensions (found " << op_matrix->getDimensionCount()
|
||||
<<
|
||||
" dimensions)\n";
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
nRow = guint(op_matrix->getDimensionSize(0));
|
||||
nCol = guint(op_matrix->getDimensionSize(1));
|
||||
}
|
||||
|
||||
gtk_table_resize(table, nRow + 1, nCol + 1);
|
||||
|
||||
//first line : labels
|
||||
guint row = 0;
|
||||
for (guint c = 1; c < nCol + 1; ++c)
|
||||
{
|
||||
GtkWidget* label = gtk_label_new("");
|
||||
gtk_widget_set_visible(label, 1);
|
||||
gtk_table_attach(table, label, c, c + 1, row, row + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(GTK_EXPAND | GTK_FILL), 0, 0);
|
||||
//g_object_unref(l_pGtkBuilderLabel);
|
||||
|
||||
const char* cstr = std::to_string(c).c_str();
|
||||
gtk_label_set_label(GTK_LABEL(label), cstr);
|
||||
m_columnLabelCache.emplace_back(GTK_LABEL(label), cstr);
|
||||
}
|
||||
|
||||
//first column : labels
|
||||
guint col = 0;
|
||||
for (guint r = 1; r < nRow + 1; ++r)
|
||||
{
|
||||
GtkWidget* label = gtk_label_new("");
|
||||
gtk_widget_set_visible(label, 1);
|
||||
gtk_table_attach(table, label, col, col + 1, r, r + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(GTK_EXPAND | GTK_FILL), 0, 0);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << char(r - 1 + int('A'));
|
||||
gtk_label_set_label(GTK_LABEL(label), ss.str().c_str());
|
||||
m_rowLabelCache.emplace_back(GTK_LABEL(label), ss.str().c_str());
|
||||
}
|
||||
|
||||
for (guint r = 1; r < nRow + 1; ++r)
|
||||
{
|
||||
for (guint c = 1; c < nCol + 1; ++c)
|
||||
{
|
||||
GtkWidget* eventBox = gtk_event_box_new();
|
||||
gtk_widget_set_visible(eventBox, 1);
|
||||
GtkWidget* label = gtk_label_new("");
|
||||
gtk_widget_set_visible(label, 1);
|
||||
gtk_container_add(GTK_CONTAINER(eventBox), label);
|
||||
gtk_table_attach(table, eventBox, c, c + 1, r, r + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GtkAttachOptions(GTK_EXPAND | GTK_FILL), 0,
|
||||
0);
|
||||
|
||||
GdkColor white;
|
||||
white.red = 65535;
|
||||
white.green = 65535;
|
||||
white.blue = 65535;
|
||||
gtk_widget_modify_bg(eventBox, GTK_STATE_NORMAL, &white);
|
||||
m_eventBoxCache.emplace_back(eventBox, white);
|
||||
|
||||
gtk_label_set_label(GTK_LABEL(label), "X");
|
||||
m_labelCache.emplace_back(GTK_LABEL(label), "X");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iMatrix->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedBuffer))
|
||||
{
|
||||
//buffer received
|
||||
//2-dimension-matrix values
|
||||
size_t nRow, nCol;
|
||||
if (op_matrix->getDimensionCount() == 1)
|
||||
{
|
||||
nRow = 1;
|
||||
nCol = op_matrix->getDimensionSize(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
nRow = op_matrix->getDimensionSize(0);
|
||||
nCol = op_matrix->getDimensionSize(1);
|
||||
}
|
||||
|
||||
if (m_realTimeMinMax || // we need recompute the min max at each loop call
|
||||
(m_max == 0 && m_min == 0)) // we have never computed the min max values.
|
||||
{
|
||||
if (op_matrix->getBufferElementCount() != 0) // if the matrix is not empty.
|
||||
{
|
||||
m_max = op_matrix->getBuffer()[0];
|
||||
m_min = op_matrix->getBuffer()[0];
|
||||
}
|
||||
}
|
||||
|
||||
// MIN-MAX computation
|
||||
for (size_t r = 0; r < nRow; ++r)
|
||||
{
|
||||
for (size_t c = 0; c < nCol; ++c)
|
||||
{
|
||||
double value = op_matrix->getBuffer()[r * nCol + c];
|
||||
m_max = (value > m_max ? value : m_max);
|
||||
m_min = (value < m_min ? value : m_min);
|
||||
|
||||
if (m_symetricMinMax)
|
||||
{
|
||||
double maxAbsValue = (fabs(m_max) > fabs(m_min) ? fabs(m_max) : fabs(m_min));
|
||||
m_max = maxAbsValue;
|
||||
m_min = -maxAbsValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t r = 0; r < nRow; ++r)
|
||||
{
|
||||
for (size_t c = 0; c < nCol; ++c)
|
||||
{
|
||||
double value = op_matrix->getBuffer()[r * nCol + c];
|
||||
if (m_max != 0 || m_min != 0) // if the first value ever sent is 0, both are 0, and we dont want to divide by 0 :)
|
||||
{
|
||||
const size_t step = size_t(((value - m_min) / (m_max - m_min)) * (m_gradientSteps - 1));
|
||||
|
||||
// gtk_widget_modify_bg uses 16bit colors, the interpolated gradients gives 8bits colors.
|
||||
GdkColor color;
|
||||
color.red = uint16_t(m_interpolatedColorGardient[step * 4 + 1] * 65535. / 100.);
|
||||
color.green = uint16_t(m_interpolatedColorGardient[step * 4 + 2] * 65535. / 100.);
|
||||
color.blue = uint16_t(m_interpolatedColorGardient[step * 4 + 3] * 65535. / 100.);
|
||||
|
||||
if (memcmp(&(m_eventBoxCache[r * nCol + c].second), &color, sizeof(GdkColor)) != 0 && m_ShowColors)
|
||||
{
|
||||
gtk_widget_modify_bg(m_eventBoxCache[r * nCol + c].first, GTK_STATE_NORMAL, &color);
|
||||
}
|
||||
m_eventBoxCache[r * nCol + c].second = color;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::fixed;
|
||||
ss << std::setprecision(2);
|
||||
if (m_ShowValues) { ss << value; }
|
||||
|
||||
if (ss.str() != m_labelCache[r * nCol + c].second) { gtk_label_set_label(m_labelCache[r * nCol + c].first, ss.str().c_str()); }
|
||||
m_labelCache[r * nCol + c].second = ss.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op_matrix->getDimensionCount() != 1)
|
||||
{
|
||||
//first line : labels
|
||||
for (size_t c = 0; c < nCol; ++c)
|
||||
{
|
||||
if (m_columnLabelCache[c].second != op_matrix->getDimensionLabel(1, c) && !std::string(op_matrix->getDimensionLabel(1, c)).empty())
|
||||
{
|
||||
gtk_label_set_label(GTK_LABEL(m_columnLabelCache[c].first), op_matrix->getDimensionLabel(1, c));
|
||||
m_columnLabelCache[c].second = op_matrix->getDimensionLabel(1, c);
|
||||
}
|
||||
}
|
||||
|
||||
//first column : labels
|
||||
for (size_t r = 0; r < nRow; ++r)
|
||||
{
|
||||
if (m_rowLabelCache[r].second != op_matrix->getDimensionLabel(0, r) && !std::string(op_matrix->getDimensionLabel(0, r)).empty())
|
||||
{
|
||||
gtk_label_set_label(GTK_LABEL(m_rowLabelCache[r].first), op_matrix->getDimensionLabel(0, r));
|
||||
m_rowLabelCache[r].second = op_matrix->getDimensionLabel(0, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//first line : labels
|
||||
for (size_t c = 0; c < nCol; ++c)
|
||||
{
|
||||
if (m_columnLabelCache[c].second != op_matrix->getDimensionLabel(0, c) && !std::string(op_matrix->getDimensionLabel(0, c)).empty())
|
||||
{
|
||||
gtk_label_set_label(GTK_LABEL(m_columnLabelCache[c].first), op_matrix->getDimensionLabel(0, c));
|
||||
m_columnLabelCache[c].second = op_matrix->getDimensionLabel(0, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*if(iMatrix->isOutputTriggerActive(OVP_GD_Algorithm_StreamedMatrixDecoder_OutputTriggerId_ReceivedEnd)) { }*/
|
||||
|
||||
boxContext.markInputAsDeprecated(0, i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SimpleVisualization
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <visualization-toolkit/ovviz_all.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SimpleVisualization {
|
||||
class CBoxAlgorithmMatrixDisplay final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_BoxAlgorithm_MatrixDisplay)
|
||||
|
||||
protected:
|
||||
|
||||
// we need an algorithm to decode the EBML stream (memory buffer) into a Streamed Matrix
|
||||
|
||||
// for the TARGET
|
||||
Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmMatrixDisplay> m_decoder;
|
||||
Kernel::IAlgorithmProxy* iMatrix = nullptr;
|
||||
Kernel::TParameterHandler<const IMemoryBuffer*> ip_buffer;
|
||||
Kernel::TParameterHandler<CMatrix*> op_matrix;
|
||||
|
||||
// Outputs: visualization in a gtk window
|
||||
GtkBuilder* m_mainWidgetInterface = nullptr;
|
||||
GtkBuilder* m_toolbarWidgetInterface = nullptr;
|
||||
GtkWidget* m_mainWidget = nullptr;
|
||||
GtkWidget* m_toolbarWidget = nullptr;
|
||||
|
||||
std::vector<std::pair<GtkWidget*, GdkColor>> m_eventBoxCache;
|
||||
std::vector<std::pair<GtkLabel*, std::string>> m_labelCache;
|
||||
std::vector<std::pair<GtkLabel*, std::string>> m_rowLabelCache;
|
||||
std::vector<std::pair<GtkLabel*, std::string>> m_columnLabelCache;
|
||||
|
||||
CMatrix m_interpolatedColorGardient;
|
||||
CMatrix m_colorGradient;
|
||||
size_t m_gradientSteps = 0;
|
||||
double m_max = 0;
|
||||
double m_min = 0;
|
||||
|
||||
bool m_symetricMinMax = false;
|
||||
bool m_realTimeMinMax = false;
|
||||
|
||||
VisualizationToolkit::IVisualizationContext* m_visualizationCtx{};
|
||||
|
||||
public:
|
||||
|
||||
bool m_ShowValues = false;
|
||||
bool m_ShowColors = false;
|
||||
|
||||
bool resetColors();
|
||||
};
|
||||
|
||||
class CBoxAlgorithmMatrixDisplayDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("Matrix Display"); }
|
||||
CString getAuthorName() const override { return CString("Laurent Bonnet"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA/IRISA"); }
|
||||
CString getShortDescription() const override { return CString("Display a streamed matrix"); }
|
||||
|
||||
CString getDetailedDescription() const override
|
||||
{
|
||||
return CString("The streamed matrix can be visualized using a table of values and/or a color gradient.");
|
||||
}
|
||||
|
||||
CString getCategory() const override { return CString("Visualization/Basic"); }
|
||||
CString getVersion() const override { return CString("1.0"); }
|
||||
CString getStockItemName() const override { return CString("gtk-select-color"); }
|
||||
CString getSoftwareComponent() const override { return CString("openvibe-designer"); }
|
||||
CString getAddedSoftwareVersion() const override { return CString("0.0.0"); }
|
||||
CString getUpdatedSoftwareVersion() const override { return CString("0.0.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_BoxAlgorithm_MatrixDisplay; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmMatrixDisplay; }
|
||||
|
||||
bool hasFunctionality(const EPluginFunctionality functionality) const override { return functionality == EPluginFunctionality::Visualization; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addSetting("Color gradient", OV_TypeId_ColorGradient, "0:2,36,58; 50:100,100,100; 100:83,17,20");
|
||||
prototype.addSetting("Steps", OV_TypeId_Integer, "100");
|
||||
prototype.addSetting("Symetric min/max", OV_TypeId_Boolean, "false");
|
||||
prototype.addSetting("Real time min/max", OV_TypeId_Boolean, "false");
|
||||
prototype.addInput("Matrix", OV_TypeId_StreamedMatrix);
|
||||
// prototype.addFlag (Kernel::BoxFlag_IsUnstable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_BoxAlgorithm_MatrixDisplayDesc)
|
||||
};
|
||||
} // namespace SimpleVisualization
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "ovpCBoxAlgorithmTopographicMap2DDisplay.h"
|
||||
#include "../algorithms/ovpCAlgorithmSphericalSplineInterpolation.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SimpleVisualization {
|
||||
|
||||
bool CBoxAlgorithmTopographicMap2DDisplay::initialize()
|
||||
|
||||
{
|
||||
m_hasFirstBuffer = false;
|
||||
m_decoder.initialize(*this, 0);
|
||||
|
||||
m_interpolation = &getAlgorithmManager().getAlgorithm(getAlgorithmManager().createAlgorithm(OVP_ClassId_Algorithm_SphericalSplineInterpolation));
|
||||
m_interpolation->initialize();
|
||||
|
||||
//create topographic map database
|
||||
m_database = new CTopographicMapDatabase(*this, *m_interpolation);
|
||||
|
||||
//retrieve settings
|
||||
CString interpolationValue;
|
||||
getStaticBoxContext().getSettingValue(0, interpolationValue);
|
||||
CString delayValue;
|
||||
getStaticBoxContext().getSettingValue(1, delayValue);
|
||||
|
||||
//create topographic map view (handling GUI interaction)
|
||||
m_view = new CTopographicMap2DView(
|
||||
*m_database, EInterpolationType(getTypeManager().getEnumerationEntryValueFromName(OVP_TypeId_SphericalLinearInterpolationType, interpolationValue)),
|
||||
strtod(delayValue, nullptr));
|
||||
|
||||
//have database notify us when new data is available
|
||||
m_database->setDrawable(m_view);
|
||||
//ask not to be notified when new data is available (refresh is handled separately)
|
||||
m_database->setRedrawOnNewData(false);
|
||||
|
||||
//send widget pointers to visualisation context for parenting
|
||||
GtkWidget* widget = nullptr;
|
||||
GtkWidget* toolbarWidget = nullptr;
|
||||
dynamic_cast<CTopographicMap2DView*>(m_view)->getWidgets(widget, toolbarWidget);
|
||||
|
||||
if (!this->canCreatePluginObject(OVP_ClassId_Plugin_VisualizationCtx))
|
||||
{
|
||||
this->getLogManager() << Kernel::LogLevel_Error << "Visualization framework is not loaded" << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_visualizationCtx = dynamic_cast<VisualizationToolkit::IVisualizationContext*>(this->createPluginObject(OVP_ClassId_Plugin_VisualizationCtx));
|
||||
m_visualizationCtx->setWidget(*this, widget);
|
||||
m_visualizationCtx->setToolbar(*this, toolbarWidget);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmTopographicMap2DDisplay::uninitialize()
|
||||
{
|
||||
m_decoder.uninitialize();
|
||||
|
||||
delete m_view;
|
||||
m_view = nullptr;
|
||||
delete m_database;
|
||||
m_database = nullptr;
|
||||
|
||||
m_interpolation->uninitialize();
|
||||
|
||||
getAlgorithmManager().releaseAlgorithm(*m_interpolation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmTopographicMap2DDisplay::processInput(const size_t /*index*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmTopographicMap2DDisplay::processClock(Kernel::CMessageClock& /*msg*/)
|
||||
{
|
||||
getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBoxAlgorithmTopographicMap2DDisplay::process()
|
||||
|
||||
{
|
||||
IDynamicBoxContext* context = getBoxAlgorithmContext()->getDynamicBoxContext();
|
||||
|
||||
//decode signal data
|
||||
for (size_t i = 0; i < context->getInputChunkCount(0); ++i)
|
||||
{
|
||||
m_decoder.decode(i);
|
||||
if (m_decoder.isBufferReceived())
|
||||
{
|
||||
CMatrix* iMatrix = m_decoder.getOutputMatrix();
|
||||
|
||||
//do we need to recopy this for each chunk?
|
||||
if (!m_hasFirstBuffer)
|
||||
{
|
||||
m_database->setMatrixDimensionCount(iMatrix->getDimensionCount());
|
||||
for (size_t dimension = 0; dimension < iMatrix->getDimensionCount(); ++dimension)
|
||||
{
|
||||
m_database->setMatrixDimensionSize(dimension, iMatrix->getDimensionSize(dimension));
|
||||
for (size_t entryIndex = 0; entryIndex < iMatrix->getDimensionSize(dimension); ++entryIndex)
|
||||
{
|
||||
m_database->setMatrixDimensionLabel(dimension, entryIndex, iMatrix->getDimensionLabel(dimension, entryIndex));
|
||||
}
|
||||
}
|
||||
m_hasFirstBuffer = true;
|
||||
}
|
||||
//
|
||||
|
||||
if (!m_database->setMatrixBuffer(iMatrix->getBuffer(), context->getInputChunkStartTime(0, i), context->getInputChunkEndTime(0, i)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//decode channel localisation data
|
||||
for (size_t i = 0; i < context->getInputChunkCount(1); ++i)
|
||||
{
|
||||
const IMemoryBuffer* buf = context->getInputChunk(1, i);
|
||||
m_database->decodeChannelLocalisationMemoryBuffer(buf, context->getInputChunkStartTime(1, i), context->getInputChunkEndTime(1, i));
|
||||
context->markInputAsDeprecated(1, i);
|
||||
}
|
||||
|
||||
const bool processValues = m_database->processValues();
|
||||
|
||||
//disable plugin upon errors
|
||||
return processValues;
|
||||
}
|
||||
|
||||
} // namespace SimpleVisualization
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
#include <visualization-toolkit/ovviz_all.h>
|
||||
|
||||
#include "topographicMap2DDisplay/ovpCTopographicMapDatabase.h"
|
||||
#include "topographicMap2DDisplay/ovpCTopographicMap2DView.h"
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SimpleVisualization {
|
||||
class CBoxAlgorithmTopographicMap2DDisplay final : public Toolkit::TBoxAlgorithm<IBoxAlgorithm>
|
||||
{
|
||||
public:
|
||||
CBoxAlgorithmTopographicMap2DDisplay() = default;
|
||||
|
||||
void release() override { delete this; }
|
||||
|
||||
uint64_t getClockFrequency() override { return uint64_t(1LL) << 37; }
|
||||
bool initialize() override;
|
||||
bool uninitialize() override;
|
||||
bool processInput(const size_t index) override;
|
||||
bool processClock(Kernel::CMessageClock& msg) override;
|
||||
bool process() override;
|
||||
|
||||
_IsDerivedFromClass_Final_(Toolkit::TBoxAlgorithm<IBoxAlgorithm>, OVP_ClassId_TopographicMap2DDisplay)
|
||||
|
||||
protected:
|
||||
Toolkit::TStreamedMatrixDecoder<CBoxAlgorithmTopographicMap2DDisplay> m_decoder;
|
||||
|
||||
VisualizationToolkit::IVisualizationContext* m_visualizationCtx = nullptr;
|
||||
Kernel::IAlgorithmProxy* m_interpolation = nullptr;
|
||||
CTopographicMapDatabase* m_database = nullptr;
|
||||
CSignalDisplayDrawable* m_view = nullptr; //main object used for the display (contains all the GUI code)
|
||||
bool m_hasFirstBuffer = false;
|
||||
};
|
||||
|
||||
class CBoxAlgorithmTopographicMap2DDisplayDesc final : public IBoxAlgorithmDesc
|
||||
{
|
||||
public:
|
||||
|
||||
void release() override { }
|
||||
|
||||
CString getName() const override { return CString("2D topographic map"); }
|
||||
CString getAuthorName() const override { return CString("Vincent Delannoy"); }
|
||||
CString getAuthorCompanyName() const override { return CString("INRIA/IRISA"); }
|
||||
|
||||
CString getShortDescription() const override { return CString("This box demonstrates how to perform spherical spline interpolation"); }
|
||||
|
||||
CString getDetailedDescription() const override { return CString(""); }
|
||||
CString getCategory() const override { return CString("Visualization/Topography"); }
|
||||
CString getVersion() const override { return CString("2.0"); }
|
||||
CString getStockItemName() const override { return CString(GTK_STOCK_EXECUTE); }
|
||||
CString getSoftwareComponent() const override { return CString("openvibe-designer"); }
|
||||
CString getAddedSoftwareVersion() const override { return CString("0.0.0"); }
|
||||
CString getUpdatedSoftwareVersion() const override { return CString("0.0.0"); }
|
||||
|
||||
CIdentifier getCreatedClass() const override { return OVP_ClassId_TopographicMap2DDisplay; }
|
||||
IPluginObject* create() override { return new CBoxAlgorithmTopographicMap2DDisplay(); }
|
||||
|
||||
bool hasFunctionality(const EPluginFunctionality functionality) const override { return functionality == EPluginFunctionality::Visualization; }
|
||||
|
||||
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
|
||||
{
|
||||
prototype.addSetting("Interpolation type", OVP_TypeId_SphericalLinearInterpolationType, "1");
|
||||
prototype.addSetting("Delay (in s)", OV_TypeId_Float, "0");
|
||||
prototype.addInput("Signal", OV_TypeId_StreamedMatrix);
|
||||
prototype.addInput("Channel localization", OV_TypeId_ChannelLocalisation);
|
||||
return true;
|
||||
}
|
||||
|
||||
_IsDerivedFromClass_Final_(IBoxAlgorithmDesc, OVP_ClassId_TopographicMap2DDisplayDesc)
|
||||
};
|
||||
} // namespace SimpleVisualization
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,682 @@
|
||||
#include "ovpCBufferDatabase.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SimpleVisualization {
|
||||
|
||||
CBufferDatabase::CBufferDatabase(Toolkit::TBoxAlgorithm<IBoxAlgorithm>& plugin) : m_ParentPlugin(plugin)
|
||||
{
|
||||
m_decoder = &m_ParentPlugin.getAlgorithmManager().getAlgorithm(
|
||||
m_ParentPlugin.getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_ChannelLocalisationDecoder));
|
||||
m_decoder->initialize();
|
||||
m_DimSizes.fill(0);
|
||||
}
|
||||
|
||||
CBufferDatabase::~CBufferDatabase()
|
||||
{
|
||||
m_decoder->uninitialize();
|
||||
m_ParentPlugin.getAlgorithmManager().releaseAlgorithm(*m_decoder);
|
||||
|
||||
//delete all the remaining buffers
|
||||
while (!m_SampleBuffers.empty())
|
||||
{
|
||||
delete[] m_SampleBuffers.front();
|
||||
m_SampleBuffers.pop_front();
|
||||
}
|
||||
|
||||
//delete channel localisation matrices
|
||||
while (!m_channelLocalisationCoords.empty())
|
||||
{
|
||||
delete m_channelLocalisationCoords.front().first;
|
||||
m_channelLocalisationCoords.pop_front();
|
||||
}
|
||||
|
||||
/*while(m_oChannelLocalisationAlternateCoords.size() > 0)
|
||||
{
|
||||
delete[] m_oChannelLocalisationAlternateCoords.front().first;
|
||||
m_oChannelLocalisationAlternateCoords.pop_front();
|
||||
}*/
|
||||
}
|
||||
|
||||
bool CBufferDatabase::decodeChannelLocalisationMemoryBuffer(const IMemoryBuffer* buffer, uint64_t startTime, uint64_t endTime)
|
||||
{
|
||||
//feed memory buffer to decoder
|
||||
m_decoder->getInputParameter(OVP_GD_Algorithm_ChannelLocalisationDecoder_InputParameterId_MemoryBufferToDecode)->setReferenceTarget(&buffer);
|
||||
|
||||
//process buffer
|
||||
m_decoder->process();
|
||||
|
||||
//copy header if needed
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_ChannelLocalisationDecoder_OutputTriggerId_ReceivedHeader))
|
||||
{
|
||||
//retrieve matrix header
|
||||
Kernel::TParameterHandler<CMatrix*> matrix;
|
||||
matrix.initialize(m_decoder->getOutputParameter(OVP_GD_Algorithm_ChannelLocalisationDecoder_OutputParameterId_Matrix));
|
||||
|
||||
//copy channel labels
|
||||
m_channelLocalisationLabels.resize(matrix->getDimensionSize(0));
|
||||
for (size_t i = 0; i < m_channelLocalisationLabels.size(); ++i) { m_channelLocalisationLabels[i] = matrix->getDimensionLabel(0, i); }
|
||||
|
||||
//retrieve dynamic flag
|
||||
Kernel::TParameterHandler<bool> dynamic;
|
||||
dynamic.initialize(m_decoder->getOutputParameter(OVP_GD_Algorithm_ChannelLocalisationDecoder_OutputParameterId_Dynamic));
|
||||
m_dynamicChannelLocalisation = dynamic;
|
||||
|
||||
if (matrix->getDimensionSize(1) == 3)
|
||||
{
|
||||
m_cartesianCoords = true;
|
||||
/*m_channelLocalisationCartesianCoords = &m_channelLocalisationCoords;
|
||||
m_channelLocalisationSphericalCoords = &m_oChannelLocalisationAlternateCoords;*/
|
||||
}
|
||||
else if (matrix->getDimensionSize(1) == 2)
|
||||
{
|
||||
m_cartesianCoords = false;
|
||||
/*m_channelLocalisationCartesianCoords = &m_oChannelLocalisationAlternateCoords;
|
||||
m_channelLocalisationSphericalCoords = &m_channelLocalisationCoords;*/
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ParentPlugin.getLogManager() << Kernel::LogLevel_Error
|
||||
<< "Wrong size found for dimension 1 of Channel localisation header! Can't process header!\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
//header information received
|
||||
m_channelLocalisationHeaderReceived = true;
|
||||
}
|
||||
|
||||
//has a chanloc buffer been received?
|
||||
if (m_decoder->isOutputTriggerActive(OVP_GD_Algorithm_ChannelLocalisationDecoder_OutputTriggerId_ReceivedBuffer))
|
||||
{
|
||||
//number of buffers required to cover displayed time range
|
||||
size_t maxNBuffer = 1;
|
||||
|
||||
//resize channel localisation queue if necessary
|
||||
if (m_dynamicChannelLocalisation)
|
||||
{
|
||||
const uint64_t bufferDuration = endTime - startTime;
|
||||
if (bufferDuration != 0)
|
||||
{
|
||||
maxNBuffer = size_t(ceil(m_TotalDuration / bufferDuration));
|
||||
if (maxNBuffer == 0) { maxNBuffer = 1; }
|
||||
}
|
||||
|
||||
//if new number of buffers decreased, resize list and destroy useless buffers
|
||||
while (m_channelLocalisationCoords.size() > maxNBuffer)
|
||||
{
|
||||
delete[] m_channelLocalisationCoords.front().first;
|
||||
m_channelLocalisationCoords.pop_front();
|
||||
// delete[] m_oChannelLocalisationAlternateCoords.front().first;
|
||||
// m_oChannelLocalisationAlternateCoords.pop_front();
|
||||
m_channelLocalisationTimes.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
//retrieve coordinates matrix
|
||||
Kernel::TParameterHandler<CMatrix*> matrix;
|
||||
matrix.initialize(m_decoder->getOutputParameter(OVP_GD_Algorithm_ChannelLocalisationDecoder_OutputParameterId_Matrix));
|
||||
|
||||
//get pointer to destination matrix
|
||||
CMatrix* channelLocalisation;
|
||||
//CMatrix* alternateChannelLocalisation = nullptr;
|
||||
if (m_channelLocalisationCoords.size() < maxNBuffer)
|
||||
{
|
||||
//create a new matrix and resize it
|
||||
channelLocalisation = new CMatrix();
|
||||
channelLocalisation->copyDescription(*matrix);
|
||||
// alternateChannelLocalisation = new CMatrix();
|
||||
// TODO : resize it appropriately depending on whether it is spherical or cartesian
|
||||
}
|
||||
else //m_channelLocalisationCoords.size() == maxNBuffer
|
||||
{
|
||||
channelLocalisation = m_channelLocalisationCoords.front().first;
|
||||
m_channelLocalisationCoords.pop_front();
|
||||
// alternateChannelLocalisation = m_oChannelLocalisationAlternateCoords.front().first;
|
||||
// m_oChannelLocalisationAlternateCoords.pop_front();
|
||||
m_channelLocalisationTimes.pop_front();
|
||||
}
|
||||
|
||||
if (channelLocalisation)
|
||||
{
|
||||
//copy coordinates and times
|
||||
channelLocalisation->copyContent(*matrix);
|
||||
m_channelLocalisationCoords.emplace_back(channelLocalisation, true);
|
||||
//m_oChannelLocalisationAlternateCoords.push_back(std::pair<CMatrix*, bool>(alternateChannelLocalisation, true));
|
||||
m_channelLocalisationTimes.emplace_back(startTime, endTime);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::onChannelLocalisationBufferReceived(const size_t index)
|
||||
{
|
||||
m_channelLocalisationCoords[index].second = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::isFirstChannelLocalisationBufferProcessed()
|
||||
{
|
||||
//at least one chanloc buffer must have been received and processed
|
||||
return (!m_channelLocalisationCoords.empty()) && (!m_channelLocalisationCoords[0].second);
|
||||
}
|
||||
|
||||
bool CBufferDatabase::adjustNumberOfDisplayedBuffers(const double time)
|
||||
{
|
||||
bool nBufferToDisplayChanged = false;
|
||||
|
||||
if (time > 0)
|
||||
{
|
||||
m_TotalDuration = time;
|
||||
m_ovTotalDuration = 0;
|
||||
m_TotalStep = 0;
|
||||
}
|
||||
|
||||
//return if buffer length is not known yet
|
||||
if (m_DimSizes[1] == 0) { return false; }
|
||||
|
||||
size_t newNbufferToDisplay = size_t(ceil((m_TotalDuration * m_Sampling) / m_DimSizes[1]));
|
||||
|
||||
//displays at least one buffer
|
||||
newNbufferToDisplay = (newNbufferToDisplay == 0) ? 1 : newNbufferToDisplay;
|
||||
if (newNbufferToDisplay != m_NBufferToDisplay || time <= 0)
|
||||
{
|
||||
m_NBufferToDisplay = newNbufferToDisplay;
|
||||
nBufferToDisplayChanged = true;
|
||||
|
||||
//if new number of buffers decreased, resize lists and destroy useless buffers
|
||||
while (m_NBufferToDisplay < m_SampleBuffers.size())
|
||||
{
|
||||
delete[] m_SampleBuffers.front();
|
||||
m_SampleBuffers.pop_front();
|
||||
m_StartTime.pop_front();
|
||||
m_EndTime.pop_front();
|
||||
|
||||
//suppress the corresponding minmax values
|
||||
for (size_t c = 0; c < m_DimSizes[0]; ++c) { m_LocalMinMaxValue[c].pop_front(); }
|
||||
}
|
||||
}
|
||||
|
||||
return nBufferToDisplayChanged;
|
||||
}
|
||||
|
||||
size_t CBufferDatabase::getChannelCount() const { return m_DimSizes[0]; }
|
||||
|
||||
double CBufferDatabase::getDisplayedTimeIntervalWidth() const { return (m_NBufferToDisplay * ((m_DimSizes[1] * 1000.0) / m_Sampling)); }
|
||||
|
||||
void CBufferDatabase::setMatrixDimensionCount(const size_t n)
|
||||
{
|
||||
if (n != 2)
|
||||
{
|
||||
m_Error = true;
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error << "Caller tried to set a " << n
|
||||
<< "-dimensional matrix. Only 2-dimensional matrices are supported (e.g. [rows X cols]).\n";
|
||||
}
|
||||
if (n == 1)
|
||||
{
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error
|
||||
<< "Note: For 1-dimensional matrices, you may try Matrix Transpose box to upgrade the stream to [N X 1] first.\n";
|
||||
}
|
||||
}
|
||||
|
||||
void CBufferDatabase::setMatrixDimensionSize(const size_t index, const size_t size)
|
||||
{
|
||||
if (index >= 2)
|
||||
{
|
||||
m_Error = true;
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error << "Tried to access dimension "
|
||||
<< index << ", only 0 and 1 supported\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_DimSizes[index] != 0 && m_DimSizes[index] != size)
|
||||
{
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error
|
||||
<< "Upstream tried to change the data chunk size after the first header, this is not supported.\n";
|
||||
m_Error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_DimSizes[index] = size;
|
||||
m_DimLabels[index].resize(size);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
m_NElectrodes = m_DimSizes[index];
|
||||
|
||||
//resize min/max values vector
|
||||
m_LocalMinMaxValue.resize(size_t(m_NElectrodes));
|
||||
}
|
||||
}
|
||||
|
||||
void CBufferDatabase::setMatrixDimensionLabel(const size_t idx1, const size_t idx2, const char* label)
|
||||
{
|
||||
if (m_Error) { return; }
|
||||
|
||||
if (idx1 >= 2)
|
||||
{
|
||||
m_Error = true;
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error << "Tried to access dimension " << idx1
|
||||
<< ", only 0 and 1 supported\n";
|
||||
return;
|
||||
}
|
||||
|
||||
m_DimLabels[idx1][idx2] = label;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::setMatrixBuffer(const double* buffer, const uint64_t startTime, const uint64_t endTime)
|
||||
{
|
||||
//if an error has occurred, do nothing
|
||||
if (m_Error) { return false; }
|
||||
|
||||
// Check for time-continuity
|
||||
if (startTime < m_LastBufferEndTime && !m_WarningPrinted)
|
||||
{
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Warning
|
||||
<< "Your signal does not appear to be continuous in time. "
|
||||
<< "Previously inserted buffer ended at " << CTime(m_LastBufferEndTime).toSeconds()
|
||||
<< "s, the current starts at " << CTime(startTime).toSeconds()
|
||||
<< "s. The display may be incorrect.\n";
|
||||
m_WarningPrinted = true;
|
||||
}
|
||||
m_LastBufferEndTime = endTime;
|
||||
|
||||
|
||||
//if this the first buffer, perform some precomputations
|
||||
if (!m_HasFirstBuffer)
|
||||
{
|
||||
m_BufferDuration = endTime - startTime;
|
||||
|
||||
//test if it is equal to zero : Error
|
||||
if (m_BufferDuration == 0)
|
||||
{
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Warning <<
|
||||
"Error : buffer start time and end time are equal : " << startTime << "\n";
|
||||
m_Error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//computes the sampling frequency for sanity checking or if the setter has not been called
|
||||
const uint64_t duration = (uint64_t(1) << 32) * m_DimSizes[1];
|
||||
size_t sampling = size_t(duration / m_BufferDuration);
|
||||
if (sampling == 0)
|
||||
{
|
||||
// Complain if estimate is bad
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Warning
|
||||
<< "The integer sampling frequency was estimated from the chunk size to be 0"
|
||||
<< " (nSamples " << m_DimSizes[1] << " / bufferLength " << CTime(m_BufferDuration).toSeconds()
|
||||
<< "s = 0). This is not supported. Forcing the rate to 1. This may lead to problems.\n";
|
||||
sampling = 1;
|
||||
}
|
||||
if (m_Sampling == 0)
|
||||
{
|
||||
// use chunking duration estimate if setter hasn't been used
|
||||
m_Sampling = sampling;
|
||||
}
|
||||
if (m_Sampling != sampling)
|
||||
{
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Warning
|
||||
<< "Sampling rate [" << sampling << "] suggested by chunk properties differs from stream-specified rate ["
|
||||
<< m_Sampling << "]. There may be a problem with an upstream box. Trying to use the estimated rate.\n";
|
||||
m_Sampling = sampling;
|
||||
}
|
||||
|
||||
//computes the number of buffer necessary to display the interval
|
||||
adjustNumberOfDisplayedBuffers(-1);
|
||||
|
||||
m_Drawable->init();
|
||||
|
||||
m_HasFirstBuffer = true;
|
||||
}
|
||||
|
||||
if (!m_ChannelLookupTableInitialized)
|
||||
{
|
||||
fillChannelLookupTable(); //to retrieve the unrecognized electrode warning
|
||||
// The above call will fail if no electrode localisation data...
|
||||
// m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error << "Unable to fill lookup table\n";
|
||||
// return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//look for chanloc buffers recently received
|
||||
for (size_t i = 0; i < m_channelLocalisationCoords.size(); ++i)
|
||||
{
|
||||
//if a new set of coordinates was received
|
||||
if (m_channelLocalisationCoords[i].second) { onChannelLocalisationBufferReceived(i); }
|
||||
}
|
||||
}
|
||||
|
||||
double* bufferToWrite = nullptr;
|
||||
const size_t nSamplesPerBuffer = m_DimSizes[0] * m_DimSizes[1];
|
||||
|
||||
//if old buffers need to be removed
|
||||
if (m_SampleBuffers.size() == m_NBufferToDisplay)
|
||||
{
|
||||
if (m_ovTotalDuration == 0) { m_ovTotalDuration = (m_StartTime.back() - m_StartTime.front()) + (m_EndTime.back() - m_StartTime.back()); }
|
||||
if (m_BufferStep == 0)
|
||||
{
|
||||
if (m_StartTime.size() <= 1) { m_BufferStep = size_t(m_ovTotalDuration); }
|
||||
else { m_BufferStep = m_StartTime[1] - m_StartTime[0]; }
|
||||
}
|
||||
if (m_TotalStep == 0) { m_TotalStep = (m_StartTime.back() - m_StartTime.front()) + m_BufferStep; }
|
||||
|
||||
//save first buffer pointer
|
||||
bufferToWrite = m_SampleBuffers.front();
|
||||
|
||||
//pop first element from queues
|
||||
m_SampleBuffers.pop_front();
|
||||
m_StartTime.pop_front();
|
||||
m_EndTime.pop_front();
|
||||
for (size_t c = 0; c < size_t(m_DimSizes[0]); ++c) { m_LocalMinMaxValue[c].pop_front(); }
|
||||
}
|
||||
|
||||
//do we need to allocate a new buffer?
|
||||
if (bufferToWrite == nullptr) { bufferToWrite = new double[nSamplesPerBuffer]; }
|
||||
|
||||
//copy new buffer into internal buffer
|
||||
if (nSamplesPerBuffer != 0) { memcpy(bufferToWrite, buffer, nSamplesPerBuffer * sizeof(double)); }
|
||||
|
||||
//push new buffer and its timestamps
|
||||
m_SampleBuffers.push_back(bufferToWrite);
|
||||
m_StartTime.push_back(startTime);
|
||||
m_EndTime.push_back(endTime);
|
||||
|
||||
//compute and push min and max values of new buffer
|
||||
size_t currentSample = 0;
|
||||
//for each channel
|
||||
for (size_t c = 0; c < size_t(m_DimSizes[0]); ++c)
|
||||
{
|
||||
double localMin = DBL_MAX;
|
||||
double localMax = -DBL_MAX;
|
||||
|
||||
//for each sample
|
||||
for (size_t i = 0; i < m_DimSizes[1]; i++, currentSample++)
|
||||
{
|
||||
//get channel local min/max
|
||||
if (buffer[currentSample] < localMin) { localMin = buffer[currentSample]; }
|
||||
if (buffer[currentSample] > localMax) { localMax = buffer[currentSample]; }
|
||||
}
|
||||
|
||||
//adds the minmax pair to the corresponding channel's list
|
||||
m_LocalMinMaxValue[c].emplace_back(localMin, localMax);
|
||||
|
||||
if (localMax > m_MaxValue) { m_MaxValue = localMax; }
|
||||
if (localMin < m_MinValue) { m_MinValue = localMin; }
|
||||
}
|
||||
|
||||
//tells the drawable to redraw himself since the signal information has been updated
|
||||
if (m_RedrawOnNewData) { m_Drawable->redraw(); }
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::setSampling(const size_t sampling)
|
||||
{
|
||||
m_Sampling = sampling;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBufferDatabase::getDisplayedChannelLocalMinMaxValue(const size_t channel, double& min, double& max)
|
||||
{
|
||||
min = +DBL_MAX;
|
||||
max = -DBL_MAX;
|
||||
|
||||
for (size_t i = 0; i < m_LocalMinMaxValue[channel].size(); ++i)
|
||||
{
|
||||
if (min > m_LocalMinMaxValue[channel][i].first) { min = m_LocalMinMaxValue[channel][i].first; }
|
||||
if (max < m_LocalMinMaxValue[channel][i].second) { max = m_LocalMinMaxValue[channel][i].second; }
|
||||
}
|
||||
}
|
||||
|
||||
bool CBufferDatabase::isTimeInDisplayedInterval(const uint64_t& time) const
|
||||
{
|
||||
if (m_StartTime.empty()) { return false; }
|
||||
|
||||
return time >= m_StartTime.front() && time <= m_EndTime.back();
|
||||
}
|
||||
|
||||
bool CBufferDatabase::getIndexOfBufferStartingAtTime(const uint64_t& time, size_t& index) const
|
||||
{
|
||||
index = 0;
|
||||
|
||||
if (m_SampleBuffers.empty() || time < m_StartTime.front() || time > m_StartTime.back()) { return false; }
|
||||
|
||||
for (size_t i = 0; i < m_StartTime.size(); ++i)
|
||||
{
|
||||
if (m_StartTime[i] == time)
|
||||
{
|
||||
index = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CBufferDatabase::getDisplayedGlobalMinMaxValue(double& min, double& max)
|
||||
{
|
||||
min = +DBL_MAX;
|
||||
max = -DBL_MAX;
|
||||
|
||||
for (auto& pairs : m_LocalMinMaxValue)
|
||||
{
|
||||
for (const auto& pair : pairs)
|
||||
{
|
||||
if (min > pair.first) { min = pair.first; }
|
||||
if (max < pair.second) { max = pair.second; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CBufferDatabase::getLastBufferChannelLocalMinMaxValue(const size_t channel, double& min, double& max)
|
||||
{
|
||||
min = m_LocalMinMaxValue[channel].back().first;
|
||||
max = m_LocalMinMaxValue[channel].back().second;
|
||||
}
|
||||
|
||||
void CBufferDatabase::getLastBufferMinMaxValue(double& min, double& max)
|
||||
{
|
||||
min = +DBL_MAX;
|
||||
max = -DBL_MAX;
|
||||
|
||||
for (auto& localValue : m_LocalMinMaxValue)
|
||||
{
|
||||
min = (localValue.back().first < min) ? localValue.back().first : min;
|
||||
max = (localValue.back().second > max) ? localValue.back().second : max;
|
||||
}
|
||||
}
|
||||
|
||||
bool CBufferDatabase::getElectrodePosition(const size_t index, double* position)
|
||||
{
|
||||
//TODO : add time parameter and look for coordinates closest to that time!
|
||||
if (index < m_channelLocalisationLabels.size())
|
||||
{
|
||||
//if(m_cartesianCoords == true)
|
||||
//{
|
||||
*position = *(m_channelLocalisationCoords[0].first->getBuffer() + 3 * index);
|
||||
*(position + 1) = *(m_channelLocalisationCoords[0].first->getBuffer() + 3 * index + 1);
|
||||
*(position + 2) = *(m_channelLocalisationCoords[0].first->getBuffer() + 3 * index + 2);
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::getElectrodePosition(const CString& label, double* position)
|
||||
{
|
||||
//TODO : add time parameter and look for coordinates closest to that time!
|
||||
for (size_t i = 0; i < m_channelLocalisationLabels.size(); ++i)
|
||||
{
|
||||
if (strcmp(label.toASCIIString(), m_channelLocalisationLabels[i].toASCIIString()) == 0)
|
||||
{
|
||||
//if(m_cartesianCoords == true)
|
||||
//{
|
||||
*position = *(m_channelLocalisationCoords[0].first->getBuffer() + 3 * i);
|
||||
*(position + 1) = *(m_channelLocalisationCoords[0].first->getBuffer() + 3 * i + 1);
|
||||
*(position + 2) = *(m_channelLocalisationCoords[0].first->getBuffer() + 3 * i + 2);
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::getElectrodeLabel(const size_t index, CString& label)
|
||||
{
|
||||
if (index >= m_channelLocalisationLabels.size()) { return false; }
|
||||
label = m_channelLocalisationLabels[index].toASCIIString();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::getChannelPosition(const size_t index, double*& position)
|
||||
{
|
||||
//TODO : add time parameter and look for coordinates closest to that time!
|
||||
if (index >= 0 && index < m_ChannelLookupIndices.size())
|
||||
{
|
||||
if (m_cartesianCoords) { position = m_channelLocalisationCoords[0].first->getBuffer() + 3 * m_ChannelLookupIndices[index]; }
|
||||
// else { } //TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::getChannelSphericalCoordinates(const size_t index, double& theta, double& phi)
|
||||
{
|
||||
//TODO : add time parameter and look for coordinates closest to that time!
|
||||
if (index >= 0 && index < m_ChannelLookupIndices.size())
|
||||
{
|
||||
if (m_cartesianCoords)
|
||||
{
|
||||
//get cartesian coords
|
||||
double* coords = m_channelLocalisationCoords[0].first->getBuffer() + 3 * m_ChannelLookupIndices[index];
|
||||
|
||||
//convert to spherical coords
|
||||
return convertCartesianToSpherical(coords, theta, phi);
|
||||
}
|
||||
//streamed coordinates are spherical already
|
||||
//TODO
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::getChannelLabel(const size_t index, CString& label)
|
||||
{
|
||||
if (index >= 0 && index < m_ChannelLookupIndices.size())
|
||||
{
|
||||
label = m_channelLocalisationLabels[m_ChannelLookupIndices[index]];
|
||||
return true;
|
||||
}
|
||||
label = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CBufferDatabase::setStimulation(const size_t /*index*/, const uint64_t id, const uint64_t date)
|
||||
{
|
||||
m_Stimulations.emplace_back(date, id);
|
||||
|
||||
if (!m_StartTime.empty())
|
||||
{
|
||||
while (m_Stimulations.begin() != m_Stimulations.end() && m_Stimulations.begin()->first < m_StartTime.front()) { m_Stimulations.pop_front(); }
|
||||
}
|
||||
}
|
||||
|
||||
bool CBufferDatabase::fillChannelLookupTable()
|
||||
{
|
||||
if (!m_HasFirstBuffer || !m_channelLocalisationHeaderReceived) { return false; }
|
||||
|
||||
bool res = true;
|
||||
|
||||
//resize lookup array and initialize lookup indices to 0
|
||||
m_ChannelLookupIndices.resize(size_t(m_NElectrodes), 0);
|
||||
|
||||
//for all channels
|
||||
for (size_t i = 0; i < m_DimSizes[0]; ++i)
|
||||
{
|
||||
//trim leading spaces
|
||||
size_t firstNonWhitespaceChar = 0;
|
||||
for (; firstNonWhitespaceChar < m_DimLabels[0][i].size(); ++firstNonWhitespaceChar)
|
||||
{
|
||||
if (isspace(m_DimLabels[0][i][firstNonWhitespaceChar]) == 0) { break; }
|
||||
}
|
||||
|
||||
//trim trailing spaces
|
||||
size_t lastNonWhitespaceChar = 0;
|
||||
if (!m_DimLabels[0][i].empty())
|
||||
{
|
||||
for (lastNonWhitespaceChar = m_DimLabels[0][i].size() - 1; lastNonWhitespaceChar >= 0; lastNonWhitespaceChar--)
|
||||
{
|
||||
if (isspace(m_DimLabels[0][i][lastNonWhitespaceChar]) == 0) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
//look for label in channel localisation labels database
|
||||
bool labelRecognized = false;
|
||||
|
||||
if (firstNonWhitespaceChar < lastNonWhitespaceChar)
|
||||
{
|
||||
std::string channelLabel(m_DimLabels[0][i].substr(firstNonWhitespaceChar, lastNonWhitespaceChar - firstNonWhitespaceChar + 1));
|
||||
|
||||
for (size_t j = 0; j < m_channelLocalisationLabels.size(); ++j)
|
||||
{
|
||||
if (strcmp(channelLabel.c_str(), m_channelLocalisationLabels[j].toASCIIString()) == 0)
|
||||
{
|
||||
labelRecognized = true;
|
||||
m_ChannelLookupIndices[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//unrecognized electrode!
|
||||
if (!labelRecognized)
|
||||
{
|
||||
m_ParentPlugin.getLogManager() << Kernel::LogLevel_Warning << "Unrecognized electrode name (index=" << i
|
||||
<< ", name=" << m_DimLabels[0][i] << ")!\n";
|
||||
res = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_ParentPlugin.getLogManager() << Kernel::LogLevel_Trace << "Electrodes list : ";
|
||||
|
||||
for (size_t i = 0; i < size_t(m_DimSizes[0]); ++i)
|
||||
{
|
||||
m_ParentPlugin.getLogManager() << m_DimLabels[0][i].c_str();
|
||||
if (i < m_DimSizes[0] - 1) { m_ParentPlugin.getLogManager() << ", "; }
|
||||
else { m_ParentPlugin.getLogManager() << "\n"; }
|
||||
}
|
||||
|
||||
if (res) { m_ChannelLookupTableInitialized = true; }
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool CBufferDatabase::convertCartesianToSpherical(const double* cartesian, double& theta, double& phi) const
|
||||
{
|
||||
const double threshold = 1e-3;
|
||||
const double radToDeg = 57.2957795131; // 180 / pi
|
||||
|
||||
//compute theta
|
||||
theta = acos(cartesian[2]) * radToDeg;
|
||||
|
||||
//compute phi so that it lies in [0, 360]
|
||||
if (fabs(cartesian[0]) < threshold) { phi = (cartesian[1] > 0) ? 90 : 270; }
|
||||
else
|
||||
{
|
||||
phi = atan(cartesian[1] / cartesian[0]) * radToDeg;
|
||||
if (cartesian[0] < 0) { phi += 180; }
|
||||
else if (cartesian[1] < 0) { phi += 360; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SimpleVisualization
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,322 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <cfloat>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SimpleVisualization {
|
||||
|
||||
class CSignalDisplayDrawable;
|
||||
|
||||
/**
|
||||
* Abtract class of objects than can be updated by a CBufferDatabase
|
||||
*/
|
||||
class CSignalDisplayDrawable
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~CSignalDisplayDrawable() = default;
|
||||
virtual void init() = 0;
|
||||
virtual void redraw() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is used to store information about the incoming signal stream. It can request a CSignalDisplayDrawable
|
||||
* object to redraw himself in case of some changes in its data.
|
||||
*/
|
||||
class CBufferDatabase
|
||||
{
|
||||
public:
|
||||
int64_t m_NElectrodes = 0; ///< Number of channels
|
||||
std::array<size_t, 2> m_DimSizes; ///< Number of channels and number of samples per buffer
|
||||
std::vector<std::string> m_DimLabels[2]; ///< Channel labels, buffer labels
|
||||
bool m_HasFirstBuffer = false; ///< Flag set to true once first buffer is received
|
||||
size_t m_Sampling = 0; ///< Sampling frequency of the incoming stream
|
||||
std::deque<double*> m_SampleBuffers; ///< double-linked list of pointers to the samples buffers of the current time window
|
||||
std::deque<std::pair<uint64_t, uint64_t>> m_Stimulations; ///< stimulations to display. pair values are <date, stimcode>
|
||||
|
||||
bool m_ChannelLookupTableInitialized = false; ///< flag set to true once channel lookup indices are determined
|
||||
std::vector<size_t> m_ChannelLookupIndices; ///< indices of electrodes in channel localisation database
|
||||
|
||||
//CMatrix m_electrodesSphericalCoords; ///< electrode spherical coordinates (in degrees)
|
||||
//std::vector<CString> m_oElectrodesLabels; ///< electrode labels (standardized)
|
||||
|
||||
size_t m_NBufferToDisplay = 2; ///< Number of buffer to display at the same time
|
||||
double m_MaxValue = -DBL_MAX; ///< The global maximum value of the signal (up to now)
|
||||
double m_MinValue = +DBL_MAX; ///< The global minimum value of the signal (up to now)
|
||||
std::deque<uint64_t> m_StartTime; ///< Double-linked list of the start times of the current buffers
|
||||
std::deque<uint64_t> m_EndTime; ///< Double-linked list of the end times of the current buffers
|
||||
double m_TotalDuration = 0; ///< Duration to display in seconds
|
||||
|
||||
/*! Duration to display in openvibe time units.
|
||||
Computed once every time the user changes the total duration to display,
|
||||
when the maximum number of buffers to store are received.*/
|
||||
uint64_t m_ovTotalDuration = 0;
|
||||
|
||||
/*! Duration of a single buffer.
|
||||
Computed once, but not constant when sampling frequency is not a multiple of buffer size!*/
|
||||
uint64_t m_BufferDuration = 0;
|
||||
|
||||
/*! Time step separating the start times of m_NBufferToDisplay+1 buffers.
|
||||
Recomputed once every time the user changes the total duration to display,
|
||||
but not constant when sampling frequency is not a multiple of buffer size!*/
|
||||
size_t m_TotalStep = 0;
|
||||
|
||||
/*! Time step separating the start times of 2 consecutive buffers.
|
||||
Computed once, but not constant when sampling frequency is not a multiple of buffer size!*/
|
||||
size_t m_BufferStep = 0;
|
||||
|
||||
uint64_t m_LastBufferEndTime = 0; ///< When did the last inserted buffer end
|
||||
bool m_WarningPrinted = false; ///< Did we print a warning about noncontinuity?
|
||||
|
||||
CSignalDisplayDrawable* m_Drawable = nullptr;///< Pointer to the drawable object to update (if needed)
|
||||
|
||||
std::vector<std::deque<std::pair<double, double>>> m_LocalMinMaxValue;
|
||||
|
||||
Toolkit::TBoxAlgorithm<IBoxAlgorithm>& m_ParentPlugin;
|
||||
|
||||
bool m_Error = false;
|
||||
|
||||
//! Redraws the associated SignalDisplayDrawable upon new data reception if true (default)
|
||||
bool m_RedrawOnNewData = true;
|
||||
|
||||
protected:
|
||||
/* \name Channel localisation */
|
||||
//@{
|
||||
//channel localisation decoder
|
||||
Kernel::IAlgorithmProxy* m_decoder = nullptr;
|
||||
//flag set to true once channel localisation buffer is received
|
||||
bool m_channelLocalisationHeaderReceived = false;
|
||||
//dynamic channel localisation flag (e.g. localisation is constantly updated with MEG)
|
||||
bool m_dynamicChannelLocalisation = false;
|
||||
//channel labels database
|
||||
std::vector<CString> m_channelLocalisationLabels;
|
||||
//flag stating whether streamed coordinates are cartesian (as opposed to spherical)
|
||||
bool m_cartesianCoords = false;
|
||||
//! double-linked list of streamed channel coordinates (if cartesian, expressed in normalized space (X right Y front Z up))
|
||||
std::deque<std::pair<CMatrix*, bool>> m_channelLocalisationCoords;
|
||||
//! double-linked list of channel coordinates (spherical if streamed coords aere cartesian and vice versa)
|
||||
//std::deque< std::pair<CMatrix*, bool> > m_oChannelLocalisationAlternateCoords;
|
||||
//pointer to double linked list of cartesian coordinates
|
||||
//std::deque< std::pair<CMatrix*, bool> > * m_channelLocalisationCartesianCoords;
|
||||
//pointer to double linked list of spherical coordinates
|
||||
//std::deque< std::pair<CMatrix*, bool> > * m_channelLocalisationSphericalCoords;
|
||||
//! double-linked list of start/end times of channel coordinates
|
||||
std::deque<std::pair<uint64_t, uint64_t>> m_channelLocalisationTimes;
|
||||
//@}
|
||||
|
||||
//! Redraw mode (shift or scan)
|
||||
ESignalDisplayMode m_displayMode = ESignalDisplayMode::Scan;
|
||||
|
||||
public:
|
||||
explicit CBufferDatabase(Toolkit::TBoxAlgorithm<IBoxAlgorithm>& plugin);
|
||||
|
||||
virtual ~CBufferDatabase();
|
||||
|
||||
/**
|
||||
* \brief Decode a channel localisation memory buffer
|
||||
* \param buffer Memory buffer to decode
|
||||
* \param startTime Start time of memory buffer
|
||||
* \param endTime End time of memory buffer
|
||||
* \return True if memory buffer could be properly decoded, false otherwise
|
||||
*/
|
||||
virtual bool decodeChannelLocalisationMemoryBuffer(const IMemoryBuffer* buffer, uint64_t startTime, uint64_t endTime);
|
||||
|
||||
/**
|
||||
* \brief Callback called upon channel localisation buffer reception
|
||||
* \param index Index of newly received channel localisation buffer
|
||||
* \return True if buffer data was correctly processed, false otherwise
|
||||
*/
|
||||
virtual bool onChannelLocalisationBufferReceived(const size_t index);
|
||||
|
||||
/**
|
||||
* \brief Sets the drawable object to update.
|
||||
* \param drawable drawable object to update.
|
||||
*/
|
||||
virtual void setDrawable(CSignalDisplayDrawable* drawable) { m_Drawable = drawable; }
|
||||
|
||||
/**
|
||||
* \brief Get error status
|
||||
* \return Error status. If true, an error occurred.
|
||||
*/
|
||||
virtual bool getErrorStatus() { return m_Error; }
|
||||
|
||||
/**
|
||||
* \brief Determines whether first buffer has been received yet
|
||||
* \return True if first buffer has been received already, false otherwise
|
||||
*/
|
||||
virtual bool hasFirstBuffer() { return m_HasFirstBuffer; }
|
||||
|
||||
/**
|
||||
* \brief Determines whether first channel localisation buffer has been processed yet
|
||||
* When this condition is true, channel coordinates may be retrieved using the
|
||||
* corresponding methods in this class.
|
||||
* \return True if first chanloc buffer was processed
|
||||
*/
|
||||
virtual bool isFirstChannelLocalisationBufferProcessed();
|
||||
|
||||
/**
|
||||
* Compute the number of buffers needed to display the signal for a certain time period.
|
||||
* \param time the time window's width in seconds.
|
||||
*/
|
||||
virtual bool adjustNumberOfDisplayedBuffers(const double time);
|
||||
|
||||
/**
|
||||
* \brief Get time interval covered by data held in this object
|
||||
* \return Time interval in ms
|
||||
*/
|
||||
virtual double getDisplayedTimeIntervalWidth() const;
|
||||
|
||||
/**
|
||||
* \brief Determine whether time passed in parameter lies in displayed data interval
|
||||
* \param time Time to test
|
||||
* \return True if time lies in displayed time interval, false otherwise
|
||||
*/
|
||||
virtual bool isTimeInDisplayedInterval(const uint64_t& time) const;
|
||||
|
||||
/**
|
||||
* \brief Get index of sample buffer which starts at a given time
|
||||
* \param time[in] Start time of buffer
|
||||
* \param index[out] Buffer index
|
||||
* \return True if buffer index could be determined, false otherwise
|
||||
*/
|
||||
virtual bool getIndexOfBufferStartingAtTime(const uint64_t& time, size_t& index) const;
|
||||
|
||||
//! Returns the min/max values currently displayed for the given channel
|
||||
virtual void getDisplayedChannelLocalMinMaxValue(const size_t channel, double& min, double& max);
|
||||
//! Returns the min/max values currently displayed (all channels taken into account)
|
||||
virtual void getDisplayedGlobalMinMaxValue(double& min, double& max);
|
||||
|
||||
virtual void getDisplayedChannelLocalMeanValue(size_t /*channel*/, double& /*mean*/) { }
|
||||
|
||||
//! Returns the min/max values of the last buffer arrived for the given channel
|
||||
virtual void getLastBufferChannelLocalMinMaxValue(const size_t channel, double& min, double& max);
|
||||
|
||||
//! Returns the min/max values of the last buffer arrived (all channels taken into account)
|
||||
virtual void getLastBufferMinMaxValue(double& min, double& max);
|
||||
|
||||
/**
|
||||
* \brief Get number of eletrodes in database
|
||||
* \return Number of electrodes
|
||||
*/
|
||||
virtual size_t getElectrodeCount() { return m_channelLocalisationLabels.size(); }
|
||||
|
||||
/**
|
||||
* \brief Get electrode normalized position
|
||||
* \remarks Position expressed in normalized cartesian frame where X is right, Y front, Z up
|
||||
* \param[in] index Index of electrode in database whose position is to be retrieved
|
||||
* \param[out] position Pointer to an array of 3 floats where to store coordinates
|
||||
* \return True if electrode position could be retrieved
|
||||
*/
|
||||
virtual bool getElectrodePosition(const size_t index, double* position);
|
||||
|
||||
/**
|
||||
* \brief Get electrode normalized position
|
||||
* \remarks Position expressed in normalized cartesian frame where X is right, Y front, Z up
|
||||
* \param[in] label Label of electrode whose position is to be retrieved
|
||||
* \param[out] position Pointer to an array of 3 floats where to store coordinates
|
||||
* \return True if electrode position could be retrieved
|
||||
*/
|
||||
virtual bool getElectrodePosition(const CString& label, double* position);
|
||||
|
||||
/**
|
||||
* \brief Get electrode label
|
||||
* \param[in] index Index of electrode in database whose label is to be retrieved
|
||||
* \param[out] label Electrode label
|
||||
* \return True if electrode label could be retrieved
|
||||
*/
|
||||
virtual bool getElectrodeLabel(const size_t index, CString& label);
|
||||
|
||||
/**
|
||||
* \brief Get number of channels
|
||||
* \return Number of channels
|
||||
*/
|
||||
virtual size_t getChannelCount() const;
|
||||
|
||||
/**
|
||||
* \brief Get channel normalized position
|
||||
* \remarks Position expressed in normalized cartesian frame where X is right, Y front, Z up
|
||||
* \param[in] index Index of channel whose position is to be retrieved
|
||||
* \param[out] position Reference on a double pointer
|
||||
* \return True if channel position could be retrieved (rChannelPosition then points to an array of 3 floats)
|
||||
*/
|
||||
virtual bool getChannelPosition(const size_t index, double*& position);
|
||||
|
||||
/**
|
||||
* \brief Get channel spherical coordinates in degrees
|
||||
* \param[in] index Index of channel whose coordinates are to be retrieved
|
||||
* \param[out] theta Reference on a float to be set with theta angle
|
||||
* \param[out] phi Reference on a float to be set with phi angle
|
||||
* \return True if channel coordinates could be retrieved
|
||||
*/
|
||||
virtual bool getChannelSphericalCoordinates(const size_t index, double& theta, double& phi);
|
||||
|
||||
/**
|
||||
* \brief Get channel label
|
||||
* \param[in] index Index of channel whose label is to be retrieved
|
||||
* \param[out] label Channel label
|
||||
* \return True if channel label could be retrieved
|
||||
*/
|
||||
virtual bool getChannelLabel(const size_t index, CString& label);
|
||||
|
||||
virtual void setMatrixDimensionCount(const size_t n);
|
||||
virtual void setMatrixDimensionSize(const size_t index, const size_t size);
|
||||
virtual void setMatrixDimensionLabel(const size_t idx1, const size_t idx2, const char* label);
|
||||
|
||||
// Returns false on failure
|
||||
virtual bool setMatrixBuffer(const double* buffer, const uint64_t startTime, const uint64_t endTime);
|
||||
|
||||
// Sets the sampling frequency. If this is not called, the frequency is estimated from the stream chunk properties.
|
||||
// Mainly used to force a warning if stream-specified rate differs from the chunk-estimated rate.
|
||||
virtual bool setSampling(const size_t sampling);
|
||||
|
||||
virtual void setStimulationCount(const size_t /*n*/) { }
|
||||
virtual void setStimulation(const size_t index, const uint64_t id, const uint64_t date);
|
||||
|
||||
/**
|
||||
* \brief Set display mode
|
||||
* \remarks Used by signal display and time ruler to determine how they should be updated
|
||||
* \param mode New display mode
|
||||
*/
|
||||
virtual void setDisplayMode(const ESignalDisplayMode mode) { m_displayMode = mode; }
|
||||
|
||||
/**
|
||||
* \brief Get current display mode
|
||||
* \return Current display mode
|
||||
*/
|
||||
virtual ESignalDisplayMode getDisplayMode() { return m_displayMode; }
|
||||
|
||||
/**
|
||||
* \brief Set flag stating whether to redraw associated SignalDisplayDrawable objet when new data is available
|
||||
* \param redraw Value to set flag with
|
||||
*/
|
||||
virtual void setRedrawOnNewData(const bool redraw) { m_RedrawOnNewData = redraw; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \brief Initialize table storing indices of electrodes in channel localisation database
|
||||
* \return True if table could be initialized
|
||||
*/
|
||||
virtual bool fillChannelLookupTable();
|
||||
|
||||
/**
|
||||
* \brief Convert a cartesian coordinates triplet to spherical coordinates
|
||||
* \param[in] cartesian Pointer to cartesian coordinates triplet
|
||||
* \param[out] theta Equivalent theta angle
|
||||
* \param[out] phi Equivalent phi angle
|
||||
* \return True if coordinates were successfully converted
|
||||
*/
|
||||
bool convertCartesianToSpherical(const double* cartesian, double& theta, double& phi) const;
|
||||
};
|
||||
|
||||
} // namespace SimpleVisualization
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,274 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "ovpCTopographicMapDatabase.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SimpleVisualization {
|
||||
/**
|
||||
* This class contains everything necessary to setup a GTK window and display
|
||||
* a 2D topographic map
|
||||
*/
|
||||
class CTopographicMap2DView final : public CTopographicMapDrawable
|
||||
{
|
||||
public:
|
||||
enum class EProjection { Axial, Radial, NumProjection };
|
||||
|
||||
enum class EView { Top, Left, Right, Back };
|
||||
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param mapDatabase Datastore
|
||||
* \param interpolation Interpolation mode
|
||||
* \param delay Delay to apply to displayed data
|
||||
*/
|
||||
CTopographicMap2DView(CTopographicMapDatabase& mapDatabase, EInterpolationType interpolation, double delay);
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
~CTopographicMap2DView() override;
|
||||
|
||||
/** \name CSignalDisplayDrawable implementation */
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Initialize widgets
|
||||
*/
|
||||
void init() override;
|
||||
|
||||
/**
|
||||
* \brief Redraw map
|
||||
*/
|
||||
void redraw() override;
|
||||
|
||||
//@}
|
||||
|
||||
/** \name CTopographicMapDrawable implementation */
|
||||
//@{
|
||||
|
||||
/**
|
||||
* \brief Get matrix of sample points coordinates (places where to interpolate values)
|
||||
* \return Pointer to matrix of sample points coordinates
|
||||
*/
|
||||
CMatrix* getSampleCoordinatesMatrix() override;
|
||||
|
||||
/**
|
||||
* \brief Set matrix of sample points values (values interpolated at places specified in sample coordinates matrix)
|
||||
* \param [in] matrix Pointer to matrix of sample points values
|
||||
* \return True if values were successfully set, false otherwise
|
||||
*/
|
||||
bool setSampleValuesMatrix(CMatrix* matrix) override;
|
||||
|
||||
//@}
|
||||
|
||||
/**
|
||||
* \brief Get pointers to plugin main widget and (optional) toolbar widget
|
||||
* \param [out] widget Pointer to main widget
|
||||
* \param [out] toolbar Pointer to (optional) toolbar widget
|
||||
*/
|
||||
void getWidgets(GtkWidget*& widget, GtkWidget*& toolbar) const;
|
||||
|
||||
/**
|
||||
* \brief Get ID of current view
|
||||
* \return ID of current view
|
||||
*/
|
||||
EView getCurrentView() const { return m_currentView; }
|
||||
|
||||
/** \name Callbacks */
|
||||
//@{
|
||||
|
||||
void resizeCB(size_t /*width*/, size_t /*height*/) { m_needResize = true; }
|
||||
void toggleElectrodesCB();
|
||||
void setProjectionCB(GtkWidget* widget);
|
||||
void setViewCB(GtkWidget* widget);
|
||||
void setInterpolationCB(GtkWidget* widget);
|
||||
void setDelayCB(const double delay) const { m_mapDatabase.setDelay(delay); }
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
//draw color palette
|
||||
void drawPalette(size_t x, size_t y, size_t width, size_t height) const;
|
||||
|
||||
//draw face (ears, nose, neck)
|
||||
void drawFace(size_t x, size_t y, size_t width, size_t height) const;
|
||||
|
||||
//draw head
|
||||
void drawHead() const;
|
||||
|
||||
//draw RGB buffer
|
||||
void drawPotentials() const;
|
||||
|
||||
//draw electrodes corresponding to visible channels as rings
|
||||
void drawElectrodes() const;
|
||||
|
||||
/**
|
||||
* \brief Get channel position in 2D
|
||||
* \param index[in] Index of channel which position is to be retrieved
|
||||
* \param x[out] X coordinate of channel location, if channel is visible
|
||||
* \param y[out] Y coordinate of channel location, if channel is visible
|
||||
* \return True if channel is visible in current view, false otherwise
|
||||
*/
|
||||
bool getChannel2DPosition(size_t index, gint& x, gint& y) const;
|
||||
|
||||
//update RGB buffer with interpolated values
|
||||
void refreshPotentials();
|
||||
|
||||
//draw a box in RGB buffer
|
||||
void drawBoxToBuffer(size_t x, size_t y, size_t width, size_t height, uint8_t red, uint8_t green, uint8_t blue) const;
|
||||
|
||||
void enableElectrodeButtonSignals(bool enable);
|
||||
void enableProjectionButtonSignals(bool enable);
|
||||
void enableViewButtonSignals(bool enable);
|
||||
void enableInterpolationButtonSignals(bool enable);
|
||||
|
||||
/**
|
||||
* \brief Compute normalized coordinates of 2D samples
|
||||
* \remarks This method should first be called with bComputeCoordinates = false, allowing caller
|
||||
* to resize data structures appropriately, and then it may be called with bComputeCoordinates = true
|
||||
* \param all If false, this method only computes the number of visible samples
|
||||
* \return Number of visible samples (samples lying within the actual skull area)
|
||||
*/
|
||||
size_t computeSamplesNormalizedCoordinates(bool all);
|
||||
|
||||
void resizeData();
|
||||
|
||||
void redrawClipmask();
|
||||
|
||||
double getThetaFromCartesianCoordinates(const std::array<double, 3>& cartesian) const;
|
||||
|
||||
double getPhiFromCartesianCoordinates(const std::array<double, 3>& cartesian) const;
|
||||
|
||||
bool compute2DCoordinates(double theta, double phi, size_t skullCenterX, size_t skullCenterY, gint& x, gint& y) const;
|
||||
|
||||
//! The database that contains the information to use to draw the signals
|
||||
CTopographicMapDatabase& m_mapDatabase;
|
||||
|
||||
//Maximum delay that can be applied to displayed data
|
||||
double m_maxDelay = 2.0;
|
||||
|
||||
GtkBuilder* m_builderInterface = nullptr;
|
||||
|
||||
GtkWidget* m_drawingArea = nullptr;
|
||||
GdkBitmap* m_clipmask = nullptr; //origin (m_skullX, m_skullY)
|
||||
size_t m_clipmaskWidth = 0;
|
||||
size_t m_clipmaskHeight = 0;
|
||||
GdkGC* m_clipmaskGC = nullptr;
|
||||
GdkRegion* m_visibleRegion = nullptr; //reallocated whenever clipmask changes
|
||||
|
||||
GdkColor m_bgColor;
|
||||
|
||||
//! Active projection
|
||||
EProjection m_currentProjection = EProjection::Radial;
|
||||
//! Projection radio buttons
|
||||
GtkRadioToolButton* m_axialProjectionButton = nullptr;
|
||||
GtkRadioToolButton* m_radialProjectionButton = nullptr;
|
||||
|
||||
//! Active view
|
||||
EView m_currentView = EView::Top;
|
||||
//! View radio buttons
|
||||
GtkRadioToolButton* m_topViewButton = nullptr;
|
||||
GtkRadioToolButton* m_leftViewButton = nullptr;
|
||||
GtkRadioToolButton* m_rightViewButton = nullptr;
|
||||
GtkRadioToolButton* m_backViewButton = nullptr;
|
||||
|
||||
//! Interpolation type
|
||||
EInterpolationType m_currentInterpolation = EInterpolationType::Laplacian;
|
||||
GtkRadioToolButton* m_mapPotentials = nullptr;
|
||||
GtkRadioToolButton* m_mapCurrents = nullptr;
|
||||
|
||||
//! Electrodes toggle button
|
||||
GtkToggleToolButton* m_electrodesToggleButton = nullptr;
|
||||
//! Electrodes toggle state
|
||||
bool m_electrodesToggledOn = true;
|
||||
|
||||
bool m_needResize = true;
|
||||
|
||||
size_t m_gridSize = 0, m_cellSize = 0;
|
||||
|
||||
CMatrix m_sampleCoordinatesMatrix;
|
||||
|
||||
std::vector<size_t> m_sampleValues;
|
||||
std::vector<std::pair<size_t, size_t>> m_sample2DCoordinates; //in skull coords
|
||||
|
||||
size_t m_minPaletteBarHeight = 10, m_maxPaletteBarHeight = 30;
|
||||
size_t m_headWindowWidth = 00, m_headWindowHeight = 00;
|
||||
size_t m_paletteWindowWidth = 00, m_paletteWindowHeight = 00;
|
||||
|
||||
size_t m_skullX = 0, m_skullY = 0, m_skullDiameter = 0;
|
||||
//angles relative to 3 o'clock position, CCW, in degrees
|
||||
float m_skullOutlineStartAngle = 0.0, m_skullOutlineEndAngle = 0.0;
|
||||
float m_skullFillStartAngle = 0.0, m_skullFillEndAngle = 0.0;
|
||||
|
||||
//determined from m_skullOutlineEndAngle
|
||||
size_t m_skullOutlineLeftPointX = 0, m_skullOutlineLeftPointY = 0;
|
||||
//determined from m_skullOutlineStartAngle
|
||||
size_t m_skullOutlineRightPointX = 0, m_skullOutlineRightPointY = 0;
|
||||
|
||||
//determined from m_skullFillEndAngle
|
||||
size_t m_skullFillLeftPointX = 0, m_skullFillLeftPointY = 0;
|
||||
//determined from m_skullFillStartAngle
|
||||
size_t m_skullFillRightPointX = 0, m_skullFillRightPointY = 0;
|
||||
|
||||
size_t m_skullFillBottomPointX = 0, m_skullFillBottomPointY = 0;
|
||||
|
||||
/////////////////////////////
|
||||
// TOP VIEW
|
||||
/////////////////////////////
|
||||
size_t m_noseY = 0;
|
||||
|
||||
/////////////////////////////
|
||||
// BOTTOM VIEW
|
||||
/////////////////////////////
|
||||
size_t m_leftNeckX = 0, m_leftNeckY = 0;
|
||||
size_t m_rightNeckX = 0, m_rightNeckY = 0;
|
||||
|
||||
//////////////////////////////////
|
||||
// LEFT/RIGHT VIEWS
|
||||
//////////////////////////////////
|
||||
/*
|
||||
+ A
|
||||
/
|
||||
/
|
||||
/
|
||||
+ B
|
||||
| C
|
||||
+----+ D
|
||||
|
|
||||
+ E
|
||||
*/
|
||||
size_t m_noseTopX = 0, m_noseTopY = 0; //A
|
||||
size_t m_noseBumpX = 0, m_noseBumpY = 0; //B
|
||||
size_t m_noseTipX = 0, m_noseTipY = 0; //C
|
||||
size_t m_noseBaseX = 0, m_noseBaseY = 0; //D
|
||||
size_t m_noseBottomX = 0, m_noseBottomY = 0; //E
|
||||
|
||||
/**
|
||||
* \brief Main pixmap
|
||||
* \remarks This pixmap is 32-bit aligned. Each row is m_rowStride wide, and the pixmap has the height of the DrawingArea's
|
||||
* window. It is pasted into the DrawingArea's window upon redraw
|
||||
*/
|
||||
//TODO
|
||||
//GdkPixmap* m_pixmap;
|
||||
|
||||
/**
|
||||
* \brief Skull pixmap
|
||||
* \remarks This pixmap is 32-bit aligned. Each row is m_rowStride wide, and the pixmap has m_skullDiameter rows.
|
||||
* It is pasted into the main pixmap everytime changes happen (window resizing, display options toggled on/off, etc)
|
||||
*/
|
||||
std::vector<guchar> m_skullRGBBuffer;
|
||||
size_t m_rowStride = 0;
|
||||
};
|
||||
} // namespace SimpleVisualization
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,283 @@
|
||||
#include "ovpCTopographicMapDatabase.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SimpleVisualization {
|
||||
|
||||
CTopographicMapDatabase::CTopographicMapDatabase(Toolkit::TBoxAlgorithm<IBoxAlgorithm>& plugin, Kernel::IAlgorithmProxy& interpolation)
|
||||
: CBufferDatabase(plugin), m_interpolation(interpolation)
|
||||
{
|
||||
//map input parameters
|
||||
//--------------------
|
||||
|
||||
//spline order
|
||||
m_interpolation.getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SplineOrder)->setReferenceTarget(&m_splineOrder);
|
||||
//number of channels (or electrodes)
|
||||
m_interpolation.getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCount)->setReferenceTarget(&m_NElectrodes);
|
||||
//matrix of pointers to electrode coordinates
|
||||
m_pElectrodeCoords = &m_electrodeCoords;
|
||||
m_interpolation.getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCoordinates)->setReferenceTarget(
|
||||
&m_pElectrodeCoords);
|
||||
//matrix of potentials measured at each electrode
|
||||
m_pElectrodePotentials = &m_electrodePotentials;
|
||||
m_interpolation.getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsValues)->setReferenceTarget(
|
||||
&m_pElectrodePotentials);
|
||||
//matrix holding sample coordinates mapped at runtime (its size is not known a priori and may vary)
|
||||
//
|
||||
|
||||
//map output parameters
|
||||
//---------------------
|
||||
m_minSamplePointValue.initialize(m_interpolation.getOutputParameter(OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_MinSamplePointValue));
|
||||
m_maxSamplePointValue.initialize(m_interpolation.getOutputParameter(OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_MaxSamplePointValue));
|
||||
}
|
||||
|
||||
void CTopographicMapDatabase::setMatrixDimensionSize(const size_t index, const size_t size)
|
||||
{
|
||||
CBufferDatabase::setMatrixDimensionSize(index, size);
|
||||
|
||||
if (index == 0) { m_electrodePotentials.resize(size_t(m_NElectrodes)); }
|
||||
}
|
||||
|
||||
bool CTopographicMapDatabase::onChannelLocalisationBufferReceived(const size_t bufferIndex)
|
||||
{
|
||||
CBufferDatabase::onChannelLocalisationBufferReceived(bufferIndex);
|
||||
|
||||
if (!m_ChannelLookupTableInitialized || m_channelLocalisationCoords.empty() || m_NElectrodes == 0)
|
||||
{
|
||||
m_ParentPlugin.getLogManager() << Kernel::LogLevel_Warning
|
||||
<< "Channel localisation buffer received before channel lookup table was initialized! Can't process buffer!\n";
|
||||
}
|
||||
|
||||
//static electrode coordinates
|
||||
if (!m_dynamicChannelLocalisation)
|
||||
{
|
||||
//if streamed coordinates are cartesian
|
||||
if (m_cartesianCoords)
|
||||
{
|
||||
//fill electrode coordinates matrix
|
||||
m_electrodeCoords.resize(size_t(3 * m_NElectrodes));
|
||||
const double* coords = m_channelLocalisationCoords[0].first->getBuffer();
|
||||
for (size_t i = 0; i < size_t(m_NElectrodes); ++i)
|
||||
{
|
||||
const size_t lookupIdx = m_ChannelLookupIndices[i];
|
||||
m_electrodeCoords[3 * i] = *(coords + 3 * lookupIdx);
|
||||
m_electrodeCoords[3 * i + 1] = *(coords + 3 * lookupIdx + 1);
|
||||
m_electrodeCoords[3 * i + 2] = *(coords + 3 * lookupIdx + 2);
|
||||
}
|
||||
|
||||
//electrode coordinates initialized : it is now possible to interpolate potentials
|
||||
m_electrodeCoordsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTopographicMapDatabase::getLastBufferInterpolatedMinMaxValue(double& min, double& max) const
|
||||
{
|
||||
min = m_minSamplePointValue;
|
||||
max = m_maxSamplePointValue;
|
||||
}
|
||||
|
||||
bool CTopographicMapDatabase::processValues()
|
||||
{
|
||||
//wait for electrode coordinates
|
||||
if (!m_electrodeCoordsInitialized) { return true; }
|
||||
|
||||
if (m_firstProcess)
|
||||
{
|
||||
//done in CBufferDatabase::setMatrixBuffer
|
||||
//initialize the drawable object
|
||||
//m_Drawable->init();
|
||||
|
||||
if (!checkElectrodeCoordinates()) { return false; }
|
||||
|
||||
//precompute sin/cos tables
|
||||
m_interpolation.activateInputTrigger(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_PrecomputeTables, true);
|
||||
|
||||
m_firstProcess = false;
|
||||
}
|
||||
|
||||
//retrieve electrode values
|
||||
//determine what buffer to use from delay
|
||||
size_t bufferIdx = 0;
|
||||
const uint64_t currentTime = m_ParentPlugin.getPlayerContext().getCurrentTime();
|
||||
const uint64_t displayTime = currentTime - m_delay;
|
||||
getBufferIndexFromTime(displayTime, bufferIdx);
|
||||
|
||||
//determine what sample to use
|
||||
size_t sampleIdx;
|
||||
if (displayTime <= m_StartTime[bufferIdx]) { sampleIdx = 0; }
|
||||
else if (displayTime >= m_EndTime[bufferIdx]) { sampleIdx = m_DimSizes[1] - 1; }
|
||||
else { sampleIdx = size_t(double(displayTime - m_StartTime[bufferIdx]) / double(m_BufferDuration) * m_DimSizes[1]); }
|
||||
|
||||
for (int64_t i = 0; i < m_NElectrodes; ++i) { *(m_electrodePotentials.getBuffer() + i) = m_SampleBuffers[bufferIdx][i * m_DimSizes[1] + sampleIdx]; }
|
||||
|
||||
//interpolate spline values (potentials)
|
||||
if (m_interpolationType == EInterpolationType::Spline)
|
||||
{
|
||||
m_interpolation.activateInputTrigger(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_ComputeSplineCoefs, true);
|
||||
}
|
||||
else //interpolate spline laplacian (currents)
|
||||
{
|
||||
m_interpolation.activateInputTrigger(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_ComputeLaplacianCoefs, true);
|
||||
}
|
||||
|
||||
//retrieve up-to-date pointer to sample matrix
|
||||
m_samplePointCoords = dynamic_cast<CTopographicMapDrawable*>(m_Drawable)->getSampleCoordinatesMatrix();
|
||||
|
||||
if (m_samplePointCoords != nullptr)
|
||||
{
|
||||
//map pointer to input parameter
|
||||
m_interpolation.getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SamplePointsCoordinates)->
|
||||
setReferenceTarget(&m_samplePointCoords);
|
||||
|
||||
if (m_interpolationType == EInterpolationType::Spline)
|
||||
{
|
||||
m_interpolation.activateInputTrigger(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateSpline, true);
|
||||
}
|
||||
else { m_interpolation.activateInputTrigger(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateLaplacian, true); }
|
||||
}
|
||||
|
||||
m_interpolation.process();
|
||||
bool process = true;
|
||||
if (m_interpolation.isOutputTriggerActive(OVP_Algorithm_SphericalSplineInterpolation_OutputTriggerId_Error))
|
||||
{
|
||||
m_ParentPlugin.getLogManager() << Kernel::LogLevel_Warning << "An error occurred while interpolating potentials!\n";
|
||||
process = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_samplePointCoords != nullptr)
|
||||
{
|
||||
//retrieve interpolation results
|
||||
Kernel::TParameterHandler<CMatrix*> sampleValuesMatrix;
|
||||
sampleValuesMatrix.initialize(
|
||||
m_interpolation.getOutputParameter(OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_SamplePointsValues));
|
||||
dynamic_cast<CTopographicMapDrawable*>(m_Drawable)->setSampleValuesMatrix(sampleValuesMatrix);
|
||||
|
||||
//tells the drawable to redraw itself since the signal information has been updated
|
||||
m_Drawable->redraw();
|
||||
}
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
bool CTopographicMapDatabase::setDelay(const double delay)
|
||||
{
|
||||
if (delay > m_TotalDuration) { return false; }
|
||||
|
||||
//convert delay to 32:32 format
|
||||
m_delay = int64_t(delay * (1LL << 32)); // $$$ Casted in (int64_t) because of Ubuntu 7.10 crash !
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTopographicMapDatabase::interpolateValues()
|
||||
{
|
||||
//can't interpolate before first buffer has been received
|
||||
if (m_firstProcess) { return false; }
|
||||
|
||||
//retrieve up-to-date pointer to sample matrix
|
||||
m_samplePointCoords = dynamic_cast<CTopographicMapDrawable*>(m_Drawable)->getSampleCoordinatesMatrix();
|
||||
|
||||
if (m_samplePointCoords != nullptr)
|
||||
{
|
||||
//map pointer to input parameter
|
||||
m_interpolation.getInputParameter(OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SamplePointsCoordinates)->
|
||||
setReferenceTarget(&m_samplePointCoords);
|
||||
|
||||
//interpolate using spline or laplacian coefficients depending on interpolation mode
|
||||
if (m_interpolationType == EInterpolationType::Spline)
|
||||
{
|
||||
m_interpolation.activateInputTrigger(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateSpline, true);
|
||||
}
|
||||
else { m_interpolation.activateInputTrigger(OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateLaplacian, true); }
|
||||
}
|
||||
|
||||
m_interpolation.process();
|
||||
|
||||
if (m_interpolation.isOutputTriggerActive(OVP_Algorithm_SphericalSplineInterpolation_OutputTriggerId_Error))
|
||||
{
|
||||
m_ParentPlugin.getLogManager() << Kernel::LogLevel_Warning << "An error occurred while interpolating potentials!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_samplePointCoords != nullptr)
|
||||
{
|
||||
//retrieve interpolation results
|
||||
Kernel::TParameterHandler<CMatrix*> sampleValuesMatrix;
|
||||
sampleValuesMatrix.initialize(m_interpolation.getOutputParameter(OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_SamplePointsValues));
|
||||
dynamic_cast<CTopographicMapDrawable*>(m_Drawable)->setSampleValuesMatrix(sampleValuesMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTopographicMapDatabase::getBufferIndexFromTime(const uint64_t time, size_t& bufferIndex)
|
||||
{
|
||||
if (m_SampleBuffers.empty()) { return false; }
|
||||
|
||||
if (time < m_StartTime[0])
|
||||
{
|
||||
bufferIndex = 0;
|
||||
return false;
|
||||
}
|
||||
if (time > m_EndTime.back())
|
||||
{
|
||||
bufferIndex = size_t(m_SampleBuffers.size() - 1);
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < m_SampleBuffers.size(); ++i)
|
||||
{
|
||||
if (time <= m_EndTime[i])
|
||||
{
|
||||
bufferIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTopographicMapDatabase::checkElectrodeCoordinates()
|
||||
{
|
||||
const size_t nChannel = getChannelCount();
|
||||
|
||||
for (size_t i = 0; i < nChannel; ++i)
|
||||
{
|
||||
double* normalizedChannelCoords = nullptr;
|
||||
if (!getChannelPosition(i, normalizedChannelCoords))
|
||||
{
|
||||
CString channelLabel;
|
||||
getChannelLabel(i, channelLabel);
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error
|
||||
<< "Couldn't retrieve coordinates of electrode #" << i
|
||||
<< "(" << channelLabel << "), aborting model frame electrode coordinates computation\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
#define MY_THRESHOLD 0.01
|
||||
if (fabs(normalizedChannelCoords[0] * normalizedChannelCoords[0] +
|
||||
normalizedChannelCoords[1] * normalizedChannelCoords[1] +
|
||||
normalizedChannelCoords[2] * normalizedChannelCoords[2] - 1.) > MY_THRESHOLD)
|
||||
#undef MY_THRESHOLD
|
||||
{
|
||||
CString channelLabel;
|
||||
getChannelLabel(i, channelLabel);
|
||||
m_ParentPlugin.getBoxAlgorithmContext()->getPlayerContext()->getLogManager() << Kernel::LogLevel_Error
|
||||
<< "Coordinates of electrode #" << i << "(" << channelLabel
|
||||
<< "), are not normalized, aborting model frame electrode coordinates computation\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace SimpleVisualization
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../ovp_defines.h"
|
||||
|
||||
#include "ovpCBufferDatabase.h"
|
||||
|
||||
#include <openvibe/ov_all.h>
|
||||
#include <toolkit/ovtk_all.h>
|
||||
|
||||
namespace OpenViBE {
|
||||
namespace Plugins {
|
||||
namespace SimpleVisualization {
|
||||
class CTopographicMapDrawable : public CSignalDisplayDrawable
|
||||
{
|
||||
public:
|
||||
~CTopographicMapDrawable() override = default;
|
||||
virtual CMatrix* getSampleCoordinatesMatrix() = 0;
|
||||
virtual bool setSampleValuesMatrix(CMatrix* pSampleValuesMatrix) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is used to store information about the incoming signal stream. It can request a CSignalDisplayDrawable
|
||||
* object to redraw himself in case of some changes in its data.
|
||||
*/
|
||||
class CTopographicMapDatabase : public CBufferDatabase
|
||||
{
|
||||
public:
|
||||
CTopographicMapDatabase(Toolkit::TBoxAlgorithm<IBoxAlgorithm>& plugin, Kernel::IAlgorithmProxy& interpolation);
|
||||
~CTopographicMapDatabase() override = default;
|
||||
|
||||
void setMatrixDimensionSize(const size_t index, const size_t size) override;
|
||||
|
||||
/**
|
||||
* \brief Callback called upon channel localisation buffer reception
|
||||
* \param bufferIndex Index of newly received channel localisation buffer
|
||||
* \return True if buffer data was correctly processed, false otherwise
|
||||
*/
|
||||
bool onChannelLocalisationBufferReceived(const size_t bufferIndex) override;
|
||||
|
||||
bool setDelay(double delay);
|
||||
|
||||
/**
|
||||
* \brief Set interpolation type
|
||||
* Spline values (potentials) can be interpolated directly, but the spline laplacian (currents) may
|
||||
* be used as well
|
||||
* \sa OVP_TypeId_SphericalLinearInterpolationType enumeration
|
||||
*/
|
||||
void setInterpolationType(const EInterpolationType type) { m_interpolationType = type; }
|
||||
|
||||
bool processValues();
|
||||
|
||||
bool interpolateValues();
|
||||
|
||||
//! Returns min/max interpolated values using the last buffer arrived (all channels taken into account)
|
||||
void getLastBufferInterpolatedMinMaxValue(double& min, double& max) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Looks for buffer whose timeframe contains time passed as parameter
|
||||
* \param time [in] Time of buffer to be retrieved
|
||||
* \param bufferIndex [out] Index of buffer closest to time passed as parameter
|
||||
* \return True if time passed as parameter lies within a buffer's timeframe, false otherwise
|
||||
*/
|
||||
bool getBufferIndexFromTime(uint64_t time, size_t& bufferIndex);
|
||||
|
||||
/**
|
||||
* \brief Ensure electrode coordinates are normalized
|
||||
* \return True if all electrode coordinates are normalized, false otherwise
|
||||
*/
|
||||
bool checkElectrodeCoordinates();
|
||||
|
||||
//true until process() is called for the first time
|
||||
bool m_firstProcess = true;
|
||||
//spherical spline interpolation
|
||||
Kernel::IAlgorithmProxy& m_interpolation;
|
||||
//order of spherical spline used for interpolation - mapped to OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SplineOrder
|
||||
int64_t m_splineOrder = 4;
|
||||
/**
|
||||
* \brief Type of interpolation
|
||||
* \sa OVP_TypeId_SphericalLinearInterpolationType enumeration
|
||||
*/
|
||||
EInterpolationType m_interpolationType = EInterpolationType::Spline;
|
||||
//number of electrodes (see CBufferDatabase) - mapped to OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCount
|
||||
//int64_t m_NElectrodes = 0;
|
||||
//flag set to true once electrode coordinates have been initialized
|
||||
bool m_electrodeCoordsInitialized = false;
|
||||
//electrode cartesian coordinates, in normalized space (X right Y front Z up)
|
||||
CMatrix m_electrodeCoords;
|
||||
//pointer to electrode coordinates matrix - mapped to OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCoordinates
|
||||
CMatrix* m_pElectrodeCoords = nullptr;
|
||||
//electrode potentials
|
||||
CMatrix m_electrodePotentials;
|
||||
//pointer to electrode potentials matrix - mapped to OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsValues
|
||||
CMatrix* m_pElectrodePotentials = nullptr;
|
||||
//pointer to sample points coordinates matrix - mapped to OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SamplePointsCoordinates
|
||||
CMatrix* m_samplePointCoords = nullptr;
|
||||
//minimum interpolated value
|
||||
Kernel::TParameterHandler<double> m_minSamplePointValue;
|
||||
//maximum interpolated value
|
||||
Kernel::TParameterHandler<double> m_maxSamplePointValue;
|
||||
//delay to apply to interpolated values
|
||||
uint64_t m_delay = 0;
|
||||
};
|
||||
} // namespace SimpleVisualization
|
||||
} // namespace Plugins
|
||||
} // namespace OpenViBE
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
// Boxes
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_ClassId_BoxAlgorithm_LevelMeasure OpenViBE::CIdentifier(0x657138E4, 0x46D6586F)
|
||||
#define OVP_ClassId_BoxAlgorithm_LevelMeasureDesc OpenViBE::CIdentifier(0x4D061428, 0x11B02233)
|
||||
#define OVP_ClassId_Algorithm_LevelMeasure OpenViBE::CIdentifier(0x63C71764, 0x34A9717F)
|
||||
#define OVP_ClassId_Algorithm_LevelMeasureDesc OpenViBE::CIdentifier(0x3EB6754F, 0x22FB1722)
|
||||
#define OVP_ClassId_Algorithm_SphericalSplineInterpolation OpenViBE::CIdentifier(0x4F112803, 0x661D4029)
|
||||
#define OVP_ClassId_Algorithm_SphericalSplineInterpolationDesc OpenViBE::CIdentifier(0x00D67A20, 0x3D3D4729)
|
||||
#define OVP_ClassId_SignalDisplay OpenViBE::CIdentifier(0x0055BE5F, 0x087BDD12)
|
||||
#define OVP_ClassId_SignalDisplayDesc OpenViBE::CIdentifier(0x00C4F2D5, 0x58810276)
|
||||
#define OVP_ClassId_GrazVisualization OpenViBE::CIdentifier(0x00DD290D, 0x5F142820)
|
||||
#define OVP_ClassId_GrazVisualizationDesc OpenViBE::CIdentifier(0x00F1955D, 0x38813A6A)
|
||||
#define OVP_ClassId_PowerSpectrumDisplay OpenViBE::CIdentifier(0x004C0EA4, 0x713EC6D9)
|
||||
#define OVP_ClassId_PowerSpectrumDisplayDesc OpenViBE::CIdentifier(0x00116B40, 0x69E1B00D)
|
||||
#define OVP_ClassId_TopographicMap2DDisplay OpenViBE::CIdentifier(0x0B104632, 0x451C265F)
|
||||
#define OVP_ClassId_TopographicMap2DDisplayDesc OpenViBE::CIdentifier(0x7154037A, 0x4BC52A9F)
|
||||
#define OVP_ClassId_Simple3DDisplay OpenViBE::CIdentifier(0x31A00483, 0x35924E6B)
|
||||
#define OVP_ClassId_Simple3DDisplayDesc OpenViBE::CIdentifier(0x443E145F, 0x77205DA0)
|
||||
#define OVP_ClassId_TopographicMap3DDisplay OpenViBE::CIdentifier(0x36F95BE4, 0x0EF06290)
|
||||
#define OVP_ClassId_TopographicMap3DDisplayDesc OpenViBE::CIdentifier(0x6AD52C48, 0x6E1C1746)
|
||||
#define OVP_ClassId_VoxelDisplay OpenViBE::CIdentifier(0x76E42EA2, 0x66FB5265)
|
||||
#define OVP_ClassId_VoxelDisplayDesc OpenViBE::CIdentifier(0x79321659, 0x642D3D0C)
|
||||
#define OVP_ClassId_TimeFrequencyMapDisplay OpenViBE::CIdentifier(0x3AE63330, 0x76532117)
|
||||
#define OVP_ClassId_TimeFrequencyMapDisplayDesc OpenViBE::CIdentifier(0x1BAE74F3, 0x20FB7C89)
|
||||
#define OVP_ClassId_BoxAlgorithm_P300IdentifierCardVisualisation OpenViBE::CIdentifier(0x3AF7FF20, 0xA68745DB)
|
||||
#define OVP_ClassId_BoxAlgorithm_P300IdentifierCardVisualisationDesc OpenViBE::CIdentifier(0x84F146EF, 0x4AA712A4)
|
||||
#define OVP_ClassId_BoxAlgorithm_MatrixDisplay OpenViBE::CIdentifier(0x54F0796D, 0x3EDE2CC0)
|
||||
#define OVP_ClassId_BoxAlgorithm_MatrixDisplayDesc OpenViBE::CIdentifier(0x63AB4BA7, 0x022C1524)
|
||||
|
||||
|
||||
// Type definitions
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#define OVP_TypeId_SphericalLinearInterpolationType OpenViBE::CIdentifier(0x44B76D9E, 0x618229BC)
|
||||
#define OVP_TypeId_SignalDisplayMode OpenViBE::CIdentifier(0x5DE046A6, 0x086340AA)
|
||||
|
||||
enum class EInterpolationType { Spline = 1, Laplacian = 2 };
|
||||
|
||||
enum class ESignalDisplayMode { Scroll, Scan };
|
||||
|
||||
enum class EDisplayMode { ZoomIn, ZoomOut, GlobalBestFit };
|
||||
|
||||
// Global defines
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
#ifdef TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
#include "ovp_global_defines.h"
|
||||
#endif // TARGET_HAS_ThirdPartyOpenViBEPluginsGlobalDefines
|
||||
|
||||
#define OVP_Algorithm_LevelMeasure_InputParameterId_Matrix OpenViBE::CIdentifier(0x59430053, 0x67C23A83)
|
||||
#define OVP_Algorithm_LevelMeasure_OutputParameterId_MainWidget OpenViBE::CIdentifier(0x101C4641, 0x466C71E3)
|
||||
#define OVP_Algorithm_LevelMeasure_OutputParameterId_ToolbarWidget OpenViBE::CIdentifier(0x14905FFC, 0x6FE425B2)
|
||||
#define OVP_Algorithm_LevelMeasure_InputTriggerId_Reset OpenViBE::CIdentifier(0x3EAF36C5, 0x74490C56)
|
||||
#define OVP_Algorithm_LevelMeasure_InputTriggerId_Refresh OpenViBE::CIdentifier(0x71356FE4, 0x3E8F62DC)
|
||||
#define OVP_Algorithm_LevelMeasure_OutputTriggerId_Refreshed OpenViBE::CIdentifier(0x3C3C1B06, 0x360305D9)
|
||||
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SplineOrder OpenViBE::CIdentifier(0x3B8200F6, 0x205162C7)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCount OpenViBE::CIdentifier(0x2ABF11FC, 0x174A2CFE)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsCoordinates OpenViBE::CIdentifier(0x36F743FE, 0x37897AB9)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_ControlPointsValues OpenViBE::CIdentifier(0x4EA55599, 0x670274A7)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputParameterId_SamplePointsCoordinates OpenViBE::CIdentifier(0x280A531D, 0x339C18AA)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_SamplePointsValues OpenViBE::CIdentifier(0x12D0319C, 0x51ED4D8B)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_MinSamplePointValue OpenViBE::CIdentifier(0x0CEE2041, 0x79455EED)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_OutputParameterId_MaxSamplePointValue OpenViBE::CIdentifier(0x1ECB03E3, 0x40EF757F)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_PrecomputeTables OpenViBE::CIdentifier(0x42A650DA, 0x62B35F76)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_ComputeSplineCoefs OpenViBE::CIdentifier(0x5B353712, 0x069F3D3B)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_ComputeLaplacianCoefs OpenViBE::CIdentifier(0x7D8C545E, 0x7C086660)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateSpline OpenViBE::CIdentifier(0x1241610E, 0x03CB1AD9)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_InputTriggerId_InterpolateLaplacian OpenViBE::CIdentifier(0x11CE0AC3, 0x0FD85469)
|
||||
#define OVP_Algorithm_SphericalSplineInterpolation_OutputTriggerId_Error OpenViBE::CIdentifier(0x08CB0679, 0x3A6F3C3A)
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "ovp_defines.h"
|
||||
|
||||
#include "algorithms/ovpCAlgorithmSphericalSplineInterpolation.h"
|
||||
|
||||
#include "box-algorithms/ovpCBoxAlgorithmTopographicMap2DDisplay.h"
|
||||
#include "box-algorithms/ovpCBoxAlgorithmMatrixDisplay.h"
|
||||
|
||||
OVP_Declare_Begin()
|
||||
context.getTypeManager().registerEnumerationType(OVP_TypeId_SphericalLinearInterpolationType, "Spherical linear interpolation type");
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SphericalLinearInterpolationType, "Spline (potentials)", size_t(EInterpolationType::Spline));
|
||||
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_SphericalLinearInterpolationType, "Spline laplacian (currents)",
|
||||
size_t(EInterpolationType::Laplacian));
|
||||
|
||||
OVP_Declare_New(OpenViBE::Plugins::Test::CAlgorithmSphericalSplineInterpolationDesc)
|
||||
|
||||
OVP_Declare_New(OpenViBE::Plugins::SimpleVisualization::CBoxAlgorithmTopographicMap2DDisplayDesc)
|
||||
OVP_Declare_New(OpenViBE::Plugins::SimpleVisualization::CBoxAlgorithmMatrixDisplayDesc)
|
||||
|
||||
|
||||
OVP_Declare_End()
|
||||