This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,553 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "mCBoxAlgorithmViz.hpp"
// OpenGL 1.2
#ifndef GL_BGRA
# define GL_BGRA 0x80E1
#endif // GL_BGRA
namespace OpenViBE {
namespace AdvancedVisualization {
template <int i>
class toolbar_sort_changed_
{
public:
static void callback(GtkButton* /*button*/, CBoxAlgorithmViz* box)
{
box->m_RendererCtx->sortSelectedChannel(i);
box->m_RedrawNeeded = true;
}
};
static void ChannelSelectionChanged(GtkTreeSelection* selection, CRendererContext* ctx)
{
size_t i = 0;
GtkTreeView* treeView = gtk_tree_selection_get_tree_view(selection);
GtkTreeModel* treeModel = gtk_tree_view_get_model(treeView);
if (treeModel != nullptr)
{
GtkTreeIter iter;
if (gtk_tree_model_get_iter_first(treeModel, &iter) != 0)
{
do
{
if (gtk_tree_selection_iter_is_selected(selection, &iter) != 0) { ctx->selectChannel(i); }
else { ctx->unselectChannel(i); }
i++;
} while (gtk_tree_model_iter_next(treeModel, &iter) != 0);
}
}
}
static void SpinbuttonTimeScaleChangeValueCB(GtkSpinButton* button, CRendererContext* ctx)
{
ctx->setTimeScale(size_t(gtk_spin_button_get_value(button) * (1LL << 32)));
}
static void SpinbuttonElementCountChangeValueCB(GtkSpinButton* button, CRendererContext* ctx)
{
ctx->setElementCount(size_t(gtk_spin_button_get_value(button)));
}
static void CheckbuttonPositiveToggledCB(GtkToggleButton* button, CRendererContext* ctx) { ctx->setPositiveOnly(gtk_toggle_button_get_active(button) != 0); }
static void CheckbuttonShowScaleToggledCB(GtkToggleButton* button, CRendererContext* ctx)
{
ctx->setScaleVisibility(gtk_toggle_button_get_active(button) != 0);
}
static void ButtonVideoRecordingPressedCB(GtkButton* button, CBoxAlgorithmViz* box)
{
box->m_IsVideoOutputWorking = !box->m_IsVideoOutputWorking;
gtk_button_set_label(button, box->m_IsVideoOutputWorking ? GTK_STOCK_MEDIA_PAUSE : GTK_STOCK_MEDIA_RECORD);
}
static void RangeERPValueChangedCB(GtkRange* range, GtkLabel* label)
{
char str[1024];
sprintf(str, "%.02f%%", gtk_range_get_value(range) * 100);
gtk_label_set_text(label, str);
getContext().stepERPFractionBy(float(gtk_range_get_value(range)) - getContext().getERPFraction());
}
static void ButtonERPPlayPausePressedCB(GtkButton* /*button*/, CRendererContext* ctx) { ctx->setERPPlayerActive(!ctx->isERPPlayerActive()); }
static void SpinbuttonFreqBandMinChangeValueCB(GtkSpinButton* button, CRendererContext* ctx)
{
ctx->setMinimumSpectrumFrequency(size_t(gtk_spin_button_get_value(button)));
}
static void SpinbuttonFreqBandMaxChangeValueCB(GtkSpinButton* button, CRendererContext* ctx)
{
ctx->setMaximumSpectrumFrequency(size_t(gtk_spin_button_get_value(button)));
}
//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
bool CBoxAlgorithmViz::initialize()
{
m_RendererCtx = new CRendererContext();
m_SubRendererCtx = new CRendererContext();
// Sets default setting values
m_Colors.clear();
m_Localisation = CString("");
m_TemporalCoherence = ETemporalCoherence::TimeLocked;
m_NElement = 50;
m_TimeScale = 10LL << 32;
m_IsPositive = false;
m_IsTimeLocked = true;
m_IsScaleVisible = true;
m_TextureID = 0;
m_lastProcessTime = 0;
m_Time1 = 0;
m_Time2 = 0;
CString color = CString("100,100,100");
m_ColorGradient = CString("0:0,0,0; 100:100,100,100");
m_XYZPlotHasDepth = false;
m_DataScale = 1;
m_Translucency = 1;
// Initializes fast forward behavior
m_fastForwardMaxFactorHD = float(this->getConfigurationManager().expandAsFloat("${AdvancedViz_HighDefinition_FastForwardFactor}", 5.F));
m_fastForwardMaxFactorLD = float(this->getConfigurationManager().expandAsFloat("${AdvancedViz_LowDefinition_FastForwardFactor}", 20.F));
// Gets data stream type
this->getStaticBoxContext().getInputType(0, m_TypeID);
// Prepares GUI
m_Builder = gtk_builder_new();
gtk_builder_add_from_file(m_Builder, std::string(Directories::getDataDir() + "/plugins/advanced-visualization.ui").c_str(), nullptr);
GtkWidget* main = GTK_WIDGET(gtk_builder_get_object(m_Builder, "table"));
GtkWidget* toolbar = GTK_WIDGET(gtk_builder_get_object(m_Builder, "toolbar-window"));
m_Viewport = GTK_WIDGET(gtk_builder_get_object(m_Builder, "viewport"));
m_Top = GTK_WIDGET(gtk_builder_get_object(m_Builder, "label_top"));
m_Left = GTK_WIDGET(gtk_builder_get_object(m_Builder, "drawingarea_left"));
m_Right = GTK_WIDGET(gtk_builder_get_object(m_Builder, "drawingarea_right"));
m_Bottom = GTK_WIDGET(gtk_builder_get_object(m_Builder, "drawingarea_bottom"));
m_CornerLeft = GTK_WIDGET(gtk_builder_get_object(m_Builder, "label_corner_left"));
m_CornerRight = GTK_WIDGET(gtk_builder_get_object(m_Builder, "label_corner_right"));
// Gets important widgets
m_TimeScaleW = GTK_WIDGET(gtk_builder_get_object(m_Builder, "spinbutton_time_scale"));
m_nElementW = GTK_WIDGET(gtk_builder_get_object(m_Builder, "spinbutton_element_count"));
m_ERPRange = GTK_WIDGET(gtk_builder_get_object(m_Builder, "range_erp"));
m_ERPPlayerButton = GTK_WIDGET(gtk_builder_get_object(m_Builder, "button_erp_play_pause"));
m_ERPPlayer = GTK_WIDGET(gtk_builder_get_object(m_Builder, "table_erp"));
m_ScaleVisible = GTK_WIDGET(gtk_builder_get_object(m_Builder, "checkbutton_show_scale"));
m_FrequencyBandMin = GTK_WIDGET(gtk_builder_get_object(m_Builder, "spinbutton_freq_band_min"));
m_FrequencyBandMax = GTK_WIDGET(gtk_builder_get_object(m_Builder, "spinbutton_freq_band_max"));
m_ChannelTreeView = GTK_TREE_VIEW(gtk_builder_get_object(m_Builder, "expander_select_treeview"));
m_ChannelListStore = GTK_LIST_STORE(gtk_builder_get_object(m_Builder, "liststore_select"));
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(m_ChannelTreeView), GTK_SELECTION_MULTIPLE);
// Sets default spectrum frequency range
gtk_spin_button_set_value(GTK_SPIN_BUTTON(m_FrequencyBandMin), 2);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(m_FrequencyBandMax), 48);
// Connects GTK signals
g_signal_connect(gtk_builder_get_object(m_Builder, "expander_sort_button_default"), "pressed", G_CALLBACK(toolbar_sort_changed_<1>::callback), this);
g_signal_connect(gtk_builder_get_object(m_Builder, "expander_sort_button_alpha"), "pressed", G_CALLBACK(toolbar_sort_changed_<2>::callback), this);
g_signal_connect(gtk_builder_get_object(m_Builder, "expander_sort_button_reversed"), "pressed", G_CALLBACK(toolbar_sort_changed_<3>::callback), this);
g_signal_connect(gtk_builder_get_object(m_Builder, "expander_sort_button_left_right"), "pressed", G_CALLBACK(toolbar_sort_changed_<4>::callback), this);
g_signal_connect(gtk_builder_get_object(m_Builder, "expander_sort_button_front_back"), "pressed", G_CALLBACK(toolbar_sort_changed_<5>::callback), this);
g_signal_connect(gtk_builder_get_object(m_Builder, "spinbutton_time_scale"), "value-changed", G_CALLBACK(SpinbuttonTimeScaleChangeValueCB), m_RendererCtx);
g_signal_connect(gtk_builder_get_object(m_Builder, "spinbutton_element_count"), "value-changed", G_CALLBACK(SpinbuttonElementCountChangeValueCB),
m_RendererCtx);
g_signal_connect(gtk_builder_get_object(m_Builder, "spinbutton_element_count"), "value-changed", G_CALLBACK(SpinbuttonElementCountChangeValueCB),
m_SubRendererCtx);
g_signal_connect(gtk_builder_get_object(m_Builder, "checkbutton_positive"), "toggled", G_CALLBACK(CheckbuttonPositiveToggledCB), m_RendererCtx);
g_signal_connect(gtk_builder_get_object(m_Builder, "checkbutton_show_scale"), "toggled", G_CALLBACK(CheckbuttonShowScaleToggledCB),
&OpenViBE::AdvancedVisualization::getContext());
g_signal_connect(gtk_builder_get_object(m_Builder, "button_erp_play_pause"), "pressed", G_CALLBACK(ButtonERPPlayPausePressedCB),
&OpenViBE::AdvancedVisualization::getContext());
g_signal_connect(gtk_builder_get_object(m_Builder, "button_video_recording"), "pressed", G_CALLBACK(ButtonVideoRecordingPressedCB), this);
g_signal_connect(gtk_builder_get_object(m_Builder, "range_erp"), "value-changed", G_CALLBACK(RangeERPValueChangedCB),
gtk_builder_get_object(m_Builder, "label_erp_progress"));
g_signal_connect(gtk_builder_get_object(m_Builder, "spinbutton_freq_band_min"), "value-changed", G_CALLBACK(SpinbuttonFreqBandMinChangeValueCB),
m_RendererCtx);
g_signal_connect(gtk_builder_get_object(m_Builder, "spinbutton_freq_band_max"), "value-changed", G_CALLBACK(SpinbuttonFreqBandMaxChangeValueCB),
m_RendererCtx);
g_signal_connect(gtk_tree_view_get_selection(m_ChannelTreeView), "changed", G_CALLBACK(ChannelSelectionChanged), m_RendererCtx);
// Hides unnecessary widgets
if (std::find(m_Parameters.begin(), m_Parameters.end(), S_DataScale) == m_Parameters.end())
{
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(m_Builder, "checkbutton_positive")));
}
if (std::find(m_Parameters.begin(), m_Parameters.end(), S_ChannelLocalisation) == m_Parameters.end())
{
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(m_Builder, "expander_sort")));
}
if (m_TypeID != OV_TypeId_Spectrum) { gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(m_Builder, "expander_freq_band"))); }
gtk_widget_hide(m_ERPPlayer);
// Prepares 3D View
m_GtkGLWidget.initialize(*this, m_Viewport, m_Left, m_Right, m_Bottom);
m_GtkGLWidget.setPointSmoothingActive(this->getConfigurationManager().expandAsBoolean("${AdvancedViz_SmoothPoint}", false));
// Fowards widgets to the OpenViBE viz context
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, main);
m_visualizationCtx->setToolbar(*this, toolbar);
// Parses box settings
size_t settingIndex = 0;
for (int iParameter : m_Parameters)
{
double value;
switch (iParameter)
{
case S_ChannelLocalisation:
m_Localisation = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
break;
case S_Caption:
m_Caption = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
break;
case S_Color:
color = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
break;
case S_ColorGradient:
m_ColorGradient = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
break;
case S_DataPositive:
m_IsPositive = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
break;
case S_TemporalCoherence:
m_TemporalCoherence = ETemporalCoherence(size_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex)));
break;
case S_TimeScale:
value = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
m_TimeScale = size_t(value * (1LL << 32));
break;
case S_ElementCount:
m_NElement = size_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex));
break;
case S_DataScale:
m_DataScale = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
break;
case S_FlowerRingCount:
m_NFlowerRing = size_t(FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex));
break;
case S_Translucency:
m_Translucency = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
break;
case S_ShowAxis:
m_ShowAxis = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
break;
case S_XYZPlotHasDepth:
m_XYZPlotHasDepth = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), settingIndex);
break;
case F_FixedChannelOrder:
settingIndex--;
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(m_Builder, "expander_sort")));
break;
case F_FixedChannelSelection:
settingIndex--;
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(m_Builder, "expander_select")));
break;
default:
settingIndex--;
break;
}
settingIndex++;
}
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(m_Builder, "checkbutton_positive")), gboolean(m_IsPositive));
// Parses color string
parseColor(m_Color, color.toASCIIString());
m_Colors.push_back(m_Color);
// Parses color string - special for instant oscilloscope which can have several inputs
for (size_t i = settingIndex; i < this->getStaticBoxContext().getSettingCount(); ++i)
{
color = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), i);
parseColor(m_Color, color.toASCIIString());
m_Colors.push_back(m_Color);
}
// Sets caption
if (m_Caption != CString("")) { gtk_label_set_text(GTK_LABEL(m_Top), std::string(m_Caption.toASCIIString()).c_str()); }
// Sets time scale
if (m_TemporalCoherence == ETemporalCoherence::TimeLocked)
{
gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(m_Builder, "spinbutton_time_scale")), (m_TimeScale >> 22) / 1024.);
m_IsTimeLocked = true;
}
else { gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(m_Builder, "vbox_time_scale"))); }
// Sets matrix count
if (m_TemporalCoherence == ETemporalCoherence::Independant)
{
gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(m_Builder, "spinbutton_element_count")), double(m_NElement));
m_IsTimeLocked = false;
}
else { gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(m_Builder, "vbox_element_count"))); }
// Shows / hides scale
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_ScaleVisible), gboolean(m_RendererCtx->getScaleVisibility()));
// Reads channel localisation
if (m_Localisation != CString(""))
{
Kernel::IAlgorithmProxy* channelPosReader = &this->getAlgorithmManager().getAlgorithm(
this->getAlgorithmManager().createAlgorithm(OVP_GD_ClassId_Algorithm_OVMatrixFileReader));
channelPosReader->initialize();
const Kernel::TParameterHandler<CString*> ip_filename(
channelPosReader->getInputParameter(OVP_GD_Algorithm_OVMatrixFileReader_InputParameterId_Filename));
Kernel::TParameterHandler<CMatrix*> op_matrix(channelPosReader->getOutputParameter(OVP_GD_Algorithm_OVMatrixFileReader_OutputParameterId_Matrix));
*ip_filename = m_Localisation;
channelPosReader->process();
if (op_matrix->getDimensionCount() != 2 || op_matrix->getDimensionSize(1) != 3)
{
this->getLogManager() << Kernel::LogLevel_Warning << "Invalid channel localisation file " << m_Localisation << "\n";
}
else
{
const size_t nChannel = op_matrix->getDimensionSize(0);
double* buffer = op_matrix->getBuffer();
for (size_t i = 0; i < nChannel; ++i)
{
std::string name = trim(op_matrix->getDimensionLabel(0, i));
std::transform(name.begin(), name.end(), name.begin(), tolower);
m_ChannelPositions[name] = CVertex(-buffer[1], buffer[2], -buffer[0]);
buffer += 3;
}
}
channelPosReader->uninitialize();
this->getAlgorithmManager().releaseAlgorithm(*channelPosReader);
channelPosReader = nullptr;
}
// Gets frame base path for video output, if the variable is not defined, no video output is performed
const CString basePath = this->getConfigurationManager().expand("${AdvancedVisualization_VideoOutputPath}");
const CString sessionId = this->getConfigurationManager().expand("[$core{date}-$core{time}]");
const CString widgetName = this->getStaticBoxContext().getName();
m_FrameFilenameFormat = basePath + sessionId + widgetName + CString("-%06i.png");
m_IsVideoOutputEnabled = (basePath != CString(""));
m_IsVideoOutputWorking = false;
m_FrameId = 0;
gtk_widget_set_visible(GTK_WIDGET(gtk_builder_get_object(m_Builder, "hbox_video_recording")), m_IsVideoOutputEnabled ? TRUE : FALSE);
gtk_label_set_markup(GTK_LABEL(gtk_builder_get_object(m_Builder, "label_video_recording_filename")),
(CString("<span foreground=\"darkblue\"><small>") + m_FrameFilenameFormat + CString("</small></span>")).toASCIIString());
m_Width = 0;
m_Height = 0;
return true;
}
bool CBoxAlgorithmViz::uninitialize()
{
g_object_unref(m_Builder);
m_Builder = nullptr;
getContext().stepERPFractionBy(-getContext().getERPFraction());
getContext().setERPPlayerActive(false);
delete m_SubRendererCtx;
m_SubRendererCtx = nullptr;
delete m_RendererCtx;
m_RendererCtx = nullptr;
this->releasePluginObject(m_visualizationCtx);
return true;
}
bool CBoxAlgorithmViz::processClock(Kernel::CMessageClock& /*msg*/)
{
const uint64_t currentTime = this->getPlayerContext().getCurrentTime();
const uint64_t minDeltaTimeHD = (1LL << 32) / 16;
const uint64_t minDeltaTimeLD = (1LL << 32);
const uint64_t minDeltaTimeLD2 = (1LL << 32) * 5;
uint64_t minDeltaTime;
if (this->getPlayerContext().getStatus() == Kernel::EPlayerStatus::Play) { minDeltaTime = minDeltaTimeHD; }
else
{
const auto fastForwardMaxFactor = float(this->getPlayerContext().getCurrentFastForwardMaximumFactor());
if (fastForwardMaxFactor <= m_fastForwardMaxFactorHD) { minDeltaTime = minDeltaTimeHD; }
else if (fastForwardMaxFactor <= m_fastForwardMaxFactorLD)
{
const float alpha = (fastForwardMaxFactor - m_fastForwardMaxFactorHD) / (m_fastForwardMaxFactorLD - m_fastForwardMaxFactorHD);
minDeltaTime = uint64_t((minDeltaTimeLD * alpha) + minDeltaTimeHD * (1.F - alpha));
}
else { minDeltaTime = minDeltaTimeLD2; }
}
if (currentTime > m_lastProcessTime + minDeltaTime || this->getPlayerContext().getStatus() == Kernel::EPlayerStatus::Step
|| this->getPlayerContext().getStatus() == Kernel::EPlayerStatus::Pause)
{
m_lastProcessTime = currentTime;
this->m_RedrawNeeded = true;
this->getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
}
this->updateRulerVisibility();
return true;
}
void CBoxAlgorithmViz::updateRulerVisibility()
{
if (m_IsScaleVisible != m_RendererCtx->getScaleVisibility())
{
m_IsScaleVisible = m_RendererCtx->getScaleVisibility();
if ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_ScaleVisible)) != 0) != m_IsScaleVisible)
{
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_ScaleVisible), gboolean(m_IsScaleVisible));
}
void (*action)(GtkWidget*) = m_IsScaleVisible ? gtk_widget_show : gtk_widget_hide;
(*action)(this->m_Top);
(*action)(this->m_Left);
(*action)(this->m_Right);
(*action)(this->m_Bottom);
(*action)(this->m_CornerLeft);
(*action)(this->m_CornerRight);
}
}
void CBoxAlgorithmViz::reshape(const int width, const int height)
{
m_Width = size_t(width);
m_Height = size_t(height);
m_RendererCtx->setAspect(float(width) / float(height));
}
void CBoxAlgorithmViz::preDraw()
{
this->updateRulerVisibility();
if (m_TextureID == 0U) { m_TextureID = m_GtkGLWidget.createTexture(m_ColorGradient.toASCIIString()); }
glBindTexture(GL_TEXTURE_1D, m_TextureID);
m_RendererCtx->setAspect(m_Viewport->allocation.width * 1.F / m_Viewport->allocation.height);
}
void CBoxAlgorithmViz::postDraw()
{
glPushAttrib(GL_ALL_ATTRIB_BITS);
if (m_Ruler != nullptr) { m_Ruler->doRender(); }
glPopAttrib();
if (m_IsVideoOutputEnabled && m_IsVideoOutputWorking && m_Width > 0 && m_Height > 0)
{
// Builds up filename to save PNG to
char filename[1024];
sprintf(filename, m_FrameFilenameFormat.toASCIIString(), ++m_FrameId);
// Reads OpenGL buffer and stores it to a cairo surface
cairo_surface_t* cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, int(m_Width), int(m_Height));
glReadPixels(0, 0, GLsizei(m_Width), GLsizei(m_Height), GL_BGRA, GL_UNSIGNED_BYTE, cairo_image_surface_get_data(cairoSurface));
// OpenGL buffers are defined bottom to top while PNG are defined top to bottom, this flips the acquired image
const size_t size = 4; // should be 3
std::vector<unsigned char> swaps(m_Width * size);
unsigned char* swap = &swaps[0];
unsigned char* src1 = cairo_image_surface_get_data(cairoSurface);
unsigned char* src2 = cairo_image_surface_get_data(cairoSurface) + m_Width * (m_Height - 1) * size;
for (size_t i = 0; i < m_Height / 2; ++i)
{
memcpy(swap, src1, m_Width * size);
memcpy(src1, src2, m_Width * size);
memcpy(src2, swap, m_Width * size);
src1 += m_Width * size;
src2 -= m_Width * size;
}
// Pixels are ready to save
cairo_surface_write_to_png(cairoSurface, filename);
cairo_surface_destroy(cairoSurface);
}
}
void CBoxAlgorithmViz::mouseButton(const int x, const int y, const int button, const int status)
{
// Mouse interacts with local renderer context
// m_MouseHandler.mouseButton(*m_rendererCtx, x, y, button, status);
// Mouse interacts with global renderer context
m_MouseHandler.mouseButton(getContext(), x, y, button, status);
this->redraw();
}
void CBoxAlgorithmViz::mouseMotion(const int x, const int y)
{
// Mouse interacts with local renderer context
// m_MouseHandler.mouseMotion(*m_rendererCtx, x, y);
// Mouse interacts with global renderer context
m_MouseHandler.mouseMotion(getContext(), x, y);
if (m_MouseHandler.hasButtonPressed()) { this->redraw(); }
}
void CBoxAlgorithmViz::keyboard(const int x, const int y, const size_t key, const bool status)
{
std::cout << "keyboard : x=" << x << " y=" << y << " key=" << key << " status=" << (status ? "pressed" : "released");
}
void CBoxAlgorithmViz::parseColor(color_t& rColor, const std::string& sColor)
{
float r, g, b;
if (sscanf(sColor.c_str(), "%f,%f,%f", &r, &g, &b) == 3)
{
rColor.r = r * .01F;
rColor.g = g * .01F;
rColor.b = b * .01F;
}
else
{
rColor.r = 1;
rColor.g = 1;
rColor.b = 1;
}
}
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,385 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "m_defines.hpp"
#include <openvibe/ov_all.h>
#include <toolkit/ovtk_all.h>
#include <visualization-toolkit/ovviz_all.h>
#include <mensia/advanced-visualization.hpp>
#include <system/ovCTime.h>
#include "mTGtkGLWidget.hpp"
#include "mCMouse.hpp"
#include "mIRuler.hpp"
#include <vector>
#include <string>
#include <cstdarg>
#include <algorithm>
#include <iostream>
namespace OpenViBE {
namespace AdvancedVisualization {
enum EParameter
{
// Parameter
P_None,
// Input
I_Matrix, I_Signal, I_Spectrum, I_TimeFrequency, I_Covariance, I_Stimulations,
// Setting
S_ChannelLocalisation, S_TemporalCoherence, S_TimeScale, S_ElementCount, S_DataScale, S_Caption, S_DataPositive,
S_Translucency, S_FlowerRingCount, S_Color, S_ColorGradient, S_ShowAxis, S_XYZPlotHasDepth,
// Flag
F_CanAddInput, F_FixedChannelOrder, F_FixedChannelSelection, F_Unstable
};
class CParameterSet
{
public:
explicit CParameterSet(int p, ...)
{
va_list args;
va_start(args, p);
while (p != P_None)
{
m_parameters.push_back(p);
p = va_arg(args, int);
}
va_end(args);
}
explicit operator const std::vector<int>&() const { return m_parameters; }
protected:
std::vector<int> m_parameters;
};
class CBoxAlgorithmViz : public Toolkit::TBoxAlgorithm<Plugins::IBoxAlgorithm>
{
public:
typedef struct
{
float r, g, b;
} color_t;
CBoxAlgorithmViz(const CIdentifier& classID, const std::vector<int>& parameters)
: m_ClassID(classID), m_Parameters(parameters), m_MouseHandler(*this)
{
m_Color.r = 1;
m_Color.g = 1;
m_Color.b = 1;
}
void release() override { delete this; }
uint64_t getClockFrequency() override { return (32LL << 32); }
bool initialize() override;
bool uninitialize() override;
bool processInput(const size_t /*index*/) override { return true; }
bool processClock(Kernel::CMessageClock& msg) override;
virtual void redrawTopLevelWindow(const bool immediate = false) { m_GtkGLWidget.redrawTopLevelWindow(immediate); }
virtual void redraw(const bool immediate = false)
{
const uint64_t currentTime = System::Time::zgetTime();
if (m_RedrawNeeded || currentTime - m_LastRenderTime > ((1LL << 32) / 16))
{
// immediate |= (currentTime - m_lastRenderTime > ((1LL<<32)/4));
m_GtkGLWidget.redraw(immediate);
m_GtkGLWidget.redrawLeft(immediate);
m_GtkGLWidget.redrawRight(immediate);
m_GtkGLWidget.redrawBottom(immediate);
m_LastRenderTime = currentTime;
m_RedrawNeeded = false;
}
}
virtual void updateRulerVisibility();
virtual void reshape(int width, int height);
virtual void preDraw();
virtual void postDraw();
virtual void draw() { }
virtual void drawLeft() { if (m_Ruler != nullptr) { m_Ruler->doRenderLeft(m_Left); } }
virtual void drawRight() { if (m_Ruler != nullptr) { m_Ruler->doRenderRight(m_Right); } }
virtual void drawBottom() { if (m_Ruler != nullptr) { m_Ruler->doRenderBottom(m_Bottom); } }
virtual void mouseButton(int x, int y, int button, int status);
virtual void mouseMotion(int x, int y);
virtual void keyboard(int x, int y, size_t key, bool status);
protected:
static void parseColor(color_t& rColor, const std::string& sColor);
public:
CIdentifier m_ClassID = CIdentifier::undefined();
std::vector<int> m_Parameters;
uint64_t m_lastProcessTime = 0;
TGtkGLWidget<CBoxAlgorithmViz> m_GtkGLWidget;
std::map<std::string, CVertex> m_ChannelPositions;
CRendererContext* m_RendererCtx = nullptr;
CRendererContext* m_SubRendererCtx = nullptr;
IRuler* m_Ruler = nullptr;
CMouse m_MouseHandler;
CString m_Localisation;
ETemporalCoherence m_TemporalCoherence = ETemporalCoherence::TimeLocked;
uint64_t m_TimeScale = 0;
size_t m_NElement = 0;
double m_DataScale = 0.0;
CString m_Caption;
uint32_t m_TextureID = 0;
size_t m_NFlowerRing = 0;
double m_Translucency = 0.0;
CString m_ColorGradient;
bool m_ShowAxis = false;
bool m_XYZPlotHasDepth = false;
bool m_IsPositive = false;
bool m_IsTimeLocked = false;
bool m_IsScaleVisible = false;
std::vector<color_t> m_Colors;
color_t m_Color;
CIdentifier m_TypeID = CIdentifier::undefined();
uint64_t m_Time1 = 0;
uint64_t m_Time2 = 0;
float m_fastForwardMaxFactorHD = 0.0;
float m_fastForwardMaxFactorLD = 0.0;
std::vector<float> m_Swaps;
GtkBuilder* m_Builder = nullptr;
GtkWidget* m_Viewport = nullptr;
GtkWidget* m_Top = nullptr;
GtkWidget* m_Left = nullptr;
GtkWidget* m_Right = nullptr;
GtkWidget* m_Bottom = nullptr;
GtkWidget* m_CornerLeft = nullptr;
GtkWidget* m_CornerRight = nullptr;
GtkWidget* m_TimeScaleW = nullptr;
GtkWidget* m_nElementW = nullptr;
GtkWidget* m_ERPRange = nullptr;
GtkWidget* m_ERPPlayerButton = nullptr;
GtkWidget* m_ERPPlayer = nullptr;
GtkWidget* m_ScaleVisible = nullptr;
GtkWidget* m_FrequencyBandMin = nullptr;
GtkWidget* m_FrequencyBandMax = nullptr;
GtkTreeView* m_ChannelTreeView = nullptr;
GtkListStore* m_ChannelListStore = nullptr;
size_t m_Width = 0;
size_t m_Height = 0;
bool m_RebuildNeeded = false;
bool m_RefreshNeeded = false;
bool m_RedrawNeeded = false;
uint64_t m_LastRenderTime = 0;
bool m_IsVideoOutputEnabled = false; // for video output
bool m_IsVideoOutputWorking = false;
size_t m_FrameId = 0;
CString m_FrameFilenameFormat;
private:
VisualizationToolkit::IVisualizationContext* m_visualizationCtx = nullptr;
};
class CBoxAlgorithmVizListener : public Toolkit::TBoxListener<Plugins::IBoxListener>
{
public:
explicit CBoxAlgorithmVizListener(const std::vector<int>& parameters) : m_Parameters(parameters) { }
size_t getBaseSettingCount()
{
size_t result = 0;
for (const auto& p : m_Parameters)
{
// if(p == I_Matrix) { result++; }
// if(p == I_Signal) { result++; }
// if(p == I_Spectrum) { result++; }
// if(p == I_Covariance) { result++; }
// if(p == I_Stimulations) { result++; }
if (p == S_ChannelLocalisation) { result++; }
if (p == S_TemporalCoherence) { result++; }
if (p == S_TimeScale) { result++; }
if (p == S_ElementCount) { result++; }
if (p == S_DataScale) { result++; }
if (p == S_Caption) { result++; }
if (p == S_DataPositive) { result++; }
if (p == S_Translucency) { result++; }
if (p == S_FlowerRingCount) { result++; }
if (p == S_Color) { result++; }
if (p == S_ColorGradient) { result++; }
if (p == S_ShowAxis) { result++; }
if (p == S_XYZPlotHasDepth) { result++; }
// if(p == F_CanAddInput) { result++; }
// if(p == F_FixedChannelOrder) { result++; }
// if(p == F_FixedChannelSelection) { result++; }
}
return result;
}
bool onInitialized(Kernel::IBox& /*box*/) override
{
#ifdef TARGET_OS_Windows
//box.addAttribute(OV_AttributeId_Box_DocumentationURLBase, OpenViBE::CString("${Path_Root}/doc/Mensia Advanced Visualization Toolkit/Mensia Advanced Visualization Toolkit.chm::"));
#endif
return true;
}
bool onDefaultInitialized(Kernel::IBox& box) override
{
const bool isSignal = (std::find(m_Parameters.begin(), m_Parameters.end(), I_Signal) != m_Parameters.end());
const bool isSpectrum = (std::find(m_Parameters.begin(), m_Parameters.end(), I_Spectrum) != m_Parameters.end());
const bool isCovariance = (std::find(m_Parameters.begin(), m_Parameters.end(), I_Covariance) != m_Parameters.end());
CIdentifier typeID = CIdentifier::undefined();
for (size_t i = 0; i < box.getInputCount(); ++i)
{
box.getInputType(i, typeID);
if (typeID == OV_TypeId_StreamedMatrix)
{
if (isSignal) { box.setInputType(i, OV_TypeId_Signal); }
if (isSpectrum) { box.setInputType(i, OV_TypeId_Spectrum); }
if (isCovariance) { box.setInputType(i, OV_TypeId_CovarianceMatrix); }
}
}
return true;
}
_IsDerivedFromClass_Final_(OpenViBE::Toolkit::TBoxListener<OpenViBE::Plugins::IBoxListener>, OpenViBE::CIdentifier::undefined())
std::vector<int> m_Parameters;
};
class CBoxAlgorithmVizDesc : public Plugins::IBoxAlgorithmDesc
{
public:
CString m_Name;
CString m_CategoryName;
CString m_ShortDesc;
CString m_DetailedDesc;
CIdentifier m_DescClassID = CIdentifier::undefined();
CIdentifier m_ClassID = CIdentifier::undefined();
CString m_AddedSoftwareVersion;
CString m_UpdatedSoftwareVersion;
std::vector<int> m_Parameters;
CBoxAlgorithmVizDesc(const CString& name, const CIdentifier& descClassID, const CIdentifier& classID,
const CString& addedSoftwareVersion, const CString& updatedSoftwareVersion,
const CParameterSet& parameterSet, const CString& shortDesc, const CString& detailedDesc)
: m_ShortDesc(shortDesc), m_DetailedDesc(detailedDesc), m_DescClassID(descClassID), m_ClassID(classID),
m_AddedSoftwareVersion(addedSoftwareVersion), m_UpdatedSoftwareVersion(updatedSoftwareVersion), m_Parameters(parameterSet)
{
const std::string fullname(name.toASCIIString());
const size_t i = fullname.rfind('/');
if (i != std::string::npos)
{
m_Name = CString(fullname.substr(i + 1).c_str());
m_CategoryName = CString(fullname.substr(0, i).c_str());
}
else
{
m_Name = CString(name);
m_CategoryName = CString("");
}
}
void release() override { }
CString getName() const override { return m_Name; }
CString getAuthorName() const override { return CString("Yann Renard"); }
CString getAuthorCompanyName() const override { return CString("Mensia Technologies SA"); }
CString getShortDescription() const override { return m_ShortDesc; }
CString getDetailedDescription() const override { return m_DetailedDesc; }
// virtual OpenViBE::CString getCategory() const { return OpenViBE::CString(""); }
CString getVersion() const override { return CString("1.0"); }
CString getSoftwareComponent() const override { return "openvibe-designer"; }
CString getAddedSoftwareVersion() const override { return m_AddedSoftwareVersion; }
CString getUpdatedSoftwareVersion() const override { return m_UpdatedSoftwareVersion; }
CString getStockItemName() const override { return CString("gtk-find"); }
CIdentifier getCreatedClass() const override { return m_ClassID; }
void releaseBoxListener(Plugins::IBoxListener* listener) const override { delete listener; }
bool hasFunctionality(const Plugins::EPluginFunctionality functionality) const override
{
return functionality == Plugins::EPluginFunctionality::Visualization;
}
bool getBoxPrototype(Kernel::IBoxProto& prototype) const override
{
for (auto p : m_Parameters)
{
if (p == I_Matrix) { prototype.addInput("Matrix", OV_TypeId_StreamedMatrix); }
if (p == I_Signal) { prototype.addInput("Matrix", OV_TypeId_StreamedMatrix); } // This is later changed in the listener
if (p == I_Spectrum) { prototype.addInput("Matrix", OV_TypeId_StreamedMatrix); } // This is later changed in the listener
if (p == I_TimeFrequency) { prototype.addInput("Matrix", OV_TypeId_TimeFrequency); } // This is later changed in the listener
if (p == I_Covariance) { prototype.addInput("Matrix", OV_TypeId_StreamedMatrix); } // This is later changed in the listener
if (p == I_Stimulations) { prototype.addInput("Markers", OV_TypeId_Stimulations); }
if (p == S_ChannelLocalisation
)
{
prototype.addSetting("Channel Localisation", OV_TypeId_Filename, "${AdvancedViz_ChannelLocalisation}");
} // "../share/electrode_sets/electrode_set_standard_cartesian.txt"
if (p == S_DataPositive) { prototype.addSetting("Positive Data Only ?", OV_TypeId_Boolean, "false"); }
if (p == S_TemporalCoherence) { prototype.addSetting("Temporal Coherence", OVP_TypeId_TemporalCoherence, "Time Locked"); }
if (p == S_TimeScale) { prototype.addSetting("Time Scale", OV_TypeId_Float, "20"); }
if (p == S_ElementCount) { prototype.addSetting("Matrix Count", OV_TypeId_Integer, "50"); }
if (p == S_DataScale) { prototype.addSetting("Gain", OV_TypeId_Float, "1"); }
if (p == S_Caption) { prototype.addSetting("Caption", OV_TypeId_String, ""); }
if (p == S_FlowerRingCount) { prototype.addSetting("Flower Ring Count", OV_TypeId_Integer, "1"); }
if (p == S_Translucency) { prototype.addSetting("Translucency", OV_TypeId_Float, "1"); }
if (p == S_ShowAxis) { prototype.addSetting("Show Axis", OV_TypeId_Boolean, "true"); }
if (p == S_XYZPlotHasDepth) { prototype.addSetting("Use third channel as depth", OV_TypeId_Boolean, "false"); } // XYZ Plot
if (p == S_Color) { prototype.addSetting("Color", OV_TypeId_Color, "${AdvancedViz_DefaultColor}"); }
if (p == S_ColorGradient) { prototype.addSetting("Color", OV_TypeId_ColorGradient, "${AdvancedViz_DefaultColorGradient}"); }
if (p == F_CanAddInput) { prototype.addFlag(Kernel::BoxFlag_CanAddInput); }
if (p == F_FixedChannelOrder) {} // prototype.addFlag(OpenViBE::Kernel::BoxFlag_);
if (p == F_FixedChannelSelection) {} // prototype.addFlag(OpenViBE::Kernel::BoxFlag_);
}
prototype.addFlag(Kernel::BoxFlag_CanModifyInput);
return true;
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,66 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "mCMouse.hpp"
#include "mCBoxAlgorithmViz.hpp"
#include <cmath>
namespace OpenViBE {
namespace AdvancedVisualization {
void CMouse::mouseButton(CRendererContext& ctx, const int x, const int y, const int button, const int status)
{
m_Buttons[button] = status;
if (m_Buttons[1] == 2)
{
ctx.setScaleVisibility(!ctx.getScaleVisibility());
m_BoxAlgorithmViz.redrawTopLevelWindow();
}
m_X = x;
m_Y = y;
}
void CMouse::mouseMotion(CRendererContext& ctx, const int x, const int y)
{
if (m_Buttons[3]) { ctx.scaleBy(powf(.99F, float(y - m_Y))); }
if (m_Buttons[2]) { ctx.zoomBy(powf(.99F, float(y - m_Y))); }
if (m_Buttons[1])
{
ctx.rotateByY(float(x - m_X) * .1F);
ctx.rotateByX(float(y - m_Y) * .1F);
}
m_X = x;
m_Y = y;
}
bool CMouse::hasButtonPressed()
{
for (const auto& button : m_Buttons) { if (button.second) { return true; } }
return false;
}
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,48 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mensia/advanced-visualization.hpp>
#include <map>
namespace OpenViBE {
namespace AdvancedVisualization {
class CBoxAlgorithmViz;
class CMouse
{
public:
explicit CMouse(CBoxAlgorithmViz& boxAlgorithmViz) : m_BoxAlgorithmViz(boxAlgorithmViz) { }
void mouseButton(CRendererContext& ctx, const int x, const int y, const int button, const int status);
void mouseMotion(CRendererContext& ctx, const int x, const int y);
bool hasButtonPressed();
CBoxAlgorithmViz& m_BoxAlgorithmViz;
std::map<int, int> m_Buttons;
int m_X = 0;
int m_Y = 0;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,107 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mensia/advanced-visualization.hpp>
#include <gtk/gtk.h>
#if defined TARGET_OS_Windows
#include <Windows.h>
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#include <cmath>
#include <vector>
#include <cstdlib>
#define IRuler_SplitCount 5
namespace OpenViBE {
namespace AdvancedVisualization {
class IRuler
{
public:
IRuler() { }
IRuler(const IRuler&) = delete;
virtual ~IRuler() = default;
virtual void setRendererContext(const CRendererContext* ctx) { m_rendererCtx = ctx; }
virtual void setRenderer(const IRenderer* renderer) { m_renderer = renderer; }
virtual void doRender() { this->render(); }
virtual void doRenderLeft(GtkWidget* widget) { if (m_rendererCtx->getScaleVisibility()) { this->renderLeft(widget); } }
virtual void doRenderRight(GtkWidget* widget) { if (m_rendererCtx->getScaleVisibility()) { this->renderRight(widget); } }
virtual void doRenderBottom(GtkWidget* widget) { if (m_rendererCtx->getScaleVisibility()) { this->renderBottom(widget); } }
protected:
virtual void render() { }
virtual void renderLeft(GtkWidget* /*widget*/) { }
virtual void renderRight(GtkWidget* /*widget*/) { }
virtual void renderBottom(GtkWidget* /*widget*/) { }
std::vector<double> splitRange(const double start, const double stop, const size_t count = 10) const
{
std::vector<double> res;
const double range = stop - start;
const double order = floor(log(range) / log(10.) - .1F);
double step = pow(10, order);
double nStep = trunc(range / step);
while (nStep < count)
{
nStep *= 2;
step /= 2;
}
while (nStep > count)
{
nStep /= 2;
step *= 2;
}
double value = trunc(start / step) * step;
while (value < start) { value += step; }
while (value <= stop)
{
res.push_back(std::abs(value) < std::abs(range / 1000) ? 0 : value);
value += step;
}
return res;
}
static std::string getLabel(const double v)
{
char label[1024];
(fabs(v) < 1E-10) ? sprintf(label, "0") : sprintf(label, "%g", v);
return label;
}
const CRendererContext* m_rendererCtx = nullptr;
const IRenderer* m_renderer = nullptr;
float m_blackAlpha = 0.9F;
float m_whiteAlpha = 1.0F;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,314 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mCBoxAlgorithmViz.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
template <class TRendererFactoryClass, class TRulerClass>
class TBoxAlgorithmContinuousViz final : public CBoxAlgorithmViz
{
public:
TBoxAlgorithmContinuousViz(const CIdentifier& classID, const std::vector<int>& parameters);
bool initialize() override;
bool uninitialize() override;
bool process() override;
_IsDerivedFromClass_Final_(CBoxAlgorithmViz, m_ClassID)
Toolkit::TStreamedMatrixDecoder<TBoxAlgorithmContinuousViz<TRendererFactoryClass, TRulerClass>> m_oMatrixDecoder;
Toolkit::TStimulationDecoder<TBoxAlgorithmContinuousViz<TRendererFactoryClass, TRulerClass>> m_oStimulationDecoder;
TRendererFactoryClass m_RendererFactory;
IRenderer* m_Renderer = nullptr;
protected:
void draw() override;
};
class CBoxAlgorithmContinuousVizListener final : public CBoxAlgorithmVizListener
{
public:
explicit CBoxAlgorithmContinuousVizListener(const std::vector<int>& parameters) : CBoxAlgorithmVizListener(parameters) { }
bool onInputTypeChanged(Kernel::IBox& box, const size_t index) override
{
CIdentifier typeID = CIdentifier::undefined();
box.getInputType(index, typeID);
if (!this->getTypeManager().isDerivedFromStream(typeID, OV_TypeId_StreamedMatrix)) { box.setInputType(index, OV_TypeId_StreamedMatrix); }
box.setInputType(1, OV_TypeId_Stimulations);
return true;
}
};
template <class TRendererFactoryClass, class TRulerClass = IRuler>
class TBoxAlgorithmContinuousVizDesc final : public CBoxAlgorithmVizDesc
{
public:
TBoxAlgorithmContinuousVizDesc(const CString& name, const CIdentifier& descClassID, const CIdentifier& classID,
const CString& addedSoftwareVersion, const CString& updatedSoftwareVersion,
const CParameterSet& parameterSet, const CString& shortDesc, const CString& detailedDesc)
: CBoxAlgorithmVizDesc(name, descClassID, classID, addedSoftwareVersion, updatedSoftwareVersion, parameterSet, shortDesc, detailedDesc) { }
Plugins::IPluginObject* create() override { return new TBoxAlgorithmContinuousViz<TRendererFactoryClass, TRulerClass>(m_ClassID, m_Parameters); }
Plugins::IBoxListener* createBoxListener() const override { return new CBoxAlgorithmContinuousVizListener(m_Parameters); }
CString getCategory() const override { return CString("Advanced Visualization/") + m_CategoryName; }
_IsDerivedFromClass_Final_(OpenViBE::Plugins::IBoxAlgorithmDesc, m_DescClassID)
};
template <class TRendererFactoryClass, class TRulerClass>
TBoxAlgorithmContinuousViz<TRendererFactoryClass, TRulerClass>::TBoxAlgorithmContinuousViz(
const CIdentifier& classID, const std::vector<int>& parameters) : CBoxAlgorithmViz(classID, parameters) { }
template <class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmContinuousViz<TRendererFactoryClass, TRulerClass>::initialize()
{
const bool res = CBoxAlgorithmViz::initialize();
m_oMatrixDecoder.initialize(*this, 0);
m_oStimulationDecoder.initialize(*this, 1);
m_Renderer = m_RendererFactory.create();
m_Ruler = new TRulerClass;
m_Ruler->setRendererContext(m_RendererCtx);
m_Ruler->setRenderer(m_Renderer);
return res;
}
template <class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmContinuousViz<TRendererFactoryClass, TRulerClass>::uninitialize()
{
m_RendererFactory.release(m_Renderer);
m_Renderer = nullptr;
delete m_Ruler;
m_Ruler = nullptr;
m_oStimulationDecoder.uninitialize();
m_oMatrixDecoder.uninitialize();
return CBoxAlgorithmViz::uninitialize();
}
template <class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmContinuousViz<TRendererFactoryClass, TRulerClass>::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
for (size_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_oMatrixDecoder.decode(i);
CMatrix* matrix = m_oMatrixDecoder.getOutputMatrix();
size_t nChannel = matrix->getDimensionSize(0);
size_t nSample = matrix->getDimensionSize(1);
if (nChannel == 0)
{
this->getLogManager() << Kernel::LogLevel_Error << "Input stream " << i << " has 0 channels\n";
return false;
}
if (matrix->getDimensionCount() == 1)
{
nChannel = matrix->getDimensionSize(0);
nSample = 1;
}
if (m_oMatrixDecoder.isHeaderReceived())
{
GtkTreeIter gtkTreeIt;
gtk_list_store_clear(m_ChannelListStore);
m_Swaps.resize(size_t(nChannel));
m_RendererCtx->clear();
m_RendererCtx->setTranslucency(float(m_Translucency));
m_RendererCtx->setFlowerRingCount(m_NFlowerRing);
m_RendererCtx->setTimeScale(m_TimeScale);
m_RendererCtx->setElementCount(m_NElement);
m_RendererCtx->scaleBy(float(m_DataScale));
m_RendererCtx->setAxisDisplay(m_ShowAxis);
m_RendererCtx->setPositiveOnly(m_IsPositive);
m_RendererCtx->setParentRendererContext(&getContext());
m_RendererCtx->setTimeLocked(m_IsTimeLocked);
m_RendererCtx->setXYZPlotDepth(m_XYZPlotHasDepth);
gtk_tree_view_set_model(m_ChannelTreeView, nullptr);
for (size_t j = 0; j < nChannel; ++j)
{
std::string name = trim(matrix->getDimensionLabel(0, j));
std::string subname = name;
std::transform(name.begin(), name.end(), subname.begin(), tolower);
const CVertex v = m_ChannelPositions[subname];
if (name.empty()) { name = "Channel " + std::to_string(j + 1); }
m_RendererCtx->addChannel(name, v.x, v.y, v.z);
gtk_list_store_append(m_ChannelListStore, &gtkTreeIt);
gtk_list_store_set(m_ChannelListStore, &gtkTreeIt, 0, j + 1, 1, name.c_str(), -1);
}
gtk_tree_view_set_model(m_ChannelTreeView, GTK_TREE_MODEL(m_ChannelListStore));
gtk_tree_selection_select_all(gtk_tree_view_get_selection(m_ChannelTreeView));
m_Renderer->setChannelCount(nChannel);
if (m_TypeID == OV_TypeId_Signal) { m_RendererCtx->setDataType(CRendererContext::EDataType::Signal); }
else if (m_TypeID == OV_TypeId_Spectrum) { m_RendererCtx->setDataType(CRendererContext::EDataType::Spectrum); }
else { m_RendererCtx->setDataType(CRendererContext::EDataType::Matrix); }
if (nSample != 1)
{
//bool warned = false;
if (m_TypeID == OV_TypeId_Spectrum)
{
//warned = true;
this->getLogManager() << Kernel::LogLevel_Warning << "Input matrix has 'spectrum' type\n";
this->getLogManager() << Kernel::LogLevel_Warning << "Such configuration is uncommon for a 'continous' kind of visualization !\n";
this->getLogManager() << Kernel::LogLevel_Warning <<
"You might want to consider the 'stacked' kind of visualization for time/frequency analysis for instance\n";
this->getLogManager() << Kernel::LogLevel_Warning << "Please double check your scenario\n";
}
else
{
if (!m_RendererCtx->isTimeLocked())
{
//warned = true;
this->getLogManager() << Kernel::LogLevel_Warning << "Input matrix has " << nSample
<< " elements and the box settings say the elements are independant with " << m_NElement << " elements to render\n";
this->getLogManager() << Kernel::LogLevel_Warning <<
"Such configuration is uncommon for a 'continous' kind of visualization !\n";
this->getLogManager() << Kernel::LogLevel_Warning << "You might want either of the following alternative :\n";
this->getLogManager() << Kernel::LogLevel_Warning << " - an 'instant' kind of visualization to highlight the " << m_NElement
<< " elements of the matrix\n";
this->getLogManager() << Kernel::LogLevel_Warning <<
" - a 'time locked' kind of elements (thus the scenario must refresh the matrix on a regular basis)\n";
this->getLogManager() << Kernel::LogLevel_Warning << "Please double check your scenario and box settings\n";
}
}
}
m_RebuildNeeded = true;
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
if (m_oMatrixDecoder.isBufferReceived())
{
m_Time1 = m_Time2;
m_Time2 = boxContext.getInputChunkEndTime(0, i);
const uint64_t duration = (m_Time2 - m_Time1) / nSample;
if ((duration & ~0xf) != (m_RendererCtx->getSampleDuration() & ~0xf) && duration != 0) // 0xf mask avoids rounding errors
{
m_RendererCtx->setSampleDuration(duration);
}
m_RendererCtx->setSpectrumFrequencyRange(
size_t((uint64_t(nSample) << 32) / (boxContext.getInputChunkEndTime(0, i) - boxContext.getInputChunkStartTime(0, i))));
m_RendererCtx->setMinimumSpectrumFrequency(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(m_FrequencyBandMin))));
m_RendererCtx->setMaximumSpectrumFrequency(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(m_FrequencyBandMax))));
// Feed renderer with actual samples
for (size_t j = 0; j < nSample; ++j)
{
for (size_t k = 0; k < nChannel; ++k) { m_Swaps[k] = float(matrix->getBuffer()[k * nSample + j]); }
m_Renderer->feed(&m_Swaps[0]);
}
// Adjust feeding depending on theoretical dates
if (m_RendererCtx->isTimeLocked() && m_RendererCtx->getSampleDuration())
{
const auto nTheoreticalSample = size_t(m_Time2 / m_RendererCtx->getSampleDuration());
if (nTheoreticalSample > m_Renderer->getHistoryCount()) { m_Renderer->prefeed(nTheoreticalSample - m_Renderer->getHistoryCount()); }
}
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
}
const size_t nInput = this->getStaticBoxContext().getInputCount();
if (nInput > 1)
{
for (size_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
{
m_oStimulationDecoder.decode(i);
if (m_oStimulationDecoder.isBufferReceived())
{
IStimulationSet* stimulationSet = m_oStimulationDecoder.getOutputStimulationSet();
for (size_t j = 0; j < stimulationSet->getStimulationCount(); ++j)
{
m_Renderer->feed(stimulationSet->getStimulationDate(j), stimulationSet->getStimulationIdentifier(j));
m_RedrawNeeded = true;
}
}
}
}
size_t rendererSampleCount = 0;
if (m_RendererCtx->isTimeLocked())
{
if (0 != m_RendererCtx->getSampleDuration()) { rendererSampleCount = size_t(m_RendererCtx->getTimeScale() / m_RendererCtx->getSampleDuration()); }
}
else
{
rendererSampleCount = size_t(m_RendererCtx->getElementCount()); // *nSample;
}
if (rendererSampleCount != 0 && rendererSampleCount != m_Renderer->getSampleCount())
{
m_Renderer->setSampleCount(rendererSampleCount);
m_RebuildNeeded = true;
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
if (m_RebuildNeeded) { m_Renderer->rebuild(*m_RendererCtx); }
if (m_RefreshNeeded) { m_Renderer->refresh(*m_RendererCtx); }
if (m_RedrawNeeded) { this->redraw(); }
m_RebuildNeeded = false;
m_RefreshNeeded = false;
m_RedrawNeeded = false;
return true;
}
template <class TRendererFactoryClass, class TRulerClass>
void TBoxAlgorithmContinuousViz<TRendererFactoryClass, TRulerClass>::draw()
{
CBoxAlgorithmViz::preDraw();
glPushAttrib(GL_ALL_ATTRIB_BITS);
glColor4f(m_Color.r, m_Color.g, m_Color.b, m_RendererCtx->getTranslucency());
m_Renderer->render(*m_RendererCtx);
glPopAttrib();
CBoxAlgorithmViz::postDraw();
}
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,123 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mTBoxAlgorithmInstantViz.hpp"
#include <array>
namespace OpenViBE {
namespace AdvancedVisualization {
template <class TRendererFactoryClass, class TRulerClass>
class TBoxAlgorithmInstantLoretaViz final : public TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>
{
public:
TBoxAlgorithmInstantLoretaViz(const CIdentifier& classID, const std::vector<int>& parameters)
: TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>(classID, parameters) { }
static void callback(GtkTreeSelection* selection, TBoxAlgorithmInstantLoretaViz<TRendererFactoryClass, TRulerClass>* box)
{
box->regionSelectionChanged(selection);
}
void regionSelectionChanged(GtkTreeSelection* pTreeSelection)
{
m_Renderer->clearRegionSelection();
GtkTreeIter iter;
char* value = nullptr;
for (size_t i = 0; i < 3; ++i)
{
GtkTreeSelection* selection = gtk_tree_view_get_selection(m_LookupTreeView[i]);
if (selection == pTreeSelection)
{
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_LookupListStore[i]), &iter))
{
do
{
if (gtk_tree_selection_iter_is_selected(selection, &iter))
{
gtk_tree_model_get(GTK_TREE_MODEL(m_LookupListStore[i]), &iter, 0, &value, -1);
m_Renderer->selectRegion(i, value);
}
} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(m_LookupListStore[i]), &iter));
}
}
else { gtk_tree_selection_unselect_all(selection); }
}
}
void fillRegion(GtkListStore* listStore, GtkTreeView* view, IRenderer* renderer, const size_t category) const
{
GtkTreeIter gtkTreeIter;
gtk_list_store_clear(listStore);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_MULTIPLE);
for (size_t i = 0; i < renderer->getRegionCount(category); ++i)
{
const char* name = m_Renderer->getRegionName(category, i);
gtk_list_store_append((listStore), &gtkTreeIter);
gtk_list_store_set(listStore, &gtkTreeIter, 0, name ? name : "", -1);
gtk_tree_selection_select_iter(gtk_tree_view_get_selection(view), &gtkTreeIter);
}
}
using TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>::m_Renderers;
using TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>::m_Builder;
bool initialize() override
{
TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>::initialize();
m_Renderer = m_Renderers[0];
gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(m_Builder, "expander_select")));
gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(m_Builder, "expander_select_sLORETA")));
m_LookupTreeView[0] = GTK_TREE_VIEW(gtk_builder_get_object(m_Builder, "expander_select_broadmann_treeview"));
m_LookupTreeView[1] = GTK_TREE_VIEW(gtk_builder_get_object(m_Builder, "expander_select_neuro_1_treeview"));
m_LookupTreeView[2] = GTK_TREE_VIEW(gtk_builder_get_object(m_Builder, "expander_select_neuro_2_treeview"));
m_LookupListStore[0] = GTK_LIST_STORE(gtk_builder_get_object(m_Builder, "liststore_select_broadmann"));
m_LookupListStore[1] = GTK_LIST_STORE(gtk_builder_get_object(m_Builder, "liststore_select_neuro_1"));
m_LookupListStore[2] = GTK_LIST_STORE(gtk_builder_get_object(m_Builder, "liststore_select_neuro_2"));
for (size_t i = 0; i < m_Renderer->getRegionCategoryCount() && i < 3; ++i)
{
this->fillRegion(m_LookupListStore[i], m_LookupTreeView[i], m_Renderer, i);
}
g_signal_connect(gtk_tree_view_get_selection(m_LookupTreeView[0]), "changed", G_CALLBACK(callback), this);
g_signal_connect(gtk_tree_view_get_selection(m_LookupTreeView[1]), "changed", G_CALLBACK(callback), this);
g_signal_connect(gtk_tree_view_get_selection(m_LookupTreeView[2]), "changed", G_CALLBACK(callback), this);
return true;
}
IRenderer* m_Renderer = nullptr;
std::array<GtkTreeView*, 3> m_LookupTreeView{};
std::array<GtkListStore*, 3> m_LookupListStore{};
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,304 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mCBoxAlgorithmViz.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
template <class TRendererFactoryClass, class TRulerClass>
class TBoxAlgorithmInstantViz : public CBoxAlgorithmViz
{
public:
TBoxAlgorithmInstantViz(const CIdentifier& classID, const std::vector<int>& params);
bool initialize() override;
bool uninitialize() override;
bool process() override;
_IsDerivedFromClass_Final_(CBoxAlgorithmViz, m_ClassID)
TRendererFactoryClass m_RendererFactory;
size_t m_NInput = 0;
std::vector<IRenderer*> m_Renderers;
std::vector<Toolkit::TStreamedMatrixDecoder<TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>>> m_Decoder;
double m_LastERPFraction = 0;
protected:
void draw() override;
};
class CBoxAlgorithmInstantVizListener final : public CBoxAlgorithmVizListener
{
public:
explicit CBoxAlgorithmInstantVizListener(const std::vector<int>& parameters) : CBoxAlgorithmVizListener(parameters) { }
bool onInputTypeChanged(Kernel::IBox& box, const size_t index) override
{
CIdentifier typeID = CIdentifier::undefined();
box.getInputType(index, typeID);
if (!this->getTypeManager().isDerivedFromStream(typeID, OV_TypeId_StreamedMatrix)) { box.setInputType(index, OV_TypeId_StreamedMatrix); }
else { for (size_t i = 0; i < box.getInputCount(); ++i) { box.setInputType(i, typeID); } }
return true;
}
bool onInputAdded(Kernel::IBox& box, const size_t index) override
{
CIdentifier typeID = CIdentifier::undefined();
box.getInputType(0, typeID);
box.setInputType(index, typeID);
box.setInputName(index, "Matrix");
box.addSetting("Color", OV_TypeId_Color, "${AdvancedViz_DefaultColor}");
return true;
}
bool onInputRemoved(Kernel::IBox& box, const size_t index) override
{
box.removeSetting(this->getBaseSettingCount() + index - 1);
return true;
}
};
template <class TRendererFactoryClass, class TRulerClass = IRuler, template < typename, typename > class TBoxAlgorithm = TBoxAlgorithmInstantViz>
class TBoxAlgorithmInstantVizDesc : public CBoxAlgorithmVizDesc
{
public:
TBoxAlgorithmInstantVizDesc(const CString& name, const CIdentifier& descClassID, const CIdentifier& classID,
const CString& addedSoftwareVersion, const CString& updatedSoftwareVersion,
const CParameterSet& parameterSet, const CString& shortDesc, const CString& detailedDesc)
: CBoxAlgorithmVizDesc(name, descClassID, classID, addedSoftwareVersion, updatedSoftwareVersion, parameterSet, shortDesc, detailedDesc) { }
Plugins::IPluginObject* create() override { return new TBoxAlgorithm<TRendererFactoryClass, TRulerClass>(m_ClassID, m_Parameters); }
Plugins::IBoxListener* createBoxListener() const override { return new CBoxAlgorithmInstantVizListener(m_Parameters); }
CString getCategory() const override { return CString("Advanced Visualization/") + m_CategoryName; }
_IsDerivedFromClass_Final_(OpenViBE::Plugins::IBoxAlgorithmDesc, m_DescClassID)
};
template <class TRendererFactoryClass, class TRulerClass>
TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>::TBoxAlgorithmInstantViz(const CIdentifier& classID, const std::vector<int>& params)
: CBoxAlgorithmViz(classID, params) { }
template <class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>::initialize()
{
bool res = CBoxAlgorithmViz::initialize();
m_LastERPFraction = 0;
m_NInput = this->getStaticBoxContext().getInputCount();
m_Renderers.resize(m_NInput);
m_Decoder.resize(m_NInput);
for (size_t i = 0; i < m_NInput; ++i)
{
m_Renderers[i] = m_RendererFactory.create();
m_Decoder[i].initialize(*this, i);
if (!m_Renderers[i])
{
this->getLogManager() << Kernel::LogLevel_Error << "Could not create renderer, it might have been disabled at compile time\n";
res = false;
}
}
m_Ruler = new TRulerClass;
m_Ruler->setRendererContext(m_RendererCtx);
m_Ruler->setRenderer(m_Renderers[0]);
gtk_widget_set_sensitive(m_TimeScaleW, false);
return res;
}
template <class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>::uninitialize()
{
for (size_t i = 0; i < m_NInput; ++i)
{
m_RendererFactory.release(m_Renderers[i]);
m_Decoder[i].uninitialize();
}
m_Decoder.clear();
m_Renderers.clear();
return CBoxAlgorithmViz::uninitialize();
}
template <class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
const size_t nInput = this->getStaticBoxContext().getInputCount();
for (size_t i = 0; i < nInput; ++i)
{
for (size_t j = 0; j < boxContext.getInputChunkCount(i); ++j)
{
m_Decoder[i].decode(j);
CMatrix* matrix = m_Decoder[i].getOutputMatrix();
size_t nChannel = matrix->getDimensionSize(0);
size_t nSample = matrix->getDimensionSize(1);
if (nChannel == 0)
{
this->getLogManager() << Kernel::LogLevel_Error << "Input stream " << i << " has 0 channels\n";
return false;
}
if (matrix->getDimensionCount() == 1)
{
nChannel = 1;
nSample = matrix->getDimensionSize(0);
}
if (m_Decoder[i].isHeaderReceived())
{
// TODO
// Check dimension coherence
// Only apply renderer context when first header is received
GtkTreeIter gtkTreeIter;
gtk_list_store_clear(m_ChannelListStore);
m_Swaps.resize(nChannel);
m_RendererCtx->clear();
m_RendererCtx->setTranslucency(float(m_Translucency));
m_RendererCtx->setFlowerRingCount(m_NFlowerRing);
// m_rendererCtx->setTimeScale(1); // Won't be used
m_RendererCtx->scaleBy(float(m_DataScale));
m_RendererCtx->setPositiveOnly(m_IsPositive);
m_RendererCtx->setAxisDisplay(m_ShowAxis);
m_RendererCtx->setParentRendererContext(&getContext());
m_RendererCtx->setXYZPlotDepth(m_XYZPlotHasDepth);
gtk_tree_view_set_model(m_ChannelTreeView, nullptr);
for (j = 0; j < nChannel; ++j)
{
std::string name = trim(matrix->getDimensionLabel(0, j));
std::string subname = name;
std::transform(name.begin(), name.end(), subname.begin(), tolower);
const CVertex v = m_ChannelPositions[subname];
if (name.empty()) { name = "Channel " + std::to_string(j + 1); }
m_RendererCtx->addChannel(name, v.x, v.y, v.z);
gtk_list_store_append(m_ChannelListStore, &gtkTreeIter);
gtk_list_store_set(m_ChannelListStore, &gtkTreeIter, 0, j + 1, 1, name.c_str(), -1);
}
gtk_tree_view_set_model(m_ChannelTreeView, GTK_TREE_MODEL(m_ChannelListStore));
gtk_tree_selection_select_all(gtk_tree_view_get_selection(m_ChannelTreeView));
m_Renderers[i]->setChannelCount(nChannel);
m_Renderers[i]->setSampleCount(nSample);
if (nSample > 1 && m_TypeID != OV_TypeId_Spectrum) { gtk_widget_show(m_ERPPlayer); }
if (m_TypeID == OV_TypeId_Signal) { m_RendererCtx->setDataType(CRendererContext::EDataType::Signal); }
else if (m_TypeID == OV_TypeId_Spectrum) { m_RendererCtx->setDataType(CRendererContext::EDataType::Spectrum); }
else { m_RendererCtx->setDataType(CRendererContext::EDataType::Matrix); }
m_RebuildNeeded = true;
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
if (m_Decoder[i].isBufferReceived())
{
const uint64_t chunkDuration = (boxContext.getInputChunkEndTime(i, j) - boxContext.getInputChunkStartTime(i, j));
m_RendererCtx->setSampleDuration(chunkDuration / nSample);
m_RendererCtx->setSpectrumFrequencyRange(size_t((uint64_t(nSample) << 32) / chunkDuration));
m_RendererCtx->setMinimumSpectrumFrequency(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(m_FrequencyBandMin))));
m_RendererCtx->setMaximumSpectrumFrequency(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(m_FrequencyBandMax))));
// Sets time scale
gtk_spin_button_set_value(
GTK_SPIN_BUTTON(gtk_builder_get_object(m_Builder, "spinbutton_time_scale")), (chunkDuration >> 22) / 1024.);
m_Renderers[i]->clear(0); // Drop last samples as they will be fed again
for (size_t k = 0; k < nSample; ++k)
{
for (size_t l = 0; l < nChannel; ++l) { m_Swaps[l] = float(matrix->getBuffer()[l * nSample + k]); }
m_Renderers[i]->feed(&m_Swaps[0]);
}
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
}
}
double erpFraction = getContext().getERPFraction();
if (m_RendererCtx->isERPPlayerActive())
{
erpFraction += .0025;
if (erpFraction > 1) { erpFraction = 0; }
}
if (m_LastERPFraction != erpFraction)
{
gtk_range_set_value(GTK_RANGE(m_ERPRange), erpFraction);
m_LastERPFraction = erpFraction;
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
if (m_RebuildNeeded) { for (auto& renderer : m_Renderers) { renderer->rebuild(*m_RendererCtx); } }
if (m_RefreshNeeded) { for (auto& renderer : m_Renderers) { renderer->refresh(*m_RendererCtx); } }
if (m_RedrawNeeded) { this->redraw(); }
m_RebuildNeeded = false;
m_RefreshNeeded = false;
m_RedrawNeeded = false;
if (m_RendererCtx->isERPPlayerActive()) { gtk_button_set_label(GTK_BUTTON(m_ERPPlayerButton), GTK_STOCK_MEDIA_PAUSE); }
else { gtk_button_set_label(GTK_BUTTON(m_ERPPlayerButton), GTK_STOCK_MEDIA_PLAY); }
return true;
}
template <class TRendererFactoryClass, class TRulerClass>
void TBoxAlgorithmInstantViz<TRendererFactoryClass, TRulerClass>::draw()
{
CBoxAlgorithmViz::preDraw();
for (size_t i = 0; i < m_NInput; ++i)
{
glPushAttrib(GL_ALL_ATTRIB_BITS);
if (i < m_Colors.size()) { glColor4f(m_Colors[i].r, m_Colors[i].g, m_Colors[i].b, m_RendererCtx->getTranslucency()); }
else { glColor4f(m_Color.r, m_Color.g, m_Color.b, m_RendererCtx->getTranslucency()); }
if (m_Renderers[i]) { m_Renderers[i]->render(*m_RendererCtx); }
glPopAttrib();
}
CBoxAlgorithmViz::postDraw();
}
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,392 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mCBoxAlgorithmViz.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
template <bool bHorizontalStack, bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
class TBoxAlgorithmStackedContinuousViz : public CBoxAlgorithmViz
{
public:
TBoxAlgorithmStackedContinuousViz(const CIdentifier& classID, const std::vector<int>& parameters);
bool initialize() override;
bool uninitialize() override;
bool process() override;
_IsDerivedFromClass_Final_(CBoxAlgorithmViz, m_ClassID)
Toolkit::TStimulationDecoder<TBoxAlgorithmStackedContinuousViz<bHorizontalStack, bDrawBorders, TRendererFactoryClass, TRulerClass>> m_StimDecoder;
Toolkit::TStreamedMatrixDecoder<TBoxAlgorithmStackedContinuousViz<bHorizontalStack, bDrawBorders, TRendererFactoryClass, TRulerClass>>
m_MatrixDecoder;
TRendererFactoryClass m_RendererFactory;
std::vector<IRenderer*> m_Renderers;
protected:
void draw() override;
};
class CBoxAlgorithmStackedContinuousVizListener final : public CBoxAlgorithmVizListener
{
public:
explicit CBoxAlgorithmStackedContinuousVizListener(const std::vector<int>& parameters) : CBoxAlgorithmVizListener(parameters) { }
bool onInputTypeChanged(Kernel::IBox& box, const size_t index) override
{
CIdentifier typeID = CIdentifier::undefined();
box.getInputType(index, typeID);
if (!this->getTypeManager().isDerivedFromStream(typeID, OV_TypeId_StreamedMatrix)) { box.setInputType(index, OV_TypeId_StreamedMatrix); }
box.setInputType(1, OV_TypeId_Stimulations);
return true;
}
};
template <bool bHorizontalStack, bool bDrawBorders, class TRendererFactoryClass, class TRulerClass = IRuler>
class TBoxAlgorithmStackedContinuousVizDesc : public CBoxAlgorithmVizDesc
{
public:
TBoxAlgorithmStackedContinuousVizDesc(const CString& name, const CIdentifier& descClassID,
const CIdentifier& classID, const CString& addedSoftwareVersion,
const CString& updatedSoftwareVersion, const CParameterSet& parameterSet,
const CString& shortDesc, const CString& detailedDesc)
: CBoxAlgorithmVizDesc(name, descClassID, classID, addedSoftwareVersion, updatedSoftwareVersion, parameterSet, shortDesc, detailedDesc) { }
Plugins::IPluginObject* create() override
{
return new TBoxAlgorithmStackedContinuousViz<bHorizontalStack, bDrawBorders, TRendererFactoryClass, TRulerClass>(m_ClassID, m_Parameters);
}
Plugins::IBoxListener* createBoxListener() const override { return new CBoxAlgorithmStackedContinuousVizListener(m_Parameters); }
CString getCategory() const override { return CString("Advanced Visualization/") + m_CategoryName; }
_IsDerivedFromClass_Final_(OpenViBE::Plugins::IBoxAlgorithmDesc, m_DescClassID)
};
template <bool bHorizontalStack, bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
TBoxAlgorithmStackedContinuousViz<bHorizontalStack, bDrawBorders, TRendererFactoryClass, TRulerClass>::TBoxAlgorithmStackedContinuousViz(
const CIdentifier& classID, const std::vector<int>& parameters) : CBoxAlgorithmViz(classID, parameters) { }
template <bool bHorizontalStack, bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmStackedContinuousViz<bHorizontalStack, bDrawBorders, TRendererFactoryClass, TRulerClass>::initialize()
{
const bool res = CBoxAlgorithmViz::initialize();
m_MatrixDecoder.initialize(*this, 0);
m_StimDecoder.initialize(*this, 1);
m_RendererCtx->clear();
m_RendererCtx->setTranslucency(float(m_Translucency));
// m_rendererCtx->setTranslucency(m_nFlowerRing);
m_RendererCtx->scaleBy(float(m_DataScale));
m_RendererCtx->setPositiveOnly(m_IsPositive);
m_RendererCtx->setAxisDisplay(m_ShowAxis);
m_RendererCtx->setParentRendererContext(&getContext());
m_SubRendererCtx->clear();
m_SubRendererCtx->setParentRendererContext(m_RendererCtx);
m_Ruler = new TRulerClass;
m_Ruler->setRendererContext(m_RendererCtx);
return res;
}
template <bool bHorizontalStack, bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmStackedContinuousViz<bHorizontalStack, bDrawBorders, TRendererFactoryClass, TRulerClass>::uninitialize()
{
for (size_t i = 0; i < m_Renderers.size(); ++i) { m_RendererFactory.release(m_Renderers[i]); }
m_Renderers.clear();
delete m_SubRendererCtx;
m_SubRendererCtx = nullptr;
delete m_RendererCtx;
m_RendererCtx = nullptr;
delete m_Ruler;
m_Ruler = nullptr;
m_StimDecoder.uninitialize();
m_MatrixDecoder.uninitialize();
return CBoxAlgorithmViz::uninitialize();
}
template <bool bHorizontalStack, bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmStackedContinuousViz<bHorizontalStack, bDrawBorders, TRendererFactoryClass, TRulerClass>::process()
{
Kernel::IBoxIO& boxContext = this->getDynamicBoxContext();
const size_t nInput = this->getStaticBoxContext().getInputCount();
size_t i, j;
for (i = 0; i < boxContext.getInputChunkCount(0); ++i)
{
m_MatrixDecoder.decode(size_t(i));
CMatrix* matrix = m_MatrixDecoder.getOutputMatrix();
size_t nChannel = matrix->getDimensionSize(0);
size_t nSample = matrix->getDimensionSize(1);
if (nChannel == 0)
{
this->getLogManager() << Kernel::LogLevel_Error << "Input stream " << i << " has 0 channels\n";
return false;
}
if (matrix->getDimensionCount() == 1)
{
nChannel = matrix->getDimensionSize(0);
nSample = 1;
}
if (m_MatrixDecoder.isHeaderReceived())
{
GtkTreeIter gtkTreeIter;
gtk_list_store_clear(m_ChannelListStore);
m_Swaps.resize(nSample);
for (j = 0; j < m_Renderers.size(); ++j) { m_RendererFactory.release(m_Renderers[j]); }
m_Renderers.clear();
m_Renderers.resize(nChannel);
m_SubRendererCtx->clear();
m_SubRendererCtx->setParentRendererContext(m_RendererCtx);
m_SubRendererCtx->setTimeLocked(m_IsTimeLocked);
m_SubRendererCtx->setStackCount(nChannel);
for (j = 0; j < nSample; ++j)
{
std::string name = trim(matrix->getDimensionLabel(1, size_t(j)));
std::string subname = name;
std::transform(name.begin(), name.end(), subname.begin(), tolower);
const CVertex v = m_ChannelPositions[subname];
m_SubRendererCtx->addChannel(name, v.x, v.y, v.z);
}
m_RendererCtx->clear();
m_RendererCtx->setTranslucency(float(m_Translucency));
m_RendererCtx->setTimeScale(m_TimeScale);
m_RendererCtx->setElementCount(m_NElement);
m_RendererCtx->scaleBy(float(m_DataScale));
m_RendererCtx->setParentRendererContext(&getContext());
m_RendererCtx->setTimeLocked(m_IsTimeLocked);
m_RendererCtx->setXYZPlotDepth(m_XYZPlotHasDepth);
gtk_tree_view_set_model(m_ChannelTreeView, nullptr);
for (j = 0; j < nChannel; ++j)
{
std::string name = trim(matrix->getDimensionLabel(0, size_t(j)));
std::string subname = name;
std::transform(name.begin(), name.end(), subname.begin(), tolower);
const CVertex v = m_ChannelPositions[subname];
if (name.empty()) { name = "Channel " + std::to_string(j + 1); }
m_Renderers[j] = m_RendererFactory.create();
m_Renderers[j]->setChannelCount(nSample);
m_Renderers[j]->setSampleCount(size_t(m_NElement)); // $$$
// m_Renderers[j]->setSampleCount(size_t(m_TimeScaleW)); // $$$
m_RendererCtx->addChannel(name, v.x, v.y, v.z);
// m_rendererCtx->addChannel(sanitize(matrix->getDimensionLabel(0, j)));
gtk_list_store_append(m_ChannelListStore, &gtkTreeIter);
gtk_list_store_set(m_ChannelListStore, &gtkTreeIter, 0, j + 1, 1, name.c_str(), -1);
}
gtk_tree_view_set_model(m_ChannelTreeView, GTK_TREE_MODEL(m_ChannelListStore));
gtk_tree_selection_select_all(gtk_tree_view_get_selection(m_ChannelTreeView));
if (m_TypeID == OV_TypeId_Signal)
{
m_RendererCtx->setDataType(CRendererContext::EDataType::Signal);
m_SubRendererCtx->setDataType(CRendererContext::EDataType::Signal);
}
else if (m_TypeID == OV_TypeId_Spectrum)
{
m_RendererCtx->setDataType(CRendererContext::EDataType::Spectrum);
m_SubRendererCtx->setDataType(CRendererContext::EDataType::Spectrum);
}
else
{
m_RendererCtx->setDataType(CRendererContext::EDataType::Matrix);
m_SubRendererCtx->setDataType(CRendererContext::EDataType::Matrix);
}
m_Ruler->setRenderer(nChannel ? m_Renderers[0] : nullptr);
m_RebuildNeeded = true;
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
if (m_MatrixDecoder.isBufferReceived())
{
m_Time1 = m_Time2;
m_Time2 = boxContext.getInputChunkEndTime(0, size_t(i));
const uint64_t interChunkDuration = m_Time2 - m_Time1;
const uint64_t chunkDuration = (boxContext.getInputChunkEndTime(0, size_t(i)) - boxContext.getInputChunkStartTime(0, size_t(i)));
const uint64_t sampleDuration = chunkDuration / m_NElement;
if (m_RendererCtx->isTimeLocked())
{
if ((interChunkDuration & ~0xf) != (m_RendererCtx->getSampleDuration() & ~0xf) && interChunkDuration != 0
) // 0xf mask avoids rounding errors
{
m_SubRendererCtx->setSampleDuration(interChunkDuration);
m_RendererCtx->setSampleDuration(interChunkDuration);
}
}
else
{
m_SubRendererCtx->setSampleDuration(sampleDuration);
m_RendererCtx->setSampleDuration(sampleDuration);
}
m_RendererCtx->setSpectrumFrequencyRange(size_t((uint64_t(nSample) << 32) / chunkDuration));
m_RendererCtx->setMinimumSpectrumFrequency(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(m_FrequencyBandMin))));
m_RendererCtx->setMaximumSpectrumFrequency(size_t(gtk_spin_button_get_value(GTK_SPIN_BUTTON(m_FrequencyBandMax))));
for (j = 0; j < nChannel; ++j)
{
// Feed renderer with actual samples
for (size_t k = 0; k < nSample; ++k) { m_Swaps[nSample - k - 1] = float(matrix->getBuffer()[j * nSample + k]); }
m_Renderers[j]->feed(&m_Swaps[0]);
// Adjust feeding depending on theoretical dates
if (m_RendererCtx->isTimeLocked() && m_RendererCtx->getSampleDuration())
{
const auto nTheoreticalSample = size_t(m_Time2 / m_RendererCtx->getSampleDuration());
if (nTheoreticalSample > m_Renderers[j]->getHistoryCount())
{
m_Renderers[j]->prefeed(nTheoreticalSample - m_Renderers[j]->getHistoryCount());
}
}
}
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
}
if (nInput > 1)
{
for (i = 0; i < boxContext.getInputChunkCount(1); ++i)
{
m_StimDecoder.decode(size_t(i));
if (m_StimDecoder.isBufferReceived())
{
IStimulationSet* stimulationSet = m_StimDecoder.getOutputStimulationSet();
for (j = 0; j < stimulationSet->getStimulationCount(); ++j)
{
m_Renderers[0]->feed(stimulationSet->getStimulationDate(j), stimulationSet->getStimulationIdentifier(j));
m_RedrawNeeded = true;
}
}
}
}
size_t rendererSampleCount = 0;
if (m_RendererCtx->isTimeLocked())
{
if (0 != m_RendererCtx->getSampleDuration()) { rendererSampleCount = size_t(m_RendererCtx->getTimeScale() / m_RendererCtx->getSampleDuration()); }
}
else { rendererSampleCount = size_t(m_RendererCtx->getElementCount()); }
if (rendererSampleCount != 0)
{
for (j = 0; j < m_Renderers.size(); ++j)
{
if (rendererSampleCount != m_Renderers[j]->getSampleCount())
{
m_Renderers[j]->setSampleCount(rendererSampleCount);
m_RebuildNeeded = true;
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
}
}
if (m_RebuildNeeded) { for (auto& renderer : m_Renderers) { renderer->rebuild(*m_SubRendererCtx); } }
if (m_RefreshNeeded) { for (auto& renderer : m_Renderers) { renderer->refresh(*m_SubRendererCtx); } }
if (m_RedrawNeeded) { this->redraw(); }
m_RebuildNeeded = false;
m_RefreshNeeded = false;
m_RedrawNeeded = false;
return true;
}
template <bool bHorizontalStack, bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
void TBoxAlgorithmStackedContinuousViz<bHorizontalStack, bDrawBorders, TRendererFactoryClass, TRulerClass>::draw()
{
CBoxAlgorithmViz::preDraw();
if (m_RendererCtx->getSelectedCount() != 0)
{
glPushMatrix();
glScalef(1, 1.F / m_RendererCtx->getSelectedCount(), 1);
for (size_t i = 0; i < m_RendererCtx->getSelectedCount(); ++i)
{
glPushAttrib(GL_ALL_ATTRIB_BITS);
glPushMatrix();
glColor4f(m_Color.r, m_Color.g, m_Color.b, m_RendererCtx->getTranslucency());
glTranslatef(0, m_RendererCtx->getSelectedCount() - i - 1.F, 0);
if (!bHorizontalStack)
{
glScalef(1, -1, 1);
glRotatef(-90, 0, 0, 1);
}
m_SubRendererCtx->setAspect(m_RendererCtx->getAspect());
m_SubRendererCtx->setStackCount(m_RendererCtx->getSelectedCount());
m_SubRendererCtx->setStackIndex(i);
m_Renderers[m_RendererCtx->getSelected(i)]->render(*m_SubRendererCtx);
if (bDrawBorders)
{
glDisable(GL_TEXTURE_1D);
glDisable(GL_BLEND);
glColor3f(0, 0, 0);
glBegin(GL_LINE_LOOP);
glVertex2f(0, 0);
glVertex2f(1, 0);
glVertex2f(1, 1);
glVertex2f(0, 1);
glEnd();
}
glPopMatrix();
glPopAttrib();
}
glPopMatrix();
}
CBoxAlgorithmViz::postDraw();
}
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,375 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include "mCBoxAlgorithmViz.hpp"
#if defined TARGET_OS_Windows
#define snprintf _snprintf
#endif
namespace OpenViBE {
namespace AdvancedVisualization {
template <bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
class TBoxAlgorithmStackedInstantViz : public CBoxAlgorithmViz
{
public:
TBoxAlgorithmStackedInstantViz(const CIdentifier& classId, const std::vector<int>& parameters);
bool initialize() override;
bool uninitialize() override;
bool process() override;
_IsDerivedFromClass_Final_(CBoxAlgorithmViz, m_ClassID)
Toolkit::TStimulationDecoder<TBoxAlgorithmStackedInstantViz<bDrawBorders, TRendererFactoryClass, TRulerClass>> m_StimDecoder;
Toolkit::TStreamedMatrixDecoder<TBoxAlgorithmStackedInstantViz<bDrawBorders, TRendererFactoryClass, TRulerClass>> m_MatrixDecoder;
TRendererFactoryClass m_RendererFactory;
std::vector<IRenderer*> m_Renderers;
protected:
void draw() override;
};
class CBoxAlgorithmStackedInstantVizListener final : public CBoxAlgorithmVizListener
{
public:
CBoxAlgorithmStackedInstantVizListener(const std::vector<int>& parameters) : CBoxAlgorithmVizListener(parameters) { }
bool onInputTypeChanged(Kernel::IBox& box, const size_t index) override
{
CIdentifier typeID = CIdentifier::undefined();
box.getInputType(index, typeID);
if (!this->getTypeManager().isDerivedFromStream(typeID, OV_TypeId_TimeFrequency)) { box.setInputType(index, OV_TypeId_TimeFrequency); }
box.setInputType(1, OV_TypeId_Stimulations);
return true;
}
};
template <bool bDrawBorders, class TRendererFactoryClass, class TRulerClass = IRuler>
class TBoxAlgorithmStackedInstantVizDesc : public CBoxAlgorithmVizDesc
{
public:
TBoxAlgorithmStackedInstantVizDesc(const CString& name, const CIdentifier& descClassID, const CIdentifier& classID,
const CString& addedSoftwareVersion, const CString& updatedSoftwareVersion,
const CParameterSet& parameterSet, const CString& shortDesc, const CString& detailedDesc)
: CBoxAlgorithmVizDesc(name, descClassID, classID, addedSoftwareVersion, updatedSoftwareVersion, parameterSet, shortDesc, detailedDesc) { }
Plugins::IPluginObject* create() override
{
return new TBoxAlgorithmStackedInstantViz<bDrawBorders, TRendererFactoryClass, TRulerClass>(m_ClassID, m_Parameters);
}
Plugins::IBoxListener* createBoxListener() const override { return new CBoxAlgorithmStackedInstantVizListener(m_Parameters); }
CString getCategory() const override { return CString("Advanced Visualization/") + m_CategoryName; }
_IsDerivedFromClass_Final_(OpenViBE::Plugins::IBoxAlgorithmDesc, m_DescClassID)
};
template <bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
TBoxAlgorithmStackedInstantViz<bDrawBorders, TRendererFactoryClass, TRulerClass>::TBoxAlgorithmStackedInstantViz(
const CIdentifier& classId, const std::vector<int>& parameters)
: CBoxAlgorithmViz(classId, parameters) { }
template <bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmStackedInstantViz<bDrawBorders, TRendererFactoryClass, TRulerClass>::initialize()
{
const bool res = CBoxAlgorithmViz::initialize();
m_MatrixDecoder.initialize(*this, 0);
m_StimDecoder.initialize(*this, 1);
m_RendererCtx->clear();
m_RendererCtx->setTranslucency(float(m_Translucency));
m_RendererCtx->scaleBy(float(m_DataScale));
m_RendererCtx->setPositiveOnly(true);
m_RendererCtx->setAxisDisplay(m_ShowAxis);
m_RendererCtx->setParentRendererContext(&getContext());
m_SubRendererCtx->clear();
m_SubRendererCtx->setParentRendererContext(m_RendererCtx);
m_Ruler = new TRulerClass;
m_Ruler->setRendererContext(m_RendererCtx);
CMatrix gradientMatrix;
VisualizationToolkit::ColorGradient::parse(gradientMatrix, m_ColorGradient);
for (size_t step = 0; step < gradientMatrix.getDimensionSize(1); ++step)
{
const double currentStepValue = gradientMatrix.getBuffer()[4 * step + 0];
gradientMatrix.getBuffer()[4 * step + 0] = (currentStepValue / 100.0) * 50.0 + 50.0;
}
VisualizationToolkit::ColorGradient::format(m_ColorGradient, gradientMatrix);
return res;
}
template <bool TDrawBorders, class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmStackedInstantViz<TDrawBorders, TRendererFactoryClass, TRulerClass>::uninitialize()
{
for (size_t i = 0; i < m_Renderers.size(); ++i) { m_RendererFactory.release(m_Renderers[i]); }
m_Renderers.clear();
delete m_SubRendererCtx;
m_SubRendererCtx = nullptr;
delete m_RendererCtx;
m_RendererCtx = nullptr;
delete m_Ruler;
m_Ruler = nullptr;
m_StimDecoder.uninitialize();
m_MatrixDecoder.uninitialize();
return CBoxAlgorithmViz::uninitialize();
}
template <bool TDrawBorders, class TRendererFactoryClass, class TRulerClass>
bool TBoxAlgorithmStackedInstantViz<TDrawBorders, TRendererFactoryClass, TRulerClass>::process()
{
const Kernel::IBox& staticBoxContext = this->getStaticBoxContext();
Kernel::IBoxIO& dynamicBoxContext = this->getDynamicBoxContext();
for (size_t chunk = 0; chunk < dynamicBoxContext.getInputChunkCount(0); ++chunk)
{
m_MatrixDecoder.decode(chunk);
CMatrix* inputMatrix = m_MatrixDecoder.getOutputMatrix();
const size_t nChannel = inputMatrix->getDimensionSize(0);
if (nChannel == 0)
{
this->getLogManager() << Kernel::LogLevel_Error << "Input stream " << chunk << " has 0 channels\n";
return false;
}
if (m_MatrixDecoder.isHeaderReceived())
{
for (auto renderer : m_Renderers) { m_RendererFactory.release(renderer); }
m_Renderers.clear();
m_Renderers.resize(nChannel);
m_SubRendererCtx->clear();
m_SubRendererCtx->setParentRendererContext(m_RendererCtx);
m_SubRendererCtx->setTimeLocked(false);
m_SubRendererCtx->setStackCount(nChannel);
m_SubRendererCtx->setPositiveOnly(true);
m_RendererCtx->clear();
m_RendererCtx->setTranslucency(float(m_Translucency));
m_RendererCtx->setTimeScale(1);
m_RendererCtx->scaleBy(float(m_DataScale));
m_RendererCtx->setParentRendererContext(&getContext());
m_RendererCtx->setTimeLocked(false);
m_RendererCtx->setXYZPlotDepth(false);
m_RendererCtx->setPositiveOnly(true);
if (m_TypeID == OV_TypeId_TimeFrequency)
{
GtkTreeIter gtkTreeIter;
gtk_list_store_clear(m_ChannelListStore);
const size_t frequencyCount = inputMatrix->getDimensionSize(1);
const size_t nSample = inputMatrix->getDimensionSize(2);
// I do not know what this is for...
for (size_t frequency = 0; frequency < frequencyCount; ++frequency)
{
try
{
const double frequencyValue = std::stod(inputMatrix->getDimensionLabel(1, frequency), nullptr);
const int stringSize = snprintf(nullptr, 0, "%.2f", frequencyValue) + 1;
if (stringSize > 0)
{
std::unique_ptr<char[]> buffer(new char[stringSize]);
snprintf(buffer.get(), size_t(stringSize), "%.2f", frequencyValue);
m_RendererCtx->setDimensionLabel(1, frequencyCount - frequency - 1, buffer.get());
}
}
catch (...) { m_RendererCtx->setDimensionLabel(1, frequencyCount - frequency - 1, "NaN"); }
m_SubRendererCtx->addChannel("", 0, 0, 0);
}
m_RendererCtx->setDataType(CRendererContext::EDataType::TimeFrequency);
m_SubRendererCtx->setDataType(CRendererContext::EDataType::TimeFrequency);
m_RendererCtx->setElementCount(nSample);
m_SubRendererCtx->setElementCount(nSample);
gtk_tree_view_set_model(m_ChannelTreeView, nullptr);
for (size_t channel = 0; channel < nChannel; ++channel)
{
std::string channelName = trim(inputMatrix->getDimensionLabel(0, channel));
std::string lowercaseChannelName = channelName;
std::transform(channelName.begin(), channelName.end(), lowercaseChannelName.begin(), tolower);
const CVertex v = m_ChannelPositions[lowercaseChannelName];
if (channelName.empty()) { channelName = "Channel " + std::to_string(channel + 1); }
m_Renderers[channel] = m_RendererFactory.create();
// The channels in the sub-renderer are the frequencies in the spectrum
m_Renderers[channel]->setChannelCount(frequencyCount);
m_Renderers[channel]->setSampleCount(nSample);
m_RendererCtx->addChannel(channelName, v.x, v.y, v.z);
gtk_list_store_append(m_ChannelListStore, &gtkTreeIter);
gtk_list_store_set(m_ChannelListStore, &gtkTreeIter, 0, channel + 1, 1, channelName.c_str(), -1);
}
gtk_tree_view_set_model(m_ChannelTreeView, GTK_TREE_MODEL(m_ChannelListStore));
gtk_tree_selection_select_all(gtk_tree_view_get_selection(m_ChannelTreeView));
}
else
{
this->getLogManager() << Kernel::LogLevel_Error << "Input stream type is not supported\n";
return false;
}
m_Ruler->setRenderer(nChannel ? m_Renderers[0] : nullptr);
m_RebuildNeeded = true;
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
if (m_MatrixDecoder.isBufferReceived())
{
if (m_TypeID == OV_TypeId_TimeFrequency)
{
m_Time1 = m_Time2;
m_Time2 = dynamicBoxContext.getInputChunkEndTime(0, chunk);
const size_t frequencyCount = inputMatrix->getDimensionSize(1);
const size_t nSample = inputMatrix->getDimensionSize(2);
const uint64_t chunkDuration = dynamicBoxContext.getInputChunkEndTime(0, chunk) - dynamicBoxContext.getInputChunkStartTime(0, chunk);
const uint64_t sampleDuration = chunkDuration / nSample;
m_SubRendererCtx->setSampleDuration(sampleDuration);
m_RendererCtx->setSampleDuration(sampleDuration);
for (size_t channel = 0; channel < nChannel; ++channel)
{
// Feed renderer with actual samples
for (size_t sample = 0; sample < nSample; ++sample)
{
m_Swaps.resize(frequencyCount);
for (size_t frequency = 0; frequency < frequencyCount; ++frequency)
{
m_Swaps[frequencyCount - frequency - 1] = float(
inputMatrix->getBuffer()[sample + frequency * nSample + channel * nSample * frequencyCount]);
}
m_Renderers[channel]->feed(&m_Swaps[0]);
}
}
m_RefreshNeeded = true;
m_RedrawNeeded = true;
}
}
}
if (staticBoxContext.getInputCount() > 1)
{
for (size_t i = 0; i < dynamicBoxContext.getInputChunkCount(1); ++i)
{
m_StimDecoder.decode(i);
if (m_StimDecoder.isBufferReceived())
{
IStimulationSet* stimSet = m_StimDecoder.getOutputStimulationSet();
for (size_t j = 0; j < stimSet->getStimulationCount(); ++j)
{
m_Renderers[0]->feed(stimSet->getStimulationDate(j), stimSet->getStimulationIdentifier(j));
m_RedrawNeeded = true;
}
}
}
}
if (m_RebuildNeeded) { for (auto& renderer : m_Renderers) { renderer->rebuild(*m_SubRendererCtx); } }
if (m_RefreshNeeded) { for (auto& renderer : m_Renderers) { renderer->refresh(*m_SubRendererCtx); } }
if (m_RedrawNeeded) { this->redraw(); }
m_RebuildNeeded = false;
m_RefreshNeeded = false;
m_RedrawNeeded = false;
return true;
}
template <bool bDrawBorders, class TRendererFactoryClass, class TRulerClass>
void TBoxAlgorithmStackedInstantViz<bDrawBorders, TRendererFactoryClass, TRulerClass>::draw()
{
CBoxAlgorithmViz::preDraw();
if (m_RendererCtx->getSelectedCount() != 0)
{
glPushMatrix();
glScalef(1, 1.F / m_RendererCtx->getSelectedCount(), 1);
for (size_t i = 0; i < m_RendererCtx->getSelectedCount(); ++i)
{
glPushAttrib(GL_ALL_ATTRIB_BITS);
glPushMatrix();
glColor4f(m_Color.r, m_Color.g, m_Color.b, m_RendererCtx->getTranslucency());
glTranslatef(0, m_RendererCtx->getSelectedCount() - i - 1.F, 0);
m_SubRendererCtx->setAspect(m_RendererCtx->getAspect());
m_SubRendererCtx->setStackCount(m_RendererCtx->getSelectedCount());
m_SubRendererCtx->setStackIndex(i);
m_Renderers[m_RendererCtx->getSelected(i)]->render(*m_SubRendererCtx);
if (bDrawBorders)
{
glDisable(GL_TEXTURE_1D);
glDisable(GL_BLEND);
glColor3f(0, 0, 0);
glBegin(GL_LINE_LOOP);
glVertex2f(0, 0);
glVertex2f(1, 0);
glVertex2f(1, 1);
glVertex2f(0, 1);
glEnd();
}
glPopMatrix();
glPopAttrib();
}
glPopMatrix();
}
CBoxAlgorithmViz::postDraw();
}
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,354 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "m_defines.hpp"
#include "m_VisualizationTools.hpp"
#include "m_GtkGL.hpp"
#include <openvibe/ov_all.h>
#include <visualization-toolkit/ovviz_all.h>
#if defined TARGET_OS_Windows
#include <Windows.h>
#endif
#include <gtk/gtk.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <string>
// #define __DIRECT_RENDER__
namespace OpenViBE {
namespace AdvancedVisualization {
template <class TBox>
class TGtkGLWidget
{
public:
TGtkGLWidget() : m_box(nullptr) { }
virtual ~TGtkGLWidget()
{
if (m_widget)
{
if (m_textureId)
{
GtkGL::preRender(m_widget);
glDeleteTextures(1, &m_textureId);
GtkGL::postRender(m_widget);
}
if (m_timeoutSrc) { g_source_destroy(m_timeoutSrc); }
GtkGL::uninitialize(m_widget);
}
}
virtual void initialize(TBox& box, GtkWidget* widget, GtkWidget* left, GtkWidget* right, GtkWidget* bottom)
{
GtkGL::initialize(widget);
{
m_box = &box;
m_widget = widget;
m_left = left;
m_right = right;
m_bottom = bottom;
::g_signal_connect(widget, "configure-event", G_CALLBACK(TGtkGLWidget<TBox>::configureCB), m_box);
::g_signal_connect(widget, "expose-event", G_CALLBACK(TGtkGLWidget<TBox>::exposeCB), m_box);
::g_signal_connect(widget, "button-press-event", G_CALLBACK(TGtkGLWidget<TBox>::mouseButtonCB), m_box);
::g_signal_connect(widget, "button-release-event", G_CALLBACK(TGtkGLWidget<TBox>::mouseButtonCB), m_box);
::g_signal_connect(widget, "motion-notify-event", G_CALLBACK(TGtkGLWidget<TBox>::motionNotifyCB), m_box);
::g_signal_connect(widget, "enter-notify-event", G_CALLBACK(TGtkGLWidget<TBox>::enterNotifyCB), m_box);
::g_signal_connect(gtk_widget_get_parent(widget), "key-press-event", G_CALLBACK(TGtkGLWidget<TBox>::keyPressCB), m_box);
::g_signal_connect(gtk_widget_get_parent(widget), "key-release-event", G_CALLBACK(TGtkGLWidget<TBox>::keyReleaseCB), m_box);
::g_signal_connect_after(left, "expose-event", G_CALLBACK(TGtkGLWidget<TBox>::exposeLeftCB), m_box);
::g_signal_connect_after(right, "expose-event", G_CALLBACK(TGtkGLWidget<TBox>::exposeRightCB), m_box);
::g_signal_connect_after(bottom, "expose-event", G_CALLBACK(TGtkGLWidget<TBox>::exposeBottomCB), m_box);
m_timeoutSrc = g_timeout_source_new(250); // timeouts every 50 ms
g_source_set_priority(m_timeoutSrc, G_PRIORITY_LOW);
g_source_set_callback(m_timeoutSrc, GSourceFunc(timeoutRedrawCB), m_box, nullptr);
g_source_attach(m_timeoutSrc, nullptr);
gtk_widget_queue_resize(widget);
}
}
virtual void redrawTopLevelWindow(const bool immediate = false)
{
GtkWidget* top = gtk_widget_get_toplevel(m_widget);
if (top != nullptr)
{
if (immediate)
{
gdk_window_process_updates(top->window, false);
gtk_widget_queue_draw(top);
}
else { gdk_window_invalidate_rect(top->window, nullptr, true); }
}
}
virtual void redraw(const bool immediate = false)
{
if (immediate)
{
gdk_window_process_updates(m_widget->window, false);
gtk_widget_queue_draw(m_widget);
}
else { gdk_window_invalidate_rect(m_widget->window, nullptr, true); }
}
virtual void redrawLeft(const bool immediate = false)
{
if (immediate)
{
gdk_window_process_updates(m_left->window, false);
gtk_widget_queue_draw(m_left);
}
else { gdk_window_invalidate_rect(m_left->window, nullptr, true); }
}
virtual void redrawRight(const bool immediate = false)
{
if (immediate)
{
gdk_window_process_updates(m_right->window, false);
gtk_widget_queue_draw(m_right);
}
else { gdk_window_invalidate_rect(m_right->window, nullptr, true); }
}
virtual void redrawBottom(const bool immediate = false)
{
if (immediate)
{
gdk_window_process_updates(m_bottom->window, false);
gtk_widget_queue_draw(m_bottom);
}
else { gdk_window_invalidate_rect(m_bottom->window, nullptr, true); }
}
virtual void setPointSmoothingActive(const bool active = false)
{
if (active) { glEnable(GL_POINT_SMOOTH); }
else { glDisable(GL_POINT_SMOOTH); }
}
virtual uint32_t createTexture(const std::string& value)
{
#define M_GRADIENT_SIZE 128
if (m_textureId == 0)
{
const std::string str = (value.empty() ? "0:0,0,100; 25:0,100,100; 50:0,49,0; 75:100,100,0; 100:100,0,0" : value);
CMatrix gradientBase, gradient;
VisualizationToolkit::ColorGradient::parse(gradientBase, str.c_str());
VisualizationToolkit::ColorGradient::interpolate(gradient, gradientBase, M_GRADIENT_SIZE);
float texture[M_GRADIENT_SIZE][3];
for (size_t i = 0; i < M_GRADIENT_SIZE; ++i)
{
texture[i][0] = float(gradient[i * 4 + 1] * .01);
texture[i][1] = float(gradient[i * 4 + 2] * .01);
texture[i][2] = float(gradient[i * 4 + 3] * .01);
}
glGenTextures(1, &m_textureId);
glBindTexture(GL_TEXTURE_1D, m_textureId);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP);
//glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
//glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, __SIZE__, 0, GL_RGB, GL_UNSIGNED_BYTE, texture);
gluBuild1DMipmaps(GL_TEXTURE_1D, GL_RGB, M_GRADIENT_SIZE, GL_RGB, GL_FLOAT, texture);
}
#undef M_GRADIENT_SIZE
return m_textureId;
}
protected:
GtkWidget* m_widget = nullptr;
GtkWidget* m_left = nullptr;
GtkWidget* m_right = nullptr;
GtkWidget* m_bottom = nullptr;
GSource* m_timeoutSrc = nullptr;
TBox* m_box = nullptr;
uint32_t m_textureId = 0;
private:
static gboolean timeoutRedrawCB(TBox* box)
{
box->redraw();
return TRUE;
}
static gboolean configureCB(GtkWidget* widget, GdkEventConfigure* /*event*/, TBox* box)
{
GtkGL::preRender(widget);
glViewport(0, 0, widget->allocation.width, widget->allocation.height);
box->reshape(widget->allocation.width, widget->allocation.height);
GtkGL::postRender(widget);
return TRUE;
}
static gboolean exposeCB(GtkWidget* widget, GdkEventExpose* /*event*/, TBox* box)
{
const float d = 1.F;
const float dx = d / (widget->allocation.width - d);
const float dy = d / (widget->allocation.height - d);
GtkGL::preRender(widget);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslatef(0.5, 0, 0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0 - dx, 1 + dx, 0 - dy, 1 + dy);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glLineWidth(1);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glEnable(GL_TEXTURE_1D);
glDisable(GL_DEPTH_TEST);
glClearDepth(100);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(1, 1, 1);
// Lighting
const float fAmbient = 0.0F;
const float fDiffuse = 1.0F;
const float fSpecular = 1.0F;
GLfloat ambient[] = { fAmbient, fAmbient, fAmbient, 1 };
GLfloat diffuse[] = { fDiffuse, fDiffuse, fDiffuse, 1 };
GLfloat specular[] = { fSpecular, fSpecular, fSpecular, 1 };
GLfloat position0[] = { 3, 1, 2, 1 };
GLfloat position1[] = { -3, 0, -2, 1 };
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glLightfv(GL_LIGHT0, GL_POSITION, position0);
glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT1, GL_SPECULAR, specular);
glLightfv(GL_LIGHT1, GL_POSITION, position1);
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_LIGHTING);
box->draw();
GtkGL::postRender(widget);
return TRUE;
}
static gboolean enterNotifyCB(GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, TBox* box)
{
box->redraw();
//box->request();
//box->m_redrawNeeded = true;
return TRUE;
}
static gboolean exposeLeftCB(GtkWidget* /*widget*/, GdkEventExpose* /*event*/, TBox* box)
{
box->drawLeft();
return TRUE;
}
static gboolean exposeRightCB(GtkWidget* /*widget*/, GdkEventExpose* /*event*/, TBox* box)
{
box->drawRight();
return TRUE;
}
static gboolean exposeBottomCB(GtkWidget* /*widget*/, GdkEventExpose* /*event*/, TBox* box)
{
box->drawBottom();
return TRUE;
}
static gboolean mouseButtonCB(GtkWidget* /*widget*/, GdkEventButton* event, TBox* box)
{
int status = 0;
switch (event->type)
{
case GDK_BUTTON_PRESS: status = 1;
break;
case GDK_2BUTTON_PRESS: status = 2;
break;
case GDK_3BUTTON_PRESS: status = 3;
break;
default: break;
}
box->mouseButton(int(event->x), int(event->y), event->button, status);
box->draw();
return TRUE;
}
static gboolean motionNotifyCB(GtkWidget* /*widget*/, GdkEventMotion* event, TBox* box)
{
box->mouseMotion(int(event->x), int(event->y));
return TRUE;
}
static gboolean keyPressCB(GtkWidget* /*widget*/, GdkEventKey* event, TBox* box)
{
box->keyboard(0, 0, /*event->x, event->y,*/ event->keyval, true);
return TRUE;
}
static gboolean keyReleaseCB(GtkWidget* /*widget*/, GdkEventKey* event, TBox* box)
{
box->keyboard(0, 0, /*event->x, event->y,*/ event->keyval, false);
return TRUE;
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,311 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "m_GtkGL.hpp"
#include "m_defines.hpp"
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#if defined TARGET_OS_Windows
#include <windows.h>
#include <gdk/gdkwin32.h>
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
#include <gdk/gdkx.h>
#include <GL/glx.h>
#else
#error unsupported platform
#endif
// ###########################################################################################################################################################
// ###########################################################################################################################################################
//
// GtkGL implementation
//
// ###########################################################################################################################################################
// ###########################################################################################################################################################
#define GTK_GL_RENDERING_CONTEXT_NAME "GL Rendering Context"
#define GTK_GL_DEVICE_CONTEXT_NAME "Device Context"
#define GTK_GL_DEBUG(s) // g_debug("GtkGL : "#s);
#define GTK_GL_WARNING(s) g_warning("GtkGL : "#s);
#if defined TARGET_OS_Windows
namespace {
typedef bool (*gl_swap_interval_ext_t)(int);
gl_swap_interval_ext_t wglSwapIntervalEXT = nullptr;
} // namespace
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
namespace
{
typedef void (*glXSwapIntervalEXT_t)(Display*, GLXDrawable, int);
glXSwapIntervalEXT_t glXSwapIntervalEXT = nullptr;
}
#endif
// ## WINDOWS ##############################################################################################################################################
#if defined TARGET_OS_Windows
namespace {
void on_realize_cb(GtkWidget* widget, void* /*data*/)
{
GTK_GL_DEBUG("realize-callback");
gdk_window_ensure_native(gtk_widget_get_window(widget));
const HWND window = HWND(GDK_WINDOW_HWND(gtk_widget_get_window(widget)));
const HDC drawingCtx = GetDC(window);
PIXELFORMATDESCRIPTOR pixelFormatDesc;
pixelFormatDesc.nSize = sizeof(pixelFormatDesc);
pixelFormatDesc.nVersion = 1;
pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pixelFormatDesc.iPixelType = PFD_TYPE_RGBA;
pixelFormatDesc.cColorBits = 24;
pixelFormatDesc.cAlphaBits = 8;
pixelFormatDesc.cDepthBits = 32;
pixelFormatDesc.iLayerType = PFD_MAIN_PLANE;
const int pixelFormatID = ChoosePixelFormat(drawingCtx, &pixelFormatDesc);
if (pixelFormatID == 0)
{
GTK_GL_WARNING("ChoosePixelFormat failed");
GTK_GL_DEBUG("realize-callback::failed");
return;
}
if (SetPixelFormat(drawingCtx, pixelFormatID, &pixelFormatDesc) == 0)
{
GTK_GL_WARNING("SetPixelFormat failed");
GTK_GL_DEBUG("realize-callback::failed");
return;
}
const HGLRC glRenderingCtx = wglCreateContext(drawingCtx);
if (glRenderingCtx == nullptr)
{
GTK_GL_WARNING("wglCreateContext failed");
GTK_GL_DEBUG("realize-callback::failed");
return;
}
g_object_set_data(G_OBJECT(widget), GTK_GL_RENDERING_CONTEXT_NAME, glRenderingCtx);
g_object_set_data(G_OBJECT(widget), GTK_GL_DEVICE_CONTEXT_NAME, drawingCtx);
gtk_widget_queue_resize(widget);
gtk_widget_set_double_buffered(widget, FALSE);
wglSwapIntervalEXT = gl_swap_interval_ext_t(wglGetProcAddress("wglSwapIntervalEXT"));
GTK_GL_DEBUG("realize-callback::success");
}
} // namespace
void OpenViBE::AdvancedVisualization::GtkGL::initialize(GtkWidget* widget)
{
GTK_GL_DEBUG("initialize");
g_signal_connect(widget, "realize", G_CALLBACK(on_realize_cb), nullptr);
GTK_GL_DEBUG("initialize::success");
}
void OpenViBE::AdvancedVisualization::GtkGL::uninitialize(GtkWidget* widget)
{
GTK_GL_DEBUG("uninitialize");
const HWND window = HWND(GDK_WINDOW_HWND(gtk_widget_get_window(widget)));
const auto glRenderingCtx = HGLRC(g_object_get_data(G_OBJECT(widget), GTK_GL_RENDERING_CONTEXT_NAME));
wglDeleteContext(glRenderingCtx);
const HDC drawingCtx = HDC(g_object_get_data(G_OBJECT(widget), GTK_GL_DEVICE_CONTEXT_NAME));
ReleaseDC(window, drawingCtx);
GTK_GL_DEBUG("uninitialize::success");
}
void OpenViBE::AdvancedVisualization::GtkGL::preRender(GtkWidget* widget, const bool verticalSync)
{
GTK_GL_DEBUG("pre-render");
const HDC drawingCtx = HDC(g_object_get_data(G_OBJECT(widget), GTK_GL_DEVICE_CONTEXT_NAME));
const auto glRenderingCtx = HGLRC(g_object_get_data(G_OBJECT(widget), GTK_GL_RENDERING_CONTEXT_NAME));
if (glRenderingCtx == nullptr)
{
GTK_GL_DEBUG("Rendering context not ready");
GTK_GL_DEBUG("pre-render::failed");
return;
}
if (wglMakeCurrent(drawingCtx, glRenderingCtx) == 0)
{
GTK_GL_WARNING("wglMakeCurrent failed");
GTK_GL_DEBUG("pre-render::failed");
return;
}
// Enable / Disable vsync
if (wglSwapIntervalEXT != nullptr) { wglSwapIntervalEXT(verticalSync ? 1 : 0); }
GTK_GL_DEBUG("pre-render::success");
}
void OpenViBE::AdvancedVisualization::GtkGL::postRender(GtkWidget* widget)
{
GTK_GL_DEBUG("post-render");
const HDC drawingCtx = HDC(g_object_get_data(G_OBJECT(widget), GTK_GL_DEVICE_CONTEXT_NAME));
if (drawingCtx == nullptr)
{
GTK_GL_DEBUG("Rendering context not ready");
GTK_GL_DEBUG("post-render::failed");
}
else if (SwapBuffers(drawingCtx) == 0)
{
GTK_GL_WARNING("SwapBuffers failed");
GTK_GL_DEBUG("post-render::failed");
}
else if (wglMakeCurrent(drawingCtx, nullptr) == 0)
{
GTK_GL_WARNING("wglMakeCurrent failed");
GTK_GL_DEBUG("post-render::failed");
}
else { GTK_GL_DEBUG("post-render::success"); }
}
// ## WINDOWS ##############################################################################################################################################
#elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
// ## LINUX ################################################################################################################################################
void OpenViBE::AdvancedVisualization::GtkGL::initialize(GtkWidget * widget)
{
GTK_GL_DEBUG("initialize");
// ::gdk_window_ensure_native(gtk_widget_get_window(widget));
::GdkScreen* screen = ::gdk_screen_get_default();
::Display* display = GDK_SCREEN_XDISPLAY(screen);
::gint screenNumber = GDK_SCREEN_XNUMBER(screen);
int visualInfoAttributes[] =
{
GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_ALPHA_SIZE, 1,
GLX_DOUBLEBUFFER,
GLX_DEPTH_SIZE, 1,
None
};
if (!::glXQueryVersion(display, nullptr, nullptr))
{
GTK_GL_WARNING("initialize::failed");
return;
}
::XVisualInfo* visualInfo = ::glXChooseVisual(display, screenNumber, visualInfoAttributes);
::GLXContext glRenderingCtx = glXCreateContext(display, visualInfo, nullptr, True);
g_object_set_data(G_OBJECT(widget), GTK_GL_RENDERING_CONTEXT_NAME, glRenderingCtx);
#if 1
/* Fix up colormap */
::GdkVisual* visual = ::gdk_x11_screen_lookup_visual(screen, visualInfo->visualid);
::GdkColormap* colorMap = ::gdk_colormap_new(visual, FALSE);
::gtk_widget_set_colormap(widget, colorMap);
#endif
::gtk_widget_queue_resize(widget);
::gtk_widget_set_double_buffered(widget, FALSE);
glXSwapIntervalEXT = (glXSwapIntervalEXT_t) ::glXGetProcAddressARB(reinterpret_cast <const unsigned char*>("glXSwapIntervalEXT"));
GTK_GL_DEBUG("initialize::success");
}
void OpenViBE::AdvancedVisualization::GtkGL::uninitialize(GtkWidget * widget)
{
GTK_GL_DEBUG("uninitialize");
::Display* display = GDK_SCREEN_XDISPLAY(gtk_widget_get_screen(widget));
::GLXContext glRenderingCtx = (GLXContext) g_object_get_data(G_OBJECT(widget), GTK_GL_RENDERING_CONTEXT_NAME);
if (!display || !glRenderingCtx)
{
GTK_GL_WARNING("uninitialize::failed");
return;
}
::glXMakeCurrent(display, None, nullptr);
::glXDestroyContext(display, glRenderingCtx);
GTK_GL_DEBUG("uninitialize::success");
}
void OpenViBE::AdvancedVisualization::GtkGL::preRender(GtkWidget * widget, bool bVerticalSync)
{
GTK_GL_DEBUG("pre-render");
::Display* display = GDK_SCREEN_XDISPLAY(gtk_widget_get_screen(widget));
::Window window = GDK_WINDOW_XID(gtk_widget_get_window(widget));
::GLXContext glRenderingCtx = (GLXContext) g_object_get_data(G_OBJECT(widget), GTK_GL_RENDERING_CONTEXT_NAME);
if (!display || !glRenderingCtx)
{
GTK_GL_WARNING("pre-render::failed");
return;
}
::glXMakeCurrent(display, window, glRenderingCtx);
// Enable / Disable vsync
if (glXSwapIntervalEXT)
{
glXSwapIntervalEXT(display, ::glXGetCurrentDrawable(), bVerticalSync ? 1 : 0);
}
GTK_GL_DEBUG("pre-render::success");
}
void OpenViBE::AdvancedVisualization::GtkGL::postRender(GtkWidget * widget)
{
GTK_GL_DEBUG("post-render");
::Display* display = GDK_SCREEN_XDISPLAY(gtk_widget_get_screen(widget));
::Window window = GDK_WINDOW_XID(gtk_widget_get_window(widget));
if (!display)
{
GTK_GL_WARNING("post-render::failed");
return;
}
::glXSwapBuffers(display, window);
GTK_GL_DEBUG("post-render::success");
}
// ## LINUX ################################################################################################################################################
#endif
@@ -0,0 +1,35 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gtk/gtk.h>
namespace OpenViBE {
namespace AdvancedVisualization {
namespace GtkGL {
void initialize(GtkWidget* widget);
void uninitialize(GtkWidget* widget);
void preRender(GtkWidget* widget, bool verticalSync = false);
void postRender(GtkWidget* widget);
} // namespace GtkGL
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,44 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "m_VisualizationTools.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
std::string trim(const std::string& value)
{
if (value.length() == 0) { return ""; }
size_t i = 0;
size_t j = value.length() - 1;
while (i < value.length() && value[i] == ' ') { i++; }
while (j > i && value[j] == ' ') { j--; }
return value.substr(i, j - i + 1);
}
CRendererContext& getContext()
{
static CRendererContext* ctx = new CRendererContext();
return *ctx;
}
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,34 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "m_defines.hpp"
#include <mensia/advanced-visualization.hpp>
#include <string>
namespace OpenViBE {
namespace AdvancedVisualization {
std::string trim(const std::string& value);
CRendererContext& getContext();
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,29 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
// Temporal coherence definition
#define OVP_TypeId_TemporalCoherence OpenViBE::CIdentifier(0x8F02E3F6, 0xFFB00F4B)
enum class ETemporalCoherence { TimeLocked, Independant };
#define OV_TypeId_TimeFrequency OpenViBE::CIdentifier(0x5A90816B, 0xFF2AFF72)
@@ -0,0 +1,221 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "m_defines.hpp"
// #define TARGET_Has_Experimental
#include "mTBoxAlgorithmStackedContinuousViz.hpp"
#include "mTBoxAlgorithmStackedInstantViz.hpp"
#include "mTBoxAlgorithmContinuousViz.hpp"
#include "mTBoxAlgorithmInstantViz.hpp"
#include "mTBoxAlgorithmInstantLoretaViz.hpp"
#include "ruler/mTRulerAutoType.hpp"
#include "ruler/mTRulerPair.hpp"
#include "ruler/mTRulerConditionalPair.hpp"
#include "ruler/mCRulerConditionIsTimeLocked.hpp"
#include "ruler/mCRulerProgress.hpp"
#include "ruler/mCRulerProgressH.hpp"
#include "ruler/mCRulerProgressV.hpp"
#include "ruler/mCRulerERPProgress.hpp"
#include "ruler/mCRulerBottomCount.hpp"
#include "ruler/mCRulerBottomERPCount.hpp"
#include "ruler/mCRulerBottomERPTime.hpp"
#include "ruler/mCRulerBottomFrequency.hpp"
#include "ruler/mCRulerBottomPercent.hpp"
#include "ruler/mCRulerBottomTexture.hpp"
#include "ruler/mCRulerBottomTime.hpp"
#include "ruler/mCRulerLeftChannelNames.hpp"
#include "ruler/mCRulerLeftTexture.hpp"
#include "ruler/mCRulerRightCount.hpp"
#include "ruler/mCRulerRightFrequency.hpp"
#include "ruler/mCRulerRightMonoScale.hpp"
#include "ruler/mCRulerRightTexture.hpp"
#include "ruler/mCRulerRightScale.hpp"
#include "ruler/mCRulerRightLabels.hpp"
#include <system/ovCTime.h>
#include <ctime>
namespace OpenViBE {
namespace AdvancedVisualization {
// Prototype to create and release final renderer instances from the renderer API
template <ERendererType TType, bool TStimulation = false>
class TRendererProto
{
public:
static IRenderer* create() { return IRenderer::create(TType, TStimulation); }
static void release(IRenderer* renderer) { return IRenderer::release(renderer); }
};
// Type definitions of our OpenViBE boxes
typedef TBoxAlgorithmContinuousVizDesc<TRendererProto<ERendererType::Bitmap, true>, TRulerPair<
CRulerProgressV, TRulerPair<
TRulerAutoType<IRuler, TRulerConditionalPair<CRulerBottomTime, CRulerBottomCount, CRulerConditionIsTimeLocked>,
IRuler>, TRulerPair<CRulerLeftChannelNames, CRulerRightTexture>>>> bitmap_t;
typedef TBoxAlgorithmContinuousVizDesc<TRendererProto<ERendererType::Line, true>, TRulerPair<
CRulerProgressV, TRulerPair<
TRulerAutoType<IRuler, TRulerConditionalPair<CRulerBottomTime, CRulerBottomCount, CRulerConditionIsTimeLocked>,
IRuler>, TRulerPair<CRulerLeftChannelNames, CRulerRightScale>>>> oscilloscope_t;
typedef TBoxAlgorithmContinuousVizDesc<TRendererProto<ERendererType::Bars, true>, TRulerPair<
CRulerProgressV, TRulerPair<
TRulerAutoType<IRuler, TRulerConditionalPair<CRulerBottomTime, CRulerBottomCount, CRulerConditionIsTimeLocked>,
IRuler>, TRulerPair<CRulerLeftChannelNames, CRulerRightScale>>>> bars_t;
typedef TBoxAlgorithmContinuousVizDesc<TRendererProto<ERendererType::MultiLine, true>, TRulerPair<
CRulerProgressV, TRulerPair<
TRulerAutoType<IRuler, TRulerConditionalPair<CRulerBottomTime, CRulerBottomCount, CRulerConditionIsTimeLocked>,
IRuler>, TRulerPair<TRulerPair<CRulerLeftChannelNames, CRulerLeftTexture>, CRulerRightMonoScale>>>> MOscilloscope;
typedef TBoxAlgorithmContinuousVizDesc<TRendererProto<ERendererType::XYZPlot>> xyz_plot_t;
typedef TBoxAlgorithmInstantVizDesc<TRendererProto<ERendererType::Bitmap>, TRulerPair<
CRulerERPProgress, TRulerPair<
TRulerAutoType<CRulerBottomERPCount, CRulerBottomERPTime, CRulerBottomFrequency>, TRulerPair<
CRulerLeftChannelNames, CRulerRightTexture>>>> i_bitmap_t;
typedef TBoxAlgorithmInstantVizDesc<TRendererProto<ERendererType::Line>, TRulerPair<
CRulerERPProgress, TRulerPair<
TRulerAutoType<CRulerBottomERPCount, CRulerBottomERPTime, CRulerBottomFrequency>, TRulerPair<
CRulerLeftChannelNames, CRulerRightScale>>>> i_oscilloscope_t;
typedef TBoxAlgorithmInstantVizDesc<TRendererProto<ERendererType::Bars>, TRulerPair<
CRulerERPProgress, TRulerPair<
TRulerAutoType<CRulerBottomERPCount, CRulerBottomERPTime, CRulerBottomFrequency>, TRulerPair<
CRulerLeftChannelNames, CRulerRightScale>>>> i_bars_t;
typedef TBoxAlgorithmInstantVizDesc<TRendererProto<ERendererType::MultiLine>, TRulerPair<
CRulerERPProgress, TRulerPair<
TRulerAutoType<CRulerBottomERPCount, CRulerBottomERPTime, CRulerBottomFrequency>, TRulerPair<
TRulerPair<CRulerLeftChannelNames, CRulerLeftTexture>, CRulerRightMonoScale>>>> MIOscilloscope;
typedef TBoxAlgorithmInstantVizDesc<TRendererProto<ERendererType::XYZPlot>> i_xyz_plot_t;
typedef TBoxAlgorithmStackedContinuousVizDesc<false, true, TRendererProto<ERendererType::Bitmap, true>, TRulerPair<
TRulerAutoType<CRulerBottomERPCount, CRulerBottomERPTime, CRulerBottomFrequency>, TRulerPair<
CRulerLeftChannelNames, CRulerProgressH>>> sv_bitmap_t;
typedef TBoxAlgorithmStackedContinuousVizDesc<true, true, TRendererProto<ERendererType::Bitmap, true>, TRulerPair<
TRulerConditionalPair<CRulerBottomTime, CRulerBottomCount, CRulerConditionIsTimeLocked>, TRulerPair<
TRulerAutoType<IRuler, IRuler, CRulerRightFrequency>, TRulerPair<CRulerLeftChannelNames, CRulerProgressV>>
>> sh_bitmap_t;
typedef TBoxAlgorithmStackedInstantVizDesc<true, TRendererProto<ERendererType::Bitmap, true>, TRulerPair<
CRulerBottomERPTime, TRulerPair<CRulerRightLabels<1>, TRulerPair<CRulerLeftChannelNames, CRulerProgressV>>>>
si_bitmap_t;
typedef TBoxAlgorithmInstantVizDesc<TRendererProto<ERendererType::Topography2D>, CRulerBottomTexture> topography_2d_t;
typedef TBoxAlgorithmInstantVizDesc<TRendererProto<ERendererType::Topography3D>, CRulerBottomTexture> topography_3d_t;
typedef TBoxAlgorithmInstantVizDesc<TRendererProto<ERendererType::Loreta>, CRulerBottomTexture, TBoxAlgorithmInstantLoretaViz> loreta_t;
typedef TBoxAlgorithmInstantVizDesc<TRendererProto<ERendererType::Cube>, CRulerBottomTexture> cubes_t;
OVP_Declare_Begin()
context.getTypeManager().registerEnumerationType(OVP_TypeId_TemporalCoherence, "Temporal Coherence");
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_TemporalCoherence, "Time Locked", size_t(ETemporalCoherence::TimeLocked));
context.getTypeManager().registerEnumerationEntry(OVP_TypeId_TemporalCoherence, "Independant", size_t(ETemporalCoherence::Independant));
OVP_Declare_New(bitmap_t("Continuous bitmap_t", CIdentifier(0x0BE99978487D3DC6), CIdentifier(0x9E1CD34586569E7E), "0.0.0", "0.0.0",
CParameterSet(I_Matrix,I_Stimulations, S_ChannelLocalisation, S_TemporalCoherence, S_TimeScale, S_ElementCount, S_DataScale, S_Caption, S_ColorGradient,
P_None),
"Displays the input matrices as a map of colored tiles, or bitmap, continuously.", ""))
OVP_Declare_New(oscilloscope_t("Continuous oscilloscope_t", CIdentifier(0x6D63896D86685134), CIdentifier(0x0842BCD1D53C1C89), "0.0.0", "0.0.0",
CParameterSet(I_Signal, I_Stimulations, S_ChannelLocalisation, S_TemporalCoherence, S_TimeScale, S_ElementCount, S_DataPositive, S_DataScale, S_Caption,
S_Translucency, S_Color, P_None),
"Displays the input matrices as a series of curves.\nChannels are vertically distributed.",""))
OVP_Declare_New(bars_t("Continuous bars_t", CIdentifier(0xE6B2C8A8B43DE2C3), CIdentifier(0x24510F3EFCBE83A2), "0.0.0", "0.0.0",
CParameterSet(I_Matrix,I_Stimulations, S_ChannelLocalisation, S_TemporalCoherence, S_TimeScale, S_ElementCount, S_DataPositive, S_DataScale, S_Caption,
S_Translucency,S_ColorGradient, P_None),
"Displays the input matrices as a series of colored bars.\nChannels are vertically distributed.",""))
OVP_Declare_New(MOscilloscope("Continuous Multi oscilloscope_t", CIdentifier(0xD4C4DCEE6F5D47E1), CIdentifier(0xA927FB20C4089F99), "0.0.0", "0.0.0",
CParameterSet(I_Signal, I_Stimulations, S_ChannelLocalisation, S_TemporalCoherence, S_TimeScale, S_ElementCount, S_DataPositive, S_DataScale,
S_Caption, S_Translucency, S_ColorGradient, P_None),
"Displays the input matrices as a series of curves.\nAll channels are rendered on the same vertical axis, with different colors.", ""))
OVP_Declare_New(xyz_plot_t("Continuous XYZ Plot", CIdentifier(0xAAFD16CF57DC7138), CIdentifier(0xDDA9A9D9D04F2C14), "0.0.0", "0.0.0",
CParameterSet(I_Signal, S_TemporalCoherence, S_TimeScale, S_ElementCount, S_DataScale, S_Caption, S_Translucency, S_ShowAxis,S_XYZPlotHasDepth,
S_ColorGradient, F_FixedChannelOrder, F_FixedChannelSelection, P_None),
"Displays the input matrices as a series of points in 2D or 3D space, continuously.", ""))
OVP_Declare_New(i_bitmap_t("Instant bitmap_t", CIdentifier(0x942CDA76A437E83E), CIdentifier(0xF862765845247823), "0.0.0", "0.0.0",
CParameterSet(I_Spectrum, S_ChannelLocalisation, S_DataScale, S_Caption, S_ColorGradient, P_None),
"Displays each and every input matrix as a map of colored tiles, or bitmap, instantly.", ""))
OVP_Declare_New(i_oscilloscope_t("Instant oscilloscope_t", CIdentifier(0x1207B96D19A805B4), CIdentifier(0x997B654D0CDB3102), "0.0.0", "0.0.0",
CParameterSet(I_Signal, S_ChannelLocalisation, S_DataPositive, S_DataScale, S_Caption, S_Translucency, S_Color, F_CanAddInput, P_None),
"Displays each and every input matrix as a series of curves, instantly.\nChannels are vertically distributed.", ""))
OVP_Declare_New(i_bars_t("Instant bars_t", CIdentifier(0xD401BE1F25F01E4E), CIdentifier(0xECB4608196DA0D49), "0.0.0", "0.0.0",
CParameterSet(I_Spectrum,S_ChannelLocalisation, S_DataPositive, S_DataScale, S_Caption, S_Translucency, S_ColorGradient, P_None),
"Displays each and every input matrix as a series of colored bars, instantly.\nChannels are vertically distributed.", ""))
OVP_Declare_New(MIOscilloscope("Instant Multi oscilloscope_t", CIdentifier(0x4F142FE87C7773E3), CIdentifier(0x072B75A9C28D588A), "0.0.0", "0.0.0",
CParameterSet(I_Signal, S_ChannelLocalisation, S_DataPositive, S_DataScale, S_Caption, S_Translucency, S_ColorGradient, P_None),
"Displays each and every input matrix as a series of curves, instantly.\nAll channels are rendered on the same vertical axis, with different colors.",
""))
OVP_Declare_New(i_xyz_plot_t("Instant XYZ Plot", CIdentifier(0x1353BED819EC86A5), CIdentifier(0xDB19374A41A22DD3), "0.0.0", "0.0.0",
CParameterSet(I_Signal, S_DataScale, S_Caption, S_Translucency, S_ShowAxis, S_XYZPlotHasDepth, S_ColorGradient,F_FixedChannelOrder,
F_FixedChannelSelection, P_None),
"Displays the input matrices as a series of points in 2D or 3D space, instantly.", ""))
OVP_Declare_New(sv_bitmap_t("Stacked bitmap_t (Vertical)", CIdentifier(0x93F400CF9F6C5AFD), CIdentifier(0x9926A761BA82D233), "0.0.0", "0.0.0",
CParameterSet(I_Matrix, I_Stimulations, S_ChannelLocalisation, S_TemporalCoherence, S_TimeScale, S_ElementCount, S_DataScale, S_Caption, S_ColorGradient
,P_None),
"Displays the input matrices as a map of colored tiles, or bitmap, continuously.\nAll the bitmaps are stacked vertically, starting from the bottom edge of the window"
, ""))
OVP_Declare_New(sh_bitmap_t("Stacked bitmap_t (Horizontal)", CIdentifier(0x6B22DC653AB0EC0F), CIdentifier(0x7B0DDB65FDC51488), "0.0.0", "0.0.0",
CParameterSet(I_Matrix, I_Stimulations, S_ChannelLocalisation, S_TemporalCoherence, S_TimeScale, S_ElementCount, S_DataScale, S_Caption, S_ColorGradient
, P_None),
"Displays the input matrices as a map of colored tiles, or bitmap, continuously.\nAll the bitmaps are stacked horizontally, starting from the left edge of the window"
, ""))
OVP_Declare_New(si_bitmap_t("Instant bitmap_t (3D Stream)", CIdentifier(0x0C61E7632A5C3178), CIdentifier(0xC3CC8B43EE985C1D), "0.1.0", "0.1.0",
CParameterSet(I_TimeFrequency, I_Stimulations, S_ChannelLocalisation, S_DataScale, S_Caption, S_ColorGradient, P_None),
"Displays the input matrices as a map of colored tiles, or bitmap, continuously.", ""))
OVP_Declare_New(topography_2d_t("2D Topography", CIdentifier(0x0A0C12A7E695F3FB), CIdentifier(0x7C3A05B8C45386F8), "0.0.0", "0.0.0",
CParameterSet(I_Signal, S_ChannelLocalisation, S_DataScale, S_Caption, S_ColorGradient, F_FixedChannelOrder, P_None),
"The input is mapped to a 2 dimensional plane model of the scalp surface, using a color gradient and interpolation.", ""))
OVP_Declare_New(topography_3d_t("3D Topography", CIdentifier(0xA3F1CF20DEC477F3), CIdentifier(0xC709EA84B577D910), "0.0.0", "0.0.0",
CParameterSet(I_Signal,S_ChannelLocalisation, S_DataScale, S_Caption, S_ColorGradient, F_FixedChannelOrder, P_None),
"The input is mapped to a 3D model of the scalp, using a color gradient and interpolation.", ""))
OVP_Declare_New(cubes_t("3D cubes_t", CIdentifier(0x6307469DF938EF27), CIdentifier(0xE028305D4ACF9D1A), "0.0.0", "0.0.0",
CParameterSet(I_Signal,S_ChannelLocalisation, S_DataScale, S_Caption, S_ColorGradient, F_FixedChannelOrder, P_None),
"The input is mapped to a 3D representation of the EEG setup where electrodes are symbolized with cubes,\nusing a color gradient and interpolation.", ""
))
OVP_Declare_New(loreta_t("3D Tomographic Visualization", CIdentifier(0xD5F62E7685E0D97C), CIdentifier(0xFD0826035C47E922), "0.0.0", "0.0.0",
CParameterSet(I_Signal, S_DataScale, S_Caption, S_Translucency, S_ColorGradient, F_FixedChannelOrder, P_None),
"Displays sources activity by mapping it to voxels in a 3D model of the scalp.", ""))
OVP_Declare_End()
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,80 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerBottomCount : public IRuler
{
public:
void renderBottom(GtkWidget* widget) override
{
if (m_renderer == nullptr) { return; }
if (m_renderer->getSampleCount() == 0) { return; }
if (m_renderer->getHistoryCount() == 0) { return; }
if (m_renderer->getHistoryIndex() == 0) { return; }
const size_t nSample = m_renderer->getSampleCount();
const size_t historyIdx = m_renderer->getHistoryIndex();
const size_t leftIdx1 = historyIdx - historyIdx % nSample;
const size_t leftIdx2 = historyIdx;
const size_t rightIdx1 = leftIdx2 - nSample;
const size_t rightIdx2 = leftIdx1;
std::vector<double> range1 = splitRange(double(leftIdx1), double(leftIdx1 + nSample), 10);
std::vector<double> range2 = splitRange(double(rightIdx1), double(rightIdx1 + nSample), 10);
gint w, h, x;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& i : range1)
{
if (i >= leftIdx1 && i < leftIdx2)
{
x = gint(((i - leftIdx1) / nSample) * w);
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
gdk_draw_layout(widget->window, drawGC, x, 5, layout);
gdk_draw_line(widget->window, drawGC, x, 0, x, 3);
g_object_unref(layout);
}
}
for (const auto& i : range2)
{
if (i >= rightIdx1 && i < rightIdx2)
{
x = gint(((i + nSample - leftIdx1) / nSample) * w);
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
gdk_draw_layout(widget->window, drawGC, x, 5, layout);
gdk_draw_line(widget->window, drawGC, x, 0, x, 3);
g_object_unref(layout);
}
}
g_object_unref(drawGC);
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,59 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerBottomERPCount : public IRuler
{
public:
void renderBottom(GtkWidget* widget) override
{
if (m_renderer == nullptr) { return; }
if (m_renderer->getSampleCount() == 0) { return; }
if (m_renderer->getHistoryCount() == 0) { return; }
if (m_renderer->getHistoryIndex() == 0) { return; }
const size_t nSample = m_renderer->getSampleCount();
const double duration = nSample * 1.;
std::vector<double> range = splitRange(0, duration, 10);
gint w, h;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& i : range)
{
const gint x = gint((i / duration) * w);
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
gdk_draw_layout(widget->window, drawGC, x, 5, layout);
gdk_draw_line(widget->window, drawGC, x, 0, x, 3);
g_object_unref(layout);
}
g_object_unref(drawGC);
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,60 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerBottomERPTime : public IRuler
{
public:
void renderBottom(GtkWidget* widget) override
{
if (m_renderer == nullptr) { return; }
if (m_renderer->getSampleCount() == 0) { return; }
if (m_renderer->getHistoryCount() == 0) { return; }
if (m_renderer->getHistoryIndex() == 0) { return; }
const size_t nSample = m_renderer->getSampleCount();
const uint64_t sampleDuration = m_rendererCtx->getSampleDuration();
const double duration = double((nSample * sampleDuration) >> 16) / 65536.;
std::vector<double> range = splitRange(0, duration, 10);
gint w, h;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& i : range)
{
const gint x = gint((i / duration) * w);
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
gdk_draw_layout(widget->window, drawGC, x, 5, layout);
gdk_draw_line(widget->window, drawGC, x, 0, x, 3);
g_object_unref(layout);
}
g_object_unref(drawGC);
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,60 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerBottomFrequency : public IRuler
{
public:
void renderBottom(GtkWidget* widget) override
{
const auto scale = float(m_rendererCtx->getSpectrumFrequencyRange());
if (m_lastScale != scale)
{
m_range = splitRange(0, scale);
m_lastScale = scale;
}
gint w, h;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& i : m_range)
{
const gint x = gint((i / scale) * w);
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
gdk_draw_layout(widget->window, drawGC, x, 5, layout);
gdk_draw_line(widget->window, drawGC, x, 0, x, 3);
g_object_unref(layout);
}
g_object_unref(drawGC);
}
protected:
float m_lastScale = 0;
std::vector<double> m_range;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,54 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerBottomPercent : public IRuler
{
public:
void renderBottom(GtkWidget* widget) override
{
gint w, h;
gint lw, lh;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (int i = 0; i <= 10; i += 2)
{
const gint x = (i * (w - 1)) / 10;
const std::string label = (std::to_string(i * 10) + "%");
PangoLayout* layout = gtk_widget_create_pango_layout(widget, label.c_str());
pango_layout_get_size(layout, &lw, &lh);
lw /= PANGO_SCALE;
lh /= PANGO_SCALE;
gdk_draw_layout(widget->window, drawGC, x, 4, layout);
gdk_draw_line(widget->window, drawGC, x, 0, x, 3);
g_object_unref(layout);
}
g_object_unref(drawGC);
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,90 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mCRulerTexture.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerBottomTexture : public CRulerTexture
{
public:
void render() override
{
this->preRender();
glColor4f(0, 0, 0, m_blackAlpha);
glBegin(GL_QUADS);
glTexCoord1f(0);
glVertex2f(0, 0.00F);
glVertex2f(0, 0.05F);
glTexCoord1f(1);
glVertex2f(1, 0.05F);
glVertex2f(1, 0.00F);
glEnd();
glColor4f(1, 1, 1, m_whiteAlpha);
glBegin(GL_QUADS);
glTexCoord1f(0);
glVertex2f(0, 0.00F);
glVertex2f(0, 0.04F);
glTexCoord1f(1);
glVertex2f(1, 0.04F);
glVertex2f(1, 0.00F);
glEnd();
this->postRender();
}
void renderBottom(GtkWidget* widget) override
{
const float scale = 1.F / m_rendererCtx->getScale();
if (m_lastScale != scale)
{
m_range = splitRange(-scale * .5, scale * .5);
m_lastScale = scale;
}
gint w, h;
gint lw, lh;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& i : m_range)
{
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
pango_layout_get_size(layout, &lw, &lh);
lw /= PANGO_SCALE;
lh /= PANGO_SCALE;
gdk_draw_layout(widget->window, drawGC, gint((.5 + i / scale) * w - lw * .5), 0, layout);
g_object_unref(layout);
}
g_object_unref(drawGC);
}
protected:
float m_lastScale = 0;
std::vector<double> m_range;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,85 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerBottomTime : public IRuler
{
public:
void renderBottom(GtkWidget* widget) override
{
if (m_renderer == nullptr) { return; }
if (m_renderer->getSampleCount() == 0) { return; }
if (m_renderer->getHistoryCount() == 0) { return; }
if (m_renderer->getHistoryIndex() == 0) { return; }
const size_t nSample = m_renderer->getSampleCount();
const size_t historyIdx = m_renderer->getHistoryIndex();
const uint64_t sampleDuration = m_rendererCtx->getSampleDuration();
const size_t leftIdx = historyIdx - historyIdx % nSample;
const size_t midIdx = historyIdx;
double startTime = double((leftIdx * sampleDuration) >> 16) / 65536.;
double midTime = double((midIdx * sampleDuration) >> 16) / 65536.;
const double duration = double((nSample * sampleDuration) >> 16) / 65536.;
const double offset = (m_renderer->getTimeOffset() >> 16) / 65536.;
startTime += offset;
midTime += offset;
std::vector<double> range1 = this->splitRange(startTime - duration, startTime, 10);
std::vector<double> range2 = this->splitRange(startTime, startTime + duration, 10);
gint w, h, x;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& i : range1)
{
if (i >= 0 && i + duration > midTime)
{
x = gint(((i + duration - startTime) / duration) * w);
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
gdk_draw_layout(widget->window, drawGC, x, 5, layout);
gdk_draw_line(widget->window, drawGC, x, 0, x, 3);
g_object_unref(layout);
}
}
for (const auto& i : range2)
{
if (i >= 0 && i < midTime)
{
x = gint(((i - startTime) / duration) * w);
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
gdk_draw_layout(widget->window, drawGC, x, 5, layout);
gdk_draw_line(widget->window, drawGC, x, 0, x, 3);
g_object_unref(layout);
}
}
g_object_unref(drawGC);
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,43 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerConditionIsTimeLocked : public IRuler
{
public:
CRulerConditionIsTimeLocked() : m_rendererCtx(nullptr), m_renderer(nullptr) { }
void setRendererContext(const CRendererContext* ctx) override { m_rendererCtx = ctx; }
void setRenderer(const IRenderer* renderer) override { m_renderer = renderer; }
bool operator()() const { return m_rendererCtx->isTimeLocked(); }
protected:
const CRendererContext* m_rendererCtx;
const IRenderer* m_renderer;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,60 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerERPProgress : public IRuler
{
public:
void render() override
{
if (m_renderer == nullptr) { return; }
if (m_renderer->getSampleCount() == 0) { return; }
if (m_renderer->getHistoryCount() == 0) { return; }
if (m_renderer->getHistoryIndex() == 0) { return; }
const float progress = m_rendererCtx->getERPFraction();
if (progress != 0 && progress != 1)
{
glDisable(GL_TEXTURE_1D);
glLineWidth(4);
glColor3f(0, 0, 0);
glBegin(GL_LINES);
glVertex2f(progress, 0);
glVertex2f(progress, 1);
glEnd();
glLineWidth(2);
glColor3f(0.25, 1, 0.25);
glBegin(GL_LINES);
glVertex2f(progress, 0);
glVertex2f(progress, 1);
glEnd();
}
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,53 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
#include <string>
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerLeftChannelNames : public IRuler
{
public:
void renderLeft(GtkWidget* widget) override
{
gint w, h, lw, lh;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (size_t i = 0; i < m_rendererCtx->getSelectedCount(); ++i)
{
const size_t idx = m_rendererCtx->getSelected(i);
const std::string label = (m_rendererCtx->getChannelName(idx) + " (" + std::to_string(idx + 1) + ")");
PangoLayout* layout = gtk_widget_create_pango_layout(widget, label.c_str());
pango_layout_get_size(layout, &lw, &lh);
lw /= PANGO_SCALE;
lh /= PANGO_SCALE;
gdk_draw_layout(widget->window, drawGC, w - lw, gint(((i + 0.5) * h) / m_rendererCtx->getSelectedCount() - float(lh) / 2), layout);
g_object_unref(layout);
}
g_object_unref(drawGC);
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,60 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mCRulerTexture.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerLeftTexture : public CRulerTexture
{
public:
void render() override
{
this->preRender();
glColor4f(0, 0, 0, m_blackAlpha);
glBegin(GL_QUADS);
glTexCoord1f(0);
glVertex2f(0.00F, 0);
glVertex2f(0.05F, 0);
glTexCoord1f(1);
glVertex2f(0.05F, 1);
glVertex2f(0.00F, 1);
glEnd();
glColor4f(1, 1, 1, m_whiteAlpha);
glBegin(GL_QUADS);
glTexCoord1f(0);
glVertex2f(0.00F, 0);
glVertex2f(0.04F, 0);
glTexCoord1f(1);
glVertex2f(0.04F, 1);
glVertex2f(0.00F, 1);
glEnd();
this->postRender();
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,48 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerProgress : public IRuler
{
public:
virtual void renderFinal(const float progress) = 0;
void render() override
{
if (m_renderer == nullptr) { return; }
if (m_renderer->getSampleCount() == 0) { return; }
if (m_renderer->getHistoryCount() == 0) { return; }
if (m_renderer->getHistoryIndex() == 0) { return; }
const size_t nSample = m_renderer->getSampleCount();
const size_t historyIdx = m_renderer->getHistoryIndex();
const float progress = float(historyIdx - (float(historyIdx) / nSample) * nSample) / nSample;
if (progress != 0 && progress != 1) { this->renderFinal(progress); }
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,59 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mCRulerProgress.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerProgressH : public CRulerProgress
{
public:
void renderFinal(const float progress) override
{
const size_t selectedCount = m_rendererCtx->getSelectedCount();
glDisable(GL_TEXTURE_1D);
glDisable(GL_BLEND);
glLineWidth(4);
glColor3f(0, 0, 0);
glBegin(GL_LINES);
for (size_t i = 0; i < selectedCount; ++i)
{
glVertex2f(0, (i + progress) / selectedCount);
glVertex2f(1, (i + progress) / selectedCount);
}
glEnd();
glLineWidth(2);
glColor3f(0.25, 1, 0.25);
glBegin(GL_LINES);
for (size_t i = 0; i < selectedCount; ++i)
{
glVertex2f(0, (i + progress) / selectedCount);
glVertex2f(1, (i + progress) / selectedCount);
}
glEnd();
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,52 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mCRulerProgress.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerProgressV : public CRulerProgress
{
public:
void renderFinal(const float progress) override
{
glDisable(GL_TEXTURE_1D);
glDisable(GL_BLEND);
glLineWidth(4);
glColor3f(0, 0, 0);
glBegin(GL_LINES);
glVertex2f(progress, 0);
glVertex2f(progress, 1);
glEnd();
glLineWidth(2);
glColor3f(0.25, 1, 0.25);
glBegin(GL_LINES);
glVertex2f(progress, 0);
glVertex2f(progress, 1);
glEnd();
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,80 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerRightCount : public IRuler
{
public:
void renderRight(GtkWidget* widget) override
{
if (m_renderer == nullptr) { return; }
if (m_renderer->getSampleCount() == 0) { return; }
if (m_renderer->getHistoryCount() == 0) { return; }
if (m_renderer->getHistoryIndex() == 0) { return; }
const size_t nSample = m_renderer->getSampleCount();
const size_t historyIdx = m_renderer->getHistoryIndex();
const size_t leftIdx1 = historyIdx - historyIdx % nSample;
const size_t leftIdx2 = historyIdx;
const size_t rightIdx1 = leftIdx2 - nSample;
const size_t rightIdx2 = leftIdx1;
std::vector<double> range1 = splitRange(double(leftIdx1), double(leftIdx1 + nSample), 10);
std::vector<double> range2 = splitRange(double(rightIdx1), double(rightIdx1 + nSample), 10);
gint w, h, y;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& i : range1)
{
if (i >= leftIdx1 && i < leftIdx2)
{
y = gint(((i - leftIdx1) / nSample) * h);
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
gdk_draw_layout(widget->window, drawGC, 5, y, layout);
gdk_draw_line(widget->window, drawGC, 0, y, 3, y);
g_object_unref(layout);
}
}
for (const auto& i : range2)
{
if (i >= rightIdx1 && i < rightIdx2)
{
y = gint(((i + nSample - leftIdx1) / nSample) * h);
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
gdk_draw_layout(widget->window, drawGC, 5, y, layout);
gdk_draw_line(widget->window, drawGC, 0, y, 3, y);
g_object_unref(layout);
}
}
g_object_unref(drawGC);
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,68 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerRightFrequency : public IRuler
{
public:
void renderRight(GtkWidget* widget) override
{
const auto scale = float(m_rendererCtx->getSpectrumFrequencyRange());
if (m_lastScale != scale)
{
m_range = splitRange(0, scale);
m_lastScale = scale;
}
gint w, h;
gint lw, lh;
const size_t nChannel = m_rendererCtx->getSelectedCount();
for (size_t i = 0; i < nChannel; ++i)
{
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& r : m_range)
{
const gint y = gint((i + r / scale) * (h * 1.F / nChannel));
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(r).c_str());
pango_layout_get_size(layout, &lw, &lh);
lw /= PANGO_SCALE;
lh /= PANGO_SCALE;
gdk_draw_layout(widget->window, drawGC, 8, h - y - lh / 2, layout);
gdk_draw_line(widget->window, drawGC, 0, h - y, 3, h - y);
g_object_unref(layout);
}
g_object_unref(drawGC);
}
}
protected:
float m_lastScale = 1;
std::vector<double> m_range;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,66 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
template <size_t TDim>
class CRulerRightLabels : public IRuler
{
public:
void renderRight(GtkWidget* widget) override
{
gint w, h, lw, lh;
const size_t nChannel = m_rendererCtx->getSelectedCount();
for (size_t channel = 0; channel < nChannel; ++channel)
{
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
const auto labelCount = float(m_rendererCtx->getDimensionLabelCount(TDim));
gint lastY = gint((channel + (-1 + 0.5F) / labelCount) * (h * 1.F / nChannel));
for (size_t label = 0; label < m_rendererCtx->getDimensionLabelCount(TDim); ++label)
{
const gint y = gint((channel + (label + 0.5F) / labelCount) * (h * 1.F / nChannel));
if (y >= lastY + 10)
{
PangoLayout* layout = gtk_widget_create_pango_layout(widget, m_rendererCtx->getDimensionLabel(TDim, label));
pango_layout_get_size(layout, &lw, &lh);
lw /= PANGO_SCALE;
lh /= PANGO_SCALE;
gdk_draw_layout(widget->window, drawGC, 8, h - y - lh / 2, layout);
gdk_draw_line(widget->window, drawGC, 0, h - y, 3, h - y);
g_object_unref(layout);
lastY = y;
}
}
g_object_unref(drawGC);
}
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,68 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerRightMonoScale : public IRuler
{
public:
CRulerRightMonoScale() : m_lastScale(-1) { }
void renderRight(GtkWidget* widget) override
{
const float scale = 1.F / m_rendererCtx->getScale();
if (m_lastScale != scale)
{
if (m_rendererCtx->isPositiveOnly()) { m_range = splitRange(0, scale, IRuler_SplitCount); }
else { m_range = splitRange(-scale * .5, scale * .5, IRuler_SplitCount); }
m_lastScale = scale;
}
const float offset = m_rendererCtx->isPositiveOnly() ? 0 : 0.5F;
gint w, h, lw, lh;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& i : m_range)
{
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
pango_layout_get_size(layout, &lw, &lh);
lw /= PANGO_SCALE;
lh /= PANGO_SCALE;
const gint y = gint((1 - (offset + i / scale)) * h);
gdk_draw_layout(widget->window, drawGC, 8, y - lh / 2, layout);
gdk_draw_line(widget->window, drawGC, 0, y, 3, y);
g_object_unref(layout);
}
g_object_unref(drawGC);
}
protected:
float m_lastScale = 1;
std::vector<double> m_range;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,74 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerRightScale : public IRuler
{
public:
CRulerRightScale() : m_lastScale(-1) { }
void renderRight(GtkWidget* pWidget) override
{
const size_t nSelected = m_rendererCtx->getSelectedCount();
if (!nSelected) { return; }
const float scale = 1.F / m_rendererCtx->getScale();
if (m_lastScale != scale)
{
if (m_rendererCtx->isPositiveOnly()) { m_range = splitRange(0, scale, IRuler_SplitCount); }
else { m_range = splitRange(-scale * .5, scale * .5, IRuler_SplitCount); }
m_lastScale = scale;
}
const float offset = m_rendererCtx->isPositiveOnly() ? 0.0F : 0.5F;
gint w, h, lw, lh;
gdk_drawable_get_size(pWidget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(pWidget->window);
for (size_t i = 0; i < m_rendererCtx->getSelectedCount(); ++i)
{
for (const auto& j : m_range)
{
PangoLayout* layout = gtk_widget_create_pango_layout(pWidget, getLabel(j).c_str());
pango_layout_get_size(layout, &lw, &lh);
lw /= PANGO_SCALE;
lh /= PANGO_SCALE;
const gint y = gint((1 - (float(i) + offset + j / scale) / nSelected) * h);
gdk_draw_layout(pWidget->window, drawGC, 8, y - lh / 2, layout);
gdk_draw_line(pWidget->window, drawGC, 0, y, 3, y);
g_object_unref(layout);
}
}
g_object_unref(drawGC);
}
protected:
float m_lastScale = 1;
std::vector<double> m_range;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,90 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mCRulerTexture.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerRightTexture : public CRulerTexture
{
public:
CRulerRightTexture() : m_lastScale(-1) { }
void render() override
{
this->preRender();
glColor4f(0, 0, 0, m_blackAlpha);
glBegin(GL_QUADS);
glTexCoord1f(0);
glVertex2f(0.95F, 0);
glVertex2f(1.00F, 0);
glTexCoord1f(1);
glVertex2f(1.00F, 1);
glVertex2f(0.95F, 1);
glEnd();
glColor4f(1, 1, 1, m_whiteAlpha);
glBegin(GL_QUADS);
glTexCoord1f(0);
glVertex2f(0.96F, 0);
glVertex2f(1.00F, 0);
glTexCoord1f(1);
glVertex2f(1.00F, 1);
glVertex2f(0.96F, 1);
glEnd();
this->postRender();
}
void renderRight(GtkWidget* widget) override
{
const float scale = 1.F / m_rendererCtx->getScale();
if (m_lastScale != scale)
{
m_range = this->splitRange(-scale * .5, scale * .5);
m_lastScale = scale;
}
gint w, h, lw, lh;
gdk_drawable_get_size(widget->window, &w, &h);
GdkGC* drawGC = gdk_gc_new(widget->window);
for (const auto& i : m_range)
{
PangoLayout* layout = gtk_widget_create_pango_layout(widget, getLabel(i).c_str());
pango_layout_get_size(layout, &lw, &lh);
lw /= PANGO_SCALE;
lh /= PANGO_SCALE;
gdk_draw_layout(widget->window, drawGC, 0, gint((.5 - i / scale) * h - lh * .5), layout);
g_object_unref(layout);
}
g_object_unref(drawGC);
}
protected:
float m_lastScale;
std::vector<double> m_range;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,61 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
class CRulerTexture : public IRuler
{
protected:
virtual void preRender()
{
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_1D);
glColor3f(1, 1, 1);
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
}
virtual void postRender()
{
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,89 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
#include "../m_VisualizationTools.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
template <class TRulerMatrix, class TRulerSignal, class TRulerSpectrum>
class TRulerAutoType : public IRuler
{
public:
void setRendererContext(const CRendererContext* ctx) override
{
IRuler::setRendererContext(ctx);
m_rulerSignal.setRendererContext(ctx);
m_rulerSpectrum.setRendererContext(ctx);
m_rulerMatrix.setRendererContext(ctx);
}
void setRenderer(const IRenderer* renderer) override
{
IRuler::setRenderer(renderer);
m_rulerSignal.setRenderer(renderer);
m_rulerSpectrum.setRenderer(renderer);
m_rulerMatrix.setRenderer(renderer);
}
void render() override
{
const CRendererContext::EDataType dataType = m_rendererCtx->getDataType();
if (dataType == CRendererContext::EDataType::Signal) { m_rulerSignal.doRender(); }
else if (dataType == CRendererContext::EDataType::Spectrum) { m_rulerSpectrum.doRender(); }
else { m_rulerMatrix.doRender(); }
}
void renderLeft(GtkWidget* widget) override
{
const CRendererContext::EDataType dataType = m_rendererCtx->getDataType();
if (dataType == CRendererContext::EDataType::Signal) { m_rulerSignal.doRenderLeft(widget); }
else if (dataType == CRendererContext::EDataType::Spectrum) { m_rulerSpectrum.doRenderLeft(widget); }
else { m_rulerMatrix.doRenderLeft(widget); }
}
void renderRight(GtkWidget* widget) override
{
const CRendererContext::EDataType dataType = m_rendererCtx->getDataType();
if (dataType == CRendererContext::EDataType::Signal) { m_rulerSignal.doRenderRight(widget); }
else if (dataType == CRendererContext::EDataType::Spectrum) { m_rulerSpectrum.doRenderRight(widget); }
else { m_rulerMatrix.doRenderRight(widget); }
}
void renderBottom(GtkWidget* widget) override
{
const CRendererContext::EDataType dataType = m_rendererCtx->getDataType();
if (dataType == CRendererContext::EDataType::Signal) { m_rulerSignal.doRenderBottom(widget); }
else if (dataType == CRendererContext::EDataType::Spectrum) { m_rulerSpectrum.doRenderBottom(widget); }
else { m_rulerMatrix.doRenderBottom(widget); }
}
protected:
TRulerMatrix m_rulerMatrix;
TRulerSignal m_rulerSignal;
TRulerSpectrum m_rulerSpectrum;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,58 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
template <class T1, class T2, class TCondition>
class TRulerConditionalPair : public IRuler
{
public:
void setRendererContext(const CRendererContext* ctx) override
{
IRuler::setRendererContext(ctx);
condition.setRendererContext(ctx);
first.setRendererContext(ctx);
second.setRendererContext(ctx);
}
void setRenderer(const IRenderer* renderer) override
{
IRuler::setRenderer(renderer);
condition.setRenderer(renderer);
first.setRenderer(renderer);
second.setRenderer(renderer);
}
void render() override { condition() ? first.doRender() : second.doRender(); }
void renderLeft(GtkWidget* widget) override { condition() ? first.doRenderLeft(widget) : second.doRenderLeft(widget); }
void renderRight(GtkWidget* widget) override { condition() ? first.doRenderRight(widget) : second.doRenderRight(widget); }
void renderBottom(GtkWidget* widget) override { condition() ? first.doRenderBottom(widget) : second.doRenderBottom(widget); }
TCondition condition;
T1 first;
T2 second;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE
@@ -0,0 +1,74 @@
/*********************************************************************
* Software License Agreement (AGPL-3 License)
*
* OpenViBE Designer
* Based on OpenViBE V1.1.0, Copyright (C) Inria, 2006-2015
* Copyright (C) Inria, 2015-2017,V1.0
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "../mIRuler.hpp"
namespace OpenViBE {
namespace AdvancedVisualization {
template <class T1, class T2>
class TRulerPair : public IRuler
{
public:
void setRendererContext(const CRendererContext* ctx) override
{
IRuler::setRendererContext(ctx);
first.setRendererContext(ctx);
second.setRendererContext(ctx);
}
void setRenderer(const IRenderer* renderer) override
{
IRuler::setRenderer(renderer);
first.setRenderer(renderer);
second.setRenderer(renderer);
}
void render() override
{
first.doRender();
second.doRender();
}
void renderLeft(GtkWidget* widget) override
{
first.doRenderLeft(widget);
second.doRenderLeft(widget);
}
void renderRight(GtkWidget* widget) override
{
first.doRenderRight(widget);
second.doRenderRight(widget);
}
void renderBottom(GtkWidget* widget) override
{
first.doRenderBottom(widget);
second.doRenderBottom(widget);
}
T1 first;
T2 second;
};
} // namespace AdvancedVisualization
} // namespace OpenViBE